diff options
Diffstat (limited to 'usr.sbin/bsnmpd')
74 files changed, 43721 insertions, 0 deletions
diff --git a/usr.sbin/bsnmpd/Makefile b/usr.sbin/bsnmpd/Makefile new file mode 100644 index 0000000..632753d --- /dev/null +++ b/usr.sbin/bsnmpd/Makefile @@ -0,0 +1,8 @@ +# $FreeBSD$ + +SUBDIR= gensnmptree \ + bsnmpd \ + modules \ + tools + +.include <bsd.subdir.mk> diff --git a/usr.sbin/bsnmpd/Makefile.inc b/usr.sbin/bsnmpd/Makefile.inc new file mode 100644 index 0000000..265f86d --- /dev/null +++ b/usr.sbin/bsnmpd/Makefile.inc @@ -0,0 +1,3 @@ +# $FreeBSD$ + +.include "../Makefile.inc" diff --git a/usr.sbin/bsnmpd/bsnmpd/Makefile b/usr.sbin/bsnmpd/bsnmpd/Makefile new file mode 100644 index 0000000..3e6df47 --- /dev/null +++ b/usr.sbin/bsnmpd/bsnmpd/Makefile @@ -0,0 +1,52 @@ +# $FreeBSD$ +# +# Author: Harti Brandt <harti@freebsd.org> + +.include <bsd.own.mk> + +CONTRIB=${.CURDIR}/../../../contrib/bsnmp +.PATH: ${CONTRIB}/snmpd + +PROG= bsnmpd +SRCS= main.c action.c config.c export.c trap.c trans_udp.c trans_lsock.c +SRCS+= oid.h tree.c tree.h +XSYM= snmpMIB begemotSnmpdModuleTable begemotSnmpd begemotTrapSinkTable \ + sysUpTime snmpTrapOID coldStart authenticationFailure \ + begemotSnmpdTransUdp begemotSnmpdTransLsock begemotSnmpdLocalPortTable \ + freeBSD freeBSDVersion +CLEANFILES= oid.h tree.c tree.h +MAN= bsnmpd.1 snmpmod.3 +NO_WERROR= + +FILESGROUPS= BMIBS DEFS + +BMIBS= FOKUS-MIB.txt BEGEMOT-MIB.txt BEGEMOT-SNMPD.txt +BMIBSDIR= ${SHAREDIR}/snmp/mibs +DEFS= tree.def +DEFSDIR= ${SHAREDIR}/snmp/defs + +CFLAGS+= -DSNMPTREE_TYPES +CFLAGS+= -I${CONTRIB}/lib -I${CONTRIB}/snmpd -I. -DUSE_LIBBEGEMOT +CFLAGS+= -DUSE_TCPWRAPPERS -DQUADFMT='"llu"' -DQUADXFMT='"llx"' +CFLAGS+= -DHAVE_STDINT_H -DHAVE_INTTYPES_H -DHAVE_ERR_H -DHAVE_STRLCPY +DPADD= ${LIBBEGEMOT} ${LIBBSNMP} ${LIBWRAP} +LDADD= -lbegemot -lbsnmp -lwrap + +LDFLAGS= -Wl,-export-dynamic + +.if ${MK_OPENSSL} != "no" +CFLAGS+= -DHAVE_LIBCRYPTO +.endif + +oid.h: tree.def Makefile + gensnmptree -e ${XSYM} < ${.ALLSRC:M*.def} > ${.TARGET} + +.ORDER: tree.c tree.h +tree.c tree.h: tree.def + gensnmptree -l < ${.ALLSRC} + +MANFILTER= sed -e 's%@MODPATH@%${LIBDIR}/%g' \ + -e 's%@DEFPATH@%${DEFSDIR}/%g' \ + -e 's%@MIBSPATH@%${BMIBSDIR}/%g' + +.include <bsd.prog.mk> diff --git a/usr.sbin/bsnmpd/gensnmptree/Makefile b/usr.sbin/bsnmpd/gensnmptree/Makefile new file mode 100644 index 0000000..a92f4eb --- /dev/null +++ b/usr.sbin/bsnmpd/gensnmptree/Makefile @@ -0,0 +1,15 @@ +# $FreeBSD$ +# +# Author: Harti Brandt <harti@freebsd.org> + +CONTRIB=${.CURDIR}/../../../contrib/bsnmp +.PATH: ${CONTRIB}/gensnmptree + +PROG= gensnmptree +CFLAGS+= -I${CONTRIB}/lib +CFLAGS+= -DQUADFMT='"llu"' -DQUADXFMT='"llx"' -DHAVE_STDINT_H +CFLAGS+= -DHAVE_INTTYPES_H + +WARNS?= 5 + +.include <bsd.prog.mk> diff --git a/usr.sbin/bsnmpd/modules/Makefile b/usr.sbin/bsnmpd/modules/Makefile new file mode 100644 index 0000000..ab378f0 --- /dev/null +++ b/usr.sbin/bsnmpd/modules/Makefile @@ -0,0 +1,28 @@ +# $FreeBSD$ + +.include <bsd.own.mk> + +.PATH: ${.CURDIR}/../../../contrib/bsnmp/snmpd + +.if ${MK_ATM} != "no" +_snmp_atm= snmp_atm +.endif + +SUBDIR= ${_snmp_atm} \ + snmp_bridge \ + snmp_hostres \ + snmp_mibII \ + snmp_pf \ + snmp_target \ + snmp_usm \ + snmp_vacm \ + snmp_wlan + +.if ${MK_NETGRAPH_SUPPORT} != "no" +SUBDIR+=snmp_netgraph +.endif + +INCS= snmpmod.h +INCSDIR= ${INCLUDEDIR}/bsnmp + +.include <bsd.prog.mk> diff --git a/usr.sbin/bsnmpd/modules/Makefile.inc b/usr.sbin/bsnmpd/modules/Makefile.inc new file mode 100644 index 0000000..b5dad56 --- /dev/null +++ b/usr.sbin/bsnmpd/modules/Makefile.inc @@ -0,0 +1,9 @@ +# $FreeBSD$ + +SHLIB_MAJOR= 6 + +MANFILTER= sed -e 's%@MODPATH@%${LIBDIR}/%g' \ + -e 's%@DEFPATH@%${DEFSDIR}/%g' \ + -e 's%@MIBSPATH@%${BMIBSDIR}/%g' + +.include "../Makefile.inc" diff --git a/usr.sbin/bsnmpd/modules/snmp_atm/BEGEMOT-ATM-FREEBSD-MIB.txt b/usr.sbin/bsnmpd/modules/snmp_atm/BEGEMOT-ATM-FREEBSD-MIB.txt new file mode 100644 index 0000000..83c4e5c --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_atm/BEGEMOT-ATM-FREEBSD-MIB.txt @@ -0,0 +1,99 @@ +-- +-- Copyright (c) 2004 +-- Hartmut Brandt. +-- All rights reserved. +-- +-- Author: Hartmut Brandt <harti@freebsd.org> +-- +-- Redistribution and use in source and binary forms, with or without +-- modification, are permitted provided that the following conditions +-- are met: +-- 1. Redistributions of source code must retain the above copyright +-- notice, this list of conditions and the following disclaimer. +-- 2. Redistributions in binary form must reproduce the above copyright +-- notice, this list of conditions and the following disclaimer in the +-- documentation and/or other materials provided with the distribution. +-- +-- THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND +-- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +-- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +-- ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE +-- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +-- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +-- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +-- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +-- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +-- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +-- SUCH DAMAGE. +-- +-- $FreeBSD$ +-- +-- Private Begemot MIB for ATM interfaces on FreeBSD +-- +BEGEMOT-ATM-FREEBSD-MIB DEFINITIONS ::= BEGIN + +IMPORTS + MODULE-IDENTITY, OBJECT-TYPE + FROM SNMPv2-SMI + NgNodeIdOrZero + FROM BEGEMOT-NETGRAPH-MIB + begemotAtmSysGroup, begemotAtmIfEntry + FROM BEGEMOT-ATM-MIB; + +begemotAtmFreeBSDGroup MODULE-IDENTITY + LAST-UPDATED "200408060000Z" + ORGANIZATION "German Aerospace Centre" + CONTACT-INFO + " Hartmut Brandt + + Postal: German Aerospace Centre (DLR) + Institute of Communications and Navigation + 82234 Wessling + Germany + + Fax: +49 8153 28 2844 + + E-mail: harti@freebsd.org" + DESCRIPTION + "The FreeBSD specific Begemot MIB for ATM interfaces." + + ::= { begemotAtmSysGroup 1 } + +-- Netgraph +begemotAtmNgGroup OBJECT IDENTIFIER ::= { begemotAtmFreeBSDGroup 1 } + +-- +-- Interfaces table +-- +begemotAtmNgIfTable OBJECT-TYPE + SYNTAX SEQUENCE OF BegemotAtmNgIfEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "This table contains an entry for each hardware ATM + interface. The table is indexed by the interface index." + ::= { begemotAtmNgGroup 1 } + +begemotAtmNgIfEntry OBJECT-TYPE + SYNTAX BegemotAtmNgIfEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "This is a table entry describing one ATM hardware interface." + AUGMENTS { begemotAtmIfEntry } + ::= { begemotAtmNgIfTable 1 } + +BegemotAtmNgIfEntry ::= SEQUENCE { + begemotAtmNgIfNodeId NgNodeIdOrZero +} + +begemotAtmNgIfNodeId OBJECT-TYPE + SYNTAX NgNodeIdOrZero + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The netgraph node id of the interface. If there is no + node corresponding to the interface, this is 0." + ::= { begemotAtmNgIfEntry 1 } + +END diff --git a/usr.sbin/bsnmpd/modules/snmp_atm/Makefile b/usr.sbin/bsnmpd/modules/snmp_atm/Makefile new file mode 100644 index 0000000..4bba7cb --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_atm/Makefile @@ -0,0 +1,21 @@ +# $FreeBSD$ +# +# Author: Harti Brandt <harti@freebsd.org> + +CONTRIB= ${.CURDIR}/../../../../contrib/ngatm +.PATH: ${CONTRIB}/snmp_atm + +MOD= atm +SRCS= snmp_atm.c atm_sys.c +XSYM= begemotAtm +MAN= snmp_atm.3 + +BMIBS= BEGEMOT-ATM.txt BEGEMOT-ATM-FREEBSD-MIB.txt +DEFS= ${MOD}_tree.def atm_freebsd.def +INCS= snmp_${MOD}.h + +EXTRAMIBDEFS= atm_freebsd.def + +CFLAGS+= -I${CONTRIB}/snmp_atm + +.include <bsd.snmpmod.mk> diff --git a/usr.sbin/bsnmpd/modules/snmp_atm/atm_freebsd.def b/usr.sbin/bsnmpd/modules/snmp_atm/atm_freebsd.def new file mode 100644 index 0000000..6cc61d0 --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_atm/atm_freebsd.def @@ -0,0 +1,56 @@ +# +# Copyright (c) 2004 +# Hartmut Brandt. +# All rights reserved. +# +# Author: Hartmut Brandt <harti@freebsd.org> +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# +# SNMP module for ATM hardware interfaces. +# +# $Begemot: libunimsg/snmp_atm/atm_tree.def,v 1.2 2004/08/05 07:14:22 brandt Exp $ +# +(1 internet + (4 private + (1 enterprises + (12325 fokus + (1 begemot + (101 begemotAtm + (1 begemotAtmObjects + (4 begemotAtmSysGroup + (1 begemotAtmFreeBSDGroup + (1 begemotAtmNgGroup + (1 begemotAtmNgIfTable + (1 begemotAtmNgIfEntry : INTEGER op_atmif_ng + (1 begemotAtmIfNodeId UNSIGNED32 GET) + )) + ) + ) + ) + ) + )) + ) + ) +)) diff --git a/usr.sbin/bsnmpd/modules/snmp_atm/atm_sys.c b/usr.sbin/bsnmpd/modules/snmp_atm/atm_sys.c new file mode 100644 index 0000000..525e805 --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_atm/atm_sys.c @@ -0,0 +1,301 @@ +/* + * Copyright (c) 2001-2002 + * Fraunhofer Institute for Open Communication Systems (FhG Fokus). + * All rights reserved. + * Copyright (c) 2003-2004 + * Hartmut Brandt. + * All rights reserved. + * + * Author: Hartmut Brandt <harti@freebsd.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + * + * SNMP module for ATM hardware interfaces - FreeBSD/Ng specific part. + */ + +#include "atm.h" +#include "atm_tree.h" +#include "atm_oid.h" + +#include <stdlib.h> +#include <syslog.h> +#include <string.h> + +#include <net/if_atm.h> + +#include <bsnmp/snmp_netgraph.h> +#include <netgraph/ng_message.h> +#include <netgraph/atm/ng_atm.h> + +static const struct hwinfo { + const char *device; + const char *vendor; +} hwinfo[] = { + ATM_DEVICE_NAMES +}; + +struct atmif_sys { + ng_ID_t atm_node; + void *regc; /* cookie registration */ +}; + +/* + * Find the interface for a given node + */ +struct atmif * +atm_node2if(u_int node) +{ + struct atmif_priv *aif; + + if (node != 0) + TAILQ_FOREACH(aif, &atmif_list, link) + if (aif->sys->atm_node == node) + return (&aif->pub); + return (NULL); +} + +u_int +atm_if2node(struct atmif *pub) +{ + struct atmif_priv *aif = (struct atmif_priv *)pub; + + return (aif->sys->atm_node); +} + +/* + * Destroy system dependend stuff. + */ +void +atmif_sys_destroy(struct atmif_priv *aif) +{ + + ng_unregister_cookie(aif->sys->regc); + free(aif->sys); + free(aif->pub.mib); +} + +/* + * Handle a message from the ATM node + */ +static void +handle_atm_message(const struct ng_mesg *mesg, const char *path __unused, + ng_ID_t node, void *uarg) +{ + struct atmif_priv *aif = uarg; + enum atmif_carrier_state ost; + + switch (mesg->header.cmd) { + + case NGM_ATM_IF_CHANGE: + { + const struct ngm_atm_if_change *arg; + + ost = aif->pub.carrier; + if (mesg->header.arglen != sizeof(*arg)) { + syslog(LOG_ERR, "ATM_IF_CHANGE: wrong size"); + atmif_check_carrier(aif); + return; + } + arg = (const struct ngm_atm_if_change *) + (const void *)mesg->data; + + if (arg->carrier) + aif->pub.carrier = ATMIF_CARRIER_ON; + else + aif->pub.carrier = ATMIF_CARRIER_OFF; + + if (ost != aif->pub.carrier) + atmif_send_notification(aif, ATMIF_NOTIFY_CARRIER, + (uintptr_t)ost); + return; + } + + case NGM_ATM_VCC_CHANGE: + { + const struct ngm_atm_vcc_change *arg; + + if (mesg->header.arglen != sizeof(*arg)) { + syslog(LOG_ERR, "ATM_VCC_CHANGE: wrong size"); + return; + } + arg = (const struct ngm_atm_vcc_change *) + (const void *)mesg->data; + atmif_send_notification(aif, ATMIF_NOTIFY_VCC, + (uintptr_t)(((arg->vpi & 0xff) << 24) | + ((arg->vci & 0xffff) << 8) | (arg->state & 1))); + return; + } + } + syslog(LOG_WARNING, "spurious message %u from node [%x]", + mesg->header.cmd, node); +} + +/* + * Attach to an ATM interface + */ +int +atmif_sys_attach_if(struct atmif_priv *aif) +{ + struct ng_mesg *resp, *resp1; + struct namelist *list; + u_int i; + + if ((aif->sys = malloc(sizeof(*aif->sys))) == NULL) { + syslog(LOG_CRIT, "out of memory"); + return (-1); + } + memset(aif->sys, 0, sizeof(*aif->sys)); + + if ((aif->pub.mib = malloc(sizeof(*aif->pub.mib))) == NULL) { + free(aif->sys); + syslog(LOG_CRIT, "out of memory"); + return (-1); + } + + atmif_sys_fill_mib(aif); + + /* + * Get ATM node Id. Must do it the hard way by scanning all nodes + * because the name may be wrong. + */ + if ((resp = ng_dialog_id(snmp_node, NGM_GENERIC_COOKIE, NGM_LISTNODES, + NULL, 0)) == NULL) { + syslog(LOG_ERR, "cannot fetch node list: %m"); + free(aif->sys); + return (-1); + } + list = (struct namelist *)(void *)resp->data; + + for (i = 0; i < list->numnames; i++) { + if (strcmp(list->nodeinfo[i].type, NG_ATM_NODE_TYPE) != 0) + continue; + if ((resp1 = ng_dialog_id(list->nodeinfo[i].id, + NGM_ATM_COOKIE, NGM_ATM_GET_IFNAME, NULL, 0)) == NULL) + continue; + if (strcmp(resp1->data, aif->pub.ifp->name) == 0) { + free(resp1); + break; + } + free(resp1); + } + if (i == list->numnames) + aif->sys->atm_node = 0; + else + aif->sys->atm_node = list->nodeinfo[i].id; + + free(resp); + + if ((aif->sys->regc = ng_register_cookie(module, NGM_ATM_COOKIE, + aif->sys->atm_node, handle_atm_message, aif)) == NULL) { + syslog(LOG_ERR, "cannot register cookie: %m"); + free(aif->sys); + return (-1); + } + return (0); +} + +/* + * Table of all ATM interfaces - Ng part + */ +int +op_atmif_ng(struct snmp_context *ctx __unused, struct snmp_value *value, + u_int sub, u_int vindex __unused, enum snmp_op op) +{ + struct atmif_priv *aif; + int err; + + if ((err = atmif_get_aif(value, sub, op, &aif)) != SNMP_ERR_NOERROR) + return (err); + + if (op == SNMP_OP_SET) { + switch (value->var.subs[sub - 1]) { + + default: + return (SNMP_ERR_NOT_WRITEABLE); + } + } + + switch (value->var.subs[sub - 1]) { + + case LEAF_begemotAtmIfNodeId: + value->v.uint32 = aif->sys->atm_node; + return (SNMP_ERR_NOERROR); + } + abort(); +} + +/* + * Get vendor string + */ +int +atm_sys_get_hw_vendor(struct atmif_priv *aif, struct snmp_value *value) +{ + + if (aif->pub.mib->device >= sizeof(hwinfo) / sizeof(hwinfo[0])) + return (string_get(value, "unknown", -1)); + return (string_get(value, hwinfo[aif->pub.mib->device].vendor, -1)); +} + +/* + * Get device string + */ +int +atm_sys_get_hw_device(struct atmif_priv *aif, struct snmp_value *value) +{ + + if (aif->pub.mib->device >= sizeof(hwinfo) / sizeof(hwinfo[0])) + return (string_get(value, "unknown", -1)); + return (string_get(value, hwinfo[aif->pub.mib->device].device, -1)); +} + +/* + * Extract the ATM MIB from the interface's private MIB + */ +void +atmif_sys_fill_mib(struct atmif_priv *aif) +{ + struct ifatm_mib *mib; + + if (aif->pub.ifp->specmiblen != sizeof(struct ifatm_mib)) { + syslog(LOG_ERR, "atmif MIB has wrong size %zu", + aif->pub.ifp->specmiblen); + memset(aif->pub.mib, 0, sizeof(*aif->pub.mib)); + aif->pub.mib->version = 0; + return; + } + mib = (struct ifatm_mib *)aif->pub.ifp->specmib; + + aif->pub.mib->device = mib->device; + aif->pub.mib->serial = mib->serial; + aif->pub.mib->hw_version = mib->hw_version; + aif->pub.mib->sw_version = mib->sw_version; + aif->pub.mib->media = mib->media; + + memcpy(aif->pub.mib->esi, mib->esi, 6); + aif->pub.mib->pcr = mib->pcr; + aif->pub.mib->vpi_bits = mib->vpi_bits; + aif->pub.mib->vci_bits = mib->vci_bits; + aif->pub.mib->max_vpcs = mib->max_vpcs; + aif->pub.mib->max_vccs = mib->max_vccs; +} diff --git a/usr.sbin/bsnmpd/modules/snmp_bridge/BEGEMOT-BRIDGE-MIB.txt b/usr.sbin/bsnmpd/modules/snmp_bridge/BEGEMOT-BRIDGE-MIB.txt new file mode 100644 index 0000000..d55ea3c --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_bridge/BEGEMOT-BRIDGE-MIB.txt @@ -0,0 +1,1166 @@ +-- +-- Copyright (C) 2006 Shteryana Shopova <syrinx@FreeBSD.org> +-- All rights reserved. +-- +-- Redistribution and use in source and binary forms, with or without +-- modification, are permitted provided that the following conditions +-- are met: +-- 1. Redistributions of source code must retain the above copyright +-- notice, this list of conditions and the following disclaimer. +-- 2. Redistributions in binary form must reproduce the above copyright +-- notice, this list of conditions and the following disclaimer in the +-- documentation and/or other materials provided with the distribution. +-- +-- THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND +-- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +-- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +-- ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE +-- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +-- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +-- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +-- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +-- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +-- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +-- SUCH DAMAGE. +-- +-- $FreeBSD$ +-- + +BEGEMOT-BRIDGE-MIB DEFINITIONS ::= BEGIN + +IMPORTS + MODULE-IDENTITY, OBJECT-TYPE, NOTIFICATION-TYPE, + Counter32, Integer32, TimeTicks, mib-2 + FROM SNMPv2-SMI + TEXTUAL-CONVENTION, MacAddress, TruthValue, RowStatus + FROM SNMPv2-TC + BridgeId, Timeout + FROM BRIDGE-MIB + InterfaceIndex FROM IF-MIB + begemot + FROM BEGEMOT-MIB; + +begemotBridge MODULE-IDENTITY + LAST-UPDATED "200708060000Z" + ORGANIZATION "Sofia University St. Kliment Ohridski" + CONTACT-INFO + " Shteryana Shopova + + Postal: Faculty of Mathematics and Informatics + 5 James Bourchier Blvd. + 1164 Sofia + Bulgaria + + Fax: +359 2 687 180 + + E-Mail: syrinx@FreeBSD.org" + DESCRIPTION + "The Begemot MIB for managing bridge interfaces." + REVISION "200708060000Z" + DESCRIPTION + "Third revision adds begemotBridgeBasePortPrivate + object." + REVISION "200611210000Z" + DESCRIPTION + "Second revision adds support for monitoring RSTP + specific variables." + REVISION "200607270000Z" + DESCRIPTION + "Initial revision." + ::= { begemot 205 } + +-- ---------------------------------------------------------- -- +BridgeIfName ::= TEXTUAL-CONVENTION + DISPLAY-HINT "16a" + STATUS current + DESCRIPTION + "Name of a bridge interface." + SYNTAX OCTET STRING (SIZE(1..16)) + +BridgeIfNameOrEmpty ::= TEXTUAL-CONVENTION + DISPLAY-HINT "16a" + STATUS current + DESCRIPTION + "Name of a bridge interface." + SYNTAX OCTET STRING (SIZE(0..16)) + +BridgePortId ::= TEXTUAL-CONVENTION + DISPLAY-HINT "1x.1x" + STATUS current + DESCRIPTION + "A port identifier that contains a bridge port's STP priority + in the first octet and the port number in the second octet." + SYNTAX OCTET STRING (SIZE(2)) + +-- ---------------------------------------------------------- -- +-- subtrees in the Begemot Bridge MIB +-- ---------------------------------------------------------- -- +begemotBridgeNotifications OBJECT IDENTIFIER ::= { begemotBridge 0 } + +begemotBridgeBase OBJECT IDENTIFIER ::= { begemotBridge 1 } + +begemotBridgeStp OBJECT IDENTIFIER ::= { begemotBridge 2 } + +begemotBridgeTp OBJECT IDENTIFIER ::= { begemotBridge 3 } + +begemotBridgePf OBJECT IDENTIFIER ::= { begemotBridge 4 } + +begemotBridgeConfigObjects OBJECT IDENTIFIER ::= { begemotBridge 5 } + +-- ---------------------------------------------------------- -- +-- the base Bridge interface table +-- ---------------------------------------------------------- -- + +begemotBridgeBaseTable OBJECT-TYPE + SYNTAX SEQUENCE OF BegemotBridgeBaseEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A table that contains generic information for each + bridge interface on the managed device." + ::= { begemotBridgeBase 1 } + +begemotBridgeBaseEntry OBJECT-TYPE + SYNTAX BegemotBridgeBaseEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A list of information for the bridge interfaces on + the managed device." + INDEX { begemotBridgeBaseName } + ::= { begemotBridgeBaseTable 1 } + +BegemotBridgeBaseEntry ::= SEQUENCE { + begemotBridgeBaseName BridgeIfName, + begemotBridgeBaseAddress MacAddress, + begemotBridgeBaseNumPorts Integer32, + begemotBridgeBaseType INTEGER, + begemotBridgeBaseStatus RowStatus +} + +begemotBridgeBaseName OBJECT-TYPE + SYNTAX BridgeIfName + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The name of the bridge interface for which this + entry contains management information." + ::= { begemotBridgeBaseEntry 1 } + +begemotBridgeBaseAddress OBJECT-TYPE + SYNTAX MacAddress + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The MAC address of the bridge interface." + ::= { begemotBridgeBaseEntry 2 } + +begemotBridgeBaseNumPorts OBJECT-TYPE + SYNTAX Integer32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of ports, members of this bridge." + ::= { begemotBridgeBaseEntry 3 } + +begemotBridgeBaseType OBJECT-TYPE + SYNTAX INTEGER { + unknown(1), + transparent-only(2), + sourceroute-only(3), + srt(4) + } + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Indicates what type of bridging this bridge can + perform." + ::= { begemotBridgeBaseEntry 4 } + +begemotBridgeBaseStatus OBJECT-TYPE + SYNTAX RowStatus + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "Used to create/destroy bridge interfaces on the + managed device." + ::= { begemotBridgeBaseEntry 5 } + +-- ---------------------------------------------------------- -- +-- the base Bridge ports table +-- ---------------------------------------------------------- -- + +begemotBridgeBasePortTable OBJECT-TYPE + SYNTAX SEQUENCE OF BegemotBridgeBasePortEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A table containing generic information about ports, + members of each bridge interface." + ::= { begemotBridgeBase 2 } + +begemotBridgeBasePortEntry OBJECT-TYPE + SYNTAX BegemotBridgeBasePortEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A list of information about a specific port, member of + a bridge interface." + INDEX { begemotBridgeBaseName, begemotBridgeBasePortIfIndex } + ::= { begemotBridgeBasePortTable 1 } + +BegemotBridgeBasePortEntry ::= SEQUENCE { + begemotBridgeBasePort Integer32, + begemotBridgeBasePortIfIndex InterfaceIndex, + begemotBridgeBaseSpanEnabled INTEGER, + begemotBridgeBasePortDelayExceededDiscards Counter32, + begemotBridgeBasePortMtuExceededDiscards Counter32, + begemotBridgeBasePortStatus RowStatus, + begemotBridgeBasePortPrivate TruthValue +} + +begemotBridgeBasePort OBJECT-TYPE + SYNTAX Integer32 (1..65535) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The system interface index of the interface corresponding + to this port." + ::= { begemotBridgeBasePortEntry 1 } + +begemotBridgeBasePortIfIndex OBJECT-TYPE + SYNTAX InterfaceIndex + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The value of the instance of the ifIndex object, + defined in IF-MIB, for the interface corresponding + to this port." + ::= { begemotBridgeBasePortEntry 2 } + +begemotBridgeBaseSpanEnabled OBJECT-TYPE + SYNTAX INTEGER { + enabled(1), + disabled(2) + } + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The value of this objects reflects whether the port + is a span port on the specified bridge interface." + ::= { begemotBridgeBasePortEntry 3 } + +begemotBridgeBasePortDelayExceededDiscards OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of frames discarded by this port due + to excessive transit delay through the bridge." + ::= { begemotBridgeBasePortEntry 4 } + +begemotBridgeBasePortMtuExceededDiscards OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of frames discarded by this port due + to an excessive size." + ::= { begemotBridgeBasePortEntry 5 } + +begemotBridgeBasePortStatus OBJECT-TYPE + SYNTAX RowStatus + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "Used to control addition of member ports to or + removal of member ports from a specified bridge." + ::= { begemotBridgeBasePortEntry 6 } + +begemotBridgeBasePortPrivate OBJECT-TYPE + SYNTAX TruthValue + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The value of this objects reflects whether the port + has a PRIVATE flag set. A port with this flags set + can only communicate with ports not having the + PRIVATE flag set." + ::= { begemotBridgeBasePortEntry 7 } + +-- ---------------------------------------------------------- -- +-- the Bridge interface STP table +-- ---------------------------------------------------------- -- + +begemotBridgeStpTable OBJECT-TYPE + SYNTAX SEQUENCE OF BegemotBridgeStpEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A table that contains Spanning Tree Protocol information + for each bridge interface on the managed device." + ::= { begemotBridgeStp 1 } + +begemotBridgeStpEntry OBJECT-TYPE + SYNTAX BegemotBridgeStpEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A list of information about the Spanning Tree Protocol + operation on a bridge interface." + AUGMENTS { begemotBridgeBaseEntry } + ::= { begemotBridgeStpTable 1 } + +BegemotBridgeStpEntry ::= SEQUENCE { + begemotBridgeStpProtocolSpecification INTEGER, + begemotBridgeStpPriority Integer32, + begemotBridgeStpTimeSinceTopologyChange TimeTicks, + begemotBridgeStpTopChanges Counter32, + begemotBridgeStpDesignatedRoot BridgeId, + begemotBridgeStpRootCost Integer32, + begemotBridgeStpRootPort Integer32, + begemotBridgeStpMaxAge Timeout, + begemotBridgeStpHelloTime Timeout, + begemotBridgeStpHoldTime Integer32, + begemotBridgeStpForwardDelay Timeout, + begemotBridgeStpBridgeMaxAge Timeout, + begemotBridgeStpBridgeHelloTime Timeout, + begemotBridgeStpBridgeForwardDelay Timeout, + begemotBridgeStpVersion INTEGER, + begemotBridgeStpTxHoldCount Integer32 +} + +begemotBridgeStpProtocolSpecification OBJECT-TYPE + SYNTAX INTEGER { + unknown(1), + decLb100(2), + ieee8021d(3) + } + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The Spanning Tree Protocol version being run on the + bridge interface. The value 'decLb100(2)' indicates the + DEC LANbridge 100 Spanning Tree protocol, 'ieee8021d(3)' + indicates the bridge is running IEEE 802.1D STP + implementation." + ::= { begemotBridgeStpEntry 1 } + +begemotBridgeStpPriority OBJECT-TYPE + SYNTAX Integer32 (0..65535) + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The priority value of the bridge interface forming the + first two octets of the bridge identifier. Acceptable + values are 0-61440, in steps of 4096." + ::= { begemotBridgeStpEntry 2 } + +begemotBridgeStpTimeSinceTopologyChange OBJECT-TYPE + SYNTAX TimeTicks + UNITS "centi-seconds" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The time (in hundreds of a second) since a topology change + was last detected by this bridge." + ::= { begemotBridgeStpEntry 3 } + +begemotBridgeStpTopChanges OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of times a topology change was detected by the + bridge interface since the management entity was initialized + or reset." + ::= { begemotBridgeStpEntry 4 } + +begemotBridgeStpDesignatedRoot OBJECT-TYPE + SYNTAX BridgeId + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The bridge identifier of the root of the spanning tree as + calculated by the Spanning Tree Protocol." + ::= { begemotBridgeStpEntry 5 } + +begemotBridgeStpRootCost OBJECT-TYPE + SYNTAX Integer32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The cost of the path from this bridge to the root bridge." + ::= { begemotBridgeStpEntry 6 } + +begemotBridgeStpRootPort OBJECT-TYPE + SYNTAX Integer32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The port number of the port that offers the lowest + cost path from this bridge to the root bridge of + the spanning tree. If this bridge is the root bridge, + this object shall have a value of zero." + ::= { begemotBridgeStpEntry 7 } + +begemotBridgeStpMaxAge OBJECT-TYPE + SYNTAX Timeout + UNITS "centi-seconds" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The maximum age of Spanning Tree Protocol information + received from the network on any port, before that + information is discarded. This is the actual value that + the bridge is currently using." + ::= { begemotBridgeStpEntry 8 } + +begemotBridgeStpHelloTime OBJECT-TYPE + SYNTAX Timeout + UNITS "centi-seconds" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The amount of time between transmission of + Configuration BPDUs by this bridge on any port, + when it is the root of the spanning tree or is + trying to become so. This is the actual value that + this bridge is currently using." + ::= { begemotBridgeStpEntry 9 } + +begemotBridgeStpHoldTime OBJECT-TYPE + SYNTAX Integer32 + UNITS "centi-seconds" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "This time value determines the interval length + during which no more than two Configuration BPDUs + shall be transmitted by this node, in units of + hundredths of a second." + ::= { begemotBridgeStpEntry 10 } + +begemotBridgeStpForwardDelay OBJECT-TYPE + SYNTAX Timeout + UNITS "centi-seconds" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "This value, measured in units of hundredths of a second + determines how long a port will stay consecutively in the + Listening and Learning states before transitioning to + Forwarding state. + This is the actual value currently used by the bridge + as opposed to begemotBridgeStpBridgeForwardDelay, which + is the value this and all bridges participating in the + spanning tree were to use, if this was the root bridge." + ::= { begemotBridgeStpEntry 11 } + +begemotBridgeStpBridgeMaxAge OBJECT-TYPE + SYNTAX Timeout (600..4000) + UNITS "centi-seconds" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The value that all bridges participating in the + spanning tree would use for MaxAge if this bridge + was the root of the spanning tree." + ::= { begemotBridgeStpEntry 12 } + +begemotBridgeStpBridgeHelloTime OBJECT-TYPE + SYNTAX Timeout (100..1000) + UNITS "centi-seconds" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The value that all bridges participating in the + spanning tree would use for HelloTime if this + bridge was the root of the spanning tree." + ::= { begemotBridgeStpEntry 13 } + +begemotBridgeStpBridgeForwardDelay OBJECT-TYPE + SYNTAX Timeout (400..3000) + UNITS "centi-seconds" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The value that all bridges participating in the + spanning tree would use for ForwardDelay if this + bridge was the root of the spanning tree." + ::= { begemotBridgeStpEntry 14 } + +begemotBridgeStpVersion OBJECT-TYPE + SYNTAX INTEGER { + stpCompatible(0), + rstp(2) + } + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The version of Spanning Tree Protocol the bridge is + currently running. The value 'stpCompatible(0)' + indicates the Spanning Tree Protocol specified in + IEEE 802.1D-1998 and 'rstp(2)' indicates the Rapid + Spanning Tree Protocol specified in IEEE 802.1w and + clause 17 of 802.1D-2004. The values are directly from + the IEEE standard. New values may be defined as future + versions of the protocol become available. + + The value of this object MUST be retained across + reinitializations of the management system." + DEFVAL { rstp } + ::= { begemotBridgeStpEntry 15 } + +begemotBridgeStpTxHoldCount OBJECT-TYPE + SYNTAX Integer32 (1..10) + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The value used by the Port Transmit state machine to limit + the maximum transmission rate of BPDUs on the bridge interface. + + The value of this object MUST be retained across + reinitializations of the management system." + DEFVAL { 3 } + ::= { begemotBridgeStpEntry 16 } + +-- ---------------------------------------------------------- -- +-- the Bridge STP ports table +-- ---------------------------------------------------------- -- + +begemotBridgeStpPortTable OBJECT-TYPE + SYNTAX SEQUENCE OF BegemotBridgeStpPortEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A table containing Spanning Tree Protocol information + about the members of each bridge interface." + ::= { begemotBridgeStp 2 } + +begemotBridgeStpPortEntry OBJECT-TYPE + SYNTAX BegemotBridgeStpPortEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A list of Spanning Tree Protocol information about + a specific member of a bridge interface." + INDEX { begemotBridgeBaseName, begemotBridgeBasePortIfIndex } + ::= { begemotBridgeStpPortTable 1 } + +BegemotBridgeStpPortEntry ::= SEQUENCE { + begemotBridgeStpPort Integer32, + begemotBridgeStpPortPriority Integer32, + begemotBridgeStpPortState INTEGER, + begemotBridgeStpPortEnable INTEGER, + begemotBridgeStpPortPathCost Integer32, + begemotBridgeStpPortDesignatedRoot BridgeId, + begemotBridgeStpPortDesignatedCost Integer32, + begemotBridgeStpPortDesignatedBridge BridgeId, + begemotBridgeStpPortDesignatedPort BridgePortId, + begemotBridgeStpPortForwardTransitions Counter32 +} + +begemotBridgeStpPort OBJECT-TYPE + SYNTAX Integer32 (1..65535) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The system interface index of the interface corresponding + to this port, for which the management entity has Spanning + Tree Protocol information." + ::= { begemotBridgeStpPortEntry 1 } + +begemotBridgeStpPortPriority OBJECT-TYPE + SYNTAX Integer32 (0..255) + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The STP priority of this port that is contained in the first + octet of its Port Identifier. The second octet contains the + value of begemotBridgeStpPort." + ::= { begemotBridgeStpPortEntry 2 } + +begemotBridgeStpPortState OBJECT-TYPE + SYNTAX INTEGER { + disabled(1), + blocking(2), + listening(3), + learning(4), + forwarding(5), + broken(6) + } + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The current state of the port as defined by the operation + of the Spanning Tree Protocol. If the Spanning Tree Protocol + is administratively disabled on the port, this object shall + have value disabled(1). A value of broken(6) does not correspond + to any legal state of a port, and if present should indicate + error in the operation of either the Spanning Tree Protocol + implementation running on the device or the management entity." + ::= { begemotBridgeStpPortEntry 3 } + +begemotBridgeStpPortEnable OBJECT-TYPE + SYNTAX INTEGER { + enabled(1), + disabled(2) + } + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The administrative Spanning Tree Protocol state of the + port - value of enabled(1) indicates that the port is + participating in the Spanning Tree Protocol operation." + ::= { begemotBridgeStpPortEntry 4 } + +begemotBridgeStpPortPathCost OBJECT-TYPE + SYNTAX Integer32 (1..65535) + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The contribution of the path through this port, when the port + is the Root Port, to the total cost of the path to the root + bridge for this bridge." + ::= { begemotBridgeStpPortEntry 5 } + +begemotBridgeStpPortDesignatedRoot OBJECT-TYPE + SYNTAX BridgeId + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The unique Bridge Identifier of the bridge recorded as the + root in the Root Identifier parameter of Configuration BPDUs + transmitted by the Designated Bridge for the LAN to which + the port is attached." + ::= { begemotBridgeStpPortEntry 6 } + +begemotBridgeStpPortDesignatedCost OBJECT-TYPE + SYNTAX Integer32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "For a Designated port, the path cost (equal to the Root + Path Cost of the bridge) offered to the LAN to which the + port is attached otherwise the cost of the path to the Root + offered by the Designated Port on the LAN to which this + Port is attached." + ::= { begemotBridgeStpPortEntry 7 } + +begemotBridgeStpPortDesignatedBridge OBJECT-TYPE + SYNTAX BridgeId + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The unique Bridge Identifier of the bridge to which the + port belongs, in the case when the port is a designated + port, otherwise the bridge believed to be the Designated + Bridge for the LAN to which this port is attached." + ::= { begemotBridgeStpPortEntry 8 } + +begemotBridgeStpPortDesignatedPort OBJECT-TYPE + SYNTAX BridgePortId + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The Port Identifier of the Bridge port, on the Designated + Bridge, through which the Designated Bridge transmits the + Configuration Message information stored by this port." + ::= { begemotBridgeStpPortEntry 9 } + +begemotBridgeStpPortForwardTransitions OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of times this port has transitioned + from the Learning state to the Forwarding state." + ::= { begemotBridgeStpPortEntry 10 } + +-- ---------------------------------------------------------- -- +-- the Bridge STP extended ports table +-- ---------------------------------------------------------- -- + +begemotBridgeStpExtPortTable OBJECT-TYPE + SYNTAX SEQUENCE OF BegemotBridgeStpExtPortEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A table that contains port-specific Rapid Spanning Tree + information for the bridge interface members." + ::= { begemotBridgeStp 3 } + +begemotBridgeStpExtPortEntry OBJECT-TYPE + SYNTAX BegemotBridgeStpExtPortEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A list of Rapid Spanning Tree information maintained by + each bridge interface member." + AUGMENTS { begemotBridgeStpPortEntry } + ::= { begemotBridgeStpExtPortTable 1 } + +BegemotBridgeStpExtPortEntry ::= SEQUENCE { + begemotBridgeStpPortProtocolMigration TruthValue, + begemotBridgeStpPortAdminEdgePort TruthValue, + begemotBridgeStpPortOperEdgePort TruthValue, + begemotBridgeStpPortAdminPointToPoint INTEGER, + begemotBridgeStpPortOperPointToPoint TruthValue, + begemotBridgeStpPortAdminPathCost Integer32 +} + +begemotBridgeStpPortProtocolMigration OBJECT-TYPE + SYNTAX TruthValue + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "When operating in RSTP (version 2) mode, writing true(1) + to this object forces this port to transmit RSTP BPDUs. + Any other operation on this object has no effect and + it always returns false(2) when read." + ::= { begemotBridgeStpExtPortEntry 1 } + +begemotBridgeStpPortAdminEdgePort OBJECT-TYPE + SYNTAX TruthValue + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The administrative value of the Edge Port parameter. A + value of true(1) indicates that this port should be + assumed as an edge-port, and a value of false(2) indicates + that this port should be assumed as a non-edge-port. + Setting this object will also cause the corresponding + instance of begemotBridgeStpPortOperEdgePort to change to + the same value. Note that even when this object's value + is true, the value of the corresponding instance of + begemotBridgeStpPortOperEdgePort can be false if a BPDU + has been received. + + The value of this object MUST be retained across + reinitializations of the management system." + ::= { begemotBridgeStpExtPortEntry 2 } + +begemotBridgeStpPortOperEdgePort OBJECT-TYPE + SYNTAX TruthValue + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The operational value of the Edge Port parameter. The + object is initialized to the value of the corresponding + instance of begemotBridgeStpPortAdminEdgePort. When the + corresponding instance of begemotBridgeStpPortAdminEdgePort + is set, this object will be changed as well. This object + will also be changed to false on reception of a BPDU." + ::= { begemotBridgeStpExtPortEntry 3 } + +begemotBridgeStpPortAdminPointToPoint OBJECT-TYPE + SYNTAX INTEGER { + forceTrue(0), + forceFalse(1), + auto(2) + } + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The administrative point-to-point status of the LAN segment + attached to this port, using the enumeration values of the + IEEE 802.1w clause. A value of forceTrue(0) indicates + that this port should always be treated as if it is + connected to a point-to-point link. A value of + forceFalse(1) indicates that this port should be treated as + having a shared media connection. A value of auto(2) + indicates that this port is considered to have a + point-to-point link if it is an Aggregator and all of its + members are aggregatable, or if the MAC entity + is configured for full duplex operation, either through + auto-negotiation or by management means. Manipulating this + object changes the underlying adminPortToPortMAC. + + The value of this object MUST be retained across + reinitializations of the management system." + ::= { begemotBridgeStpExtPortEntry 4 } + +begemotBridgeStpPortOperPointToPoint OBJECT-TYPE + SYNTAX TruthValue + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The operational point-to-point status of the LAN segment + attached to this port. It indicates whether a port is + considered to have a point-to-point connection. + If adminPointToPointMAC is set to auto(2), then the value + of operPointToPointMAC is determined in accordance with the + specific procedures defined for the MAC entity concerned, + as defined in IEEE 802.1w, clause 6.5. The value is + determined dynamically; that is, it is re-evaluated whenever + the value of adminPointToPointMAC changes, and whenever + the specific procedures defined for the MAC entity evaluates + a change in its point-to-point status." + ::= { begemotBridgeStpExtPortEntry 5 } + +begemotBridgeStpPortAdminPathCost OBJECT-TYPE + SYNTAX Integer32 (0..200000000) + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The administratively assigned value for the contribution + of this port to the path cost of paths toward the spanning + tree root. + + Writing a value of '0' assigns the automatically calculated + default Path Cost value to the port. If the default Path + Cost is being used, this object returns '0' when read. + + This complements the object begemotBridgeStpPortPathCost or + begemotBridgeStpPortPathCost32, which returns the operational + value of the path cost. + + The value of this object MUST be retained across + reinitializations of the management system." + ::= { begemotBridgeStpExtPortEntry 6 } + +-- ---------------------------------------------------------- -- +-- the Bridge interface Transparent bridging table +-- ---------------------------------------------------------- -- + +begemotBridgeTpTable OBJECT-TYPE + SYNTAX SEQUENCE OF BegemotBridgeTpEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A table that contains information regarding transparent + bridging for each bridge interface on the managed device." + ::= { begemotBridgeTp 1 } + +begemotBridgeTpEntry OBJECT-TYPE + SYNTAX BegemotBridgeTpEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A list of information regarding transparent bridging + on a bridge interface." + AUGMENTS { begemotBridgeBaseEntry } + ::= { begemotBridgeTpTable 1 } + +BegemotBridgeTpEntry ::= SEQUENCE { + begemotBridgeTpLearnedEntryDiscards Counter32, + begemotBridgeTpAgingTime Integer32, + begemotBridgeTpMaxAddresses Integer32 +} + +begemotBridgeTpLearnedEntryDiscards OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of Forwarding Database entries that would + have been learnt, but have been discarded due to Forwarding + Address Table having reached it's maximum entries limit." + ::= { begemotBridgeTpEntry 1 } + +begemotBridgeTpAgingTime OBJECT-TYPE + SYNTAX Integer32 (10..1000000) + UNITS "seconds" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The timeout period in seconds before aging out + dynamically learnt forwarding entries." + ::= { begemotBridgeTpEntry 2 } + +begemotBridgeTpMaxAddresses OBJECT-TYPE + SYNTAX Integer32 (1..10000) + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The maximum number of entires that this bridge can + learn in it's Forwarding Address Table and use for + making forwarding decisions." + ::= { begemotBridgeTpEntry 3 } + +-- ---------------------------------------------------------- -- +-- The Forwarding Database for Transparent Bridging interfaces +-- ---------------------------------------------------------- -- + +begemotBridgeTpFdbTable OBJECT-TYPE + SYNTAX SEQUENCE OF BegemotBridgeTpFdbEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A table that contains information about unicast entries + for which the bridge interfaces have forwarding and/or + filtering information. This information is used by the + bridge interfaces to make forwarding decisions." + ::= { begemotBridgeTp 2 } + +begemotBridgeTpFdbEntry OBJECT-TYPE + SYNTAX BegemotBridgeTpFdbEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Information about a specific unicast MAC address + for which the bridge interface has some forwarding + and/or filtering information." + INDEX { begemotBridgeBaseName, begemotBridgeTpFdbAddress } + ::= { begemotBridgeTpFdbTable 1 } + +BegemotBridgeTpFdbEntry ::= SEQUENCE { + begemotBridgeTpFdbAddress MacAddress, + begemotBridgeTpFdbPort Integer32, + begemotBridgeTpFdbStatus INTEGER +} + +begemotBridgeTpFdbAddress OBJECT-TYPE + SYNTAX MacAddress + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "A unicast MAC address for which the bridge has which the + bridge interface has some forwarding and/or filtering + information." + ::= { begemotBridgeTpFdbEntry 1 } + +begemotBridgeTpFdbPort OBJECT-TYPE + SYNTAX Integer32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The port number of the bridge port on which a frame having + a source address equal to the value of the corresponding + instance of begemotBridgeTpFdbAddress has been seen." + ::= { begemotBridgeTpFdbEntry 2 } + +begemotBridgeTpFdbStatus OBJECT-TYPE + SYNTAX INTEGER { + other(1), + invalid(2), + learned(3), + self(4), + mgmt(5) + } + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The status of this entry. The meanings of the + values are: + other(1) - none of the following. + invalid(2) - this entry is no longer valid (e.g., + it was learned but has since aged out), but has + not yet been flushed from the table. + learned(3) - the value of the corresponding instance + of begemotBridgeTpFdbPort was learned, and is being + used. + self(4) - the value of the corresponding instance of + begemotBridgeTpFdbAddress represents one of the + bridge's addresses. The corresponding instance of + begemotBridgeTpFdbPort indicates which of the bridge's + ports has this address. + mgmt(5) - the value of the corresponding instance of + begemotBridgeTpFdbAddress has been added to the + bridge's Forwarding Database by some management + means." + ::= { begemotBridgeTpFdbEntry 3 } + +-- ---------------------------------------------------------- -- +-- Ports table for Transparent Bridging interfaces +-- ---------------------------------------------------------- -- + +begemotBridgeTpPortTable OBJECT-TYPE + SYNTAX SEQUENCE OF BegemotBridgeTpPortEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A table that contains information about every bridge port, + member of a bridge interface, associated with the transparent + bridging function of the bridge." + ::= { begemotBridgeTp 3 } + +begemotBridgeTpPortEntry OBJECT-TYPE + SYNTAX BegemotBridgeTpPortEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A list of information about every bridge port, member of a + bridge interface, associated with the bridge's transparent + bridging function." + INDEX { begemotBridgeBaseName, begemotBridgeBasePortIfIndex } + ::= { begemotBridgeTpPortTable 1 } + +BegemotBridgeTpPortEntry ::= SEQUENCE { + begemotBridgeTpPort Integer32, + begemotBridgeTpPortMaxInfo Integer32, + begemotBridgeTpPortInFrames Counter32, + begemotBridgeTpPortOutFrames Counter32, + begemotBridgeTpPortInDiscards Counter32 +} + +begemotBridgeTpPort OBJECT-TYPE + SYNTAX Integer32 (1..65535) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The system interface index of the port for which this entry + contains Transparent bridging management information." + ::= { begemotBridgeTpPortEntry 1 } + +begemotBridgeTpPortMaxInfo OBJECT-TYPE + SYNTAX Integer32 + UNITS "bytes" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The maximum size of the INFO (non-MAC) field that this port + will receive or transmit." + ::= { begemotBridgeTpPortEntry 2 } + +begemotBridgeTpPortInFrames OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of frames that have been received by this + port from its segment. Note that a frame received on the + interface corresponding to this port is only counted by + this object if and only if it is for a protocol being + processed by the local bridging function, including + bridge management frames." + ::= { begemotBridgeTpPortEntry 3 } + +begemotBridgeTpPortOutFrames OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of frames that have been transmitted by this + port to its segment. Note that a frame transmitted on + the interface corresponding to this port is only counted + by this object if and only if it is for a protocol being + processed by the local bridging function, including + bridge management frames." + ::= { begemotBridgeTpPortEntry 4 } + +begemotBridgeTpPortInDiscards OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Count of received valid frames that were discarded + (i.e., filtered) by the Forwarding Process." + ::= { begemotBridgeTpPortEntry 5 } + +-- ---------------------------------------------------------- -- +-- the begemotBridgePf objects +-- ---------------------------------------------------------- -- + +begemotBridgePfilStatus OBJECT-TYPE + SYNTAX TruthValue + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Indicates whether packet filtering by some firewall + package is enabled on the bridge interface." + ::= { begemotBridgePf 1 } + +begemotBridgePfilMembers OBJECT-TYPE + SYNTAX TruthValue + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "A value of true(1) indicates that packet filtering is + enabled on both incoming and outgoing bridge member + interfaces." + ::= { begemotBridgePf 2 } + +begemotBridgePfilIpOnly OBJECT-TYPE + SYNTAX TruthValue + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "This value controls the handling of non-IP packets which + are not passed on for further processing to a firewall + package. A value of false(0) indicates that all non-IP + Ethernet frames are passed unconditionally." + ::= { begemotBridgePf 3 } + +begemotBridgeLayer2PfStatus OBJECT-TYPE + SYNTAX INTEGER { + enabled(1), + disabled(2) + } + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "This value indicates whether layer2 filtering by a + firewall package is enabled for bridge interfaces." + ::= { begemotBridgePf 4 } + +-- ---------------------------------------------------------- -- +-- the begemotBridgeConfigObjects objects +-- ---------------------------------------------------------- -- + +begemotBridgeDefaultBridgeIf OBJECT-TYPE + + SYNTAX BridgeIfNameOrEmpty + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The name of the bridge interface that will be managed + via objects in IETF BRIDGE-MIB (RFC4188). If the + object's value is set to an empty string, bridge interfaces + will only be managed via objects in this MIB module." + DEFVAL { "bridge0" } + ::= { begemotBridgeConfigObjects 1 } + +begemotBridgeDataUpdate OBJECT-TYPE + + SYNTAX Timeout (1..300) + UNITS "seconds" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The maximum age in seconds of the cached data." + DEFVAL { 10 } + ::= { begemotBridgeConfigObjects 2 } + +begemotBridgeDataPoll OBJECT-TYPE + + SYNTAX Timeout (1..3600) + UNITS "seconds" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The polling rate of data when the module is idle." + DEFVAL { 300 } + ::= { begemotBridgeConfigObjects 3 } + +-- ---------------------------------------------------------- -- +-- Notifications for the Spanning Tree Protocol +-- ---------------------------------------------------------- -- + +begemotBridgeNewRoot NOTIFICATION-TYPE + OBJECTS { begemotBridgeBaseName } + STATUS current + DESCRIPTION + "The begemotBridgeNewRoot trap indicates that one of the + bridge interfaces on the sending agent's device has + become the new root of the spanning tree topology it is + participating in." + ::= { begemotBridgeNotifications 1 } + +begemotBridgeTopologyChange NOTIFICATION-TYPE + OBJECTS { begemotBridgeBaseName } + STATUS current + DESCRIPTION + "A begemotBridgeTopologyChange trap is send when a member + port on one of the bridge interfaces, monitored by the agent, + transitions from the Learning state to the Forwarding state, + or from the Forwarding state to the Blocking state. The trap + is not sent if a begemotBridgeNewRoot trap is sent for the + same transition." + ::= { begemotBridgeNotifications 2 } + +END diff --git a/usr.sbin/bsnmpd/modules/snmp_bridge/BRIDGE-MIB.txt b/usr.sbin/bsnmpd/modules/snmp_bridge/BRIDGE-MIB.txt new file mode 100644 index 0000000..9f87b65 --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_bridge/BRIDGE-MIB.txt @@ -0,0 +1,1483 @@ +-- +-- Copyright (C) The Internet Society (2005). +-- +-- This document is subject to the rights, licenses and restrictions +-- contained in BCP 78, and except as set forth therein, the authors +-- retain all their rights. +-- +-- This document and the information contained herein are provided on an +-- "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS +-- OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET +-- ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, +-- INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE +-- INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED +-- WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +-- +-- $FreeBSD$ +-- + +BRIDGE-MIB DEFINITIONS ::= BEGIN + +-- ---------------------------------------------------------- -- +-- MIB for IEEE 802.1D devices +-- ---------------------------------------------------------- -- +IMPORTS + MODULE-IDENTITY, OBJECT-TYPE, NOTIFICATION-TYPE, + Counter32, Integer32, TimeTicks, mib-2 + FROM SNMPv2-SMI + TEXTUAL-CONVENTION, MacAddress + FROM SNMPv2-TC + MODULE-COMPLIANCE, OBJECT-GROUP, NOTIFICATION-GROUP + FROM SNMPv2-CONF + InterfaceIndex FROM IF-MIB + ; + +dot1dBridge MODULE-IDENTITY + LAST-UPDATED "200509190000Z" + ORGANIZATION "IETF Bridge MIB Working Group" + CONTACT-INFO + "Email: bridge-mib@ietf.org + + K.C. Norseth (Editor) + L-3 Communications + Tel: +1 801-594-2809 + Email: kenyon.c.norseth@L-3com.com + Postal: 640 N. 2200 West. + Salt Lake City, Utah 84116-0850 + Les Bell (Editor) + 3Com Europe Limited + Phone: +44 1442 438025 + Email: elbell@ntlworld.com + Postal: 3Com Centre, Boundary Way + Hemel Hempstead + Herts. HP2 7YU + UK + + Send comments to <bridge-mib@ietf.org>" + DESCRIPTION + "The Bridge MIB module for managing devices that support + IEEE 802.1D. + + Copyright (C) The Internet Society (2005). This version of + this MIB module is part of RFC 4188; see the RFC itself for + full legal notices." + REVISION "200509190000Z" + DESCRIPTION + "Third revision, published as part of RFC 4188. + + The MIB module has been converted to SMIv2 format. + Conformance statements have been added and some + description and reference clauses have been updated. + + The object dot1dStpPortPathCost32 was added to + support IEEE 802.1t and the permissible values of + dot1dStpPriority and dot1dStpPortPriority have been + clarified for bridges supporting IEEE 802.1t or + IEEE 802.1w. + + The interpretation of dot1dStpTimeSinceTopologyChange + has been clarified for bridges supporting the Rapid + Spanning Tree Protocol (RSTP)." + REVISION "199307310000Z" + DESCRIPTION + "Second revision, published as part of RFC 1493." + REVISION "199112310000Z" + DESCRIPTION + "Initial revision, published as part of RFC 1286." + ::= { mib-2 17 } + + +-- ---------------------------------------------------------- -- +-- Textual Conventions +-- ---------------------------------------------------------- -- + +BridgeId ::= TEXTUAL-CONVENTION + STATUS current + DESCRIPTION + "The Bridge-Identifier, as used in the Spanning Tree + Protocol, to uniquely identify a bridge. Its first two + octets (in network byte order) contain a priority value, + and its last 6 octets contain the MAC address used to + refer to a bridge in a unique fashion (typically, the + numerically smallest MAC address of all ports on the + bridge)." + SYNTAX OCTET STRING (SIZE (8)) + +Timeout ::= TEXTUAL-CONVENTION + DISPLAY-HINT "d" + STATUS current + DESCRIPTION + "A Spanning Tree Protocol (STP) timer in units of 1/100 + seconds. Several objects in this MIB module represent + values of timers used by the Spanning Tree Protocol. + In this MIB, these timers have values in units of + hundredths of a second (i.e., 1/100 secs). + + These timers, when stored in a Spanning Tree Protocol's + BPDU, are in units of 1/256 seconds. Note, however, that + 802.1D-1998 specifies a settable granularity of no more + than one second for these timers. To avoid ambiguity, + a conversion algorithm is defined below for converting + between the different units, which ensures a timer's + value is not distorted by multiple conversions. + + To convert a Timeout value into a value in units of + 1/256 seconds, the following algorithm should be used: + + b = floor( (n * 256) / 100) + + where: + floor = quotient [ignore remainder] + n is the value in 1/100 second units + b is the value in 1/256 second units + + To convert the value from 1/256 second units back to + 1/100 seconds, the following algorithm should be used: + + n = ceiling( (b * 100) / 256) + + where: + ceiling = quotient [if remainder is 0], or + quotient + 1 [if remainder is nonzero] + n is the value in 1/100 second units + b is the value in 1/256 second units + + Note: it is important that the arithmetic operations are + done in the order specified (i.e., multiply first, + divide second)." + SYNTAX Integer32 + +-- ---------------------------------------------------------- -- +-- subtrees in the Bridge MIB +-- ---------------------------------------------------------- -- + +dot1dNotifications OBJECT IDENTIFIER ::= { dot1dBridge 0 } + +dot1dBase OBJECT IDENTIFIER ::= { dot1dBridge 1 } +dot1dStp OBJECT IDENTIFIER ::= { dot1dBridge 2 } + +dot1dSr OBJECT IDENTIFIER ::= { dot1dBridge 3 } +-- documented in RFC 1525 + +dot1dTp OBJECT IDENTIFIER ::= { dot1dBridge 4 } +dot1dStatic OBJECT IDENTIFIER ::= { dot1dBridge 5 } + +-- Subtrees used by Bridge MIB Extensions: +-- pBridgeMIB MODULE-IDENTITY ::= { dot1dBridge 6 } +-- qBridgeMIB MODULE-IDENTITY ::= { dot1dBridge 7 } +-- Note that the practice of registering related MIB modules +-- below dot1dBridge has been discouraged since there is no +-- robust mechanism to track such registrations. + +dot1dConformance OBJECT IDENTIFIER ::= { dot1dBridge 8 } + +-- ---------------------------------------------------------- -- +-- the dot1dBase subtree +-- ---------------------------------------------------------- -- +-- Implementation of the dot1dBase subtree is mandatory for all +-- bridges. +-- ---------------------------------------------------------- -- + +dot1dBaseBridgeAddress OBJECT-TYPE + + SYNTAX MacAddress + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The MAC address used by this bridge when it must be + referred to in a unique fashion. It is recommended + that this be the numerically smallest MAC address of + all ports that belong to this bridge. However, it is only + required to be unique. When concatenated with + dot1dStpPriority, a unique BridgeIdentifier is formed, + which is used in the Spanning Tree Protocol." + REFERENCE + "IEEE 802.1D-1998: clauses 14.4.1.1.3 and 7.12.5" + ::= { dot1dBase 1 } + +dot1dBaseNumPorts OBJECT-TYPE + SYNTAX Integer32 + UNITS "ports" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of ports controlled by this bridging + entity." + REFERENCE + "IEEE 802.1D-1998: clause 14.4.1.1.3" + ::= { dot1dBase 2 } + +dot1dBaseType OBJECT-TYPE + SYNTAX INTEGER { + unknown(1), + transparent-only(2), + sourceroute-only(3), + srt(4) + } + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Indicates what type of bridging this bridge can + perform. If a bridge is actually performing a + certain type of bridging, this will be indicated by + entries in the port table for the given type." + ::= { dot1dBase 3 } + +-- ---------------------------------------------------------- -- +-- The Generic Bridge Port Table +-- ---------------------------------------------------------- -- +dot1dBasePortTable OBJECT-TYPE + SYNTAX SEQUENCE OF Dot1dBasePortEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A table that contains generic information about every + port that is associated with this bridge. Transparent, + source-route, and srt ports are included." + ::= { dot1dBase 4 } + +dot1dBasePortEntry OBJECT-TYPE + SYNTAX Dot1dBasePortEntry + MAX-ACCESS not-accessible + STATUS current + + DESCRIPTION + "A list of information for each port of the bridge." + REFERENCE + "IEEE 802.1D-1998: clause 14.4.2, 14.6.1" + INDEX { dot1dBasePort } + ::= { dot1dBasePortTable 1 } + +Dot1dBasePortEntry ::= + SEQUENCE { + dot1dBasePort + Integer32, + dot1dBasePortIfIndex + InterfaceIndex, + dot1dBasePortCircuit + OBJECT IDENTIFIER, + dot1dBasePortDelayExceededDiscards + Counter32, + dot1dBasePortMtuExceededDiscards + Counter32 + } + +dot1dBasePort OBJECT-TYPE + SYNTAX Integer32 (1..65535) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The port number of the port for which this entry + contains bridge management information." + ::= { dot1dBasePortEntry 1 } + +dot1dBasePortIfIndex OBJECT-TYPE + SYNTAX InterfaceIndex + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The value of the instance of the ifIndex object, + defined in IF-MIB, for the interface corresponding + to this port." + ::= { dot1dBasePortEntry 2 } + +dot1dBasePortCircuit OBJECT-TYPE + SYNTAX OBJECT IDENTIFIER + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "For a port that (potentially) has the same value of + dot1dBasePortIfIndex as another port on the same bridge. + This object contains the name of an object instance + unique to this port. For example, in the case where + multiple ports correspond one-to-one with multiple X.25 + virtual circuits, this value might identify an (e.g., + the first) object instance associated with the X.25 + virtual circuit corresponding to this port. + + For a port which has a unique value of + dot1dBasePortIfIndex, this object can have the value + { 0 0 }." + ::= { dot1dBasePortEntry 3 } + +dot1dBasePortDelayExceededDiscards OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of frames discarded by this port due + to excessive transit delay through the bridge. It + is incremented by both transparent and source + route bridges." + REFERENCE + "IEEE 802.1D-1998: clause 14.6.1.1.3" + ::= { dot1dBasePortEntry 4 } + +dot1dBasePortMtuExceededDiscards OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of frames discarded by this port due + to an excessive size. It is incremented by both + transparent and source route bridges." + REFERENCE + "IEEE 802.1D-1998: clause 14.6.1.1.3" + ::= { dot1dBasePortEntry 5 } + +-- ---------------------------------------------------------- -- +-- the dot1dStp subtree +-- ---------------------------------------------------------- -- +-- Implementation of the dot1dStp subtree is optional. It is +-- implemented by those bridges that support the Spanning Tree +-- Protocol. +-- ---------------------------------------------------------- -- +dot1dStpProtocolSpecification OBJECT-TYPE + SYNTAX INTEGER { + unknown(1), + decLb100(2), + ieee8021d(3) + } + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "An indication of what version of the Spanning Tree + Protocol is being run. The value 'decLb100(2)' + indicates the DEC LANbridge 100 Spanning Tree protocol. + IEEE 802.1D implementations will return 'ieee8021d(3)'. + If future versions of the IEEE Spanning Tree Protocol + that are incompatible with the current version + are released a new value will be defined." + ::= { dot1dStp 1 } + +dot1dStpPriority OBJECT-TYPE + SYNTAX Integer32 (0..65535) + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The value of the write-able portion of the Bridge ID + (i.e., the first two octets of the (8 octet long) Bridge + ID). The other (last) 6 octets of the Bridge ID are + given by the value of dot1dBaseBridgeAddress. + On bridges supporting IEEE 802.1t or IEEE 802.1w, + permissible values are 0-61440, in steps of 4096." + REFERENCE + "IEEE 802.1D-1998 clause 8.10.2, Table 8-4, + IEEE 802.1t clause 8.10.2, Table 8-4, clause 14.3." + ::= { dot1dStp 2 } + +dot1dStpTimeSinceTopologyChange OBJECT-TYPE + SYNTAX TimeTicks + UNITS "centi-seconds" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The time (in hundredths of a second) since the + last time a topology change was detected by the + bridge entity. + For RSTP, this reports the time since the tcWhile + timer for any port on this Bridge was nonzero." + REFERENCE + "IEEE 802.1D-1998 clause 14.8.1.1., + IEEE 802.1w clause 14.8.1.1." + ::= { dot1dStp 3 } + +dot1dStpTopChanges OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of topology changes detected by + this bridge since the management entity was last + reset or initialized." + REFERENCE + "IEEE 802.1D-1998 clause 14.8.1.1." + ::= { dot1dStp 4 } + +dot1dStpDesignatedRoot OBJECT-TYPE + SYNTAX BridgeId + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The bridge identifier of the root of the spanning + tree, as determined by the Spanning Tree Protocol, + as executed by this node. This value is used as + the Root Identifier parameter in all Configuration + Bridge PDUs originated by this node." + REFERENCE + "IEEE 802.1D-1998: clause 8.5.3.1" + ::= { dot1dStp 5 } + +dot1dStpRootCost OBJECT-TYPE + SYNTAX Integer32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The cost of the path to the root as seen from + this bridge." + REFERENCE + "IEEE 802.1D-1998: clause 8.5.3.2" + ::= { dot1dStp 6 } + +dot1dStpRootPort OBJECT-TYPE + SYNTAX Integer32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The port number of the port that offers the lowest + cost path from this bridge to the root bridge." + REFERENCE + "IEEE 802.1D-1998: clause 8.5.3.3" + ::= { dot1dStp 7 } + +dot1dStpMaxAge OBJECT-TYPE + SYNTAX Timeout + UNITS "centi-seconds" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The maximum age of Spanning Tree Protocol information + learned from the network on any port before it is + discarded, in units of hundredths of a second. This is + the actual value that this bridge is currently using." + REFERENCE + "IEEE 802.1D-1998: clause 8.5.3.4" + ::= { dot1dStp 8 } + +dot1dStpHelloTime OBJECT-TYPE + SYNTAX Timeout + UNITS "centi-seconds" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The amount of time between the transmission of + Configuration bridge PDUs by this node on any port when + it is the root of the spanning tree, or trying to become + so, in units of hundredths of a second. This is the + actual value that this bridge is currently using." + REFERENCE + "IEEE 802.1D-1998: clause 8.5.3.5" + ::= { dot1dStp 9 } + +dot1dStpHoldTime OBJECT-TYPE + SYNTAX Integer32 + UNITS "centi-seconds" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "This time value determines the interval length + during which no more than two Configuration bridge + PDUs shall be transmitted by this node, in units + of hundredths of a second." + REFERENCE + "IEEE 802.1D-1998: clause 8.5.3.14" + ::= { dot1dStp 10 } + +dot1dStpForwardDelay OBJECT-TYPE + SYNTAX Timeout + UNITS "centi-seconds" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "This time value, measured in units of hundredths of a + second, controls how fast a port changes its spanning + state when moving towards the Forwarding state. The + value determines how long the port stays in each of the + Listening and Learning states, which precede the + Forwarding state. This value is also used when a + topology change has been detected and is underway, to + age all dynamic entries in the Forwarding Database. + [Note that this value is the one that this bridge is + currently using, in contrast to + dot1dStpBridgeForwardDelay, which is the value that this + bridge and all others would start using if/when this + bridge were to become the root.]" + REFERENCE + "IEEE 802.1D-1998: clause 8.5.3.6" + ::= { dot1dStp 11 } + +dot1dStpBridgeMaxAge OBJECT-TYPE + SYNTAX Timeout (600..4000) + UNITS "centi-seconds" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The value that all bridges use for MaxAge when this + bridge is acting as the root. Note that 802.1D-1998 + specifies that the range for this parameter is related + to the value of dot1dStpBridgeHelloTime. The + granularity of this timer is specified by 802.1D-1998 to + be 1 second. An agent may return a badValue error if a + set is attempted to a value that is not a whole number + of seconds." + REFERENCE + "IEEE 802.1D-1998: clause 8.5.3.8" + ::= { dot1dStp 12 } + +dot1dStpBridgeHelloTime OBJECT-TYPE + SYNTAX Timeout (100..1000) + UNITS "centi-seconds" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The value that all bridges use for HelloTime when this + bridge is acting as the root. The granularity of this + timer is specified by 802.1D-1998 to be 1 second. An + agent may return a badValue error if a set is attempted + to a value that is not a whole number of seconds." + REFERENCE + "IEEE 802.1D-1998: clause 8.5.3.9" + ::= { dot1dStp 13 } + +dot1dStpBridgeForwardDelay OBJECT-TYPE + SYNTAX Timeout (400..3000) + UNITS "centi-seconds" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The value that all bridges use for ForwardDelay when + this bridge is acting as the root. Note that + 802.1D-1998 specifies that the range for this parameter + is related to the value of dot1dStpBridgeMaxAge. The + granularity of this timer is specified by 802.1D-1998 to + be 1 second. An agent may return a badValue error if a + set is attempted to a value that is not a whole number + of seconds." + REFERENCE + "IEEE 802.1D-1998: clause 8.5.3.10" + ::= { dot1dStp 14 } + +-- ---------------------------------------------------------- -- +-- The Spanning Tree Port Table +-- ---------------------------------------------------------- -- + +dot1dStpPortTable OBJECT-TYPE + SYNTAX SEQUENCE OF Dot1dStpPortEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A table that contains port-specific information + for the Spanning Tree Protocol." + ::= { dot1dStp 15 } + +dot1dStpPortEntry OBJECT-TYPE + SYNTAX Dot1dStpPortEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A list of information maintained by every port about + the Spanning Tree Protocol state for that port." + INDEX { dot1dStpPort } + ::= { dot1dStpPortTable 1 } + +Dot1dStpPortEntry ::= + SEQUENCE { + dot1dStpPort + Integer32, + dot1dStpPortPriority + Integer32, + dot1dStpPortState + INTEGER, + dot1dStpPortEnable + INTEGER, + dot1dStpPortPathCost + Integer32, + dot1dStpPortDesignatedRoot + BridgeId, + dot1dStpPortDesignatedCost + Integer32, + dot1dStpPortDesignatedBridge + BridgeId, + dot1dStpPortDesignatedPort + OCTET STRING, + dot1dStpPortForwardTransitions + Counter32, + dot1dStpPortPathCost32 + Integer32 + } + +dot1dStpPort OBJECT-TYPE + SYNTAX Integer32 (1..65535) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The port number of the port for which this entry + contains Spanning Tree Protocol management information." + REFERENCE + "IEEE 802.1D-1998: clause 14.8.2.1.2" + ::= { dot1dStpPortEntry 1 } + +dot1dStpPortPriority OBJECT-TYPE + SYNTAX Integer32 (0..255) + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The value of the priority field that is contained in + the first (in network byte order) octet of the (2 octet + long) Port ID. The other octet of the Port ID is given + by the value of dot1dStpPort. + On bridges supporting IEEE 802.1t or IEEE 802.1w, + permissible values are 0-240, in steps of 16." + REFERENCE + "IEEE 802.1D-1998 clause 8.10.2, Table 8-4, + IEEE 802.1t clause 8.10.2, Table 8-4, clause 14.3." + ::= { dot1dStpPortEntry 2 } + +dot1dStpPortState OBJECT-TYPE + SYNTAX INTEGER { + disabled(1), + blocking(2), + listening(3), + learning(4), + forwarding(5), + broken(6) + } + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The port's current state, as defined by application of + the Spanning Tree Protocol. This state controls what + action a port takes on reception of a frame. If the + bridge has detected a port that is malfunctioning, it + will place that port into the broken(6) state. For + ports that are disabled (see dot1dStpPortEnable), this + object will have a value of disabled(1)." + REFERENCE + "IEEE 802.1D-1998: clause 8.5.5.2" + ::= { dot1dStpPortEntry 3 } + +dot1dStpPortEnable OBJECT-TYPE + SYNTAX INTEGER { + enabled(1), + disabled(2) + } + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The enabled/disabled status of the port." + REFERENCE + "IEEE 802.1D-1998: clause 8.5.5.2" + ::= { dot1dStpPortEntry 4 } + +dot1dStpPortPathCost OBJECT-TYPE + SYNTAX Integer32 (1..65535) + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The contribution of this port to the path cost of + paths towards the spanning tree root which include + this port. 802.1D-1998 recommends that the default + value of this parameter be in inverse proportion to + the speed of the attached LAN. + + New implementations should support dot1dStpPortPathCost32. + If the port path costs exceeds the maximum value of this + object then this object should report the maximum value, + namely 65535. Applications should try to read the + dot1dStpPortPathCost32 object if this object reports + the maximum value." + REFERENCE "IEEE 802.1D-1998: clause 8.5.5.3" + ::= { dot1dStpPortEntry 5 } + +dot1dStpPortDesignatedRoot OBJECT-TYPE + SYNTAX BridgeId + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The unique Bridge Identifier of the Bridge + recorded as the Root in the Configuration BPDUs + transmitted by the Designated Bridge for the + segment to which the port is attached." + REFERENCE + "IEEE 802.1D-1998: clause 8.5.5.4" + ::= { dot1dStpPortEntry 6 } + +dot1dStpPortDesignatedCost OBJECT-TYPE + SYNTAX Integer32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The path cost of the Designated Port of the segment + connected to this port. This value is compared to the + Root Path Cost field in received bridge PDUs." + REFERENCE + "IEEE 802.1D-1998: clause 8.5.5.5" + ::= { dot1dStpPortEntry 7 } + +dot1dStpPortDesignatedBridge OBJECT-TYPE + SYNTAX BridgeId + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The Bridge Identifier of the bridge that this + port considers to be the Designated Bridge for + this port's segment." + REFERENCE + "IEEE 802.1D-1998: clause 8.5.5.6" + ::= { dot1dStpPortEntry 8 } + +dot1dStpPortDesignatedPort OBJECT-TYPE + SYNTAX OCTET STRING (SIZE (2)) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The Port Identifier of the port on the Designated + Bridge for this port's segment." + REFERENCE + "IEEE 802.1D-1998: clause 8.5.5.7" + ::= { dot1dStpPortEntry 9 } + +dot1dStpPortForwardTransitions OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of times this port has transitioned + from the Learning state to the Forwarding state." + ::= { dot1dStpPortEntry 10 } + +dot1dStpPortPathCost32 OBJECT-TYPE + SYNTAX Integer32 (1..200000000) + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The contribution of this port to the path cost of + paths towards the spanning tree root which include + this port. 802.1D-1998 recommends that the default + value of this parameter be in inverse proportion to + the speed of the attached LAN. + + This object replaces dot1dStpPortPathCost to support + IEEE 802.1t." + REFERENCE + "IEEE 802.1t clause 8.10.2, Table 8-5." + ::= { dot1dStpPortEntry 11 } + +-- ---------------------------------------------------------- -- +-- the dot1dTp subtree +-- ---------------------------------------------------------- -- +-- Implementation of the dot1dTp subtree is optional. It is +-- implemented by those bridges that support the transparent +-- bridging mode. A transparent or SRT bridge will implement +-- this subtree. +-- ---------------------------------------------------------- -- + +dot1dTpLearnedEntryDiscards OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of Forwarding Database entries that + have been or would have been learned, but have been + discarded due to a lack of storage space in the + Forwarding Database. If this counter is increasing, it + indicates that the Forwarding Database is regularly + becoming full (a condition that has unpleasant + performance effects on the subnetwork). If this counter + has a significant value but is not presently increasing, + it indicates that the problem has been occurring but is + not persistent." + REFERENCE + "IEEE 802.1D-1998: clause 14.7.1.1.3" + ::= { dot1dTp 1 } + +dot1dTpAgingTime OBJECT-TYPE + SYNTAX Integer32 (10..1000000) + UNITS "seconds" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The timeout period in seconds for aging out + dynamically-learned forwarding information. + 802.1D-1998 recommends a default of 300 seconds." + REFERENCE + "IEEE 802.1D-1998: clause 14.7.1.1.3" + ::= { dot1dTp 2 } + + +-- ---------------------------------------------------------- -- +-- The Forwarding Database for Transparent Bridges +-- ---------------------------------------------------------- -- + +dot1dTpFdbTable OBJECT-TYPE + SYNTAX SEQUENCE OF Dot1dTpFdbEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A table that contains information about unicast + entries for which the bridge has forwarding and/or + filtering information. This information is used + by the transparent bridging function in + determining how to propagate a received frame." + ::= { dot1dTp 3 } + +dot1dTpFdbEntry OBJECT-TYPE + SYNTAX Dot1dTpFdbEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Information about a specific unicast MAC address + for which the bridge has some forwarding and/or + filtering information." + INDEX { dot1dTpFdbAddress } + ::= { dot1dTpFdbTable 1 } + +Dot1dTpFdbEntry ::= + SEQUENCE { + dot1dTpFdbAddress + MacAddress, + dot1dTpFdbPort + Integer32, + dot1dTpFdbStatus + INTEGER + } + +dot1dTpFdbAddress OBJECT-TYPE + SYNTAX MacAddress + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "A unicast MAC address for which the bridge has + forwarding and/or filtering information." + REFERENCE + "IEEE 802.1D-1998: clause 7.9.1, 7.9.2" + ::= { dot1dTpFdbEntry 1 } + +dot1dTpFdbPort OBJECT-TYPE + SYNTAX Integer32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Either the value '0', or the port number of the port on + which a frame having a source address equal to the value + of the corresponding instance of dot1dTpFdbAddress has + been seen. A value of '0' indicates that the port + number has not been learned, but that the bridge does + have some forwarding/filtering information about this + address (e.g., in the dot1dStaticTable). Implementors + are encouraged to assign the port value to this object + whenever it is learned, even for addresses for which the + corresponding value of dot1dTpFdbStatus is not + learned(3)." + ::= { dot1dTpFdbEntry 2 } +dot1dTpFdbStatus OBJECT-TYPE + SYNTAX INTEGER { + other(1), + invalid(2), + learned(3), + self(4), + mgmt(5) + } + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The status of this entry. The meanings of the + values are: + other(1) - none of the following. This would + include the case where some other MIB object + (not the corresponding instance of + dot1dTpFdbPort, nor an entry in the + dot1dStaticTable) is being used to determine if + and how frames addressed to the value of the + corresponding instance of dot1dTpFdbAddress are + being forwarded. + invalid(2) - this entry is no longer valid (e.g., + it was learned but has since aged out), but has + not yet been flushed from the table. + learned(3) - the value of the corresponding instance + of dot1dTpFdbPort was learned, and is being + used. + self(4) - the value of the corresponding instance of + dot1dTpFdbAddress represents one of the bridge's + addresses. The corresponding instance of + dot1dTpFdbPort indicates which of the bridge's + ports has this address. + mgmt(5) - the value of the corresponding instance of + dot1dTpFdbAddress is also the value of an + existing instance of dot1dStaticAddress." + ::= { dot1dTpFdbEntry 3 } + +-- ---------------------------------------------------------- -- +-- Port Table for Transparent Bridges +-- ---------------------------------------------------------- -- + +dot1dTpPortTable OBJECT-TYPE + SYNTAX SEQUENCE OF Dot1dTpPortEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A table that contains information about every port that + is associated with this transparent bridge." + ::= { dot1dTp 4 } + +dot1dTpPortEntry OBJECT-TYPE + SYNTAX Dot1dTpPortEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A list of information for each port of a transparent + bridge." + INDEX { dot1dTpPort } + ::= { dot1dTpPortTable 1 } + +Dot1dTpPortEntry ::= + SEQUENCE { + dot1dTpPort + Integer32, + dot1dTpPortMaxInfo + Integer32, + dot1dTpPortInFrames + Counter32, + dot1dTpPortOutFrames + Counter32, + dot1dTpPortInDiscards + Counter32 + } + +dot1dTpPort OBJECT-TYPE + SYNTAX Integer32 (1..65535) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The port number of the port for which this entry + contains Transparent bridging management information." + ::= { dot1dTpPortEntry 1 } + +-- It would be nice if we could use ifMtu as the size of the +-- largest INFO field, but we can't because ifMtu is defined +-- to be the size that the (inter-)network layer can use, which +-- can differ from the MAC layer (especially if several layers +-- of encapsulation are used). + +dot1dTpPortMaxInfo OBJECT-TYPE + SYNTAX Integer32 + UNITS "bytes" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The maximum size of the INFO (non-MAC) field that + this port will receive or transmit." + ::= { dot1dTpPortEntry 2 } + +dot1dTpPortInFrames OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of frames that have been received by this + port from its segment. Note that a frame received on the + interface corresponding to this port is only counted by + this object if and only if it is for a protocol being + processed by the local bridging function, including + bridge management frames." + REFERENCE + "IEEE 802.1D-1998: clause 14.6.1.1.3" + ::= { dot1dTpPortEntry 3 } + +dot1dTpPortOutFrames OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of frames that have been transmitted by this + port to its segment. Note that a frame transmitted on + the interface corresponding to this port is only counted + by this object if and only if it is for a protocol being + processed by the local bridging function, including + bridge management frames." + REFERENCE + "IEEE 802.1D-1998: clause 14.6.1.1.3" + ::= { dot1dTpPortEntry 4 } + +dot1dTpPortInDiscards OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Count of received valid frames that were discarded + (i.e., filtered) by the Forwarding Process." + REFERENCE + "IEEE 802.1D-1998: clause 14.6.1.1.3" + ::= { dot1dTpPortEntry 5 } + +-- ---------------------------------------------------------- -- +-- The Static (Destination-Address Filtering) Database +-- ---------------------------------------------------------- -- +-- Implementation of this subtree is optional. +-- ---------------------------------------------------------- -- + +dot1dStaticTable OBJECT-TYPE + SYNTAX SEQUENCE OF Dot1dStaticEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A table containing filtering information configured + into the bridge by (local or network) management + specifying the set of ports to which frames received + from specific ports and containing specific destination + addresses are allowed to be forwarded. The value of + zero in this table, as the port number from which frames + with a specific destination address are received, is + used to specify all ports for which there is no specific + entry in this table for that particular destination + address. Entries are valid for unicast and for + group/broadcast addresses." + REFERENCE + "IEEE 802.1D-1998: clause 14.7.2" + ::= { dot1dStatic 1 } + +dot1dStaticEntry OBJECT-TYPE + SYNTAX Dot1dStaticEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Filtering information configured into the bridge by + (local or network) management specifying the set of + ports to which frames received from a specific port and + containing a specific destination address are allowed to + be forwarded." + REFERENCE + "IEEE 802.1D-1998: clause 14.7.2" + INDEX { dot1dStaticAddress, dot1dStaticReceivePort } + ::= { dot1dStaticTable 1 } + +Dot1dStaticEntry ::= + SEQUENCE { + dot1dStaticAddress MacAddress, + dot1dStaticReceivePort Integer32, + dot1dStaticAllowedToGoTo OCTET STRING, + dot1dStaticStatus INTEGER + } + +dot1dStaticAddress OBJECT-TYPE + SYNTAX MacAddress + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The destination MAC address in a frame to which this + entry's filtering information applies. This object can + take the value of a unicast address, a group address, or + the broadcast address." + REFERENCE + "IEEE 802.1D-1998: clause 7.9.1, 7.9.2" + ::= { dot1dStaticEntry 1 } + +dot1dStaticReceivePort OBJECT-TYPE + SYNTAX Integer32 (0..65535) + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "Either the value '0', or the port number of the port + from which a frame must be received in order for this + entry's filtering information to apply. A value of zero + indicates that this entry applies on all ports of the + bridge for which there is no other applicable entry." + ::= { dot1dStaticEntry 2 } + +dot1dStaticAllowedToGoTo OBJECT-TYPE + SYNTAX OCTET STRING (SIZE (0..512)) + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The set of ports to which frames received from a + specific port and destined for a specific MAC address, + are allowed to be forwarded. Each octet within the + value of this object specifies a set of eight ports, + with the first octet specifying ports 1 through 8, the + second octet specifying ports 9 through 16, etc. Within + each octet, the most significant bit represents the + lowest numbered port, and the least significant bit + represents the highest numbered port. Thus, each port + of the bridge is represented by a single bit within the + value of this object. If that bit has a value of '1', + then that port is included in the set of ports; the port + is not included if its bit has a value of '0'. (Note + that the setting of the bit corresponding to the port + from which a frame is received is irrelevant.) The + default value of this object is a string of ones of + appropriate length. + + The value of this object may exceed the required minimum + maximum message size of some SNMP transport (484 bytes, + in the case of SNMP over UDP, see RFC 3417, section 3.2). + SNMP engines on bridges supporting a large number of + ports must support appropriate maximum message sizes." + ::= { dot1dStaticEntry 3 } + +dot1dStaticStatus OBJECT-TYPE + SYNTAX INTEGER { + other(1), + invalid(2), + permanent(3), + deleteOnReset(4), + deleteOnTimeout(5) + } + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "This object indicates the status of this entry. + The default value is permanent(3). + other(1) - this entry is currently in use but the + conditions under which it will remain so are + different from each of the following values. + invalid(2) - writing this value to the object + removes the corresponding entry. + permanent(3) - this entry is currently in use and + will remain so after the next reset of the + bridge. + deleteOnReset(4) - this entry is currently in use + and will remain so until the next reset of the + bridge. + deleteOnTimeout(5) - this entry is currently in use + and will remain so until it is aged out." + ::= { dot1dStaticEntry 4 } + +-- ---------------------------------------------------------- -- +-- Notifications for use by Bridges +-- ---------------------------------------------------------- -- +-- Notifications for the Spanning Tree Protocol +-- ---------------------------------------------------------- -- + +newRoot NOTIFICATION-TYPE + -- OBJECTS { } + STATUS current + DESCRIPTION + "The newRoot trap indicates that the sending agent has + become the new root of the Spanning Tree; the trap is + sent by a bridge soon after its election as the new + root, e.g., upon expiration of the Topology Change Timer, + immediately subsequent to its election. Implementation + of this trap is optional." + ::= { dot1dNotifications 1 } + +topologyChange NOTIFICATION-TYPE + -- OBJECTS { } + STATUS current + DESCRIPTION + "A topologyChange trap is sent by a bridge when any of + its configured ports transitions from the Learning state + to the Forwarding state, or from the Forwarding state to + the Blocking state. The trap is not sent if a newRoot + trap is sent for the same transition. Implementation of + this trap is optional." + ::= { dot1dNotifications 2 } + +-- ---------------------------------------------------------- -- +-- IEEE 802.1D MIB - Conformance Information +-- ---------------------------------------------------------- -- + +dot1dGroups OBJECT IDENTIFIER ::= { dot1dConformance 1 } +dot1dCompliances OBJECT IDENTIFIER ::= { dot1dConformance 2 } + +-- ---------------------------------------------------------- -- +-- units of conformance +-- ---------------------------------------------------------- -- + +-- ---------------------------------------------------------- -- +-- the dot1dBase group +-- ---------------------------------------------------------- -- + +dot1dBaseBridgeGroup OBJECT-GROUP + OBJECTS { + dot1dBaseBridgeAddress, + dot1dBaseNumPorts, + dot1dBaseType + } + STATUS current + DESCRIPTION + "Bridge level information for this device." + ::= { dot1dGroups 1 } + +dot1dBasePortGroup OBJECT-GROUP + OBJECTS { + dot1dBasePort, + dot1dBasePortIfIndex, + dot1dBasePortCircuit, + dot1dBasePortDelayExceededDiscards, + dot1dBasePortMtuExceededDiscards + } + STATUS current + DESCRIPTION + "Information for each port on this device." + ::= { dot1dGroups 2 } + +-- ---------------------------------------------------------- -- +-- the dot1dStp group +-- ---------------------------------------------------------- -- + +dot1dStpBridgeGroup OBJECT-GROUP + OBJECTS { + dot1dStpProtocolSpecification, + dot1dStpPriority, + dot1dStpTimeSinceTopologyChange, + dot1dStpTopChanges, + dot1dStpDesignatedRoot, + dot1dStpRootCost, + dot1dStpRootPort, + dot1dStpMaxAge, + dot1dStpHelloTime, + dot1dStpHoldTime, + dot1dStpForwardDelay, + dot1dStpBridgeMaxAge, + dot1dStpBridgeHelloTime, + dot1dStpBridgeForwardDelay + } + STATUS current + DESCRIPTION + "Bridge level Spanning Tree data for this device." + ::= { dot1dGroups 3 } + +dot1dStpPortGroup OBJECT-GROUP + OBJECTS { + dot1dStpPort, + dot1dStpPortPriority, + dot1dStpPortState, + dot1dStpPortEnable, + dot1dStpPortPathCost, + dot1dStpPortDesignatedRoot, + dot1dStpPortDesignatedCost, + dot1dStpPortDesignatedBridge, + dot1dStpPortDesignatedPort, + dot1dStpPortForwardTransitions + } + STATUS current + DESCRIPTION + "Spanning Tree data for each port on this device." + ::= { dot1dGroups 4 } + +dot1dStpPortGroup2 OBJECT-GROUP + OBJECTS { + dot1dStpPort, + dot1dStpPortPriority, + dot1dStpPortState, + dot1dStpPortEnable, + dot1dStpPortDesignatedRoot, + dot1dStpPortDesignatedCost, + dot1dStpPortDesignatedBridge, + dot1dStpPortDesignatedPort, + dot1dStpPortForwardTransitions, + dot1dStpPortPathCost32 + } + STATUS current + DESCRIPTION + "Spanning Tree data for each port on this device." + ::= { dot1dGroups 5 } + +dot1dStpPortGroup3 OBJECT-GROUP + OBJECTS { + dot1dStpPortPathCost32 + } + STATUS current + DESCRIPTION + "Spanning Tree data for devices supporting 32-bit + path costs." + ::= { dot1dGroups 6 } + +-- ---------------------------------------------------------- -- +-- the dot1dTp group +-- ---------------------------------------------------------- -- + +dot1dTpBridgeGroup OBJECT-GROUP + OBJECTS { + dot1dTpLearnedEntryDiscards, + dot1dTpAgingTime + } + STATUS current + DESCRIPTION + "Bridge level Transparent Bridging data." + ::= { dot1dGroups 7 } + +dot1dTpFdbGroup OBJECT-GROUP + OBJECTS { + dot1dTpFdbAddress, + dot1dTpFdbPort, + dot1dTpFdbStatus + } + + STATUS current + DESCRIPTION + "Filtering Database information for the Bridge." + ::= { dot1dGroups 8 } + +dot1dTpGroup OBJECT-GROUP + OBJECTS { + dot1dTpPort, + dot1dTpPortMaxInfo, + dot1dTpPortInFrames, + dot1dTpPortOutFrames, + dot1dTpPortInDiscards + } + STATUS current + DESCRIPTION + "Dynamic Filtering Database information for each port of + the Bridge." + ::= { dot1dGroups 9 } + +-- ---------------------------------------------------------- -- +-- The Static (Destination-Address Filtering) Database +-- ---------------------------------------------------------- -- + +dot1dStaticGroup OBJECT-GROUP + OBJECTS { + dot1dStaticAddress, + dot1dStaticReceivePort, + dot1dStaticAllowedToGoTo, + dot1dStaticStatus + } + STATUS current + DESCRIPTION + "Static Filtering Database information for each port of + the Bridge." + ::= { dot1dGroups 10 } + +-- ---------------------------------------------------------- -- +-- The Trap Notification Group +-- ---------------------------------------------------------- -- + +dot1dNotificationGroup NOTIFICATION-GROUP + NOTIFICATIONS { + newRoot, + topologyChange + } + STATUS current + DESCRIPTION + "Group of objects describing notifications (traps)." + ::= { dot1dGroups 11 } + +-- ---------------------------------------------------------- -- +-- compliance statements +-- ---------------------------------------------------------- -- + +bridgeCompliance1493 MODULE-COMPLIANCE + STATUS current + DESCRIPTION + "The compliance statement for device support of bridging + services, as per RFC1493." + + MODULE + MANDATORY-GROUPS { + dot1dBaseBridgeGroup, + dot1dBasePortGroup + } + + GROUP dot1dStpBridgeGroup + DESCRIPTION + "Implementation of this group is mandatory for bridges + that support the Spanning Tree Protocol." + + GROUP dot1dStpPortGroup + DESCRIPTION + "Implementation of this group is mandatory for bridges + that support the Spanning Tree Protocol." + + GROUP dot1dTpBridgeGroup + DESCRIPTION + "Implementation of this group is mandatory for bridges + that support the transparent bridging mode. A + transparent or SRT bridge will implement this group." + + GROUP dot1dTpFdbGroup + DESCRIPTION + "Implementation of this group is mandatory for bridges + that support the transparent bridging mode. A + transparent or SRT bridge will implement this group." + + GROUP dot1dTpGroup + DESCRIPTION + "Implementation of this group is mandatory for bridges + that support the transparent bridging mode. A + transparent or SRT bridge will implement this group." + + GROUP dot1dStaticGroup + DESCRIPTION + "Implementation of this group is optional." + + GROUP dot1dNotificationGroup + DESCRIPTION + "Implementation of this group is optional." + ::= { dot1dCompliances 1 } + +bridgeCompliance4188 MODULE-COMPLIANCE + STATUS current + DESCRIPTION + "The compliance statement for device support of bridging + services. This supports 32-bit Path Cost values and the + more restricted bridge and port priorities, as per IEEE + 802.1t. + + Full support for the 802.1D management objects requires that + the SNMPv2-MIB [RFC3418] objects sysDescr, and sysUpTime, as + well as the IF-MIB [RFC2863] objects ifIndex, ifType, + ifDescr, ifPhysAddress, and ifLastChange are implemented." + + MODULE + MANDATORY-GROUPS { + dot1dBaseBridgeGroup, + dot1dBasePortGroup + } + + GROUP dot1dStpBridgeGroup + DESCRIPTION + "Implementation of this group is mandatory for + bridges that support the Spanning Tree Protocol." + + OBJECT dot1dStpPriority + SYNTAX Integer32 (0|4096|8192|12288|16384|20480|24576 + |28672|32768|36864|40960|45056|49152 + |53248|57344|61440) + DESCRIPTION + "The possible values defined by IEEE 802.1t." + + GROUP dot1dStpPortGroup2 + DESCRIPTION + "Implementation of this group is mandatory for + bridges that support the Spanning Tree Protocol." + + GROUP dot1dStpPortGroup3 + DESCRIPTION + "Implementation of this group is mandatory for bridges + that support the Spanning Tree Protocol and 32-bit path + costs. In particular, this includes devices supporting + IEEE 802.1t and IEEE 802.1w." + + OBJECT dot1dStpPortPriority + SYNTAX Integer32 (0|16|32|48|64|80|96|112|128 + |144|160|176|192|208|224|240) + DESCRIPTION + "The possible values defined by IEEE 802.1t." + + GROUP dot1dTpBridgeGroup + DESCRIPTION + "Implementation of this group is mandatory for + bridges that support the transparent bridging + mode. A transparent or SRT bridge will implement + this group." + + GROUP dot1dTpFdbGroup + DESCRIPTION + "Implementation of this group is mandatory for + bridges that support the transparent bridging + mode. A transparent or SRT bridge will implement + this group." + + GROUP dot1dTpGroup + DESCRIPTION + "Implementation of this group is mandatory for + bridges that support the transparent bridging + mode. A transparent or SRT bridge will implement + this group." + + GROUP dot1dStaticGroup + DESCRIPTION + "Implementation of this group is optional." + + GROUP dot1dNotificationGroup + DESCRIPTION + "Implementation of this group is optional." + + ::= { dot1dCompliances 2 } + +END diff --git a/usr.sbin/bsnmpd/modules/snmp_bridge/Makefile b/usr.sbin/bsnmpd/modules/snmp_bridge/Makefile new file mode 100644 index 0000000..5d0afec --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_bridge/Makefile @@ -0,0 +1,19 @@ +# +# $FreeBSD$ +# + +MOD= bridge +SRCS= bridge_snmp.c bridge_if.c bridge_port.c bridge_addrs.c \ + bridge_pf.c bridge_sys.c +CFLAGS+= -DSNMPTREE_TYPES + +XSYM= dot1dBridge newRoot topologyChange begemotBridgeNewRoot \ + begemotBridgeTopologyChange begemotBridgeBaseName + +MAN= snmp_bridge.3 + +BMIBS= BRIDGE-MIB.txt BEGEMOT-BRIDGE-MIB.txt RSTP-MIB.txt +DEFS= ${MOD}_tree.def +INCS= ${MOD}_snmp.h + +.include <bsd.snmpmod.mk> diff --git a/usr.sbin/bsnmpd/modules/snmp_bridge/RSTP-MIB.txt b/usr.sbin/bsnmpd/modules/snmp_bridge/RSTP-MIB.txt new file mode 100644 index 0000000..ea6648e --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_bridge/RSTP-MIB.txt @@ -0,0 +1,325 @@ +-- +-- Copyright (C) The Internet Society (2005). +-- +-- This document is subject to the rights, licenses and restrictions +-- contained in BCP 78, and except as set forth therein, the authors +-- retain all their rights. +-- +-- This document and the information contained herein are provided on an +-- "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS +-- OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET +-- ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, +-- INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE +-- INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED +-- WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +-- +-- $FreeBSD$ +-- + +RSTP-MIB DEFINITIONS ::= BEGIN + +-- ------------------------------------------------------------- +-- MIB for IEEE 802.1w Rapid Spanning Tree Protocol +-- ------------------------------------------------------------- + +IMPORTS + MODULE-IDENTITY, OBJECT-TYPE, Integer32, mib-2 + FROM SNMPv2-SMI + TruthValue + FROM SNMPv2-TC + MODULE-COMPLIANCE, OBJECT-GROUP + FROM SNMPv2-CONF + dot1dStp, dot1dStpPortEntry + FROM BRIDGE-MIB; + +rstpMIB MODULE-IDENTITY + LAST-UPDATED "200512070000Z" + ORGANIZATION "IETF Bridge MIB Working Group" + CONTACT-INFO + "Email: Bridge-mib@ietf.org" + DESCRIPTION + "The Bridge MIB Extension module for managing devices + that support the Rapid Spanning Tree Protocol defined + by IEEE 802.1w. + + Copyright (C) The Internet Society (2005). This version of + this MIB module is part of RFC 4318; See the RFC itself for + full legal notices." + + REVISION "200512070000Z" + DESCRIPTION + "The initial version of this MIB module as published in + RFC 4318." + ::= { mib-2 134 } + +-- ---------------------------------------------------------- -- +-- subtrees in the RSTP-MIB +-- ---------------------------------------------------------- -- + +rstpNotifications OBJECT IDENTIFIER ::= { rstpMIB 0 } +rstpObjects OBJECT IDENTIFIER ::= { rstpMIB 1 } +rstpConformance OBJECT IDENTIFIER ::= { rstpMIB 2 } + +-- ------------------------------------------------------------- +-- Addition to the dot1dStp group +-- ------------------------------------------------------------- + +dot1dStpVersion OBJECT-TYPE + SYNTAX INTEGER { + stpCompatible(0), + rstp(2) + } + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The version of Spanning Tree Protocol the bridge is + currently running. The value 'stpCompatible(0)' + indicates the Spanning Tree Protocol specified in + IEEE 802.1D-1998 and 'rstp(2)' indicates the Rapid + Spanning Tree Protocol specified in IEEE 802.1w and + clause 17 of 802.1D-2004. The values are directly from + the IEEE standard. New values may be defined as future + versions of the protocol become available. + + The value of this object MUST be retained across + reinitializations of the management system." + REFERENCE + "IEEE 802.1w clause 14.8.1, 17.12, 17.16.1" + DEFVAL { rstp } + ::= { dot1dStp 16 } + +dot1dStpTxHoldCount OBJECT-TYPE + SYNTAX Integer32 (1..10) + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The value used by the Port Transmit state machine to limit + the maximum transmission rate. + + The value of this object MUST be retained across + reinitializations of the management system." + + REFERENCE + "IEEE 802.1w clause 17.16.6" + DEFVAL { 3 } + ::= { dot1dStp 17 } + +-- +-- { dot1dStp 18 } was used to represent dot1dStpPathCostDefault +-- in an earlier version of this MIB. It has since been +-- obsoleted, and should not be used. +-- + +dot1dStpExtPortTable OBJECT-TYPE + SYNTAX SEQUENCE OF Dot1dStpExtPortEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A table that contains port-specific Rapid Spanning Tree + information." + ::= { dot1dStp 19 } + +dot1dStpExtPortEntry OBJECT-TYPE + SYNTAX Dot1dStpExtPortEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A list of Rapid Spanning Tree information maintained by + each port." + AUGMENTS { dot1dStpPortEntry } + ::= { dot1dStpExtPortTable 1 } + +Dot1dStpExtPortEntry ::= + SEQUENCE { + dot1dStpPortProtocolMigration + TruthValue, + dot1dStpPortAdminEdgePort + TruthValue, + dot1dStpPortOperEdgePort + TruthValue, + dot1dStpPortAdminPointToPoint + INTEGER, + dot1dStpPortOperPointToPoint + TruthValue, + dot1dStpPortAdminPathCost + Integer32 + } + +dot1dStpPortProtocolMigration OBJECT-TYPE + SYNTAX TruthValue + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "When operating in RSTP (version 2) mode, writing true(1) + to this object forces this port to transmit RSTP BPDUs. + Any other operation on this object has no effect and + it always returns false(2) when read." + REFERENCE + "IEEE 802.1w clause 14.8.2.4, 17.18.10, 17.26" + ::= { dot1dStpExtPortEntry 1 } + +dot1dStpPortAdminEdgePort OBJECT-TYPE + SYNTAX TruthValue + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The administrative value of the Edge Port parameter. A + value of true(1) indicates that this port should be + assumed as an edge-port, and a value of false(2) indicates + that this port should be assumed as a non-edge-port. + Setting this object will also cause the corresponding + instance of dot1dStpPortOperEdgePort to change to the + same value. Note that even when this object's value + is true, the value of the corresponding instance of + dot1dStpPortOperEdgePort can be false if a BPDU has + been received. + + The value of this object MUST be retained across + reinitializations of the management system." + + REFERENCE + "IEEE 802.1t clause 14.8.2, 18.3.3" + ::= { dot1dStpExtPortEntry 2 } + +dot1dStpPortOperEdgePort OBJECT-TYPE + SYNTAX TruthValue + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The operational value of the Edge Port parameter. The + object is initialized to the value of the corresponding + instance of dot1dStpPortAdminEdgePort. When the + corresponding instance of dot1dStpPortAdminEdgePort is + set, this object will be changed as well. This object + will also be changed to false on reception of a BPDU." + + REFERENCE + "IEEE 802.1t clause 14.8.2, 18.3.4" + ::= { dot1dStpExtPortEntry 3 } + +dot1dStpPortAdminPointToPoint OBJECT-TYPE + SYNTAX INTEGER { + forceTrue(0), + forceFalse(1), + auto(2) + } + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The administrative point-to-point status of the LAN segment + attached to this port, using the enumeration values of the + IEEE 802.1w clause. A value of forceTrue(0) indicates + that this port should always be treated as if it is + connected to a point-to-point link. A value of + forceFalse(1) indicates that this port should be treated as + having a shared media connection. A value of auto(2) + indicates that this port is considered to have a + point-to-point link if it is an Aggregator and all of its + members are aggregatable, or if the MAC entity + is configured for full duplex operation, either through + auto-negotiation or by management means. Manipulating this + object changes the underlying adminPortToPortMAC. + + The value of this object MUST be retained across + reinitializations of the management system." + + REFERENCE + "IEEE 802.1w clause 6.4.3, 6.5, 14.8.2" + ::= { dot1dStpExtPortEntry 4 } + +dot1dStpPortOperPointToPoint OBJECT-TYPE + SYNTAX TruthValue + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The operational point-to-point status of the LAN segment + attached to this port. It indicates whether a port is + considered to have a point-to-point connection. + If adminPointToPointMAC is set to auto(2), then the value + of operPointToPointMAC is determined in accordance with the + specific procedures defined for the MAC entity concerned, + as defined in IEEE 802.1w, clause 6.5. The value is + determined dynamically; that is, it is re-evaluated whenever + the value of adminPointToPointMAC changes, and whenever + the specific procedures defined for the MAC entity evaluate + a change in its point-to-point status." + REFERENCE + "IEEE 802.1w clause 6.4.3, 6.5, 14.8.2" + ::= { dot1dStpExtPortEntry 5 } + +dot1dStpPortAdminPathCost OBJECT-TYPE + SYNTAX Integer32 (0..200000000) + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The administratively assigned value for the contribution + of this port to the path cost of paths toward the spanning + tree root. + + Writing a value of '0' assigns the automatically calculated + default Path Cost value to the port. If the default Path + Cost is being used, this object returns '0' when read. + + This complements the object dot1dStpPortPathCost or + dot1dStpPortPathCost32, which returns the operational value + of the path cost. + + The value of this object MUST be retained across + reinitializations of the management system." + REFERENCE + "IEEE 802.1D-1998: Section 8.5.5.3" + ::= { dot1dStpExtPortEntry 6 } + +-- ------------------------------------------------------------- +-- rstpMIB - Conformance Information +-- ------------------------------------------------------------- + +rstpGroups OBJECT IDENTIFIER ::= { rstpConformance 1 } + +rstpCompliances OBJECT IDENTIFIER ::= { rstpConformance 2 } + +-- ------------------------------------------------------------- +-- Units of conformance +-- ------------------------------------------------------------- + +rstpBridgeGroup OBJECT-GROUP + OBJECTS { + dot1dStpVersion, + dot1dStpTxHoldCount + } + STATUS current + DESCRIPTION + "Rapid Spanning Tree information for the bridge." + ::= { rstpGroups 1 } + +rstpPortGroup OBJECT-GROUP + OBJECTS { + dot1dStpPortProtocolMigration, + dot1dStpPortAdminEdgePort, + dot1dStpPortOperEdgePort, + dot1dStpPortAdminPointToPoint, + dot1dStpPortOperPointToPoint, + dot1dStpPortAdminPathCost + } + STATUS current + DESCRIPTION + "Rapid Spanning Tree information for individual ports." + ::= { rstpGroups 2 } + +-- ------------------------------------------------------------- +-- Compliance statements +-- ------------------------------------------------------------- + +rstpCompliance MODULE-COMPLIANCE + STATUS current + DESCRIPTION + "The compliance statement for device support of Rapid + Spanning Tree Protocol (RSTP) bridging services." + MODULE + MANDATORY-GROUPS { + rstpBridgeGroup, + rstpPortGroup + } + ::= { rstpCompliances 1 } + +END diff --git a/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_addrs.c b/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_addrs.c new file mode 100644 index 0000000..0daceec --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_addrs.c @@ -0,0 +1,589 @@ +/*- + * Copyright (c) 2006 Shteryana Shopova <syrinx@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Bridge MIB implementation for SNMPd. + * Bridge addresses. + * + * $FreeBSD$ + */ + +#include <sys/queue.h> +#include <sys/socket.h> +#include <sys/types.h> + +#include <net/ethernet.h> +#include <net/if.h> +#include <net/if_mib.h> + +#include <assert.h> +#include <errno.h> +#include <stdarg.h> +#include <string.h> +#include <stdlib.h> +#include <syslog.h> + +#include <bsnmp/snmpmod.h> +#include <bsnmp/snmp_mibII.h> + +#include "bridge_tree.h" +#include "bridge_snmp.h" + +TAILQ_HEAD(tp_entries, tp_entry); + +/* + * Free the bridge address list. + */ +static void +bridge_tpe_free(struct tp_entries *headp) +{ + struct tp_entry *t; + + while ((t = TAILQ_FIRST(headp)) != NULL) { + TAILQ_REMOVE(headp, t, tp_e); + free(t); + } +} + +/* + * Free the bridge address entries from the address list, + * for the specified bridge interface only. + */ +static void +bridge_tpe_bif_free(struct tp_entries *headp, + struct bridge_if *bif) +{ + struct tp_entry *tp; + + while (bif->f_tpa != NULL && bif->sysindex == bif->f_tpa->sysindex) { + tp = TAILQ_NEXT(bif->f_tpa, tp_e); + TAILQ_REMOVE(headp, bif->f_tpa, tp_e); + free(bif->f_tpa); + bif->f_tpa = tp; + } +} + +/* + * Compare two mac addresses. + * m1 < m2 : -1 + * m1 > m2 : +1 + * m1 = m2 : 0 + */ +static int +bridge_compare_macs(const uint8_t *m1, const uint8_t *m2) +{ + int i; + + for (i = 0; i < ETHER_ADDR_LEN; i++) { + if (m1[i] < m2[i]) + return (-1); + if (m1[i] > m2[i]) + return (1); + } + + return (0); +} + +/* + * Insert an address entry in the bridge address TAILQ starting to search + * for its place from the position of the first bridge address for the bridge + * interface. Update the first bridge address if necessary. + */ +static void +bridge_addrs_insert_at(struct tp_entries *headp, + struct tp_entry *ta, struct tp_entry **f_tpa) +{ + struct tp_entry *t1; + + assert(f_tpa != NULL); + + for (t1 = *f_tpa; + t1 != NULL && ta->sysindex == t1->sysindex; + t1 = TAILQ_NEXT(t1, tp_e)) { + if (bridge_compare_macs(ta->tp_addr, t1->tp_addr) < 0) { + TAILQ_INSERT_BEFORE(t1, ta, tp_e); + if (*f_tpa == t1) + (*f_tpa) = ta; + return; + } + } + + if (t1 == NULL) + TAILQ_INSERT_TAIL(headp, ta, tp_e); + else + TAILQ_INSERT_BEFORE(t1, ta, tp_e); +} + +/* + * Find an address entry's position in the address list + * according to bridge interface name. + */ +static struct tp_entry * +bridge_addrs_find_pos(struct tp_entries *headp, uint32_t b_idx) +{ + uint32_t t_idx; + struct tp_entry *t1; + + if ((t1 = TAILQ_FIRST(headp)) == NULL || + bridge_compare_sysidx(b_idx, t1->sysindex) < 0) + return (NULL); + + t_idx = t1->sysindex; + + for (t1 = TAILQ_NEXT(t1, tp_e); t1 != NULL; t1 = TAILQ_NEXT(t1, tp_e)) { + + if (t1->sysindex != t_idx) { + if (bridge_compare_sysidx(b_idx, t1->sysindex) < 0) + return (TAILQ_PREV(t1, tp_entries, tp_e)); + else + t_idx = t1->sysindex; + } + } + + if (t1 == NULL) + t1 = TAILQ_LAST(headp, tp_entries); + + return (t1); +} + +/* + * Insert a bridge address in the bridge addresses list. + */ +static void +bridge_addrs_bif_insert(struct tp_entries *headp, struct tp_entry *te, + struct tp_entry **f_tpa) +{ + struct tp_entry *temp; + + if (*f_tpa != NULL) + bridge_addrs_insert_at(headp, te, f_tpa); + else { + temp = bridge_addrs_find_pos(headp, te->sysindex); + + if (temp == NULL) + TAILQ_INSERT_HEAD(headp, te, tp_e); + else + TAILQ_INSERT_AFTER(headp, temp, te, tp_e); + *f_tpa = te; + } +} + +static struct tp_entries tp_entries = TAILQ_HEAD_INITIALIZER(tp_entries); +static time_t address_list_age; + +void +bridge_addrs_update_listage(void) +{ + address_list_age = time(NULL); +} + +void +bridge_addrs_fini(void) +{ + bridge_tpe_free(&tp_entries); +} + +void +bridge_addrs_free(struct bridge_if *bif) +{ + bridge_tpe_bif_free(&tp_entries, bif); +} + +/* + * Find the first address in the list. + */ +static struct tp_entry * +bridge_addrs_first(void) +{ + return (TAILQ_FIRST(&tp_entries)); +} + +/* + * Find the next address in the list. + */ +static struct tp_entry * +bridge_addrs_next(struct tp_entry *te) +{ + return (TAILQ_NEXT(te, tp_e)); +} + +/* + * Find the first address, learnt by the specified bridge interface. + */ +struct tp_entry * +bridge_addrs_bif_first(struct bridge_if *bif) +{ + return (bif->f_tpa); +} + +/* + * Find the next address, learnt by the specified bridge interface. + */ +struct tp_entry * +bridge_addrs_bif_next(struct tp_entry *te) +{ + struct tp_entry *te_next; + + if ((te_next = TAILQ_NEXT(te, tp_e)) == NULL || + te_next->sysindex != te->sysindex) + return (NULL); + + return (te_next); +} + +/* + * Remove a bridge address from the list. + */ +void +bridge_addrs_remove(struct tp_entry *te, struct bridge_if *bif) +{ + if (bif->f_tpa == te) + bif->f_tpa = bridge_addrs_bif_next(te); + + TAILQ_REMOVE(&tp_entries, te, tp_e); + free(te); +} + +/* + * Allocate memory for a new bridge address and insert it in the list. + */ +struct tp_entry * +bridge_new_addrs(uint8_t *mac, struct bridge_if *bif) +{ + struct tp_entry *te; + + if ((te = (struct tp_entry *) malloc(sizeof(*te))) == NULL) { + syslog(LOG_ERR, "bridge new address: failed: %s", + strerror(errno)); + return (NULL); + } + + bzero(te, sizeof(*te)); + + te->sysindex = bif->sysindex; + bcopy(mac, te->tp_addr, ETHER_ADDR_LEN); + bridge_addrs_bif_insert(&tp_entries, te, &(bif->f_tpa)); + + return (te); +} + +/* + * Given a mac address, learnt on a bridge, + * find the corrsponding TP entry for it. + */ +struct tp_entry * +bridge_addrs_find(uint8_t *mac, struct bridge_if *bif) +{ + struct tp_entry *te; + + for (te = bif->f_tpa; te != NULL; te = TAILQ_NEXT(te, tp_e)) { + if (te->sysindex != bif->sysindex) { + te = NULL; + break; + } + + if (bridge_compare_macs(te->tp_addr, mac) == 0) + break; + } + + return (te); +} + +void +bridge_addrs_dump(struct bridge_if *bif) +{ + struct tp_entry *te; + + syslog(LOG_ERR, "Addresses count - %d", bif->num_addrs); + for (te = bridge_addrs_bif_first(bif); te != NULL; + te = bridge_addrs_bif_next(te)) { + syslog(LOG_ERR, "address %x:%x:%x:%x:%x:%x on port %d.%d", + te->tp_addr[0], te->tp_addr[1], te->tp_addr[2], + te->tp_addr[3], te->tp_addr[4], te->tp_addr[5], + te->sysindex, te->port_no); + } +} + +/* + * RFC4188 specifics. + */ + +/* + * Construct the SNMP index from the address DST Mac. + */ +static void +bridge_addrs_index_append(struct asn_oid *oid, uint sub, + const struct tp_entry *te) +{ + int i; + + oid->len = sub + ETHER_ADDR_LEN + 1; + oid->subs[sub] = ETHER_ADDR_LEN; + + for (i = 1; i <= ETHER_ADDR_LEN; i++) + oid->subs[sub + i] = te->tp_addr[i - 1]; +} + +/* + * Find the address entry for the SNMP index from the default bridge only. + */ +static struct tp_entry * +bridge_addrs_get(const struct asn_oid *oid, uint sub, + struct bridge_if *bif) +{ + int i; + uint8_t tp_addr[ETHER_ADDR_LEN]; + + if (oid->len - sub != ETHER_ADDR_LEN + 1 || + oid->subs[sub] != ETHER_ADDR_LEN) + return (NULL); + + for (i = 0; i < ETHER_ADDR_LEN; i++) + tp_addr[i] = oid->subs[sub + i + 1]; + + return (bridge_addrs_find(tp_addr, bif)); +} + +/* + * Find the next address entry for the SNMP index + * from the default bridge only. + */ +static struct tp_entry * +bridge_addrs_getnext(const struct asn_oid *oid, uint sub, + struct bridge_if *bif) +{ + int i; + uint8_t tp_addr[ETHER_ADDR_LEN]; + static struct tp_entry *te; + + if (oid->len - sub == 0) + return (bridge_addrs_bif_first(bif)); + + if (oid->len - sub != ETHER_ADDR_LEN + 1 || + oid->subs[sub] != ETHER_ADDR_LEN) + return (NULL); + + for (i = 0; i < ETHER_ADDR_LEN; i++) + tp_addr[i] = oid->subs[sub + i + 1]; + + if ((te = bridge_addrs_find(tp_addr, bif)) == NULL) + return (NULL); + + return (bridge_addrs_bif_next(te)); +} + +int +op_dot1d_tp_fdb(struct snmp_context *c __unused, struct snmp_value *val, + uint sub, uint iidx __unused, enum snmp_op op) +{ + struct bridge_if *bif; + struct tp_entry *te; + + if ((bif = bridge_get_default()) == NULL) + return (SNMP_ERR_NOSUCHNAME); + + if (time(NULL) - bif->addrs_age > bridge_get_data_maxage() && + bridge_update_addrs(bif) <= 0) + return (SNMP_ERR_NOSUCHNAME); + + switch (op) { + case SNMP_OP_GET: + if ((te = bridge_addrs_get(&val->var, sub, bif)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + goto get; + + case SNMP_OP_GETNEXT: + if ((te = bridge_addrs_getnext(&val->var, sub, bif)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + bridge_addrs_index_append(&val->var, sub, te); + goto get; + + case SNMP_OP_SET: + return (SNMP_ERR_NOT_WRITEABLE); + + case SNMP_OP_ROLLBACK: + case SNMP_OP_COMMIT: + break; + } + abort(); + +get: + switch (val->var.subs[sub - 1]) { + case LEAF_dot1dTpFdbAddress: + return (string_get(val, te->tp_addr, ETHER_ADDR_LEN)); + case LEAF_dot1dTpFdbPort : + val->v.integer = te->port_no; + return (SNMP_ERR_NOERROR); + case LEAF_dot1dTpFdbStatus: + val->v.integer = te->status; + return (SNMP_ERR_NOERROR); + } + + abort(); +} + +/* + * Private BEGEMOT-BRIDGE-MIB specifics. + */ + +/* + * Construct the SNMP index from the bridge interface name + * and the address DST Mac. + */ +static int +bridge_addrs_begemot_index_append(struct asn_oid *oid, uint sub, + const struct tp_entry *te) +{ + uint i, n_len; + const char *b_name; + + if ((b_name = bridge_if_find_name(te->sysindex)) == NULL) + return (-1); + + n_len = strlen(b_name); + oid->len = sub++; + oid->subs[oid->len++] = n_len; + + for (i = 1; i <= n_len; i++) + oid->subs[oid->len++] = b_name[i - 1]; + + oid->subs[oid->len++] = ETHER_ADDR_LEN; + for (i = 1 ; i <= ETHER_ADDR_LEN; i++) + oid->subs[oid->len++] = te->tp_addr[i - 1]; + + return (0); +} + +/* + * Find a bridge address entry by the bridge interface name + * and the address DST Mac. + */ +static struct tp_entry * +bridge_addrs_begemot_get(const struct asn_oid *oid, uint sub) +{ + uint i, n_len; + uint8_t tp_addr[ETHER_ADDR_LEN]; + char bif_name[IFNAMSIZ]; + struct bridge_if *bif; + + n_len = oid->subs[sub]; + if (oid->len - sub != n_len + ETHER_ADDR_LEN + 3 || + n_len >= IFNAMSIZ || oid->subs[sub + n_len + 1] != ETHER_ADDR_LEN) + return (NULL); + + for (i = 0; i < n_len; i++) + bif_name[i] = oid->subs[n_len + i + 1]; + bif_name[i] = '\0'; + + for (i = 1; i <= ETHER_ADDR_LEN; i++) + tp_addr[i - 1] = oid->subs[n_len + i + 1]; + + if ((bif = bridge_if_find_ifname(bif_name)) == NULL) + return (NULL); + + return (bridge_addrs_find(tp_addr, bif)); +} + +/* + * Find the next bridge address entry by the bridge interface name + * and the address DST Mac. + */ +static struct tp_entry * +bridge_addrs_begemot_getnext(const struct asn_oid *oid, uint sub) +{ + uint i, n_len; + uint8_t tp_addr[ETHER_ADDR_LEN]; + char bif_name[IFNAMSIZ]; + struct bridge_if *bif; + struct tp_entry *tp; + + if (oid->len - sub == 0) + return (bridge_addrs_first()); + + n_len = oid->subs[sub]; + if (oid->len - sub != n_len + ETHER_ADDR_LEN + 2 || + n_len >= IFNAMSIZ || oid->subs[sub + n_len + 1] != ETHER_ADDR_LEN) + return (NULL); + + for (i = 1; i <= n_len; i++) + bif_name[i - 1] = oid->subs[sub + i]; + + bif_name[i - 1] = '\0'; + + for (i = 1; i <= ETHER_ADDR_LEN; i++) + tp_addr[i - 1] = oid->subs[sub + n_len + i + 1]; + + if ((bif = bridge_if_find_ifname(bif_name)) == NULL || + (tp = bridge_addrs_find(tp_addr, bif)) == NULL) + return (NULL); + + return (bridge_addrs_next(tp)); +} + +int +op_begemot_tp_fdb(struct snmp_context *c __unused, struct snmp_value *val, + uint sub, uint iidx __unused, enum snmp_op op) +{ + struct tp_entry *te; + + if (time(NULL) - address_list_age > bridge_get_data_maxage()) + bridge_update_all_addrs(); + + switch (op) { + case SNMP_OP_GET: + if ((te = bridge_addrs_begemot_get(&val->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + goto get; + + case SNMP_OP_GETNEXT: + if ((te = bridge_addrs_begemot_getnext(&val->var, + sub)) == NULL || + bridge_addrs_begemot_index_append(&val->var, + sub, te) < 0) + return (SNMP_ERR_NOSUCHNAME); + goto get; + + case SNMP_OP_SET: + return (SNMP_ERR_NOT_WRITEABLE); + + case SNMP_OP_ROLLBACK: + case SNMP_OP_COMMIT: + break; + } + abort(); + +get: + switch (val->var.subs[sub - 1]) { + case LEAF_begemotBridgeTpFdbAddress: + return (string_get(val, te->tp_addr, ETHER_ADDR_LEN)); + case LEAF_begemotBridgeTpFdbPort: + val->v.integer = te->port_no; + return (SNMP_ERR_NOERROR); + case LEAF_begemotBridgeTpFdbStatus: + val->v.integer = te->status; + return (SNMP_ERR_NOERROR); + } + + abort(); +} diff --git a/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_if.c b/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_if.c new file mode 100644 index 0000000..e5f5c50 --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_if.c @@ -0,0 +1,1479 @@ +/*- + * Copyright (c) 2006 Shteryana Shopova <syrinx@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Bridge MIB implementation for SNMPd. + * Bridge interface objects. + * + * $FreeBSD$ + */ + +#include <sys/queue.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <sys/types.h> + +#include <net/ethernet.h> +#include <net/if.h> +#include <net/if_mib.h> +#include <net/if_types.h> + +#include <errno.h> +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> + +#include <bsnmp/snmpmod.h> +#include <bsnmp/snmp_mibII.h> + +#include "bridge_tree.h" +#include "bridge_snmp.h" +#include "bridge_oid.h" + +static const struct asn_oid oid_newRoot = OIDX_newRoot; +static const struct asn_oid oid_TopologyChange = OIDX_topologyChange; +static const struct asn_oid oid_begemotBrigeName = \ + OIDX_begemotBridgeBaseName; +static const struct asn_oid oid_begemotNewRoot = OIDX_begemotBridgeNewRoot; +static const struct asn_oid oid_begemotTopologyChange = \ + OIDX_begemotBridgeTopologyChange; + +TAILQ_HEAD(bridge_ifs, bridge_if); + +/* + * Free the bridge interface list. + */ +static void +bridge_ifs_free(struct bridge_ifs *headp) +{ + struct bridge_if *b; + + while ((b = TAILQ_FIRST(headp)) != NULL) { + TAILQ_REMOVE(headp, b, b_if); + free(b); + } +} + +/* + * Insert an entry in the bridge interface TAILQ. Keep the + * TAILQ sorted by the bridge's interface name. + */ +static void +bridge_ifs_insert(struct bridge_ifs *headp, + struct bridge_if *b) +{ + struct bridge_if *temp; + + if ((temp = TAILQ_FIRST(headp)) == NULL || + strcmp(b->bif_name, temp->bif_name) < 0) { + TAILQ_INSERT_HEAD(headp, b, b_if); + return; + } + + TAILQ_FOREACH(temp, headp, b_if) + if(strcmp(b->bif_name, temp->bif_name) < 0) + TAILQ_INSERT_BEFORE(temp, b, b_if); + + TAILQ_INSERT_TAIL(headp, b, b_if); +} + +/* The global bridge interface list. */ +static struct bridge_ifs bridge_ifs = TAILQ_HEAD_INITIALIZER(bridge_ifs); +static time_t bridge_list_age; + +/* + * Free the global list. + */ +void +bridge_ifs_fini(void) +{ + bridge_ifs_free(&bridge_ifs); +} + +/* + * Find a bridge interface entry by the bridge interface system index. + */ +struct bridge_if * +bridge_if_find_ifs(uint32_t sysindex) +{ + struct bridge_if *b; + + TAILQ_FOREACH(b, &bridge_ifs, b_if) + if (b->sysindex == sysindex) + return (b); + + return (NULL); +} + +/* + * Find a bridge interface entry by the bridge interface name. + */ +struct bridge_if * +bridge_if_find_ifname(const char *b_name) +{ + struct bridge_if *b; + + TAILQ_FOREACH(b, &bridge_ifs, b_if) + if (strcmp(b_name, b->bif_name) == 0) + return (b); + + return (NULL); +} + +/* + * Find a bridge name by the bridge interface system index. + */ +const char * +bridge_if_find_name(uint32_t sysindex) +{ + struct bridge_if *b; + + TAILQ_FOREACH(b, &bridge_ifs, b_if) + if (b->sysindex == sysindex) + return (b->bif_name); + + return (NULL); +} + +/* + * Given two bridge interfaces' system indexes, find their + * corresponding names and return the result of the name + * comparison. Returns: + * error : -2 + * i1 < i2 : -1 + * i1 > i2 : +1 + * i1 = i2 : 0 + */ +int +bridge_compare_sysidx(uint32_t i1, uint32_t i2) +{ + int c; + const char *b1, *b2; + + if (i1 == i2) + return (0); + + if ((b1 = bridge_if_find_name(i1)) == NULL) { + syslog(LOG_ERR, "Bridge interface %d does not exist", i1); + return (-2); + } + + if ((b2 = bridge_if_find_name(i2)) == NULL) { + syslog(LOG_ERR, "Bridge interface %d does not exist", i2); + return (-2); + } + + if ((c = strcmp(b1, b2)) < 0) + return (-1); + else if (c > 0) + return (1); + + return (0); +} + +/* + * Fetch the first bridge interface from the list. + */ +struct bridge_if * +bridge_first_bif(void) +{ + return (TAILQ_FIRST(&bridge_ifs)); +} + +/* + * Fetch the next bridge interface from the list. + */ +struct bridge_if * +bridge_next_bif(struct bridge_if *b_pr) +{ + return (TAILQ_NEXT(b_pr, b_if)); +} + +/* + * Create a new entry for a bridge interface and insert + * it in the list. + */ +static struct bridge_if * +bridge_new_bif(const char *bif_n, uint32_t sysindex, const u_char *physaddr) +{ + struct bridge_if *bif; + + if ((bif = (struct bridge_if *) malloc(sizeof(*bif)))== NULL) { + syslog(LOG_ERR, "bridge new interface failed: %s", + strerror(errno)); + return (NULL); + } + + bzero(bif, sizeof(struct bridge_if)); + strlcpy(bif->bif_name, bif_n, IFNAMSIZ); + bcopy(physaddr, bif->br_addr.octet, ETHER_ADDR_LEN); + bif->sysindex = sysindex; + bif->br_type = BaseType_transparent_only; + /* 1 - all bridges default hold time * 100 - centi-seconds */ + bif->hold_time = 1 * 100; + bif->prot_spec = dot1dStpProtocolSpecification_ieee8021d; + bridge_ifs_insert(&bridge_ifs, bif); + + return (bif); +} + +/* + * Remove a bridge interface from the list, freeing all it's ports + * and address entries. + */ +void +bridge_remove_bif(struct bridge_if *bif) +{ + bridge_members_free(bif); + bridge_addrs_free(bif); + TAILQ_REMOVE(&bridge_ifs, bif, b_if); + free(bif); +} + + +/* + * Prepare the variable (bridge interface name) for the private + * begemot notifications. + */ +static struct snmp_value* +bridge_basename_var(struct bridge_if *bif, struct snmp_value* b_val) +{ + uint i; + + b_val->var = oid_begemotBrigeName; + b_val->var.subs[b_val->var.len++] = strlen(bif->bif_name); + + if ((b_val->v.octetstring.octets = (u_char *) + malloc(strlen(bif->bif_name))) == NULL) + return (NULL); + + for (i = 0; i < strlen(bif->bif_name); i++) + b_val->var.subs[b_val->var.len++] = bif->bif_name[i]; + + b_val->v.octetstring.len = strlen(bif->bif_name); + bcopy(bif->bif_name, b_val->v.octetstring.octets, + strlen(bif->bif_name)); + b_val->syntax = SNMP_SYNTAX_OCTETSTRING; + + return (b_val); +} + +/* + * Compare the values of the old and the new root port and + * send a new root notification, if they are not matching. + */ +static void +bridge_new_root(struct bridge_if *bif) +{ + struct snmp_value bif_idx; + + if (bridge_get_default() == bif) + snmp_send_trap(&oid_newRoot, (struct snmp_value *) NULL); + + if (bridge_basename_var(bif, &bif_idx) == NULL) + return; + + snmp_send_trap(&oid_begemotTopologyChange, + &bif_idx, (struct snmp_value *) NULL); +} + +/* + * Compare the new and old topology change times and send a + * topology change notification if necessary. + */ +static void +bridge_top_change(struct bridge_if *bif) +{ + struct snmp_value bif_idx; + + if (bridge_get_default() == bif) + snmp_send_trap(&oid_TopologyChange, + (struct snmp_value *) NULL); + + if (bridge_basename_var(bif, &bif_idx) == NULL) + return; + + snmp_send_trap(&oid_begemotNewRoot, + &bif_idx, (struct snmp_value *) NULL); +} + +static int +bridge_if_create(const char* b_name, int8_t up) +{ + if (bridge_create(b_name) < 0) + return (-1); + + if (up == 1 && (bridge_set_if_up(b_name, 1) < 0)) + return (-1); + + /* + * Do not create a new bridge entry here - + * wait until the mibII module notifies us. + */ + return (0); +} + +static int +bridge_if_destroy(struct bridge_if *bif) +{ + if (bridge_destroy(bif->bif_name) < 0) + return (-1); + + bridge_remove_bif(bif); + + return (0); +} + +/* + * Calculate the timeticks since the last topology change. + */ +static int +bridge_get_time_since_tc(struct bridge_if *bif, uint32_t *ticks) +{ + struct timeval ct; + + if (gettimeofday(&ct, NULL) < 0) { + syslog(LOG_ERR, "bridge get time since last TC:" + "getttimeofday failed: %s", strerror(errno)); + return (-1); + } + + if (ct.tv_usec - bif->last_tc_time.tv_usec < 0) { + ct.tv_sec -= 1; + ct.tv_usec += 1000000; + } + + ct.tv_sec -= bif->last_tc_time.tv_sec; + ct.tv_usec -= bif->last_tc_time.tv_usec; + + *ticks = ct.tv_sec * 100 + ct.tv_usec/10000; + + return (0); +} + +/* + * Update the info we have for a single bridge interface. + * Return: + * 1, if successful + * 0, if the interface was deleted + * -1, error occurred while fetching the info from the kernel. + */ +static int +bridge_update_bif(struct bridge_if *bif) +{ + struct mibif *ifp; + + /* Walk through the mibII interface list. */ + for (ifp = mib_first_if(); ifp != NULL; ifp = mib_next_if(ifp)) + if (strcmp(ifp->name, bif->bif_name) == 0) + break; + + if (ifp == NULL) { + /* Ops, we do not exist anymore. */ + bridge_remove_bif(bif); + return (0); + } + + if (ifp->physaddr != NULL ) + bcopy(ifp->physaddr, bif->br_addr.octet, ETHER_ADDR_LEN); + else + bridge_get_basemac(bif->bif_name, bif->br_addr.octet, + ETHER_ADDR_LEN); + + if (ifp->mib.ifmd_flags & IFF_RUNNING) + bif->if_status = RowStatus_active; + else + bif->if_status = RowStatus_notInService; + + switch (bridge_getinfo_bif(bif)) { + case 2: + bridge_new_root(bif); + break; + case 1: + bridge_top_change(bif); + break; + case -1: + bridge_remove_bif(bif); + return (-1); + default: + break; + } + + /* + * The number of ports is accessible via SNMP - + * update the ports each time the bridge interface data + * is refreshed too. + */ + bif->num_ports = bridge_update_memif(bif); + bif->entry_age = time(NULL); + + return (1); +} + +/* + * Update all bridge interfaces' ports only - + * make sure each bridge interface exists first. + */ +void +bridge_update_all_ports(void) +{ + struct mibif *ifp; + struct bridge_if *bif, *t_bif; + + for (bif = bridge_first_bif(); bif != NULL; bif = t_bif) { + t_bif = bridge_next_bif(bif); + + for (ifp = mib_first_if(); ifp != NULL; + ifp = mib_next_if(ifp)) + if (strcmp(ifp->name, bif->bif_name) == 0) + break; + + if (ifp != NULL) + bif->num_ports = bridge_update_memif(bif); + else /* Ops, we do not exist anymore. */ + bridge_remove_bif(bif); + } + + bridge_ports_update_listage(); +} + +/* + * Update all addresses only. + */ +void +bridge_update_all_addrs(void) +{ + struct mibif *ifp; + struct bridge_if *bif, *t_bif; + + for (bif = bridge_first_bif(); bif != NULL; bif = t_bif) { + t_bif = bridge_next_bif(bif); + + for (ifp = mib_first_if(); ifp != NULL; + ifp = mib_next_if(ifp)) + if (strcmp(ifp->name, bif->bif_name) == 0) + break; + + if (ifp != NULL) + bif->num_addrs = bridge_update_addrs(bif); + else /* Ops, we don't exist anymore. */ + bridge_remove_bif(bif); + } + + bridge_addrs_update_listage(); +} + +/* + * Update only the bridge interfaces' data - skip addresses. + */ +void +bridge_update_all_ifs(void) +{ + struct bridge_if *bif, *t_bif; + + for (bif = bridge_first_bif(); bif != NULL; bif = t_bif) { + t_bif = bridge_next_bif(bif); + bridge_update_bif(bif); + } + + bridge_ports_update_listage(); + bridge_list_age = time(NULL); +} + +/* + * Update all info we have for all bridges. + */ +void +bridge_update_all(void *arg __unused) +{ + struct bridge_if *bif, *t_bif; + + for (bif = bridge_first_bif(); bif != NULL; bif = t_bif) { + t_bif = bridge_next_bif(bif); + if (bridge_update_bif(bif) <= 0) + continue; + + /* Update our learnt addresses. */ + bif->num_addrs = bridge_update_addrs(bif); + } + + bridge_list_age = time(NULL); + bridge_ports_update_listage(); + bridge_addrs_update_listage(); +} + +/* + * Callback for polling our last topology change time - + * check whether we are root or whether a TC was detected once every + * 30 seconds, so that we can send the newRoot and TopologyChange traps + * on time. The rest of the data is polled only once every 5 min. + */ +void +bridge_update_tc_time(void *arg __unused) +{ + struct bridge_if *bif; + struct mibif *ifp; + + TAILQ_FOREACH(bif, &bridge_ifs, b_if) { + /* Walk through the mibII interface list. */ + for (ifp = mib_first_if(); ifp != NULL; ifp = mib_next_if(ifp)) + if (strcmp(ifp->name, bif->bif_name) == 0) + break; + + if (ifp == NULL) { + bridge_remove_bif(bif); + continue; + } + + switch (bridge_get_op_param(bif)) { + case 2: + bridge_new_root(bif); + break; + case 1: + bridge_top_change(bif); + break; + } + } +} + +/* + * Callback for handling new bridge interface creation. + */ +int +bridge_attach_newif(struct mibif *ifp) +{ + u_char mac[ETHER_ADDR_LEN]; + struct bridge_if *bif; + + if (ifp->mib.ifmd_data.ifi_type != IFT_BRIDGE) + return (0); + + /* Make sure it does not exist in our list. */ + TAILQ_FOREACH(bif, &bridge_ifs, b_if) + if(strcmp(bif->bif_name, ifp->name) == 0) { + syslog(LOG_ERR, "bridge interface %s already " + "in list", bif->bif_name); + return (-1); + } + + if (ifp->physaddr == NULL) { + if (bridge_get_basemac(ifp->name, mac, sizeof(mac)) == NULL) { + syslog(LOG_ERR, "bridge attach new %s failed - " + "no bridge mac address", ifp->name); + return (-1); + } + } else + bcopy(ifp->physaddr, &mac, sizeof(mac)); + + if ((bif = bridge_new_bif(ifp->name, ifp->sysindex, mac)) == NULL) + return (-1); + + if (ifp->mib.ifmd_flags & IFF_RUNNING) + bif->if_status = RowStatus_active; + else + bif->if_status = RowStatus_notInService; + + /* Skip sending notifications if the interface was just created. */ + if (bridge_getinfo_bif(bif) < 0 || + (bif->num_ports = bridge_getinfo_bif_ports(bif)) < 0 || + (bif->num_addrs = bridge_getinfo_bif_addrs(bif)) < 0) { + bridge_remove_bif(bif); + return (-1); + } + + /* Check whether we are the default bridge interface. */ + if (strcmp(ifp->name, bridge_get_default_name()) == 0) + bridge_set_default(bif); + + return (0); +} + +void +bridge_ifs_dump(void) +{ + struct bridge_if *bif; + + for (bif = bridge_first_bif(); bif != NULL; + bif = bridge_next_bif(bif)) { + syslog(LOG_ERR, "Bridge %s, index - %d", bif->bif_name, + bif->sysindex); + bridge_ports_dump(bif); + bridge_addrs_dump(bif); + } +} + +/* + * RFC4188 specifics. + */ +int +op_dot1d_base(struct snmp_context *ctx __unused, struct snmp_value *value, + uint sub, uint iidx __unused, enum snmp_op op) +{ + struct bridge_if *bif; + + if ((bif = bridge_get_default()) == NULL) + return (SNMP_ERR_NOSUCHNAME); + + if (time(NULL) - bif->entry_age > bridge_get_data_maxage() && + bridge_update_bif(bif) <= 0) /* It was just deleted. */ + return (SNMP_ERR_NOSUCHNAME); + + switch (op) { + case SNMP_OP_GET: + switch (value->var.subs[sub - 1]) { + case LEAF_dot1dBaseBridgeAddress: + return (string_get(value, bif->br_addr.octet, + ETHER_ADDR_LEN)); + case LEAF_dot1dBaseNumPorts: + value->v.integer = bif->num_ports; + return (SNMP_ERR_NOERROR); + case LEAF_dot1dBaseType: + value->v.integer = bif->br_type; + return (SNMP_ERR_NOERROR); + } + abort(); + + case SNMP_OP_SET: + return (SNMP_ERR_NOT_WRITEABLE); + + case SNMP_OP_GETNEXT: + case SNMP_OP_ROLLBACK: + case SNMP_OP_COMMIT: + break; + } + + abort(); +} + +int +op_dot1d_stp(struct snmp_context *ctx, struct snmp_value *val, uint sub, + uint iidx __unused, enum snmp_op op) +{ + struct bridge_if *bif; + + if ((bif = bridge_get_default()) == NULL) + return (SNMP_ERR_NOSUCHNAME); + + if (time(NULL) - bif->entry_age > bridge_get_data_maxage() && + bridge_update_bif(bif) <= 0) /* It was just deleted. */ + return (SNMP_ERR_NOSUCHNAME); + + switch (op) { + case SNMP_OP_GET: + switch (val->var.subs[sub - 1]) { + case LEAF_dot1dStpProtocolSpecification: + val->v.integer = bif->prot_spec; + return (SNMP_ERR_NOERROR); + + case LEAF_dot1dStpPriority: + val->v.integer = bif->priority; + return (SNMP_ERR_NOERROR); + + case LEAF_dot1dStpTimeSinceTopologyChange: + if (bridge_get_time_since_tc(bif, + &(val->v.uint32)) < 0) + return (SNMP_ERR_GENERR); + return (SNMP_ERR_NOERROR); + + case LEAF_dot1dStpTopChanges: + val->v.uint32 = bif->top_changes; + return (SNMP_ERR_NOERROR); + + case LEAF_dot1dStpDesignatedRoot: + return (string_get(val, bif->design_root, + SNMP_BRIDGE_ID_LEN)); + + case LEAF_dot1dStpRootCost: + val->v.integer = bif->root_cost; + return (SNMP_ERR_NOERROR); + + case LEAF_dot1dStpRootPort: + val->v.integer = bif->root_port; + return (SNMP_ERR_NOERROR); + + case LEAF_dot1dStpMaxAge: + val->v.integer = bif->max_age; + return (SNMP_ERR_NOERROR); + + case LEAF_dot1dStpHelloTime: + val->v.integer = bif->hello_time; + return (SNMP_ERR_NOERROR); + + case LEAF_dot1dStpHoldTime: + val->v.integer = bif->hold_time; + return (SNMP_ERR_NOERROR); + + case LEAF_dot1dStpForwardDelay: + val->v.integer = bif->fwd_delay; + return (SNMP_ERR_NOERROR); + + case LEAF_dot1dStpBridgeMaxAge: + val->v.integer = bif->bridge_max_age; + return (SNMP_ERR_NOERROR); + + case LEAF_dot1dStpBridgeHelloTime: + val->v.integer = bif->bridge_hello_time; + return (SNMP_ERR_NOERROR); + + case LEAF_dot1dStpBridgeForwardDelay: + val->v.integer = bif->bridge_fwd_delay; + return (SNMP_ERR_NOERROR); + + case LEAF_dot1dStpVersion: + val->v.integer = bif->stp_version; + return (SNMP_ERR_NOERROR); + + case LEAF_dot1dStpTxHoldCount: + val->v.integer = bif->tx_hold_count; + return (SNMP_ERR_NOERROR); + } + abort(); + + case SNMP_OP_GETNEXT: + abort(); + + case SNMP_OP_SET: + switch (val->var.subs[sub - 1]) { + case LEAF_dot1dStpPriority: + if (val->v.integer > SNMP_BRIDGE_MAX_PRIORITY || + val->v.integer % 4096 != 0) + return (SNMP_ERR_WRONG_VALUE); + + ctx->scratch->int1 = bif->priority; + if (bridge_set_priority(bif, val->v.integer) < 0) + return (SNMP_ERR_GENERR); + return (SNMP_ERR_NOERROR); + + case LEAF_dot1dStpBridgeMaxAge: + if (val->v.integer < SNMP_BRIDGE_MIN_MAGE || + val->v.integer > SNMP_BRIDGE_MAX_MAGE) + return (SNMP_ERR_WRONG_VALUE); + + ctx->scratch->int1 = bif->bridge_max_age; + if (bridge_set_maxage(bif, val->v.integer) < 0) + return (SNMP_ERR_GENERR); + return (SNMP_ERR_NOERROR); + + case LEAF_dot1dStpBridgeHelloTime: + if (val->v.integer < SNMP_BRIDGE_MIN_HTIME || + val->v.integer > SNMP_BRIDGE_MAX_HTIME) + return (SNMP_ERR_WRONG_VALUE); + + ctx->scratch->int1 = bif->bridge_hello_time; + if (bridge_set_hello_time(bif, val->v.integer) < 0) + return (SNMP_ERR_GENERR); + return (SNMP_ERR_NOERROR); + + case LEAF_dot1dStpBridgeForwardDelay: + if (val->v.integer < SNMP_BRIDGE_MIN_FDELAY || + val->v.integer > SNMP_BRIDGE_MAX_FDELAY) + return (SNMP_ERR_WRONG_VALUE); + + ctx->scratch->int1 = bif->bridge_fwd_delay; + if (bridge_set_forward_delay(bif, val->v.integer) < 0) + return (SNMP_ERR_GENERR); + return (SNMP_ERR_NOERROR); + + case LEAF_dot1dStpVersion: + if (val->v.integer != dot1dStpVersion_stpCompatible && + val->v.integer != dot1dStpVersion_rstp) + return (SNMP_ERR_WRONG_VALUE); + + ctx->scratch->int1 = bif->stp_version; + if (bridge_set_stp_version(bif, val->v.integer) < 0) + return (SNMP_ERR_GENERR); + return (SNMP_ERR_NOERROR); + + case LEAF_dot1dStpTxHoldCount: + if (val->v.integer < SNMP_BRIDGE_MIN_TXHC || + val->v.integer > SNMP_BRIDGE_MAX_TXHC) + return (SNMP_ERR_WRONG_VALUE); + + ctx->scratch->int1 = bif->tx_hold_count; + if (bridge_set_tx_hold_count(bif, val->v.integer) < 0) + return (SNMP_ERR_GENERR); + return (SNMP_ERR_NOERROR); + + case LEAF_dot1dStpProtocolSpecification: + case LEAF_dot1dStpTimeSinceTopologyChange: + case LEAF_dot1dStpTopChanges: + case LEAF_dot1dStpDesignatedRoot: + case LEAF_dot1dStpRootCost: + case LEAF_dot1dStpRootPort: + case LEAF_dot1dStpMaxAge: + case LEAF_dot1dStpHelloTime: + case LEAF_dot1dStpHoldTime: + case LEAF_dot1dStpForwardDelay: + return (SNMP_ERR_NOT_WRITEABLE); + } + abort(); + + case SNMP_OP_ROLLBACK: + switch (val->var.subs[sub - 1]) { + case LEAF_dot1dStpPriority: + bridge_set_priority(bif, ctx->scratch->int1); + break; + case LEAF_dot1dStpBridgeMaxAge: + bridge_set_maxage(bif, ctx->scratch->int1); + break; + case LEAF_dot1dStpBridgeHelloTime: + bridge_set_hello_time(bif, ctx->scratch->int1); + break; + case LEAF_dot1dStpBridgeForwardDelay: + bridge_set_forward_delay(bif, ctx->scratch->int1); + break; + case LEAF_dot1dStpVersion: + bridge_set_stp_version(bif, ctx->scratch->int1); + break; + case LEAF_dot1dStpTxHoldCount: + bridge_set_tx_hold_count(bif, ctx->scratch->int1); + break; + } + return (SNMP_ERR_NOERROR); + + case SNMP_OP_COMMIT: + return (SNMP_ERR_NOERROR); + } + + abort(); +} + +int +op_dot1d_tp(struct snmp_context *ctx, struct snmp_value *value, + uint sub, uint iidx __unused, enum snmp_op op) +{ + struct bridge_if *bif; + + if ((bif = bridge_get_default()) == NULL) + return (SNMP_ERR_NOSUCHNAME); + + if (time(NULL) - bif->entry_age > bridge_get_data_maxage() && + bridge_update_bif(bif) <= 0) /* It was just deleted. */ + return (SNMP_ERR_NOSUCHNAME); + + switch (op) { + case SNMP_OP_GET: + switch (value->var.subs[sub - 1]) { + case LEAF_dot1dTpLearnedEntryDiscards: + value->v.uint32 = bif->lrnt_drops; + return (SNMP_ERR_NOERROR); + case LEAF_dot1dTpAgingTime: + value->v.integer = bif->age_time; + return (SNMP_ERR_NOERROR); + } + abort(); + + case SNMP_OP_GETNEXT: + abort(); + + case SNMP_OP_SET: + switch (value->var.subs[sub - 1]) { + case LEAF_dot1dTpLearnedEntryDiscards: + return (SNMP_ERR_NOT_WRITEABLE); + + case LEAF_dot1dTpAgingTime: + if (value->v.integer < SNMP_BRIDGE_MIN_AGE_TIME || + value->v.integer > SNMP_BRIDGE_MAX_AGE_TIME) + return (SNMP_ERR_WRONG_VALUE); + + ctx->scratch->int1 = bif->age_time; + if (bridge_set_aging_time(bif, value->v.integer) < 0) + return (SNMP_ERR_GENERR); + return (SNMP_ERR_NOERROR); + } + abort(); + + case SNMP_OP_ROLLBACK: + if (value->var.subs[sub - 1] == LEAF_dot1dTpAgingTime) + bridge_set_aging_time(bif, ctx->scratch->int1); + return (SNMP_ERR_NOERROR); + + case SNMP_OP_COMMIT: + return (SNMP_ERR_NOERROR); + } + + abort(); +} + +/* + * Private BEGEMOT-BRIDGE-MIB specifics. + */ + +/* + * Get the bridge name from an OID index. + */ +static char * +bridge_name_index_get(const struct asn_oid *oid, uint sub, char *b_name) +{ + uint i; + + if (oid->len - sub != oid->subs[sub] + 1 || oid->subs[sub] >= IFNAMSIZ) + return (NULL); + + for (i = 0; i < oid->subs[sub]; i++) + b_name[i] = oid->subs[sub + i + 1]; + b_name[i] = '\0'; + + return (b_name); +} + +static void +bridge_if_index_append(struct asn_oid *oid, uint sub, + const struct bridge_if *bif) +{ + uint i; + + oid->len = sub + strlen(bif->bif_name) + 1; + oid->subs[sub] = strlen(bif->bif_name); + + for (i = 1; i <= strlen(bif->bif_name); i++) + oid->subs[sub + i] = bif->bif_name[i - 1]; +} + +static struct bridge_if * +bridge_if_index_get(const struct asn_oid *oid, uint sub) +{ + uint i; + char bif_name[IFNAMSIZ]; + + if (oid->len - sub != oid->subs[sub] + 1 || oid->subs[sub] >= IFNAMSIZ) + return (NULL); + + for (i = 0; i < oid->subs[sub]; i++) + bif_name[i] = oid->subs[sub + i + 1]; + bif_name[i] = '\0'; + + return (bridge_if_find_ifname(bif_name)); +} + +static struct bridge_if * +bridge_if_index_getnext(const struct asn_oid *oid, uint sub) +{ + uint i; + char bif_name[IFNAMSIZ]; + struct bridge_if *bif; + + if (oid->len - sub == 0) + return (bridge_first_bif()); + + if (oid->len - sub != oid->subs[sub] + 1 || oid->subs[sub] >= IFNAMSIZ) + return (NULL); + + for (i = 0; i < oid->subs[sub]; i++) + bif_name[i] = oid->subs[sub + i + 1]; + bif_name[i] = '\0'; + + if ((bif = bridge_if_find_ifname(bif_name)) == NULL) + return (NULL); + + return (bridge_next_bif(bif)); +} + +static int +bridge_set_if_status(struct snmp_context *ctx, + struct snmp_value *val, uint sub) +{ + struct bridge_if *bif; + char bif_name[IFNAMSIZ]; + + bif = bridge_if_index_get(&val->var, sub); + + switch (val->v.integer) { + case RowStatus_active: + if (bif == NULL) + return (SNMP_ERR_INCONS_VALUE); + + ctx->scratch->int1 = bif->if_status; + + switch (bif->if_status) { + case RowStatus_active: + return (SNMP_ERR_NOERROR); + case RowStatus_notInService: + if (bridge_set_if_up(bif->bif_name, 1) < 0) + return (SNMP_ERR_GENERR); + return (SNMP_ERR_NOERROR); + default: + break; + } + return (SNMP_ERR_INCONS_VALUE); + + case RowStatus_notInService: + if (bif == NULL) + return (SNMP_ERR_INCONS_VALUE); + + ctx->scratch->int1 = bif->if_status; + + switch (bif->if_status) { + case RowStatus_active: + if (bridge_set_if_up(bif->bif_name, 1) < 0) + return (SNMP_ERR_GENERR); + return (SNMP_ERR_NOERROR); + case RowStatus_notInService: + return (SNMP_ERR_NOERROR); + default: + break; + } + return (SNMP_ERR_INCONS_VALUE); + + case RowStatus_notReady: + return (SNMP_ERR_INCONS_VALUE); + + case RowStatus_createAndGo: + if (bif != NULL) + return (SNMP_ERR_INCONS_VALUE); + + ctx->scratch->int1 = RowStatus_destroy; + + if (bridge_name_index_get(&val->var, sub, bif_name) == NULL) + return (SNMP_ERR_BADVALUE); + if (bridge_if_create(bif_name, 1) < 0) + return (SNMP_ERR_GENERR); + return (SNMP_ERR_NOERROR); + + case RowStatus_createAndWait: + if (bif != NULL) + return (SNMP_ERR_INCONS_VALUE); + + if (bridge_name_index_get(&val->var, sub, bif_name) == NULL) + return (SNMP_ERR_BADVALUE); + + ctx->scratch->int1 = RowStatus_destroy; + + if (bridge_if_create(bif_name, 0) < 0) + return (SNMP_ERR_GENERR); + return (SNMP_ERR_NOERROR); + + case RowStatus_destroy: + if (bif == NULL) + return (SNMP_ERR_NOSUCHNAME); + + ctx->scratch->int1 = bif->if_status; + bif->if_status = RowStatus_destroy; + } + + return (SNMP_ERR_NOERROR); +} + +static int +bridge_rollback_if_status(struct snmp_context *ctx, + struct snmp_value *val, uint sub) +{ + struct bridge_if *bif; + + if ((bif = bridge_if_index_get(&val->var, sub)) == NULL) + return (SNMP_ERR_GENERR); + + switch (ctx->scratch->int1) { + case RowStatus_destroy: + bridge_if_destroy(bif); + return (SNMP_ERR_NOERROR); + + case RowStatus_notInService: + if (bif->if_status != ctx->scratch->int1) + bridge_set_if_up(bif->bif_name, 0); + bif->if_status = RowStatus_notInService; + return (SNMP_ERR_NOERROR); + + case RowStatus_active: + if (bif->if_status != ctx->scratch->int1) + bridge_set_if_up(bif->bif_name, 1); + bif->if_status = RowStatus_active; + return (SNMP_ERR_NOERROR); + } + + abort(); +} + +static int +bridge_commit_if_status(struct snmp_value *val, uint sub) +{ + struct bridge_if *bif; + + if ((bif = bridge_if_index_get(&val->var, sub)) == NULL) + return (SNMP_ERR_GENERR); + + if (bif->if_status == RowStatus_destroy && + bridge_if_destroy(bif) < 0) + return (SNMP_ERR_COMMIT_FAILED); + + return (SNMP_ERR_NOERROR); +} + +int +op_begemot_base_bridge(struct snmp_context *ctx, struct snmp_value *val, + uint sub, uint iidx __unused, enum snmp_op op) +{ + struct bridge_if *bif; + + if (time(NULL) - bridge_list_age > bridge_get_data_maxage()) + bridge_update_all_ifs(); + + switch (op) { + case SNMP_OP_GET: + if ((bif = bridge_if_index_get(&val->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + goto get; + + case SNMP_OP_GETNEXT: + if ((bif = bridge_if_index_getnext(&val->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + bridge_if_index_append(&val->var, sub, bif); + goto get; + + case SNMP_OP_SET: + switch (val->var.subs[sub - 1]) { + case LEAF_begemotBridgeBaseStatus: + return (bridge_set_if_status(ctx, val, sub)); + case LEAF_begemotBridgeBaseName: + case LEAF_begemotBridgeBaseAddress: + case LEAF_begemotBridgeBaseNumPorts: + case LEAF_begemotBridgeBaseType: + return (SNMP_ERR_NOT_WRITEABLE); + } + abort(); + + case SNMP_OP_ROLLBACK: + return (bridge_rollback_if_status(ctx, val, sub)); + + case SNMP_OP_COMMIT: + return (bridge_commit_if_status(val, sub)); + } + abort(); + +get: + switch (val->var.subs[sub - 1]) { + case LEAF_begemotBridgeBaseName: + return (string_get(val, bif->bif_name, -1)); + + case LEAF_begemotBridgeBaseAddress: + return (string_get(val, bif->br_addr.octet, ETHER_ADDR_LEN)); + + case LEAF_begemotBridgeBaseNumPorts: + val->v.integer = bif->num_ports; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotBridgeBaseType: + val->v.integer = bif->br_type; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotBridgeBaseStatus: + val->v.integer = bif->if_status; + return (SNMP_ERR_NOERROR); + } + + abort(); +} + +int +op_begemot_stp(struct snmp_context *ctx, struct snmp_value *val, + uint sub, uint iidx __unused, enum snmp_op op) +{ + struct bridge_if *bif; + + if (time(NULL) - bridge_list_age > bridge_get_data_maxage()) + bridge_update_all_ifs(); + + switch (op) { + case SNMP_OP_GET: + if ((bif = bridge_if_index_get(&val->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + goto get; + + case SNMP_OP_GETNEXT: + if ((bif = bridge_if_index_getnext(&val->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + bridge_if_index_append(&val->var, sub, bif); + goto get; + + case SNMP_OP_SET: + if ((bif = bridge_if_index_get(&val->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + + switch (val->var.subs[sub - 1]) { + case LEAF_begemotBridgeStpPriority: + if (val->v.integer > SNMP_BRIDGE_MAX_PRIORITY || + val->v.integer % 4096 != 0) + return (SNMP_ERR_WRONG_VALUE); + + ctx->scratch->int1 = bif->priority; + if (bridge_set_priority(bif, val->v.integer) < 0) + return (SNMP_ERR_GENERR); + return (SNMP_ERR_NOERROR); + + case LEAF_begemotBridgeStpBridgeMaxAge: + if (val->v.integer < SNMP_BRIDGE_MIN_MAGE || + val->v.integer > SNMP_BRIDGE_MAX_MAGE) + return (SNMP_ERR_WRONG_VALUE); + + ctx->scratch->int1 = bif->bridge_max_age; + if (bridge_set_maxage(bif, val->v.integer) < 0) + return (SNMP_ERR_GENERR); + return (SNMP_ERR_NOERROR); + + case LEAF_begemotBridgeStpBridgeHelloTime: + if (val->v.integer < SNMP_BRIDGE_MIN_HTIME || + val->v.integer > SNMP_BRIDGE_MAX_HTIME) + return (SNMP_ERR_WRONG_VALUE); + + ctx->scratch->int1 = bif->bridge_hello_time; + if (bridge_set_hello_time(bif, val->v.integer) < 0) + return (SNMP_ERR_GENERR); + return (SNMP_ERR_NOERROR); + + case LEAF_begemotBridgeStpBridgeForwardDelay: + if (val->v.integer < SNMP_BRIDGE_MIN_FDELAY || + val->v.integer > SNMP_BRIDGE_MAX_FDELAY) + return (SNMP_ERR_WRONG_VALUE); + + ctx->scratch->int1 = bif->bridge_fwd_delay; + if (bridge_set_forward_delay(bif, val->v.integer) < 0) + return (SNMP_ERR_GENERR); + return (SNMP_ERR_NOERROR); + + case LEAF_begemotBridgeStpVersion: + if (val->v.integer != + begemotBridgeStpVersion_stpCompatible && + val->v.integer != begemotBridgeStpVersion_rstp) + return (SNMP_ERR_WRONG_VALUE); + + ctx->scratch->int1 = bif->stp_version; + if (bridge_set_stp_version(bif, val->v.integer) < 0) + return (SNMP_ERR_GENERR); + return (SNMP_ERR_NOERROR); + + case LEAF_begemotBridgeStpTxHoldCount: + if (val->v.integer < SNMP_BRIDGE_MIN_TXHC || + val->v.integer > SNMP_BRIDGE_MAX_TXHC) + return (SNMP_ERR_WRONG_VALUE); + + ctx->scratch->int1 = bif->tx_hold_count; + if (bridge_set_tx_hold_count(bif, val->v.integer) < 0) + return (SNMP_ERR_GENERR); + return (SNMP_ERR_NOERROR); + + case LEAF_begemotBridgeStpProtocolSpecification: + case LEAF_begemotBridgeStpTimeSinceTopologyChange: + case LEAF_begemotBridgeStpTopChanges: + case LEAF_begemotBridgeStpDesignatedRoot: + case LEAF_begemotBridgeStpRootCost: + case LEAF_begemotBridgeStpRootPort: + case LEAF_begemotBridgeStpMaxAge: + case LEAF_begemotBridgeStpHelloTime: + case LEAF_begemotBridgeStpHoldTime: + case LEAF_begemotBridgeStpForwardDelay: + return (SNMP_ERR_NOT_WRITEABLE); + } + abort(); + + case SNMP_OP_ROLLBACK: + if ((bif = bridge_if_index_get(&val->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + + switch (val->var.subs[sub - 1]) { + case LEAF_begemotBridgeStpPriority: + bridge_set_priority(bif, ctx->scratch->int1); + break; + + case LEAF_begemotBridgeStpBridgeMaxAge: + bridge_set_maxage(bif, ctx->scratch->int1); + break; + + case LEAF_begemotBridgeStpBridgeHelloTime: + bridge_set_hello_time(bif, ctx->scratch->int1); + break; + + case LEAF_begemotBridgeStpBridgeForwardDelay: + bridge_set_forward_delay(bif, ctx->scratch->int1); + break; + + case LEAF_begemotBridgeStpVersion: + bridge_set_stp_version(bif, ctx->scratch->int1); + break; + + case LEAF_begemotBridgeStpTxHoldCount: + bridge_set_tx_hold_count(bif, ctx->scratch->int1); + break; + } + return (SNMP_ERR_NOERROR); + + case SNMP_OP_COMMIT: + return (SNMP_ERR_NOERROR); + } + abort(); + +get: + switch (val->var.subs[sub - 1]) { + case LEAF_begemotBridgeStpProtocolSpecification: + val->v.integer = bif->prot_spec; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotBridgeStpPriority: + val->v.integer = bif->priority; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotBridgeStpTimeSinceTopologyChange: + if (bridge_get_time_since_tc(bif, &(val->v.uint32)) < 0) + return (SNMP_ERR_GENERR); + return (SNMP_ERR_NOERROR); + + case LEAF_begemotBridgeStpTopChanges: + val->v.uint32 = bif->top_changes; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotBridgeStpDesignatedRoot: + return (string_get(val, bif->design_root, SNMP_BRIDGE_ID_LEN)); + + case LEAF_begemotBridgeStpRootCost: + val->v.integer = bif->root_cost; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotBridgeStpRootPort: + val->v.integer = bif->root_port; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotBridgeStpMaxAge: + val->v.integer = bif->max_age; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotBridgeStpHelloTime: + val->v.integer = bif->hello_time; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotBridgeStpHoldTime: + val->v.integer = bif->hold_time; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotBridgeStpForwardDelay: + val->v.integer = bif->fwd_delay; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotBridgeStpBridgeMaxAge: + val->v.integer = bif->bridge_max_age; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotBridgeStpBridgeHelloTime: + val->v.integer = bif->bridge_hello_time; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotBridgeStpBridgeForwardDelay: + val->v.integer = bif->bridge_fwd_delay; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotBridgeStpVersion: + val->v.integer = bif->stp_version; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotBridgeStpTxHoldCount: + val->v.integer = bif->tx_hold_count; + return (SNMP_ERR_NOERROR); + } + + abort(); +} + +int +op_begemot_tp(struct snmp_context *ctx, struct snmp_value *val, + uint sub, uint iidx __unused, enum snmp_op op) +{ + struct bridge_if *bif; + + if (time(NULL) - bridge_list_age > bridge_get_data_maxage()) + bridge_update_all_ifs(); + + switch (op) { + case SNMP_OP_GET: + if ((bif = bridge_if_index_get(&val->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + goto get; + + case SNMP_OP_GETNEXT: + if ((bif = bridge_if_index_getnext(&val->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + bridge_if_index_append(&val->var, sub, bif); + goto get; + + case SNMP_OP_SET: + if ((bif = bridge_if_index_get(&val->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + + switch (val->var.subs[sub - 1]) { + case LEAF_begemotBridgeTpAgingTime: + if (val->v.integer < SNMP_BRIDGE_MIN_AGE_TIME || + val->v.integer > SNMP_BRIDGE_MAX_AGE_TIME) + return (SNMP_ERR_WRONG_VALUE); + + ctx->scratch->int1 = bif->age_time; + if (bridge_set_aging_time(bif, val->v.integer) < 0) + return (SNMP_ERR_GENERR); + return (SNMP_ERR_NOERROR); + + case LEAF_begemotBridgeTpMaxAddresses: + ctx->scratch->int1 = bif->max_addrs; + if (bridge_set_max_cache(bif, val->v.integer) < 0) + return (SNMP_ERR_GENERR); + return (SNMP_ERR_NOERROR); + + case LEAF_begemotBridgeTpLearnedEntryDiscards: + return (SNMP_ERR_NOT_WRITEABLE); + } + abort(); + + case SNMP_OP_ROLLBACK: + if ((bif = bridge_if_index_get(&val->var, sub)) == NULL) + return (SNMP_ERR_GENERR); + + switch (val->var.subs[sub - 1]) { + case LEAF_begemotBridgeTpAgingTime: + bridge_set_aging_time(bif, ctx->scratch->int1); + break; + + case LEAF_begemotBridgeTpMaxAddresses: + bridge_set_max_cache(bif, ctx->scratch->int1); + break; + } + return (SNMP_ERR_NOERROR); + + case SNMP_OP_COMMIT: + return (SNMP_ERR_NOERROR); + } + abort(); + +get: + switch (val->var.subs[sub - 1]) { + case LEAF_begemotBridgeTpLearnedEntryDiscards: + val->v.uint32 = bif->lrnt_drops; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotBridgeTpAgingTime: + val->v.integer = bif->age_time; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotBridgeTpMaxAddresses: + val->v.integer = bif->max_addrs; + return (SNMP_ERR_NOERROR); + } + + abort(); +} diff --git a/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_pf.c b/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_pf.c new file mode 100644 index 0000000..0ad8cbf --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_pf.c @@ -0,0 +1,116 @@ +/*- + * Copyright (c) 2006 Shteryana Shopova <syrinx@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Bridge MIB implementation for SNMPd. + * Bridge pfil controls. + * + * $FreeBSD$ + */ + +#include <sys/types.h> +#include <sys/sysctl.h> +#include <sys/socket.h> + +#include <net/ethernet.h> +#include <net/if.h> +#include <net/if_mib.h> +#include <net/if_types.h> + +#include <errno.h> +#include <string.h> +#include <stdlib.h> +#include <syslog.h> + +#include <bsnmp/snmpmod.h> +#include <bsnmp/snmp_mibII.h> + +#include "bridge_tree.h" +#include "bridge_snmp.h" + +static int +val2snmp_truth(uint8_t val) +{ + if (val == 0) + return (2); + + return (1); +} + +static int +snmp_truth2val(int32_t truth) +{ + if (truth == 2) + return (0); + else if (truth == 1) + return (1); + + return (-1); +} + +int +op_begemot_bridge_pf(struct snmp_context *ctx, struct snmp_value *val, + uint sub, uint iidx __unused, enum snmp_op op) +{ + int k_val; + + if (val->var.subs[sub - 1] > LEAF_begemotBridgeLayer2PfStatus) + return (SNMP_ERR_NOSUCHNAME); + + switch (op) { + case SNMP_OP_GETNEXT: + abort(); + case SNMP_OP_ROLLBACK: + bridge_do_pfctl(val->var.subs[sub - 1] - 1, + op, &(ctx->scratch->int1)); + return (SNMP_ERR_NOERROR); + + case SNMP_OP_COMMIT: + return (SNMP_ERR_NOERROR); + + case SNMP_OP_SET: + ctx->scratch->int1 = + bridge_get_pfval(val->var.subs[sub - 1]); + + if ((k_val = snmp_truth2val(val->v.integer)) < 0) + return (SNMP_ERR_BADVALUE); + return (SNMP_ERR_NOERROR); + + case SNMP_OP_GET: + switch (val->var.subs[sub - 1]) { + case LEAF_begemotBridgePfilStatus: + case LEAF_begemotBridgePfilMembers: + case LEAF_begemotBridgePfilIpOnly: + case LEAF_begemotBridgeLayer2PfStatus: + if (bridge_do_pfctl(val->var.subs[sub - 1] - 1, + op, &k_val) < 0) + return (SNMP_ERR_GENERR); + val->v.integer = val2snmp_truth(k_val); + return (SNMP_ERR_NOERROR); + } + abort(); + } + + abort(); +} diff --git a/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_port.c b/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_port.c new file mode 100644 index 0000000..0127cea --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_port.c @@ -0,0 +1,1513 @@ +/*- + * Copyright (c) 2006 Shteryana Shopova <syrinx@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Bridge MIB implementation for SNMPd. + * Bridge ports. + * + * $FreeBSD$ + */ + +#include <sys/queue.h> +#include <sys/socket.h> +#include <sys/types.h> + +#include <net/ethernet.h> +#include <net/if.h> +#include <net/if_mib.h> + +#include <assert.h> +#include <errno.h> +#include <stdarg.h> +#include <string.h> +#include <stdlib.h> +#include <syslog.h> + +#include <bsnmp/snmpmod.h> +#include <bsnmp/snmp_mibII.h> + +#include "bridge_tree.h" +#include "bridge_snmp.h" + +TAILQ_HEAD(bridge_ports, bridge_port); + +/* + * Free the bridge base ports list. + */ +static void +bridge_ports_free(struct bridge_ports *headp) +{ + struct bridge_port *bp; + + while ((bp = TAILQ_FIRST(headp)) != NULL) { + TAILQ_REMOVE(headp, bp, b_p); + free(bp); + } +} + +/* + * Free the bridge base ports from the base ports list, + * members of a specified bridge interface only. + */ +static void +bridge_port_memif_free(struct bridge_ports *headp, + struct bridge_if *bif) +{ + struct bridge_port *bp; + + while (bif->f_bp != NULL && bif->sysindex == bif->f_bp->sysindex) { + bp = TAILQ_NEXT(bif->f_bp, b_p); + TAILQ_REMOVE(headp, bif->f_bp, b_p); + free(bif->f_bp); + bif->f_bp = bp; + } +} + +/* + * Insert a port entry in the base port TAILQ starting to search + * for its place from the position of the first bridge port for the bridge + * interface. Update the first bridge port if necessary. + */ +static void +bridge_port_insert_at(struct bridge_ports *headp, + struct bridge_port *bp, struct bridge_port **f_bp) +{ + struct bridge_port *t1; + + assert(f_bp != NULL); + + for (t1 = *f_bp; + t1 != NULL && bp->sysindex == t1->sysindex; + t1 = TAILQ_NEXT(t1, b_p)) { + if (bp->if_idx < t1->if_idx) { + TAILQ_INSERT_BEFORE(t1, bp, b_p); + if (*f_bp == t1) + *f_bp = bp; + return; + } + } + + /* + * Handle the case when our first port was actually the + * last element of the TAILQ. + */ + if (t1 == NULL) + TAILQ_INSERT_TAIL(headp, bp, b_p); + else + TAILQ_INSERT_BEFORE(t1, bp, b_p); +} + +/* + * Find a port entry's position in the ports list according + * to it's parent bridge interface name. Returns a NULL if + * we should be at the TAILQ head, otherwise the entry after + * which we should be inserted. + */ +static struct bridge_port * +bridge_port_find_pos(struct bridge_ports *headp, uint32_t b_idx) +{ + uint32_t t_idx; + struct bridge_port *t1; + + if ((t1 = TAILQ_FIRST(headp)) == NULL || + bridge_compare_sysidx(b_idx, t1->sysindex) < 0) + return (NULL); + + t_idx = t1->sysindex; + + for (t1 = TAILQ_NEXT(t1, b_p); t1 != NULL; t1 = TAILQ_NEXT(t1, b_p)) { + if (t1->sysindex != t_idx) { + if (bridge_compare_sysidx(b_idx, t1->sysindex) < 0) + return (TAILQ_PREV(t1, bridge_ports, b_p)); + else + t_idx = t1->sysindex; + } + } + + if (t1 == NULL) + t1 = TAILQ_LAST(headp, bridge_ports); + + return (t1); +} + +/* + * Insert a bridge member interface in the ports TAILQ. + */ +static void +bridge_port_memif_insert(struct bridge_ports *headp, + struct bridge_port *bp, struct bridge_port **f_bp) +{ + struct bridge_port *temp; + + if (*f_bp != NULL) + bridge_port_insert_at(headp, bp, f_bp); + else { + temp = bridge_port_find_pos(headp, bp->sysindex); + + if (temp == NULL) + TAILQ_INSERT_HEAD(headp, bp, b_p); + else + TAILQ_INSERT_AFTER(headp, temp, bp, b_p); + *f_bp = bp; + } +} + +/* The global ports list. */ +static struct bridge_ports bridge_ports = TAILQ_HEAD_INITIALIZER(bridge_ports); +static time_t ports_list_age; + +void +bridge_ports_update_listage(void) +{ + ports_list_age = time(NULL); +} + +void +bridge_ports_fini(void) +{ + bridge_ports_free(&bridge_ports); +} + +void +bridge_members_free(struct bridge_if *bif) +{ + bridge_port_memif_free(&bridge_ports, bif); +} + +/* + * Find the first port in the ports list. + */ +static struct bridge_port * +bridge_port_first(void) +{ + return (TAILQ_FIRST(&bridge_ports)); +} + +/* + * Find the next port in the ports list. + */ +static struct bridge_port * +bridge_port_next(struct bridge_port *bp) +{ + return (TAILQ_NEXT(bp, b_p)); +} + +/* + * Find the first member of the specified bridge interface. + */ +struct bridge_port * +bridge_port_bif_first(struct bridge_if *bif) +{ + return (bif->f_bp); +} + +/* + * Find the next member of the specified bridge interface. + */ +struct bridge_port * +bridge_port_bif_next(struct bridge_port *bp) +{ + struct bridge_port *bp_next; + + if ((bp_next = TAILQ_NEXT(bp, b_p)) == NULL || + bp_next->sysindex != bp->sysindex) + return (NULL); + + return (bp_next); +} + +/* + * Remove a bridge port from the ports list. + */ +void +bridge_port_remove(struct bridge_port *bp, struct bridge_if *bif) +{ + if (bif->f_bp == bp) + bif->f_bp = bridge_port_bif_next(bp); + + TAILQ_REMOVE(&bridge_ports, bp, b_p); + free(bp); +} + +/* + * Allocate memory for a new bridge port and insert it + * in the base ports list. Return a pointer to the port's + * structure in case we want to do anything else with it. + */ +struct bridge_port * +bridge_new_port(struct mibif *mif, struct bridge_if *bif) +{ + struct bridge_port *bp; + + if ((bp = (struct bridge_port *) malloc(sizeof(*bp))) == NULL) { + syslog(LOG_ERR, "bridge new member: failed: %s", + strerror(errno)); + return (NULL); + } + + bzero(bp, sizeof(*bp)); + + bp->sysindex = bif->sysindex; + bp->if_idx = mif->index; + bp->port_no = mif->sysindex; + strlcpy(bp->p_name, mif->name, IFNAMSIZ); + bp->circuit = oid_zeroDotZero; + + /* + * Initialize all rstpMib specific values to false/default. + * These will be set to their true values later if the bridge + * supports RSTP. + */ + bp->proto_migr = TruthValue_false; + bp->admin_edge = TruthValue_false; + bp->oper_edge = TruthValue_false; + bp->oper_ptp = TruthValue_false; + bp->admin_ptp = StpPortAdminPointToPointType_auto; + + bridge_port_memif_insert(&bridge_ports, bp, &(bif->f_bp)); + + return (bp); +} + +/* + * Update our info from the corresponding mibII interface info. + */ +void +bridge_port_getinfo_mibif(struct mibif *m_if, struct bridge_port *bp) +{ + bp->max_info = m_if->mib.ifmd_data.ifi_mtu; + bp->in_frames = m_if->mib.ifmd_data.ifi_ipackets; + bp->out_frames = m_if->mib.ifmd_data.ifi_opackets; + bp->in_drops = m_if->mib.ifmd_data.ifi_iqdrops; +} + +/* + * Find a port, whose SNMP's mibII ifIndex matches one of the ports, + * members of the specified bridge interface. + */ +struct bridge_port * +bridge_port_find(int32_t if_idx, struct bridge_if *bif) +{ + struct bridge_port *bp; + + for (bp = bif->f_bp; bp != NULL; bp = TAILQ_NEXT(bp, b_p)) { + if (bp->sysindex != bif->sysindex) { + bp = NULL; + break; + } + + if (bp->if_idx == if_idx) + break; + } + + return (bp); +} + +void +bridge_ports_dump(struct bridge_if *bif) +{ + struct bridge_port *bp; + + for (bp = bridge_port_bif_first(bif); bp != NULL; + bp = bridge_port_bif_next(bp)) { + syslog(LOG_ERR, "memif - %s, index - %d", + bp->p_name, bp->port_no); + } +} + +/* + * RFC4188 specifics. + */ +int +op_dot1d_base_port(struct snmp_context *c __unused, struct snmp_value *val, + uint sub, uint iidx __unused, enum snmp_op op) +{ + struct bridge_if *bif; + struct bridge_port *bp; + + if ((bif = bridge_get_default()) == NULL) + return (SNMP_ERR_NOSUCHNAME); + + if (time(NULL) - bif->ports_age > bridge_get_data_maxage() && + bridge_update_memif(bif) <= 0) + return (SNMP_ERR_NOSUCHNAME); + + switch (op) { + case SNMP_OP_GET: + if (val->var.len - sub != 1) + return (SNMP_ERR_NOSUCHNAME); + if ((bp = bridge_port_find(val->var.subs[sub], + bif)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + goto get; + + case SNMP_OP_GETNEXT: + if (val->var.len - sub == 0) { + if ((bp = bridge_port_bif_first(bif)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + } else { + if ((bp = bridge_port_find(val->var.subs[sub], + bif)) == NULL || + (bp = bridge_port_bif_next(bp)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + } + val->var.len = sub + 1; + val->var.subs[sub] = bp->port_no; + goto get; + + case SNMP_OP_SET: + return (SNMP_ERR_NOT_WRITEABLE); + + case SNMP_OP_ROLLBACK: + case SNMP_OP_COMMIT: + break; + } + abort(); + +get: + switch (val->var.subs[sub - 1]) { + case LEAF_dot1dBasePort: + val->v.integer = bp->port_no; + return (SNMP_ERR_NOERROR); + + case LEAF_dot1dBasePortIfIndex: + val->v.integer = bp->if_idx; + return (SNMP_ERR_NOERROR); + + case LEAF_dot1dBasePortCircuit: + val->v.oid = bp->circuit; + return (SNMP_ERR_NOERROR); + + case LEAF_dot1dBasePortDelayExceededDiscards: + val->v.uint32 = bp->dly_ex_drops; + return (SNMP_ERR_NOERROR); + + case LEAF_dot1dBasePortMtuExceededDiscards: + val->v.uint32 = bp->dly_mtu_drops; + return (SNMP_ERR_NOERROR); + } + + abort(); +} + +int +op_dot1d_stp_port(struct snmp_context *ctx, struct snmp_value *val, + uint sub, uint iidx __unused, enum snmp_op op) +{ + struct bridge_if *bif; + struct bridge_port *bp; + + if ((bif = bridge_get_default()) == NULL) + return (SNMP_ERR_NOSUCHNAME); + + if (time(NULL) - bif->ports_age > bridge_get_data_maxage() && + bridge_update_memif(bif) <= 0) + return (SNMP_ERR_NOSUCHNAME); + + switch (op) { + case SNMP_OP_GET: + if (val->var.len - sub != 1) + return (SNMP_ERR_NOSUCHNAME); + if ((bp = bridge_port_find(val->var.subs[sub], + bif)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + goto get; + + case SNMP_OP_GETNEXT: + if (val->var.len - sub == 0) { + if ((bp = bridge_port_bif_first(bif)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + } else { + if ((bp = bridge_port_find(val->var.subs[sub], + bif)) == NULL || + (bp = bridge_port_bif_next(bp)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + } + val->var.len = sub + 1; + val->var.subs[sub] = bp->port_no; + goto get; + + case SNMP_OP_SET: + if (val->var.len - sub != 1) + return (SNMP_ERR_NOSUCHNAME); + if ((bp = bridge_port_find(val->var.subs[sub], + bif)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + + switch (val->var.subs[sub - 1]) { + case LEAF_dot1dStpPortPriority: + if (val->v.integer < 0 || val->v.integer > 255) + return (SNMP_ERR_WRONG_VALUE); + + ctx->scratch->int1 = bp->priority; + if (bridge_port_set_priority(bif->bif_name, bp, + val->v.integer) < 0) + return (SNMP_ERR_GENERR); + return (SNMP_ERR_NOERROR); + + case LEAF_dot1dStpPortEnable: + if (val->v.integer != dot1dStpPortEnable_enabled && + val->v.integer != dot1dStpPortEnable_disabled) + return (SNMP_ERR_WRONG_VALUE); + + ctx->scratch->int1 = bp->enable; + if (bridge_port_set_stp_enable(bif->bif_name, + bp, val->v.integer) < 0) + return (SNMP_ERR_GENERR); + return (SNMP_ERR_NOERROR); + + case LEAF_dot1dStpPortPathCost: + if (val->v.integer < SNMP_PORT_MIN_PATHCOST || + val->v.integer > SNMP_PORT_MAX_PATHCOST) + return (SNMP_ERR_WRONG_VALUE); + + ctx->scratch->int1 = bp->path_cost; + if (bridge_port_set_path_cost(bif->bif_name, bp, + val->v.integer) < 0) + return (SNMP_ERR_GENERR); + return (SNMP_ERR_NOERROR); + + case LEAF_dot1dStpPort: + case LEAF_dot1dStpPortState: + case LEAF_dot1dStpPortDesignatedRoot: + case LEAF_dot1dStpPortDesignatedCost: + case LEAF_dot1dStpPortDesignatedBridge: + case LEAF_dot1dStpPortDesignatedPort: + case LEAF_dot1dStpPortForwardTransitions: + return (SNMP_ERR_NOT_WRITEABLE); + } + abort(); + + case SNMP_OP_ROLLBACK: + if ((bp = bridge_port_find(val->var.subs[sub], + bif)) == NULL) + return (SNMP_ERR_GENERR); + switch (val->var.subs[sub - 1]) { + case LEAF_dot1dStpPortPriority: + bridge_port_set_priority(bif->bif_name, bp, + ctx->scratch->int1); + break; + case LEAF_dot1dStpPortEnable: + bridge_port_set_stp_enable(bif->bif_name, bp, + ctx->scratch->int1); + break; + case LEAF_dot1dStpPortPathCost: + bridge_port_set_path_cost(bif->bif_name, bp, + ctx->scratch->int1); + break; + } + return (SNMP_ERR_NOERROR); + + case SNMP_OP_COMMIT: + return (SNMP_ERR_NOERROR); + } + abort(); + +get: + switch (val->var.subs[sub - 1]) { + case LEAF_dot1dStpPort: + val->v.integer = bp->port_no; + return (SNMP_ERR_NOERROR); + + case LEAF_dot1dStpPortPriority: + val->v.integer = bp->priority; + return (SNMP_ERR_NOERROR); + + case LEAF_dot1dStpPortState: + val->v.integer = bp->state; + return (SNMP_ERR_NOERROR); + + case LEAF_dot1dStpPortEnable: + val->v.integer = bp->enable; + return (SNMP_ERR_NOERROR); + + case LEAF_dot1dStpPortPathCost: + val->v.integer = bp->path_cost; + return (SNMP_ERR_NOERROR); + + case LEAF_dot1dStpPortDesignatedRoot: + return (string_get(val, bp->design_root, + SNMP_BRIDGE_ID_LEN)); + + case LEAF_dot1dStpPortDesignatedCost: + val->v.integer = bp->design_cost; + return (SNMP_ERR_NOERROR); + + case LEAF_dot1dStpPortDesignatedBridge: + return (string_get(val, bp->design_bridge, + SNMP_BRIDGE_ID_LEN)); + + case LEAF_dot1dStpPortDesignatedPort: + return (string_get(val, bp->design_port, 2)); + + case LEAF_dot1dStpPortForwardTransitions: + val->v.uint32 = bp->fwd_trans; + return (SNMP_ERR_NOERROR); + } + + abort(); +} + +int +op_dot1d_stp_ext_port(struct snmp_context *ctx, struct snmp_value *val, + uint sub, uint iidx __unused, enum snmp_op op) +{ + struct bridge_if *bif; + struct bridge_port *bp; + + if ((bif = bridge_get_default()) == NULL) + return (SNMP_ERR_NOSUCHNAME); + + if (time(NULL) - bif->ports_age > bridge_get_data_maxage() && + bridge_update_memif(bif) <= 0) + return (SNMP_ERR_NOSUCHNAME); + + switch (op) { + case SNMP_OP_GET: + if (val->var.len - sub != 1) + return (SNMP_ERR_NOSUCHNAME); + if ((bp = bridge_port_find(val->var.subs[sub], + bif)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + goto get; + + case SNMP_OP_GETNEXT: + if (val->var.len - sub == 0) { + if ((bp = bridge_port_bif_first(bif)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + } else { + if ((bp = bridge_port_find(val->var.subs[sub], + bif)) == NULL || + (bp = bridge_port_bif_next(bp)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + } + val->var.len = sub + 1; + val->var.subs[sub] = bp->port_no; + goto get; + + case SNMP_OP_SET: + if (val->var.len - sub != 1) + return (SNMP_ERR_NOSUCHNAME); + if ((bp = bridge_port_find(val->var.subs[sub], + bif)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + + switch (val->var.subs[sub - 1]) { + case LEAF_dot1dStpPortAdminEdgePort: + if (val->v.integer != TruthValue_true && + val->v.integer != TruthValue_false) + return (SNMP_ERR_WRONG_VALUE); + + ctx->scratch->int1 = bp->admin_edge; + if (bridge_port_set_admin_edge(bif->bif_name, bp, + val->v.integer) < 0) + return (SNMP_ERR_GENERR); + return (SNMP_ERR_NOERROR); + + case LEAF_dot1dStpPortAdminPointToPoint: + if (val->v.integer < 0 || val->v.integer > + StpPortAdminPointToPointType_auto) + return (SNMP_ERR_WRONG_VALUE); + + ctx->scratch->int1 = bp->admin_ptp; + if (bridge_port_set_admin_ptp(bif->bif_name, bp, + val->v.integer) < 0) + return (SNMP_ERR_GENERR); + return (SNMP_ERR_NOERROR); + + case LEAF_dot1dStpPortAdminPathCost: + if (val->v.integer < SNMP_PORT_MIN_PATHCOST || + val->v.integer > SNMP_PORT_MAX_PATHCOST) + return (SNMP_ERR_WRONG_VALUE); + + ctx->scratch->int1 = bp->admin_path_cost; + if (bridge_port_set_path_cost(bif->bif_name, bp, + val->v.integer) < 0) + return (SNMP_ERR_GENERR); + return (SNMP_ERR_NOERROR); + + case LEAF_dot1dStpPortProtocolMigration: + case LEAF_dot1dStpPortOperEdgePort: + case LEAF_dot1dStpPortOperPointToPoint: + return (SNMP_ERR_NOT_WRITEABLE); + } + abort(); + + case SNMP_OP_ROLLBACK: + if ((bp = bridge_port_find(val->var.subs[sub], + bif)) == NULL) + return (SNMP_ERR_GENERR); + + switch (val->var.subs[sub - 1]) { + case LEAF_dot1dStpPortAdminEdgePort: + bridge_port_set_admin_edge(bif->bif_name, bp, + ctx->scratch->int1); + break; + case LEAF_dot1dStpPortAdminPointToPoint: + bridge_port_set_admin_ptp(bif->bif_name, bp, + ctx->scratch->int1); + break; + case LEAF_dot1dStpPortAdminPathCost: + bridge_port_set_path_cost(bif->bif_name, bp, + ctx->scratch->int1); + break; + } + return (SNMP_ERR_NOERROR); + + case SNMP_OP_COMMIT: + return (SNMP_ERR_NOERROR); + } + abort(); + +get: + switch (val->var.subs[sub - 1]) { + case LEAF_dot1dStpPortProtocolMigration: + val->v.integer = bp->proto_migr; + return (SNMP_ERR_NOERROR); + + case LEAF_dot1dStpPortAdminEdgePort: + val->v.integer = bp->admin_edge; + return (SNMP_ERR_NOERROR); + + case LEAF_dot1dStpPortOperEdgePort: + val->v.integer = bp->oper_edge; + return (SNMP_ERR_NOERROR); + + case LEAF_dot1dStpPortAdminPointToPoint: + val->v.integer = bp->admin_ptp; + return (SNMP_ERR_NOERROR); + + case LEAF_dot1dStpPortOperPointToPoint: + val->v.integer = bp->oper_ptp; + return (SNMP_ERR_NOERROR); + + case LEAF_dot1dStpPortAdminPathCost: + val->v.integer = bp->admin_path_cost; + return (SNMP_ERR_NOERROR); + } + + abort(); +} + +int +op_dot1d_tp_port(struct snmp_context *c __unused, struct snmp_value *val, + uint sub, uint iidx __unused, enum snmp_op op) +{ + struct bridge_if *bif; + struct bridge_port *bp; + + if ((bif = bridge_get_default()) == NULL) + return (SNMP_ERR_NOSUCHNAME); + + if (time(NULL) - bif->ports_age > bridge_get_data_maxage() && + bridge_update_memif(bif) <= 0) + return (SNMP_ERR_NOSUCHNAME); + + switch (op) { + case SNMP_OP_GET: + if (val->var.len - sub != 1) + return (SNMP_ERR_NOSUCHNAME); + if ((bp = bridge_port_find(val->var.subs[sub], + bif)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + goto get; + + case SNMP_OP_GETNEXT: + if (val->var.len - sub == 0) { + if ((bp = bridge_port_bif_first(bif)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + } else { + if ((bp = bridge_port_find(val->var.subs[sub], + bif)) == NULL || + (bp = bridge_port_bif_next(bp)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + } + val->var.len = sub + 1; + val->var.subs[sub] = bp->port_no; + goto get; + + case SNMP_OP_SET: + return (SNMP_ERR_NOT_WRITEABLE); + + case SNMP_OP_ROLLBACK: + case SNMP_OP_COMMIT: + break; + } + abort(); + +get: + switch (val->var.subs[sub - 1]) { + case LEAF_dot1dTpPort: + val->v.integer = bp->port_no; + return (SNMP_ERR_NOERROR); + + case LEAF_dot1dTpPortMaxInfo: + val->v.integer = bp->max_info; + return (SNMP_ERR_NOERROR); + + case LEAF_dot1dTpPortInFrames: + val->v.uint32 = bp->in_frames; + return (SNMP_ERR_NOERROR); + + case LEAF_dot1dTpPortOutFrames: + val->v.uint32 = bp->out_frames; + return (SNMP_ERR_NOERROR); + + case LEAF_dot1dTpPortInDiscards: + val->v.uint32 = bp->in_drops; + return (SNMP_ERR_NOERROR); + } + + abort(); +} + +/* + * Private BEGEMOT-BRIDGE-MIB specifics. + */ + +/* + * Construct a bridge port entry index. + */ +static int +bridge_port_index_append(struct asn_oid *oid, uint sub, + const struct bridge_port *bp) +{ + uint i; + const char *b_name; + + if ((b_name = bridge_if_find_name(bp->sysindex)) == NULL) + return (-1); + + oid->len = sub + strlen(b_name) + 1 + 1; + oid->subs[sub] = strlen(b_name); + + for (i = 1; i <= strlen(b_name); i++) + oid->subs[sub + i] = b_name[i - 1]; + + oid->subs[sub + i] = bp->port_no; + + return (0); +} + +/* + * Get the port entry from an entry's index. + */ +static struct bridge_port * +bridge_port_index_get(const struct asn_oid *oid, uint sub, int8_t status) +{ + uint i; + int32_t port_no; + char bif_name[IFNAMSIZ]; + struct bridge_if *bif; + struct bridge_port *bp; + + if (oid->len - sub != oid->subs[sub] + 2 || + oid->subs[sub] >= IFNAMSIZ) + return (NULL); + + for (i = 0; i < oid->subs[sub]; i++) + bif_name[i] = oid->subs[sub + i + 1]; + bif_name[i] = '\0'; + + port_no = oid->subs[sub + i + 1]; + + if ((bif = bridge_if_find_ifname(bif_name)) == NULL) + return (NULL); + + if ((bp = bridge_port_find(port_no, bif)) == NULL || + (status == 0 && bp->status != RowStatus_active)) + return (NULL); + + return (bp); +} + +/* + * Get the next port entry from an entry's index. + */ +static struct bridge_port * +bridge_port_index_getnext(const struct asn_oid *oid, uint sub, int8_t status) +{ + uint i; + int32_t port_no; + char bif_name[IFNAMSIZ]; + struct bridge_if *bif; + struct bridge_port *bp; + + if (oid->len - sub == 0) + bp = bridge_port_first(); + else { + if (oid->len - sub != oid->subs[sub] + 2 || + oid->subs[sub] >= IFNAMSIZ) + return (NULL); + + for (i = 0; i < oid->subs[sub]; i++) + bif_name[i] = oid->subs[sub + i + 1]; + bif_name[i] = '\0'; + + port_no = oid->subs[sub + i + 1]; + + if ((bif = bridge_if_find_ifname(bif_name)) == NULL || + (bp = bridge_port_find(port_no, bif)) == NULL) + return (NULL); + + bp = bridge_port_next(bp); + } + + if (status == 1) + return (bp); + + while (bp != NULL) { + if (bp->status == RowStatus_active) + break; + bp = bridge_port_next(bp); + } + + return (bp); +} + +/* + * Read the bridge name and port index from a ASN OID structure. + */ +static int +bridge_port_index_decode(const struct asn_oid *oid, uint sub, + char *b_name, int32_t *idx) +{ + uint i; + + if (oid->len - sub != oid->subs[sub] + 2 || + oid->subs[sub] >= IFNAMSIZ) + return (-1); + + for (i = 0; i < oid->subs[sub]; i++) + b_name[i] = oid->subs[sub + i + 1]; + b_name[i] = '\0'; + + *idx = oid->subs[sub + i + 1]; + return (0); +} + +static int +bridge_port_set_status(struct snmp_context *ctx, + struct snmp_value *val, uint sub) +{ + int32_t if_idx; + char b_name[IFNAMSIZ]; + struct bridge_if *bif; + struct bridge_port *bp; + struct mibif *mif; + + if (bridge_port_index_decode(&val->var, sub, b_name, &if_idx) < 0) + return (SNMP_ERR_INCONS_VALUE); + + if ((bif = bridge_if_find_ifname(b_name)) == NULL || + (mif = mib_find_if(if_idx)) == NULL) + return (SNMP_ERR_INCONS_VALUE); + + bp = bridge_port_find(if_idx, bif); + + switch (val->v.integer) { + case RowStatus_active: + if (bp == NULL) + return (SNMP_ERR_INCONS_VALUE); + + if (bp->span_enable == 0) + return (SNMP_ERR_INCONS_VALUE); + + ctx->scratch->int1 = bp->status; + bp->status = RowStatus_active; + break; + + case RowStatus_notInService: + if (bp == NULL || bp->span_enable == 0 || + bp->status == RowStatus_active) + return (SNMP_ERR_INCONS_VALUE); + + ctx->scratch->int1 = bp->status; + bp->status = RowStatus_notInService; + + case RowStatus_notReady: + /* FALLTHROUGH */ + case RowStatus_createAndGo: + return (SNMP_ERR_INCONS_VALUE); + + case RowStatus_createAndWait: + if (bp != NULL) + return (SNMP_ERR_INCONS_VALUE); + + if ((bp = bridge_new_port(mif, bif)) == NULL) + return (SNMP_ERR_GENERR); + + ctx->scratch->int1 = RowStatus_destroy; + bp->status = RowStatus_notReady; + break; + + case RowStatus_destroy: + if (bp == NULL) + return (SNMP_ERR_INCONS_VALUE); + + ctx->scratch->int1 = bp->status; + bp->status = RowStatus_destroy; + break; + } + + return (SNMP_ERR_NOERROR); +} + +static int +bridge_port_rollback_status(struct snmp_context *ctx, + struct snmp_value *val, uint sub) +{ + int32_t if_idx; + char b_name[IFNAMSIZ]; + struct bridge_if *bif; + struct bridge_port *bp; + + if (bridge_port_index_decode(&val->var, sub, b_name, &if_idx) < 0) + return (SNMP_ERR_GENERR); + + if ((bif = bridge_if_find_ifname(b_name)) == NULL || + (bp = bridge_port_find(if_idx, bif)) == NULL) + return (SNMP_ERR_GENERR); + + if (ctx->scratch->int1 == RowStatus_destroy) + bridge_port_remove(bp, bif); + else + bp->status = ctx->scratch->int1; + + return (SNMP_ERR_NOERROR); +} + +static int +bridge_port_commit_status(struct snmp_value *val, uint sub) +{ + int32_t if_idx; + char b_name[IFNAMSIZ]; + struct bridge_if *bif; + struct bridge_port *bp; + + if (bridge_port_index_decode(&val->var, sub, b_name, &if_idx) < 0) + return (SNMP_ERR_GENERR); + + if ((bif = bridge_if_find_ifname(b_name)) == NULL || + (bp = bridge_port_find(if_idx, bif)) == NULL) + return (SNMP_ERR_GENERR); + + switch (bp->status) { + case RowStatus_active: + if (bridge_port_addm(bp, b_name) < 0) + return (SNMP_ERR_COMMIT_FAILED); + break; + + case RowStatus_destroy: + if (bridge_port_delm(bp, b_name) < 0) + return (SNMP_ERR_COMMIT_FAILED); + bridge_port_remove(bp, bif); + break; + } + + return (SNMP_ERR_NOERROR); +} + +static int +bridge_port_set_span_enable(struct snmp_context *ctx, + struct snmp_value *val, uint sub) +{ + int32_t if_idx; + char b_name[IFNAMSIZ]; + struct bridge_if *bif; + struct bridge_port *bp; + struct mibif *mif; + + if (val->v.integer != begemotBridgeBaseSpanEnabled_enabled && + val->v.integer != begemotBridgeBaseSpanEnabled_disabled) + return (SNMP_ERR_BADVALUE); + + if (bridge_port_index_decode(&val->var, sub, b_name, &if_idx) < 0) + return (SNMP_ERR_INCONS_VALUE); + + if ((bif = bridge_if_find_ifname(b_name)) == NULL) + return (SNMP_ERR_INCONS_VALUE); + + if ((bp = bridge_port_find(if_idx, bif)) == NULL) { + if ((mif = mib_find_if(if_idx)) == NULL) + return (SNMP_ERR_INCONS_VALUE); + + if ((bp = bridge_new_port(mif, bif)) == NULL) + return (SNMP_ERR_GENERR); + + ctx->scratch->int1 = RowStatus_destroy; + } else if (bp->status == RowStatus_active) { + return (SNMP_ERR_INCONS_VALUE); + } else { + ctx->scratch->int1 = bp->status; + } + + bp->span_enable = val->v.integer; + bp->status = RowStatus_notInService; + + return (SNMP_ERR_NOERROR); +} + +int +op_begemot_base_port(struct snmp_context *ctx, struct snmp_value *val, + uint sub, uint iidx __unused, enum snmp_op op) +{ + int8_t status, which; + const char *bname; + struct bridge_port *bp; + + if (time(NULL) - ports_list_age > bridge_get_data_maxage()) + bridge_update_all_ports(); + + which = val->var.subs[sub - 1]; + status = 0; + + switch (op) { + case SNMP_OP_GET: + if (which == LEAF_begemotBridgeBaseSpanEnabled || + which == LEAF_begemotBridgeBasePortStatus) + status = 1; + if ((bp = bridge_port_index_get(&val->var, sub, + status)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + goto get; + + case SNMP_OP_GETNEXT: + if (which == LEAF_begemotBridgeBaseSpanEnabled || + which == LEAF_begemotBridgeBasePortStatus) + status = 1; + if ((bp = bridge_port_index_getnext(&val->var, sub, + status)) == NULL || + bridge_port_index_append(&val->var, sub, bp) < 0) + return (SNMP_ERR_NOSUCHNAME); + goto get; + + case SNMP_OP_SET: + switch (which) { + case LEAF_begemotBridgeBaseSpanEnabled: + return (bridge_port_set_span_enable(ctx, val, sub)); + + case LEAF_begemotBridgeBasePortStatus: + return (bridge_port_set_status(ctx, val, sub)); + + case LEAF_begemotBridgeBasePortPrivate: + if ((bp = bridge_port_index_get(&val->var, sub, + status)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + if ((bname = bridge_if_find_name(bp->sysindex)) == NULL) + return (SNMP_ERR_GENERR); + ctx->scratch->int1 = bp->priv_set; + return (bridge_port_set_private(bname, bp, + val->v.integer)); + + case LEAF_begemotBridgeBasePort: + case LEAF_begemotBridgeBasePortIfIndex: + case LEAF_begemotBridgeBasePortDelayExceededDiscards: + case LEAF_begemotBridgeBasePortMtuExceededDiscards: + return (SNMP_ERR_NOT_WRITEABLE); + } + abort(); + + case SNMP_OP_ROLLBACK: + switch (which) { + case LEAF_begemotBridgeBaseSpanEnabled: + /* FALLTHROUGH */ + case LEAF_begemotBridgeBasePortStatus: + return (bridge_port_rollback_status(ctx, val, sub)); + case LEAF_begemotBridgeBasePortPrivate: + if ((bp = bridge_port_index_get(&val->var, sub, + status)) == NULL) + return (SNMP_ERR_GENERR); + if ((bname = bridge_if_find_name(bp->sysindex)) == NULL) + return (SNMP_ERR_GENERR); + return (bridge_port_set_private(bname, bp, + ctx->scratch->int1)); + } + return (SNMP_ERR_NOERROR); + + case SNMP_OP_COMMIT: + if (which == LEAF_begemotBridgeBasePortStatus) + return (bridge_port_commit_status(val, sub)); + + return (SNMP_ERR_NOERROR); + } + abort(); + +get: + switch (which) { + case LEAF_begemotBridgeBasePort: + val->v.integer = bp->port_no; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotBridgeBasePortIfIndex: + val->v.integer = bp->if_idx; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotBridgeBaseSpanEnabled: + val->v.integer = bp->span_enable; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotBridgeBasePortDelayExceededDiscards: + val->v.uint32 = bp->dly_ex_drops; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotBridgeBasePortMtuExceededDiscards: + val->v.uint32 = bp->dly_mtu_drops; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotBridgeBasePortStatus: + val->v.integer = bp->status; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotBridgeBasePortPrivate: + val->v.integer = bp->priv_set; + return (SNMP_ERR_NOERROR); + } + + abort(); +} + +int +op_begemot_stp_port(struct snmp_context *ctx, struct snmp_value *val, + uint sub, uint iidx __unused, enum snmp_op op) +{ + struct bridge_port *bp; + const char *b_name; + + if (time(NULL) - ports_list_age > bridge_get_data_maxage()) + bridge_update_all_ports(); + + switch (op) { + case SNMP_OP_GET: + if ((bp = bridge_port_index_get(&val->var, sub, 0)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + goto get; + + case SNMP_OP_GETNEXT: + if ((bp = bridge_port_index_getnext(&val->var, sub, 0)) == + NULL || bridge_port_index_append(&val->var, sub, bp) < 0) + return (SNMP_ERR_NOSUCHNAME); + goto get; + + case SNMP_OP_SET: + if ((bp = bridge_port_index_get(&val->var, sub, 0)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + if ((b_name = bridge_if_find_name(bp->sysindex)) == NULL) + return (SNMP_ERR_GENERR); + + switch (val->var.subs[sub - 1]) { + case LEAF_begemotBridgeStpPortPriority: + if (val->v.integer < 0 || val->v.integer > 255) + return (SNMP_ERR_WRONG_VALUE); + + ctx->scratch->int1 = bp->priority; + if (bridge_port_set_priority(b_name, bp, + val->v.integer) < 0) + return (SNMP_ERR_GENERR); + return (SNMP_ERR_NOERROR); + + case LEAF_begemotBridgeStpPortEnable: + if (val->v.integer != + begemotBridgeStpPortEnable_enabled || + val->v.integer != + begemotBridgeStpPortEnable_disabled) + return (SNMP_ERR_WRONG_VALUE); + + ctx->scratch->int1 = bp->enable; + if (bridge_port_set_stp_enable(b_name, bp, + val->v.integer) < 0) + return (SNMP_ERR_GENERR); + return (SNMP_ERR_NOERROR); + + case LEAF_begemotBridgeStpPortPathCost: + if (val->v.integer < SNMP_PORT_MIN_PATHCOST || + val->v.integer > SNMP_PORT_MAX_PATHCOST) + return (SNMP_ERR_WRONG_VALUE); + + ctx->scratch->int1 = bp->path_cost; + if (bridge_port_set_path_cost(b_name, bp, + val->v.integer) < 0) + return (SNMP_ERR_GENERR); + return (SNMP_ERR_NOERROR); + + case LEAF_begemotBridgeStpPort: + case LEAF_begemotBridgeStpPortState: + case LEAF_begemotBridgeStpPortDesignatedRoot: + case LEAF_begemotBridgeStpPortDesignatedCost: + case LEAF_begemotBridgeStpPortDesignatedBridge: + case LEAF_begemotBridgeStpPortDesignatedPort: + case LEAF_begemotBridgeStpPortForwardTransitions: + return (SNMP_ERR_NOT_WRITEABLE); + } + abort(); + + case SNMP_OP_ROLLBACK: + if ((bp = bridge_port_index_get(&val->var, sub, 0)) == NULL || + (b_name = bridge_if_find_name(bp->sysindex)) == NULL) + return (SNMP_ERR_GENERR); + + switch (val->var.subs[sub - 1]) { + case LEAF_begemotBridgeStpPortPriority: + bridge_port_set_priority(b_name, bp, + ctx->scratch->int1); + break; + case LEAF_begemotBridgeStpPortEnable: + bridge_port_set_stp_enable(b_name, bp, + ctx->scratch->int1); + break; + case LEAF_begemotBridgeStpPortPathCost: + bridge_port_set_path_cost(b_name, bp, + ctx->scratch->int1); + break; + } + return (SNMP_ERR_NOERROR); + + case SNMP_OP_COMMIT: + return (SNMP_ERR_NOERROR); + } + abort(); + +get: + switch (val->var.subs[sub - 1]) { + case LEAF_begemotBridgeStpPort: + val->v.integer = bp->port_no; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotBridgeStpPortPriority: + val->v.integer = bp->priority; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotBridgeStpPortState: + val->v.integer = bp->state; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotBridgeStpPortEnable: + val->v.integer = bp->enable; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotBridgeStpPortPathCost: + val->v.integer = bp->path_cost; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotBridgeStpPortDesignatedRoot: + return (string_get(val, bp->design_root, SNMP_BRIDGE_ID_LEN)); + + case LEAF_begemotBridgeStpPortDesignatedCost: + val->v.integer = bp->design_cost; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotBridgeStpPortDesignatedBridge: + return (string_get(val, bp->design_bridge, SNMP_BRIDGE_ID_LEN)); + + case LEAF_begemotBridgeStpPortDesignatedPort: + return (string_get(val, bp->design_port, 2)); + + case LEAF_begemotBridgeStpPortForwardTransitions: + val->v.uint32 = bp->fwd_trans; + return (SNMP_ERR_NOERROR); + } + + abort(); +} + +int +op_begemot_stp_ext_port(struct snmp_context *ctx, struct snmp_value *val, + uint sub, uint iidx __unused, enum snmp_op op) +{ + struct bridge_port *bp; + const char *b_name; + + if (time(NULL) - ports_list_age > bridge_get_data_maxage()) + bridge_update_all_ports(); + + switch (op) { + case SNMP_OP_GET: + if ((bp = bridge_port_index_get(&val->var, sub, 0)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + goto get; + + case SNMP_OP_GETNEXT: + if ((bp = bridge_port_index_getnext(&val->var, sub, 0)) == + NULL || bridge_port_index_append(&val->var, sub, bp) < 0) + return (SNMP_ERR_NOSUCHNAME); + goto get; + + case SNMP_OP_SET: + if ((bp = bridge_port_index_get(&val->var, sub, 0)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + if ((b_name = bridge_if_find_name(bp->sysindex)) == NULL) + return (SNMP_ERR_GENERR); + + switch (val->var.subs[sub - 1]) { + case LEAF_begemotBridgeStpPortAdminEdgePort: + if (val->v.integer != TruthValue_true && + val->v.integer != TruthValue_false) + return (SNMP_ERR_WRONG_VALUE); + + ctx->scratch->int1 = bp->admin_edge; + if (bridge_port_set_admin_edge(b_name, bp, + val->v.integer) < 0) + return (SNMP_ERR_GENERR); + return (SNMP_ERR_NOERROR); + + case LEAF_begemotBridgeStpPortAdminPointToPoint: + if (val->v.integer < 0 || val->v.integer > + StpPortAdminPointToPointType_auto) + return (SNMP_ERR_WRONG_VALUE); + + ctx->scratch->int1 = bp->admin_ptp; + if (bridge_port_set_admin_ptp(b_name, bp, + val->v.integer) < 0) + return (SNMP_ERR_GENERR); + return (SNMP_ERR_NOERROR); + + case LEAF_begemotBridgeStpPortAdminPathCost: + if (val->v.integer < SNMP_PORT_MIN_PATHCOST || + val->v.integer > SNMP_PORT_MAX_PATHCOST) + return (SNMP_ERR_WRONG_VALUE); + + ctx->scratch->int1 = bp->admin_path_cost; + if (bridge_port_set_path_cost(b_name, bp, + val->v.integer) < 0) + return (SNMP_ERR_GENERR); + return (SNMP_ERR_NOERROR); + + case LEAF_begemotBridgeStpPortProtocolMigration: + case LEAF_begemotBridgeStpPortOperEdgePort: + case LEAF_begemotBridgeStpPortOperPointToPoint: + return (SNMP_ERR_NOT_WRITEABLE); + } + abort(); + + case SNMP_OP_ROLLBACK: + if ((bp = bridge_port_index_get(&val->var, sub, 0)) == NULL || + (b_name = bridge_if_find_name(bp->sysindex)) == NULL) + return (SNMP_ERR_GENERR); + + switch (val->var.subs[sub - 1]) { + case LEAF_begemotBridgeStpPortAdminEdgePort: + bridge_port_set_admin_edge(b_name, bp, + ctx->scratch->int1); + break; + case LEAF_begemotBridgeStpPortAdminPointToPoint: + bridge_port_set_admin_ptp(b_name, bp, + ctx->scratch->int1); + break; + case LEAF_begemotBridgeStpPortAdminPathCost: + bridge_port_set_path_cost(b_name, bp, + ctx->scratch->int1); + break; + } + return (SNMP_ERR_NOERROR); + + case SNMP_OP_COMMIT: + return (SNMP_ERR_NOERROR); + } + abort(); + +get: + switch (val->var.subs[sub - 1]) { + case LEAF_begemotBridgeStpPortProtocolMigration: + val->v.integer = bp->proto_migr; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotBridgeStpPortAdminEdgePort: + val->v.integer = bp->admin_edge; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotBridgeStpPortOperEdgePort: + val->v.integer = bp->oper_edge; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotBridgeStpPortAdminPointToPoint: + val->v.integer = bp->admin_ptp; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotBridgeStpPortOperPointToPoint: + val->v.integer = bp->oper_ptp; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotBridgeStpPortAdminPathCost: + val->v.integer = bp->admin_path_cost; + return (SNMP_ERR_NOERROR); + } + + abort(); +} + +int +op_begemot_tp_port(struct snmp_context *c __unused, struct snmp_value *val, + uint sub, uint iidx __unused, enum snmp_op op) +{ + struct bridge_port *bp; + + if (time(NULL) - ports_list_age > bridge_get_data_maxage()) + bridge_update_all_ports(); + + switch (op) { + case SNMP_OP_GET: + if ((bp = bridge_port_index_get(&val->var, sub, 0)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + goto get; + + case SNMP_OP_GETNEXT: + if ((bp = bridge_port_index_getnext(&val->var, sub, 0)) == + NULL || bridge_port_index_append(&val->var, sub, bp) < 0) + return (SNMP_ERR_NOSUCHNAME); + goto get; + + case SNMP_OP_SET: + return (SNMP_ERR_NOT_WRITEABLE); + + case SNMP_OP_ROLLBACK: + case SNMP_OP_COMMIT: + break; + } + abort(); + +get: + switch (val->var.subs[sub - 1]) { + case LEAF_begemotBridgeTpPort: + val->v.integer = bp->port_no; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotBridgeTpPortMaxInfo: + val->v.integer = bp->max_info; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotBridgeTpPortInFrames: + val->v.uint32 = bp->in_frames; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotBridgeTpPortOutFrames: + val->v.uint32 = bp->out_frames; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotBridgeTpPortInDiscards: + val->v.uint32 = bp->in_drops; + return (SNMP_ERR_NOERROR); + } + + abort(); +} diff --git a/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_snmp.c b/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_snmp.c new file mode 100644 index 0000000..81acc4d --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_snmp.c @@ -0,0 +1,338 @@ +/*- + * Copyright (c) 2006 Shteryana Shopova <syrinx@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Bridge MIB implementation for SNMPd. + * + * $FreeBSD$ + */ + +#include <sys/param.h> +#include <sys/queue.h> +#include <sys/socket.h> +#include <sys/types.h> + +#include <net/ethernet.h> +#include <net/if.h> +#include <net/if_mib.h> +#include <net/if_types.h> + +#include <errno.h> +#include <stdarg.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <syslog.h> + +#include <bsnmp/snmpmod.h> +#include <bsnmp/snmp_mibII.h> + +#include "bridge_tree.h" +#include "bridge_snmp.h" +#include "bridge_oid.h" + +static struct lmodule *bridge_module; + +/* For the registration. */ +static const struct asn_oid oid_dot1Bridge = OIDX_dot1dBridge; +/* The registration. */ +static uint reg_bridge; + +/* Periodic timer for polling all bridges' data. */ +static void *bridge_data_timer; +static void *bridge_tc_timer; + +static int bridge_data_maxage = SNMP_BRIDGE_DATA_MAXAGE; +static int bridge_poll_ticks = SNMP_BRIDGE_POLL_INTERVAL * 100; +static int bridge_tc_poll_ticks = SNMP_BRIDGE_TC_POLL_INTERVAL * 100; + +/* + * Our default bridge, whose info will be visible under + * the dot1dBridge subtree and functions to set/fetch it. + */ +static char bif_default_name[IFNAMSIZ] = "bridge0"; +static struct bridge_if *bif_default; + +struct bridge_if * +bridge_get_default(void) +{ + struct mibif *ifp; + + if (bif_default != NULL) { + + /* Walk through the mibII interface list. */ + for (ifp = mib_first_if(); ifp != NULL; ifp = mib_next_if(ifp)) + if (strcmp(ifp->name, bif_default->bif_name) == 0) + break; + + if (ifp == NULL) + bif_default = NULL; + } + + return (bif_default); +} + +void +bridge_set_default(struct bridge_if *bif) +{ + bif_default = bif; + + syslog(LOG_ERR, "Set default bridge interface to: %s", + bif == NULL ? "(none)" : bif->bif_name); +} + +const char * +bridge_get_default_name(void) +{ + return (bif_default_name); +} + +static int +bridge_set_default_name(const char *bif_name, uint len) +{ + struct bridge_if *bif; + + if (len >= IFNAMSIZ) + return (-1); + + bcopy(bif_name, bif_default_name, len); + bif_default_name[len] = '\0'; + + if ((bif = bridge_if_find_ifname(bif_default_name)) == NULL) { + bif_default = NULL; + return (0); + } + + bif_default = bif; + return (1); +} + +int +bridge_get_data_maxage(void) +{ + return (bridge_data_maxage); +} + +static void +bridge_set_poll_ticks(int poll_ticks) +{ + if (bridge_data_timer != NULL) + timer_stop(bridge_data_timer); + + bridge_poll_ticks = poll_ticks; + bridge_data_timer = timer_start_repeat(bridge_poll_ticks, + bridge_poll_ticks, bridge_update_all, NULL, bridge_module); +} +/* + * The bridge module configuration via SNMP. + */ +static int +bridge_default_name_save(struct snmp_context *ctx, const char *bridge_default) +{ + if ((ctx->scratch->int1 = strlen(bridge_default)) >= IFNAMSIZ) + return (-1); + + if ((ctx->scratch->ptr1 = malloc(IFNAMSIZ)) == NULL) + return (-1); + + strncpy(ctx->scratch->ptr1, bridge_default, ctx->scratch->int1); + return (0); +} + +int +op_begemot_bridge_config(struct snmp_context *ctx, struct snmp_value *val, + uint sub, uint iidx __unused, enum snmp_op op) +{ + switch (op) { + case SNMP_OP_GET: + switch (val->var.subs[sub - 1]) { + case LEAF_begemotBridgeDefaultBridgeIf: + return (string_get(val, bridge_get_default_name(), -1)); + + case LEAF_begemotBridgeDataUpdate: + val->v.integer = bridge_data_maxage; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotBridgeDataPoll: + val->v.integer = bridge_poll_ticks / 100; + return (SNMP_ERR_NOERROR); + } + abort(); + + case SNMP_OP_GETNEXT: + abort(); + + case SNMP_OP_SET: + switch (val->var.subs[sub - 1]) { + case LEAF_begemotBridgeDefaultBridgeIf: + /* + * Cannot use string_save() here - requires either + * a fixed-sized or var-length string - not less + * than or equal. + */ + if (bridge_default_name_save(ctx, + bridge_get_default_name()) < 0) + return (SNMP_ERR_RES_UNAVAIL); + + if (bridge_set_default_name(val->v.octetstring.octets, + val->v.octetstring.len) < 0) + return (SNMP_ERR_BADVALUE); + return (SNMP_ERR_NOERROR); + + case LEAF_begemotBridgeDataUpdate: + if (val->v.integer < SNMP_BRIDGE_DATA_MAXAGE_MIN || + val->v.integer > SNMP_BRIDGE_DATA_MAXAGE_MAX) + return (SNMP_ERR_WRONG_VALUE); + ctx->scratch->int1 = bridge_data_maxage; + bridge_data_maxage = val->v.integer; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotBridgeDataPoll: + if (val->v.integer < SNMP_BRIDGE_POLL_INTERVAL_MIN || + val->v.integer > SNMP_BRIDGE_POLL_INTERVAL_MAX) + return (SNMP_ERR_WRONG_VALUE); + ctx->scratch->int1 = val->v.integer; + return (SNMP_ERR_NOERROR); + } + abort(); + + case SNMP_OP_ROLLBACK: + switch (val->var.subs[sub - 1]) { + case LEAF_begemotBridgeDefaultBridgeIf: + bridge_set_default_name(ctx->scratch->ptr1, + ctx->scratch->int1); + free(ctx->scratch->ptr1); + break; + case LEAF_begemotBridgeDataUpdate: + bridge_data_maxage = ctx->scratch->int1; + break; + } + return (SNMP_ERR_NOERROR); + + case SNMP_OP_COMMIT: + switch (val->var.subs[sub - 1]) { + case LEAF_begemotBridgeDefaultBridgeIf: + free(ctx->scratch->ptr1); + break; + case LEAF_begemotBridgeDataPoll: + bridge_set_poll_ticks(ctx->scratch->int1 * 100); + break; + } + return (SNMP_ERR_NOERROR); + } + + abort(); +} + +/* + * Bridge mib module initialization hook. + * Returns 0 on success, < 0 on error. + */ +static int +bridge_init(struct lmodule * mod, int argc __unused, char *argv[] __unused) +{ + bridge_module = mod; + + if (bridge_kmod_load() < 0) + return (-1); + + if (bridge_ioctl_init() < 0) + return (-1); + + /* Register to get creation messages for bridge interfaces. */ + if (mib_register_newif(bridge_attach_newif, bridge_module)) { + syslog(LOG_ERR, "Cannot register newif function: %s", + strerror(errno)); + return (-1); + } + + return (0); +} + +/* + * Bridge mib module finalization hook. + */ +static int +bridge_fini(void) +{ + mib_unregister_newif(bridge_module); + or_unregister(reg_bridge); + + if (bridge_data_timer != NULL) { + timer_stop(bridge_data_timer); + bridge_data_timer = NULL; + } + + if (bridge_tc_timer != NULL) { + timer_stop(bridge_tc_timer); + bridge_tc_timer = NULL; + } + + bridge_ifs_fini(); + bridge_ports_fini(); + bridge_addrs_fini(); + + return (0); +} + +/* + * Bridge mib module start operation. + */ +static void +bridge_start(void) +{ + reg_bridge = or_register(&oid_dot1Bridge, + "The IETF MIB for Bridges (RFC 4188).", bridge_module); + + bridge_data_timer = timer_start_repeat(bridge_poll_ticks, + bridge_poll_ticks, bridge_update_all, NULL, bridge_module); + + bridge_tc_timer = timer_start_repeat(bridge_tc_poll_ticks, + bridge_tc_poll_ticks, bridge_update_tc_time, NULL, bridge_module); +} + +static void +bridge_dump(void) +{ + struct bridge_if *bif; + + if ((bif = bridge_get_default()) == NULL) + syslog(LOG_ERR, "Dump: no default bridge interface"); + else + syslog(LOG_ERR, "Dump: default bridge interface %s", + bif->bif_name); + + bridge_ifs_dump(); + bridge_pf_dump(); +} + +const struct snmp_module config = { + .comment = "This module implements the bridge mib (RFC 4188).", + .init = bridge_init, + .fini = bridge_fini, + .start = bridge_start, + .tree = bridge_ctree, + .dump = bridge_dump, + .tree_size = bridge_CTREE_SIZE, +}; diff --git a/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_snmp.h b/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_snmp.h new file mode 100644 index 0000000..7f48950 --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_snmp.h @@ -0,0 +1,357 @@ +/*- + * Copyright (c) 2006 Shteryana Shopova <syrinx@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Bridge MIB implementation for SNMPd. + * + * $FreeBSD$ + */ + +#ifndef SNMP_BRIDGE_H +#define SNMP_BRIDGE_H + +#define SNMP_BRIDGE_ID_LEN 8 + +typedef uint8_t port_id[2]; +typedef u_char bridge_id[SNMP_BRIDGE_ID_LEN]; + +#define SNMP_BRIDGE_MAX_PRIORITY 65535 + +#define SNMP_BRIDGE_MIN_AGE_TIME 10 +#define SNMP_BRIDGE_MAX_AGE_TIME 1000000 + +#define SNMP_BRIDGE_MIN_TXHC 1 +#define SNMP_BRIDGE_MAX_TXHC 10 + +#define SNMP_BRIDGE_MIN_MAGE 600 +#define SNMP_BRIDGE_MAX_MAGE 4000 + +#define SNMP_BRIDGE_MIN_HTIME 100 +#define SNMP_BRIDGE_MAX_HTIME 1000 + +#define SNMP_BRIDGE_MIN_FDELAY 400 +#define SNMP_BRIDGE_MAX_FDELAY 3000 + +#define SNMP_PORT_PATHCOST_OBSOLETE 65535 +#define SNMP_PORT_MIN_PATHCOST 0 +#define SNMP_PORT_MAX_PATHCOST 200000000 +#define SNMP_PORT_PATHCOST_AUTO 0 + +#define SNMP_BRIDGE_DATA_MAXAGE 10 +#define SNMP_BRIDGE_DATA_MAXAGE_MIN 1 +#define SNMP_BRIDGE_DATA_MAXAGE_MAX 300 + +/* By default poll kernel data every 5 minutes. */ +#define SNMP_BRIDGE_POLL_INTERVAL (5 * 60) +#define SNMP_BRIDGE_POLL_INTERVAL_MIN 1 +#define SNMP_BRIDGE_POLL_INTERVAL_MAX 3600 + +/* Poll for a topology change once every 30 seconds. */ +#define SNMP_BRIDGE_TC_POLL_INTERVAL 30 + +struct bridge_if *bridge_get_default(void); + +void bridge_set_default(struct bridge_if *bif); + +const char *bridge_get_default_name(void); + +int bridge_get_data_maxage(void); + +/* + * Bridge Addresses Table. + */ +struct tp_entry { + uint32_t sysindex; /* The bridge if sysindex. */ + int32_t port_no; + enum TpFdbStatus status; + uint8_t tp_addr[ETHER_ADDR_LEN]; + uint8_t flags; + TAILQ_ENTRY(tp_entry) tp_e; +}; + +/* + * Bridge ports. + * The bridge port system interface index is used for a + * port number. Transparent bridging statistics and STP + * information for a port are also contained here. + */ +struct bridge_port { + /* dot1dBase subtree objects. */ + uint32_t sysindex; /* The bridge interface sysindex. */ + int32_t port_no; /* The bridge member system index. */ + int32_t if_idx; /* SNMP ifIndex from mibII. */ + int8_t span_enable; /* Span flag set - private MIB. */ + struct asn_oid circuit; /* Unused. */ + uint32_t dly_ex_drops; /* Drops on output. */ + uint32_t dly_mtu_drops; /* MTU exceeded drops. */ + int32_t status; /* The entry status. */ + enum TruthValue priv_set; /* The private flag. */ + + /* dot1dStp subtree objects. */ + int32_t path_cost; + int32_t priority; + int32_t design_cost; + uint32_t fwd_trans; + char p_name[IFNAMSIZ]; /* Not in BRIDGE-MIB. */ + enum StpPortState state; + enum dot1dStpPortEnable enable; + port_id design_port; + bridge_id design_root; + bridge_id design_bridge; + + /* rstpMib extensions. */ + int32_t admin_path_cost; + enum TruthValue proto_migr; + enum TruthValue admin_edge; + enum TruthValue oper_edge; + enum TruthValue oper_ptp; + enum StpPortAdminPointToPointType admin_ptp; + + /* dot1dTp subtree objects. */ + int32_t max_info; + int32_t in_frames; + int32_t out_frames; + int32_t in_drops; + + uint8_t flags; + TAILQ_ENTRY(bridge_port) b_p; +}; + +/* + * A bridge interface. + * The system interface index of the bridge is not required neither by the + * standard BRIDGE-MIB nor by the private BEGEMOT-BRIDGE-MIB, but is used + * as key for looking up the other info for this bridge. + */ +struct bridge_if { + /* dot1dBase subtree objects. */ + uint32_t sysindex; /* The system interface index. */ + int32_t num_ports; /* Number of ports. */ + enum BaseType br_type; /* Bridge type. */ + enum RowStatus if_status; /* Bridge status. */ + char bif_name[IFNAMSIZ]; /* Bridge interface name. */ + struct ether_addr br_addr; /* Bridge address. */ + struct bridge_port *f_bp; /* This bridge's first entry + * in the base ports TAILQ. */ + /* dot1dStp subtree objects. */ + int32_t priority; + int32_t root_cost; + int32_t root_port; + int32_t max_age; /* Current max age. */ + int32_t hello_time; /* Current hello time. */ + int32_t fwd_delay; /* Current forward delay. */ + int32_t hold_time; + int32_t bridge_max_age; /* Configured max age. */ + int32_t bridge_hello_time; /* Configured hello time. */ + int32_t bridge_fwd_delay; /* Configured forward delay. */ + int32_t tx_hold_count; + uint32_t top_changes; + enum dot1dStpVersion stp_version; + enum dot1dStpProtocolSpecification prot_spec; + struct timeval last_tc_time; + bridge_id design_root; + + /* dot1dTp subtree objects. */ + int32_t lrnt_drops; /* Dropped addresses. */ + int32_t age_time; /* Address entry timeout. */ + int32_t num_addrs; /* Current # of addresses in cache. */ + int32_t max_addrs; /* Max # of addresses in cache. */ + struct tp_entry *f_tpa; /* This bridge's first entry in + * the tp addresses TAILQ. */ + + time_t entry_age; + time_t ports_age; + time_t addrs_age; + TAILQ_ENTRY(bridge_if) b_if; +}; + +void bridge_ifs_fini(void); + +struct bridge_if *bridge_if_find_ifs(uint32_t sysindex); + +struct bridge_if *bridge_if_find_ifname(const char *b_name); + +const char *bridge_if_find_name(uint32_t sysindex); + +int bridge_compare_sysidx(uint32_t i1, uint32_t i2); + +int bridge_attach_newif(struct mibif *ifp); + +struct bridge_if *bridge_first_bif(void); + +struct bridge_if *bridge_next_bif(struct bridge_if *b_pr); + +void bridge_remove_bif(struct bridge_if *bif); + +void bridge_update_all_ports(void); + +void bridge_update_all_addrs(void); + +void bridge_update_all_ifs(void); + +void bridge_update_all(void *arg); + +void bridge_update_tc_time(void *arg); + +void bridge_ifs_dump(void); + +/* Bridge ports. */ +void bridge_ports_update_listage(void); + +void bridge_ports_fini(void); + +void bridge_members_free(struct bridge_if *bif); + +struct bridge_port *bridge_new_port(struct mibif *mif, struct bridge_if *bif); + +void bridge_port_remove(struct bridge_port *bp, struct bridge_if *bif); + +struct bridge_port *bridge_port_bif_first(struct bridge_if *bif); + +struct bridge_port *bridge_port_bif_next(struct bridge_port *bp); + +struct bridge_port *bridge_port_find(int32_t if_idx, struct bridge_if *bif); + +void bridge_port_getinfo_mibif(struct mibif *m_if, struct bridge_port *bp); + +int bridge_getinfo_bif_ports(struct bridge_if *bif); + +int bridge_update_memif(struct bridge_if *bif); + +void bridge_ports_dump(struct bridge_if *bif); + +/* Bridge addresses. */ +void bridge_addrs_update_listage(void); + +void bridge_addrs_fini(void); + +void bridge_addrs_free(struct bridge_if *bif); + +struct tp_entry *bridge_new_addrs(uint8_t *mac, struct bridge_if *bif); + +void bridge_addrs_remove(struct tp_entry *te, struct bridge_if *bif); + +struct tp_entry *bridge_addrs_find(uint8_t *mac, struct bridge_if *bif); + +struct tp_entry *bridge_addrs_bif_first(struct bridge_if *bif); + +struct tp_entry *bridge_addrs_bif_next(struct tp_entry *te); + +int bridge_getinfo_bif_addrs(struct bridge_if *bif); + +int bridge_update_addrs(struct bridge_if *bif); + +void bridge_addrs_dump(struct bridge_if *bif); + +/* Bridge PF. */ + +void bridge_pf_dump(void); + +/* System specific. */ + +/* Open the socket for the ioctls. */ +int bridge_ioctl_init(void); + +/* Load bridge kernel module. */ +int bridge_kmod_load(void); + +/* Get the bridge interface information. */ +int bridge_getinfo_bif(struct bridge_if *bif); + +/* Get the bridge interface STP parameters. */ +int bridge_get_op_param(struct bridge_if *bif); + +/* Set the bridge priority. */ +int bridge_set_priority(struct bridge_if *bif, int32_t priority); + +/* Set the bridge max age. */ +int bridge_set_maxage(struct bridge_if *bif, int32_t max_age); + +/* Set the bridge hello time.*/ +int bridge_set_hello_time(struct bridge_if *bif, int32_t hello_time); + +/* Set the bridge forward delay.*/ +int bridge_set_forward_delay(struct bridge_if *bif, int32_t fwd_delay); + +/* Set the bridge address cache max age. */ +int bridge_set_aging_time(struct bridge_if *bif, int32_t age_time); + +/* Set the max number of entries in the bridge address cache. */ +int bridge_set_max_cache(struct bridge_if *bif, int32_t max_cache); + +/* Set the bridge TX hold count. */ +int bridge_set_tx_hold_count(struct bridge_if *bif, int32_t tx_hc); + +/* Set the bridge STP protocol version. */ +int bridge_set_stp_version(struct bridge_if *bif, int32_t stp_proto); + +/* Set the bridge interface status to up/down. */ +int bridge_set_if_up(const char* b_name, int8_t up); + +/* Create a bridge interface. */ +int bridge_create(const char *b_name); + +/* Destroy a bridge interface. */ +int bridge_destroy(const char *b_name); + +/* Fetch the bridge mac address. */ +u_char *bridge_get_basemac(const char *bif_name, u_char *mac, size_t mlen); + +/* Set a bridge member priority. */ +int bridge_port_set_priority(const char *bif_name, struct bridge_port *bp, + int32_t priority); + +/* Set a bridge member STP-enabled flag. */ +int bridge_port_set_stp_enable(const char *bif_name, struct bridge_port *bp, + uint32_t enable); + +/* Set a bridge member STP path cost. */ +int bridge_port_set_path_cost(const char *bif_name, struct bridge_port *bp, + int32_t path_cost); + +/* Set admin point-to-point link. */ +int bridge_port_set_admin_ptp(const char *bif_name, struct bridge_port *bp, + uint32_t admin_ptp); + +/* Set admin edge. */ +int bridge_port_set_admin_edge(const char *bif_name, struct bridge_port *bp, + uint32_t enable); + +/* Set 'private' flag. */ +int bridge_port_set_private(const char *bif_name, struct bridge_port *bp, + uint32_t priv_set); + +/* Add a bridge member port. */ +int bridge_port_addm(struct bridge_port *bp, const char *b_name); + +/* Delete a bridge member port. */ +int bridge_port_delm(struct bridge_port *bp, const char *b_name); + +/* Get the current value from the module for bridge PF control. */ +int32_t bridge_get_pfval(uint8_t which); + +/* Get/Set a bridge PF control. */ +int32_t bridge_do_pfctl(int32_t bridge_ctl, enum snmp_op op, int32_t *val); + +#endif /* SNMP_BRIDGE_H */ diff --git a/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_sys.c b/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_sys.c new file mode 100644 index 0000000..f8644f4 --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_sys.c @@ -0,0 +1,1503 @@ +/*- + * Copyright (c) 2006 Shteryana Shopova <syrinx@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Bridge MIB implementation for SNMPd. + * Bridge OS specific ioctls. + * + * $FreeBSD$ + */ + +#include <sys/ioctl.h> +#include <sys/param.h> +#include <sys/module.h> +#include <sys/linker.h> +#include <sys/socket.h> +#include <sys/sysctl.h> + +#include <net/bridgestp.h> +#include <net/ethernet.h> +#include <net/if.h> +#include <net/if_bridgevar.h> +#include <net/if_dl.h> +#include <net/if_mib.h> +#include <net/if_types.h> +#include <netinet/in.h> + +#include <errno.h> +#include <ifaddrs.h> +#include <stdarg.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <syslog.h> + +#include <bsnmp/snmpmod.h> +#include <bsnmp/snmp_mibII.h> + +#include "bridge_tree.h" +#include "bridge_snmp.h" + +int sock = -1; + +int +bridge_ioctl_init(void) +{ + if ((sock = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { + syslog(LOG_ERR, "cannot open socket : %s", strerror(errno)); + return (-1); + } + + return (0); +} + +/* + * Load the if_bridge.ko module in kernel if not already there. + */ +int +bridge_kmod_load(void) +{ + int fileid, modid; + const char mod_name[] = "if_bridge"; + struct module_stat mstat; + + /* Scan files in kernel. */ + mstat.version = sizeof(struct module_stat); + for (fileid = kldnext(0); fileid > 0; fileid = kldnext(fileid)) { + /* Scan modules in file. */ + for (modid = kldfirstmod(fileid); modid > 0; + modid = modfnext(modid)) { + + if (modstat(modid, &mstat) < 0) + continue; + + if (strcmp(mod_name, mstat.name) == 0) + return (0); + } + } + + /* Not present - load it. */ + if (kldload(mod_name) < 0) { + syslog(LOG_ERR, "failed to load %s kernel module", mod_name); + return (-1); + } + + return (1); +} + +/************************************************************************ + * Bridge interfaces. + */ + +/* + * Convert the kernel uint64_t value for a bridge id + */ +static void +snmp_uint64_to_bridgeid(uint64_t id, bridge_id b_id) +{ + int i; + u_char *o; + + o = (u_char *) &id; + + for (i = 0; i < SNMP_BRIDGE_ID_LEN; i++, o++) + b_id[SNMP_BRIDGE_ID_LEN - i - 1] = *o; +} + +/* + * Fetch the bridge configuration parameters from the kernel excluding + * it's base MAC address. + */ +static int +bridge_get_conf_param(struct bridge_if *bif) +{ + struct ifdrv ifd; + struct ifbrparam b_param; + + strlcpy(ifd.ifd_name, bif->bif_name, IFNAMSIZ); + ifd.ifd_len = sizeof(b_param); + ifd.ifd_data = &b_param; + + /* Bridge priority. */ + ifd.ifd_cmd = BRDGGPRI; + if (ioctl(sock, SIOCGDRVSPEC, &ifd) < 0) { + syslog(LOG_ERR, "update bridge: ioctl(BRDGGPRI) failed: %s", + strerror(errno)); + return (-1); + } + + bif->priority = b_param.ifbrp_prio; + + /* Configured max age. */ + ifd.ifd_cmd = BRDGGMA; + if (ioctl(sock, SIOCGDRVSPEC, &ifd) < 0) { + syslog(LOG_ERR, "update bridge: ioctl(BRDGGMA) failed: %s", + strerror(errno)); + return (-1); + } + + /* Centi-seconds. */ + bif->bridge_max_age = 100 * b_param.ifbrp_maxage; + + /* Configured hello time. */ + ifd.ifd_cmd = BRDGGHT; + if (ioctl(sock, SIOCGDRVSPEC, &ifd) < 0) { + syslog(LOG_ERR, "update bridge: ioctl(BRDGGHT) failed: %s", + strerror(errno)); + return (-1); + } + bif->bridge_hello_time = 100 * b_param.ifbrp_hellotime; + + /* Forward delay. */ + ifd.ifd_cmd = BRDGGFD; + if (ioctl(sock, SIOCGDRVSPEC, &ifd) < 0) { + syslog(LOG_ERR, "update bridge: ioctl(BRDGGFD) failed: %s", + strerror(errno)); + return (-1); + } + bif->bridge_fwd_delay = 100 * b_param.ifbrp_fwddelay; + + /* Number of dropped addresses. */ + ifd.ifd_cmd = BRDGGRTE; + if (ioctl(sock, SIOCGDRVSPEC, &ifd) < 0) { + syslog(LOG_ERR, "update bridge: ioctl(BRDGGRTE) failed: %s", + strerror(errno)); + return (-1); + } + bif->lrnt_drops = b_param.ifbrp_cexceeded; + + /* Address table timeout. */ + ifd.ifd_cmd = BRDGGTO; + if (ioctl(sock, SIOCGDRVSPEC, &ifd) < 0) { + syslog(LOG_ERR, "update bridge: ioctl(BRDGGTO) failed: %s", + strerror(errno)); + return (-1); + } + bif->age_time = b_param.ifbrp_ctime; + + /* Address table size. */ + ifd.ifd_cmd = BRDGGCACHE; + if (ioctl(sock, SIOCGDRVSPEC, &ifd) < 0) { + syslog(LOG_ERR, "update bridge: ioctl(BRDGGCACHE) " + "failed: %s", strerror(errno)); + return (-1); + } + bif->max_addrs = b_param.ifbrp_csize; + + return (0); +} + +/* + * Fetch the current bridge STP operational parameters. + * Returns: -1 - on error; + * 0 - old TC time and Root Port values are same; + * 1 - topologyChange notification should be sent; + * 2 - newRoot notification should be sent. + */ +int +bridge_get_op_param(struct bridge_if *bif) +{ + int new_root_send; + struct ifdrv ifd; + struct ifbropreq b_req; + + strlcpy(ifd.ifd_name, bif->bif_name, IFNAMSIZ); + ifd.ifd_len = sizeof(b_req); + ifd.ifd_data = &b_req; + ifd.ifd_cmd = BRDGPARAM; + + if (ioctl(sock, SIOCGDRVSPEC, &ifd) < 0) { + syslog(LOG_ERR, "update bridge: ioctl(BRDGPARAM) failed: %s", + strerror(errno)); + return (-1); + } + + bif->max_age = 100 * b_req.ifbop_maxage; + bif->hello_time = 100 * b_req.ifbop_hellotime; + bif->fwd_delay = 100 * b_req.ifbop_fwddelay; + bif->stp_version = b_req.ifbop_protocol; + bif->tx_hold_count = b_req.ifbop_holdcount; + + if (b_req.ifbop_root_port == 0 && + bif->root_port != b_req.ifbop_root_port) + new_root_send = 2; + else + new_root_send = 0; + + bif->root_port = b_req.ifbop_root_port; + bif->root_cost = b_req.ifbop_root_path_cost; + snmp_uint64_to_bridgeid(b_req.ifbop_designated_root, + bif->design_root); + + if (bif->last_tc_time.tv_sec != b_req.ifbop_last_tc_time.tv_sec) { + bif->top_changes++; + bif->last_tc_time.tv_sec = b_req.ifbop_last_tc_time.tv_sec; + bif->last_tc_time.tv_usec = b_req.ifbop_last_tc_time.tv_usec; + + /* + * "The trap is not sent if a (begemotBridge)NewRoot + * trap is sent for the same transition." + */ + if (new_root_send == 0) + return (1); + } + + return (new_root_send); +} + +int +bridge_getinfo_bif(struct bridge_if *bif) +{ + if (bridge_get_conf_param(bif) < 0) + return (-1); + + return (bridge_get_op_param(bif)); +} + +int +bridge_set_priority(struct bridge_if *bif, int32_t priority) +{ + struct ifdrv ifd; + struct ifbrparam b_param; + + strlcpy(ifd.ifd_name, bif->bif_name, IFNAMSIZ); + ifd.ifd_len = sizeof(b_param); + ifd.ifd_data = &b_param; + b_param.ifbrp_prio = (uint32_t) priority; + ifd.ifd_cmd = BRDGSPRI; + + if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) { + syslog(LOG_ERR, "set bridge param: ioctl(BRDGSPRI) " + "failed: %s", strerror(errno)); + return (-1); + } + + /* + * Re-fetching the data from the driver after that might be a good + * idea, since changing our bridge's priority should invoke + * recalculation of the active spanning tree topology in the network. + */ + bif->priority = priority; + return (0); +} + +/* + * Convert 1/100 of seconds to 1/256 of seconds. + * Timeout ::= TEXTUAL-CONVENTION. + * To convert a Timeout value into a value in units of + * 1/256 seconds, the following algorithm should be used: + * b = floor( (n * 256) / 100) + * The conversion to 1/256 of a second happens in the kernel - + * just make sure we correctly convert the seconds to Timout + * and vice versa. + */ +static uint32_t +snmp_timeout2_sec(int32_t secs) +{ + return (secs / 100); +} + +int +bridge_set_maxage(struct bridge_if *bif, int32_t max_age) +{ + struct ifdrv ifd; + struct ifbrparam b_param; + + strlcpy(ifd.ifd_name, bif->bif_name, IFNAMSIZ); + ifd.ifd_len = sizeof(b_param); + ifd.ifd_data = &b_param; + b_param.ifbrp_maxage = snmp_timeout2_sec(max_age); + ifd.ifd_cmd = BRDGSMA; + + if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) { + syslog(LOG_ERR, "set bridge param: ioctl(BRDGSMA) " + "failed: %s", strerror(errno)); + return (-1); + } + + bif->bridge_max_age = max_age; + return (0); +} + +int +bridge_set_hello_time(struct bridge_if *bif, int32_t hello_time) +{ + struct ifdrv ifd; + struct ifbrparam b_param; + + strlcpy(ifd.ifd_name, bif->bif_name, IFNAMSIZ); + ifd.ifd_len = sizeof(b_param); + ifd.ifd_data = &b_param; + b_param.ifbrp_hellotime = snmp_timeout2_sec(hello_time); + ifd.ifd_cmd = BRDGSHT; + + if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) { + syslog(LOG_ERR, "set bridge param: ioctl(BRDGSHT) " + "failed: %s", strerror(errno)); + return (-1); + } + + bif->bridge_hello_time = b_param.ifbrp_hellotime; + return (0); +} + +int +bridge_set_forward_delay(struct bridge_if *bif, int32_t fwd_delay) +{ + struct ifdrv ifd; + struct ifbrparam b_param; + + strlcpy(ifd.ifd_name, bif->bif_name, IFNAMSIZ); + ifd.ifd_len = sizeof(b_param); + ifd.ifd_data = &b_param; + b_param.ifbrp_fwddelay = snmp_timeout2_sec(fwd_delay); + ifd.ifd_cmd = BRDGSFD; + + if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) { + syslog(LOG_ERR, "set bridge param: ioctl(BRDGSFD) " + "failed: %s", strerror(errno)); + return (-1); + } + + bif->bridge_fwd_delay = b_param.ifbrp_fwddelay; + return (0); +} + +int +bridge_set_aging_time(struct bridge_if *bif, int32_t age_time) +{ + struct ifdrv ifd; + struct ifbrparam b_param; + + strlcpy(ifd.ifd_name, bif->bif_name, IFNAMSIZ); + ifd.ifd_len = sizeof(b_param); + ifd.ifd_data = &b_param; + b_param.ifbrp_ctime = (uint32_t) age_time; + ifd.ifd_cmd = BRDGSTO; + + if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) { + syslog(LOG_ERR, "set bridge param: ioctl(BRDGSTO) " + "failed: %s", strerror(errno)); + return (-1); + } + + bif->age_time = age_time; + return (0); +} + +int +bridge_set_max_cache(struct bridge_if *bif, int32_t max_cache) +{ + struct ifdrv ifd; + struct ifbrparam b_param; + + strlcpy(ifd.ifd_name, bif->bif_name, IFNAMSIZ); + ifd.ifd_len = sizeof(b_param); + ifd.ifd_data = &b_param; + b_param.ifbrp_csize = max_cache; + ifd.ifd_cmd = BRDGSCACHE; + + if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) { + syslog(LOG_ERR, "set bridge param: ioctl(BRDGSCACHE) " + "failed: %s", strerror(errno)); + return (-1); + } + + bif->max_addrs = b_param.ifbrp_csize; + return (0); +} + +int +bridge_set_tx_hold_count(struct bridge_if *bif, int32_t tx_hc) +{ + struct ifdrv ifd; + struct ifbrparam b_param; + + if (tx_hc < SNMP_BRIDGE_MIN_TXHC || tx_hc > SNMP_BRIDGE_MAX_TXHC) + return (-1); + + strlcpy(ifd.ifd_name, bif->bif_name, IFNAMSIZ); + ifd.ifd_len = sizeof(b_param); + ifd.ifd_data = &b_param; + b_param.ifbrp_txhc = tx_hc; + ifd.ifd_cmd = BRDGSTXHC; + + if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) { + syslog(LOG_ERR, "set bridge param: ioctl(BRDGSTXHC) " + "failed: %s", strerror(errno)); + return (-1); + } + + bif->tx_hold_count = b_param.ifbrp_txhc; + return (0); +} + +int +bridge_set_stp_version(struct bridge_if *bif, int32_t stp_proto) +{ + struct ifdrv ifd; + struct ifbrparam b_param; + + strlcpy(ifd.ifd_name, bif->bif_name, IFNAMSIZ); + ifd.ifd_len = sizeof(b_param); + ifd.ifd_data = &b_param; + b_param.ifbrp_proto = stp_proto; + ifd.ifd_cmd = BRDGSPROTO; + + if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) { + syslog(LOG_ERR, "set bridge param: ioctl(BRDGSPROTO) " + "failed: %s", strerror(errno)); + return (-1); + } + + bif->stp_version = b_param.ifbrp_proto; + return (0); +} + +/* + * Set the bridge interface status to up/down. + */ +int +bridge_set_if_up(const char* b_name, int8_t up) +{ + int flags; + struct ifreq ifr; + + bzero(&ifr, sizeof(ifr)); + strcpy(ifr.ifr_name, b_name); + if (ioctl(sock, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) { + syslog(LOG_ERR, "set bridge up: ioctl(SIOCGIFFLAGS) " + "failed: %s", strerror(errno)); + return (-1); + } + + flags = (ifr.ifr_flags & 0xffff) | (ifr.ifr_flagshigh << 16); + if (up == 1) + flags |= IFF_UP; + else + flags &= ~IFF_UP; + + ifr.ifr_flags = flags & 0xffff; + ifr.ifr_flagshigh = flags >> 16; + if (ioctl(sock, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) { + syslog(LOG_ERR, "set bridge up: ioctl(SIOCSIFFLAGS) " + "failed: %s", strerror(errno)); + return (-1); + } + + return (0); +} + +int +bridge_create(const char *b_name) +{ + char *new_name; + struct ifreq ifr; + + bzero(&ifr, sizeof(ifr)); + strcpy(ifr.ifr_name, b_name); + + if (ioctl(sock, SIOCIFCREATE, &ifr) < 0) { + syslog(LOG_ERR, "create bridge: ioctl(SIOCIFCREATE) " + "failed: %s", strerror(errno)); + return (-1); + } + + if (strcmp(b_name, ifr.ifr_name) == 0) + return (0); + + if ((new_name = strdup(b_name)) == NULL) { + syslog(LOG_ERR, "create bridge: strdup() failed"); + return (-1); + } + + ifr.ifr_data = new_name; + if (ioctl(sock, SIOCSIFNAME, (caddr_t) &ifr) < 0) { + syslog(LOG_ERR, "create bridge: ioctl(SIOCSIFNAME) " + "failed: %s", strerror(errno)); + free(new_name); + return (-1); + } + + return (0); +} + +int +bridge_destroy(const char *b_name) +{ + struct ifreq ifr; + + bzero(&ifr, sizeof(ifr)); + strcpy(ifr.ifr_name, b_name); + + if (ioctl(sock, SIOCIFDESTROY, &ifr) < 0) { + syslog(LOG_ERR, "destroy bridge: ioctl(SIOCIFDESTROY) " + "failed: %s", strerror(errno)); + return (-1); + } + + return (0); +} + +/* + * Fetch the bridge base MAC address. Return pointer to the + * buffer containing the MAC address, NULL on failure. + */ +u_char * +bridge_get_basemac(const char *bif_name, u_char *mac, size_t mlen) +{ + int len; + char if_name[IFNAMSIZ]; + struct ifaddrs *ifap, *ifa; + struct sockaddr_dl sdl; + + if (getifaddrs(&ifap) != 0) { + syslog(LOG_ERR, "bridge get mac: getifaddrs() failed - %s", + strerror(errno)); + return (NULL); + } + + for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) { + if (ifa->ifa_addr->sa_family != AF_LINK) + continue; + + /* + * Not just casting because of alignment constraints + * on sparc64 and ia64. + */ + bcopy(ifa->ifa_addr, &sdl, sizeof(struct sockaddr_dl)); + + if (sdl.sdl_alen > mlen) + continue; + + if ((len = sdl.sdl_nlen) >= IFNAMSIZ) + len = IFNAMSIZ - 1; + + bcopy(sdl.sdl_data, if_name, len); + if_name[len] = '\0'; + + if (strcmp(bif_name, if_name) == 0) { + bcopy(sdl.sdl_data + sdl.sdl_nlen, mac, sdl.sdl_alen); + freeifaddrs(ifap); + return (mac); + } + } + + freeifaddrs(ifap); + return (NULL); +} + +/************************************************************************ + * Bridge ports. + */ + +/* + * Convert the kernel STP port state into + * the corresopnding enumerated type from SNMP Bridge MIB. + */ +static int +state2snmp_st(uint8_t ifbr_state) +{ + switch (ifbr_state) { + case BSTP_IFSTATE_DISABLED: + return (StpPortState_disabled); + case BSTP_IFSTATE_LISTENING: + return (StpPortState_listening); + case BSTP_IFSTATE_LEARNING: + return (StpPortState_learning); + case BSTP_IFSTATE_FORWARDING: + return (StpPortState_forwarding); + case BSTP_IFSTATE_BLOCKING: + case BSTP_IFSTATE_DISCARDING: + return (StpPortState_blocking); + } + + return (StpPortState_broken); +} + +/* + * Fill in a bridge member information according to data polled from kernel. + */ +static void +bridge_port_getinfo_conf(struct ifbreq *k_info, struct bridge_port *bp) +{ + bp->state = state2snmp_st(k_info->ifbr_state); + bp->priority = k_info->ifbr_priority; + + /* + * RFC 4188: + * "New implementations should support dot1dStpPortPathCost32. + * If the port path costs exceeds the maximum value of this + * object then this object should report the maximum value, + * namely 65535. Applications should try to read the + * dot1dStpPortPathCost32 object if this object reports + * the maximum value." + */ + + if (k_info->ifbr_ifsflags & IFBIF_BSTP_ADMCOST) + bp->admin_path_cost = k_info->ifbr_path_cost; + else + bp->admin_path_cost = 0; + + bp->path_cost = k_info->ifbr_path_cost; + + if (k_info->ifbr_ifsflags & IFBIF_STP) + bp->enable = dot1dStpPortEnable_enabled; + else + bp->enable = dot1dStpPortEnable_disabled; + + /* Begemot Bridge MIB only. */ + if (k_info->ifbr_ifsflags & IFBIF_SPAN) + bp->span_enable = begemotBridgeBaseSpanEnabled_enabled; + else + bp->span_enable = begemotBridgeBaseSpanEnabled_disabled; + + if (k_info->ifbr_ifsflags & IFBIF_PRIVATE) + bp->priv_set = TruthValue_true; + else + bp->priv_set = TruthValue_false; + + if (k_info->ifbr_ifsflags & IFBIF_BSTP_ADMEDGE) + bp->admin_edge = TruthValue_true; + else + bp->admin_edge = TruthValue_false; + + if (k_info->ifbr_ifsflags & IFBIF_BSTP_EDGE) + bp->oper_edge = TruthValue_true; + else + bp->oper_edge = TruthValue_false; + + if (k_info->ifbr_ifsflags & IFBIF_BSTP_AUTOPTP) { + bp->admin_ptp = StpPortAdminPointToPointType_auto; + if (k_info->ifbr_ifsflags & IFBIF_BSTP_PTP) + bp->oper_ptp = TruthValue_true; + else + bp->oper_ptp = TruthValue_false; + } else if (k_info->ifbr_ifsflags & IFBIF_BSTP_PTP) { + bp->admin_ptp = StpPortAdminPointToPointType_forceTrue; + bp->oper_ptp = TruthValue_true; + } else { + bp->admin_ptp = StpPortAdminPointToPointType_forceFalse; + bp->oper_ptp = TruthValue_false; + } +} + +/* + * Fill in a bridge interface STP information according to + * data polled from kernel. + */ +static void +bridge_port_getinfo_opstp(struct ifbpstpreq *bp_stp, struct bridge_port *bp) +{ + bp->enable = dot1dStpPortEnable_enabled; + bp->fwd_trans = bp_stp->ifbp_fwd_trans; + bp->design_cost = bp_stp->ifbp_design_cost; + snmp_uint64_to_bridgeid(bp_stp->ifbp_design_root, bp->design_root); + snmp_uint64_to_bridgeid(bp_stp->ifbp_design_bridge, bp->design_bridge); + bcopy(&(bp_stp->ifbp_design_port), &(bp->design_port), + sizeof(uint16_t)); +} + +/* + * Clear a bridge interface STP information. + */ +static void +bridge_port_clearinfo_opstp(struct bridge_port *bp) +{ + if (bp->enable == dot1dStpPortEnable_enabled) { + bp->design_cost = 0; + bzero(&(bp->design_root), sizeof(bridge_id)); + bzero(&(bp->design_bridge), sizeof(bridge_id)); + bzero(&(bp->design_port), sizeof(port_id)); + bp->fwd_trans = 0; + } + + bp->enable = dot1dStpPortEnable_disabled; +} + +/* + * Set a bridge member priority. + */ +int +bridge_port_set_priority(const char *bif_name, struct bridge_port *bp, + int32_t priority) +{ + struct ifdrv ifd; + struct ifbreq b_req; + + strlcpy(ifd.ifd_name, bif_name, sizeof(ifd.ifd_name)); + ifd.ifd_len = sizeof(b_req); + ifd.ifd_data = &b_req; + strlcpy(b_req.ifbr_ifsname, bp->p_name, sizeof(b_req.ifbr_ifsname)); + + b_req.ifbr_priority = (uint8_t) priority; + ifd.ifd_cmd = BRDGSIFPRIO; + + if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) { + syslog(LOG_ERR, "set member %s param: ioctl(BRDGSIFPRIO) " + "failed: %s", bp->p_name, strerror(errno)); + return (-1); + } + + bp->priority = priority; + return (0); +} + +/* + * Set a bridge member STP-enabled flag. + */ +int +bridge_port_set_stp_enable(const char *bif_name, struct bridge_port *bp, + uint32_t enable) +{ + struct ifdrv ifd; + struct ifbreq b_req; + + if (bp->enable == enable) + return (0); + + bzero(&b_req, sizeof(b_req)); + strlcpy(ifd.ifd_name, bif_name, sizeof(ifd.ifd_name)); + ifd.ifd_len = sizeof(b_req); + ifd.ifd_data = &b_req; + strlcpy(b_req.ifbr_ifsname, bp->p_name, sizeof(b_req.ifbr_ifsname)); + ifd.ifd_cmd = BRDGGIFFLGS; + + if (ioctl(sock, SIOCGDRVSPEC, &ifd) < 0) { + syslog(LOG_ERR, "get member %s param: ioctl(BRDGGIFFLGS) " + "failed: %s", bp->p_name, strerror(errno)); + return (-1); + } + + if (enable == dot1dStpPortEnable_enabled) + b_req.ifbr_ifsflags |= IFBIF_STP; + else + b_req.ifbr_ifsflags &= ~IFBIF_STP; + + ifd.ifd_cmd = BRDGSIFFLGS; + if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) { + syslog(LOG_ERR, "set member %s param: ioctl(BRDGSIFFLGS) " + "failed: %s", bp->p_name, strerror(errno)); + return (-1); + } + + bp->enable = enable; + return (0); +} + +/* + * Set a bridge member STP path cost. + */ +int +bridge_port_set_path_cost(const char *bif_name, struct bridge_port *bp, + int32_t path_cost) +{ + struct ifdrv ifd; + struct ifbreq b_req; + + if (path_cost < SNMP_PORT_MIN_PATHCOST || + path_cost > SNMP_PORT_PATHCOST_OBSOLETE) + return (-2); + + strlcpy(ifd.ifd_name, bif_name, sizeof(ifd.ifd_name)); + ifd.ifd_len = sizeof(b_req); + ifd.ifd_data = &b_req; + strlcpy(b_req.ifbr_ifsname, bp->p_name, sizeof(b_req.ifbr_ifsname)); + + b_req.ifbr_path_cost = path_cost; + ifd.ifd_cmd = BRDGSIFCOST; + + if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) { + syslog(LOG_ERR, "set member %s param: ioctl(BRDGSIFCOST) " + "failed: %s", bp->p_name, strerror(errno)); + return (-1); + } + + bp->admin_path_cost = path_cost; + + return (0); +} + +/* + * Set the PonitToPoint status of the link administratively. + */ +int +bridge_port_set_admin_ptp(const char *bif_name, struct bridge_port *bp, + uint32_t admin_ptp) +{ + struct ifdrv ifd; + struct ifbreq b_req; + + if (bp->admin_ptp == admin_ptp) + return (0); + + bzero(&b_req, sizeof(b_req)); + strlcpy(ifd.ifd_name, bif_name, sizeof(ifd.ifd_name)); + ifd.ifd_len = sizeof(b_req); + ifd.ifd_data = &b_req; + strlcpy(b_req.ifbr_ifsname, bp->p_name, sizeof(b_req.ifbr_ifsname)); + ifd.ifd_cmd = BRDGGIFFLGS; + + if (ioctl(sock, SIOCGDRVSPEC, &ifd) < 0) { + syslog(LOG_ERR, "get member %s param: ioctl(BRDGGIFFLGS) " + "failed: %s", bp->p_name, strerror(errno)); + return (-1); + } + + switch (admin_ptp) { + case StpPortAdminPointToPointType_forceTrue: + b_req.ifbr_ifsflags &= ~IFBIF_BSTP_AUTOPTP; + b_req.ifbr_ifsflags |= IFBIF_BSTP_PTP; + break; + case StpPortAdminPointToPointType_forceFalse: + b_req.ifbr_ifsflags &= ~IFBIF_BSTP_AUTOPTP; + b_req.ifbr_ifsflags &= ~IFBIF_BSTP_PTP; + break; + case StpPortAdminPointToPointType_auto: + b_req.ifbr_ifsflags |= IFBIF_BSTP_AUTOPTP; + break; + } + + ifd.ifd_cmd = BRDGSIFFLGS; + if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) { + syslog(LOG_ERR, "set member %s param: ioctl(BRDGSIFFLGS) " + "failed: %s", bp->p_name, strerror(errno)); + return (-1); + } + + bp->admin_ptp = admin_ptp; + return (0); +} + +/* + * Set admin edge. + */ +int +bridge_port_set_admin_edge(const char *bif_name, struct bridge_port *bp, + uint32_t enable) +{ + struct ifdrv ifd; + struct ifbreq b_req; + + if (bp->admin_edge == enable) + return (0); + + bzero(&b_req, sizeof(b_req)); + strlcpy(ifd.ifd_name, bif_name, sizeof(ifd.ifd_name)); + ifd.ifd_len = sizeof(b_req); + ifd.ifd_data = &b_req; + strlcpy(b_req.ifbr_ifsname, bp->p_name, sizeof(b_req.ifbr_ifsname)); + ifd.ifd_cmd = BRDGGIFFLGS; + + if (ioctl(sock, SIOCGDRVSPEC, &ifd) < 0) { + syslog(LOG_ERR, "get member %s param: ioctl(BRDGGIFFLGS) " + "failed: %s", bp->p_name, strerror(errno)); + return (-1); + } + + if (enable == TruthValue_true) { + b_req.ifbr_ifsflags &= ~IFBIF_BSTP_AUTOEDGE; + b_req.ifbr_ifsflags |= IFBIF_BSTP_EDGE; + } else + b_req.ifbr_ifsflags &= ~IFBIF_BSTP_EDGE; + + ifd.ifd_cmd = BRDGSIFFLGS; + if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) { + syslog(LOG_ERR, "set member %s param: ioctl(BRDGSIFFLGS) " + "failed: %s", bp->p_name, strerror(errno)); + return (-1); + } + + bp->admin_edge = enable; + + return (0); +} + +/* + * Set 'private' flag. + */ +int +bridge_port_set_private(const char *bif_name, struct bridge_port *bp, + uint32_t priv_set) +{ + struct ifdrv ifd; + struct ifbreq b_req; + + if (bp->priv_set == priv_set) + return (0); + + bzero(&b_req, sizeof(b_req)); + strlcpy(ifd.ifd_name, bif_name, sizeof(ifd.ifd_name)); + ifd.ifd_len = sizeof(b_req); + ifd.ifd_data = &b_req; + strlcpy(b_req.ifbr_ifsname, bp->p_name, sizeof(b_req.ifbr_ifsname)); + ifd.ifd_cmd = BRDGGIFFLGS; + + if (ioctl(sock, SIOCGDRVSPEC, &ifd) < 0) { + syslog(LOG_ERR, "get member %s param: ioctl(BRDGGIFFLGS) " + "failed: %s", bp->p_name, strerror(errno)); + return (-1); + } + + if (priv_set == TruthValue_true) + b_req.ifbr_ifsflags |= IFBIF_PRIVATE; + else if (priv_set == TruthValue_false) + b_req.ifbr_ifsflags &= ~IFBIF_PRIVATE; + else + return (SNMP_ERR_WRONG_VALUE); + + ifd.ifd_cmd = BRDGSIFFLGS; + if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) { + syslog(LOG_ERR, "set member %s param: ioctl(BRDGSIFFLGS) " + "failed: %s", bp->p_name, strerror(errno)); + return (-1); + } + + bp->priv_set = priv_set; + + return (0); +} + + +/* + * Add a bridge member port. + */ +int +bridge_port_addm(struct bridge_port *bp, const char *b_name) +{ + struct ifdrv ifd; + struct ifbreq b_req; + + bzero(&ifd, sizeof(ifd)); + bzero(&b_req, sizeof(b_req)); + + strlcpy(ifd.ifd_name, b_name, sizeof(ifd.ifd_name)); + ifd.ifd_len = sizeof(b_req); + ifd.ifd_data = &b_req; + strlcpy(b_req.ifbr_ifsname, bp->p_name, sizeof(b_req.ifbr_ifsname)); + + if (bp->span_enable == begemotBridgeBaseSpanEnabled_enabled) + ifd.ifd_cmd = BRDGADDS; + else + ifd.ifd_cmd = BRDGADD; + + if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) { + syslog(LOG_ERR, "%s - add member : ioctl(%s) failed: %s", + bp->p_name, + (ifd.ifd_cmd == BRDGADDS ? "BRDGADDS" : "BRDGADD"), + strerror(errno)); + return (-1); + } + + return (0); +} + +/* + * Delete a bridge member port. + */ +int +bridge_port_delm(struct bridge_port *bp, const char *b_name) +{ + struct ifdrv ifd; + struct ifbreq b_req; + + bzero(&ifd, sizeof(ifd)); + bzero(&b_req, sizeof(b_req)); + + strlcpy(ifd.ifd_name, b_name, sizeof(ifd.ifd_name)); + ifd.ifd_len = sizeof(b_req); + ifd.ifd_data = &b_req; + strlcpy(b_req.ifbr_ifsname, bp->p_name, sizeof(b_req.ifbr_ifsname)); + + if (bp->span_enable == begemotBridgeBaseSpanEnabled_enabled) + ifd.ifd_cmd = BRDGDELS; + else + ifd.ifd_cmd = BRDGDEL; + + if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) { + syslog(LOG_ERR, "%s - add member : ioctl(%s) failed: %s", + bp->p_name, + (ifd.ifd_cmd == BRDGDELS ? "BRDGDELS" : "BRDGDEL"), + strerror(errno)); + return (-1); + } + + return (0); +} + +/* + * Fetch the bridge member list from kernel. + * Return -1 on error, or buffer len if successful. + */ +static int32_t +bridge_port_get_iflist(struct bridge_if *bif, struct ifbreq **buf) +{ + int n = 128; + uint32_t len; + struct ifbreq *ninbuf; + struct ifbifconf ifbc; + struct ifdrv ifd; + + *buf = NULL; + strlcpy(ifd.ifd_name, bif->bif_name, IFNAMSIZ); + ifd.ifd_cmd = BRDGGIFS; + ifd.ifd_len = sizeof(ifbc); + ifd.ifd_data = &ifbc; + + for ( ; ; ) { + len = n * sizeof(struct ifbreq); + if ((ninbuf = (struct ifbreq *)realloc(*buf, len)) == NULL) { + syslog(LOG_ERR, "get bridge member list: " + "realloc failed: %s", strerror(errno)); + free(*buf); + *buf = NULL; + return (-1); + } + + ifbc.ifbic_len = len; + ifbc.ifbic_req = *buf = ninbuf; + + if (ioctl(sock, SIOCGDRVSPEC, &ifd) < 0) { + syslog(LOG_ERR, "get bridge member list: ioctl " + "(BRDGGIFS) failed: %s", strerror(errno)); + free(*buf); + buf = NULL; + return (-1); + } + + if ((ifbc.ifbic_len + sizeof(struct ifbreq)) < len) + break; + + n += 64; + } + + return (ifbc.ifbic_len); +} + +/* + * Fetch the bridge STP member list from kernel. + * Return -1 on error, or buffer len if successful. + */ +static int32_t +bridge_port_get_ifstplist(struct bridge_if *bif, struct ifbpstpreq **buf) +{ + int n = 128; + uint32_t len; + struct ifbpstpreq *ninbuf; + struct ifbpstpconf ifbstp; + struct ifdrv ifd; + + *buf = NULL; + strlcpy(ifd.ifd_name, bif->bif_name, IFNAMSIZ); + ifd.ifd_cmd = BRDGGIFSSTP; + ifd.ifd_len = sizeof(ifbstp); + ifd.ifd_data = &ifbstp; + + for ( ; ; ) { + len = n * sizeof(struct ifbpstpreq); + if ((ninbuf = (struct ifbpstpreq *) + realloc(*buf, len)) == NULL) { + syslog(LOG_ERR, "get bridge STP ports list: " + "realloc failed: %s", strerror(errno)); + free(*buf); + *buf = NULL; + return (-1); + } + + ifbstp.ifbpstp_len = len; + ifbstp.ifbpstp_req = *buf = ninbuf; + + if (ioctl(sock, SIOCGDRVSPEC, &ifd) < 0) { + syslog(LOG_ERR, "get bridge STP ports list: ioctl " + "(BRDGGIFSSTP) failed: %s", strerror(errno)); + free(*buf); + buf = NULL; + return (-1); + } + + if ((ifbstp.ifbpstp_len + sizeof(struct ifbpstpreq)) < len) + break; + + n += 64; + } + + return (ifbstp.ifbpstp_len); +} + +/* + * Locate a bridge if STP params structure in a buffer. + */ +static struct ifbpstpreq * +bridge_port_find_ifstplist(uint8_t port_no, struct ifbpstpreq *buf, + uint32_t buf_len) +{ + uint32_t i; + struct ifbpstpreq *bstp; + + for (i = 0; i < buf_len / sizeof(struct ifbpstpreq); i++) { + bstp = buf + i; + if (bstp->ifbp_portno == port_no) + return (bstp); + } + + return (NULL); +} + +/* + * Read the initial info for all members of a bridge interface. + * Returns the number of ports, 0 - if none, otherwise + * -1 if some other error occurred. + */ +int +bridge_getinfo_bif_ports(struct bridge_if *bif) +{ + uint32_t i; + int32_t buf_len; + struct ifbreq *b_req_buf, *b_req; + struct ifbpstpreq *bs_req_buf, *bs_req; + struct bridge_port *bp; + struct mibif *m_if; + + if ((buf_len = bridge_port_get_iflist(bif, &b_req_buf)) < 0) + return (-1); + + for (i = 0; i < buf_len / sizeof(struct ifbreq); i++) { + b_req = b_req_buf + i; + + if ((m_if = mib_find_if_sys(b_req->ifbr_portno)) != NULL) { + /* Hopefully we will not fail here. */ + if ((bp = bridge_new_port(m_if, bif)) != NULL) { + bp->status = RowStatus_active; + bridge_port_getinfo_conf(b_req, bp); + bridge_port_getinfo_mibif(m_if, bp); + } + } else { + syslog(LOG_ERR, "bridge member %s not present " + "in mibII ifTable", b_req->ifbr_ifsname); + } + } + free(b_req_buf); + + if ((buf_len = bridge_port_get_ifstplist(bif, &bs_req_buf)) < 0) + return (-1); + + for (bp = bridge_port_bif_first(bif); bp != NULL; + bp = bridge_port_bif_next(bp)) { + if ((bs_req = bridge_port_find_ifstplist(bp->port_no, + bs_req_buf, buf_len)) == NULL) + bridge_port_clearinfo_opstp(bp); + else + bridge_port_getinfo_opstp(bs_req, bp); + } + free(bs_req_buf); + + return (i); +} + +/* + * Update the information for the bridge interface members. + */ +int +bridge_update_memif(struct bridge_if *bif) +{ + int added, updated; + uint32_t i; + int32_t buf_len; + struct ifbreq *b_req_buf, *b_req; + struct ifbpstpreq *bs_req_buf, *bs_req; + struct bridge_port *bp, *bp_next; + struct mibif *m_if; + + if ((buf_len = bridge_port_get_iflist(bif, &b_req_buf)) < 0) + return (-1); + + added = updated = 0; + +#define BP_FOUND 0x01 + for (i = 0; i < buf_len / sizeof(struct ifbreq); i++) { + b_req = b_req_buf + i; + + if ((m_if = mib_find_if_sys(b_req->ifbr_portno)) == NULL) { + syslog(LOG_ERR, "bridge member %s not present " + "in mibII ifTable", b_req->ifbr_ifsname); + continue; + } + + if ((bp = bridge_port_find(m_if->index, bif)) == NULL && + (bp = bridge_new_port(m_if, bif)) != NULL) { + bp->status = RowStatus_active; + added++; + } + + if (bp != NULL) { + updated++; + bridge_port_getinfo_conf(b_req, bp); + bridge_port_getinfo_mibif(m_if, bp); + bp->flags |= BP_FOUND; + } + } + free(b_req_buf); + + /* Clean up list. */ + for (bp = bridge_port_bif_first(bif); bp != NULL; bp = bp_next) { + bp_next = bridge_port_bif_next(bp); + + if ((bp->flags & BP_FOUND) == 0 && + bp->status == RowStatus_active) + bridge_port_remove(bp, bif); + else + bp->flags |= ~BP_FOUND; + } +#undef BP_FOUND + + if ((buf_len = bridge_port_get_ifstplist(bif, &bs_req_buf)) < 0) + return (-1); + + for (bp = bridge_port_bif_first(bif); bp != NULL; + bp = bridge_port_bif_next(bp)) { + if ((bs_req = bridge_port_find_ifstplist(bp->port_no, + bs_req_buf, buf_len)) == NULL) + bridge_port_clearinfo_opstp(bp); + else + bridge_port_getinfo_opstp(bs_req, bp); + } + free(bs_req_buf); + bif->ports_age = time(NULL); + + return (updated); +} + +/************************************************************************ + * Bridge addresses. + */ + +/* + * Update the bridge address info according to the polled data. + */ +static void +bridge_addrs_info_ifaddrlist(struct ifbareq *ifba, struct tp_entry *tpe) +{ + tpe->port_no = if_nametoindex(ifba->ifba_ifsname); + + if ((ifba->ifba_flags & IFBAF_TYPEMASK) == IFBAF_STATIC) + tpe->status = TpFdbStatus_mgmt; + else + tpe->status = TpFdbStatus_learned; +} + +/* + * Read the bridge addresses from kernel. + * Return -1 on error, or buffer len if successful. + */ +static int32_t +bridge_addrs_getinfo_ifalist(struct bridge_if *bif, struct ifbareq **buf) +{ + int n = 128; + uint32_t len; + struct ifbareq *ninbuf; + struct ifbaconf bac; + struct ifdrv ifd; + + *buf = NULL; + strlcpy(ifd.ifd_name, bif->bif_name, IFNAMSIZ); + ifd.ifd_cmd = BRDGRTS; + ifd.ifd_len = sizeof(bac); + ifd.ifd_data = &bac; + + for ( ; ; ) { + len = n * sizeof(struct ifbareq); + if ((ninbuf = (struct ifbareq *)realloc(*buf, len)) == NULL) { + syslog(LOG_ERR, "get bridge address list: " + " realloc failed: %s", strerror(errno)); + free(*buf); + *buf = NULL; + return (-1); + } + + bac.ifbac_len = len; + bac.ifbac_req = *buf = ninbuf; + + if (ioctl(sock, SIOCGDRVSPEC, &ifd) < 0) { + syslog(LOG_ERR, "get bridge address list: " + "ioctl(BRDGRTS) failed: %s", strerror(errno)); + free(*buf); + buf = NULL; + return (-1); + } + + if ((bac.ifbac_len + sizeof(struct ifbareq)) < len) + break; + + n += 64; + } + + return (bac.ifbac_len); +} + +/* + * Read the initial info for all addresses on a bridge interface. + * Returns the number of addresses, 0 - if none, otherwise + * -1 if some other error occurred. + */ +int +bridge_getinfo_bif_addrs(struct bridge_if *bif) +{ + uint32_t i; + int32_t buf_len; + struct ifbareq *addr_req_buf, *addr_req; + struct tp_entry *te; + + if ((buf_len = bridge_addrs_getinfo_ifalist(bif, &addr_req_buf)) < 0) + return (-1); + + for (i = 0; i < buf_len / sizeof(struct ifbareq); i++) { + addr_req = addr_req_buf + i; + + if ((te = bridge_new_addrs(addr_req->ifba_dst, bif)) != NULL) + bridge_addrs_info_ifaddrlist(addr_req, te); + } + + free(addr_req_buf); + return (i); +} + +/* + * Update the addresses for the bridge interface. + */ +int +bridge_update_addrs(struct bridge_if *bif) +{ + int added, updated; + uint32_t i; + int32_t buf_len; + struct tp_entry *te, *te_next; + struct ifbareq *addr_req_buf, *addr_req; + + if ((buf_len = bridge_addrs_getinfo_ifalist(bif, &addr_req_buf)) < 0) + return (-1); + + added = updated = 0; + +#define BA_FOUND 0x01 + for (i = 0; i < buf_len / sizeof(struct ifbareq); i++) { + addr_req = addr_req_buf + i; + + if ((te = bridge_addrs_find(addr_req->ifba_dst, bif)) == NULL) { + added++; + + if ((te = bridge_new_addrs(addr_req->ifba_dst, bif)) + == NULL) + continue; + } else + updated++; + + bridge_addrs_info_ifaddrlist(addr_req, te); + te-> flags |= BA_FOUND; + } + free(addr_req_buf); + + for (te = bridge_addrs_bif_first(bif); te != NULL; te = te_next) { + te_next = bridge_addrs_bif_next(te); + + if ((te-> flags & BA_FOUND) == 0) + bridge_addrs_remove(te, bif); + else + te-> flags &= ~BA_FOUND; + } +#undef BA_FOUND + + bif->addrs_age = time(NULL); + return (updated + added); +} + +/************************************************************************ + * Bridge packet filtering. + */ +const char bridge_sysctl[] = "net.link.bridge."; + +static struct { + int32_t val; + const char *name; +} bridge_pf_sysctl[] = { + { 1, "pfil_bridge" }, + { 1, "pfil_member" }, + { 1, "pfil_onlyip" }, + { 0, "ipfw" }, +}; + +int32_t +bridge_get_pfval(uint8_t which) +{ + if (which > sizeof(bridge_pf_sysctl) / sizeof(bridge_pf_sysctl[0]) + || which < 1) + return (-1); + + return (bridge_pf_sysctl[which - 1].val); +} + +int32_t +bridge_do_pfctl(int32_t bridge_ctl, enum snmp_op op, int32_t *val) +{ + char mib_name[100]; + int32_t i, s_i; + size_t len, s_len; + + if (bridge_ctl >= LEAF_begemotBridgeLayer2PfStatus) + return (-2); + + if (op == SNMP_OP_SET) { + s_i = *val; + s_len = sizeof(s_i); + } else + s_len = 0; + + len = sizeof(i); + + strcpy(mib_name, bridge_sysctl); + + if (sysctlbyname(strcat(mib_name, + bridge_pf_sysctl[bridge_ctl].name), &i, &len, + (op == SNMP_OP_SET ? &s_i : NULL), s_len) == -1) { + syslog(LOG_ERR, "sysctl(%s%s) failed - %s", bridge_sysctl, + bridge_pf_sysctl[bridge_ctl].name, strerror(errno)); + return (-1); + } + + bridge_pf_sysctl[bridge_ctl].val = i; + *val = i; + + return (i); +} + +void +bridge_pf_dump(void) +{ + uint8_t i; + + for (i = 0; i < sizeof(bridge_pf_sysctl) / sizeof(bridge_pf_sysctl[0]); + i++) { + syslog(LOG_ERR, "%s%s = %d", bridge_sysctl, + bridge_pf_sysctl[i].name, bridge_pf_sysctl[i].val); + } +} diff --git a/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_tree.def b/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_tree.def new file mode 100644 index 0000000..2e88e5e --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_tree.def @@ -0,0 +1,283 @@ +#- +# Copyright (c) 2006 Shteryana Shopova <syrinx@FreeBSD.org> +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# + +#include "tc.def" + +typedef RowStatus ENUM ( + 1 active + 2 notInService + 3 notReady + 4 createAndGo + 5 createAndWait + 6 destroy +) + +typedef TruthValue ENUM ( + 1 true + 2 false +) + +typedef StpPortState ENUM ( + 1 disabled + 2 blocking + 3 listening + 4 learning + 5 forwarding + 6 broken +) + +typedef StpPortAdminPointToPointType ENUM ( + 0 forceTrue + 1 forceFalse + 2 auto +) + +typedef BaseType ENUM ( + 1 unknown + 2 transparent-only + 3 sourceroute-only + 4 srt +) + +typedef TpFdbStatus ENUM ( + 1 other + 2 invalid + 3 learned + 4 self + 5 mgmt +) + +(1 internet + (2 mgmt + (1 mib_2 + (17 dot1dBridge + (0 dot1dNotifications + (1 newRoot OID op_snmp_trap) + (2 topologyChange OID op_snmp_trap) + ) + (1 dot1dBase + (1 dot1dBaseBridgeAddress OCTETSTRING | MacAddress op_dot1d_base GET) + (2 dot1dBaseNumPorts INTEGER32 op_dot1d_base GET) + (3 dot1dBaseType BaseType op_dot1d_base GET) + (4 dot1dBasePortTable + (1 dot1dBasePortEntry : INTEGER op_dot1d_base_port + (1 dot1dBasePort INTEGER GET) + (2 dot1dBasePortIfIndex INTEGER GET) + (3 dot1dBasePortCircuit OID GET) + (4 dot1dBasePortDelayExceededDiscards COUNTER GET) + (5 dot1dBasePortMtuExceededDiscards COUNTER GET) + )) + ) + (2 dot1dStp + (1 dot1dStpProtocolSpecification ENUM ( 1 unknown 2 decLb100 3 ieee8021d ) op_dot1d_stp GET) + (2 dot1dStpPriority INTEGER op_dot1d_stp GET SET) + (3 dot1dStpTimeSinceTopologyChange TIMETICKS op_dot1d_stp GET) + (4 dot1dStpTopChanges COUNTER op_dot1d_stp GET) + (5 dot1dStpDesignatedRoot OCTETSTRING | BridgeId op_dot1d_stp GET) + (6 dot1dStpRootCost INTEGER32 op_dot1d_stp GET) + (7 dot1dStpRootPort INTEGER32 op_dot1d_stp GET) + (8 dot1dStpMaxAge INTEGER op_dot1d_stp GET) + (9 dot1dStpHelloTime INTEGER op_dot1d_stp GET) + (10 dot1dStpHoldTime INTEGER32 op_dot1d_stp GET) + (11 dot1dStpForwardDelay INTEGER op_dot1d_stp GET) + (12 dot1dStpBridgeMaxAge INTEGER op_dot1d_stp GET SET) + (13 dot1dStpBridgeHelloTime INTEGER op_dot1d_stp GET SET) + (14 dot1dStpBridgeForwardDelay INTEGER op_dot1d_stp GET SET) + (15 dot1dStpPortTable + (1 dot1dStpPortEntry : INTEGER op_dot1d_stp_port + (1 dot1dStpPort INTEGER GET) + (2 dot1dStpPortPriority INTEGER GET SET) + (3 dot1dStpPortState StpPortState GET) + (4 dot1dStpPortEnable ENUM ( 1 enabled 2 disabled ) GET SET) + (5 dot1dStpPortPathCost INTEGER GET SET) + (6 dot1dStpPortDesignatedRoot OCTETSTRING | BridgeId GET) + (7 dot1dStpPortDesignatedCost INTEGER32 GET) + (8 dot1dStpPortDesignatedBridge OCTETSTRING | BridgeId GET) + (9 dot1dStpPortDesignatedPort OCTETSTRING | BridgePortId GET) + (10 dot1dStpPortForwardTransitions COUNTER GET) + )) + (16 dot1dStpVersion ENUM ( 0 stpCompatible 2 rstp ) op_dot1d_stp GET SET) + (17 dot1dStpTxHoldCount INTEGER op_dot1d_stp GET SET) + (19 dot1dStpExtPortTable + (1 dot1dStpExtPortEntry : INTEGER op_dot1d_stp_ext_port + (1 dot1dStpPortProtocolMigration TruthValue GET) # SET + (2 dot1dStpPortAdminEdgePort TruthValue GET SET) + (3 dot1dStpPortOperEdgePort TruthValue GET) + (4 dot1dStpPortAdminPointToPoint StpPortAdminPointToPointType GET SET) + (5 dot1dStpPortOperPointToPoint TruthValue GET) + (6 dot1dStpPortAdminPathCost INTEGER GET SET) + )) + ) + (3 dot1dSr + ) + (4 dot1dTp + (1 dot1dTpLearnedEntryDiscards COUNTER op_dot1d_tp GET) + (2 dot1dTpAgingTime INTEGER op_dot1d_tp GET SET) + (3 dot1dTpFdbTable + (1 dot1dTpFdbEntry : OCTETSTRING | MacAddress op_dot1d_tp_fdb + (1 dot1dTpFdbAddress OCTETSTRING | MacAddress GET) + (2 dot1dTpFdbPort INTEGER32 GET) + (3 dot1dTpFdbStatus TpFdbStatus GET) + )) + (4 dot1dTpPortTable + (1 dot1dTpPortEntry : INTEGER op_dot1d_tp_port + (1 dot1dTpPort INTEGER GET) + (2 dot1dTpPortMaxInfo INTEGER32 GET) + (3 dot1dTpPortInFrames COUNTER GET) + (4 dot1dTpPortOutFrames COUNTER GET) + (5 dot1dTpPortInDiscards COUNTER GET) + )) + ) + (5 dot1dStatic + ) + (8 dot1dConformance + (1 dot1dGroups + ) + (2 dot1dCompliances + ) + ) + ) + (134 rstpMIB + (0 rstpNotifications + ) + (1 rstpObjects + ) + (2 rstpConformance + (1 rstpGroups + ) + (2 rstpCompliances + ) + ) + ))) + (4 private + (1 enterprises + (12325 fokus + (1 begemot + (205 begemotBridge + (0 begemotBridgeNotifications + (1 begemotBridgeNewRoot OID op_snmp_trap) + (2 begemotBridgeTopologyChange OID op_snmp_trap) + ) + (1 begemotBridgeBase + (1 begemotBridgeBaseTable + (1 begemotBridgeBaseEntry : OCTETSTRING | BridgeIfName op_begemot_base_bridge + (1 begemotBridgeBaseName OCTETSTRING | BridgeIfName GET) + (2 begemotBridgeBaseAddress OCTETSTRING | MacAddress GET) + (3 begemotBridgeBaseNumPorts INTEGER32 GET) + (4 begemotBridgeBaseType BaseType GET) + (5 begemotBridgeBaseStatus RowStatus GET SET) + )) + (2 begemotBridgeBasePortTable + (1 begemotBridgeBasePortEntry : OCTETSTRING | BridgeIfName INTEGER op_begemot_base_port + (1 begemotBridgeBasePort INTEGER GET) + (2 begemotBridgeBasePortIfIndex INTEGER GET) + (3 begemotBridgeBaseSpanEnabled ENUM ( 1 enabled 2 disabled ) GET SET) + (4 begemotBridgeBasePortDelayExceededDiscards COUNTER GET) + (5 begemotBridgeBasePortMtuExceededDiscards COUNTER GET) + (6 begemotBridgeBasePortStatus RowStatus GET SET) + (7 begemotBridgeBasePortPrivate TruthValue GET SET) + )) + ) + (2 begemotBridgeStp + (1 begemotBridgeStpTable + (1 begemotBridgeStpEntry : OCTETSTRING | BridgeIfName op_begemot_stp + (1 begemotBridgeStpProtocolSpecification ENUM ( 1 unknown 2 decLb100 3 ieee8021d ) GET) + (2 begemotBridgeStpPriority INTEGER GET SET) + (3 begemotBridgeStpTimeSinceTopologyChange TIMETICKS GET) + (4 begemotBridgeStpTopChanges COUNTER GET) + (5 begemotBridgeStpDesignatedRoot OCTETSTRING | BridgeId GET) + (6 begemotBridgeStpRootCost INTEGER32 GET) + (7 begemotBridgeStpRootPort INTEGER32 GET) + (8 begemotBridgeStpMaxAge INTEGER GET) + (9 begemotBridgeStpHelloTime INTEGER GET) + (10 begemotBridgeStpHoldTime INTEGER32 GET) + (11 begemotBridgeStpForwardDelay INTEGER GET) + (12 begemotBridgeStpBridgeMaxAge INTEGER GET SET) + (13 begemotBridgeStpBridgeHelloTime INTEGER GET SET) + (14 begemotBridgeStpBridgeForwardDelay INTEGER GET SET) + (15 begemotBridgeStpVersion ENUM ( 0 stpCompatible 2 rstp ) GET SET) + (16 begemotBridgeStpTxHoldCount INTEGER GET SET) + )) + (2 begemotBridgeStpPortTable + (1 begemotBridgeStpPortEntry : OCTETSTRING | BridgeIfName INTEGER op_begemot_stp_port + (1 begemotBridgeStpPort INTEGER GET) + (2 begemotBridgeStpPortPriority INTEGER GET SET) + (3 begemotBridgeStpPortState StpPortState GET) + (4 begemotBridgeStpPortEnable ENUM ( 1 enabled 2 disabled ) GET SET) + (5 begemotBridgeStpPortPathCost INTEGER GET SET) + (6 begemotBridgeStpPortDesignatedRoot OCTETSTRING | BridgeId GET) + (7 begemotBridgeStpPortDesignatedCost INTEGER32 GET) + (8 begemotBridgeStpPortDesignatedBridge OCTETSTRING | BridgeId GET) + (9 begemotBridgeStpPortDesignatedPort OCTETSTRING | BridgePortId GET) + (10 begemotBridgeStpPortForwardTransitions COUNTER GET) + )) + (3 begemotBridgeStpExtPortTable + (1 begemotBridgeStpExtPortEntry : OCTETSTRING | BridgeIfName INTEGER op_begemot_stp_ext_port + (1 begemotBridgeStpPortProtocolMigration TruthValue GET) # SET + (2 begemotBridgeStpPortAdminEdgePort TruthValue GET SET) + (3 begemotBridgeStpPortOperEdgePort TruthValue GET) + (4 begemotBridgeStpPortAdminPointToPoint StpPortAdminPointToPointType GET SET) + (5 begemotBridgeStpPortOperPointToPoint TruthValue GET) + (6 begemotBridgeStpPortAdminPathCost INTEGER GET SET) + )) + ) + (3 begemotBridgeTp + (1 begemotBridgeTpTable + (1 begemotBridgeTpEntry : OCTETSTRING | BridgeIfName op_begemot_tp + (1 begemotBridgeTpLearnedEntryDiscards COUNTER GET) + (2 begemotBridgeTpAgingTime INTEGER GET SET) + (3 begemotBridgeTpMaxAddresses INTEGER GET SET) + )) + (2 begemotBridgeTpFdbTable + (1 begemotBridgeTpFdbEntry : OCTETSTRING | BridgeIfName OCTETSTRING | MacAddress op_begemot_tp_fdb + (1 begemotBridgeTpFdbAddress OCTETSTRING | MacAddress GET) + (2 begemotBridgeTpFdbPort INTEGER32 GET) + (3 begemotBridgeTpFdbStatus TpFdbStatus GET) + )) + (3 begemotBridgeTpPortTable + (1 begemotBridgeTpPortEntry : OCTETSTRING | BridgeIfName INTEGER op_begemot_tp_port + (1 begemotBridgeTpPort INTEGER GET) + (2 begemotBridgeTpPortMaxInfo INTEGER32 GET) + (3 begemotBridgeTpPortInFrames COUNTER GET) + (4 begemotBridgeTpPortOutFrames COUNTER GET) + (5 begemotBridgeTpPortInDiscards COUNTER GET) + )) + ) + (4 begemotBridgePf + (1 begemotBridgePfilStatus TruthValue op_begemot_bridge_pf GET SET) + (2 begemotBridgePfilMembers TruthValue op_begemot_bridge_pf GET SET) + (3 begemotBridgePfilIpOnly TruthValue op_begemot_bridge_pf GET SET) + (4 begemotBridgeLayer2PfStatus ENUM ( 1 enabled 2 disabled ) op_begemot_bridge_pf GET SET) + ) + (5 begemotBridgeConfigObjects + (1 begemotBridgeDefaultBridgeIf OCTETSTRING | BridgeIfNameOrEmpty op_begemot_bridge_config GET SET) + (2 begemotBridgeDataUpdate INTEGER op_begemot_bridge_config GET SET) + (3 begemotBridgeDataPoll INTEGER op_begemot_bridge_config GET SET) + ) + ))))) +) diff --git a/usr.sbin/bsnmpd/modules/snmp_bridge/snmp_bridge.3 b/usr.sbin/bsnmpd/modules/snmp_bridge/snmp_bridge.3 new file mode 100644 index 0000000..b77b5f7 --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_bridge/snmp_bridge.3 @@ -0,0 +1,119 @@ +.\"- +.\" Copyright (C) 2006 Shteryana Shopova <syrinx@FreeBSD.org> +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd August 6, 2007 +.Dt SNMP_BRIDGE 3 +.Os +.Sh NAME +.Nm snmp_bridge +.Nd "bridge module for snmpd" +.Sh LIBRARY +.Pq begemotSnmpdModulePath."bridge" = "/usr/lib/snmp_bridge.so" +.Sh DESCRIPTION +The +.Nm snmp_bridge +module implements the BRIDGE-MIB as standardized in RFC 4188, the RSTP-MIB +standardized in RFC4318 and a private BEGEMOT-BRIDGE-MIB, which allows +management of multiple bridge interfaces. +Most of the objects defined in the private BEGEMOT-BRIDGE-MIB are duplicates +of the original objects defined by the standard BRIDGE-MIB, but the private +MIB also defines additional objects which make the functionality of +.Nm +similar to +.Xr ifconfig 8 +for configuring bridge interfaces. +Therefore one should consider adding write communities or loading the +.Nm +module on systems where security is crucial. +.Sh IMPLEMENTATION NOTES +The additional objects to configure a bridge are: +.Bl -tag -width "XXXXXXXXX" +.It Va begemotBridgeBaseStatus +Bridge interfaces can be created and destroyed via this object. +SNMP SET operations with the following values are allowed: +.Bl -tag -width ".It Va createAndWait" +.It Va createAndWait +will attempt to create a bridge interface with the name given by the table +index. +.It Va createAndGo +will attempt to create a bridge interface with the name given by the table +index and set the status of the interface to "active/up". +.It Va destroy +will attempt to destroy the bridge interface. +.El +.It Va begemotBridgeBaseSpanEnabled +A SNMP SET operation on this object is only successful if the corresponding +port has not been added as member of the bridge interface on the system. +.It Va begemotBridgeBasePortStatus +SNMP SET operations with the following values are allowed: +.Bl -tag -width ".It Va createAndWait" +.It Va createAndWait +will create a new row for the bridge member in the SNMP +.Va begemotBridgeBasePortTable +but will not try to commit the information to the system. +.It Va active +will attempt to commit the information to the system and will be successful +only if a value for +.Va begemotBridgeBaseSpanEnabled +has been SET already. +.It Va destroy +will attempt to remove the interface from the system bridge interface. +.El +.It Va begemotBridgeBasePortPrivate +This object controls a bridge interface flag called PRIVATE where any private +port can not communicate with another private port. +.El +.Sh RESTRICTIONS +Not all information in the MIBs is currently available in FreeBSD. +The following variables carry no information: +.Bl -tag -width "XXXXXXXXX" +.It Va dot1dBasePortCircuit +.It Va dot1dBasePortDelayExceededDiscards +.It Va dot1dBasePortMtuExceededDiscards +.It Va begemotBridgeBasePortDelayExceededDiscards +.It Va begemotBridgeBasePortMtuExceededDiscards +.El +.Sh FILES +.Bl -tag -width "XXXXXXXXX" +.It Pa /usr/share/snmp/defs/bridge_tree.def +The description of the MIB tree implemented by +.Nm . +.It Pa /usr/share/snmp/mibs/BRIDGE-MIB.txt +This is the BRIDGE-MIB that is implemented by this module. +.It Pa /usr/share/snmp/mibs/RSTP-MIB.txt +This is the RSTP-MIB implemented by this module. +.It Pa /usr/share/snmp/mibs/BEGEMOT-BRIDGE-MIB.txt +This is the private BEGEMOT-BRIDGE-MIB that is implemented by this module. +.El +.Sh SEE ALSO +.Xr bsnmpd 1 , +.Xr gensnmptree 1 , +.Xr if_bridge 4 , +.Xr ifconfig 8 , +.Xr snmpmod 3 +.Sh AUTHORS +.An Shteryana Shopova Aq syrinx@FreeBSD.org diff --git a/usr.sbin/bsnmpd/modules/snmp_hostres/BEGEMOT-HOSTRES-MIB.txt b/usr.sbin/bsnmpd/modules/snmp_hostres/BEGEMOT-HOSTRES-MIB.txt new file mode 100644 index 0000000..ee8d284 --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_hostres/BEGEMOT-HOSTRES-MIB.txt @@ -0,0 +1,125 @@ +-- +-- Copyright (c) 2005-2006 +-- Hartmut Brandt +-- All rights reserved. +-- +-- Author: Harti Brandt <harti@freebsd.org> +-- +-- Redistribution and use in source and binary forms, with or without +-- modification, are permitted provided that the following conditions +-- are met: +-- 1. Redistributions of source code must retain the above copyright +-- notice, this list of conditions and the following disclaimer. +-- 2. Redistributions in binary form must reproduce the above copyright +-- notice, this list of conditions and the following disclaimer in the +-- documentation and/or other materials provided with the distribution. +-- +-- THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND +-- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +-- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +-- ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE +-- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +-- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +-- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +-- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +-- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +-- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +-- SUCH DAMAGE. +-- +-- $FreeBSD$ +-- +-- Additional stuff for the HOST-RESOURCES MIB. +-- +BEGEMOT-HOSTRES-MIB DEFINITIONS ::= BEGIN + +IMPORTS + MODULE-IDENTITY, OBJECT-TYPE, TimeTicks + FROM SNMPv2-SMI + begemot + FROM BEGEMOT-MIB; + +begemotHostres MODULE-IDENTITY + LAST-UPDATED "200601030000Z" + ORGANIZATION "German Aerospace Center" + CONTACT-INFO + " Hartmut Brandt + + Postal: German Aerospace Center + Oberpfaffenhofen + 82234 Wessling + Germany + + Fax: +49 8153 28 2843 + + E-mail: harti@freebsd.org" + DESCRIPTION + "The MIB for additional HOST-RESOURCES data." + ::= { begemot 202 } + +begemotHostresObjects OBJECT IDENTIFIER ::= { begemotHostres 1 } + +begemotHrStorageUpdate OBJECT-TYPE + SYNTAX TimeTicks + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The maximum number of ticks the storage table is cached." + DEFVAL { 700 } + ::= { begemotHostresObjects 1 } + +begemotHrFSUpdate OBJECT-TYPE + SYNTAX TimeTicks + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The maximum number of ticks the FS table is cached." + DEFVAL { 700 } + ::= { begemotHostresObjects 2 } + +begemotHrDiskStorageUpdate OBJECT-TYPE + SYNTAX TimeTicks + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The maximum number of ticks the disk storage table is cached." + DEFVAL { 300 } + ::= { begemotHostresObjects 3 } + +begemotHrNetworkUpdate OBJECT-TYPE + SYNTAX TimeTicks + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The maximum number of ticks the network table is cached." + DEFVAL { 700 } + ::= { begemotHostresObjects 4 } + +begemotHrSWInstalledUpdate OBJECT-TYPE + SYNTAX TimeTicks + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The maximum number of ticks the hrSWInstalledTable is cached." + DEFVAL { 1200 } + ::= { begemotHostresObjects 5 } + +begemotHrSWRunUpdate OBJECT-TYPE + SYNTAX TimeTicks + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The maximum number of ticks the hrSWRunTable and + hrSWRunPerfTable are cached." + DEFVAL { 300 } + ::= { begemotHostresObjects 6 } + +begemotHrPkgDir OBJECT-TYPE + SYNTAX OCTET STRING + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The path to the package DB directory." + DEFVAL { "/var/db/pkg" } + ::= { begemotHostresObjects 7 } + +END diff --git a/usr.sbin/bsnmpd/modules/snmp_hostres/Makefile b/usr.sbin/bsnmpd/modules/snmp_hostres/Makefile new file mode 100644 index 0000000..2922f45 --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_hostres/Makefile @@ -0,0 +1,82 @@ +# +# Copyright (c) 2005-2006 The FreeBSD Project +# All rights reserved. +# Author: Victor Cruceru <soc-victor@freebsd.org> +# +# Redistribution of this software and documentation and use in source and +# binary forms, with or without modification, are permitted provided that +# the following conditions are met: +# +# 1. Redistributions of source code or documentation must retain the above +# copyright notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# + +LPRSRC= ${.CURDIR}/../../../lpr/common_source +.PATH: ${LPRSRC} + +MOD= hostres +SRCS= hostres_begemot.c \ + hostres_device_tbl.c \ + hostres_diskstorage_tbl.c \ + hostres_fs_tbl.c \ + hostres_network_tbl.c \ + hostres_partition_tbl.c \ + hostres_printer_tbl.c \ + hostres_processor_tbl.c \ + hostres_scalars.c \ + hostres_snmp.c \ + hostres_storage_tbl.c \ + hostres_swinstalled_tbl.c \ + hostres_swrun_tbl.c \ + printcap.c + +#Not having NDEBUG defined will enable assertions and a lot of output on stderr +CFLAGS+= -DNDEBUG -I${LPRSRC} +XSYM= host hrStorageOther hrStorageRam hrStorageVirtualMemory \ + hrStorageFixedDisk hrStorageRemovableDisk hrStorageFloppyDisk \ + hrStorageCompactDisc hrStorageRamDisk hrStorageFlashMemory \ + hrStorageNetworkDisk hrDeviceOther hrDeviceUnknown \ + hrDeviceProcessor hrDeviceNetwork hrDevicePrinter \ + hrDeviceDiskStorage hrDeviceVideo hrDeviceAudio \ + hrDeviceCoprocessor hrDeviceKeyboard hrDeviceModem \ + hrDeviceParallelPort hrDevicePointing \ + hrDeviceSerialPort hrDeviceTape hrDeviceClock \ + hrDeviceVolatileMemory hrDeviceNonVolatileMemory \ + hrFSOther hrFSUnknown hrFSBerkeleyFFS hrFSSys5FS hrFSFat\ + hrFSHPFS hrFSHFS hrFSMFS hrFSNTFS hrFSVNode hrFSJournaled \ + hrFSiso9660 hrFSRockRidge hrFSNFS hrFSNetware hrFSAFS hrFSDFS \ + hrFSAppleshare hrFSRFS hrFSDGCFS hrFSBFS hrFSFAT32 hrFSLinuxExt2 + +MAN= snmp_hostres.3 + +DEFS= ${MOD}_tree.def +BMIBS= BEGEMOT-HOSTRES-MIB.txt + +DPADD= ${LIBKVM} ${LIBDEVINFO} ${LIBM} ${LIBGEOM} ${LIBMEMSTAT} +LDADD= -lkvm -ldevinfo -lm -lgeom -lmemstat + +.include <bsd.snmpmod.mk> + +printcap.So: printcap.c + ${CC} ${PICFLAG} -DPIC ${CFLAGS:C/^-W.*//} -c ${.IMPSRC} -o ${.TARGET} + +smilint: + env SMIPATH=.:/usr/share/snmp/mibs:/usr/local/share/snmp/mibs \ + smilint -c /dev/null -l6 -i group-membership BEGEMOT-HOSTRES-MIB diff --git a/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_begemot.c b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_begemot.c new file mode 100644 index 0000000..f1cc5e3 --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_begemot.c @@ -0,0 +1,171 @@ +/*- + * Copyright (c) 2005-2006. + * Hartmut Brandt. + * All rights reserved. + * + * Author: Hartmut Brandt <harti@freebsd.org> + * + * Redistribution of this software and documentation and use in source and + * binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code or documentation must retain the above + * copyright notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include <stdlib.h> + +#include "hostres_snmp.h" +#include "hostres_oid.h" +#include "hostres_tree.h" + +int +op_begemot(struct snmp_context *ctx, struct snmp_value *value, + u_int sub, u_int iidx __unused, enum snmp_op op) +{ + + switch (op) { + + case SNMP_OP_GET: + switch (value->var.subs[sub - 1]) { + + case LEAF_begemotHrStorageUpdate: + value->v.uint32 = storage_tbl_refresh; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotHrFSUpdate: + value->v.uint32 = fs_tbl_refresh; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotHrDiskStorageUpdate: + value->v.uint32 = disk_storage_tbl_refresh; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotHrNetworkUpdate: + value->v.uint32 = network_tbl_refresh; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotHrSWInstalledUpdate: + value->v.uint32 = swins_tbl_refresh; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotHrSWRunUpdate: + value->v.uint32 = swrun_tbl_refresh; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotHrPkgDir: + return (string_get(value, pkg_dir, -1)); + } + abort(); + + case SNMP_OP_GETNEXT: + abort(); + + case SNMP_OP_SET: + switch (value->var.subs[sub - 1]) { + + case LEAF_begemotHrStorageUpdate: + ctx->scratch->int1 = storage_tbl_refresh; + storage_tbl_refresh = value->v.uint32; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotHrFSUpdate: + ctx->scratch->int1 = fs_tbl_refresh; + fs_tbl_refresh = value->v.uint32; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotHrDiskStorageUpdate: + ctx->scratch->int1 = disk_storage_tbl_refresh; + disk_storage_tbl_refresh = value->v.uint32; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotHrNetworkUpdate: + ctx->scratch->int1 = network_tbl_refresh; + network_tbl_refresh = value->v.uint32; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotHrSWInstalledUpdate: + ctx->scratch->int1 = swins_tbl_refresh; + swins_tbl_refresh = value->v.uint32; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotHrSWRunUpdate: + ctx->scratch->int1 = swrun_tbl_refresh; + swrun_tbl_refresh = value->v.uint32; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotHrPkgDir: + return (string_save(value, ctx, -1, &pkg_dir)); + } + abort(); + + case SNMP_OP_COMMIT: + switch (value->var.subs[sub - 1]) { + + case LEAF_begemotHrStorageUpdate: + case LEAF_begemotHrFSUpdate: + case LEAF_begemotHrDiskStorageUpdate: + case LEAF_begemotHrNetworkUpdate: + case LEAF_begemotHrSWInstalledUpdate: + case LEAF_begemotHrSWRunUpdate: + return (SNMP_ERR_NOERROR); + + case LEAF_begemotHrPkgDir: + string_commit(ctx); + return (SNMP_ERR_NOERROR); + } + abort(); + + case SNMP_OP_ROLLBACK: + switch (value->var.subs[sub - 1]) { + + case LEAF_begemotHrStorageUpdate: + storage_tbl_refresh = ctx->scratch->int1; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotHrFSUpdate: + fs_tbl_refresh = ctx->scratch->int1; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotHrDiskStorageUpdate: + disk_storage_tbl_refresh = ctx->scratch->int1; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotHrNetworkUpdate: + network_tbl_refresh = ctx->scratch->int1; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotHrSWInstalledUpdate: + swins_tbl_refresh = ctx->scratch->int1; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotHrSWRunUpdate: + swrun_tbl_refresh = ctx->scratch->int1; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotHrPkgDir: + string_rollback(ctx, &pkg_dir); + return (SNMP_ERR_NOERROR); + } + abort(); + } + + abort(); +} diff --git a/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_device_tbl.c b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_device_tbl.c new file mode 100644 index 0000000..1741502 --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_device_tbl.c @@ -0,0 +1,684 @@ + /*- + * Copyright (c) 2005-2006 The FreeBSD Project + * All rights reserved. + * + * Author: Victor Cruceru <soc-victor@freebsd.org> + * + * Redistribution of this software and documentation and use in source and + * binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code or documentation must retain the above + * copyright notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * Host Resources MIB: hrDeviceTable implementation for SNMPd. + */ + +#include <sys/un.h> +#include <sys/limits.h> + +#include <assert.h> +#include <err.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <unistd.h> +#include <sysexits.h> + +#include "hostres_snmp.h" +#include "hostres_oid.h" +#include "hostres_tree.h" + +#define FREE_DEV_STRUCT(entry_p) do { \ + free(entry_p->name); \ + free(entry_p->location); \ + free(entry_p->descr); \ + free(entry_p); \ +} while (0) + +/* + * Status of a device + */ +enum DeviceStatus { + DS_UNKNOWN = 1, + DS_RUNNING = 2, + DS_WARNING = 3, + DS_TESTING = 4, + DS_DOWN = 5 +}; + +TAILQ_HEAD(device_tbl, device_entry); + +/* the head of the list with hrDeviceTable's entries */ +static struct device_tbl device_tbl = TAILQ_HEAD_INITIALIZER(device_tbl); + +/* Table used for consistent device table indexing. */ +struct device_map device_map = STAILQ_HEAD_INITIALIZER(device_map); + +/* next int available for indexing the hrDeviceTable */ +static uint32_t next_device_index = 1; + +/* last (agent) tick when hrDeviceTable was updated */ +static uint64_t device_tick = 0; + +/* maximum number of ticks between updates of device table */ +uint32_t device_tbl_refresh = 10 * 100; + +/* socket for /var/run/devd.pipe */ +static int devd_sock = -1; + +/* used to wait notifications from /var/run/devd.pipe */ +static void *devd_fd; + +/* some constants */ +static const struct asn_oid OIDX_hrDeviceProcessor_c = OIDX_hrDeviceProcessor; +static const struct asn_oid OIDX_hrDeviceOther_c = OIDX_hrDeviceOther; + +/** + * Create a new entry out of thin air. + */ +struct device_entry * +device_entry_create(const char *name, const char *location, const char *descr) +{ + struct device_entry *entry = NULL; + struct device_map_entry *map = NULL; + size_t name_len; + size_t location_len; + + assert((name[0] != 0) || (location[0] != 0)); + + if (name[0] == 0 && location[0] == 0) + return (NULL); + + STAILQ_FOREACH(map, &device_map, link) { + assert(map->name_key != NULL); + assert(map->location_key != NULL); + + if (strcmp(map->name_key, name) == 0 && + strcmp(map->location_key, location) == 0) { + break; + } + } + + if (map == NULL) { + /* new object - get a new index */ + if (next_device_index > INT_MAX) { + syslog(LOG_ERR, + "%s: hrDeviceTable index wrap", __func__); + /* There isn't much we can do here. + * If the next_swins_index is consumed + * then we can't add entries to this table + * So it is better to exit - if the table is sparsed + * at the next agent run we can fill it fully. + */ + errx(EX_SOFTWARE, "hrDeviceTable index wrap"); + /* not reachable */ + } + + if ((map = malloc(sizeof(*map))) == NULL) { + syslog(LOG_ERR, "hrDeviceTable: %s: %m", __func__ ); + return (NULL); + } + + map->entry_p = NULL; + + name_len = strlen(name) + 1; + if (name_len > DEV_NAME_MLEN) + name_len = DEV_NAME_MLEN; + + if ((map->name_key = malloc(name_len)) == NULL) { + syslog(LOG_ERR, "hrDeviceTable: %s: %m", __func__ ); + free(map); + return (NULL); + } + + location_len = strlen(location) + 1; + if (location_len > DEV_LOC_MLEN) + location_len = DEV_LOC_MLEN; + + if ((map->location_key = malloc(location_len )) == NULL) { + syslog(LOG_ERR, "hrDeviceTable: %s: %m", __func__ ); + free(map->name_key); + free(map); + return (NULL); + } + + map->hrIndex = next_device_index++; + + strlcpy(map->name_key, name, name_len); + strlcpy(map->location_key, location, location_len); + + STAILQ_INSERT_TAIL(&device_map, map, link); + HRDBG("%s at %s added into hrDeviceMap at index=%d", + name, location, map->hrIndex); + } else { + HRDBG("%s at %s exists in hrDeviceMap index=%d", + name, location, map->hrIndex); + } + + if ((entry = malloc(sizeof(*entry))) == NULL) { + syslog(LOG_WARNING, "hrDeviceTable: %s: %m", __func__); + return (NULL); + } + memset(entry, 0, sizeof(*entry)); + + entry->index = map->hrIndex; + map->entry_p = entry; + + if ((entry->name = strdup(map->name_key)) == NULL) { + syslog(LOG_ERR, "hrDeviceTable: %s: %m", __func__ ); + free(entry); + return (NULL); + } + + if ((entry->location = strdup(map->location_key)) == NULL) { + syslog(LOG_ERR, "hrDeviceTable: %s: %m", __func__ ); + free(entry->name); + free(entry); + return (NULL); + } + + /* + * From here till the end of this function we reuse name_len + * for a different purpose - for device_entry::descr + */ + if (name[0] != '\0') + name_len = strlen(name) + strlen(descr) + + strlen(": ") + 1; + else + name_len = strlen(location) + strlen(descr) + + strlen("unknown at : ") + 1; + + if (name_len > DEV_DESCR_MLEN) + name_len = DEV_DESCR_MLEN; + + if ((entry->descr = malloc(name_len )) == NULL) { + syslog(LOG_ERR, "hrDeviceTable: %s: %m", __func__ ); + free(entry->name); + free(entry->location); + free(entry); + return (NULL); + } + + memset(&entry->descr[0], '\0', name_len); + + if (name[0] != '\0') + snprintf(entry->descr, name_len, + "%s: %s", name, descr); + else + snprintf(entry->descr, name_len, + "unknown at %s: %s", location, descr); + + entry->id = &oid_zeroDotZero; /* unknown id - FIXME */ + entry->status = (u_int)DS_UNKNOWN; + entry->errors = 0; + entry->type = &OIDX_hrDeviceOther_c; + + INSERT_OBJECT_INT(entry, &device_tbl); + + return (entry); +} + +/** + * Create a new entry into the device table. + */ +static struct device_entry * +device_entry_create_devinfo(const struct devinfo_dev *dev_p) +{ + + assert(dev_p->dd_name != NULL); + assert(dev_p->dd_location != NULL); + + return (device_entry_create(dev_p->dd_name, dev_p->dd_location, + dev_p->dd_desc)); +} + +/** + * Delete an entry from the device table. + */ +void +device_entry_delete(struct device_entry *entry) +{ + struct device_map_entry *map; + + assert(entry != NULL); + + TAILQ_REMOVE(&device_tbl, entry, link); + + STAILQ_FOREACH(map, &device_map, link) + if (map->entry_p == entry) { + map->entry_p = NULL; + break; + } + + FREE_DEV_STRUCT(entry); +} + +/** + * Find an entry given its name and location + */ +static struct device_entry * +device_find_by_dev(const struct devinfo_dev *dev_p) +{ + struct device_map_entry *map; + + assert(dev_p != NULL); + + STAILQ_FOREACH(map, &device_map, link) + if (strcmp(map->name_key, dev_p->dd_name) == 0 && + strcmp(map->location_key, dev_p->dd_location) == 0) + return (map->entry_p); + return (NULL); +} + +/** + * Find an entry given its index. + */ +struct device_entry * +device_find_by_index(int32_t idx) +{ + struct device_entry *entry; + + TAILQ_FOREACH(entry, &device_tbl, link) + if (entry->index == idx) + return (entry); + return (NULL); +} + +/** + * Find an device entry given its name. + */ +struct device_entry * +device_find_by_name(const char *dev_name) +{ + struct device_map_entry *map; + + assert(dev_name != NULL); + + STAILQ_FOREACH(map, &device_map, link) + if (strcmp(map->name_key, dev_name) == 0) + return (map->entry_p); + + return (NULL); +} + +/** + * Find out the type of device. CPU only currently. + */ +static void +device_get_type(struct devinfo_dev *dev_p, const struct asn_oid **out_type_p) +{ + + assert(dev_p != NULL); + assert(out_type_p != NULL); + + if (dev_p == NULL) + return; + + if (strncmp(dev_p->dd_name, "cpu", strlen("cpu")) == 0 && + strstr(dev_p->dd_location, ".CPU") != NULL) { + *out_type_p = &OIDX_hrDeviceProcessor_c; + return; + } +} + +/** + * Get the status of a device + */ +static enum DeviceStatus +device_get_status(struct devinfo_dev *dev) +{ + + assert(dev != NULL); + + switch (dev->dd_state) { + case DS_ALIVE: /* probe succeeded */ + case DS_NOTPRESENT: /* not probed or probe failed */ + return (DS_DOWN); + case DS_ATTACHED: /* attach method called */ + case DS_BUSY: /* device is open */ + return (DS_RUNNING); + default: + return (DS_UNKNOWN); + } +} + +/** + * Get the info for the given device and then recursively process all + * child devices. + */ +static int +device_collector(struct devinfo_dev *dev, void *arg) +{ + struct device_entry *entry; + + HRDBG("%llu/%llu name='%s' desc='%s' drivername='%s' location='%s'", + (unsigned long long)dev->dd_handle, + (unsigned long long)dev->dd_parent, dev->dd_name, dev->dd_desc, + dev->dd_drivername, dev->dd_location); + + if (dev->dd_name[0] != '\0' || dev->dd_location[0] != '\0') { + HRDBG("ANALYZING dev %s at %s", + dev->dd_name, dev->dd_location); + + if ((entry = device_find_by_dev(dev)) != NULL) { + entry->flags |= HR_DEVICE_FOUND; + entry->status = (u_int)device_get_status(dev); + } else if ((entry = device_entry_create_devinfo(dev)) != NULL) { + device_get_type(dev, &entry->type); + + entry->flags |= HR_DEVICE_FOUND; + entry->status = (u_int)device_get_status(dev); + } + } else { + HRDBG("SKIPPED unknown device at location '%s'", + dev->dd_location ); + } + + return (devinfo_foreach_device_child(dev, device_collector, arg)); +} + +/** + * Create the socket to the device daemon. + */ +static int +create_devd_socket(void) +{ + int d_sock; + struct sockaddr_un devd_addr; + + bzero(&devd_addr, sizeof(struct sockaddr_un)); + + if ((d_sock = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) { + syslog(LOG_ERR, "Failed to create the socket for %s: %m", + PATH_DEVD_PIPE); + return (-1); + } + + devd_addr.sun_family = PF_LOCAL; + devd_addr.sun_len = sizeof(devd_addr); + strlcpy(devd_addr.sun_path, PATH_DEVD_PIPE, + sizeof(devd_addr.sun_path) - 1); + + if (connect(d_sock, (struct sockaddr *)&devd_addr, + sizeof(devd_addr)) == -1) { + syslog(LOG_ERR,"Failed to connect socket for %s: %m", + PATH_DEVD_PIPE); + if (close(d_sock) < 0 ) + syslog(LOG_ERR,"Failed to close socket for %s: %m", + PATH_DEVD_PIPE); + return (-1); + } + + return (d_sock); +} + +/* + * Event on the devd socket. + * + * We should probably directly process entries here. For simplicity just + * call the refresh routine with the force flag for now. + */ +static void +devd_socket_callback(int fd, void *arg __unused) +{ + char buf[512]; + int read_len = -1; + + assert(fd == devd_sock); + + HRDBG("called"); + +again: + read_len = read(fd, buf, sizeof(buf)); + if (read_len < 0) { + if (errno == EBADF) { + devd_sock = -1; + if (devd_fd != NULL) { + fd_deselect(devd_fd); + devd_fd = NULL; + } + syslog(LOG_ERR, "Closing devd_fd, revert to " + "devinfo polling"); + } + + } else if (read_len == 0) { + syslog(LOG_ERR, "zero bytes read from devd pipe... " + "closing socket!"); + + if (close(devd_sock) < 0 ) + syslog(LOG_ERR, "Failed to close devd socket: %m"); + + devd_sock = -1; + if (devd_fd != NULL) { + fd_deselect(devd_fd); + devd_fd = NULL; + } + syslog(LOG_ERR, "Closing devd_fd, revert to devinfo polling"); + + } else { + if (read_len == sizeof(buf)) + goto again; + refresh_device_tbl(1); + } +} + +/** + * Initialize and populate the device table. + */ +void +init_device_tbl(void) +{ + + /* initially populate table for the other tables */ + refresh_device_tbl(1); + + /* no problem if that fails - just use polling mode */ + devd_sock = create_devd_socket(); +} + +/** + * Start devd(8) monitoring. + */ +void +start_device_tbl(struct lmodule *mod) +{ + + if (devd_sock > 0) { + devd_fd = fd_select(devd_sock, devd_socket_callback, NULL, mod); + if (devd_fd == NULL) + syslog(LOG_ERR, "fd_select failed on devd socket: %m"); + } +} + +/** + * Finalization routine for hrDeviceTable + * It destroys the lists and frees any allocated heap memory + */ +void +fini_device_tbl(void) +{ + struct device_map_entry *n1; + + if (devd_fd != NULL) + fd_deselect(devd_fd); + + if (devd_sock != -1) + (void)close(devd_sock); + + devinfo_free(); + + while ((n1 = STAILQ_FIRST(&device_map)) != NULL) { + STAILQ_REMOVE_HEAD(&device_map, link); + if (n1->entry_p != NULL) { + TAILQ_REMOVE(&device_tbl, n1->entry_p, link); + FREE_DEV_STRUCT(n1->entry_p); + } + free(n1->name_key); + free(n1->location_key); + free(n1); + } + assert(TAILQ_EMPTY(&device_tbl)); +} + +/** + * Refresh routine for hrDeviceTable. We don't refresh here if the devd socket + * is open, because in this case we have the actual information always. We + * also don't refresh when the table is new enough (if we don't have a devd + * socket). In either case a refresh can be forced by passing a non-zero value. + */ +void +refresh_device_tbl(int force) +{ + struct device_entry *entry, *entry_tmp; + struct devinfo_dev *dev_root; + static int act = 0; + + if (!force && (devd_sock >= 0 || + (device_tick != 0 && this_tick - device_tick < device_tbl_refresh))){ + HRDBG("no refresh needed"); + return; + } + + if (act) { + syslog(LOG_ERR, "%s: recursive call", __func__); + return; + } + + if (devinfo_init() != 0) { + syslog(LOG_ERR,"%s: devinfo_init failed: %m", __func__); + return; + } + + act = 1; + if ((dev_root = devinfo_handle_to_device(DEVINFO_ROOT_DEVICE)) == NULL){ + syslog(LOG_ERR, "%s: can't get the root device: %m", __func__); + goto out; + } + + /* mark each entry as missing */ + TAILQ_FOREACH(entry, &device_tbl, link) + entry->flags &= ~HR_DEVICE_FOUND; + + if (devinfo_foreach_device_child(dev_root, device_collector, NULL)) + syslog(LOG_ERR, "%s: devinfo_foreach_device_child failed", + __func__); + + /* + * Purge items that disappeared + */ + TAILQ_FOREACH_SAFE(entry, &device_tbl, link, entry_tmp) { + /* + * If HR_DEVICE_IMMUTABLE bit is set then this means that + * this entry was not detected by the above + * devinfo_foreach_device() call. So we are not deleting + * it there. + */ + if (!(entry->flags & HR_DEVICE_FOUND) && + !(entry->flags & HR_DEVICE_IMMUTABLE)) + device_entry_delete(entry); + } + + device_tick = this_tick; + + /* + * Force a refresh for the hrDiskStorageTable + * XXX Why not the other dependen tables? + */ + refresh_disk_storage_tbl(1); + + out: + devinfo_free(); + act = 0; +} + +/** + * This is the implementation for a generated (by a SNMP tool) + * function prototype, see hostres_tree.h + * It handles the SNMP operations for hrDeviceTable + */ +int +op_hrDeviceTable(struct snmp_context *ctx __unused, struct snmp_value *value, + u_int sub, u_int iidx __unused, enum snmp_op curr_op) +{ + struct device_entry *entry; + + refresh_device_tbl(0); + + switch (curr_op) { + + case SNMP_OP_GETNEXT: + if ((entry = NEXT_OBJECT_INT(&device_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(&device_tbl, + &value->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + goto get; + + case SNMP_OP_SET: + if ((entry = FIND_OBJECT_INT(&device_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_hrDeviceIndex: + value->v.integer = entry->index; + return (SNMP_ERR_NOERROR); + + case LEAF_hrDeviceType: + assert(entry->type != NULL); + value->v.oid = *(entry->type); + return (SNMP_ERR_NOERROR); + + case LEAF_hrDeviceDescr: + return (string_get(value, entry->descr, -1)); + + case LEAF_hrDeviceID: + value->v.oid = *(entry->id); + return (SNMP_ERR_NOERROR); + + case LEAF_hrDeviceStatus: + value->v.integer = entry->status; + return (SNMP_ERR_NOERROR); + + case LEAF_hrDeviceErrors: + value->v.uint32 = entry->errors; + return (SNMP_ERR_NOERROR); + } + abort(); +} diff --git a/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_diskstorage_tbl.c b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_diskstorage_tbl.c new file mode 100644 index 0000000..25ba56a --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_diskstorage_tbl.c @@ -0,0 +1,647 @@ +/*- + * Copyright (c) 2005-2006 The FreeBSD Project + * All rights reserved. + * + * Author: Victor Cruceru <soc-victor@freebsd.org> + * + * Redistribution of this software and documentation and use in source and + * binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code or documentation must retain the above + * copyright notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * Host Resources MIB for SNMPd. Implementation for the hrDiskStorageTable + */ + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/ata.h> +#include <sys/disk.h> +#include <sys/linker.h> +#include <sys/mdioctl.h> +#include <sys/module.h> +#include <sys/sysctl.h> + +#include <assert.h> +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <paths.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" + +enum hrDiskStrorageAccess { + DS_READ_WRITE = 1, + DS_READ_ONLY = 2 +}; + +enum hrDiskStrorageMedia { + DSM_OTHER = 1, + DSM_UNKNOWN = 2, + DSM_HARDDISK = 3, + DSM_FLOPPYDISK = 4, + DSM_OPTICALDISKROM= 5, + DSM_OPTICALDISKWORM= 6, + DSM_OPTICALDISKRW= 7, + DSM_RAMDISK = 8 +}; + +/* + * This structure is used to hold a SNMP table entry for HOST-RESOURCES-MIB's + * hrDiskStorageTable. Note that index is external being allocated and + * maintained by the hrDeviceTable code. + * + * NOTE: according to MIB removable means removable media, not the + * device itself (like a USB card reader) + */ +struct disk_entry { + int32_t index; + int32_t access; /* enum hrDiskStrorageAccess */ + int32_t media; /* enum hrDiskStrorageMedia*/ + int32_t removable; /* enum snmpTCTruthValue*/ + int32_t capacity; + TAILQ_ENTRY(disk_entry) link; + /* + * next items are not from the SNMP mib table, only to be used + * internally + */ +#define HR_DISKSTORAGE_FOUND 0x001 +#define HR_DISKSTORAGE_ATA 0x002 /* belongs to the ATA subsystem */ +#define HR_DISKSTORAGE_MD 0x004 /* it is a MD (memory disk) */ + uint32_t flags; + uint64_t r_tick; + u_char dev_name[32]; /* device name, i.e. "ad4" or "acd0" */ +}; +TAILQ_HEAD(disk_tbl, disk_entry); + +/* the head of the list with hrDiskStorageTable's entries */ +static struct disk_tbl disk_tbl = + TAILQ_HEAD_INITIALIZER(disk_tbl); + +/* last tick when hrFSTable was updated */ +static uint64_t disk_storage_tick; + +/* minimum number of ticks between refreshs */ +uint32_t disk_storage_tbl_refresh = HR_DISK_TBL_REFRESH * 100; + +/* fd for "/dev/mdctl"*/ +static int md_fd = -1; + +/* buffer for sysctl("kern.disks") */ +static char *disk_list; +static size_t disk_list_len; + +/* some constants */ +static const struct asn_oid OIDX_hrDeviceDiskStorage_c = + OIDX_hrDeviceDiskStorage; + +/** + * Load the MD driver if it isn't loaded already. + */ +static void +mdmaybeload(void) +{ + char name1[64], name2[64]; + + snprintf(name1, sizeof(name1), "g_%s", MD_NAME); + snprintf(name2, sizeof(name2), "geom_%s", MD_NAME); + if (modfind(name1) == -1) { + /* Not present in kernel, try loading it. */ + if (kldload(name2) == -1 || modfind(name1) == -1) { + if (errno != EEXIST) { + errx(EXIT_FAILURE, + "%s module not available!", name2); + } + } + } +} + +/** + * Create a new entry into the DiskStorageTable. + */ +static struct disk_entry * +disk_entry_create(const struct device_entry *devEntry) +{ + struct disk_entry *entry; + + assert(devEntry != NULL); + if (devEntry == NULL) + return NULL; + + if ((entry = malloc(sizeof(*entry))) == NULL) { + syslog(LOG_WARNING, "hrDiskStorageTable: %s: %m", __func__); + return (NULL); + } + + memset(entry, 0, sizeof(*entry)); + entry->index = devEntry->index; + INSERT_OBJECT_INT(entry, &disk_tbl); + + return (entry); +} + +/** + * Delete a disk table entry. + */ +static void +disk_entry_delete(struct disk_entry *entry) +{ + struct device_entry *devEntry; + + assert(entry != NULL); + TAILQ_REMOVE(&disk_tbl, entry, link); + + devEntry = device_find_by_index(entry->index); + + free(entry); + + /* + * Also delete the respective device entry - + * this is needed for disk devices that are not + * detected by libdevinfo + */ + if (devEntry != NULL && + (devEntry->flags & HR_DEVICE_IMMUTABLE) == HR_DEVICE_IMMUTABLE) + device_entry_delete(devEntry); +} + +/** + * Find a disk storage entry given its index. + */ +static struct disk_entry * +disk_find_by_index(int32_t idx) +{ + struct disk_entry *entry; + + TAILQ_FOREACH(entry, &disk_tbl, link) + if (entry->index == idx) + return (entry); + + return (NULL); +} + +/** + * Get the disk parameters + */ +static void +disk_query_disk(struct disk_entry *entry) +{ + char dev_path[128]; + int fd; + off_t mediasize; + + if (entry == NULL || entry->dev_name[0] == '\0') + return; + + snprintf(dev_path, sizeof(dev_path), + "%s%s", _PATH_DEV, entry->dev_name); + entry->capacity = 0; + + HRDBG("OPENING device %s", dev_path); + if ((fd = open(dev_path, O_RDONLY|O_NONBLOCK)) == -1) { + HRDBG("OPEN device %s failed: %s", dev_path, strerror(errno)); + return; + } + + if (ioctl(fd, DIOCGMEDIASIZE, &mediasize) < 0) { + HRDBG("DIOCGMEDIASIZE for device %s failed: %s", + dev_path, strerror(errno)); + (void)close(fd); + return; + } + + mediasize = mediasize / 1024; + entry->capacity = (mediasize > INT_MAX ? INT_MAX : mediasize); + partition_tbl_handle_disk(entry->index, entry->dev_name); + + (void)close(fd); +} + +/** + * Find all ATA disks in the device table. + */ +static void +disk_OS_get_ATA_disks(void) +{ + struct device_map_entry *map; + struct device_entry *entry; + struct disk_entry *disk_entry; + const struct disk_entry *found; + + /* Things we know are ata disks */ + static const struct disk_entry lookup[] = { + { + .dev_name = "ad", + .media = DSM_HARDDISK, + .removable = SNMP_FALSE + }, + { + .dev_name = "ar", + .media = DSM_OTHER, + .removable = SNMP_FALSE + }, + { + .dev_name = "acd", + .media = DSM_OPTICALDISKROM, + .removable = SNMP_TRUE + }, + { + .dev_name = "afd", + .media = DSM_FLOPPYDISK, + .removable = SNMP_TRUE + }, + { + .dev_name = "ast", + .media = DSM_OTHER, + .removable = SNMP_TRUE + }, + + { .media = DSM_UNKNOWN } + }; + + /* Walk over the device table looking for ata disks */ + STAILQ_FOREACH(map, &device_map, link) { + for (found = lookup; found->media != DSM_UNKNOWN; found++) { + if (strncmp(map->name_key, found->dev_name, + strlen(found->dev_name)) != 0) + continue; + + /* + * Avoid false disk devices. For example adw(4) and + * adv(4) - they are not disks! + */ + if (strlen(map->name_key) > strlen(found->dev_name) && + !isdigit(map->name_key[strlen(found->dev_name)])) + continue; + + /* First get the entry from the hrDeviceTbl */ + entry = map->entry_p; + entry->type = &OIDX_hrDeviceDiskStorage_c; + + /* Then check hrDiskStorage table for this device */ + disk_entry = disk_find_by_index(entry->index); + if (disk_entry == NULL) { + disk_entry = disk_entry_create(entry); + if (disk_entry == NULL) + continue; + + disk_entry->access = DS_READ_WRITE; + strlcpy(disk_entry->dev_name, entry->name, + sizeof(disk_entry->dev_name)); + + disk_entry->media = found->media; + disk_entry->removable = found->removable; + } + + disk_entry->flags |= HR_DISKSTORAGE_FOUND; + disk_entry->flags |= HR_DISKSTORAGE_ATA; + + disk_query_disk(disk_entry); + disk_entry->r_tick = this_tick; + } + } +} + +/** + * Find MD disks in the device table. + */ +static void +disk_OS_get_MD_disks(void) +{ + struct device_map_entry *map; + struct device_entry *entry; + struct disk_entry *disk_entry; + struct md_ioctl mdio; + int unit; + + if (md_fd <= 0) + return; + + /* Look for md devices */ + STAILQ_FOREACH(map, &device_map, link) { + if (sscanf(map->name_key, "md%d", &unit) != 1) + continue; + + /* First get the entry from the hrDeviceTbl */ + entry = device_find_by_index(map->hrIndex); + entry->type = &OIDX_hrDeviceDiskStorage_c; + + /* Then check hrDiskStorage table for this device */ + disk_entry = disk_find_by_index(entry->index); + if (disk_entry == NULL) { + disk_entry = disk_entry_create(entry); + if (disk_entry == NULL) + continue; + + memset(&mdio, 0, sizeof(mdio)); + mdio.md_version = MDIOVERSION; + mdio.md_unit = unit; + + if (ioctl(md_fd, MDIOCQUERY, &mdio) < 0) { + syslog(LOG_ERR, + "hrDiskStorageTable: Couldnt ioctl"); + continue; + } + + if ((mdio.md_options & MD_READONLY) == MD_READONLY) + disk_entry->access = DS_READ_ONLY; + else + disk_entry->access = DS_READ_WRITE; + + strlcpy(disk_entry->dev_name, entry->name, + sizeof(disk_entry->dev_name)); + + disk_entry->media = DSM_RAMDISK; + disk_entry->removable = SNMP_FALSE; + } + + disk_entry->flags |= HR_DISKSTORAGE_FOUND; + disk_entry->flags |= HR_DISKSTORAGE_MD; + disk_entry->r_tick = this_tick; + } +} + +/** + * Find rest of disks + */ +static void +disk_OS_get_disks(void) +{ + size_t disk_cnt = 0; + struct device_entry *entry; + struct disk_entry *disk_entry; + + size_t need = 0; + + if (sysctlbyname("kern.disks", NULL, &need, NULL, 0) == -1) { + syslog(LOG_ERR, "%s: sysctl_1 kern.disks failed: %m", __func__); + return; + } + + if (need == 0) + return; + + if (disk_list_len != need + 1 || disk_list == NULL) { + disk_list_len = need + 1; + disk_list = reallocf(disk_list, disk_list_len); + } + + if (disk_list == NULL) { + syslog(LOG_ERR, "%s: reallocf failed", __func__); + disk_list_len = 0; + return; + } + + memset(disk_list, 0, disk_list_len); + + if (sysctlbyname("kern.disks", disk_list, &need, NULL, 0) == -1 || + disk_list[0] == 0) { + syslog(LOG_ERR, "%s: sysctl_2 kern.disks failed: %m", __func__); + return; + } + + for (disk_cnt = 0; disk_cnt < need; disk_cnt++) { + char *disk = NULL; + char disk_device[128] = ""; + + disk = strsep(&disk_list, " "); + if (disk == NULL) + break; + + snprintf(disk_device, sizeof(disk_device), + "%s%s", _PATH_DEV, disk); + + /* First check if the disk is in the hrDeviceTable. */ + if ((entry = device_find_by_name(disk)) == NULL) { + /* + * not found there - insert it as immutable + */ + syslog(LOG_WARNING, "%s: adding device '%s' to " + "device list", __func__, disk); + + if ((entry = device_entry_create(disk, "", "")) == NULL) + continue; + + entry->flags |= HR_DEVICE_IMMUTABLE; + } + + entry->type = &OIDX_hrDeviceDiskStorage_c; + + /* Then check hrDiskStorage table for this device */ + disk_entry = disk_find_by_index(entry->index); + if (disk_entry == NULL) { + disk_entry = disk_entry_create(entry); + if (disk_entry == NULL) + continue; + } + + disk_entry->flags |= HR_DISKSTORAGE_FOUND; + + if ((disk_entry->flags & HR_DISKSTORAGE_ATA) || + (disk_entry->flags & HR_DISKSTORAGE_MD)) { + /* + * ATA/MD detection is running before this one, + * so don't waste the time here + */ + continue; + } + + disk_entry->access = DS_READ_WRITE; + disk_entry->media = DSM_UNKNOWN; + disk_entry->removable = SNMP_FALSE; + + if (strncmp(disk_entry->dev_name, "da", 2) == 0 || + strncmp(disk_entry->dev_name, "ada", 3) == 0) { + disk_entry->media = DSM_HARDDISK; + disk_entry->removable = SNMP_FALSE; + } else if (strncmp(disk_entry->dev_name, "cd", 2) == 0) { + disk_entry->media = DSM_OPTICALDISKROM; + disk_entry->removable = SNMP_TRUE; + } else { + disk_entry->media = DSM_UNKNOWN; + disk_entry->removable = SNMP_FALSE; + } + + strlcpy((char *)disk_entry->dev_name, disk, + sizeof(disk_entry->dev_name)); + + disk_query_disk(disk_entry); + disk_entry->r_tick = this_tick; + } +} + +/** + * Refresh routine for hrDiskStorageTable + * Usable for polling the system for any changes. + */ +void +refresh_disk_storage_tbl(int force) +{ + struct disk_entry *entry, *entry_tmp; + + if (disk_storage_tick != 0 && !force && + this_tick - disk_storage_tick < disk_storage_tbl_refresh) { + HRDBG("no refresh needed"); + return; + } + + partition_tbl_pre_refresh(); + + /* mark each entry as missing */ + TAILQ_FOREACH(entry, &disk_tbl, link) + entry->flags &= ~HR_DISKSTORAGE_FOUND; + + disk_OS_get_ATA_disks(); /* this must be called first ! */ + disk_OS_get_MD_disks(); + disk_OS_get_disks(); + + /* + * Purge items that disappeared + */ + TAILQ_FOREACH_SAFE(entry, &disk_tbl, link, entry_tmp) + if (!(entry->flags & HR_DISKSTORAGE_FOUND)) + /* XXX remove IMMUTABLE entries that have disappeared */ + disk_entry_delete(entry); + + disk_storage_tick = this_tick; + + partition_tbl_post_refresh(); + + HRDBG("refresh DONE"); +} + +/* + * Init the things for both of hrDiskStorageTable + */ +int +init_disk_storage_tbl(void) +{ + char mddev[32] = ""; + + /* Try to load md.ko if not loaded already */ + mdmaybeload(); + + md_fd = -1; + snprintf(mddev, sizeof(mddev) - 1, "%s%s", _PATH_DEV, MDCTL_NAME); + if ((md_fd = open(mddev, O_RDWR)) == -1) { + syslog(LOG_ERR, "open %s failed - will not include md(4) " + "info: %m", mddev); + } + + refresh_disk_storage_tbl(1); + + return (0); +} + +/* + * Finalization routine for hrDiskStorageTable + * It destroys the lists and frees any allocated heap memory + */ +void +fini_disk_storage_tbl(void) +{ + struct disk_entry *n1; + + while ((n1 = TAILQ_FIRST(&disk_tbl)) != NULL) { + TAILQ_REMOVE(&disk_tbl, n1, link); + free(n1); + } + + free(disk_list); + + if (md_fd > 0) { + if (close(md_fd) == -1) + syslog(LOG_ERR,"close (/dev/mdctl) failed: %m"); + md_fd = -1; + } +} + +/* + * This is the implementation for a generated (by our SNMP "compiler" tool) + * function prototype, see hostres_tree.h + * It handles the SNMP operations for hrDiskStorageTable + */ +int +op_hrDiskStorageTable(struct snmp_context *ctx __unused, + struct snmp_value *value, u_int sub, u_int iidx __unused, + enum snmp_op curr_op) +{ + struct disk_entry *entry; + + refresh_disk_storage_tbl(0); + + switch (curr_op) { + + case SNMP_OP_GETNEXT: + if ((entry = NEXT_OBJECT_INT(&disk_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(&disk_tbl, + &value->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + goto get; + + case SNMP_OP_SET: + if ((entry = FIND_OBJECT_INT(&disk_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_hrDiskStorageAccess: + value->v.integer = entry->access; + return (SNMP_ERR_NOERROR); + + case LEAF_hrDiskStorageMedia: + value->v.integer = entry->media; + return (SNMP_ERR_NOERROR); + + case LEAF_hrDiskStorageRemovable: + value->v.integer = entry->removable; + return (SNMP_ERR_NOERROR); + + case LEAF_hrDiskStorageCapacity: + value->v.integer = entry->capacity; + return (SNMP_ERR_NOERROR); + } + abort(); +} diff --git a/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_fs_tbl.c b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_fs_tbl.c new file mode 100644 index 0000000..91505d5 --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_fs_tbl.c @@ -0,0 +1,473 @@ +/*- + * Copyright (c) 2005-2006 The FreeBSD Project + * All rights reserved. + * + * Author: Victor Cruceru <soc-victor@freebsd.org> + * + * Redistribution of this software and documentation and use in source and + * binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code or documentation must retain the above + * copyright notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * Host Resources MIB for SNMPd. Implementation for hrFSTable + */ + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/sysctl.h> +#include <sys/mount.h> + +#include <assert.h> +#include <err.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <sysexits.h> + +#include "hostres_snmp.h" +#include "hostres_oid.h" +#include "hostres_tree.h" + +/* + * File system access enum + */ +enum hrFSAccess { + FS_READ_WRITE = 1, + FS_READ_ONLY = 2 +}; + +/* maximum length (according to MIB) for fs_entry::mountPoint */ +#define FS_MP_MLEN (128 + 1) + +/* maximum length (according to MIB) for fs_entry::remoteMountPoint */ +#define FS_RMP_MLEN (128 + 1) + +/* + * This structure is used to hold a SNMP table entry + * for HOST-RESOURCES-MIB's hrFSTable + */ +struct fs_entry { + int32_t index; + u_char *mountPoint; + u_char *remoteMountPoint; + const struct asn_oid *type; + int32_t access; /* enum hrFSAccess, see above */ + int32_t bootable; /* TruthValue */ + int32_t storageIndex; /* hrStorageTblEntry::index */ + u_char lastFullBackupDate[11]; + u_char lastPartialBackupDate[11]; +#define HR_FS_FOUND 0x001 + uint32_t flags; /* not in mib table, for internal use */ + TAILQ_ENTRY(fs_entry) link; +}; +TAILQ_HEAD(fs_tbl, fs_entry); + +/* + * Next structure is used to keep o list of mappings from a specific name + * (a_name) to an entry in the hrFSTblEntry. We are trying to keep the same + * index for a specific name at least for the duration of one SNMP agent run. + */ +struct fs_map_entry { + int32_t hrIndex; /* used for fs_entry::index */ + u_char *a_name; /* map key same as fs_entry::mountPoint */ + + /* may be NULL if the respective hrFSTblEntry is (temporally) gone */ + struct fs_entry *entry; + STAILQ_ENTRY(fs_map_entry) link; +}; +STAILQ_HEAD(fs_map, fs_map_entry); + +/* head of the list with hrFSTable's entries */ +static struct fs_tbl fs_tbl = TAILQ_HEAD_INITIALIZER(fs_tbl); + +/* for consistent table indexing */ +static struct fs_map fs_map = STAILQ_HEAD_INITIALIZER(fs_map); + +/* next index available for hrFSTable */ +static uint32_t next_fs_index = 1; + +/* last tick when hrFSTable was updated */ +static uint64_t fs_tick; + +/* maximum number of ticks between refreshs */ +uint32_t fs_tbl_refresh = HR_FS_TBL_REFRESH * 100; + +/* some constants */ +static const struct asn_oid OIDX_hrFSBerkeleyFFS_c = OIDX_hrFSBerkeleyFFS; +static const struct asn_oid OIDX_hrFSiso9660_c = OIDX_hrFSiso9660; +static const struct asn_oid OIDX_hrFSNFS_c = OIDX_hrFSNFS; +static const struct asn_oid OIDX_hrFSLinuxExt2_c = OIDX_hrFSLinuxExt2; +static const struct asn_oid OIDX_hrFSOther_c = OIDX_hrFSOther; +static const struct asn_oid OIDX_hrFSFAT32_c = OIDX_hrFSFAT32; +static const struct asn_oid OIDX_hrFSNTFS_c = OIDX_hrFSNTFS; +static const struct asn_oid OIDX_hrFSNetware_c = OIDX_hrFSNetware; +static const struct asn_oid OIDX_hrFSHPFS_c = OIDX_hrFSHPFS; +static const struct asn_oid OIDX_hrFSUnknown_c = OIDX_hrFSUnknown; + +/* file system type map */ +static const struct { + const char *str; /* the type string */ + const struct asn_oid *oid; /* the OID to return */ +} fs_type_map[] = { + { "ufs", &OIDX_hrFSBerkeleyFFS_c }, + { "zfs", &OIDX_hrFSOther_c }, + { "cd9660", &OIDX_hrFSiso9660_c }, + { "nfs", &OIDX_hrFSNFS_c }, + { "ext2fs", &OIDX_hrFSLinuxExt2_c }, + { "procfs", &OIDX_hrFSOther_c }, + { "devfs", &OIDX_hrFSOther_c }, + { "msdosfs", &OIDX_hrFSFAT32_c }, + { "ntfs", &OIDX_hrFSNTFS_c }, + { "nwfs", &OIDX_hrFSNetware_c }, + { "hpfs", &OIDX_hrFSHPFS_c }, + { "smbfs", &OIDX_hrFSOther_c }, +}; +#define N_FS_TYPE_MAP (sizeof(fs_type_map) / sizeof(fs_type_map[0])) + +/** + * Create an entry into the FS table and an entry in the map (if needed). + */ +static struct fs_entry * +fs_entry_create(const char *name) +{ + struct fs_entry *entry; + struct fs_map_entry *map; + + assert(name != NULL); + assert(strlen(name) > 0); + + STAILQ_FOREACH(map, &fs_map, link) + if (strcmp(map->a_name, name) == 0) + break; + + if (map == NULL) { + size_t mount_point_len; + + /* new object - get a new index */ + if (next_fs_index > INT_MAX) { + /* Unrecoverable error - die clean and quicly*/ + syslog(LOG_ERR, "%s: hrFSTable index wrap", __func__); + errx(EX_SOFTWARE, "hrFSTable index wrap"); + } + + if ((map = malloc(sizeof(*map))) == NULL) { + syslog(LOG_ERR, "%s: %m", __func__); + return (NULL); + } + + mount_point_len = strlen(name) + 1; + if (mount_point_len > FS_MP_MLEN) + mount_point_len = FS_MP_MLEN; + + if ((map->a_name = malloc(mount_point_len)) == NULL) { + syslog(LOG_ERR, "%s: %m", __func__); + free(map); + return (NULL); + } + + strlcpy(map->a_name, name, mount_point_len); + + map->hrIndex = next_fs_index++; + map->entry = NULL; + STAILQ_INSERT_TAIL(&fs_map, map, link); + + HRDBG("%s added into hrFSMap at index=%d", name, map->hrIndex); + } else { + HRDBG("%s exists in hrFSMap index=%d", name, map->hrIndex); + } + + if ((entry = malloc(sizeof(*entry))) == NULL) { + syslog(LOG_WARNING, "%s: %m", __func__); + return (NULL); + } + + if ((entry->mountPoint = strdup(name)) == NULL) { + syslog(LOG_ERR, "%s: %m", __func__); + free(entry); + return (NULL); + } + + entry->index = map->hrIndex; + map->entry = entry; + + INSERT_OBJECT_INT(entry, &fs_tbl); + return (entry); +} + +/** + * Delete an entry in the FS table. + */ +static void +fs_entry_delete(struct fs_entry* entry) +{ + struct fs_map_entry *map; + + assert(entry != NULL); + + TAILQ_REMOVE(&fs_tbl, entry, link); + STAILQ_FOREACH(map, &fs_map, link) + if (map->entry == entry) { + map->entry = NULL; + break; + } + free(entry->mountPoint); + free(entry->remoteMountPoint); + free(entry); +} + +/** + * Find a table entry by its name + */ +static struct fs_entry * +fs_find_by_name(const char *name) +{ + struct fs_entry *entry; + + TAILQ_FOREACH(entry, &fs_tbl, link) + if (strcmp(entry->mountPoint, name) == 0) + return (entry); + + return (NULL); +} + +/** + * Get rid of all data + */ +void +fini_fs_tbl(void) +{ + struct fs_map_entry *n1; + + while ((n1 = STAILQ_FIRST(&fs_map)) != NULL) { + STAILQ_REMOVE_HEAD(&fs_map, link); + if (n1->entry != NULL) { + TAILQ_REMOVE(&fs_tbl, n1->entry, link); + free(n1->entry->mountPoint); + free(n1->entry->remoteMountPoint); + free(n1->entry); + } + free(n1->a_name); + free(n1); + } + assert(TAILQ_EMPTY(&fs_tbl)); +} + +/** + * Called before the refreshing is started from the storage table. + */ +void +fs_tbl_pre_refresh(void) +{ + struct fs_entry *entry; + + /* mark each entry as missisng */ + TAILQ_FOREACH(entry, &fs_tbl, link) + entry->flags &= ~HR_FS_FOUND; +} + +/** + * Called after refreshing from the storage table. + */ +void +fs_tbl_post_refresh(void) +{ + struct fs_entry *entry, *entry_tmp; + + /* + * Purge items that disappeared + */ + TAILQ_FOREACH_SAFE(entry, &fs_tbl, link, entry_tmp) + if (!(entry->flags & HR_FS_FOUND)) + fs_entry_delete(entry); + + fs_tick = this_tick; +} + +/* + * Refresh the FS table. This is done by forcing a refresh of the storage table. + */ +void +refresh_fs_tbl(void) +{ + + if (fs_tick == 0 || this_tick - fs_tick >= fs_tbl_refresh) { + refresh_storage_tbl(1); + HRDBG("refresh DONE"); + } +} + +/** + * Get the type OID for a given file system + */ +const struct asn_oid * +fs_get_type(const struct statfs *fs_p) +{ + u_int t; + + assert(fs_p != NULL); + + for (t = 0; t < N_FS_TYPE_MAP; t++) + if (strcmp(fs_type_map[t].str, fs_p->f_fstypename) == 0) + return (fs_type_map[t].oid); + + return (&OIDX_hrFSUnknown_c); +} + +/* + * Given information returned from statfs(2) either create a new entry into + * the fs_tbl or refresh the entry if it is already there. + */ +void +fs_tbl_process_statfs_entry(const struct statfs *fs_p, int32_t storage_idx) +{ + struct fs_entry *entry; + + assert(fs_p != 0); + + HRDBG("for hrStorageEntry::index %d", storage_idx); + + if (fs_p == NULL) + return; + + if ((entry = fs_find_by_name(fs_p->f_mntonname)) != NULL || + (entry = fs_entry_create(fs_p->f_mntonname)) != NULL) { + entry->flags |= HR_FS_FOUND; + + if (!(fs_p->f_flags & MNT_LOCAL)) { + /* this is a remote mount */ + entry->remoteMountPoint = strdup(fs_p->f_mntfromname); + /* if strdup failed, let it be NULL */ + + } else { + entry->remoteMountPoint = strdup(""); + /* if strdup failed, let it be NULL */ + } + + entry->type = fs_get_type(fs_p); + + if ((fs_p->f_flags & MNT_RDONLY) == MNT_RDONLY) + entry->access = FS_READ_ONLY; + else + entry->access = FS_READ_WRITE; + + /* FIXME - bootable fs ?! */ + entry->bootable = TRUTH_MK((fs_p->f_flags & MNT_ROOTFS) + == MNT_ROOTFS); + + entry->storageIndex = storage_idx; + + /* Info not available */ + memset(entry->lastFullBackupDate, 0, + sizeof(entry->lastFullBackupDate)); + + /* Info not available */ + memset(entry->lastPartialBackupDate, 0, + sizeof(entry->lastPartialBackupDate)); + + handle_partition_fs_index(fs_p->f_mntfromname, entry->index); + } +} + +/* + * This is the implementation for a generated (by our SNMP "compiler" tool) + * function prototype, see hostres_tree.h + * It handles the SNMP operations for hrFSTable + */ +int +op_hrFSTable(struct snmp_context *ctx __unused, struct snmp_value *value, + u_int sub, u_int iidx __unused, enum snmp_op curr_op) +{ + struct fs_entry *entry; + + refresh_fs_tbl(); + + switch (curr_op) { + + case SNMP_OP_GETNEXT: + if ((entry = NEXT_OBJECT_INT(&fs_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(&fs_tbl, + &value->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + goto get; + + case SNMP_OP_SET: + if ((entry = FIND_OBJECT_INT(&fs_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_hrFSIndex: + value->v.integer = entry->index; + return (SNMP_ERR_NOERROR); + + case LEAF_hrFSMountPoint: + return (string_get(value, entry->mountPoint, -1)); + + case LEAF_hrFSRemoteMountPoint: + if (entry->remoteMountPoint == NULL) + return (string_get(value, "", -1)); + else + return (string_get(value, entry->remoteMountPoint, -1)); + break; + + case LEAF_hrFSType: + assert(entry->type != NULL); + value->v.oid = *(entry->type); + return (SNMP_ERR_NOERROR); + + case LEAF_hrFSAccess: + value->v.integer = entry->access; + return (SNMP_ERR_NOERROR); + + case LEAF_hrFSBootable: + value->v.integer = entry->bootable; + return (SNMP_ERR_NOERROR); + + case LEAF_hrFSStorageIndex: + value->v.integer = entry->storageIndex; + return (SNMP_ERR_NOERROR); + + case LEAF_hrFSLastFullBackupDate: + return (string_get(value, entry->lastFullBackupDate, 8)); + + case LEAF_hrFSLastPartialBackupDate: + return (string_get(value, entry->lastPartialBackupDate, 8)); + } + abort(); +} diff --git a/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_network_tbl.c b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_network_tbl.c new file mode 100644 index 0000000..4329a1c --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_network_tbl.c @@ -0,0 +1,302 @@ +/*- + * Copyright (c) 2005-2006 The FreeBSD Project + * All rights reserved. + * + * Author: Victor Cruceru <soc-victor@freebsd.org> + * + * Redistribution of this software and documentation and use in source and + * binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code or documentation must retain the above + * copyright notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * Host Resources MIB implementation for SNMPd: instrumentation for + * hrNetworkTable + */ + +#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> + +/* + * This structure is used to hold a SNMP table entry + * for HOST-RESOURCES-MIB's hrNetworkTable + */ +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); + +/* the head of the list with hrNetworkTable's entries */ +static struct network_tbl network_tbl = TAILQ_HEAD_INITIALIZER(network_tbl); + +/* last (agent) tick when hrNetworkTable was updated */ +static uint64_t network_tick; + +/* maximum number of ticks between updates of network table */ +uint32_t network_tbl_refresh = HR_NETWORK_TBL_REFRESH * 100; + +/* Constants */ +static const struct asn_oid OIDX_hrDeviceNetwork_c = OIDX_hrDeviceNetwork; + +/** + * Create a new entry into the network table + */ +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); +} + +/** + * Delete an entry in the network table + */ +static void +network_entry_delete(struct network_entry* entry) +{ + + TAILQ_REMOVE(&network_tbl, entry, link); + free(entry); +} + +/** + * Fetch the interfaces from the mibII module, get their real name from the + * kernel and try to find it in the device table. + */ +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; + + /* get the original name */ + 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); + + /* Then check hrNetworkTable for this device */ + 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; +} + +/** + * Finalization routine for hrNetworkTable. + * It destroys the lists and frees any allocated heap memory. + */ +void +fini_network_tbl(void) +{ + struct network_entry *n1; + + while ((n1 = TAILQ_FIRST(&network_tbl)) != NULL) { + TAILQ_REMOVE(&network_tbl, n1, link); + free(n1); + } +} + +/** + * Get the interface list from mibII only at this point to be sure that + * it is there already. + */ +void +start_network_tbl(void) +{ + + mib_refresh_iflist(); + network_get_interfaces(); +} + +/** + * Refresh the table. + */ +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; + } + + /* mark each entry as missing */ + TAILQ_FOREACH(entry, &network_tbl, link) + entry->flags &= ~HR_NETWORK_FOUND; + + network_get_interfaces(); + + /* + * Purge items that disappeared + */ + TAILQ_FOREACH_SAFE(entry, &network_tbl, link, entry_tmp) { + if (!(entry->flags & HR_NETWORK_FOUND)) + network_entry_delete(entry); + } + + HRDBG("refresh DONE"); +} + +/* + * This is the implementation for a generated (by our SNMP tool) + * function prototype, see hostres_tree.h + * It handles the SNMP operations for hrNetworkTable + */ +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(); +} diff --git a/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_partition_tbl.c b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_partition_tbl.c new file mode 100644 index 0000000..65c0012 --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_partition_tbl.c @@ -0,0 +1,630 @@ +/*- + * Copyright (c) 2005-2006 The FreeBSD Project + * All rights reserved. + * + * Author: Victor Cruceru <soc-victor@freebsd.org> + * + * Redistribution of this software and documentation and use in source and + * binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code or documentation must retain the above + * copyright notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * Host Resources MIB: hrPartitionTable implementation for SNMPd. + */ + +#include <sys/types.h> +#include <sys/limits.h> + +#include <assert.h> +#include <err.h> +#include <inttypes.h> +#include <libgeom.h> +#include <paths.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <sysexits.h> + +#include "hostres_snmp.h" +#include "hostres_oid.h" +#include "hostres_tree.h" + +#ifdef PC98 +#define HR_FREEBSD_PART_TYPE 0xc494 +#else +#define HR_FREEBSD_PART_TYPE 165 +#endif + +/* Maximum length for label and id including \0 */ +#define PART_STR_MLEN (128 + 1) + +/* + * One row in the hrPartitionTable + */ +struct partition_entry { + asn_subid_t index[2]; + u_char *label; /* max allocated len will be PART_STR_MLEN */ + u_char *id; /* max allocated len will be PART_STR_MLEN */ + int32_t size; + int32_t fs_Index; + TAILQ_ENTRY(partition_entry) link; +#define HR_PARTITION_FOUND 0x001 + uint32_t flags; +}; +TAILQ_HEAD(partition_tbl, partition_entry); + +/* + * This table is used to get a consistent indexing. It saves the name -> index + * mapping while we rebuild the partition table. + */ +struct partition_map_entry { + int32_t index; /* partition_entry::index */ + u_char *id; /* max allocated len will be PART_STR_MLEN */ + + /* + * next may be NULL if the respective partition_entry + * is (temporally) gone. + */ + struct partition_entry *entry; + STAILQ_ENTRY(partition_map_entry) link; +}; +STAILQ_HEAD(partition_map, partition_map_entry); + +/* Mapping table for consistent indexing */ +static struct partition_map partition_map = + STAILQ_HEAD_INITIALIZER(partition_map); + +/* THE partition table. */ +static struct partition_tbl partition_tbl = + TAILQ_HEAD_INITIALIZER(partition_tbl); + +/* next int available for indexing the hrPartitionTable */ +static uint32_t next_partition_index = 1; + +/* + * Partition_entry_cmp is used for INSERT_OBJECT_FUNC_LINK + * macro. + */ +static int +partition_entry_cmp(const struct partition_entry *a, + const struct partition_entry *b) +{ + assert(a != NULL); + assert(b != NULL); + + if (a->index[0] < b->index[0]) + return (-1); + + if (a->index[0] > b->index[0]) + return (+1); + + if (a->index[1] < b->index[1]) + return (-1); + + if (a->index[1] > b->index[1]) + return (+1); + + return (0); +} + +/* + * Partition_idx_cmp is used for NEXT_OBJECT_FUNC and FIND_OBJECT_FUNC + * macros + */ +static int +partition_idx_cmp(const struct asn_oid *oid, u_int sub, + const struct partition_entry *entry) +{ + u_int i; + + for (i = 0; i < 2 && i < oid->len - sub; i++) { + if (oid->subs[sub + i] < entry->index[i]) + return (-1); + if (oid->subs[sub + i] > entry->index[i]) + return (+1); + } + if (oid->len - sub < 2) + return (-1); + if (oid->len - sub > 2) + return (+1); + + return (0); +} + +/** + * Create a new partition table entry + */ +static struct partition_entry * +partition_entry_create(int32_t ds_index, const char *chunk_name) +{ + struct partition_entry *entry; + struct partition_map_entry *map; + size_t id_len; + + /* sanity checks */ + assert(chunk_name != NULL); + if (chunk_name == NULL || chunk_name[0] == '\0') + return (NULL); + + /* check whether we already have seen this partition */ + STAILQ_FOREACH(map, &partition_map, link) + if (strcmp(map->id, chunk_name) == 0) + break; + + if (map == NULL) { + /* new object - get a new index and create a map */ + + if (next_partition_index > INT_MAX) { + /* Unrecoverable error - die clean and quicly*/ + syslog(LOG_ERR, "%s: hrPartitionTable index wrap", + __func__); + errx(EX_SOFTWARE, "hrPartitionTable index wrap"); + } + + if ((map = malloc(sizeof(*map))) == NULL) { + syslog(LOG_ERR, "hrPartitionTable: %s: %m", __func__); + return (NULL); + } + + id_len = strlen(chunk_name) + 1; + if (id_len > PART_STR_MLEN) + id_len = PART_STR_MLEN; + + if ((map->id = malloc(id_len)) == NULL) { + free(map); + return (NULL); + } + + map->index = next_partition_index++; + + strlcpy(map->id, chunk_name, id_len); + + map->entry = NULL; + + STAILQ_INSERT_TAIL(&partition_map, map, link); + + HRDBG("%s added into hrPartitionMap at index=%d", + chunk_name, map->index); + + } else { + HRDBG("%s exists in hrPartitionMap index=%d", + chunk_name, map->index); + } + + if ((entry = malloc(sizeof(*entry))) == NULL) { + syslog(LOG_WARNING, "hrPartitionTable: %s: %m", __func__); + return (NULL); + } + memset(entry, 0, sizeof(*entry)); + + /* create the index */ + entry->index[0] = ds_index; + entry->index[1] = map->index; + + map->entry = entry; + + if ((entry->id = strdup(map->id)) == NULL) { + free(entry); + return (NULL); + } + + /* + * reuse id_len from here till the end of this function + * for partition_entry::label + */ + id_len = strlen(_PATH_DEV) + strlen(chunk_name) + 1; + + if (id_len > PART_STR_MLEN) + id_len = PART_STR_MLEN; + + if ((entry->label = malloc(id_len )) == NULL) { + free(entry->id); + free(entry); + return (NULL); + } + + snprintf(entry->label, id_len, "%s%s", _PATH_DEV, chunk_name); + + INSERT_OBJECT_FUNC_LINK(entry, &partition_tbl, link, + partition_entry_cmp); + + return (entry); +} + +/** + * Delete a partition table entry but keep the map entry intact. + */ +static void +partition_entry_delete(struct partition_entry *entry) +{ + struct partition_map_entry *map; + + assert(entry != NULL); + + TAILQ_REMOVE(&partition_tbl, entry, link); + STAILQ_FOREACH(map, &partition_map, link) + if (map->entry == entry) { + map->entry = NULL; + break; + } + free(entry->id); + free(entry->label); + free(entry); +} + +/** + * Find a partition table entry by name. If none is found, return NULL. + */ +static struct partition_entry * +partition_entry_find_by_name(const char *name) +{ + struct partition_entry *entry = NULL; + + TAILQ_FOREACH(entry, &partition_tbl, link) + if (strcmp(entry->id, name) == 0) + return (entry); + + return (NULL); +} + +/** + * Find a partition table entry by label. If none is found, return NULL. + */ +static struct partition_entry * +partition_entry_find_by_label(const char *name) +{ + struct partition_entry *entry = NULL; + + TAILQ_FOREACH(entry, &partition_tbl, link) + if (strcmp(entry->label, name) == 0) + return (entry); + + return (NULL); +} + +/** + * Process a chunk from libgeom(4). A chunk is either a slice or a partition. + * If necessary create a new partition table entry for it. In any case + * set the size field of the entry and set the FOUND flag. + */ +static void +handle_chunk(int32_t ds_index, const char *chunk_name, off_t chunk_size) +{ + struct partition_entry *entry; + daddr_t k_size; + + assert(chunk_name != NULL); + assert(chunk_name[0] != '\0'); + if (chunk_name == NULL || chunk_name == '\0') + return; + + HRDBG("ANALYZE chunk %s", chunk_name); + + if ((entry = partition_entry_find_by_name(chunk_name)) == NULL) + if ((entry = partition_entry_create(ds_index, + chunk_name)) == NULL) + return; + + entry->flags |= HR_PARTITION_FOUND; + + /* actual size may overflow the SNMP type */ + k_size = chunk_size / 1024; + entry->size = (k_size > (off_t)INT_MAX ? INT_MAX : k_size); +} + +/** + * Start refreshing the partition table. A call to this function will + * be followed by a call to handleDiskStorage() for every disk, followed + * by a single call to the post_refresh function. + */ +void +partition_tbl_pre_refresh(void) +{ + struct partition_entry *entry; + + /* mark each entry as missing */ + TAILQ_FOREACH(entry, &partition_tbl, link) + entry->flags &= ~HR_PARTITION_FOUND; +} + +/** + * Try to find a geom(4) class by its name. Returns a pointer to that + * class if found NULL otherways. + */ +static struct gclass * +find_class(struct gmesh *mesh, const char *name) +{ + struct gclass *classp; + + LIST_FOREACH(classp, &mesh->lg_class, lg_class) + if (strcmp(classp->lg_name, name) == 0) + return (classp); + return (NULL); +} + +/** + * Process all MBR-type partitions from the given disk. + */ +static void +get_mbr(struct gclass *classp, int32_t ds_index, const char *disk_dev_name) +{ + struct ggeom *gp; + struct gprovider *pp; + struct gconfig *conf; + long part_type; + + LIST_FOREACH(gp, &classp->lg_geom, lg_geom) { + /* We are only interested in partitions from this disk */ + if (strcmp(gp->lg_name, disk_dev_name) != 0) + continue; + + /* + * Find all the non-BSD providers (these are handled in get_bsd) + */ + LIST_FOREACH(pp, &gp->lg_provider, lg_provider) { + LIST_FOREACH(conf, &pp->lg_config, lg_config) { + if (conf->lg_name == NULL || + conf->lg_val == NULL || + strcmp(conf->lg_name, "type") != 0) + continue; + + /* + * We are not interested in BSD partitions + * (ie ad0s1 is not interesting at this point). + * We'll take care of them in detail (slice + * by slice) in get_bsd. + */ + part_type = strtol(conf->lg_val, NULL, 10); + if (part_type == HR_FREEBSD_PART_TYPE) + break; + HRDBG("-> MBR PROVIDER Name: %s", pp->lg_name); + HRDBG("Mediasize: %jd", + (intmax_t)pp->lg_mediasize / 1024); + HRDBG("Sectorsize: %u", pp->lg_sectorsize); + HRDBG("Mode: %s", pp->lg_mode); + HRDBG("CONFIG: %s: %s", + conf->lg_name, conf->lg_val); + + handle_chunk(ds_index, pp->lg_name, + pp->lg_mediasize); + } + } + } +} + +/** + * Process all BSD-type partitions from the given disk. + */ +static void +get_bsd_sun(struct gclass *classp, int32_t ds_index, const char *disk_dev_name) +{ + struct ggeom *gp; + struct gprovider *pp; + + LIST_FOREACH(gp, &classp->lg_geom, lg_geom) { + /* + * We are only interested in those geoms starting with + * the disk_dev_name passed as parameter to this function. + */ + if (strncmp(gp->lg_name, disk_dev_name, + strlen(disk_dev_name)) != 0) + continue; + + LIST_FOREACH(pp, &gp->lg_provider, lg_provider) { + if (pp->lg_name == NULL) + continue; + handle_chunk(ds_index, pp->lg_name, pp->lg_mediasize); + } + } +} + +/** + * Called from the DiskStorage table for every row. Open the GEOM(4) framework + * and process all the partitions in it. + * ds_index is the index into the DiskStorage table. + * This is done in two steps: for non BSD partitions the geom class "MBR" is + * used, for our BSD slices the "BSD" geom class. + */ +void +partition_tbl_handle_disk(int32_t ds_index, const char *disk_dev_name) +{ + struct gmesh mesh; /* GEOM userland tree */ + struct gclass *classp; + int error; + + assert(disk_dev_name != NULL); + assert(ds_index > 0); + + HRDBG("===> getting partitions for %s <===", disk_dev_name); + + /* try to construct the GEOM tree */ + if ((error = geom_gettree(&mesh)) != 0) { + syslog(LOG_WARNING, "cannot get GEOM tree: %m"); + return; + } + + /* + * First try the GEOM "MBR" class. + * This is needed for non-BSD slices (aka partitions) + * on PC architectures. + */ + if ((classp = find_class(&mesh, "MBR")) != NULL) { + get_mbr(classp, ds_index, disk_dev_name); + } else { + HRDBG("cannot find \"MBR\" geom class"); + } + + /* + * Get the "BSD" GEOM class. + * Here we'll find all the info needed about the BSD slices. + */ + if ((classp = find_class(&mesh, "BSD")) != NULL) { + get_bsd_sun(classp, ds_index, disk_dev_name); + } else { + /* no problem on sparc64 */ + HRDBG("cannot find \"BSD\" geom class"); + } + + /* + * Get the "SUN" GEOM class. + * Here we'll find all the info needed about the BSD slices. + */ + if ((classp = find_class(&mesh, "SUN")) != NULL) { + get_bsd_sun(classp, ds_index, disk_dev_name); + } else { + /* no problem on i386 */ + HRDBG("cannot find \"SUN\" geom class"); + } + + geom_deletetree(&mesh); +} + +/** + * Finish refreshing the table. + */ +void +partition_tbl_post_refresh(void) +{ + struct partition_entry *e, *etmp; + + /* + * Purge items that disappeared + */ + TAILQ_FOREACH_SAFE(e, &partition_tbl, link, etmp) + if (!(e->flags & HR_PARTITION_FOUND)) + partition_entry_delete(e); +} + +/* + * Finalization routine for hrPartitionTable + * It destroys the lists and frees any allocated heap memory + */ +void +fini_partition_tbl(void) +{ + struct partition_map_entry *m; + + while ((m = STAILQ_FIRST(&partition_map)) != NULL) { + STAILQ_REMOVE_HEAD(&partition_map, link); + if(m->entry != NULL) { + TAILQ_REMOVE(&partition_tbl, m->entry, link); + free(m->entry->id); + free(m->entry->label); + free(m->entry); + } + free(m->id); + free(m); + } + assert(TAILQ_EMPTY(&partition_tbl)); +} + +/** + * Called from the file system code to insert the file system table index + * into the partition table entry. Note, that an partition table entry exists + * only for local file systems. + */ +void +handle_partition_fs_index(const char *name, int32_t fs_idx) +{ + struct partition_entry *entry; + + if ((entry = partition_entry_find_by_label(name)) == NULL) { + HRDBG("%s IS MISSING from hrPartitionTable", name); + return; + } + HRDBG("%s [FS index = %d] IS in hrPartitionTable", name, fs_idx); + entry->fs_Index = fs_idx; +} + +/* + * This is the implementation for a generated (by our SNMP tool) + * function prototype, see hostres_tree.h + * It handles the SNMP operations for hrPartitionTable + */ +int +op_hrPartitionTable(struct snmp_context *ctx __unused, struct snmp_value *value, + u_int sub, u_int iidx __unused, enum snmp_op op) +{ + struct partition_entry *entry; + + /* + * Refresh the disk storage table (which refreshes the partition + * table) if necessary. + */ + refresh_disk_storage_tbl(0); + + switch (op) { + + case SNMP_OP_GETNEXT: + if ((entry = NEXT_OBJECT_FUNC(&partition_tbl, + &value->var, sub, partition_idx_cmp)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + + value->var.len = sub + 2; + value->var.subs[sub] = entry->index[0]; + value->var.subs[sub + 1] = entry->index[1]; + + goto get; + + case SNMP_OP_GET: + if ((entry = FIND_OBJECT_FUNC(&partition_tbl, + &value->var, sub, partition_idx_cmp)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + goto get; + + case SNMP_OP_SET: + if ((entry = FIND_OBJECT_FUNC(&partition_tbl, + &value->var, sub, partition_idx_cmp)) == NULL) + return (SNMP_ERR_NOT_WRITEABLE); + return (SNMP_ERR_NO_CREATION); + + case SNMP_OP_ROLLBACK: + case SNMP_OP_COMMIT: + abort(); + } + abort(); + + get: + switch (value->var.subs[sub - 1]) { + + case LEAF_hrPartitionIndex: + value->v.integer = entry->index[1]; + return (SNMP_ERR_NOERROR); + + case LEAF_hrPartitionLabel: + return (string_get(value, entry->label, -1)); + + case LEAF_hrPartitionID: + return(string_get(value, entry->id, -1)); + + case LEAF_hrPartitionSize: + value->v.integer = entry->size; + return (SNMP_ERR_NOERROR); + + case LEAF_hrPartitionFSIndex: + value->v.integer = entry->fs_Index; + return (SNMP_ERR_NOERROR); + } + abort(); +} diff --git a/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_printer_tbl.c b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_printer_tbl.c new file mode 100644 index 0000000..883d67a --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_printer_tbl.c @@ -0,0 +1,398 @@ +/*- + * Copyright (c) 2005-2006 The FreeBSD Project + * All rights reserved. + * + * Author: Victor Cruceru <soc-victor@freebsd.org> + * + * Redistribution of this software and documentation and use in source and + * binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code or documentation must retain the above + * copyright notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * Host Resources MIB implementation for SNMPd: instrumentation for + * hrPrinterTable + */ + +#include <sys/param.h> +#include <sys/stat.h> + +#include <assert.h> +#include <err.h> +#include <errno.h> +#include <paths.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 <sys/dirent.h> +#include "lp.h" + +/* Constants */ +static const struct asn_oid OIDX_hrDevicePrinter_c = OIDX_hrDevicePrinter; + +enum PrinterStatus { + PS_OTHER = 1, + PS_UNKNOWN = 2, + PS_IDLE = 3, + PS_PRINTING = 4, + PS_WARMUP = 5 +}; + +/* + * This structure is used to hold a SNMP table entry + * for HOST-RESOURCES-MIB's hrPrinterTable. + */ +struct printer_entry { + int32_t index; + int32_t status; /* values from PrinterStatus enum above */ + u_char detectedErrorState[2]; + TAILQ_ENTRY(printer_entry) link; +#define HR_PRINTER_FOUND 0x001 + uint32_t flags; + +}; +TAILQ_HEAD(printer_tbl, printer_entry); + +/* the hrPrinterTable */ +static struct printer_tbl printer_tbl = TAILQ_HEAD_INITIALIZER(printer_tbl); + +/* last (agent) tick when hrPrinterTable was updated */ +static uint64_t printer_tick; + +/** + * Create entry into the printer table. + */ +static struct printer_entry * +printer_entry_create(const struct device_entry *devEntry) +{ + struct printer_entry *entry = NULL; + + assert(devEntry != NULL); + if (devEntry == NULL) + return (NULL); + + if ((entry = malloc(sizeof(*entry))) == NULL) { + syslog(LOG_WARNING, "hrPrinterTable: %s: %m", __func__); + return (NULL); + } + memset(entry, 0, sizeof(*entry)); + entry->index = devEntry->index; + INSERT_OBJECT_INT(entry, &printer_tbl); + return (entry); +} + +/** + * Delete entry from the printer table. + */ +static void +printer_entry_delete(struct printer_entry *entry) +{ + + assert(entry != NULL); + if (entry == NULL) + return; + + TAILQ_REMOVE(&printer_tbl, entry, link); + free(entry); +} + +/** + * Find a printer by its index + */ +static struct printer_entry * +printer_find_by_index(int32_t idx) +{ + struct printer_entry *entry; + + TAILQ_FOREACH(entry, &printer_tbl, link) + if (entry->index == idx) + return (entry); + + return (NULL); +} + +/** + * Get the status of a printer + */ +static enum PrinterStatus +get_printer_status(const struct printer *pp) +{ + char statfile[MAXPATHLEN]; + char lockfile[MAXPATHLEN]; + char fline[128]; + int fd; + FILE *f = NULL; + enum PrinterStatus ps = PS_UNKNOWN; + + if (pp->lock_file[0] == '/') + strlcpy(lockfile, pp->lock_file, sizeof(lockfile)); + else + snprintf(lockfile, sizeof(lockfile), "%s/%s", + pp->spool_dir, pp->lock_file); + + fd = open(lockfile, O_RDONLY); + if (fd < 0 || flock(fd, LOCK_SH | LOCK_NB) == 0) { + ps = PS_IDLE; + goto LABEL_DONE; + } + + if (pp->status_file[0] == '/') + strlcpy(statfile, pp->status_file, sizeof(statfile)); + else + snprintf(statfile, sizeof(statfile), "%s/%s", + pp->spool_dir, pp->status_file); + + f = fopen(statfile, "r"); + if (f == NULL) { + syslog(LOG_ERR, "cannot open status file: %s", strerror(errno)); + ps = PS_UNKNOWN; + goto LABEL_DONE; + } + + memset(&fline[0], '\0', sizeof(line)); + if (fgets(fline, sizeof(fline) -1, f) == NULL) { + ps = PS_UNKNOWN; + goto LABEL_DONE; + } + + if (strstr(fline, "is ready and printing") != NULL) { + ps = PS_PRINTING; + goto LABEL_DONE; + } + + if (strstr(fline, "to become ready (offline?)") != NULL) { + ps = PS_OTHER; + goto LABEL_DONE; + } + +LABEL_DONE: + if (fd >= 0) + (void)close(fd); /* unlocks as well */ + + if (f != NULL) + (void)fclose(f); + + return (ps); +} + +/** + * Called for each printer found in /etc/printcap. + */ +static void +handle_printer(struct printer *pp) +{ + struct device_entry *dev_entry; + struct printer_entry *printer_entry; + char dev_only[128]; + struct stat sb; + + if (pp->remote_host != NULL) { + HRDBG("skipped %s -- remote", pp->printer); + return; + } + + if (strncmp(pp->lp, _PATH_DEV, strlen(_PATH_DEV)) != 0) { + HRDBG("skipped %s [device %s] -- remote", pp->printer, pp->lp); + return; + } + + memset(dev_only, '\0', sizeof(dev_only)); + snprintf(dev_only, sizeof(dev_only), "%s", pp->lp + strlen(_PATH_DEV)); + + HRDBG("printer %s has device %s", pp->printer, dev_only); + + if (stat(pp->lp, &sb) < 0) { + if (errno == ENOENT) { + HRDBG("skipped %s -- device %s missing", + pp->printer, pp->lp); + return; + } + } + + if ((dev_entry = device_find_by_name(dev_only)) == NULL) { + HRDBG("%s not in hrDeviceTable", pp->lp); + return; + } + HRDBG("%s found in hrDeviceTable", pp->lp); + dev_entry->type = &OIDX_hrDevicePrinter_c; + + dev_entry->flags |= HR_DEVICE_IMMUTABLE; + + /* Then check hrPrinterTable for this device */ + if ((printer_entry = printer_find_by_index(dev_entry->index)) == NULL && + (printer_entry = printer_entry_create(dev_entry)) == NULL) + return; + + printer_entry->flags |= HR_PRINTER_FOUND; + printer_entry->status = get_printer_status(pp); + memset(printer_entry->detectedErrorState, 0, + sizeof(printer_entry->detectedErrorState)); +} + +static void +hrPrinter_get_OS_entries(void) +{ + int status, more; + struct printer myprinter, *pp = &myprinter; + + init_printer(pp); + HRDBG("---->Getting printers ....."); + more = firstprinter(pp, &status); + if (status) + goto errloop; + + while (more) { + do { + HRDBG("---->Got printer %s", pp->printer); + + handle_printer(pp); + more = nextprinter(pp, &status); +errloop: + if (status) + syslog(LOG_WARNING, + "hrPrinterTable: printcap entry for %s " + "has errors, skipping", + pp->printer ? pp->printer : "<noname?>"); + } while (more && status); + } + + lastprinter(); + printer_tick = this_tick; +} + +/** + * Init the things for hrPrinterTable + */ +void +init_printer_tbl(void) +{ + + hrPrinter_get_OS_entries(); +} + +/** + * Finalization routine for hrPrinterTable + * It destroys the lists and frees any allocated heap memory + */ +void +fini_printer_tbl(void) +{ + struct printer_entry *n1; + + while ((n1 = TAILQ_FIRST(&printer_tbl)) != NULL) { + TAILQ_REMOVE(&printer_tbl, n1, link); + free(n1); + } +} + +/** + * Refresh the printer table if needed. + */ +void +refresh_printer_tbl(void) +{ + struct printer_entry *entry; + struct printer_entry *entry_tmp; + + if (this_tick <= printer_tick) { + HRDBG("no refresh needed"); + return; + } + + /* mark each entry as missing */ + TAILQ_FOREACH(entry, &printer_tbl, link) + entry->flags &= ~HR_PRINTER_FOUND; + + hrPrinter_get_OS_entries(); + + /* + * Purge items that disappeared + */ + entry = TAILQ_FIRST(&printer_tbl); + while (entry != NULL) { + entry_tmp = TAILQ_NEXT(entry, link); + if (!(entry->flags & HR_PRINTER_FOUND)) + printer_entry_delete(entry); + entry = entry_tmp; + } + + printer_tick = this_tick; + + HRDBG("refresh DONE "); +} + +int +op_hrPrinterTable(struct snmp_context *ctx __unused, struct snmp_value *value, + u_int sub, u_int iidx __unused, enum snmp_op curr_op) +{ + struct printer_entry *entry; + + refresh_printer_tbl(); + + switch (curr_op) { + + case SNMP_OP_GETNEXT: + if ((entry = NEXT_OBJECT_INT(&printer_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(&printer_tbl, &value->var, + sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + goto get; + + case SNMP_OP_SET: + if ((entry = FIND_OBJECT_INT(&printer_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_hrPrinterStatus: + value->v.integer = entry->status; + return (SNMP_ERR_NOERROR); + + case LEAF_hrPrinterDetectedErrorState: + return (string_get(value, entry->detectedErrorState, + sizeof(entry->detectedErrorState))); + } + abort(); +} diff --git a/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_processor_tbl.c b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_processor_tbl.c new file mode 100644 index 0000000..e75b55e --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_processor_tbl.c @@ -0,0 +1,431 @@ +/*- + * Copyright (c) 2005-2006 The FreeBSD Project + * All rights reserved. + * + * Author: Victor Cruceru <soc-victor@freebsd.org> + * + * Redistribution of this software and documentation and use in source and + * binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code or documentation must retain the above + * copyright notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * Host Resources MIB for SNMPd. Implementation for hrProcessorTable + */ + +#include <sys/param.h> +#include <sys/sysctl.h> +#include <sys/user.h> + +#include <assert.h> +#include <math.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> + +#include "hostres_snmp.h" +#include "hostres_oid.h" +#include "hostres_tree.h" + +/* + * This structure is used to hold a SNMP table entry + * for HOST-RESOURCES-MIB's hrProcessorTable. + * Note that index is external being allocated & maintained + * by the hrDeviceTable code.. + */ +struct processor_entry { + int32_t index; + const struct asn_oid *frwId; + int32_t load; /* average cpu usage */ + int32_t sample_cnt; /* number of usage samples */ + int32_t cur_sample_idx; /* current valid sample */ + TAILQ_ENTRY(processor_entry) link; + u_char cpu_no; /* which cpu, counted from 0 */ + + /* the samples from the last minute, as required by MIB */ + double samples[MAX_CPU_SAMPLES]; + long states[MAX_CPU_SAMPLES][CPUSTATES]; +}; +TAILQ_HEAD(processor_tbl, processor_entry); + +/* the head of the list with hrDeviceTable's entries */ +static struct processor_tbl processor_tbl = + TAILQ_HEAD_INITIALIZER(processor_tbl); + +/* number of processors in dev tbl */ +static int32_t detected_processor_count; + +/* sysctlbyname(hw.ncpu) */ +static int hw_ncpu; + +/* sysctlbyname(kern.cp_times) */ +static int cpmib[2]; +static size_t cplen; + +/* periodic timer used to get cpu load stats */ +static void *cpus_load_timer; + +/** + * Returns the CPU usage of a given processor entry. + * + * It needs at least two cp_times "tick" samples to calculate a delta and + * thus, the usage over the sampling period. + */ +static int +get_avg_load(struct processor_entry *e) +{ + u_int i, oldest; + long delta = 0; + double usage = 0.0; + + assert(e != NULL); + + /* Need two samples to perform delta calculation. */ + if (e->sample_cnt <= 1) + return (0); + + /* Oldest usable index, we wrap around. */ + if (e->sample_cnt == MAX_CPU_SAMPLES) + oldest = (e->cur_sample_idx + 1) % MAX_CPU_SAMPLES; + else + oldest = 0; + + /* Sum delta for all states. */ + for (i = 0; i < CPUSTATES; i++) { + delta += e->states[e->cur_sample_idx][i]; + delta -= e->states[oldest][i]; + } + if (delta == 0) + return 0; + + /* Take idle time from the last element and convert to + * percent usage by contrasting with total ticks delta. */ + usage = (double)(e->states[e->cur_sample_idx][CPUSTATES-1] - + e->states[oldest][CPUSTATES-1]) / delta; + usage = 100 - (usage * 100); + HRDBG("CPU no. %d, delta ticks %ld, pct usage %.2f", e->cpu_no, + delta, usage); + + return ((int)(usage)); +} + +/** + * Save a new sample to proc entry and get the average usage. + * + * Samples are stored in a ringbuffer from 0..(MAX_CPU_SAMPLES-1) + */ +static void +save_sample(struct processor_entry *e, long *cp_times) +{ + int i; + + e->cur_sample_idx = (e->cur_sample_idx + 1) % MAX_CPU_SAMPLES; + for (i = 0; cp_times != NULL && i < CPUSTATES; i++) + e->states[e->cur_sample_idx][i] = cp_times[i]; + + e->sample_cnt++; + if (e->sample_cnt > MAX_CPU_SAMPLES) + e->sample_cnt = MAX_CPU_SAMPLES; + + HRDBG("sample count for CPU no. %d went to %d", e->cpu_no, e->sample_cnt); + e->load = get_avg_load(e); + +} + +/** + * Create a new entry into the processor table. + */ +static struct processor_entry * +proc_create_entry(u_int cpu_no, struct device_map_entry *map) +{ + struct device_entry *dev; + struct processor_entry *entry; + char name[128]; + + /* + * If there is no map entry create one by creating a device table + * entry. + */ + if (map == NULL) { + snprintf(name, sizeof(name), "cpu%u", cpu_no); + if ((dev = device_entry_create(name, "", "")) == NULL) + return (NULL); + dev->flags |= HR_DEVICE_IMMUTABLE; + STAILQ_FOREACH(map, &device_map, link) + if (strcmp(map->name_key, name) == 0) + break; + if (map == NULL) + abort(); + } + + if ((entry = malloc(sizeof(*entry))) == NULL) { + syslog(LOG_ERR, "hrProcessorTable: %s malloc " + "failed: %m", __func__); + return (NULL); + } + memset(entry, 0, sizeof(*entry)); + + entry->index = map->hrIndex; + entry->load = 0; + entry->sample_cnt = 0; + entry->cur_sample_idx = -1; + entry->cpu_no = (u_char)cpu_no; + entry->frwId = &oid_zeroDotZero; /* unknown id FIXME */ + + INSERT_OBJECT_INT(entry, &processor_tbl); + + HRDBG("CPU %d added with SNMP index=%d", + entry->cpu_no, entry->index); + + return (entry); +} + +/** + * Scan the device map table for CPUs and create an entry into the + * processor table for each CPU. + * + * Make sure that the number of processors announced by the kernel hw.ncpu + * is equal to the number of processors we have found in the device table. + */ +static void +create_proc_table(void) +{ + struct device_map_entry *map; + struct processor_entry *entry; + int cpu_no; + size_t len; + + detected_processor_count = 0; + + /* + * Because hrProcessorTable depends on hrDeviceTable, + * the device detection must be performed at this point. + * If not, no entries will be present in the hrProcessor Table. + * + * For non-ACPI system the processors are not in the device table, + * therefore insert them after checking hw.ncpu. + */ + STAILQ_FOREACH(map, &device_map, link) + if (strncmp(map->name_key, "cpu", strlen("cpu")) == 0 && + strstr(map->location_key, ".CPU") != NULL) { + if (sscanf(map->name_key,"cpu%d", &cpu_no) != 1) { + syslog(LOG_ERR, "hrProcessorTable: Failed to " + "get cpu no. from device named '%s'", + map->name_key); + continue; + } + + if ((entry = proc_create_entry(cpu_no, map)) == NULL) + continue; + + detected_processor_count++; + } + + len = sizeof(hw_ncpu); + if (sysctlbyname("hw.ncpu", &hw_ncpu, &len, NULL, 0) == -1 || + len != sizeof(hw_ncpu)) { + syslog(LOG_ERR, "hrProcessorTable: sysctl(hw.ncpu) failed"); + hw_ncpu = 0; + } + + HRDBG("%d CPUs detected via device table; hw.ncpu is %d", + detected_processor_count, hw_ncpu); + + /* XXX Can happen on non-ACPI systems? Create entries by hand. */ + for (; detected_processor_count < hw_ncpu; detected_processor_count++) + proc_create_entry(detected_processor_count, NULL); + + len = 2; + if (sysctlnametomib("kern.cp_times", cpmib, &len)) { + syslog(LOG_ERR, "hrProcessorTable: sysctlnametomib(kern.cp_times) failed"); + cpmib[0] = 0; + cpmib[1] = 0; + cplen = 0; + } else if (sysctl(cpmib, 2, NULL, &len, NULL, 0)) { + syslog(LOG_ERR, "hrProcessorTable: sysctl(kern.cp_times) length query failed"); + cplen = 0; + } else { + cplen = len / sizeof(long); + } + HRDBG("%zu entries for kern.cp_times", cplen); + +} + +/** + * Free the processor table + */ +static void +free_proc_table(void) +{ + struct processor_entry *n1; + + while ((n1 = TAILQ_FIRST(&processor_tbl)) != NULL) { + TAILQ_REMOVE(&processor_tbl, n1, link); + free(n1); + detected_processor_count--; + } + + assert(detected_processor_count == 0); + detected_processor_count = 0; +} + +/** + * Refresh all values in the processor table. We call this once for + * every PDU that accesses the table. + */ +static void +refresh_processor_tbl(void) +{ + struct processor_entry *entry; + size_t size; + + long pcpu_cp_times[cplen]; + memset(pcpu_cp_times, 0, sizeof(pcpu_cp_times)); + + size = cplen * sizeof(long); + if (sysctl(cpmib, 2, pcpu_cp_times, &size, NULL, 0) == -1 && + !(errno == ENOMEM && size >= cplen * sizeof(long))) { + syslog(LOG_ERR, "hrProcessorTable: sysctl(kern.cp_times) failed"); + return; + } + + TAILQ_FOREACH(entry, &processor_tbl, link) { + assert(hr_kd != NULL); + save_sample(entry, &pcpu_cp_times[entry->cpu_no * CPUSTATES]); + } + +} + +/** + * This function is called MAX_CPU_SAMPLES times per minute to collect the + * CPU load. + */ +static void +get_cpus_samples(void *arg __unused) +{ + + HRDBG("[%llu] ENTER", (unsigned long long)get_ticks()); + refresh_processor_tbl(); + HRDBG("[%llu] EXIT", (unsigned long long)get_ticks()); +} + +/** + * Called to start this table. We need to start the periodic idle + * time collection. + */ +void +start_processor_tbl(struct lmodule *mod) +{ + + /* + * Start the cpu stats collector + * The semantics of timer_start parameters is in "SNMP ticks"; + * we have 100 "SNMP ticks" per second, thus we are trying below + * to get MAX_CPU_SAMPLES per minute + */ + cpus_load_timer = timer_start_repeat(100, 100 * 60 / MAX_CPU_SAMPLES, + get_cpus_samples, NULL, mod); +} + +/** + * Init the things for hrProcessorTable. + * Scan the device table for processor entries. + */ +void +init_processor_tbl(void) +{ + + /* create the initial processor table */ + create_proc_table(); + /* and get first samples */ + refresh_processor_tbl(); +} + +/** + * Finalization routine for hrProcessorTable. + * It destroys the lists and frees any allocated heap memory. + */ +void +fini_processor_tbl(void) +{ + + if (cpus_load_timer != NULL) { + timer_stop(cpus_load_timer); + cpus_load_timer = NULL; + } + + free_proc_table(); +} + +/** + * Access routine for the processor table. + */ +int +op_hrProcessorTable(struct snmp_context *ctx __unused, + struct snmp_value *value, u_int sub, u_int iidx __unused, + enum snmp_op curr_op) +{ + struct processor_entry *entry; + + switch (curr_op) { + + case SNMP_OP_GETNEXT: + if ((entry = NEXT_OBJECT_INT(&processor_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(&processor_tbl, + &value->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + goto get; + + case SNMP_OP_SET: + if ((entry = FIND_OBJECT_INT(&processor_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_hrProcessorFrwID: + assert(entry->frwId != NULL); + value->v.oid = *entry->frwId; + return (SNMP_ERR_NOERROR); + + case LEAF_hrProcessorLoad: + value->v.integer = entry->load; + return (SNMP_ERR_NOERROR); + } + abort(); +} diff --git a/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_scalars.c b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_scalars.c new file mode 100644 index 0000000..87b78e8 --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_scalars.c @@ -0,0 +1,492 @@ +/*- + * Copyright (c) 2005-2006 The FreeBSD Project + * All rights reserved. + * + * Author: Victor Cruceru <soc-victor@freebsd.org> + * + * Redistribution of this software and documentation and use in source and + * binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code or documentation must retain the above + * copyright notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * Host Resources MIB scalars implementation for SNMPd. + */ + +#include <sys/types.h> +#include <sys/sysctl.h> + +#include <pwd.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <syslog.h> +#include <utmpx.h> + +#include "hostres_snmp.h" +#include "hostres_oid.h" +#include "hostres_tree.h" + +/* boot timestamp in centi-seconds */ +static uint64_t kernel_boot; + +/* physical memory size in Kb */ +static uint64_t phys_mem_size; + +/* boot line (malloced) */ +static u_char *boot_line; + +/* maximum number of processes */ +static uint32_t max_proc; + +/** + * Free all static data + */ +void +fini_scalars(void) +{ + + free(boot_line); +} + +/** + * Get system uptime in hundredths of seconds since the epoch + * Returns 0 in case of an error + */ +static int +OS_getSystemUptime(uint32_t *ut) +{ + struct timeval right_now; + uint64_t now; + + if (kernel_boot == 0) { + /* first time, do the sysctl */ + struct timeval kernel_boot_timestamp; + int mib[2] = { CTL_KERN, KERN_BOOTTIME }; + size_t len = sizeof(kernel_boot_timestamp); + + if (sysctl(mib, 2, &kernel_boot_timestamp, + &len, NULL, 0) == -1) { + syslog(LOG_ERR, "sysctl KERN_BOOTTIME failed: %m"); + return (SNMP_ERR_GENERR); + } + + HRDBG("boot timestamp from kernel: {%lld, %ld}", + (long long)kernel_boot_timestamp.tv_sec, + (long)kernel_boot_timestamp.tv_usec); + + kernel_boot = ((uint64_t)kernel_boot_timestamp.tv_sec * 100) + + (kernel_boot_timestamp.tv_usec / 10000); + } + + if (gettimeofday(&right_now, NULL) < 0) { + syslog(LOG_ERR, "gettimeofday failed: %m"); + return (SNMP_ERR_GENERR); + } + now = ((uint64_t)right_now.tv_sec * 100) + (right_now.tv_usec / 10000); + + if (now - kernel_boot > UINT32_MAX) + *ut = UINT32_MAX; + else + *ut = now - kernel_boot; + + return (SNMP_ERR_NOERROR); +} + +/** + * Get system local date and time in a foramt suitable for DateAndTime TC: + * field octets contents range + * ----- ------ -------- ----- + * 1 1-2 year* 0..65536 + * 2 3 month 1..12 + * 3 4 day 1..31 + * 4 5 hour 0..23 + * 5 6 minutes 0..59 + * 6 7 seconds 0..60 + * (use 60 for leap-second) + * 7 8 deci-seconds 0..9 + * 8 9 direction from UTC '+' / '-' + * 9 10 hours from UTC* 0..13 + * 10 11 minutes from UTC 0..59 + * + * * Notes: + * - the value of year is in network-byte order + * - daylight saving time in New Zealand is +13 + * + * For example, Tuesday May 26, 1992 at 1:30:15 PM EDT would be + * displayed as: + * + * 1992-5-26,13:30:15.0,-4:0 + * + * Returns -1 in case of an error or the length of the string (8 or 11) + * Actually returns always 11 on freebsd + */ +static int +OS_getSystemDate(struct snmp_value *value) +{ + u_char s_date_time[11]; + struct tm tloc_tm; + time_t tloc_time_t; + struct timeval right_now; + int string_len; + + if (gettimeofday(&right_now, NULL) < 0) { + syslog(LOG_ERR, "gettimeofday failed: %m"); + return (SNMP_ERR_GENERR); + } + + tloc_time_t = right_now.tv_sec; + + if (localtime_r(&tloc_time_t, &tloc_tm) == NULL) { + syslog(LOG_ERR, "localtime_r() failed: %m "); + return (SNMP_ERR_GENERR); + } + + string_len = make_date_time(s_date_time, &tloc_tm, + right_now.tv_usec / 100000); + + return (string_get(value, s_date_time, string_len)); +} + +/** + * Get kernel boot path. For FreeBSD it seems that no arguments are + * present. Returns NULL if an error occurred. The returned data is a + * pointer to a global storage. + */ +int +OS_getSystemInitialLoadParameters(u_char **params) +{ + + if (boot_line == NULL) { + int mib[2] = { CTL_KERN, KERN_BOOTFILE }; + char *buf; + size_t buf_len = 0; + + /* get the needed buffer len */ + if (sysctl(mib, 2, NULL, &buf_len, NULL, 0) != 0) { + syslog(LOG_ERR, + "sysctl({CTL_KERN,KERN_BOOTFILE}) failed: %m"); + return (SNMP_ERR_GENERR); + } + + if ((buf = malloc(buf_len)) == NULL) { + syslog(LOG_ERR, "malloc failed"); + return (SNMP_ERR_GENERR); + } + if (sysctl(mib, 2, buf, &buf_len, NULL, 0)) { + syslog(LOG_ERR, + "sysctl({CTL_KERN,KERN_BOOTFILE}) failed: %m"); + free(buf); + return (SNMP_ERR_GENERR); + } + + boot_line = buf; + HRDBG("kernel boot file: %s", boot_line); + } + + *params = boot_line; + return (SNMP_ERR_NOERROR); +} + +/** + * Get number of current users which are logged in + */ +static int +OS_getSystemNumUsers(uint32_t *nu) +{ + struct utmpx *utmp; + + setutxent(); + *nu = 0; + while ((utmp = getutxent()) != NULL) { + if (utmp->ut_type == USER_PROCESS) + (*nu)++; + } + endutxent(); + + return (SNMP_ERR_NOERROR); +} + +/** + * Get number of current processes existing into the system + */ +static int +OS_getSystemProcesses(uint32_t *proc_count) +{ + int pc; + + if (hr_kd == NULL) + return (SNMP_ERR_GENERR); + + if (kvm_getprocs(hr_kd, KERN_PROC_ALL, 0, &pc) == NULL) { + syslog(LOG_ERR, "kvm_getprocs failed: %m"); + return (SNMP_ERR_GENERR); + } + + *proc_count = pc; + return (SNMP_ERR_NOERROR); +} + +/** + * Get maximum number of processes allowed on this system + */ +static int +OS_getSystemMaxProcesses(uint32_t *mproc) +{ + + if (max_proc == 0) { + int mib[2] = { CTL_KERN, KERN_MAXPROC }; + int mp; + size_t len = sizeof(mp); + + if (sysctl(mib, 2, &mp, &len, NULL, 0) == -1) { + syslog(LOG_ERR, "sysctl KERN_MAXPROC failed: %m"); + return (SNMP_ERR_GENERR); + } + max_proc = mp; + } + + *mproc = max_proc; + return (SNMP_ERR_NOERROR); +} + +/* + * Get the physical memeory size in Kbytes. + * Returns SNMP error code. + */ +static int +OS_getMemorySize(uint32_t *ms) +{ + + if (phys_mem_size == 0) { + int mib[2] = { CTL_HW, HW_PHYSMEM }; + u_long physmem; + size_t len = sizeof(physmem); + + if (sysctl(mib, 2, &physmem, &len, NULL, 0) == -1) { + syslog(LOG_ERR, + "sysctl({ CTL_HW, HW_PHYSMEM }) failed: %m"); + return (SNMP_ERR_GENERR); + } + + phys_mem_size = physmem / 1024; + } + + if (phys_mem_size > UINT32_MAX) + *ms = UINT32_MAX; + else + *ms = phys_mem_size; + return (SNMP_ERR_NOERROR); +} + +/* + * Try to use the s_date_time parameter as a DateAndTime TC to fill in + * the second parameter. + * Returns 0 on succes and -1 for an error. + * Bug: time zone info is not used + */ +static struct timeval * +OS_checkSystemDateInput(const u_char *str, u_int len) +{ + struct tm tm_to_set; + time_t t; + struct timeval *tv; + + if (len != 8 && len != 11) + return (NULL); + + if (str[2] == 0 || str[2] > 12 || + str[3] == 0 || str[3] > 31 || + str[4] > 23 || str[5] > 59 || str[6] > 60 || str[7] > 9) + return (NULL); + + tm_to_set.tm_year = ((str[0] << 8) + str[1]) - 1900; + tm_to_set.tm_mon = str[2] - 1; + tm_to_set.tm_mday = str[3]; + tm_to_set.tm_hour = str[4]; + tm_to_set.tm_min = str[5]; + tm_to_set.tm_sec = str[6]; + tm_to_set.tm_isdst = 0; + + /* now make UTC from it */ + if ((t = timegm(&tm_to_set)) == (time_t)-1) + return (NULL); + + /* now apply timezone if specified */ + if (len == 11) { + if (str[9] > 13 || str[10] > 59) + return (NULL); + if (str[8] == '+') + t += 3600 * str[9] + 60 * str[10]; + else + t -= 3600 * str[9] + 60 * str[10]; + } + + if ((tv = malloc(sizeof(*tv))) == NULL) + return (NULL); + + tv->tv_sec = t; + tv->tv_usec = (int32_t)str[7] * 100000; + + return (tv); +} + +/* + * Set system date and time. Timezone is not changed + */ +static int +OS_setSystemDate(const struct timeval *timeval_to_set) +{ + if (settimeofday(timeval_to_set, NULL) == -1) { + syslog(LOG_ERR, "settimeofday failed: %m"); + return (SNMP_ERR_GENERR); + } + return (SNMP_ERR_NOERROR); +} + +/* + * prototype of this function was genrated by gensnmptree tool in header file + * hostres_tree.h + * Returns SNMP_ERR_NOERROR on success + */ +int +op_hrSystem(struct snmp_context *ctx, struct snmp_value *value, + u_int sub, u_int iidx __unused, enum snmp_op curr_op) +{ + int err; + u_char *str; + + switch (curr_op) { + + case SNMP_OP_GET: + switch (value->var.subs[sub - 1]) { + + case LEAF_hrSystemUptime: + return (OS_getSystemUptime(&value->v.uint32)); + + case LEAF_hrSystemDate: + return (OS_getSystemDate(value)); + + case LEAF_hrSystemInitialLoadDevice: + value->v.uint32 = 0; /* FIXME */ + return (SNMP_ERR_NOERROR); + + case LEAF_hrSystemInitialLoadParameters: + if ((err = OS_getSystemInitialLoadParameters(&str)) != + SNMP_ERR_NOERROR) + return (err); + return (string_get(value, str, -1)); + + case LEAF_hrSystemNumUsers: + return (OS_getSystemNumUsers(&value->v.uint32)); + + case LEAF_hrSystemProcesses: + return (OS_getSystemProcesses(&value->v.uint32)); + + case LEAF_hrSystemMaxProcesses: + return (OS_getSystemMaxProcesses(&value->v.uint32)); + } + abort(); + + case SNMP_OP_SET: + switch (value->var.subs[sub - 1]) { + + case LEAF_hrSystemDate: + if ((ctx->scratch->ptr1 = + OS_checkSystemDateInput(value->v.octetstring.octets, + value->v.octetstring.len)) == NULL) + return (SNMP_ERR_WRONG_VALUE); + + return (SNMP_ERR_NOERROR); + + case LEAF_hrSystemInitialLoadDevice: + case LEAF_hrSystemInitialLoadParameters: + return (SNMP_ERR_NOT_WRITEABLE); + + } + abort(); + + case SNMP_OP_ROLLBACK: + switch (value->var.subs[sub - 1]) { + + case LEAF_hrSystemDate: + free(ctx->scratch->ptr1); + return (SNMP_ERR_NOERROR); + + case LEAF_hrSystemInitialLoadDevice: + case LEAF_hrSystemInitialLoadParameters: + abort(); + } + abort(); + + case SNMP_OP_COMMIT: + switch (value->var.subs[sub - 1]) { + + case LEAF_hrSystemDate: + (void)OS_setSystemDate(ctx->scratch->ptr1); + free(ctx->scratch->ptr1); + return (SNMP_ERR_NOERROR); + + case LEAF_hrSystemInitialLoadDevice: + case LEAF_hrSystemInitialLoadParameters: + abort(); + } + abort(); + + case SNMP_OP_GETNEXT: + abort(); + } + abort(); +} + +/* + * prototype of this function was genrated by gensnmptree tool + * in the header file hostres_tree.h + * Returns SNMP_ERR_NOERROR on success + */ +int +op_hrStorage(struct snmp_context *ctx __unused, struct snmp_value *value, + u_int sub, u_int iidx __unused, enum snmp_op curr_op) +{ + + /* only GET is possible */ + switch (curr_op) { + + case SNMP_OP_GET: + switch (value->var.subs[sub - 1]) { + + case LEAF_hrMemorySize: + return (OS_getMemorySize(&value->v.uint32)); + } + abort(); + + case SNMP_OP_SET: + return (SNMP_ERR_NOT_WRITEABLE); + + case SNMP_OP_ROLLBACK: + case SNMP_OP_COMMIT: + case SNMP_OP_GETNEXT: + abort(); + } + abort(); +} diff --git a/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_snmp.c b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_snmp.c new file mode 100644 index 0000000..c2a06db --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_snmp.c @@ -0,0 +1,208 @@ +/*- + * Copyright (c) 2005-2006 The FreeBSD Project + * All rights reserved. + * + * Author: Victor Cruceru <soc-victor@freebsd.org> + * + * Redistribution of this software and documentation and use in source and + * binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code or documentation must retain the above + * copyright notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This C file contains code developed by Poul-Henning Kamp under the + * following license: + * + * FreeBSD: src/sbin/mdconfig/mdconfig.c,v 1.33.2.1 2004/09/14 03:32:21 jmg Exp + * ---------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * <phk@FreeBSD.ORG> wrote this file. As long as you retain this notice you + * can do whatever you want with this stuff. If we meet some day, and you think + * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp + * ---------------------------------------------------------------------------- + * + * $FreeBSD$ + */ + +/* + * Host Resources MIB implementation for bsnmpd. + */ + +#include <paths.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" + +/* Internal id got after we'll register this module with the agent */ +static u_int host_registration_id = 0; + +/* This our hostres module */ +static struct lmodule *hostres_module; + +/* See the generated file hostres_oid.h */ +static const struct asn_oid oid_host = OIDX_host; + +/* descriptor to access kernel memory */ +kvm_t *hr_kd; + +/* + * HOST RESOURCES mib module finalization hook. + * Returns 0 on success, < 0 on error + */ +static int +hostres_fini(void) +{ + + if (hr_kd != NULL) + (void)kvm_close(hr_kd); + + fini_storage_tbl(); + fini_fs_tbl(); + fini_processor_tbl(); + fini_disk_storage_tbl(); + fini_device_tbl(); + fini_partition_tbl(); + fini_network_tbl(); + fini_printer_tbl(); + + fini_swrun_tbl(); + fini_swins_tbl(); + + fini_scalars(); + + if (host_registration_id > 0) + or_unregister(host_registration_id); + + HRDBG("done."); + return (0); +} + +/* + * HOST RESOURCES mib module initialization hook. + * Returns 0 on success, < 0 on error + */ +static int +hostres_init(struct lmodule *mod, int argc __unused, char *argv[] __unused) +{ + + hostres_module = mod; + + /* + * NOTE: order of these calls is important here! + */ + if ((hr_kd = kvm_open(NULL, _PATH_DEVNULL, NULL, O_RDONLY, + "kvm_open")) == NULL) { + syslog(LOG_ERR, "kvm_open failed: %m "); + return (-1); + } + + /* + * The order is relevant here, because some table depend on each other. + */ + init_device_tbl(); + + /* populates partition table too */ + if (init_disk_storage_tbl()) { + hostres_fini(); + return (-1); + } + init_processor_tbl(); + init_printer_tbl(); + + /* + * populate storage and FS tables. Must be done after device + * initialisation because the FS refresh code calls into the + * partition refresh code. + */ + init_storage_tbl(); + + + /* also the hrSWRunPerfTable's support is initialized here */ + init_swrun_tbl(); + init_swins_tbl(); + + HRDBG("done."); + + return (0); +} + +/* + * HOST RESOURCES mib module start operation + * returns nothing + */ +static void +hostres_start(void) +{ + + host_registration_id = or_register(&oid_host, + "The MIB module for Host Resource MIB (RFC 2790).", + hostres_module); + + start_device_tbl(hostres_module); + start_processor_tbl(hostres_module); + start_network_tbl(); + + HRDBG("done."); +} + +/* this identifies the HOST RESOURCES mib module */ +const struct snmp_module config = { + "This module implements the host resource mib (rfc 2790)", + hostres_init, + hostres_fini, + NULL, /* idle function, do not use it */ + NULL, + NULL, + hostres_start, + NULL, /* proxy a PDU */ + hostres_ctree, /* see the generated hostres_tree.h */ + hostres_CTREE_SIZE, /* see the generated hostres_tree.h */ + NULL +}; + +/** + * Make an SNMP DateAndTime from a struct tm. This should be in the library. + */ +int +make_date_time(u_char *str, const struct tm *tm, u_int decisecs) +{ + + str[0] = (u_char)((tm->tm_year + 1900) >> 8); + str[1] = (u_char)(tm->tm_year + 1900); + str[2] = tm->tm_mon + 1; + str[3] = tm->tm_mday; + str[4] = tm->tm_hour; + str[5] = tm->tm_min; + str[6] = tm->tm_sec; + str[7] = decisecs; + if (tm->tm_gmtoff < 0) + str[8] = '-'; + else + str[8] = '+'; + + str[9] = (u_char)(abs(tm->tm_gmtoff) / 3600); + str[10] = (u_char)((abs(tm->tm_gmtoff) % 3600) / 60); + + return (11); +} diff --git a/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_snmp.h b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_snmp.h new file mode 100644 index 0000000..04928d2 --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_snmp.h @@ -0,0 +1,324 @@ +/* + * Copyright (c) 2005-2006 The FreeBSD Project + * All rights reserved. + * + * Author: Victor Cruceru <soc-victor@freebsd.org> + * + * Redistribution of this software and documentation and use in source and + * binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code or documentation must retain the above + * copyright notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Host Resources MIB for SNMPd. + * + * $FreeBSD$ + */ + +#ifndef HOSTRES_SNMP_H_1132245017 +#define HOSTRES_SNMP_H_1132245017 + +#include <sys/types.h> +#include <sys/queue.h> + +#include <stdio.h> +#include <fcntl.h> +#include <kvm.h> +#include <devinfo.h> + +#include <bsnmp/asn1.h> +#include <bsnmp/snmp.h> + +#include <bsnmp/snmpmod.h> + +/* + * Default package directory for hrSWInstalledTable. Can be overridden + * via SNMP or configuration file. + */ +#define PATH_PKGDIR "/var/db/pkg" + +/* + * These are the default maximum caching intervals for the various tables + * in seconds. They can be overridden from the configuration file. + */ +#define HR_STORAGE_TBL_REFRESH 7 +#define HR_FS_TBL_REFRESH 7 +#define HR_DISK_TBL_REFRESH 7 +#define HR_NETWORK_TBL_REFRESH 7 +#define HR_SWINS_TBL_REFRESH 120 +#define HR_SWRUN_TBL_REFRESH 3 + +struct tm; +struct statfs; + +/* a debug macro */ +#ifndef NDEBUG + +#define HRDBG(...) do { \ + fprintf(stderr, "HRDEBUG: %s: ", __func__); \ + fprintf(stderr, __VA_ARGS__); \ + fprintf(stderr, "\n"); \ + } while (0) + +#else + +#define HRDBG(...) do { } while (0) + +#endif /*NDEBUG*/ + +/* path to devd(8) output pipe */ +#define PATH_DEVD_PIPE "/var/run/devd.pipe" + +#define IS_KERNPROC(kp) (((kp)->ki_flag & P_KTHREAD) == P_KTHREAD) + +enum snmpTCTruthValue { + SNMP_TRUE = 1, + SNMP_FALSE= 2 +}; + +/* The number of CPU load samples per one minute, per each CPU */ +#define MAX_CPU_SAMPLES 4 + + +/* + * max len (including '\0'), for device_entry::descr field below, + * according to MIB + */ +#define DEV_DESCR_MLEN (64 + 1) + +/* + * max len (including '\0'), for device_entry::name and + * device_map_entry::name_key fields below, according to MIB + */ +#define DEV_NAME_MLEN (32 + 1) + +/* + * max len (including '\0'), for device_entry::location and + * device_map_entry::location_key fields below, according to MIB + */ +#define DEV_LOC_MLEN (128 + 1) + +/* + * This structure is used to hold a SNMP table entry + * for HOST-RESOURCES-MIB's hrDeviceTable + */ +struct device_entry { + int32_t index; + const struct asn_oid *type; + u_char *descr; + const struct asn_oid *id; /* only oid_zeroDotZero as (*id) value*/ + int32_t status; /* enum DeviceStatus */ + uint32_t errors; + +#define HR_DEVICE_FOUND 0x001 + /* not dectected by libdevice, so don't try to refresh it*/ +#define HR_DEVICE_IMMUTABLE 0x002 + + /* next 3 are not from the SNMP mib table, only to be used internally */ + uint32_t flags; + + u_char *name; + u_char *location; + TAILQ_ENTRY(device_entry) link; +}; + +/* + * Next structure is used to keep o list of mappings from a specific + * name (a_name) to an entry in the hrFSTblEntry; + * We are trying to keep the same index for a specific name at least + * for the duration of one SNMP agent run. + */ +struct device_map_entry { + int32_t hrIndex; /* used for hrDeviceTblEntry::index */ + + /* map key is the pair (name_key, location_key) */ + u_char *name_key; /* copy of device name */ + u_char *location_key; + + /* + * Next may be NULL if the respective hrDeviceTblEntry + * is (temporally) gone. + */ + struct device_entry *entry_p; + STAILQ_ENTRY(device_map_entry) link; +}; +STAILQ_HEAD(device_map, device_map_entry); + +/* descriptor to access kernel memory */ +extern kvm_t *hr_kd; + +/* Table used for consistent device table indexing. */ +extern struct device_map device_map; + +/* Maximum number of ticks between two updates for hrStorageTable */ +extern uint32_t storage_tbl_refresh; + +/* Maximum number of ticks between updated of FS table */ +extern uint32_t fs_tbl_refresh; + +/* maximum number of ticks between updates of SWRun and SWRunPerf table */ +extern uint32_t swrun_tbl_refresh; + +/* Maximum number of ticks between device table refreshs. */ +extern uint32_t device_tbl_refresh; + +/* maximum number of ticks between refreshs */ +extern uint32_t disk_storage_tbl_refresh; + +/* maximum number of ticks between updates of network table */ +extern uint32_t swins_tbl_refresh; + +/* maximum number of ticks between updates of network table */ +extern uint32_t network_tbl_refresh; + +/* package directory */ +extern u_char *pkg_dir; + +/* Initialize and populate storage table */ +void init_storage_tbl(void); + +/* Finalization routine for hrStorageTable. */ +void fini_storage_tbl(void); + +/* Refresh routine for hrStorageTable. */ +void refresh_storage_tbl(int); + +/* + * Get the type of filesystem referenced in a struct statfs * - + * used by FSTbl and StorageTbl functions. + */ +const struct asn_oid *fs_get_type(const struct statfs *); + +/* + * Because hrFSTable depends to hrStorageTable we are + * refreshing hrFSTable by refreshing hrStorageTable. + * When one entry "of type" fs from hrStorageTable is refreshed + * then the corresponding entry from hrFSTable is refreshed + * FS_tbl_pre_refresh_v() is called before refeshing fs part of hrStorageTable + */ +void fs_tbl_pre_refresh(void); +void fs_tbl_process_statfs_entry(const struct statfs *, int32_t); + +/* Called after refreshing fs part of hrStorageTable */ +void fs_tbl_post_refresh(void); + +/* Refresh the FS table if necessary. */ +void refresh_fs_tbl(void); + +/* Finalization routine for hrFSTable. */ +void fini_fs_tbl(void); + +/* Init the things for both of hrSWRunTable and hrSWRunPerfTable */ +void init_swrun_tbl(void); + +/* Finalization routine for both of hrSWRunTable and hrSWRunPerfTable */ +void fini_swrun_tbl(void); + +/* Init and populate hrDeviceTable */ +void init_device_tbl(void); + +/* start devd monitoring */ +void start_device_tbl(struct lmodule *); + +/* Finalization routine for hrDeviceTable */ +void fini_device_tbl(void); + +/* Refresh routine for hrDeviceTable. */ +void refresh_device_tbl(int); + +/* Find an item in hrDeviceTbl by its entry->index. */ +struct device_entry *device_find_by_index(int32_t); + +/* Find an item in hrDeviceTbl by name. */ +struct device_entry *device_find_by_name(const char *); + +/* Create a new entry out of thin air. */ +struct device_entry *device_entry_create(const char *, const char *, + const char *); + +/* Delete an entry from hrDeviceTbl */ +void device_entry_delete(struct device_entry *entry); + +/* Init the things for hrProcessorTable. */ +void init_processor_tbl(void); + +/* Finalization routine for hrProcessorTable. */ +void fini_processor_tbl(void); + +/* Start the processor table CPU load collector. */ +void start_processor_tbl(struct lmodule *); + +/* Init the things for hrDiskStorageTable */ +int init_disk_storage_tbl(void); + +/* Finalization routine for hrDiskStorageTable. */ +void fini_disk_storage_tbl(void); + +/* Refresh routine for hrDiskStorageTable. */ +void refresh_disk_storage_tbl(int); + +/* Finalization routine for hrPartitionTable. */ +void fini_partition_tbl(void); + +/* Finalization routine for hrNetworkTable. */ +void fini_network_tbl(void); + +/* populate network table */ +void start_network_tbl(void); + +/* initialize installed software table */ +void init_swins_tbl(void); + +/* finalize installed software table */ +void fini_swins_tbl(void); + +/* refresh the hrSWInstalledTable if necessary */ +void refresh_swins_tbl(void); + +/* Init the things for hrPrinterTable */ +void init_printer_tbl(void); + +/* Finalization routine for hrPrinterTable. */ +void fini_printer_tbl(void); + +/* Refresh printer table */ +void refresh_printer_tbl(void); + +/* get boot command line */ +int OS_getSystemInitialLoadParameters(u_char **); + +/* Start refreshing the partition table */ +void partition_tbl_post_refresh(void); + +/* Handle refresh for the given disk */ +void partition_tbl_handle_disk(int32_t, const char *); + +/* Finish refreshing the partition table. */ +void partition_tbl_pre_refresh(void); + +/* Set the FS index in a partition table entry */ +void handle_partition_fs_index(const char *, int32_t); + +/* Make an SNMP DateAndTime from a struct tm. */ +int make_date_time(u_char *, const struct tm *, u_int); + +/* Free all static data */ +void fini_scalars(void); + +#endif /* HOSTRES_SNMP_H_1132245017 */ diff --git a/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_storage_tbl.c b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_storage_tbl.c new file mode 100644 index 0000000..ee8bdcc --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_storage_tbl.c @@ -0,0 +1,668 @@ +/*- + * Copyright (c) 2005-2006 The FreeBSD Project + * All rights reserved. + * + * Author: Victor Cruceru <soc-victor@freebsd.org> + * + * Redistribution of this software and documentation and use in source and + * binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code or documentation must retain the above + * copyright notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * Host Resources MIB for SNMPd. Implementation for hrStorageTable + */ + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/sysctl.h> +#include <sys/vmmeter.h> +#include <sys/mount.h> + +#include <vm/vm_param.h> + +#include <assert.h> +#include <err.h> +#include <limits.h> +#include <memstat.h> +#include <paths.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <unistd.h> /* for getpagesize() */ +#include <sysexits.h> + +#include "hostres_snmp.h" +#include "hostres_oid.h" +#include "hostres_tree.h" + +/* maximum length for descritpion string according to MIB */ +#define SE_DESC_MLEN (255 + 1) + +/* + * This structure is used to hold a SNMP table entry + * for HOST-RESOURCES-MIB's hrStorageTable + */ +struct storage_entry { + int32_t index; + const struct asn_oid *type; + u_char *descr; + int32_t allocationUnits; + int32_t size; + int32_t used; + uint32_t allocationFailures; +#define HR_STORAGE_FOUND 0x001 + uint32_t flags; /* to be used internally*/ + TAILQ_ENTRY(storage_entry) link; +}; +TAILQ_HEAD(storage_tbl, storage_entry); + +/* + * Next structure is used to keep o list of mappings from a specific name + * (a_name) to an entry in the hrStorageTblEntry. We are trying to keep the + * same index for a specific name at least for the duration of one SNMP agent + * run. + */ +struct storage_map_entry { + int32_t hrIndex; /* used for storage_entry::index */ + + /* map key, also used for storage_entry::descr */ + u_char *a_name; + + /* + * next may be NULL if the respective storage_entry + * is (temporally) gone + */ + struct storage_entry *entry; + STAILQ_ENTRY(storage_map_entry) link; +}; +STAILQ_HEAD(storage_map, storage_map_entry); + +/* the head of the list with table's entries */ +static struct storage_tbl storage_tbl = TAILQ_HEAD_INITIALIZER(storage_tbl); + +/*for consistent table indexing*/ +static struct storage_map storage_map = + STAILQ_HEAD_INITIALIZER(storage_map); + +/* last (agent) tick when hrStorageTable was updated */ +static uint64_t storage_tick; + +/* maximum number of ticks between two refreshs */ +uint32_t storage_tbl_refresh = HR_STORAGE_TBL_REFRESH * 100; + +/* for kvm_getswapinfo, malloc'd */ +static struct kvm_swap *swap_devs; +static size_t swap_devs_len; /* item count for swap_devs */ + +/* for getfsstat, malloc'd */ +static struct statfs *fs_buf; +static size_t fs_buf_count; /* item count for fs_buf */ + +static struct vmtotal mem_stats; + +/* next int available for indexing the hrStorageTable */ +static uint32_t next_storage_index = 1; + +/* start of list for memory detailed stats */ +static struct memory_type_list *mt_list; + +/* Constants */ +static const struct asn_oid OIDX_hrStorageRam_c = OIDX_hrStorageRam; +static const struct asn_oid OIDX_hrStorageVirtualMemory_c = + OIDX_hrStorageVirtualMemory; + +/** + * Create a new entry into the storage table and, if necessary, an + * entry into the storage map. + */ +static struct storage_entry * +storage_entry_create(const char *name) +{ + struct storage_entry *entry; + struct storage_map_entry *map; + size_t name_len; + + assert(name != NULL); + assert(strlen(name) > 0); + + STAILQ_FOREACH(map, &storage_map, link) + if (strcmp(map->a_name, name) == 0) + break; + + if (map == NULL) { + /* new object - get a new index */ + if (next_storage_index > INT_MAX) { + syslog(LOG_ERR, + "%s: hrStorageTable index wrap", __func__); + errx(EX_SOFTWARE, "hrStorageTable index wrap"); + } + + if ((map = malloc(sizeof(*map))) == NULL) { + syslog(LOG_ERR, "hrStorageTable: %s: %m", __func__ ); + return (NULL); + } + + name_len = strlen(name) + 1; + if (name_len > SE_DESC_MLEN) + name_len = SE_DESC_MLEN; + + if ((map->a_name = malloc(name_len)) == NULL) { + free(map); + return (NULL); + } + + strlcpy(map->a_name, name, name_len); + map->hrIndex = next_storage_index++; + + STAILQ_INSERT_TAIL(&storage_map, map, link); + + HRDBG("%s added into hrStorageMap at index=%d", + name, map->hrIndex); + } else { + HRDBG("%s exists in hrStorageMap index=%d\n", + name, map->hrIndex); + } + + if ((entry = malloc(sizeof(*entry))) == NULL) { + syslog(LOG_WARNING, "%s: %m", __func__); + return (NULL); + } + memset(entry, 0, sizeof(*entry)); + + entry->index = map->hrIndex; + + if ((entry->descr = strdup(map->a_name)) == NULL) { + free(entry); + return (NULL); + } + + map->entry = entry; + + INSERT_OBJECT_INT(entry, &storage_tbl); + + return (entry); +} + +/** + * Delete an entry from the storage table. + */ +static void +storage_entry_delete(struct storage_entry *entry) +{ + struct storage_map_entry *map; + + assert(entry != NULL); + + TAILQ_REMOVE(&storage_tbl, entry, link); + STAILQ_FOREACH(map, &storage_map, link) + if (map->entry == entry) { + map->entry = NULL; + break; + } + free(entry->descr); + free(entry); +} + +/** + * Find a table entry by its name. + */ +static struct storage_entry * +storage_find_by_name(const char *name) +{ + struct storage_entry *entry; + + TAILQ_FOREACH(entry, &storage_tbl, link) + if (strcmp(entry->descr, name) == 0) + return (entry); + + return (NULL); +} + +/* + * VM info. + */ +static void +storage_OS_get_vm(void) +{ + int mib[2] = { CTL_VM, VM_TOTAL }; + size_t len = sizeof(mem_stats); + int page_size_bytes; + struct storage_entry *entry; + + if (sysctl(mib, 2, &mem_stats, &len, NULL, 0) < 0) { + syslog(LOG_ERR, + "hrStoragetable: %s: sysctl({CTL_VM, VM_METER}) " + "failed: %m", __func__); + assert(0); + return; + } + + page_size_bytes = getpagesize(); + + /* Real Memory Metrics */ + if ((entry = storage_find_by_name("Real Memory Metrics")) == NULL && + (entry = storage_entry_create("Real Memory Metrics")) == NULL) + return; /* I'm out of luck now, maybe next time */ + + entry->flags |= HR_STORAGE_FOUND; + entry->type = &OIDX_hrStorageRam_c; + entry->allocationUnits = page_size_bytes; + entry->size = mem_stats.t_rm; + entry->used = mem_stats.t_arm; /* ACTIVE is not USED - FIXME */ + entry->allocationFailures = 0; + + /* Shared Real Memory Metrics */ + if ((entry = storage_find_by_name("Shared Real Memory Metrics")) == + NULL && + (entry = storage_entry_create("Shared Real Memory Metrics")) == + NULL) + return; + + entry->flags |= HR_STORAGE_FOUND; + entry->type = &OIDX_hrStorageRam_c; + entry->allocationUnits = page_size_bytes; + entry->size = mem_stats.t_rmshr; + /* ACTIVE is not USED - FIXME */ + entry->used = mem_stats.t_armshr; + entry->allocationFailures = 0; +} + +static void +storage_OS_get_memstat(void) +{ + struct memory_type *mt_item; + struct storage_entry *entry; + + if (mt_list == NULL) { + if ((mt_list = memstat_mtl_alloc()) == NULL) + /* again? we have a serious problem */ + return; + } + + if (memstat_sysctl_all(mt_list, 0) < 0) { + syslog(LOG_ERR, "memstat_sysctl_all failed: %s", + memstat_strerror(memstat_mtl_geterror(mt_list)) ); + return; + } + + if ((mt_item = memstat_mtl_first(mt_list)) == NULL) { + /* usually this is not an error, no errno for this failure*/ + HRDBG("memstat_mtl_first failed"); + return; + } + + do { + const char *memstat_name; + uint64_t tmp_size; + int allocator; + char alloc_descr[SE_DESC_MLEN]; + + memstat_name = memstat_get_name(mt_item); + + if (memstat_name == NULL || strlen(memstat_name) == 0) + continue; + + switch (allocator = memstat_get_allocator(mt_item)) { + + case ALLOCATOR_MALLOC: + snprintf(alloc_descr, sizeof(alloc_descr), + "MALLOC: %s", memstat_name); + break; + + case ALLOCATOR_UMA: + snprintf(alloc_descr, sizeof(alloc_descr), + "UMA: %s", memstat_name); + break; + + default: + snprintf(alloc_descr, sizeof(alloc_descr), + "UNKNOWN%d: %s", allocator, memstat_name); + break; + } + + if ((entry = storage_find_by_name(alloc_descr)) == NULL && + (entry = storage_entry_create(alloc_descr)) == NULL) + return; + + entry->flags |= HR_STORAGE_FOUND; + entry->type = &OIDX_hrStorageRam_c; + + if ((tmp_size = memstat_get_size(mt_item)) == 0) + tmp_size = memstat_get_sizemask(mt_item); + entry->allocationUnits = + (tmp_size > INT_MAX ? INT_MAX : (int32_t)tmp_size); + + tmp_size = memstat_get_countlimit(mt_item); + entry->size = + (tmp_size > INT_MAX ? INT_MAX : (int32_t)tmp_size); + + tmp_size = memstat_get_count(mt_item); + entry->used = + (tmp_size > INT_MAX ? INT_MAX : (int32_t)tmp_size); + + tmp_size = memstat_get_failures(mt_item); + entry->allocationFailures = + (tmp_size > INT_MAX ? INT_MAX : (int32_t)tmp_size); + + } while((mt_item = memstat_mtl_next(mt_item)) != NULL); +} + +/** + * Get swap info + */ +static void +storage_OS_get_swap(void) +{ + int nswapdev = 0; + size_t len = sizeof(nswapdev); + struct storage_entry *entry; + char swap_w_prefix[SE_DESC_MLEN]; + + if (sysctlbyname("vm.nswapdev", &nswapdev, &len, NULL,0 ) < 0) { + syslog(LOG_ERR, + "hrStorageTable: sysctlbyname(\"vm.nswapdev\") " + "failed. %m"); + assert(0); + return; + } + + if (nswapdev <= 0) { + HRDBG("vm.nswapdev is %d", nswapdev); + return; + } + + if (nswapdev + 1 != (int)swap_devs_len || swap_devs == NULL) { + swap_devs_len = nswapdev + 1; + swap_devs = reallocf(swap_devs, + swap_devs_len * sizeof(struct kvm_swap)); + + assert(swap_devs != NULL); + if (swap_devs == NULL) { + swap_devs_len = 0; + return; + } + } + + nswapdev = kvm_getswapinfo(hr_kd, swap_devs, swap_devs_len, 0); + if (nswapdev < 0) { + syslog(LOG_ERR, + "hrStorageTable: kvm_getswapinfo failed. %m\n"); + assert(0); + return; + } + + for (len = 0; len < (size_t)nswapdev; len++) { + memset(&swap_w_prefix[0], '\0', sizeof(swap_w_prefix)); + snprintf(swap_w_prefix, sizeof(swap_w_prefix) - 1, + "Swap:%s%s", _PATH_DEV, swap_devs[len].ksw_devname); + + entry = storage_find_by_name(swap_w_prefix); + if (entry == NULL) + entry = storage_entry_create(swap_w_prefix); + + assert (entry != NULL); + if (entry == NULL) + return; /* Out of luck */ + + entry->flags |= HR_STORAGE_FOUND; + entry->type = &OIDX_hrStorageVirtualMemory_c; + entry->allocationUnits = getpagesize(); + entry->size = swap_devs[len].ksw_total; + entry->used = swap_devs[len].ksw_used; + entry->allocationFailures = 0; + } +} + +/** + * Query the underlaying OS for the mounted file systems + * anf fill in the respective lists (for hrStorageTable and for hrFSTable) + */ +static void +storage_OS_get_fs(void) +{ + struct storage_entry *entry; + uint64_t used_blocks_count = 0; + char fs_string[SE_DESC_MLEN]; + int mounted_fs_count; + int i = 0; + + if ((mounted_fs_count = getfsstat(NULL, 0, MNT_NOWAIT)) < 0) { + syslog(LOG_ERR, "hrStorageTable: getfsstat() failed: %m"); + return; /* out of luck this time */ + } + + if (mounted_fs_count != (int)fs_buf_count || fs_buf == NULL) { + fs_buf_count = mounted_fs_count; + fs_buf = reallocf(fs_buf, fs_buf_count * sizeof(struct statfs)); + if (fs_buf == NULL) { + fs_buf_count = 0; + assert(0); + return; + } + } + + if ((mounted_fs_count = getfsstat(fs_buf, + fs_buf_count * sizeof(struct statfs), MNT_NOWAIT)) < 0) { + syslog(LOG_ERR, "hrStorageTable: getfsstat() failed: %m"); + return; /* out of luck this time */ + } + + HRDBG("got %d mounted FS", mounted_fs_count); + + fs_tbl_pre_refresh(); + + for (i = 0; i < mounted_fs_count; i++) { + snprintf(fs_string, sizeof(fs_string), + "%s, type: %s, dev: %s", fs_buf[i].f_mntonname, + fs_buf[i].f_fstypename, fs_buf[i].f_mntfromname); + + entry = storage_find_by_name(fs_string); + if (entry == NULL) + entry = storage_entry_create(fs_string); + + assert (entry != NULL); + if (entry == NULL) + return; /* Out of luck */ + + entry->flags |= HR_STORAGE_FOUND; + entry->type = fs_get_type(&fs_buf[i]); /*XXX - This is wrong*/ + + if (fs_buf[i].f_bsize > INT_MAX) + entry->allocationUnits = INT_MAX; + else + entry->allocationUnits = fs_buf[i].f_bsize; + + if (fs_buf[i].f_blocks > INT_MAX) + entry->size = INT_MAX; + else + entry->size = fs_buf[i].f_blocks; + + used_blocks_count = fs_buf[i].f_blocks - fs_buf[i].f_bfree; + + if (used_blocks_count > INT_MAX) + entry->used = INT_MAX; + else + entry->used = used_blocks_count; + + entry->allocationFailures = 0; + + /* take care of hrFSTable */ + fs_tbl_process_statfs_entry(&fs_buf[i], entry->index); + } + + fs_tbl_post_refresh(); +} + +/** + * Initialize storage table and populate it. + */ +void +init_storage_tbl(void) +{ + if ((mt_list = memstat_mtl_alloc()) == NULL) + syslog(LOG_ERR, + "hrStorageTable: memstat_mtl_alloc() failed: %m"); + + refresh_storage_tbl(1); +} + +void +fini_storage_tbl(void) +{ + struct storage_map_entry *n1; + + if (swap_devs != NULL) { + free(swap_devs); + swap_devs = NULL; + } + swap_devs_len = 0; + + if (fs_buf != NULL) { + free(fs_buf); + fs_buf = NULL; + } + fs_buf_count = 0; + + while ((n1 = STAILQ_FIRST(&storage_map)) != NULL) { + STAILQ_REMOVE_HEAD(&storage_map, link); + if (n1->entry != NULL) { + TAILQ_REMOVE(&storage_tbl, n1->entry, link); + free(n1->entry->descr); + free(n1->entry); + } + free(n1->a_name); + free(n1); + } + assert(TAILQ_EMPTY(&storage_tbl)); +} + +void +refresh_storage_tbl(int force) +{ + struct storage_entry *entry, *entry_tmp; + + if (!force && storage_tick != 0 && + this_tick - storage_tick < storage_tbl_refresh) { + HRDBG("no refresh needed"); + return; + } + + /* mark each entry as missing */ + TAILQ_FOREACH(entry, &storage_tbl, link) + entry->flags &= ~HR_STORAGE_FOUND; + + storage_OS_get_vm(); + storage_OS_get_swap(); + storage_OS_get_fs(); + storage_OS_get_memstat(); + + /* + * Purge items that disappeared + */ + TAILQ_FOREACH_SAFE(entry, &storage_tbl, link, entry_tmp) + if (!(entry->flags & HR_STORAGE_FOUND)) + storage_entry_delete(entry); + + storage_tick = this_tick; + + HRDBG("refresh DONE"); +} + +/* + * This is the implementation for a generated (by our SNMP tool) + * function prototype, see hostres_tree.h + * It handles the SNMP operations for hrStorageTable + */ +int +op_hrStorageTable(struct snmp_context *ctx __unused, struct snmp_value *value, + u_int sub, u_int iidx __unused, enum snmp_op curr_op) +{ + struct storage_entry *entry; + + refresh_storage_tbl(0); + + switch (curr_op) { + + case SNMP_OP_GETNEXT: + if ((entry = NEXT_OBJECT_INT(&storage_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(&storage_tbl, + &value->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + goto get; + + case SNMP_OP_SET: + if ((entry = FIND_OBJECT_INT(&storage_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_hrStorageIndex: + value->v.integer = entry->index; + return (SNMP_ERR_NOERROR); + + case LEAF_hrStorageType: + assert(entry->type != NULL); + value->v.oid = *entry->type; + return (SNMP_ERR_NOERROR); + + case LEAF_hrStorageDescr: + assert(entry->descr != NULL); + return (string_get(value, entry->descr, -1)); + break; + + case LEAF_hrStorageAllocationUnits: + value->v.integer = entry->allocationUnits; + return (SNMP_ERR_NOERROR); + + case LEAF_hrStorageSize: + value->v.integer = entry->size; + return (SNMP_ERR_NOERROR); + + case LEAF_hrStorageUsed: + value->v.integer = entry->used; + return (SNMP_ERR_NOERROR); + + case LEAF_hrStorageAllocationFailures: + value->v.uint32 = entry->allocationFailures; + return (SNMP_ERR_NOERROR); + } + abort(); +} diff --git a/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_swinstalled_tbl.c b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_swinstalled_tbl.c new file mode 100644 index 0000000..5fa5b4c --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_swinstalled_tbl.c @@ -0,0 +1,555 @@ +/* + * Copyright (c) 2005-2006 The FreeBSD Project + * All rights reserved. + * + * Author: Victor Cruceru <soc-victor@freebsd.org> + * + * Redistribution of this software and documentation and use in source and + * binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code or documentation must retain the above + * copyright notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + * + * Host Resources MIB implementation for SNMPd: instrumentation for + * hrSWInstalledTable + */ + +#include <sys/limits.h> +#include <sys/stat.h> +#include <sys/sysctl.h> +#include <sys/utsname.h> + +#include <assert.h> +#include <dirent.h> +#include <err.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <sysexits.h> + +#include "hostres_snmp.h" +#include "hostres_oid.h" +#include "hostres_tree.h" + +#define CONTENTS_FNAME "+CONTENTS" + +enum SWInstalledType { + SWI_UNKNOWN = 1, + SWI_OPERATING_SYSTEM = 2, + SWI_DEVICE_DRIVER = 3, + SWI_APPLICATION = 4 +}; + +#define SW_NAME_MLEN (64 + 1) + +/* + * This structure is used to hold a SNMP table entry + * for HOST-RESOURCES-MIB's hrSWInstalledTable + */ +struct swins_entry { + int32_t index; + u_char *name; /* max len for this is SW_NAME_MLEN */ + const struct asn_oid *id; + int32_t type; /* from enum SWInstalledType */ + u_char date[11]; + u_int date_len; + +#define HR_SWINSTALLED_FOUND 0x001 +#define HR_SWINSTALLED_IMMUTABLE 0x002 + uint32_t flags; + + TAILQ_ENTRY(swins_entry) link; +}; +TAILQ_HEAD(swins_tbl, swins_entry); + +/* + * Table to keep a conistent mapping between software and indexes. + */ +struct swins_map_entry { + int32_t index; /* swins_entry::index */ + u_char *name; /* map key,a copy of swins_entry::name*/ + + /* + * next may be NULL if the respective hrSWInstalledTblEntry + * is (temporally) gone + */ + struct swins_entry *entry; + + STAILQ_ENTRY(swins_map_entry) link; +}; +STAILQ_HEAD(swins_map, swins_map_entry); + +/* map for consistent indexing */ +static struct swins_map swins_map = STAILQ_HEAD_INITIALIZER(swins_map); + +/* the head of the list with hrSWInstalledTable's entries */ +static struct swins_tbl swins_tbl = TAILQ_HEAD_INITIALIZER(swins_tbl); + +/* next int available for indexing the hrSWInstalledTable */ +static uint32_t next_swins_index = 1; + +/* last (agent) tick when hrSWInstalledTable was updated */ +static uint64_t swins_tick; + +/* maximum number of ticks between updates of network table */ +uint32_t swins_tbl_refresh = HR_SWINS_TBL_REFRESH * 100; + +/* package directory */ +u_char *pkg_dir; + +/* last change of package list */ +static time_t os_pkg_last_change; + +/** + * Create a new entry into the hrSWInstalledTable + */ +static struct swins_entry * +swins_entry_create(const char *name) +{ + struct swins_entry *entry; + struct swins_map_entry *map; + + STAILQ_FOREACH(map, &swins_map, link) + if (strcmp((const char *)map->name, name) == 0) + break; + + if (map == NULL) { + size_t name_len; + /* new object - get a new index */ + if (next_swins_index > INT_MAX) { + syslog(LOG_ERR, "%s: hrSWInstalledTable index wrap", + __func__ ); + /* There isn't much we can do here. + * If the next_swins_index is consumed + * then we can't add entries to this table + * So it is better to exit - if the table is sparsed + * at the next agent run we can fill it fully. + */ + errx(EX_SOFTWARE, "hrSWInstalledTable index wrap"); + } + + if ((map = malloc(sizeof(*map))) == NULL) { + syslog(LOG_ERR, "%s: %m", __func__ ); + return (NULL); + } + + name_len = strlen(name) + 1; + if (name_len > SW_NAME_MLEN) + name_len = SW_NAME_MLEN; + + if ((map->name = malloc(name_len)) == NULL) { + syslog(LOG_WARNING, "%s: %m", __func__); + free(map); + return (NULL); + } + + map->index = next_swins_index++; + strlcpy((char *)map->name, name, name_len); + + STAILQ_INSERT_TAIL(&swins_map, map, link); + + HRDBG("%s added into hrSWInstalled at %d", name, map->index); + } + + if ((entry = malloc(sizeof(*entry))) == NULL) { + syslog(LOG_WARNING, "%s: %m", __func__); + return (NULL); + } + memset(entry, 0, sizeof(*entry)); + + if ((entry->name = strdup(map->name)) == NULL) { + syslog(LOG_WARNING, "%s: %m", __func__); + free(entry); + return (NULL); + } + + entry->index = map->index; + map->entry = entry; + + INSERT_OBJECT_INT(entry, &swins_tbl); + + return (entry); +} + +/** + * Delete an entry in the hrSWInstalledTable + */ +static void +swins_entry_delete(struct swins_entry *entry) +{ + struct swins_map_entry *map; + + assert(entry != NULL); + + TAILQ_REMOVE(&swins_tbl, entry, link); + + STAILQ_FOREACH(map, &swins_map, link) + if (map->entry == entry) { + map->entry = NULL; + break; + } + + free(entry->name); + free(entry); +} + +/** + * Find an entry given it's name + */ +static struct swins_entry * +swins_find_by_name(const char *name) +{ + struct swins_entry *entry; + + TAILQ_FOREACH(entry, &swins_tbl, link) + if (strcmp((const char*)entry->name, name) == 0) + return (entry); + return (NULL); +} + +/** + * Finalize this table + */ +void +fini_swins_tbl(void) +{ + struct swins_map_entry *n1; + + while ((n1 = STAILQ_FIRST(&swins_map)) != NULL) { + STAILQ_REMOVE_HEAD(&swins_map, link); + if (n1->entry != NULL) { + TAILQ_REMOVE(&swins_tbl, n1->entry, link); + free(n1->entry->name); + free(n1->entry); + } + free(n1->name); + free(n1); + } + assert(TAILQ_EMPTY(&swins_tbl)); +} + +/** + * Get the *running* O/S identification + */ +static void +swins_get_OS_ident(void) +{ + struct utsname os_id; + char os_string[SW_NAME_MLEN] = ""; + struct swins_entry *entry; + u_char *boot; + struct stat sb; + struct tm k_ts; + + if (uname(&os_id) == -1) { + syslog(LOG_WARNING, "%s: %m", __func__); + return; + } + + snprintf(os_string, sizeof(os_string), "%s: %s", + os_id.sysname, os_id.version); + + if ((entry = swins_find_by_name(os_string)) != NULL || + (entry = swins_entry_create(os_string)) == NULL) + return; + + entry->flags |= (HR_SWINSTALLED_FOUND | HR_SWINSTALLED_IMMUTABLE); + entry->id = &oid_zeroDotZero; + entry->type = (int32_t)SWI_OPERATING_SYSTEM; + memset(entry->date, 0, sizeof(entry->date)); + + if (OS_getSystemInitialLoadParameters(&boot) == SNMP_ERR_NOERROR && + strlen(boot) > 0 && stat(boot, &sb) == 0 && + localtime_r(&sb.st_ctime, &k_ts) != NULL) + entry->date_len = make_date_time(entry->date, &k_ts, 0); +} + +/** + * Read the installed packages + */ +static int +swins_get_packages(void) +{ + struct stat sb; + DIR *p_dir; + struct dirent *ent; + struct tm k_ts; + char *pkg_file; + struct swins_entry *entry; + int ret = 0; + + if (pkg_dir == NULL) + /* initialisation may have failed */ + return (-1); + + if (stat(pkg_dir, &sb) != 0) { + syslog(LOG_ERR, "hrSWInstalledTable: stat(\"%s\") failed: %m", + pkg_dir); + return (-1); + } + if (!S_ISDIR(sb.st_mode)) { + syslog(LOG_ERR, "hrSWInstalledTable: \"%s\" is not a directory", + pkg_dir); + return (-1); + } + if (sb.st_ctime <= os_pkg_last_change) { + HRDBG("no need to rescan installed packages -- " + "directory time-stamp unmodified"); + + TAILQ_FOREACH(entry, &swins_tbl, link) + entry->flags |= HR_SWINSTALLED_FOUND; + + return (0); + } + + if ((p_dir = opendir(pkg_dir)) == NULL) { + syslog(LOG_ERR, "hrSWInstalledTable: opendir(\"%s\") failed: " + "%m", pkg_dir); + return (-1); + } + + while (errno = 0, (ent = readdir(p_dir)) != NULL) { + HRDBG(" pkg file: %s", ent->d_name); + + /* check that the contents file is a regular file */ + if (asprintf(&pkg_file, "%s/%s/%s", pkg_dir, ent->d_name, + CONTENTS_FNAME) == -1) + continue; + + if (stat(pkg_file, &sb) != 0 ) { + free(pkg_file); + continue; + } + + if (!S_ISREG(sb.st_mode)) { + syslog(LOG_ERR, "hrSWInstalledTable: \"%s\" not a " + "regular file -- skipped", pkg_file); + free(pkg_file); + continue; + } + free(pkg_file); + + /* read directory timestamp on package */ + if (asprintf(&pkg_file, "%s/%s", pkg_dir, ent->d_name) == -1) + continue; + + if (stat(pkg_file, &sb) == -1 || + localtime_r(&sb.st_ctime, &k_ts) == NULL) { + free(pkg_file); + continue; + } + free(pkg_file); + + /* update or create entry */ + if ((entry = swins_find_by_name(ent->d_name)) == NULL && + (entry = swins_entry_create(ent->d_name)) == NULL) { + ret = -1; + goto PKG_LOOP_END; + } + + entry->flags |= HR_SWINSTALLED_FOUND; + entry->id = &oid_zeroDotZero; + entry->type = (int32_t)SWI_APPLICATION; + + entry->date_len = make_date_time(entry->date, &k_ts, 0); + } + + if (errno != 0) { + syslog(LOG_ERR, "hrSWInstalledTable: readdir_r(\"%s\") failed:" + " %m", pkg_dir); + ret = -1; + } else { + /* + * save the timestamp of directory + * to avoid any further scanning + */ + os_pkg_last_change = sb.st_ctime; + } + PKG_LOOP_END: + (void)closedir(p_dir); + return (ret); +} + +/** + * Refresh the installed software table. + */ +void +refresh_swins_tbl(void) +{ + int ret; + struct swins_entry *entry, *entry_tmp; + + if (this_tick - swins_tick < swins_tbl_refresh) { + HRDBG("no refresh needed"); + return; + } + + /* mark each entry as missing */ + TAILQ_FOREACH(entry, &swins_tbl, link) + entry->flags &= ~HR_SWINSTALLED_FOUND; + + ret = swins_get_packages(); + + TAILQ_FOREACH_SAFE(entry, &swins_tbl, link, entry_tmp) + if (!(entry->flags & HR_SWINSTALLED_FOUND) && + !(entry->flags & HR_SWINSTALLED_IMMUTABLE)) + swins_entry_delete(entry); + + if (ret == 0) + swins_tick = this_tick; +} + +/** + * Create and populate the package table + */ +void +init_swins_tbl(void) +{ + + if ((pkg_dir = malloc(sizeof(PATH_PKGDIR))) == NULL) + syslog(LOG_ERR, "%s: %m", __func__); + else + strcpy(pkg_dir, PATH_PKGDIR); + + swins_get_OS_ident(); + refresh_swins_tbl(); + + HRDBG("init done"); +} + +/** + * SNMP handler + */ +int +op_hrSWInstalledTable(struct snmp_context *ctx __unused, + struct snmp_value *value, u_int sub, u_int iidx __unused, + enum snmp_op curr_op) +{ + struct swins_entry *entry; + + refresh_swins_tbl(); + + switch (curr_op) { + + case SNMP_OP_GETNEXT: + if ((entry = NEXT_OBJECT_INT(&swins_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(&swins_tbl, + &value->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + goto get; + + case SNMP_OP_SET: + if ((entry = FIND_OBJECT_INT(&swins_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_hrSWInstalledIndex: + value->v.integer = entry->index; + return (SNMP_ERR_NOERROR); + + case LEAF_hrSWInstalledName: + return (string_get(value, entry->name, -1)); + break; + + case LEAF_hrSWInstalledID: + assert(entry->id != NULL); + value->v.oid = *entry->id; + return (SNMP_ERR_NOERROR); + + case LEAF_hrSWInstalledType: + value->v.integer = entry->type; + return (SNMP_ERR_NOERROR); + + case LEAF_hrSWInstalledDate: + return (string_get(value, entry->date, entry->date_len)); + } + abort(); +} + +/** + * Scalars + */ +int +op_hrSWInstalled(struct snmp_context *ctx __unused, + struct snmp_value *value __unused, u_int sub, + u_int iidx __unused, enum snmp_op curr_op) +{ + + /* only SNMP GET is possible */ + switch (curr_op) { + + case SNMP_OP_GET: + goto get; + + case SNMP_OP_SET: + return (SNMP_ERR_NOT_WRITEABLE); + + case SNMP_OP_ROLLBACK: + case SNMP_OP_COMMIT: + case SNMP_OP_GETNEXT: + abort(); + } + abort(); + + get: + switch (value->var.subs[sub - 1]) { + + case LEAF_hrSWInstalledLastChange: + case LEAF_hrSWInstalledLastUpdateTime: + /* + * We always update the entire table so these two tick + * values should be equal. + */ + refresh_swins_tbl(); + if (swins_tick <= start_tick) + value->v.uint32 = 0; + else { + uint64_t lastChange = swins_tick - start_tick; + + /* may overflow the SNMP type */ + value->v.uint32 = + (lastChange > UINT_MAX ? UINT_MAX : lastChange); + } + + return (SNMP_ERR_NOERROR); + + default: + abort(); + } +} diff --git a/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_swrun_tbl.c b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_swrun_tbl.c new file mode 100644 index 0000000..1f82648 --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_swrun_tbl.c @@ -0,0 +1,792 @@ +/* + * Copyright (c) 2005-2006 The FreeBSD Project + * All rights reserved. + * + * Author: Victor Cruceru <soc-victor@freebsd.org> + * + * Redistribution of this software and documentation and use in source and + * binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code or documentation must retain the above + * copyright notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + * + * Host Resources MIB for SNMPd. Implementation for hrSWRunTable + */ + +#include <sys/param.h> +#include <sys/proc.h> +#include <sys/sysctl.h> +#include <sys/user.h> +#include <sys/linker.h> + +#include <assert.h> +#include <signal.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> + +#include "hostres_snmp.h" +#include "hostres_oid.h" +#include "hostres_tree.h" + +/* + * Ugly thing: PID_MAX, NO_PID defined only in kernel + */ +#define NO_PID 100000 + +enum SWRunType { + SRT_UNKNOWN = 1, + SRT_OPERATING_SYSTEM = 2, + SRT_DEVICE_DRIVER = 3, + SRT_APPLICATION = 4 + +}; + +enum SWRunStatus { + SRS_RUNNING = 1, + SRS_RUNNABLE = 2, + SRS_NOT_RUNNABLE = 3, + SRS_INVALID = 4 +}; + +/* Maximum lengths for the strings according to the MIB */ +#define SWR_NAME_MLEN (64 + 1) +#define SWR_PATH_MLEN (128 + 1) +#define SWR_PARAM_MLEN (128 + 1) + +/* + * This structure is used to hold a SNMP table entry + * for both hrSWRunTable and hrSWRunPerfTable because + * hrSWRunPerfTable AUGMENTS hrSWRunTable + */ +struct swrun_entry { + int32_t index; + u_char *name; /* it may be NULL */ + const struct asn_oid *id; + u_char *path; /* it may be NULL */ + u_char *parameters; /* it may be NULL */ + int32_t type; /* enum SWRunType */ + int32_t status; /* enum SWRunStatus */ + int32_t perfCPU; + int32_t perfMemory; +#define HR_SWRUN_FOUND 0x001 + uint32_t flags; + uint64_t r_tick; /* tick when entry refreshed */ + TAILQ_ENTRY(swrun_entry) link; +}; +TAILQ_HEAD(swrun_tbl, swrun_entry); + +/* the head of the list with hrSWRunTable's entries */ +static struct swrun_tbl swrun_tbl = TAILQ_HEAD_INITIALIZER(swrun_tbl); + +/* last (agent) tick when hrSWRunTable and hrSWRunPerTable was updated */ +static uint64_t swrun_tick; + +/* maximum number of ticks between updates of SWRun and SWRunPerf table */ +uint32_t swrun_tbl_refresh = HR_SWRUN_TBL_REFRESH * 100; + +/* the value of the MIB object with the same name */ +static int32_t SWOSIndex; + +/** + * Malloc a new entry and add it to the list + * associated to this table. The item identified by + * the index parameter must not exist in this list. + */ +static struct swrun_entry * +swrun_entry_create(int32_t idx) +{ + struct swrun_entry *entry; + + if ((entry = malloc(sizeof(*entry))) == NULL) { + syslog(LOG_WARNING, "%s: %m", __func__); + return (NULL); + } + memset(entry, 0, sizeof(*entry)); + entry->index = idx; + + INSERT_OBJECT_INT(entry, &swrun_tbl); + return (entry); +} + +/** + * Unlink the entry from the list and then free its heap memory + */ +static void +swrun_entry_delete(struct swrun_entry *entry) +{ + + assert(entry != NULL); + + TAILQ_REMOVE(&swrun_tbl, entry, link); + + free(entry->name); + free(entry->path); + free(entry->parameters); + free(entry); +} + +/** + * Search one item by its index, return NULL if none found + */ +static struct swrun_entry * +swrun_entry_find_by_index(int32_t idx) +{ + struct swrun_entry *entry; + + TAILQ_FOREACH(entry, &swrun_tbl, link) + if (entry->index == idx) + return (entry); + return (NULL); +} + +/** + * Translate the kernel's process status to SNMP. + */ +static enum SWRunStatus +swrun_OS_get_proc_status(const struct kinfo_proc *kp) +{ + + assert(kp != NULL); + if(kp == NULL) { + return (SRS_INVALID); + } + + /* + * I'm using the old style flags - they look cleaner to me, + * at least for the purpose of this SNMP table + */ + switch (kp->ki_stat) { + + case SSTOP: + return (SRS_NOT_RUNNABLE); + + case SWAIT: + case SLOCK: + case SSLEEP: + return (SRS_RUNNABLE); + + case SZOMB: + return (SRS_INVALID); + + case SIDL: + case SRUN: + return (SRS_RUNNING); + + default: + syslog(LOG_ERR,"Unknown process state: %d", kp->ki_stat); + return (SRS_INVALID); + } +} + +/** + * Make an SNMP table entry from a kernel one. + */ +static void +kinfo_proc_to_swrun_entry(const struct kinfo_proc *kp, + struct swrun_entry *entry) +{ + char **argv = NULL; + uint64_t cpu_time = 0; + size_t pname_len; + + pname_len = strlen(kp->ki_comm) + 1; + entry->name = reallocf(entry->name, pname_len); + if (entry->name != NULL) + strlcpy(entry->name, kp->ki_comm, pname_len); + + entry->id = &oid_zeroDotZero; /* unknown id - FIXME */ + + assert(hr_kd != NULL); + + argv = kvm_getargv(hr_kd, kp, SWR_PARAM_MLEN - 1); + if(argv != NULL){ + u_char param[SWR_PARAM_MLEN]; + + memset(param, '\0', sizeof(param)); + + /* + * FIXME + * Path seems to not be available. + * Try to hack the info in argv[0]; + * this argv is under control of the program so this info + * is not realiable + */ + if(*argv != NULL && (*argv)[0] == '/') { + size_t path_len; + + path_len = strlen(*argv) + 1; + if (path_len > SWR_PATH_MLEN) + path_len = SWR_PATH_MLEN; + + entry->path = reallocf(entry->path, path_len); + if (entry->path != NULL) { + memset(entry->path, '\0', path_len); + strlcpy((char*)entry->path, *argv, path_len); + } + } + + argv++; /* skip the first one which was used for path */ + + while (argv != NULL && *argv != NULL ) { + if (param[0] != 0) { + /* + * add a space between parameters, + * except before the first one + */ + strlcat((char *)param, " ", sizeof(param)); + } + strlcat((char *)param, *argv, sizeof(param)); + argv++; + } + /* reuse pname_len */ + pname_len = strlen(param) + 1; + if (pname_len > SWR_PARAM_MLEN) + pname_len = SWR_PARAM_MLEN; + + entry->parameters = reallocf(entry->parameters, pname_len); + strlcpy(entry->parameters, param, pname_len); + } + + entry->type = (int32_t)(IS_KERNPROC(kp) ? SRT_OPERATING_SYSTEM : + SRT_APPLICATION); + + entry->status = (int32_t)swrun_OS_get_proc_status(kp); + cpu_time = kp->ki_runtime / 100000; /* centi-seconds */ + + /* may overflow the snmp type */ + entry->perfCPU = (cpu_time > (uint64_t)INT_MAX ? INT_MAX : cpu_time); + entry->perfMemory = kp->ki_size / 1024; /* in kilo-bytes */ + entry->r_tick = get_ticks(); +} + +/** + * Create a table entry for a KLD + */ +static void +kld_file_stat_to_swrun(const struct kld_file_stat *kfs, + struct swrun_entry *entry) +{ + size_t name_len; + + assert(kfs != NULL); + assert(entry != NULL); + + name_len = strlen(kfs->name) + 1; + if (name_len > SWR_NAME_MLEN) + name_len = SWR_NAME_MLEN; + + entry->name = reallocf(entry->name, name_len); + if (entry->name != NULL) + strlcpy((char *)entry->name, kfs->name, name_len); + + /* FIXME: can we find the location where the module was loaded from? */ + entry->path = NULL; + + /* no parameters for kernel files (.ko) of for the kernel */ + entry->parameters = NULL; + + entry->id = &oid_zeroDotZero; /* unknown id - FIXME */ + + if (strcmp(kfs->name, "kernel") == 0) { + entry->type = (int32_t)SRT_OPERATING_SYSTEM; + SWOSIndex = entry->index; + } else { + entry->type = (int32_t)SRT_DEVICE_DRIVER; /* well, not really */ + } + entry->status = (int32_t)SRS_RUNNING; + entry->perfCPU = 0; /* Info not available */ + entry->perfMemory = kfs->size / 1024; /* in kilo-bytes */ + entry->r_tick = get_ticks(); +} + +/** + * Get all visible proceses including the kernel visible threads + */ +static void +swrun_OS_get_procs(void) +{ + struct kinfo_proc *plist, *kp; + int i; + int nproc; + struct swrun_entry *entry; + + plist = kvm_getprocs(hr_kd, KERN_PROC_ALL, 0, &nproc); + if (plist == NULL || nproc < 0) { + syslog(LOG_ERR, "kvm_getprocs() failed: %m"); + return; + } + for (i = 0, kp = plist; i < nproc; i++, kp++) { + /* + * The SNMP table's index must begin from 1 (as specified by + * this table definition), the PIDs are starting from 0 + * so we are translating the PIDs to +1 + */ + entry = swrun_entry_find_by_index((int32_t)kp->ki_pid + 1); + if (entry == NULL) { + /* new entry - get memory for it */ + entry = swrun_entry_create((int32_t)kp->ki_pid + 1); + if (entry == NULL) + continue; + } + entry->flags |= HR_SWRUN_FOUND; /* mark it as found */ + + kinfo_proc_to_swrun_entry(kp, entry); + } +} + +/* + * Get kernel items: first the kernel itself, then the loaded modules. + */ +static void +swrun_OS_get_kinfo(void) +{ + int fileid; + struct swrun_entry *entry; + struct kld_file_stat stat; + + for (fileid = kldnext(0); fileid > 0; fileid = kldnext(fileid)) { + stat.version = sizeof(struct kld_file_stat); + if (kldstat(fileid, &stat) < 0) { + syslog(LOG_ERR, "kldstat() failed: %m"); + continue; + } + + /* + * kernel and kernel files (*.ko) will be indexed starting with + * NO_PID + 1; NO_PID is PID_MAX + 1 thus it will be no risk to + * overlap with real PIDs which are in range of 1 .. NO_PID + */ + entry = swrun_entry_find_by_index(NO_PID + 1 + stat.id); + if (entry == NULL) { + /* new entry - get memory for it */ + entry = swrun_entry_create(NO_PID + 1 + stat.id); + if (entry == NULL) + continue; + } + entry->flags |= HR_SWRUN_FOUND; /* mark it as found */ + + kld_file_stat_to_swrun(&stat, entry); + } +} + +/** + * Refresh the hrSWRun and hrSWRunPert tables. + */ +static void +refresh_swrun_tbl(void) +{ + + struct swrun_entry *entry, *entry_tmp; + + if (this_tick - swrun_tick < swrun_tbl_refresh) { + HRDBG("no refresh needed "); + return; + } + + /* mark each entry as missing */ + TAILQ_FOREACH(entry, &swrun_tbl, link) + entry->flags &= ~HR_SWRUN_FOUND; + + swrun_OS_get_procs(); + swrun_OS_get_kinfo(); + + /* + * Purge items that disappeared + */ + TAILQ_FOREACH_SAFE(entry, &swrun_tbl, link, entry_tmp) + if (!(entry->flags & HR_SWRUN_FOUND)) + swrun_entry_delete(entry); + + swrun_tick = this_tick; + + HRDBG("refresh DONE"); +} + +/** + * Update the information in this entry + */ +static void +fetch_swrun_entry(struct swrun_entry *entry) +{ + struct kinfo_proc *plist; + int nproc; + struct kld_file_stat stat; + + assert(entry != NULL); + + if (entry->index >= NO_PID + 1) { + /* + * kernel and kernel files (*.ko) will be indexed + * starting with NO_PID + 1; NO_PID is PID_MAX + 1 + * thus it will be no risk to overlap with real PIDs + * which are in range of 1 .. NO_PID + */ + stat.version = sizeof(stat); + if (kldstat(entry->index - NO_PID - 1, &stat) == -1) { + /* + * not found, it's gone. Mark it as invalid for now, it + * will be removed from the list at next global refersh + */ + HRDBG("missing item with kid=%d", + entry->index - NO_PID - 1); + entry->status = (int32_t)SRS_INVALID; + } else + kld_file_stat_to_swrun(&stat, entry); + + } else { + /* this is a process */ + assert(hr_kd != NULL); + plist = kvm_getprocs(hr_kd, KERN_PROC_PID, + entry->index - 1, &nproc); + if (plist == NULL || nproc != 1) { + HRDBG("missing item with PID=%d", entry->index - 1); + entry->status = (int32_t)SRS_INVALID; + } else + kinfo_proc_to_swrun_entry(plist, entry); + } +} + +/** + * Invalidate entry. For KLDs we try to unload it, for processes we SIGKILL it. + */ +static int +invalidate_swrun_entry(struct swrun_entry *entry, int commit) +{ + struct kinfo_proc *plist; + int nproc; + struct kld_file_stat stat; + + assert(entry != NULL); + + if (entry->index >= NO_PID + 1) { + /* this is a kernel item */ + HRDBG("atempt to unload KLD %d", + entry->index - NO_PID - 1); + + if (entry->index == SWOSIndex) { + /* can't invalidate the kernel itself */ + return (SNMP_ERR_NOT_WRITEABLE); + } + + stat.version = sizeof(stat); + if (kldstat(entry->index - NO_PID - 1, &stat) == -1) { + /* + * not found, it's gone. Mark it as invalid for now, it + * will be removed from the list at next global + * refresh + */ + HRDBG("missing item with kid=%d", + entry->index - NO_PID - 1); + entry->status = (int32_t)SRS_INVALID; + return (SNMP_ERR_NOERROR); + } + /* + * There is no way to try to unload a module. There seems + * also no way to find out whether it is busy without unloading + * it. We can assume that it is busy, if the reference count + * is larger than 2, but if it is 1 nothing helps. + */ + if (!commit) { + if (stat.refs > 1) + return (SNMP_ERR_NOT_WRITEABLE); + return (SNMP_ERR_NOERROR); + } + if (kldunload(stat.id) == -1) { + syslog(LOG_ERR,"kldunload for %d/%s failed: %m", + stat.id, stat.name); + if (errno == EBUSY) + return (SNMP_ERR_NOT_WRITEABLE); + else + return (SNMP_ERR_RES_UNAVAIL); + } + } else { + /* this is a process */ + assert(hr_kd != NULL); + + plist = kvm_getprocs(hr_kd, KERN_PROC_PID, + entry->index - 1, &nproc); + if (plist == NULL || nproc != 1) { + HRDBG("missing item with PID=%d", entry->index - 1); + entry->status = (int32_t)SRS_INVALID; + return (SNMP_ERR_NOERROR); + } + if (IS_KERNPROC(plist)) { + /* you don't want to do this */ + return (SNMP_ERR_NOT_WRITEABLE); + } + if (kill(entry->index - 1, commit ? SIGKILL : 0) < 0) { + syslog(LOG_ERR,"kill (%d, SIGKILL) failed: %m", + entry->index - 1); + if (errno == ESRCH) { + /* race: just gone */ + entry->status = (int32_t)SRS_INVALID; + return (SNMP_ERR_NOERROR); + } + return (SNMP_ERR_GENERR); + } + } + return (SNMP_ERR_NOERROR); +} + +/** + * Popuplate the hrSWRunTable. + */ +void +init_swrun_tbl(void) +{ + + refresh_swrun_tbl(); + HRDBG("done"); +} + +/** + * Finalize the hrSWRunTable. + */ +void +fini_swrun_tbl(void) +{ + struct swrun_entry *n1; + + while ((n1 = TAILQ_FIRST(&swrun_tbl)) != NULL) { + TAILQ_REMOVE(&swrun_tbl, n1, link); + free(n1); + } +} + +/* + * This is the implementation for a generated (by a SNMP tool) + * function prototype, see hostres_tree.h + * It hanldes the SNMP operations for hrSWRunTable + */ +int +op_hrSWRunTable(struct snmp_context *ctx __unused, struct snmp_value *value, + u_int sub, u_int iidx __unused, enum snmp_op curr_op) +{ + struct swrun_entry *entry; + int ret; + + refresh_swrun_tbl(); + + switch (curr_op) { + + case SNMP_OP_GETNEXT: + if ((entry = NEXT_OBJECT_INT(&swrun_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(&swrun_tbl, + &value->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + goto get; + + case SNMP_OP_SET: + if ((entry = FIND_OBJECT_INT(&swrun_tbl, + &value->var, sub)) == NULL) + return (SNMP_ERR_NO_CREATION); + + if (entry->r_tick < this_tick) + fetch_swrun_entry(entry); + + switch (value->var.subs[sub - 1]) { + + case LEAF_hrSWRunStatus: + if (value->v.integer != (int32_t)SRS_INVALID) + return (SNMP_ERR_WRONG_VALUE); + + if (entry->status == (int32_t)SRS_INVALID) + return (SNMP_ERR_NOERROR); + + /* + * Here we have a problem with the entire SNMP + * model: if we kill now, we cannot rollback. + * If we kill in the commit code, we cannot + * return an error. Because things may change between + * SET and COMMIT this is impossible to handle + * correctly. + */ + return (invalidate_swrun_entry(entry, 0)); + } + return (SNMP_ERR_NOT_WRITEABLE); + + case SNMP_OP_ROLLBACK: + return (SNMP_ERR_NOERROR); + + case SNMP_OP_COMMIT: + if ((entry = FIND_OBJECT_INT(&swrun_tbl, + &value->var, sub)) == NULL) + return (SNMP_ERR_NOERROR); + + switch (value->var.subs[sub - 1]) { + + case LEAF_hrSWRunStatus: + if (value->v.integer == (int32_t)SRS_INVALID && + entry->status != (int32_t)SRS_INVALID) + (void)invalidate_swrun_entry(entry, 1); + return (SNMP_ERR_NOERROR); + } + abort(); + } + abort(); + + get: + ret = SNMP_ERR_NOERROR; + switch (value->var.subs[sub - 1]) { + + case LEAF_hrSWRunIndex: + value->v.integer = entry->index; + break; + + case LEAF_hrSWRunName: + if (entry->name != NULL) + ret = string_get(value, entry->name, -1); + else + ret = string_get(value, "", -1); + break; + + case LEAF_hrSWRunID: + assert(entry->id != NULL); + value->v.oid = *entry->id; + break; + + case LEAF_hrSWRunPath: + if (entry->path != NULL) + ret = string_get(value, entry->path, -1); + else + ret = string_get(value, "", -1); + break; + + case LEAF_hrSWRunParameters: + if (entry->parameters != NULL) + ret = string_get(value, entry->parameters, -1); + else + ret = string_get(value, "", -1); + break; + + case LEAF_hrSWRunType: + value->v.integer = entry->type; + break; + + case LEAF_hrSWRunStatus: + value->v.integer = entry->status; + break; + + default: + abort(); + } + return (ret); +} + +/** + * Scalar(s) in the SWRun group + */ +int +op_hrSWRun(struct snmp_context *ctx __unused, struct snmp_value *value, + u_int sub, u_int iidx __unused, enum snmp_op curr_op) +{ + + /* only SNMP GET is possible */ + switch (curr_op) { + + case SNMP_OP_GET: + goto get; + + case SNMP_OP_SET: + return (SNMP_ERR_NOT_WRITEABLE); + + case SNMP_OP_ROLLBACK: + case SNMP_OP_COMMIT: + case SNMP_OP_GETNEXT: + abort(); + } + abort(); + + get: + switch (value->var.subs[sub - 1]) { + + case LEAF_hrSWOSIndex: + value->v.uint32 = SWOSIndex; + return (SNMP_ERR_NOERROR); + + default: + abort(); + } +} + +/* + * This is the implementation for a generated (by a SNMP tool) + * function prototype, see hostres_tree.h + * It handles the SNMP operations for hrSWRunPerfTable + */ +int +op_hrSWRunPerfTable(struct snmp_context *ctx __unused, + struct snmp_value *value, u_int sub, u_int iidx __unused, + enum snmp_op curr_op ) +{ + struct swrun_entry *entry; + + refresh_swrun_tbl(); + + switch (curr_op) { + + case SNMP_OP_GETNEXT: + if ((entry = NEXT_OBJECT_INT(&swrun_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(&swrun_tbl, + &value->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + goto get; + + case SNMP_OP_SET: + if ((entry = FIND_OBJECT_INT(&swrun_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_hrSWRunPerfCPU: + value->v.integer = entry->perfCPU; + return (SNMP_ERR_NOERROR); + + case LEAF_hrSWRunPerfMem: + value->v.integer = entry->perfMemory; + return (SNMP_ERR_NOERROR); + } + abort(); +} diff --git a/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_tree.def b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_tree.def new file mode 100644 index 0000000..83c187b --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_tree.def @@ -0,0 +1,292 @@ +# +# Copyright (c) 2005-2006 The FreeBSD Project +# All rights reserved. +# +# Author: Victor Cruceru <soc-victor@freebsd.org> +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# +# This is the .def file for both HOST-RESOURCES-MIB and HOST-RESOURCES-TYPES +# + +(1 internet + (2 mgmt + (1 mib_2 + (25 host + (1 hrSystem + (1 hrSystemUptime TIMETICKS op_hrSystem GET) + (2 hrSystemDate OCTETSTRING op_hrSystem GET SET) + (3 hrSystemInitialLoadDevice INTEGER op_hrSystem GET SET) + (4 hrSystemInitialLoadParameters OCTETSTRING op_hrSystem GET SET) + (5 hrSystemNumUsers GAUGE op_hrSystem GET) + (6 hrSystemProcesses GAUGE op_hrSystem GET) + (7 hrSystemMaxProcesses INTEGER op_hrSystem GET) + ) + (2 hrStorage + (1 hrStorageTypes + (1 hrStorageOther + ) + (2 hrStorageRam + ) + (3 hrStorageVirtualMemory + ) + (4 hrStorageFixedDisk + ) + (5 hrStorageRemovableDisk + ) + (6 hrStorageFloppyDisk + ) + (7 hrStorageCompactDisc + ) + (8 hrStorageRamDisk + ) + (9 hrStorageFlashMemory + ) + (10 hrStorageNetworkDisk + ) + ) + (2 hrMemorySize INTEGER op_hrStorage GET) + (3 hrStorageTable + (1 hrStorageEntry : INTEGER op_hrStorageTable + (1 hrStorageIndex INTEGER GET) + (2 hrStorageType OID GET) + (3 hrStorageDescr OCTETSTRING GET) + (4 hrStorageAllocationUnits INTEGER GET) + (5 hrStorageSize INTEGER GET SET) + (6 hrStorageUsed INTEGER GET) + (7 hrStorageAllocationFailures COUNTER GET) + ) + ) + ) + (3 hrDevice + (1 hrDeviceTypes + (1 hrDeviceOther + ) + (2 hrDeviceUnknown + ) + (3 hrDeviceProcessor + ) + (4 hrDeviceNetwork + ) + (5 hrDevicePrinter + ) + (6 hrDeviceDiskStorage + ) + (10 hrDeviceVideo + ) + (11 hrDeviceAudio + ) + (12 hrDeviceCoprocessor + ) + (13 hrDeviceKeyboard + ) + (14 hrDeviceModem + ) + (15 hrDeviceParallelPort + ) + (16 hrDevicePointing + ) + (17 hrDeviceSerialPort + ) + (18 hrDeviceTape + ) + (19 hrDeviceClock + ) + (20 hrDeviceVolatileMemory + ) + (21 hrDeviceNonVolatileMemory + ) + ) + (2 hrDeviceTable + (1 hrDeviceEntry : INTEGER op_hrDeviceTable + (1 hrDeviceIndex INTEGER GET) + (2 hrDeviceType OID GET) + (3 hrDeviceDescr OCTETSTRING GET) + (4 hrDeviceID OID GET) + (5 hrDeviceStatus INTEGER GET) + (6 hrDeviceErrors COUNTER GET) + ) + ) + (3 hrProcessorTable + (1 hrProcessorEntry : INTEGER op_hrProcessorTable + (1 hrProcessorFrwID OID GET) + (2 hrProcessorLoad INTEGER GET) + ) + ) + (4 hrNetworkTable + (1 hrNetworkEntry : INTEGER op_hrNetworkTable + (1 hrNetworkIfIndex INTEGER GET) + ) + ) + (5 hrPrinterTable + (1 hrPrinterEntry : INTEGER op_hrPrinterTable + (1 hrPrinterStatus INTEGER GET) + (2 hrPrinterDetectedErrorState OCTETSTRING GET) + ) + ) + (6 hrDiskStorageTable + (1 hrDiskStorageEntry : INTEGER op_hrDiskStorageTable + (1 hrDiskStorageAccess INTEGER GET) + (2 hrDiskStorageMedia INTEGER GET) + (3 hrDiskStorageRemovable INTEGER GET) + (4 hrDiskStorageCapacity INTEGER GET) + ) + ) + (7 hrPartitionTable + (1 hrPartitionEntry : INTEGER INTEGER op_hrPartitionTable + (1 hrPartitionIndex INTEGER GET) + (2 hrPartitionLabel OCTETSTRING GET) + (3 hrPartitionID OCTETSTRING GET) + (4 hrPartitionSize INTEGER GET) + (5 hrPartitionFSIndex INTEGER GET) + ) + ) + (8 hrFSTable + (1 hrFSEntry : INTEGER op_hrFSTable + (1 hrFSIndex INTEGER GET) + (2 hrFSMountPoint OCTETSTRING GET) + (3 hrFSRemoteMountPoint OCTETSTRING GET) + (4 hrFSType OID GET) + (5 hrFSAccess INTEGER GET) + (6 hrFSBootable INTEGER GET) + (7 hrFSStorageIndex INTEGER GET) + (8 hrFSLastFullBackupDate OCTETSTRING GET SET) + (9 hrFSLastPartialBackupDate OCTETSTRING GET SET) + ) + ) + (9 hrFSTypes + (1 hrFSOther + ) + (2 hrFSUnknown + ) + (3 hrFSBerkeleyFFS + ) + (4 hrFSSys5FS + ) + (5 hrFSFat + ) + (6 hrFSHPFS + ) + (7 hrFSHFS + ) + (8 hrFSMFS + ) + (9 hrFSNTFS + ) + (10 hrFSVNode + ) + (11 hrFSJournaled + ) + (12 hrFSiso9660 + ) + (13 hrFSRockRidge + ) + (14 hrFSNFS + ) + (15 hrFSNetware + ) + (16 hrFSAFS + ) + (17 hrFSDFS + ) + (18 hrFSAppleshare + ) + (19 hrFSRFS + ) + (20 hrFSDGCFS + ) + (21 hrFSBFS + ) + (22 hrFSFAT32 + ) + (23 hrFSLinuxExt2 + ) + ) + ) + (4 hrSWRun + (1 hrSWOSIndex INTEGER op_hrSWRun GET) + (2 hrSWRunTable + (1 hrSWRunEntry : INTEGER op_hrSWRunTable + (1 hrSWRunIndex INTEGER GET) + (2 hrSWRunName OCTETSTRING GET) + (3 hrSWRunID OID GET) + (4 hrSWRunPath OCTETSTRING GET) + (5 hrSWRunParameters OCTETSTRING GET) + (6 hrSWRunType INTEGER GET) + (7 hrSWRunStatus INTEGER GET SET) + ) + ) + ) + (5 hrSWRunPerf + (1 hrSWRunPerfTable + (1 hrSWRunPerfEntry : INTEGER op_hrSWRunPerfTable + (1 hrSWRunPerfCPU INTEGER GET) + (2 hrSWRunPerfMem INTEGER GET) + ) + ) + ) + (6 hrSWInstalled + (1 hrSWInstalledLastChange TIMETICKS op_hrSWInstalled GET) + (2 hrSWInstalledLastUpdateTime TIMETICKS op_hrSWInstalled GET) + (3 hrSWInstalledTable + (1 hrSWInstalledEntry : INTEGER op_hrSWInstalledTable + (1 hrSWInstalledIndex INTEGER GET) + (2 hrSWInstalledName OCTETSTRING GET) + (3 hrSWInstalledID OID GET) + (4 hrSWInstalledType INTEGER GET) + (5 hrSWInstalledDate OCTETSTRING GET) + ) + ) + ) + (7 hrMIBAdminInfo + (1 hostResourcesMibModule + ) + (2 hrMIBCompliances + ) + (3 hrMIBGroups + ) + ) + ) + ) + ) + (4 private + (1 enterprises + (12325 fokus + (1 begemot + (202 begemotHostres + (1 begemotHostresObjects + (1 begemotHrStorageUpdate TIMETICKS op_begemot GET SET) + (2 begemotHrFSUpdate TIMETICKS op_begemot GET SET) + (3 begemotHrDiskStorageUpdate TIMETICKS op_begemot GET SET) + (4 begemotHrNetworkUpdate TIMETICKS op_begemot GET SET) + (5 begemotHrSWInstalledUpdate TIMETICKS op_begemot GET SET) + (6 begemotHrSWRunUpdate TIMETICKS op_begemot GET SET) + (7 begemotHrPkgDir OCTETSTRING op_begemot GET SET) + ) + ) + ) + ) + ) + ) +) diff --git a/usr.sbin/bsnmpd/modules/snmp_hostres/snmp_hostres.3 b/usr.sbin/bsnmpd/modules/snmp_hostres/snmp_hostres.3 new file mode 100644 index 0000000..774c027 --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_hostres/snmp_hostres.3 @@ -0,0 +1,88 @@ +.\" +.\" Copyright (C) 2005-2006 +.\" The FreeBSD Project. +.\" All rights reserved. +.\" +.\" Author: Harti Brandt <harti@FreeBSD.org> +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd January 3, 2006 +.Dt SNMP_HOSTRES 3 +.Os +.Sh NAME +.Nm snmp_hostres +.Nd host resources module for +.Xr bsnmpd 8 +.Sh LIBRARY +.Pq begemotSnmpdModulePath."hostres" = "/usr/lib/snmp_hostres.so" +.Sh DESCRIPTION +The +.Nm +module implements the HOST-RESOURCES-MIB as standardized in RFC 2790. +.Sh RESTRICTIONS +Not all information in the MIB is meaningful in FreeBSD or is available. +The following variables are not implemented or carry no information: +.Bl -tag -width indent +.It Va hrFSType +There are several types of file systems for which no appropriate OID +exists yet which are supported by +.Fx . +For smbfs, procfs and devfs , +.Va hrFSOther +is returned. +In all other cases, +.Va hrFSUnknown . +.It Va hrFSBootable +It is questionable what bootable means here. +Does it mean that the BIOS is available to start a boot on that file system +or does it mean that there is something bootable? +In either case this information is not available so this variable returns True +for the root file system (which is not necessarily correct) and False for +all others. +.It Va hrFSLastFullBackupDate , hrFSLastPartialBackupDate +This is not available and always returns an empty string. +Theoretically, this could be retrieved from +.Pa /etc/dumpdates , +which would +hardly be correct given the different ways of doing backups. +.It Va hrDiskStorageTable +Floppy devices are currently not reported. +Also the names of the disks are hard-coded in the module. +.El +.Sh FILES +.Bl -tag -width indent +.It Pa /usr/share/snmp/defs/hostres_tree.def +The description of the MIB tree implemented by +.Nm . +.It Pa /usr/share/snmp/mibs/HOST-RESOURCES-TYPES.txt +.It Pa /usr/share/snmp/mibs/HOST-RESOURCES-MIB.txt +.It Pa /usr/share/snmp/mibs/BEGEMOT-HOSTRES-MIB.txt +This is the MIB that is implemented by this module. +.El +.Sh SEE ALSO +.Xr gensnmptree 1 , +.Xr snmpmod 3 +.Sh AUTHORS +.An Victor Cruceru Aq soc-victor@FreeBSD.org diff --git a/usr.sbin/bsnmpd/modules/snmp_mibII/Makefile b/usr.sbin/bsnmpd/modules/snmp_mibII/Makefile new file mode 100644 index 0000000..32bca7d --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_mibII/Makefile @@ -0,0 +1,29 @@ +# $FreeBSD$ +# +# Author: Harti Brandt <harti@freebsd.org> + +CONTRIB= ${.CURDIR}/../../../../contrib/bsnmp +.PATH: ${CONTRIB}/snmp_mibII + +MOD= mibII +SRCS= mibII.c mibII_begemot.c mibII_ifmib.c mibII_ifstack.c \ + mibII_interfaces.c mibII_ip.c mibII_ipaddr.c mibII_nettomedia.c \ + mibII_rcvaddr.c mibII_route.c mibII_tcp.c mibII_udp.c +XSYM= ipAddrTable ifTable ifRcvAddressEntry ifMIB ipMIB tcpMIB udpMIB \ + ipForward ifIndex linkDown linkUp +MAN= snmp_mibII.3 + +CFLAGS+= -I${CONTRIB}/lib -I${CONTRIB}/snmpd +CFLAGS+= -DHAVE_ERR_H -DHAVE_GETADDRINFO -DHAVE_STRLCPY -DHAVE_SYS_TREE_H +# XXX Work around clang warning, until maintainer approves fix. +NO_WERROR.clang= + +DEFS= ${MOD}_tree.def +INCS= snmp_${MOD}.h +BMIBS= BEGEMOT-IP-MIB.txt BEGEMOT-MIB2-MIB.txt + +.include <bsd.snmpmod.mk> + +smilint: + env SMIPATH=/usr/share/snmp/mibs:/usr/local/share/snmp/mibs \ + smilint -c /dev/null -l6 -i group-membership ${BMIBS:C/^/${CONTRIB}\/snmp_mibII\//} diff --git a/usr.sbin/bsnmpd/modules/snmp_netgraph/BEGEMOT-NETGRAPH.txt b/usr.sbin/bsnmpd/modules/snmp_netgraph/BEGEMOT-NETGRAPH.txt new file mode 100644 index 0000000..1896fe6 --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_netgraph/BEGEMOT-NETGRAPH.txt @@ -0,0 +1,398 @@ +-- +-- Copyright (c) 2001-2003 +-- Fraunhofer Institute for Open Communication Systems (FhG Fokus). +-- All rights reserved. +-- +-- Author: Harti Brandt <harti@freebsd.org> +-- +-- Redistribution of this software and documentation and use in source and +-- binary forms, with or without modification, are permitted provided that +-- the following conditions are met: +-- +-- 1. Redistributions of source code or documentation must retain the above +-- copyright notice, this list of conditions and the following disclaimer. +-- 2. Redistributions in binary form must reproduce the above copyright +-- notice, this list of conditions and the following disclaimer in the +-- documentation and/or other materials provided with the distribution. +-- +-- THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY FRAUNHOFER FOKUS +-- AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, +-- INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +-- FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +-- FRAUNHOFER FOKUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +-- INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +-- LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +-- OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +-- LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +-- NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +-- EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +-- +-- $FreeBSD$ +-- +-- Private MIB for netgraph part of Begemot SNMP daemon. +-- +BEGEMOT-NETGRAPH-MIB DEFINITIONS ::= BEGIN + +IMPORTS + MODULE-IDENTITY, OBJECT-TYPE, Counter32, Unsigned32 + FROM SNMPv2-SMI + TEXTUAL-CONVENTION, TruthValue + FROM SNMPv2-TC + MODULE-COMPLIANCE, OBJECT-GROUP + FROM SNMPv2-CONF + begemot + FROM BEGEMOT-MIB; + +begemotNg MODULE-IDENTITY + LAST-UPDATED "200311140000Z" + ORGANIZATION "Fraunhofer FOKUS, CATS" + CONTACT-INFO + " Hartmut Brandt + + Postal: Fraunhofer Institute for Open Communication Systems + Kaiserin-Augusta-Allee 31 + 10589 Berlin + Germany + + Fax: +49 30 3463 7352 + + E-mail: harti@freebsd.org" + DESCRIPTION + "The MIB for the NetGraph access module for SNMP." + ::= { begemot 2 } + +begemotNgObjects OBJECT IDENTIFIER ::= { begemotNg 1 } + +-- -------------------------------------------------------------------------- + +NgTypeName ::= TEXTUAL-CONVENTION + DISPLAY-HINT "31a" + STATUS current + DESCRIPTION + "Name of a netgraph type." + SYNTAX OCTET STRING (SIZE(1..31)) + +NgNodeName ::= TEXTUAL-CONVENTION + DISPLAY-HINT "31a" + STATUS current + DESCRIPTION + "Name of a netgraph node." + SYNTAX OCTET STRING (SIZE(1..31)) + +NgNodeNameOrEmpty ::= TEXTUAL-CONVENTION + DISPLAY-HINT "31a" + STATUS current + DESCRIPTION + "Name of a netgraph node." + SYNTAX OCTET STRING (SIZE(0..31)) + +NgHookName ::= TEXTUAL-CONVENTION + DISPLAY-HINT "31a" + STATUS current + DESCRIPTION + "Name of a netgraph hook." + SYNTAX OCTET STRING (SIZE(1..31)) + +NgNodeId ::= TEXTUAL-CONVENTION + DISPLAY-HINT "x" + STATUS current + DESCRIPTION + "Node identifier." + SYNTAX Unsigned32 (1..4294967295) + +NgNodeIdOrZero ::= TEXTUAL-CONVENTION + DISPLAY-HINT "x" + STATUS current + DESCRIPTION + "Node identifier or 0 for 'no-node'." + SYNTAX Unsigned32 (0..4294967295) + +-- -------------------------------------------------------------------------- +-- +-- Configuration parameters +-- +begemotNgConfig OBJECT IDENTIFIER ::= { begemotNgObjects 1 } + +begemotNgControlNodeName OBJECT-TYPE + SYNTAX NgNodeName + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The name of the netgraph node of this daemon. The name is + writeable during initialisation. If the name is set from + the empty string to the non-empty string, the netgraph socket + is created. Once set it cannot be changed." + ::= { begemotNgConfig 1 } + +begemotNgResBufSiz OBJECT-TYPE + SYNTAX INTEGER (1024..65536) + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The size of the receive buffers for netgraph messages." + DEFVAL { 20000 } + ::= { begemotNgConfig 2 } + +begemotNgTimeout OBJECT-TYPE + SYNTAX INTEGER (10..10000) + UNITS "milliseconds" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The maximum time to wait for a response to a netgraph message." + DEFVAL { 1000 } + ::= { begemotNgConfig 3 } + +begemotNgDebugLevel OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The netgraph library debug level. This should be set only + if the daemon is run with a terminal attached." + DEFVAL { 0 } + ::= { begemotNgConfig 4 } + +-- -------------------------------------------------------------------------- +-- +-- The STATISTICS Group +-- +begemotNgStats OBJECT IDENTIFIER ::= { begemotNgObjects 2 } + +begemotNgNoMems OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of times a memory allocation has failed for buffers + or the message queue." + ::= { begemotNgStats 1 } + +begemotNgMsgReadErrs OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of times reading a netgraph message has failed." + ::= { begemotNgStats 2 } + +begemotNgTooLargeMsgs OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of times a netgraph message was too large for + the buffer. Try increasing begemotNgResBufSiz if + this happens." + ::= { begemotNgStats 3 } + +begemotNgDataReadErrs OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of times reading a netgraph data message has failed." + ::= { begemotNgStats 4 } + +begemotNgTooLargeDatas OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of times a netgraph data message was too large. + You need to increase begemotNgResBufSiz." + ::= { begemotNgStats 5 } + +-- ----------------------------------------------------- +-- +-- The NODE table +-- +begemotNgTypeTable OBJECT-TYPE + SYNTAX SEQUENCE OF BegemotNgTypeEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A table containing information about all netgraph node types." + ::= { begemotNgObjects 3 } + +begemotNgTypeEntry OBJECT-TYPE + SYNTAX BegemotNgTypeEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Table entry that describes one netgraph node." + INDEX { begemotNgTypeName } + ::= { begemotNgTypeTable 1 } + +BegemotNgTypeEntry ::= SEQUENCE { + begemotNgTypeName NgTypeName, + begemotNgTypeStatus INTEGER +} + +begemotNgTypeName OBJECT-TYPE + SYNTAX NgTypeName + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The name of the type. Used as index." + ::= { begemotNgTypeEntry 1 } + +begemotNgTypeStatus OBJECT-TYPE + SYNTAX INTEGER { loaded(1), unloaded(2) } + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "If loaded then the node type is available. A type can be load + by setting this field to loaded. It is unload if the field is + set to unloaded. Note, that a type cannot be unloaded if it + is compiled into the kernel or has nodes of this type. The name + of the file containing the type implementation is constructed by + prepending ng_ to the type name." + ::= { begemotNgTypeEntry 2 } + +-- +-- Node table +-- +begemotNgNodeTable OBJECT-TYPE + SYNTAX SEQUENCE OF BegemotNgNodeEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A table containing information about all netgraph nodes." + ::= { begemotNgObjects 4 } + +begemotNgNodeEntry OBJECT-TYPE + SYNTAX BegemotNgNodeEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Table entry that describes one netgraph node." + INDEX { begemotNgNodeId } + ::= { begemotNgNodeTable 1 } + +BegemotNgNodeEntry ::= SEQUENCE { + begemotNgNodeId NgNodeId, + begemotNgNodeStatus INTEGER, + begemotNgNodeName NgNodeNameOrEmpty, + begemotNgNodeType NgTypeName, + begemotNgNodeHooks Unsigned32 +} + +begemotNgNodeId OBJECT-TYPE + SYNTAX NgNodeId + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The 32bit node id of this node. 0 is an illegal value." + ::= { begemotNgNodeEntry 1 } + +begemotNgNodeStatus OBJECT-TYPE + SYNTAX INTEGER { valid(1), invalid(2) } + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Indicates whether the node exists or not." + ::= { begemotNgNodeEntry 2 } + +begemotNgNodeName OBJECT-TYPE + SYNTAX NgNodeNameOrEmpty + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Name of the node (if any)." + ::= { begemotNgNodeEntry 3 } + +begemotNgNodeType OBJECT-TYPE + SYNTAX NgTypeName + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Type name of the node." + ::= { begemotNgNodeEntry 4 } + +begemotNgNodeHooks OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of hooks on this node." + ::= { begemotNgNodeEntry 5 } + +-- +-- Hook table +-- +begemotNgHookTable OBJECT-TYPE + SYNTAX SEQUENCE OF BegemotNgHookEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A table containing information about all netgraph hooks." + ::= { begemotNgObjects 5 } + +begemotNgHookEntry OBJECT-TYPE + SYNTAX BegemotNgHookEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Table entry that describes one netgraph node." + INDEX { begemotNgHookNodeId, begemotNgHookHook } + ::= { begemotNgHookTable 1 } + +BegemotNgHookEntry ::= SEQUENCE { + begemotNgHookNodeId NgNodeId, + begemotNgHookHook NgHookName, + begemotNgHookStatus INTEGER, + begemotNgHookPeerNodeId NgNodeId, + begemotNgHookPeerHook NgHookName, + begemotNgHookPeerType NgTypeName +} + +begemotNgHookNodeId OBJECT-TYPE + SYNTAX NgNodeId + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The 32bit node id of this node." + ::= { begemotNgHookEntry 1 } + +begemotNgHookHook OBJECT-TYPE + SYNTAX NgHookName + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Name of the hook." + ::= { begemotNgHookEntry 2 } + +begemotNgHookStatus OBJECT-TYPE + SYNTAX INTEGER { valid(1), invalid(2) } + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Indicates whether the hook exists or not." + ::= { begemotNgHookEntry 3 } + +begemotNgHookPeerNodeId OBJECT-TYPE + SYNTAX NgNodeId + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The 32bit node id of the peer node of this hook." + ::= { begemotNgHookEntry 4 } + +begemotNgHookPeerHook OBJECT-TYPE + SYNTAX NgHookName + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Name of the peer hook." + ::= { begemotNgHookEntry 5 } + +begemotNgHookPeerType OBJECT-TYPE + SYNTAX NgTypeName + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Name of the peer type." + ::= { begemotNgHookEntry 6 } + +END diff --git a/usr.sbin/bsnmpd/modules/snmp_netgraph/Makefile b/usr.sbin/bsnmpd/modules/snmp_netgraph/Makefile new file mode 100644 index 0000000..7caf7e4 --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_netgraph/Makefile @@ -0,0 +1,17 @@ +# $FreeBSD$ +# +# Author: Harti Brandt <harti@freebsd.org> + +MOD= netgraph +SRCS= snmp_netgraph.c +XSYM= begemotNg +MAN= snmp_netgraph.3 + +BMIBS= BEGEMOT-NETGRAPH.txt +DEFS= ${MOD}_tree.def +INCS= snmp_${MOD}.h + +DPADD= ${LIBNETGRAPH} +LDADD= -lnetgraph + +.include <bsd.snmpmod.mk> diff --git a/usr.sbin/bsnmpd/modules/snmp_netgraph/netgraph_tree.def b/usr.sbin/bsnmpd/modules/snmp_netgraph/netgraph_tree.def new file mode 100644 index 0000000..eff59ff --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_netgraph/netgraph_tree.def @@ -0,0 +1,78 @@ +# +# Copyright (c) 2001-2003 +# Fraunhofer Institute for Open Communication Systems (FhG Fokus). +# All rights reserved. +# +# Author: Harti Brandt <harti@freebsd.org> +# +# Redistribution of this software and documentation and use in source and +# binary forms, with or without modification, are permitted provided that +# the following conditions are met: +# +# 1. Redistributions of source code or documentation must retain the above +# copyright notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY FRAUNHOFER FOKUS +# AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +# FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +# FRAUNHOFER FOKUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +# EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# $FreeBSD$ +# +# Definition of the tree implemented by snmp_netgraph. +# +(1 internet + (4 private + (1 enterprises + (12325 fokus + (1 begemot + (2 begemotNg + (1 begemotNgObjects + (1 begemotNgConfig + (1 begemotNgControlNodeName OCTETSTRING op_ng_config GET SET) + (2 begemotNgResBufSiz INTEGER op_ng_config GET SET) + (3 begemotNgTimeout INTEGER op_ng_config GET SET) + (4 begemotNgDebugLevel UNSIGNED32 op_ng_config GET SET) + ) +# Change definition of stats array if you change StatsGroup + (2 begemotNgStats + (1 begemotNgNoMems COUNTER op_ng_stats GET) + (2 begemotNgMsgReadErrs COUNTER op_ng_stats GET) + (3 begemotNgTooLargeMsgs COUNTER op_ng_stats GET) + (4 begemotNgDataReadErrs COUNTER op_ng_stats GET) + (5 begemotNgTooLargeDatas COUNTER op_ng_stats GET) + ) + (3 begemotNgTypeTable + (1 begemotNgTypeEntry : OCTETSTRING op_ng_type + (1 begemotNgTypeName OCTETSTRING) + (2 begemotNgTypeStatus INTEGER GET SET) + )) + (4 begemotNgNodeTable + (1 begemotNgNodeEntry : INTEGER op_ng_node + (1 begemotNgNodeId UNSIGNED32) + (2 begemotNgNodeStatus INTEGER GET) + (3 begemotNgNodeName OCTETSTRING GET) + (4 begemotNgNodeType OCTETSTRING GET) + (5 begemotNgNodeHooks UNSIGNED32 GET) + )) + (5 begemotNgHookTable + (1 begemotNgHookEntry : UNSIGNED32 OCTETSTRING op_ng_hook + (1 begemotNgHookNodeId UNSIGNED32) + (2 begemotNgHookHook OCTETSTRING) + (3 begemotNgHookStatus INTEGER GET) + (4 begemotNgHookPeerNodeId UNSIGNED32 GET) + (5 begemotNgHookPeerHook OCTETSTRING GET) + (6 begemotNgHookPeerType OCTETSTRING GET) + )) + )) +))))) diff --git a/usr.sbin/bsnmpd/modules/snmp_netgraph/snmp_netgraph.3 b/usr.sbin/bsnmpd/modules/snmp_netgraph/snmp_netgraph.3 new file mode 100644 index 0000000..9d9e599 --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_netgraph/snmp_netgraph.3 @@ -0,0 +1,436 @@ +.\" +.\" Copyright (c) 2001-2003 +.\" Fraunhofer Institute for Open Communication Systems (FhG Fokus). +.\" All rights reserved. +.\" +.\" Author: Harti Brandt <harti@FreeBSD.org> +.\" +.\" Redistribution of this software and documentation and use in source and +.\" binary forms, with or without modification, are permitted provided that +.\" the following conditions are met: +.\" +.\" 1. Redistributions of source code or documentation must retain the above +.\" copyright notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY FRAUNHOFER FOKUS +.\" AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, +.\" INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +.\" FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +.\" FRAUNHOFER FOKUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +.\" LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +.\" OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +.\" LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +.\" NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +.\" EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd November 14, 2003 +.Dt SNMP_NETGRAPH 3 +.Os +.Sh NAME +.Nm snmp_netgraph , +.Nm snmp_node , +.Nm snmp_nodename , +.Nm ng_cookie_f , +.Nm ng_hook_f , +.Nm ng_register_cookie , +.Nm ng_unregister_cookie , +.Nm ng_register_hook , +.Nm ng_unregister_hook , +.Nm ng_unregister_module , +.Nm ng_output , +.Nm ng_output_node , +.Nm ng_output_id , +.Nm ng_dialog , +.Nm ng_dialog_node , +.Nm ng_dialog_id , +.Nm ng_send_data , +.Nm ng_mkpeer_id , +.Nm ng_connect_node , +.Nm ng_connect_id , +.Nm ng_connect2_id , +.Nm ng_connect2_tee_id , +.Nm ng_rmhook , +.Nm ng_rmhook_id , +.Nm ng_rmhook_tee_id , +.Nm ng_shutdown_id , +.Nm ng_next_node_id , +.Nm ng_node_id , +.Nm ng_node_id_node , +.Nm ng_node_name , +.Nm ng_node_type , +.Nm ng_peer_hook_id +.Nd "netgraph module for snmpd" +.Sh LIBRARY +.Pq begemotSnmpdModulePath."netgraph" = "/usr/lib/snmp_netgraph.so" +.Sh SYNOPSIS +.In bsnmp/snmpmod.h +.In bsnmp/snmp_netgraph.h +.Vt extern ng_ID_t snmp_node ; +.Vt extern u_char *snmp_nodename ; +.Ft typedef void +.Fn ng_cookie_f "const struct ng_mesg *mesg" "const char *path" "ng_ID_t id" "void *uarg" +.Ft typedef void +.Fn ng_hook_f "const char *hook" "const u_char *mesg" "size_t len" "void *uarg" +.Ft void * +.Fn ng_register_cookie "const struct lmodule *mod" "uint32_t cookie" "ng_ID_t id" "ng_cookie_f *func" "void *uarg" +.Ft void +.Fn ng_unregister_cookie "void *reg" +.Ft void * +.Fn ng_register_hook "const struct lmodule *mod" "const char *hook" "ng_hook_f *func" "void *uarg" +.Ft void +.Fn ng_unregister_hook "void *reg" +.Ft void +.Fn ng_unregister_module "const struct lmodule *mod" +.Ft int +.Fn ng_output "const char *path" "u_int cookie" "u_int opcode" "const void *arg" "size_t arglen" +.Ft int +.Fn ng_output_node "const char *node" "u_int cookie" "u_int opcode" "const void *arg" "size_t arglen" +.Ft int +.Fn ng_output_id "ng_ID_t node" "u_int cookie" "u_int opcode" "const void *arg" "size_t arglen" +.Ft struct ng_mesg * +.Fn ng_dialog "const char *path" "u_int cookie" "u_int opcode" "const void *arg" "size_t arglen" +.Ft struct ng_mesg * +.Fn ng_dialog_node "const char *node" "u_int cookie" "u_int opcode" "const void *arg" "size_t arglen" +.Ft struct ng_mesg * +.Fn ng_dialog_id "ng_ID_t id" "u_int cookie" "u_int opcode" "const void *arg" "size_t arglen" +.Ft int +.Fn ng_send_data "const char *hook" "const void *sndbuf" "size_t sndlen" +.Ft ng_ID_t +.Fn ng_mkpeer_id "ng_ID_t id" "const char *name" "const char *type" "const char *hook" "const char *peerhook" +.Ft int +.Fn ng_connect_node "const char *node" "const char *ourhook" "const char *peerhook" +.Ft int +.Fn ng_connect_id "ng_ID_t id" "const char *ourhook" "const char *peerhook" +.Ft int +.Fn ng_connect2_id "ng_ID_t id" "ng_ID_t peer" "const char *ourhook" "const char *peerhook" +.Ft int +.Fn ng_connect2_tee_id "ng_ID_t id" "ng_ID_t peer" "const char *ourhook" "const char *peerhook" +.Ft int +.Fn ng_rmhook "const char *ourhook" +.Ft int +.Fn ng_rmhook_id "ng_ID_t id" "const char *hook" +.Ft int +.Fn ng_rmhook_tee_id "ng_ID_t id" "const char *hook" +.Ft int +.Fn ng_shutdown_id "ng_ID_t id" +.Ft ng_ID_t +.Fn ng_next_node_id "ng_ID_t node" "const char *type" "const char *hook" +.Ft ng_ID_t +.Fn ng_node_id "const char *path" +.Ft ng_ID_t +.Fn ng_node_id_node "const char *node" +.Ft ng_ID_t +.Fn ng_node_name "ng_ID_t id" "char *name" +.Ft ng_ID_t +.Fn ng_node_type "ng_ID_t id" "char *type" +.Ft int +.Fn ng_peer_hook_id "ng_ID_t id" "const char *hook" "char *peerhook" +.Sh DESCRIPTION +The +.Nm snmp_netgraph +module implements a number of tables and scalars that enable remote access to +the netgraph subsystem. +It also exports a number of global variables and +functions, that allow other modules to easily use the netgraph system. +.Pp +If upon start up of the module the variable +.Va begemotNgControlNodeName +is not empty the module opens a netgraph socket and names that socket node. +If the variable is empty, the socket is created, as soon as the variable is +written with a non-empty name. +The socket can be closed by writing an empty +string to the variable. +The socket itself and its name are available in +.Va snmp_node +and +.Va snmp_nodename . +.Ss SENDING AND RECEIVING MESSAGES AND DATA +There are three functions for sending control message: +.Bl -tag -width ".It Fn ng_output_node" +.It Fn ng_output +sends a control message along the given +.Fa path . +.It Fn ng_output_node +sends a control message to the node with name +.Fa node +and +.It Fn ng_output_id +sends a control message to the node with node id +.Fa id . +.El +.Pp +Each of these functions takes the following arguments: +.Bl -tag -width ".It Fa cookie" +.It Fa cookie +is the node specific command cookie, +.It Fa opcode +is the node specific code for the operation to perform, +.It Fa arg +is a pointer to the message itself. +This message must start with a +.Vt struct ng_mesg . +.It Fa arglen +is the overall length of the message (header plus arguments). +.El +The functions return the message id that can be used to match incoming responses +or -1 if an error occurs. +.Pp +Another class of functions is used to send a control message and to wait for +a matching response. +Note, that this operation blocks the daemon, so use it +only if you are sure that the response will happen. +There is a maximum timeout +that is configurable in the MIB variable +.Va begemotNgTimeout . +Other messages arriving while the functions are waiting for the response are +queued and delivered on the next call to the module's idle function. +.Bl -tag -width ".It Fn ng_output_node" +.It Fn ng_dialog +sends a control message along the given +.Fa path +and waits for a matching response. +.It Fn ng_dialog_node +sends a control message to the node with name +.Fa node +and waits for a matching response. +.It Fn ng_dialog_id +sends a control message to the node with id +.Fa id +and waits for a matching response. +.El +.Pp +All three functions take the same arguments as the +.Fn ng_output* +functions. +The functions return the response message in a buffer allocated by +.Xr malloc 3 +or NULL in case of an error. +The maximum size of the response buffer can be +configured in the variable +.Va begemotNgResBufSiz . +.Pp +A data message can be send with the function +.Fn ng_send_data . +This function takes the name of the +.Va snmp_node Ns 's +hook through which to send the data, a pointer to the message buffer and +the size of the message. +It returns -1 if an error happens. +.Ss ASYNCHRONOUS CONTROL AND DATA MESSAGES +A module can register functions to asynchronously receive control and data +message. +.Pp +The function +.Fn ng_register_cookie +registers a control message receive function. +If a control message is +received, that is not consumed by the dialog functions, the list of registered +control message receive functions is scanned. +If the cookie in the received +message is the same as the +.Fa cookie +argument to the +.Fn ng_register_cookie +call and the +.Fa id +argument to the +.Fn ng_register_cookie +call was either 0 or equals the node id which sent the control message, the +handler function +.Fa func +is called with a pointer to the received message, the hook on which the +message was received (or NULL if it was received not on a hook), the id +of the sender's node and the +.Fa uarg +argument of the registration call. +The handler function should not modify +the contents of the message, because more than one function may be registered +to the same cookie and node id. +.Pp +A control message registration can be undone by calling +.Fn ng_unregister_cookie +with the return value of the registration call. +If an error occurs while registering, +.Fn ng_register_cookie +returns NULL. +.Pp +A module can call +.Fn ng_register_hook +to register a callback for data messages on one of the +.Va snmp_node Ns 's +hooks. +If a data message is received on that hook, the callback function +.Fa func +is called with the hook name, a pointer to the data message, the size of +the message and the argument +.Fa uarg +to the registration function. +The message should be treated as read-only. +A data message registration can be undone by calling +.Fn ng_unregister_hook +with the return value of the registration call. +If an error occurs while registering, +.Fn ng_register_hook +returns NULL. +.Pp +The function +.Fn ng_unregister_module +removes all control and data registrations for that module. +.Ss FINDING NODES AND NODE CHARACTERISTICS +The function +.Fn ng_node_id +returns the id of the node addressed by +.Fa path +or 0 if the node does not exists. +.Pp +The function +.Fn ng_node_id_node +returns the id of the node with name +.Fa node +or 0 if the node does not exist. +.Pp +The function +.Fn ng_node_node +retrieves the name of the node with id +.Fa id +and writes it to the buffer pointed to by +.Fa name . +This buffer should be at least +.Li NG_NODESIZ +bytes long. +The function returns the node id or 0 if the +node is not found. +.Pp +The function +.Fn ng_node_type +retrieves the name of the node with id +.Fa id +and writes it to the buffer pointed to by +.Fa type . +This buffer should be at least +.Li NG_TYPESIZ +bytes long. +The function returns the node id or 0 if the +node is not found. +.Pp +The function +.Fn ng_peer_hook_id +writes the name of the peer hook of the hook +.Fa hook +on the node with +.Fa id +to the buffer pointed to by +.Fa peer_hook . +The buffer should be at least +.Li NG_HOOKSIZ +bytes long. +The function returns 0 if the node and the hook is found, -1 +otherwise. +The function skips intermediate tee nodes (see +.Xr ng_tee 4 ) . +.Pp +The function +.Fn ng_next_node_id +returns the node id of the peer node that is on the other side of hook +.Fa hook +of node +.Fa id . +If +.Fa type +is not NULL, the function checks, that the peer node's type is +.Fa type . +The function skips intermediate tee nodes (see +.Xr ng_tee 4 ) . +It returns the node id of the peer node or 0 if an error occurs or the +types do not match. +.Ss CHANGING THE GRAPH +A number of functions can be used to create or destroy nodes and hooks. +.Pp +The function +.Fn ng_mkpeer_id +creates a new node of type +.Fa type +whose hook +.Fa peerhook +will be connected to +.Fa hook +of node +.Fa id . +If +.Fa name +is not NULL the new node is named with this name. +The function returns +The node id of the new node or 0 if an error happens. +.Pp +The functions +.Fn ng_connect_node +and +.Fn ng_connect_id +make a new hook connecting +.Fa ourhook +of the modules socket node +.Va snmp_node +to +.Fa peerhook +of the node identified by id +.Fa id +or name +.Fa node . +The functions return 0 on success or -1 otherwise. +.Pp +The function +.Fn ng_connect2_id +connects hook +.Fa ourhook +of the node with id +.Fa id +to hook +.Fa peerhook +of the node with id +.Fa peer . +The functions return 0 on success or -1 otherwise. +The function +.Fn ng_connect2_tee_id +does the same as +.Fn ng_connect2_id +except, that it puts an unnamed tee node between the two nodes. +.Pp +The function +.Fn ng_rmhook +removes hook +.Fa hook +on the module's +.Va snmp_node . +The function +.Fn ng_rmhook_id +removes hook +.Fa hook +on the node with id +.Fa id . +The function +.Fn ng_rmhook_tee_id +additionally shuts down all tee nodes between the node and the first non-tee +peer. +.Pp +The function +.Fn ng_shutdown_id +destroys the given node. +.Sh FILES +.Bl -tag -width "XXXXXXXXX" +.It Pa /usr/share/bsnmp/defs/netgraph_tree.def +The description of the MIB tree implemented by +.Nm . +.It Pa /usr/share/bsnmp/mibs/BEGEMOT-NETGRAPH.txt +This is the MIB that is implemented by this module. +.El +.Sh SEE ALSO +.Xr gensnmptree 1 , +.Xr snmpmod 3 +.Sh AUTHORS +.An Hartmut Brandt Aq harti@FreeBSD.org diff --git a/usr.sbin/bsnmpd/modules/snmp_netgraph/snmp_netgraph.c b/usr.sbin/bsnmpd/modules/snmp_netgraph/snmp_netgraph.c new file mode 100644 index 0000000..d9d136a --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_netgraph/snmp_netgraph.c @@ -0,0 +1,1690 @@ +/* + * Copyright (c) 2001-2003 + * Fraunhofer Institute for Open Communication Systems (FhG Fokus). + * All rights reserved. + * + * Author: Harti Brandt <harti@freebsd.org> + * + * Redistribution of this software and documentation and use in source and + * binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code or documentation must retain the above + * copyright notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY FRAUNHOFER FOKUS + * AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * FRAUNHOFER FOKUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + * + * Netgraph interface for SNMPd. + */ +#include <sys/types.h> +#include <sys/param.h> +#include <sys/linker.h> +#include <sys/socket.h> +#include <sys/syslog.h> +#include <sys/queue.h> +#include <sys/sysctl.h> +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <unistd.h> +#include <string.h> +#include <netgraph.h> +#include <bsnmp/snmpmod.h> +#include "snmp_netgraph.h" +#include "netgraph_tree.h" +#include "netgraph_oid.h" + +/* maximum message size */ +#define RESBUFSIZ 20000 + +/* default node name */ +#define NODENAME "NgSnmpd" + +/* my node Id */ +ng_ID_t snmp_node; +u_char *snmp_nodename; + +/* the Object Resource registration index */ +static u_int reg_index; +static const struct asn_oid oid_begemotNg = OIDX_begemotNg; + +/* configuration */ +/* this must be smaller than int32_t because the functions in libnetgraph + * falsely return an int */ +static size_t resbufsiz = RESBUFSIZ; +static u_int timeout = 1000; +static u_int debug_level; + +/* number of microseconds per clock tick */ +static struct clockinfo clockinfo; + +/* Csock buffers. Communication on the csock is asynchronuous. This means + * if we wait for a specific response, we may get other messages. Put these + * into a queue and execute them when we are idle. */ +struct csock_buf { + STAILQ_ENTRY(csock_buf) link; + struct ng_mesg *mesg; + char path[NG_PATHSIZ]; +}; +static STAILQ_HEAD(, csock_buf) csock_bufs = + STAILQ_HEAD_INITIALIZER(csock_bufs); + +/* + * We dispatch unsolicieted messages by node cookies and ids. + * So we must keep a list of hook names and dispatch functions. + */ +struct msgreg { + u_int32_t cookie; + ng_ID_t id; + ng_cookie_f *func; + void *arg; + const struct lmodule *mod; + SLIST_ENTRY(msgreg) link; +}; +static SLIST_HEAD(, msgreg) msgreg_list = + SLIST_HEAD_INITIALIZER(msgreg_list); + +/* + * Data messages are dispatched by hook names. + */ +struct datareg { + char hook[NG_HOOKSIZ]; + ng_hook_f *func; + void *arg; + const struct lmodule *mod; + SLIST_ENTRY(datareg) link; +}; +static SLIST_HEAD(, datareg) datareg_list = + SLIST_HEAD_INITIALIZER(datareg_list); + +/* the netgraph sockets */ +static int csock, dsock; +static void *csock_fd, *dsock_fd; + +/* our module handle */ +static struct lmodule *module; + +/* statistics */ +static u_int32_t stats[LEAF_begemotNgTooLargeDatas+1]; + +/* netgraph type list */ +struct ngtype { + char name[NG_TYPESIZ]; + struct asn_oid index; + TAILQ_ENTRY(ngtype) link; +}; +TAILQ_HEAD(ngtype_list, ngtype); + +static struct ngtype_list ngtype_list; +static uint64_t ngtype_tick; + + +/* + * Register a function to receive unsolicited messages + */ +void * +ng_register_cookie(const struct lmodule *mod, u_int32_t cookie, ng_ID_t id, + ng_cookie_f *func, void *arg) +{ + struct msgreg *d; + + if ((d = malloc(sizeof(*d))) == NULL) + return (NULL); + + d->cookie = cookie; + d->id = id; + d->func = func; + d->arg = arg; + d->mod = mod; + + SLIST_INSERT_HEAD(&msgreg_list, d, link); + + return (d); +} + +/* + * Remove a registration. + */ +void +ng_unregister_cookie(void *dd) +{ + struct msgreg *d = dd; + + SLIST_REMOVE(&msgreg_list, d, msgreg, link); + free(d); +} + +/* + * Register a function for hook data. + */ +void * +ng_register_hook(const struct lmodule *mod, const char *hook, + ng_hook_f *func, void *arg) +{ + struct datareg *d; + + if ((d = malloc(sizeof(*d))) == NULL) + return (NULL); + + strcpy(d->hook, hook); + d->func = func; + d->arg = arg; + d->mod = mod; + + SLIST_INSERT_HEAD(&datareg_list, d, link); + + return (d); +} + +/* + * Unregister a hook function + */ +void +ng_unregister_hook(void *dd) +{ + struct datareg *d = dd; + + SLIST_REMOVE(&datareg_list, d, datareg, link); + free(d); +} + +/* + * Unregister all hooks and cookies for that module. Note: doesn't disconnect + * any hooks! + */ +void +ng_unregister_module(const struct lmodule *mod) +{ + struct msgreg *m, *m1; + struct datareg *d, *d1; + + m = SLIST_FIRST(&msgreg_list); + while (m != NULL) { + m1 = SLIST_NEXT(m, link); + if (m->mod == mod) { + SLIST_REMOVE(&msgreg_list, m, msgreg, link); + free(m); + } + m = m1; + } + + d = SLIST_FIRST(&datareg_list); + while (d != NULL) { + d1 = SLIST_NEXT(d, link); + if (d->mod == mod) { + SLIST_REMOVE(&datareg_list, d, datareg, link); + free(d); + } + d = d1; + } +} + +/* + * Dispatch a message to the correct module and delete it. More than one + * module can get a message. + */ +static void +csock_handle(struct ng_mesg *mesg, const char *path) +{ + struct msgreg *d, *d1; + u_int id; + int len; + + if (sscanf(path, "[%x]:%n", &id, &len) != 1 || + (u_int)len != strlen(path)) { + syslog(LOG_ERR, "cannot parse message path '%s'", path); + id = 0; + } + + d = SLIST_FIRST(&msgreg_list); + while (d != NULL) { + d1 = SLIST_NEXT(d, link); + if (d->cookie == mesg->header.typecookie && + (d->id == 0 || d->id == id || id == 0)) + (*d->func)(mesg, path, id, d->arg); + d = d1; + } + free(mesg); +} + +/* + * Input from the control socket. + */ +static struct ng_mesg * +csock_read(char *path) +{ + struct ng_mesg *mesg; + int ret, err; + + if ((mesg = malloc(resbufsiz + 1)) == NULL) { + stats[LEAF_begemotNgNoMems]++; + syslog(LOG_CRIT, "out of memory"); + errno = ENOMEM; + return (NULL); + } + if ((ret = NgRecvMsg(csock, mesg, resbufsiz + 1, path)) < 0) { + err = errno; + free(mesg); + if (errno == EWOULDBLOCK) { + errno = err; + return (NULL); + } + stats[LEAF_begemotNgMsgReadErrs]++; + syslog(LOG_WARNING, "read from csock: %m"); + errno = err; + return (NULL); + } + if (ret == 0) { + syslog(LOG_DEBUG, "node closed -- exiting"); + exit(0); + } + if ((size_t)ret > resbufsiz) { + stats[LEAF_begemotNgTooLargeMsgs]++; + syslog(LOG_WARNING, "ng message too large"); + free(mesg); + errno = EFBIG; + return (NULL); + } + return (mesg); +} + +static void +csock_input(int fd __unused, void *udata __unused) +{ + struct ng_mesg *mesg; + char path[NG_PATHSIZ]; + + if ((mesg = csock_read(path)) == NULL) + return; + + csock_handle(mesg, path); +} + +/* + * Write a message to a node. + */ +int +ng_output(const char *path, u_int cookie, u_int opcode, + const void *arg, size_t arglen) +{ + return (NgSendMsg(csock, path, (int)cookie, (int)opcode, arg, arglen)); +} +int +ng_output_node(const char *node, u_int cookie, u_int opcode, + const void *arg, size_t arglen) +{ + char path[NG_PATHSIZ]; + + sprintf(path, "%s:", node); + return (ng_output(path, cookie, opcode, arg, arglen)); +} +int +ng_output_id(ng_ID_t node, u_int cookie, u_int opcode, + const void *arg, size_t arglen) +{ + char path[NG_PATHSIZ]; + + sprintf(path, "[%x]:", node); + return (ng_output(path, cookie, opcode, arg, arglen)); +} + + + +/* + * Execute a synchronuous dialog with the csock. All message we receive, that + * do not match our request, are queue until the next call to the IDLE function. + */ +struct ng_mesg * +ng_dialog(const char *path, u_int cookie, u_int opcode, + const void *arg, size_t arglen) +{ + int token, err; + struct ng_mesg *mesg; + char rpath[NG_PATHSIZ]; + struct csock_buf *b; + struct timeval end, tv; + + if ((token = ng_output(path, cookie, opcode, arg, arglen)) < 0) + return (NULL); + + if (csock_fd) + fd_suspend(csock_fd); + + gettimeofday(&end, NULL); + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; + timeradd(&end, &tv, &end); + for (;;) { + mesg = NULL; + gettimeofday(&tv, NULL); + if (timercmp(&tv, &end, >=)) { + block: + syslog(LOG_WARNING, "no response for request %u/%u", + cookie, opcode); + errno = EWOULDBLOCK; + break; + } + timersub(&end, &tv, &tv); + if (tv.tv_sec == 0 && tv.tv_usec < clockinfo.tick) + goto block; + + if (setsockopt(csock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1) + syslog(LOG_WARNING, "setsockopt(SO_RCVTIMEO): %m"); + if ((mesg = csock_read(rpath)) == NULL) { + if (errno == EWOULDBLOCK) + continue; + break; + } + if (mesg->header.token == (u_int)token) + break; + if ((b = malloc(sizeof(*b))) == NULL) { + stats[LEAF_begemotNgNoMems]++; + syslog(LOG_ERR, "out of memory"); + free(mesg); + continue; + } + b->mesg = mesg; + strcpy(b->path, rpath); + STAILQ_INSERT_TAIL(&csock_bufs, b, link); + } + + tv.tv_sec = 0; + tv.tv_usec = 0; + if (setsockopt(csock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1) + syslog(LOG_WARNING, "setsockopt(SO_RCVTIMEO,0): %m"); + + if (csock_fd) { + err = errno; + fd_resume(csock_fd); + errno = err; + } + + return (mesg); +} +struct ng_mesg * +ng_dialog_node(const char *node, u_int cookie, u_int opcode, + const void *arg, size_t arglen) +{ + char path[NG_PATHSIZ]; + + sprintf(path, "%s:", node); + return (ng_dialog(path, cookie, opcode, arg, arglen)); +} +struct ng_mesg * +ng_dialog_id(ng_ID_t id, u_int cookie, u_int opcode, + const void *arg, size_t arglen) +{ + char path[NG_PATHSIZ]; + + sprintf(path, "[%x]:", id); + return (ng_dialog(path, cookie, opcode, arg, arglen)); +} + + +/* + * Send a data message to a given hook. + */ +int +ng_send_data(const char *hook, const void *sndbuf, size_t sndlen) +{ + return (NgSendData(dsock, hook, sndbuf, sndlen)); +} + +/* + * Input from a data socket. Dispatch to the function for that hook. + */ +static void +dsock_input(int fd __unused, void *udata __unused) +{ + u_char *resbuf, embuf[100]; + ssize_t len; + char hook[NG_HOOKSIZ]; + struct datareg *d, *d1; + + if ((resbuf = malloc(resbufsiz + 1)) == NULL) { + stats[LEAF_begemotNgNoMems]++; + syslog(LOG_CRIT, "out of memory"); + (void)NgRecvData(fd, embuf, sizeof(embuf), hook); + errno = ENOMEM; + return; + } + if ((len = NgRecvData(fd, resbuf, resbufsiz + 1, hook)) == -1) { + stats[LEAF_begemotNgDataReadErrs]++; + syslog(LOG_ERR, "reading message: %m"); + free(resbuf); + return; + } + if (len == 0) { + free(resbuf); + return; + } + if ((size_t)len == resbufsiz + 1) { + stats[LEAF_begemotNgTooLargeDatas]++; + syslog(LOG_WARNING, "message too long"); + free(resbuf); + return; + } + + /* + * Dispatch message. Maybe dispatched to more than one function. + */ + d = SLIST_FIRST(&datareg_list); + while (d != NULL) { + d1 = SLIST_NEXT(d, link); + if (strcmp(hook, d->hook) == 0) + (*d->func)(hook, resbuf, len, d->arg); + d = d1; + } + + free(resbuf); +} + +/* + * The SNMP daemon is about to wait for an event. Look whether we have + * netgraph messages waiting. If yes, drain the queue. + */ +static void +ng_idle(void) +{ + struct csock_buf *b; + + /* execute waiting csock_bufs */ + while ((b = STAILQ_FIRST(&csock_bufs)) != NULL) { + STAILQ_REMOVE_HEAD(&csock_bufs, link); + csock_handle(b->mesg, b->path); + free(b); + } +} + +/* + * Called when the module is loaded. Returning a non-zero value means, + * rejecting the initialisation. + * + * We make the netgraph socket. + */ +static int +ng_init(struct lmodule *mod, int argc, char *argv[]) +{ + int name[2]; + size_t len; + + module = mod; + + if (argc == 0) { + if ((snmp_nodename = malloc(strlen(NODENAME) + 1)) == NULL) + return (ENOMEM); + strcpy(snmp_nodename, NODENAME); + } else { + if ((snmp_nodename = malloc(NG_NODESIZ)) == NULL) + return (ENOMEM); + strlcpy(snmp_nodename, argv[0], NG_NODESIZ); + } + + /* fetch clockinfo (for the number of microseconds per tick) */ + name[0] = CTL_KERN; + name[1] = KERN_CLOCKRATE; + len = sizeof(clockinfo); + if (sysctl(name, 2, &clockinfo, &len, NULL, 0) == -1) + return (errno); + + TAILQ_INIT(&ngtype_list); + + return (0); +} + +/* + * Get the node Id/name/type of a node. + */ +ng_ID_t +ng_node_id(const char *path) +{ + struct ng_mesg *resp; + ng_ID_t id; + + if ((resp = ng_dialog(path, NGM_GENERIC_COOKIE, NGM_NODEINFO, + NULL, 0)) == NULL) + return (0); + id = ((struct nodeinfo *)(void *)resp->data)->id; + free(resp); + return (id); +} +ng_ID_t +ng_node_id_node(const char *node) +{ + struct ng_mesg *resp; + ng_ID_t id; + + if ((resp = ng_dialog_node(node, NGM_GENERIC_COOKIE, NGM_NODEINFO, + NULL, 0)) == NULL) + return (0); + id = ((struct nodeinfo *)(void *)resp->data)->id; + free(resp); + return (id); +} +ng_ID_t +ng_node_name(ng_ID_t id, char *name) +{ + struct ng_mesg *resp; + + if ((resp = ng_dialog_id(id, NGM_GENERIC_COOKIE, NGM_NODEINFO, + NULL, 0)) == NULL) + return (0); + strcpy(name, ((struct nodeinfo *)(void *)resp->data)->name); + free(resp); + return (id); + +} +ng_ID_t +ng_node_type(ng_ID_t id, char *type) +{ + struct ng_mesg *resp; + + if ((resp = ng_dialog_id(id, NGM_GENERIC_COOKIE, NGM_NODEINFO, + NULL, 0)) == NULL) + return (0); + strcpy(type, ((struct nodeinfo *)(void *)resp->data)->type); + free(resp); + return (id); +} + +/* + * Connect our node to some other node + */ +int +ng_connect_node(const char *node, const char *ourhook, const char *peerhook) +{ + struct ngm_connect conn; + + snprintf(conn.path, NG_PATHSIZ, "%s:", node); + strlcpy(conn.ourhook, ourhook, NG_HOOKSIZ); + strlcpy(conn.peerhook, peerhook, NG_HOOKSIZ); + return (NgSendMsg(csock, ".:", + NGM_GENERIC_COOKIE, NGM_CONNECT, &conn, sizeof(conn))); +} +int +ng_connect_id(ng_ID_t id, const char *ourhook, const char *peerhook) +{ + struct ngm_connect conn; + + snprintf(conn.path, NG_PATHSIZ, "[%x]:", id); + strlcpy(conn.ourhook, ourhook, NG_HOOKSIZ); + strlcpy(conn.peerhook, peerhook, NG_HOOKSIZ); + return (NgSendMsg(csock, ".:", + NGM_GENERIC_COOKIE, NGM_CONNECT, &conn, sizeof(conn))); +} + +int +ng_connect2_id(ng_ID_t id, ng_ID_t peer, const char *ourhook, + const char *peerhook) +{ + struct ngm_connect conn; + char path[NG_PATHSIZ]; + + snprintf(path, NG_PATHSIZ, "[%x]:", id); + + snprintf(conn.path, NG_PATHSIZ, "[%x]:", peer); + strlcpy(conn.ourhook, ourhook, NG_HOOKSIZ); + strlcpy(conn.peerhook, peerhook, NG_HOOKSIZ); + return (NgSendMsg(csock, path, + NGM_GENERIC_COOKIE, NGM_CONNECT, &conn, sizeof(conn))); +} + +int +ng_connect2_tee_id(ng_ID_t id, ng_ID_t peer, const char *ourhook, + const char *peerhook) +{ + struct ngm_connect conn; + char path[NG_PATHSIZ]; + ng_ID_t tee; + + if ((tee = ng_mkpeer_id(id, NULL, "tee", ourhook, "left")) == 0) + return (-1); + + snprintf(path, NG_PATHSIZ, "[%x]:", tee); + + snprintf(conn.path, NG_PATHSIZ, "[%x]:", peer); + strlcpy(conn.ourhook, "right", NG_HOOKSIZ); + strlcpy(conn.peerhook, peerhook, NG_HOOKSIZ); + return (NgSendMsg(csock, path, + NGM_GENERIC_COOKIE, NGM_CONNECT, &conn, sizeof(conn))); +} + +/* + * Ensure that a node of type 'type' is connected to 'hook' of 'node' + * and return its node id. tee nodes between node and the target node + * are skipped. If the type is wrong, or the hook is a dead-end return 0. + * If type is NULL, it is not checked. + */ +static ng_ID_t +ng_next_node_id_internal(ng_ID_t node, const char *type, const char *hook, + int skip_tee) +{ + struct ng_mesg *resp; + struct hooklist *hooklist; + u_int i; + + if ((resp = ng_dialog_id(node, NGM_GENERIC_COOKIE, NGM_LISTHOOKS, + NULL, 0)) == NULL) { + syslog(LOG_ERR, "get hook list: %m"); + exit(1); + } + hooklist = (struct hooklist *)(void *)resp->data; + + for (i = 0; i < hooklist->nodeinfo.hooks; i++) + if (strcmp(hooklist->link[i].ourhook, hook) == 0) + break; + + if (i == hooklist->nodeinfo.hooks) { + free(resp); + return (0); + } + + node = hooklist->link[i].nodeinfo.id; + + if (skip_tee && strcmp(hooklist->link[i].nodeinfo.type, "tee") == 0) { + if (strcmp(hooklist->link[i].peerhook, "left") == 0) + node = ng_next_node_id(node, type, "right"); + else if (strcmp(hooklist->link[i].peerhook, "right") == 0) + node = ng_next_node_id(node, type, "left"); + else if (type != NULL && + strcmp(hooklist->link[i].nodeinfo.type, type) != 0) + node = 0; + + } else if (type != NULL && + strcmp(hooklist->link[i].nodeinfo.type, type) != 0) + node = 0; + + free(resp); + + return (node); +} + +/* + * Ensure that a node of type 'type' is connected to 'hook' of 'node' + * and return its node id. tee nodes between node and the target node + * are skipped. If the type is wrong, or the hook is a dead-end return 0. + * If type is NULL, it is not checked. + */ +ng_ID_t +ng_next_node_id(ng_ID_t node, const char *type, const char *hook) +{ + return (ng_next_node_id_internal(node, type, hook, 1)); +} + +ng_ID_t +ng_mkpeer_id(ng_ID_t id, const char *nodename, const char *type, + const char *hook, const char *peerhook) +{ + char path[NG_PATHSIZ]; + struct ngm_mkpeer mkpeer; + struct ngm_name name; + + strlcpy(mkpeer.type, type, NG_TYPESIZ); + strlcpy(mkpeer.ourhook, hook, NG_HOOKSIZ); + strlcpy(mkpeer.peerhook, peerhook, NG_HOOKSIZ); + + sprintf(path, "[%x]:", id); + if (NgSendMsg(csock, path, NGM_GENERIC_COOKIE, NGM_MKPEER, + &mkpeer, sizeof(mkpeer)) == -1) + return (0); + + if ((id = ng_next_node_id_internal(id, NULL, hook, 0)) == 0) + return (0); + + if (nodename != NULL) { + strcpy(name.name, nodename); + sprintf(path, "[%x]:", id); + if (NgSendMsg(csock, path, NGM_GENERIC_COOKIE, NGM_NAME, + &name, sizeof(name)) == -1) + return (0); + } + return (id); +} + +/* + * SHutdown node + */ +int +ng_shutdown_id(ng_ID_t id) +{ + char path[NG_PATHSIZ]; + + snprintf(path, NG_PATHSIZ, "[%x]:", id); + return (NgSendMsg(csock, path, NGM_GENERIC_COOKIE, + NGM_SHUTDOWN, NULL, 0)); +} + +/* + * Disconnect one of our hooks + */ +int +ng_rmhook(const char *ourhook) +{ + struct ngm_rmhook rmhook; + + strlcpy(rmhook.ourhook, ourhook, NG_HOOKSIZ); + return (NgSendMsg(csock, ".:", + NGM_GENERIC_COOKIE, NGM_RMHOOK, &rmhook, sizeof(rmhook))); +} + +/* + * Disconnect a hook of a node + */ +int +ng_rmhook_id(ng_ID_t id, const char *hook) +{ + struct ngm_rmhook rmhook; + char path[NG_PATHSIZ]; + + strlcpy(rmhook.ourhook, hook, NG_HOOKSIZ); + snprintf(path, NG_PATHSIZ, "[%x]:", id); + return (NgSendMsg(csock, path, + NGM_GENERIC_COOKIE, NGM_RMHOOK, &rmhook, sizeof(rmhook))); +} + +/* + * Disconnect a hook and shutdown all tee nodes that were connected to that + * hook. + */ +int +ng_rmhook_tee_id(ng_ID_t node, const char *hook) +{ + struct ng_mesg *resp; + struct hooklist *hooklist; + u_int i; + int first = 1; + ng_ID_t next_node; + const char *next_hook; + + again: + /* if we have just shutdown a tee node, which had no other hooks + * connected, the node id may already be wrong here. */ + if ((resp = ng_dialog_id(node, NGM_GENERIC_COOKIE, NGM_LISTHOOKS, + NULL, 0)) == NULL) + return (0); + + hooklist = (struct hooklist *)(void *)resp->data; + + for (i = 0; i < hooklist->nodeinfo.hooks; i++) + if (strcmp(hooklist->link[i].ourhook, hook) == 0) + break; + + if (i == hooklist->nodeinfo.hooks) { + free(resp); + return (0); + } + + next_node = 0; + next_hook = NULL; + if (strcmp(hooklist->link[i].nodeinfo.type, "tee") == 0) { + if (strcmp(hooklist->link[i].peerhook, "left") == 0) { + next_node = hooklist->link[i].nodeinfo.id; + next_hook = "right"; + } else if (strcmp(hooklist->link[i].peerhook, "right") == 0) { + next_node = hooklist->link[i].nodeinfo.id; + next_hook = "left"; + } + } + free(resp); + + if (first) { + ng_rmhook_id(node, hook); + first = 0; + } else { + ng_shutdown_id(node); + } + if ((node = next_node) == 0) + return (0); + hook = next_hook; + + goto again; +} + +/* + * Get the peer hook of a hook on a given node. Skip any tee nodes in between + */ +int +ng_peer_hook_id(ng_ID_t node, const char *hook, char *peerhook) +{ + struct ng_mesg *resp; + struct hooklist *hooklist; + u_int i; + int ret; + + if ((resp = ng_dialog_id(node, NGM_GENERIC_COOKIE, NGM_LISTHOOKS, + NULL, 0)) == NULL) { + syslog(LOG_ERR, "get hook list: %m"); + exit(1); + } + hooklist = (struct hooklist *)(void *)resp->data; + + for (i = 0; i < hooklist->nodeinfo.hooks; i++) + if (strcmp(hooklist->link[i].ourhook, hook) == 0) + break; + + if (i == hooklist->nodeinfo.hooks) { + free(resp); + return (-1); + } + + node = hooklist->link[i].nodeinfo.id; + + ret = 0; + if (strcmp(hooklist->link[i].nodeinfo.type, "tee") == 0) { + if (strcmp(hooklist->link[i].peerhook, "left") == 0) + ret = ng_peer_hook_id(node, "right", peerhook); + else if (strcmp(hooklist->link[i].peerhook, "right") == 0) + ret = ng_peer_hook_id(node, "left", peerhook); + else + strcpy(peerhook, hooklist->link[i].peerhook); + + } else + strcpy(peerhook, hooklist->link[i].peerhook); + + free(resp); + + return (ret); +} + + +/* + * Now the module is started. Select on the sockets, so that we can get + * unsolicited input. + */ +static void +ng_start(void) +{ + if (snmp_node == 0) { + if (NgMkSockNode(snmp_nodename, &csock, &dsock) < 0) { + syslog(LOG_ERR, "NgMkSockNode: %m"); + exit(1); + } + snmp_node = ng_node_id(".:"); + } + + if ((csock_fd = fd_select(csock, csock_input, NULL, module)) == NULL) { + syslog(LOG_ERR, "fd_select failed on csock: %m"); + return; + } + if ((dsock_fd = fd_select(dsock, dsock_input, NULL, module)) == NULL) { + syslog(LOG_ERR, "fd_select failed on dsock: %m"); + return; + } + + reg_index = or_register(&oid_begemotNg, + "The MIB for the NetGraph access module for SNMP.", module); +} + +/* + * Called, when the module is to be unloaded after it was successfully loaded + */ +static int +ng_fini(void) +{ + struct ngtype *t; + + while ((t = TAILQ_FIRST(&ngtype_list)) != NULL) { + TAILQ_REMOVE(&ngtype_list, t, link); + free(t); + } + + if (csock_fd != NULL) + fd_deselect(csock_fd); + (void)close(csock); + + if (dsock_fd != NULL) + fd_deselect(dsock_fd); + (void)close(dsock); + + free(snmp_nodename); + + or_unregister(reg_index); + + return (0); +} + +const struct snmp_module config = { + "This module implements access to the netgraph sub-system", + ng_init, + ng_fini, + ng_idle, + NULL, + NULL, + ng_start, + NULL, + netgraph_ctree, + netgraph_CTREE_SIZE, + NULL +}; + +int +op_ng_config(struct snmp_context *ctx, struct snmp_value *value, + u_int sub, u_int iidx __unused, enum snmp_op op) +{ + asn_subid_t which = value->var.subs[sub - 1]; + int ret; + + switch (op) { + + case SNMP_OP_GETNEXT: + abort(); + + case SNMP_OP_GET: + /* + * Come here for GET, GETNEXT and COMMIT + */ + switch (which) { + + case LEAF_begemotNgControlNodeName: + return (string_get(value, snmp_nodename, -1)); + + case LEAF_begemotNgResBufSiz: + value->v.integer = resbufsiz; + break; + + case LEAF_begemotNgTimeout: + value->v.integer = timeout; + break; + + case LEAF_begemotNgDebugLevel: + value->v.uint32 = debug_level; + break; + + default: + abort(); + } + return (SNMP_ERR_NOERROR); + + case SNMP_OP_SET: + switch (which) { + + case LEAF_begemotNgControlNodeName: + /* only at initialisation */ + if (community != COMM_INITIALIZE) + return (SNMP_ERR_NOT_WRITEABLE); + + if (snmp_node != 0) + return (SNMP_ERR_NOT_WRITEABLE); + + if ((ret = string_save(value, ctx, -1, &snmp_nodename)) + != SNMP_ERR_NOERROR) + return (ret); + + if (NgMkSockNode(snmp_nodename, &csock, &dsock) < 0) { + syslog(LOG_ERR, "NgMkSockNode: %m"); + string_rollback(ctx, &snmp_nodename); + return (SNMP_ERR_GENERR); + } + snmp_node = ng_node_id(".:"); + + return (SNMP_ERR_NOERROR); + + case LEAF_begemotNgResBufSiz: + ctx->scratch->int1 = resbufsiz; + if (value->v.integer < 1024 || + value->v.integer > 0x10000) + return (SNMP_ERR_WRONG_VALUE); + resbufsiz = value->v.integer; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotNgTimeout: + ctx->scratch->int1 = timeout; + if (value->v.integer < 10 || + value->v.integer > 10000) + return (SNMP_ERR_WRONG_VALUE); + timeout = value->v.integer; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotNgDebugLevel: + ctx->scratch->int1 = debug_level; + debug_level = value->v.uint32; + NgSetDebug(debug_level); + return (SNMP_ERR_NOERROR); + } + abort(); + + case SNMP_OP_ROLLBACK: + switch (which) { + + case LEAF_begemotNgControlNodeName: + string_rollback(ctx, &snmp_nodename); + close(csock); + close(dsock); + snmp_node = 0; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotNgResBufSiz: + resbufsiz = ctx->scratch->int1; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotNgTimeout: + timeout = ctx->scratch->int1; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotNgDebugLevel: + debug_level = ctx->scratch->int1; + NgSetDebug(debug_level); + return (SNMP_ERR_NOERROR); + } + abort(); + + case SNMP_OP_COMMIT: + switch (which) { + + case LEAF_begemotNgControlNodeName: + string_commit(ctx); + return (SNMP_ERR_NOERROR); + + case LEAF_begemotNgResBufSiz: + case LEAF_begemotNgTimeout: + case LEAF_begemotNgDebugLevel: + return (SNMP_ERR_NOERROR); + } + abort(); + } + abort(); +} + +int +op_ng_stats(struct snmp_context *ctx __unused, struct snmp_value *value, + u_int sub, u_int iidx __unused, enum snmp_op op) +{ + switch (op) { + + case SNMP_OP_GETNEXT: + abort(); + + case SNMP_OP_GET: + value->v.uint32 = stats[value->var.subs[sub - 1] - 1]; + return (SNMP_ERR_NOERROR); + + case SNMP_OP_SET: + return (SNMP_ERR_NOT_WRITEABLE); + + case SNMP_OP_ROLLBACK: + case SNMP_OP_COMMIT: + abort(); + } + abort(); +} + +/* + * Netgraph type table + */ +static int +fetch_types(void) +{ + struct ngtype *t; + struct typelist *typelist; + struct ng_mesg *resp; + u_int u, i; + + if (this_tick <= ngtype_tick) + return (0); + + while ((t = TAILQ_FIRST(&ngtype_list)) != NULL) { + TAILQ_REMOVE(&ngtype_list, t, link); + free(t); + } + + if ((resp = ng_dialog_id(snmp_node, NGM_GENERIC_COOKIE, + NGM_LISTTYPES, NULL, 0)) == NULL) + return (SNMP_ERR_GENERR); + typelist = (struct typelist *)(void *)resp->data; + + for (u = 0; u < typelist->numtypes; u++) { + if ((t = malloc(sizeof(*t))) == NULL) { + free(resp); + return (SNMP_ERR_GENERR); + } + strcpy(t->name, typelist->typeinfo[u].type_name); + t->index.subs[0] = strlen(t->name); + t->index.len = t->index.subs[0] + 1; + for (i = 0; i < t->index.subs[0]; i++) + t->index.subs[i + 1] = t->name[i]; + + INSERT_OBJECT_OID(t, &ngtype_list); + } + + ngtype_tick = this_tick; + + free(resp); + return (0); +} + +/* + * Try to load the netgraph type with the given name. We assume, that + * type 'type' is implemented in the kernel module 'ng_type'. + */ +static int +ngtype_load(const u_char *name, size_t namelen) +{ + char *mod; + int ret; + + if ((mod = malloc(namelen + 4)) == NULL) + return (-1); + strcpy(mod, "ng_"); + strncpy(mod + 3, name, namelen); + mod[namelen + 3] = '\0'; + + ret = kldload(mod); + free(mod); + return (ret); +} + +/* + * Unload a netgraph type. + */ +static int +ngtype_unload(const u_char *name, size_t namelen) +{ + char *mod; + int id; + + if ((mod = malloc(namelen + 4)) == NULL) + return (-1); + strcpy(mod, "ng_"); + strncpy(mod + 3, name, namelen); + mod[namelen + 3] = '\0'; + + if ((id = kldfind(mod)) == -1) { + free(mod); + return (-1); + } + free(mod); + return (kldunload(id)); +} + +int +op_ng_type(struct snmp_context *ctx, struct snmp_value *value, + u_int sub, u_int iidx, enum snmp_op op) +{ + asn_subid_t which = value->var.subs[sub - 1]; + struct ngtype *t; + u_char *name; + size_t namelen; + int status = 1; + int ret; + + switch (op) { + + case SNMP_OP_GETNEXT: + if ((ret = fetch_types()) != 0) + return (ret); + if ((t = NEXT_OBJECT_OID(&ngtype_list, &value->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + index_append(&value->var, sub, &t->index); + break; + + case SNMP_OP_GET: + if ((ret = fetch_types()) != 0) + return (ret); + if ((t = FIND_OBJECT_OID(&ngtype_list, &value->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + break; + + case SNMP_OP_SET: + if (index_decode(&value->var, sub, iidx, &name, &namelen)) + return (SNMP_ERR_NO_CREATION); + if (namelen == 0 || namelen >= NG_TYPESIZ) { + free(name); + return (SNMP_ERR_NO_CREATION); + } + if ((ret = fetch_types()) != 0) { + free(name); + return (ret); + } + t = FIND_OBJECT_OID(&ngtype_list, &value->var, sub); + + if (which != LEAF_begemotNgTypeStatus) { + free(name); + if (t != NULL) + return (SNMP_ERR_NOT_WRITEABLE); + return (SNMP_ERR_NO_CREATION); + } + if (!TRUTH_OK(value->v.integer)) { + free(name); + return (SNMP_ERR_WRONG_VALUE); + } + ctx->scratch->int1 = TRUTH_GET(value->v.integer); + ctx->scratch->int1 |= (t != NULL) << 1; + ctx->scratch->ptr2 = name; + ctx->scratch->int2 = namelen; + + if (t == NULL) { + /* type not loaded */ + if (ctx->scratch->int1 & 1) { + /* request to load */ + if (ngtype_load(name, namelen) == -1) { + free(name); + if (errno == ENOENT) + return (SNMP_ERR_INCONS_NAME); + else + return (SNMP_ERR_GENERR); + } + } + } else { + /* is type loaded */ + if (!(ctx->scratch->int1 & 1)) { + /* request to unload */ + if (ngtype_unload(name, namelen) == -1) { + free(name); + return (SNMP_ERR_GENERR); + } + } + } + return (SNMP_ERR_NOERROR); + + case SNMP_OP_ROLLBACK: + ret = SNMP_ERR_NOERROR; + if (!(ctx->scratch->int1 & 2)) { + /* did not exist */ + if (ctx->scratch->int1 & 1) { + /* request to load - unload */ + if (ngtype_unload(ctx->scratch->ptr2, + ctx->scratch->int2) == -1) + ret = SNMP_ERR_UNDO_FAILED; + } + } else { + /* did exist */ + if (!(ctx->scratch->int1 & 1)) { + /* request to unload - reload */ + if (ngtype_load(ctx->scratch->ptr2, + ctx->scratch->int2) == -1) + ret = SNMP_ERR_UNDO_FAILED; + } + } + free(ctx->scratch->ptr2); + return (ret); + + case SNMP_OP_COMMIT: + free(ctx->scratch->ptr2); + return (SNMP_ERR_NOERROR); + + default: + abort(); + } + + /* + * Come here for GET and COMMIT + */ + switch (which) { + + case LEAF_begemotNgTypeStatus: + value->v.integer = status; + break; + + default: + abort(); + } + return (SNMP_ERR_NOERROR); +} + +/* + * Implement the node table + */ +static int +find_node(const struct asn_oid *oid, u_int sub, struct nodeinfo *info) +{ + ng_ID_t id = oid->subs[sub]; + struct ng_mesg *resp; + + if ((resp = ng_dialog_id(id, NGM_GENERIC_COOKIE, NGM_NODEINFO, + NULL, 0)) == NULL) + return (-1); + + *info = *(struct nodeinfo *)(void *)resp->data; + free(resp); + return (0); +} + +static int +ncmp(const void *p1, const void *p2) +{ + const struct nodeinfo *i1 = p1; + const struct nodeinfo *i2 = p2; + + if (i1->id < i2->id) + return (-1); + if (i1->id > i2->id) + return (+1); + return (0); +} + +static int +find_node_next(const struct asn_oid *oid, u_int sub, struct nodeinfo *info) +{ + u_int idxlen = oid->len - sub; + struct ng_mesg *resp; + struct namelist *list; + ng_ID_t id; + u_int i; + + if ((resp = ng_dialog_id(snmp_node, NGM_GENERIC_COOKIE, NGM_LISTNODES, + NULL, 0)) == NULL) + return (-1); + list = (struct namelist *)(void *)resp->data; + + qsort(list->nodeinfo, list->numnames, sizeof(list->nodeinfo[0]), ncmp); + + if (idxlen == 0) { + if (list->numnames == 0) { + free(resp); + return (-1); + } + *info = list->nodeinfo[0]; + free(resp); + return (0); + } + id = oid->subs[sub]; + + for (i = 0; i < list->numnames; i++) + if (list->nodeinfo[i].id > id) { + *info = list->nodeinfo[i]; + free(resp); + return (0); + } + + free(resp); + return (-1); +} + +int +op_ng_node(struct snmp_context *ctx __unused, struct snmp_value *value, + u_int sub, u_int iidx __unused, enum snmp_op op) +{ + asn_subid_t which = value->var.subs[sub - 1]; + u_int idxlen = value->var.len - sub; + struct nodeinfo nodeinfo; + + switch (op) { + + case SNMP_OP_GETNEXT: + if (find_node_next(&value->var, sub, &nodeinfo) == -1) + return (SNMP_ERR_NOSUCHNAME); + value->var.len = sub + 1; + value->var.subs[sub] = nodeinfo.id; + break; + + case SNMP_OP_GET: + if (idxlen != 1) + return (SNMP_ERR_NOSUCHNAME); + if (find_node(&value->var, sub, &nodeinfo) == -1) + return (SNMP_ERR_NOSUCHNAME); + break; + + case SNMP_OP_SET: + if (idxlen != 1) + return (SNMP_ERR_NO_CREATION); + if (find_node(&value->var, sub, &nodeinfo) == -1) + return (SNMP_ERR_NO_CREATION); + return (SNMP_ERR_NOT_WRITEABLE); + + case SNMP_OP_ROLLBACK: + case SNMP_OP_COMMIT: + default: + abort(); + } + + /* + * Come here for GET and COMMIT + */ + switch (which) { + + case LEAF_begemotNgNodeStatus: + value->v.integer = 1; + break; + case LEAF_begemotNgNodeName: + return (string_get(value, nodeinfo.name, -1)); + case LEAF_begemotNgNodeType: + return (string_get(value, nodeinfo.type, -1)); + case LEAF_begemotNgNodeHooks: + value->v.uint32 = nodeinfo.hooks; + break; + + default: + abort(); + } + return (SNMP_ERR_NOERROR); +} + +/* + * Implement the hook table + */ +static int +find_hook(int32_t id, const u_char *hook, size_t hooklen, struct linkinfo *info) +{ + struct ng_mesg *resp; + struct hooklist *list; + u_int i; + + if ((resp = ng_dialog_id(id, NGM_GENERIC_COOKIE, + NGM_LISTHOOKS, NULL, 0)) == NULL) + return (-1); + + list = (struct hooklist *)(void *)resp->data; + + for (i = 0; i < list->nodeinfo.hooks; i++) { + if (strlen(list->link[i].ourhook) == hooklen && + strncmp(list->link[i].ourhook, hook, hooklen) == 0) { + *info = list->link[i]; + free(resp); + return (0); + } + } + free(resp); + return (-1); +} + +static int +hook_cmp(const void *p1, const void *p2) +{ + const struct linkinfo *i1 = p1; + const struct linkinfo *i2 = p2; + + if (strlen(i1->ourhook) < strlen(i2->ourhook)) + return (-1); + if (strlen(i1->ourhook) > strlen(i2->ourhook)) + return (+1); + return (strcmp(i1->ourhook, i2->ourhook)); +} + +static int +find_hook_next(const struct asn_oid *oid, u_int sub, struct nodeinfo *nodeinfo, + struct linkinfo *linkinfo) +{ + u_int idxlen = oid->len - sub; + struct namelist *list; + struct ng_mesg *resp; + struct hooklist *hooks; + struct ng_mesg *resp1; + u_int node_index; + struct asn_oid idx; + u_int i, j; + + /* + * Get and sort Node list + */ + if ((resp = ng_dialog_id(snmp_node, NGM_GENERIC_COOKIE, NGM_LISTNODES, + NULL, 0)) == NULL) + return (-1); + list = (struct namelist *)(void *)resp->data; + + qsort(list->nodeinfo, list->numnames, sizeof(list->nodeinfo[0]), ncmp); + + /* + * If we have no index, take the first node and return the + * first hook. + */ + if (idxlen == 0) { + node_index = 0; + goto return_first_hook; + } + + /* + * Locate node + */ + for (node_index = 0; node_index < list->numnames; node_index++) + if (list->nodeinfo[node_index].id >= oid->subs[sub]) + break; + + /* + * If we have only the node part of the index take, or + * there is no node with that Id, take the first hook of that node. + */ + if (idxlen == 1 || node_index >= list->numnames || + list->nodeinfo[node_index].id > oid->subs[sub]) + goto return_first_hook; + + /* + * We had an exact match on the node id and have (at last part) + * of the hook name index. Loop through the hooks of the node + * and find the next one. + */ + if ((resp1 = ng_dialog_id(list->nodeinfo[node_index].id, + NGM_GENERIC_COOKIE, NGM_LISTHOOKS, NULL, 0)) == NULL) { + free(resp); + return (-1); + } + hooks = (struct hooklist *)(void *)resp1->data; + if (hooks->nodeinfo.hooks > 0) { + qsort(hooks->link, hooks->nodeinfo.hooks, + sizeof(hooks->link[0]), hook_cmp); + for (i = 0; i < hooks->nodeinfo.hooks; i++) { + idx.len = strlen(hooks->link[i].ourhook) + 1; + idx.subs[0] = idx.len - 1; + for (j = 0; j < idx.len; j++) + idx.subs[j + 1] = hooks->link[i].ourhook[j]; + if (index_compare(oid, sub + 1, &idx) < 0) + break; + } + if (i < hooks->nodeinfo.hooks) { + *nodeinfo = hooks->nodeinfo; + *linkinfo = hooks->link[i]; + + free(resp); + free(resp1); + return (0); + } + } + + /* no hook found larger than the index on the index node - take + * first hook of next node */ + free(resp1); + node_index++; + + return_first_hook: + while (node_index < list->numnames) { + if ((resp1 = ng_dialog_id(list->nodeinfo[node_index].id, + NGM_GENERIC_COOKIE, NGM_LISTHOOKS, NULL, 0)) == NULL) + break; + hooks = (struct hooklist *)(void *)resp1->data; + if (hooks->nodeinfo.hooks > 0) { + qsort(hooks->link, hooks->nodeinfo.hooks, + sizeof(hooks->link[0]), hook_cmp); + + *nodeinfo = hooks->nodeinfo; + *linkinfo = hooks->link[0]; + + free(resp); + free(resp1); + return (0); + } + + /* if we don't have hooks, try next node */ + free(resp1); + node_index++; + } + + free(resp); + return (-1); +} + +int +op_ng_hook(struct snmp_context *ctx __unused, struct snmp_value *value, + u_int sub, u_int iidx, enum snmp_op op) +{ + asn_subid_t which = value->var.subs[sub - 1]; + struct linkinfo linkinfo; + struct nodeinfo nodeinfo; + u_int32_t lid; + u_char *hook; + size_t hooklen; + u_int i; + + switch (op) { + + case SNMP_OP_GETNEXT: + if (find_hook_next(&value->var, sub, &nodeinfo, &linkinfo) == -1) + return (SNMP_ERR_NOSUCHNAME); + + value->var.len = sub + 1 + 1 + strlen(linkinfo.ourhook); + value->var.subs[sub] = nodeinfo.id; + value->var.subs[sub + 1] = strlen(linkinfo.ourhook); + for (i = 0; i < strlen(linkinfo.ourhook); i++) + value->var.subs[sub + i + 2] = + linkinfo.ourhook[i]; + break; + + case SNMP_OP_GET: + if (index_decode(&value->var, sub, iidx, &lid, + &hook, &hooklen)) + return (SNMP_ERR_NOSUCHNAME); + if (find_hook(lid, hook, hooklen, &linkinfo) == -1) { + free(hook); + return (SNMP_ERR_NOSUCHNAME); + } + free(hook); + break; + + case SNMP_OP_SET: + if (index_decode(&value->var, sub, iidx, &lid, + &hook, &hooklen)) + return (SNMP_ERR_NO_CREATION); + if (find_hook(lid, hook, hooklen, &linkinfo) == -1) { + free(hook); + return (SNMP_ERR_NO_CREATION); + } + free(hook); + return (SNMP_ERR_NOT_WRITEABLE); + + case SNMP_OP_ROLLBACK: + case SNMP_OP_COMMIT: + default: + abort(); + + } + + switch (which) { + + case LEAF_begemotNgHookStatus: + value->v.integer = 1; + break; + case LEAF_begemotNgHookPeerNodeId: + value->v.uint32 = linkinfo.nodeinfo.id; + break; + case LEAF_begemotNgHookPeerHook: + return (string_get(value, linkinfo.peerhook, -1)); + case LEAF_begemotNgHookPeerType: + return (string_get(value, linkinfo.nodeinfo.type, -1)); + default: + abort(); + } + return (SNMP_ERR_NOERROR); +} diff --git a/usr.sbin/bsnmpd/modules/snmp_netgraph/snmp_netgraph.h b/usr.sbin/bsnmpd/modules/snmp_netgraph/snmp_netgraph.h new file mode 100644 index 0000000..21e553c --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_netgraph/snmp_netgraph.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2001-2003 + * Fraunhofer Institute for Open Communication Systems (FhG Fokus). + * All rights reserved. + * + * Author: Harti Brandt <harti@freebsd.org> + * + * Redistribution of this software and documentation and use in source and + * binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code or documentation must retain the above + * copyright notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY FRAUNHOFER FOKUS + * AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * FRAUNHOFER FOKUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + * + * Netgraph interface for SNMPd. Exported stuff. + */ +#ifndef SNMP_NETGRAPH_H_ +#define SNMP_NETGRAPH_H_ + +#include <netgraph/ng_message.h> + +extern ng_ID_t snmp_node; +extern u_char *snmp_nodename; + +typedef void ng_cookie_f(const struct ng_mesg *, const char *, ng_ID_t, void *); +typedef void ng_hook_f(const char *, const u_char *, size_t, void *); + +void *ng_register_cookie(const struct lmodule *, u_int32_t cookie, + ng_ID_t, ng_cookie_f *, void *); +void ng_unregister_cookie(void *reg); + +void *ng_register_hook(const struct lmodule *, const char *, + ng_hook_f *, void *); +void ng_unregister_hook(void *reg); + +void ng_unregister_module(const struct lmodule *); + +int ng_output(const char *path, u_int cookie, u_int opcode, + const void *arg, size_t arglen); +int ng_output_node(const char *node, u_int cookie, u_int opcode, + const void *arg, size_t arglen); +int ng_output_id(ng_ID_t node, u_int cookie, u_int opcode, + const void *arg, size_t arglen); + +struct ng_mesg *ng_dialog(const char *path, u_int cookie, u_int opcode, + const void *arg, size_t arglen); +struct ng_mesg *ng_dialog_node(const char *node, u_int cookie, u_int opcode, + const void *arg, size_t arglen); +struct ng_mesg *ng_dialog_id(ng_ID_t id, u_int cookie, u_int opcode, + const void *arg, size_t arglen); + +int ng_send_data(const char *hook, const void *sndbuf, size_t sndlen); + +ng_ID_t ng_mkpeer_id(ng_ID_t, const char *name, const char *type, + const char *hook, const char *peerhook); +int ng_connect_node(const char *node, const char *ourhook, const char *peerhook); +int ng_connect_id(ng_ID_t id, const char *ourhook, const char *peerhook); +int ng_connect2_id(ng_ID_t id, ng_ID_t peer, const char *ourhook, + const char *peerhook); +int ng_connect2_tee_id(ng_ID_t id, ng_ID_t peer, const char *ourhook, + const char *peerhook); +int ng_rmhook(const char *ourhook); +int ng_rmhook_id(ng_ID_t, const char *); +int ng_rmhook_tee_id(ng_ID_t, const char *); +int ng_shutdown_id(ng_ID_t); + +ng_ID_t ng_next_node_id(ng_ID_t node, const char *type, const char *hook); +ng_ID_t ng_node_id(const char *path); +ng_ID_t ng_node_id_node(const char *node); +ng_ID_t ng_node_name(ng_ID_t, char *); +ng_ID_t ng_node_type(ng_ID_t, char *); +int ng_peer_hook_id(ng_ID_t, const char *, char *); + +#endif diff --git a/usr.sbin/bsnmpd/modules/snmp_pf/BEGEMOT-PF-MIB.txt b/usr.sbin/bsnmpd/modules/snmp_pf/BEGEMOT-PF-MIB.txt new file mode 100644 index 0000000..0b90bb2 --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_pf/BEGEMOT-PF-MIB.txt @@ -0,0 +1,1347 @@ +-- +-- ---------------------------------------------------------------------------- +-- "THE BEER-WARE LICENSE" (Revision 42): +-- <philip@FreeBSD.org> wrote this file. As long as you retain this notice you +-- can do whatever you want with this stuff. If we meet some day, and you think +-- this stuff is worth it, you can buy me a beer in return. -Philip Paeps +-- ---------------------------------------------------------------------------- +-- +-- $FreeBSD$ +-- + +BEGEMOT-PF-MIB DEFINITIONS ::= BEGIN + +IMPORTS + MODULE-IDENTITY, OBJECT-TYPE, Counter64, Integer32, + TimeTicks, Unsigned32 + FROM SNMPv2-SMI + TruthValue + FROM SNMPv2-TC + InetAddress, InetAddressType, InetAddressPrefixLength + FROM INET-ADDRESS-MIB + begemot + FROM BEGEMOT-MIB; + +begemotPf MODULE-IDENTITY + LAST-UPDATED "201003180000Z" + ORGANIZATION "NixSys BVBA" + CONTACT-INFO + " Philip Paeps + + Postal: NixSys BVBA + Louizastraat 14 + BE-2800 Mechelen + Belgium + + E-Mail: philip@FreeBSD.org" + DESCRIPTION + "The Begemot MIB for the pf packet filter." + REVISION "201003180000Z" + DESCRIPTION + "Modified pfTablesAddrEntry to support IPv6 + addresses - added pfTablesAddrNetType column + and modified type of pfTablesAddrNet to + InetAddress." + REVISION "200912050000Z" + DESCRIPTION + "Added support for retrieving counters of labeled + pf filter rules via pfLabelspfLabels subtree." + REVISION "200501240000Z" + DESCRIPTION + "Initial revision." + + ::= { begemot 200 } + +begemotPfObjects OBJECT IDENTIFIER ::= { begemotPf 1 } + +-- -------------------------------------------------------------------------- + +pfStatus OBJECT IDENTIFIER ::= { begemotPfObjects 1 } +pfCounter OBJECT IDENTIFIER ::= { begemotPfObjects 2 } +pfStateTable OBJECT IDENTIFIER ::= { begemotPfObjects 3 } +pfSrcNodes OBJECT IDENTIFIER ::= { begemotPfObjects 4 } +pfLimits OBJECT IDENTIFIER ::= { begemotPfObjects 5 } +pfTimeouts OBJECT IDENTIFIER ::= { begemotPfObjects 6 } +pfLogInterface OBJECT IDENTIFIER ::= { begemotPfObjects 7 } +pfInterfaces OBJECT IDENTIFIER ::= { begemotPfObjects 8 } +pfTables OBJECT IDENTIFIER ::= { begemotPfObjects 9 } +pfAltq OBJECT IDENTIFIER ::= { begemotPfObjects 10 } +pfLabels OBJECT IDENTIFIER ::= { begemotPfObjects 11 } + +-- -------------------------------------------------------------------------- + +-- +-- status information +-- + +pfStatusRunning OBJECT-TYPE + SYNTAX TruthValue + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "True if pf is currently enabled." + ::= { pfStatus 1 } + +pfStatusRuntime OBJECT-TYPE + SYNTAX TimeTicks + UNITS "1/100th of a Second" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Indicates how long pf has been enabled. If pf is not currently + enabled, indicates how long it has been disabled. If pf has not + been enabled or disabled since the system was started, the value + will be 0." + ::= { pfStatus 2 } + +pfStatusDebug OBJECT-TYPE + SYNTAX INTEGER { none(0), urgent(1), misc(2), loud(3) } + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Indicates the debug level at which pf is running." + ::= { pfStatus 3 } + +pfStatusHostId OBJECT-TYPE + SYNTAX OCTET STRING + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The (unique) host identifier of the machine running pf." + ::= { pfStatus 4 } + +-- -------------------------------------------------------------------------- + +-- +-- counters +-- + +pfCounterMatch OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of packets that matched a filter rule." + ::= { pfCounter 1 } + +pfCounterBadOffset OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of packets with bad offset." + ::= { pfCounter 2 } + +pfCounterFragment OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of fragmented packets." + ::= { pfCounter 3 } + +pfCounterShort OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of short packets." + ::= { pfCounter 4 } + +pfCounterNormalize OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of normalized packets." + ::= { pfCounter 5 } + +pfCounterMemDrop OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of packets dropped due to memory limitations." + ::= { pfCounter 6 } + +-- -------------------------------------------------------------------------- + +-- +-- state table +-- + +pfStateTableCount OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of entries in the state table." + ::= { pfStateTable 1 } + +pfStateTableSearches OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of searches against the state table." + ::= { pfStateTable 2 } + +pfStateTableInserts OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of entries inserted into the state table." + ::= { pfStateTable 3 } + +pfStateTableRemovals OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of entries removed from the state table." + ::= { pfStateTable 4 } + +-- -------------------------------------------------------------------------- + +-- +-- source nodes +-- + +pfSrcNodesCount OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of entries in the source tracking table." + ::= { pfSrcNodes 1 } + +pfSrcNodesSearches OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of searches against the source tracking table." + ::= { pfSrcNodes 2 } + +pfSrcNodesInserts OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of entries inserted into the source tracking table." + ::= { pfSrcNodes 3 } + +pfSrcNodesRemovals OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of entries removed from the source tracking table." + ::= { pfSrcNodes 4 } + +-- -------------------------------------------------------------------------- + +-- +-- limits +-- + +pfLimitsStates OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Maximum number of 'keep state' rules in the ruleset." + ::= { pfLimits 1 } + +pfLimitsSrcNodes OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Maximum number of 'sticky-address' or 'source-track' rules + in the ruleset." + ::= { pfLimits 2 } + +pfLimitsFrags OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Maximum number of 'scrub' rules in the ruleset." + ::= { pfLimits 3 } + +-- -------------------------------------------------------------------------- + +-- +-- timeouts +-- + +pfTimeoutsTcpFirst OBJECT-TYPE + SYNTAX Integer32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "State after the first packet in a connection." + ::= { pfTimeouts 1 } + +pfTimeoutsTcpOpening OBJECT-TYPE + SYNTAX Integer32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "State before the destination host ever sends a packet." + ::= { pfTimeouts 2 } + +pfTimeoutsTcpEstablished OBJECT-TYPE + SYNTAX Integer32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The fully established state." + ::= { pfTimeouts 3 } + +pfTimeoutsTcpClosing OBJECT-TYPE + SYNTAX Integer32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "State after the first FIN has been sent." + ::= { pfTimeouts 4 } + +pfTimeoutsTcpFinWait OBJECT-TYPE + SYNTAX Integer32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "State after both FINs have been exchanged and the + connection is closed." + ::= { pfTimeouts 5 } + +pfTimeoutsTcpClosed OBJECT-TYPE + SYNTAX Integer32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "State after one endpoint sends an RST." + ::= { pfTimeouts 6 } + +pfTimeoutsUdpFirst OBJECT-TYPE + SYNTAX Integer32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "State after the first packet." + ::= { pfTimeouts 7 } + +pfTimeoutsUdpSingle OBJECT-TYPE + SYNTAX Integer32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "State if the source host sends more than one packet but + the destination host has never sent one back." + ::= { pfTimeouts 8 } + +pfTimeoutsUdpMultiple OBJECT-TYPE + SYNTAX Integer32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "State if both hosts have sent packets." + ::= { pfTimeouts 9 } + +pfTimeoutsIcmpFirst OBJECT-TYPE + SYNTAX Integer32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "State after the first packet." + ::= { pfTimeouts 10 } + +pfTimeoutsIcmpError OBJECT-TYPE + SYNTAX Integer32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "State after an ICMP error came back in response to an + ICMP packet." + ::= { pfTimeouts 11 } + +pfTimeoutsOtherFirst OBJECT-TYPE + SYNTAX Integer32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "State after the first packet." + ::= { pfTimeouts 12 } + +pfTimeoutsOtherSingle OBJECT-TYPE + SYNTAX Integer32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "State if the source host sends more than one packet but + the destination host has never sent one back." + ::= { pfTimeouts 13 } + +pfTimeoutsOtherMultiple OBJECT-TYPE + SYNTAX Integer32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "State if both hosts have sent packets." + ::= { pfTimeouts 14 } + +pfTimeoutsFragment OBJECT-TYPE + SYNTAX Integer32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Seconds before an unassembled fragment is expired." + ::= { pfTimeouts 15 } + +pfTimeoutsInterval OBJECT-TYPE + SYNTAX Integer32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Interval between purging expired states and fragments." + ::= { pfTimeouts 16 } + +pfTimeoutsAdaptiveStart OBJECT-TYPE + SYNTAX Integer32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "When the number of state entries exceeds this value, + adaptive scaling begins." + ::= { pfTimeouts 17 } + +pfTimeoutsAdaptiveEnd OBJECT-TYPE + SYNTAX Integer32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "When reaching this number of state entries, all timeout + values become zero, effectively purging all state entries + immediately." + ::= { pfTimeouts 18 } + +pfTimeoutsSrcNode OBJECT-TYPE + SYNTAX Integer32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Length of time to retain a source tracking entry after + the last state expires." + ::= { pfTimeouts 19 } + +-- -------------------------------------------------------------------------- + +-- +-- log interface +-- + +pfLogInterfaceName OBJECT-TYPE + SYNTAX OCTET STRING + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The name of the interface configured with 'set loginterface'. + If no interface has been configured, the object will be empty." + ::= { pfLogInterface 1 } + +pfLogInterfaceIp4BytesIn OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of IPv4 bytes passed in on the loginterface." + ::= { pfLogInterface 2 } + +pfLogInterfaceIp4BytesOut OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of IPv4 bytes passed out on the loginterface." + ::= { pfLogInterface 3 } + +pfLogInterfaceIp4PktsInPass OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of IPv4 packets passed in on the loginterface." + ::= { pfLogInterface 4 } + +pfLogInterfaceIp4PktsInDrop OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of IPv4 packets dropped coming in on the loginterface." + ::= { pfLogInterface 5 } + +pfLogInterfaceIp4PktsOutPass OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of IPv4 packets passed out on the loginterface." + ::= { pfLogInterface 6 } + +pfLogInterfaceIp4PktsOutDrop OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of IPv4 packets dropped going out on the loginterface." + ::= { pfLogInterface 7 } + +pfLogInterfaceIp6BytesIn OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of IPv6 bytes passed in on the loginterface." + ::= { pfLogInterface 8 } + +pfLogInterfaceIp6BytesOut OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of IPv6 bytes passed out on the loginterface." + ::= { pfLogInterface 9 } + +pfLogInterfaceIp6PktsInPass OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of IPv6 packets passed in on the loginterface." + ::= { pfLogInterface 10 } + +pfLogInterfaceIp6PktsInDrop OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of IPv6 packets dropped coming in on the loginterface." + ::= { pfLogInterface 11 } + +pfLogInterfaceIp6PktsOutPass OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of IPv6 packets passed out on the loginterface." + ::= { pfLogInterface 12 } + +pfLogInterfaceIp6PktsOutDrop OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Number of IPv6 packets dropped going out on the loginterface." + ::= { pfLogInterface 13 } + +-- -------------------------------------------------------------------------- + +-- +-- interfaces +-- + +pfInterfacesIfNumber OBJECT-TYPE + SYNTAX Integer32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of network interfaces on this system." + ::= { pfInterfaces 1 } + +pfInterfacesIfTable OBJECT-TYPE + SYNTAX SEQUENCE OF PfInterfacesIfEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Table of network interfaces, indexed on pfInterfacesIfNumber." + ::= { pfInterfaces 2 } + +pfInterfacesIfEntry OBJECT-TYPE + SYNTAX PfInterfacesIfEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "An entry in the pfInterfacesIfTable containing information + about a particular network interface in the machine." + INDEX { pfInterfacesIfIndex } + ::= { pfInterfacesIfTable 1 } + +PfInterfacesIfEntry ::= SEQUENCE { + pfInterfacesIfIndex Integer32, + pfInterfacesIfDescr OCTET STRING, + pfInterfacesIfType INTEGER, + pfInterfacesIfTZero TimeTicks, + pfInterfacesIfRefsState Unsigned32, + pfInterfacesIfRefsRule Unsigned32, + pfInterfacesIf4BytesInPass Counter64, + pfInterfacesIf4BytesInBlock Counter64, + pfInterfacesIf4BytesOutPass Counter64, + pfInterfacesIf4BytesOutBlock Counter64, + pfInterfacesIf4PktsInPass Counter64, + pfInterfacesIf4PktsInBlock Counter64, + pfInterfacesIf4PktsOutPass Counter64, + pfInterfacesIf4PktsOutBlock Counter64, + pfInterfacesIf6BytesInPass Counter64, + pfInterfacesIf6BytesInBlock Counter64, + pfInterfacesIf6BytesOutPass Counter64, + pfInterfacesIf6BytesOutBlock Counter64, + pfInterfacesIf6PktsInPass Counter64, + pfInterfacesIf6PktsInBlock Counter64, + pfInterfacesIf6PktsOutPass Counter64, + pfInterfacesIf6PktsOutBlock Counter64 +} + +pfInterfacesIfIndex OBJECT-TYPE + SYNTAX Integer32 (1..2147483647) + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A unique value, greater than zero, for each interface." + ::= { pfInterfacesIfEntry 1 } + +pfInterfacesIfDescr OBJECT-TYPE + SYNTAX OCTET STRING + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The name of the interface." + ::= { pfInterfacesIfEntry 2 } + +pfInterfacesIfType OBJECT-TYPE + SYNTAX INTEGER { group(0), instance(1), detached(2) } + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Indicates whether the interface is a group inteface, an + interface instance, or whether it has been removed or + destroyed." + ::= { pfInterfacesIfEntry 3 } + +pfInterfacesIfTZero OBJECT-TYPE + SYNTAX TimeTicks + UNITS "1/100th of a Second" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Time since statistics were last reset or since the + interface was loaded." + ::= { pfInterfacesIfEntry 4 } + +pfInterfacesIfRefsState OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of state and/or source track entries referencing + this interface." + ::= { pfInterfacesIfEntry 5 } + +pfInterfacesIfRefsRule OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of rules referencing this interface." + ::= { pfInterfacesIfEntry 6 } + +pfInterfacesIf4BytesInPass OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of IPv4 bytes passed coming in on this interface." + ::= { pfInterfacesIfEntry 7 } + +pfInterfacesIf4BytesInBlock OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of IPv4 bytes blocked coming in on this interface." + ::= { pfInterfacesIfEntry 8 } + +pfInterfacesIf4BytesOutPass OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of IPv4 bytes passed going out on this interface." + ::= { pfInterfacesIfEntry 9 } + +pfInterfacesIf4BytesOutBlock OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of IPv4 bytes blocked going out on this interface." + ::= { pfInterfacesIfEntry 10 } + +pfInterfacesIf4PktsInPass OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of IPv4 packets passed coming in on this interface." + ::= { pfInterfacesIfEntry 11 } + +pfInterfacesIf4PktsInBlock OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of IPv4 packets blocked coming in on this interface." + ::= { pfInterfacesIfEntry 12 } + +pfInterfacesIf4PktsOutPass OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of IPv4 packets passed going out on this interface." + ::= { pfInterfacesIfEntry 13 } + +pfInterfacesIf4PktsOutBlock OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of IPv4 packets blocked going out on this interface." + ::= { pfInterfacesIfEntry 14 } + +pfInterfacesIf6BytesInPass OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of IPv6 bytes passed coming in on this interface." + ::= { pfInterfacesIfEntry 15 } + +pfInterfacesIf6BytesInBlock OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of IPv6 bytes blocked coming in on this interface." + ::= { pfInterfacesIfEntry 16 } + +pfInterfacesIf6BytesOutPass OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of IPv6 bytes passed going out on this interface." + ::= { pfInterfacesIfEntry 17 } + +pfInterfacesIf6BytesOutBlock OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of IPv6 bytes blocked going out on this interface." + ::= { pfInterfacesIfEntry 18 } + + +pfInterfacesIf6PktsInPass OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of IPv6 packets passed coming in on this interface." + ::= { pfInterfacesIfEntry 19 } + +pfInterfacesIf6PktsInBlock OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of IPv6 packets blocked coming in on this interface." + ::= { pfInterfacesIfEntry 20 } + +pfInterfacesIf6PktsOutPass OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of IPv6 packets passed going out on this interface." + ::= { pfInterfacesIfEntry 21 } + +pfInterfacesIf6PktsOutBlock OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of IPv6 packets blocked going out on this interface." + ::= { pfInterfacesIfEntry 22 } + +-- -------------------------------------------------------------------------- + +-- +-- tables +-- + +pfTablesTblNumber OBJECT-TYPE + SYNTAX Integer32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of tables on this system." + ::= { pfTables 1 } + +pfTablesTblTable OBJECT-TYPE + SYNTAX SEQUENCE OF PfTablesTblEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Table of tables, index on pfTablesTblIndex." + ::= { pfTables 2 } + +pfTablesTblEntry OBJECT-TYPE + SYNTAX PfTablesTblEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Any entry in the pfTablesTblTable containing information + about a particular table on the system." + INDEX { pfTablesTblIndex } + ::= { pfTablesTblTable 1 } + +PfTablesTblEntry ::= SEQUENCE { + pfTablesTblIndex Integer32, + pfTablesTblDescr OCTET STRING, + pfTablesTblCount Integer32, + pfTablesTblTZero TimeTicks, + pfTablesTblRefsAnchor Integer32, + pfTablesTblRefsRule Integer32, + pfTablesTblEvalMatch Counter64, + pfTablesTblEvalNoMatch Counter64, + pfTablesTblBytesInPass Counter64, + pfTablesTblBytesInBlock Counter64, + pfTablesTblBytesInXPass Counter64, + pfTablesTblBytesOutPass Counter64, + pfTablesTblBytesOutBlock Counter64, + pfTablesTblBytesOutXPass Counter64, + pfTablesTblPktsInPass Counter64, + pfTablesTblPktsInBlock Counter64, + pfTablesTblPktsInXPass Counter64, + pfTablesTblPktsOutPass Counter64, + pfTablesTblPktsOutBlock Counter64, + pfTablesTblPktsOutXPass Counter64 +} + +pfTablesTblIndex OBJECT-TYPE + SYNTAX Integer32 (1..2147483647) + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A unique value, greater than zero, for each table." + ::= { pfTablesTblEntry 1 } + +pfTablesTblDescr OBJECT-TYPE + SYNTAX OCTET STRING + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The name of the table." + ::= { pfTablesTblEntry 2 } + +pfTablesTblCount OBJECT-TYPE + SYNTAX Integer32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of addresses in the table." + ::= { pfTablesTblEntry 3 } + +pfTablesTblTZero OBJECT-TYPE + SYNTAX TimeTicks + UNITS "1/100th of a Second" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The time passed since the statistics of this table were last + cleared or the time since this table was loaded, whichever is + sooner." + ::= { pfTablesTblEntry 4 } + +pfTablesTblRefsAnchor OBJECT-TYPE + SYNTAX Integer32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of anchors referencing this table." + ::= { pfTablesTblEntry 5 } + +pfTablesTblRefsRule OBJECT-TYPE + SYNTAX Integer32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of rules referencing this table." + ::= { pfTablesTblEntry 6 } + +pfTablesTblEvalMatch OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of evaluations returning a match." + ::= { pfTablesTblEntry 7 } + +pfTablesTblEvalNoMatch OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of evaluations not returning a match." + ::= { pfTablesTblEntry 8 } + +pfTablesTblBytesInPass OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of bytes passed in matching the table." + ::= { pfTablesTblEntry 9 } + +pfTablesTblBytesInBlock OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of bytes blocked coming in matching the table." + ::= { pfTablesTblEntry 10 } + +pfTablesTblBytesInXPass OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of bytes statefully passed in where the state + entry refers to the table, but the table no longer contains + the address in question." + ::= { pfTablesTblEntry 11 } + +pfTablesTblBytesOutPass OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of bytes passed out matching the table." + ::= { pfTablesTblEntry 12 } + +pfTablesTblBytesOutBlock OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of bytes blocked going out matching the table." + ::= { pfTablesTblEntry 13 } + +pfTablesTblBytesOutXPass OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of bytes statefully passed out where the state + entry refers to the table, but the table no longer contains + the address in question." + ::= { pfTablesTblEntry 14 } + +pfTablesTblPktsInPass OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of packets passed in matching the table." + ::= { pfTablesTblEntry 15 } + +pfTablesTblPktsInBlock OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of packets blocked coming in matching the table." + ::= { pfTablesTblEntry 16 } + +pfTablesTblPktsInXPass OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of packets statefully passed in where the state + entry refers to the table, but the table no longer contains + the address in question." + ::= { pfTablesTblEntry 17 } + +pfTablesTblPktsOutPass OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of packets passed out matching the table." + ::= { pfTablesTblEntry 18 } + +pfTablesTblPktsOutBlock OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of packets blocked going out matching the table." + ::= { pfTablesTblEntry 19 } + +pfTablesTblPktsOutXPass OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of packets statefully passed out where the state + entry refers to the table, but the table no longer contains + the address in question." + ::= { pfTablesTblEntry 20 } + +pfTablesAddrTable OBJECT-TYPE + SYNTAX SEQUENCE OF PfTablesAddrEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Table of addresses from every table on the system." + ::= { pfTables 3 } + +pfTablesAddrEntry OBJECT-TYPE + SYNTAX PfTablesAddrEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "An entry in the pfTablesAddrTable containing information + about a particular entry in a table." + INDEX { pfTablesAddrIndex } + ::= { pfTablesAddrTable 1 } + +PfTablesAddrEntry ::= SEQUENCE { + pfTablesAddrIndex Integer32, + pfTablesAddrNetType InetAddressType, + pfTablesAddrNet InetAddress, + pfTablesAddrPrefix InetAddressPrefixLength, + pfTablesAddrTZero TimeTicks, + pfTablesAddrBytesInPass Counter64, + pfTablesAddrBytesInBlock Counter64, + pfTablesAddrBytesOutPass Counter64, + pfTablesAddrBytesOutBlock Counter64, + pfTablesAddrPktsInPass Counter64, + pfTablesAddrPktsInBlock Counter64, + pfTablesAddrPktsOutPass Counter64, + pfTablesAddrPktsOutBlock Counter64 +} + +pfTablesAddrIndex OBJECT-TYPE + SYNTAX Integer32 (1..2147483647) + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A unique value, greater than zero, for each address." + ::= { pfTablesAddrEntry 1 } + +pfTablesAddrNetType OBJECT-TYPE + SYNTAX InetAddressType + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The type of address in the corresponding pfTablesAddrNet object." + ::= { pfTablesAddrEntry 2 } + +pfTablesAddrNet OBJECT-TYPE + SYNTAX InetAddress + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The IP address of this particular table entry." + ::= { pfTablesAddrEntry 3 } + +pfTablesAddrPrefix OBJECT-TYPE + SYNTAX InetAddressPrefixLength + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The CIDR netmask of this particular table entry." + ::= { pfTablesAddrEntry 4 } + +pfTablesAddrTZero OBJECT-TYPE + SYNTAX TimeTicks + UNITS "1/100th of a Second" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The time passed since this entry's statistics were last + cleared, or the time passed since this entry was loaded + into the table, whichever is sooner." + ::= { pfTablesAddrEntry 5 } + +pfTablesAddrBytesInPass OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of inbound bytes passed as a result of this entry." + ::= { pfTablesAddrEntry 6 } + +pfTablesAddrBytesInBlock OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of inbound bytes blocked as a result of this entry." + ::= { pfTablesAddrEntry 7 } + +pfTablesAddrBytesOutPass OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of outbound bytes passed as a result of this entry." + ::= { pfTablesAddrEntry 8 } + +pfTablesAddrBytesOutBlock OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of outbound bytes blocked as a result of this entry." + ::= { pfTablesAddrEntry 9 } + +pfTablesAddrPktsInPass OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of inbound packets passed as a result of this entry." + ::= { pfTablesAddrEntry 10 } + +pfTablesAddrPktsInBlock OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of inbound packets blocked as a result of this entry." + ::= { pfTablesAddrEntry 11 } + +pfTablesAddrPktsOutPass OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of outbound packets passed as a result of this entry." + ::= { pfTablesAddrEntry 12 } + +pfTablesAddrPktsOutBlock OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of outbound packets blocked as a result of this + entry." + ::= { pfTablesAddrEntry 13 } + +-- -------------------------------------------------------------------------- + +-- +-- Altq information +-- + +pfAltqQueueNumber OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of queues in the active set." + ::= { pfAltq 1 } + +pfAltqQueueTable OBJECT-TYPE + SYNTAX SEQUENCE OF PfAltqQueueEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Table containing the rules that are active on this system." + ::= { pfAltq 2 } + +pfAltqQueueEntry OBJECT-TYPE + SYNTAX PfAltqQueueEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "An entry in the pfAltqQueueTable table." + INDEX { pfAltqQueueIndex } + ::= { pfAltqQueueTable 1 } + +PfAltqQueueEntry ::= SEQUENCE { + pfAltqQueueIndex Integer32, + pfAltqQueueDescr OCTET STRING, + pfAltqQueueParent OCTET STRING, + pfAltqQueueScheduler INTEGER, + pfAltqQueueBandwidth Unsigned32, + pfAltqQueuePriority Integer32, + pfAltqQueueLimit Integer32 +} + +pfAltqQueueIndex OBJECT-TYPE + SYNTAX Integer32 (1..2147483647) + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A unique value, greater than zero, for each queue." + ::= { pfAltqQueueEntry 1 } + +pfAltqQueueDescr OBJECT-TYPE + SYNTAX OCTET STRING + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The name of the queue." + ::= { pfAltqQueueEntry 2 } + +pfAltqQueueParent OBJECT-TYPE + SYNTAX OCTET STRING + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Name of the queue's parent if it has one." + ::= { pfAltqQueueEntry 3 } + +pfAltqQueueScheduler OBJECT-TYPE + SYNTAX INTEGER { cbq(1), hfsc(8), priq(11) } + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Scheduler algorithm implemented by this queue." + ::= { pfAltqQueueEntry 4 } + +pfAltqQueueBandwidth OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Bandwitch assigned to this queue." + ::= { pfAltqQueueEntry 5 } + +pfAltqQueuePriority OBJECT-TYPE + SYNTAX Integer32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Priority level of the queue." + ::= { pfAltqQueueEntry 6 } + +pfAltqQueueLimit OBJECT-TYPE + SYNTAX Integer32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Maximum number of packets in the queue." + ::= { pfAltqQueueEntry 7 } + +pfLabelsLblNumber OBJECT-TYPE + SYNTAX Integer32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of labeled filter rules on this system." + ::= { pfLabels 1 } + +pfLabelsLblTable OBJECT-TYPE + SYNTAX SEQUENCE OF PfLabelsLblEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Table of filter rules, index on pfLabelsLblIndex." + ::= { pfLabels 2 } + +pfLabelsLblEntry OBJECT-TYPE + SYNTAX PfLabelsLblEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Any entry in the pfLabelsLblTable containing information + about a particular filter rule on the system." + INDEX { pfLabelsLblIndex } + ::= { pfLabelsLblTable 1 } + +PfLabelsLblEntry ::= SEQUENCE { + pfLabelsLblIndex Integer32, + pfLabelsLblName OCTET STRING, + pfLabelsLblEvals Counter64, + pfLabelsLblBytesIn Counter64, + pfLabelsLblBytesOut Counter64, + pfLabelsLblPktsIn Counter64, + pfLabelsLblPktsOut Counter64 +} + +pfLabelsLblIndex OBJECT-TYPE + SYNTAX Integer32 (1..2147483647) + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A unique value, greater than zero, for each label." + ::= { pfLabelsLblEntry 1 } + +pfLabelsLblName OBJECT-TYPE + SYNTAX OCTET STRING + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The name of the rule label." + ::= { pfLabelsLblEntry 2 } + +pfLabelsLblEvals OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of rule evaluations." + ::= { pfLabelsLblEntry 3 } + +pfLabelsLblBytesIn OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of incoming bytes matched by the rule." + ::= { pfLabelsLblEntry 4 } + +pfLabelsLblBytesOut OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of outgoing bytes matched by the rule." + ::= { pfLabelsLblEntry 5 } + +pfLabelsLblPktsIn OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of incoming packets matched by the rule." + ::= { pfLabelsLblEntry 6 } + +pfLabelsLblPktsOut OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of outgoing packets matched by the rule." + ::= { pfLabelsLblEntry 7 } + +END diff --git a/usr.sbin/bsnmpd/modules/snmp_pf/Makefile b/usr.sbin/bsnmpd/modules/snmp_pf/Makefile new file mode 100644 index 0000000..6218932 --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_pf/Makefile @@ -0,0 +1,13 @@ +# $FreeBSD$ +# +# Author: Philip Paeps <philip@freebsd.org> + +MOD= pf +SRCS= pf_snmp.c +CFLAGS+= -DSNMPTREE_TYPES + +XSYM= begemotPf +DEFS= ${MOD}_tree.def +BMIBS= BEGEMOT-PF-MIB.txt + +.include <bsd.snmpmod.mk> diff --git a/usr.sbin/bsnmpd/modules/snmp_pf/pf_snmp.c b/usr.sbin/bsnmpd/modules/snmp_pf/pf_snmp.c new file mode 100644 index 0000000..bc4bc35 --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_pf/pf_snmp.c @@ -0,0 +1,1800 @@ +/*- + * Copyright (c) 2005 Philip Paeps <philip@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include <sys/queue.h> +#include <bsnmp/snmpmod.h> + +#include <net/pfvar.h> +#include <sys/ioctl.h> + +#include <errno.h> +#include <fcntl.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <unistd.h> + +#include "pf_oid.h" +#include "pf_tree.h" + +struct lmodule *module; + +static int dev = -1; +static int started; +static uint64_t pf_tick; + +static struct pf_status pfs; + +enum { IN, OUT }; +enum { IPV4, IPV6 }; +enum { PASS, BLOCK }; + +#define PFI_IFTYPE_GROUP 0 +#define PFI_IFTYPE_INSTANCE 1 +#define PFI_IFTYPE_DETACHED 2 + +struct pfi_entry { + struct pfi_kif pfi; + u_int index; + TAILQ_ENTRY(pfi_entry) link; +}; +TAILQ_HEAD(pfi_table, pfi_entry); + +static struct pfi_table pfi_table; +static time_t pfi_table_age; +static int pfi_table_count; + +#define PFI_TABLE_MAXAGE 5 + +struct pft_entry { + struct pfr_tstats pft; + u_int index; + TAILQ_ENTRY(pft_entry) link; +}; +TAILQ_HEAD(pft_table, pft_entry); + +static struct pft_table pft_table; +static time_t pft_table_age; +static int pft_table_count; + +#define PFT_TABLE_MAXAGE 5 + +struct pfa_entry { + struct pfr_astats pfas; + u_int index; + TAILQ_ENTRY(pfa_entry) link; +}; +TAILQ_HEAD(pfa_table, pfa_entry); + +static struct pfa_table pfa_table; +static time_t pfa_table_age; +static int pfa_table_count; + +#define PFA_TABLE_MAXAGE 5 + +struct pfq_entry { + struct pf_altq altq; + u_int index; + TAILQ_ENTRY(pfq_entry) link; +}; +TAILQ_HEAD(pfq_table, pfq_entry); + +static struct pfq_table pfq_table; +static time_t pfq_table_age; +static int pfq_table_count; + +static int altq_enabled = 0; + +#define PFQ_TABLE_MAXAGE 5 + +struct pfl_entry { + char name[MAXPATHLEN + PF_RULE_LABEL_SIZE]; + u_int64_t evals; + u_int64_t bytes[2]; + u_int64_t pkts[2]; + u_int index; + TAILQ_ENTRY(pfl_entry) link; +}; +TAILQ_HEAD(pfl_table, pfl_entry); + +static struct pfl_table pfl_table; +static time_t pfl_table_age; +static int pfl_table_count; + +#define PFL_TABLE_MAXAGE 5 + +/* Forward declarations */ +static int pfi_refresh(void); +static int pfq_refresh(void); +static int pfs_refresh(void); +static int pft_refresh(void); +static int pfa_refresh(void); +static int pfl_refresh(void); +static struct pfi_entry * pfi_table_find(u_int idx); +static struct pfq_entry * pfq_table_find(u_int idx); +static struct pft_entry * pft_table_find(u_int idx); +static struct pfa_entry * pfa_table_find(u_int idx); +static struct pfl_entry * pfl_table_find(u_int idx); + +static int altq_is_enabled(int pfdevice); + +int +pf_status(struct snmp_context __unused *ctx, struct snmp_value *val, + u_int sub, u_int __unused vindex, enum snmp_op op) +{ + asn_subid_t which = val->var.subs[sub - 1]; + time_t runtime; + unsigned char str[128]; + + if (op == SNMP_OP_SET) + return (SNMP_ERR_NOT_WRITEABLE); + + if (op == SNMP_OP_GET) { + if (pfs_refresh() == -1) + return (SNMP_ERR_GENERR); + + switch (which) { + case LEAF_pfStatusRunning: + val->v.uint32 = pfs.running; + break; + case LEAF_pfStatusRuntime: + runtime = (pfs.since > 0) ? + time(NULL) - pfs.since : 0; + val->v.uint32 = runtime * 100; + break; + case LEAF_pfStatusDebug: + val->v.uint32 = pfs.debug; + break; + case LEAF_pfStatusHostId: + sprintf(str, "0x%08x", ntohl(pfs.hostid)); + return (string_get(val, str, strlen(str))); + + default: + return (SNMP_ERR_NOSUCHNAME); + } + + return (SNMP_ERR_NOERROR); + } + + abort(); +} + +int +pf_counter(struct snmp_context __unused *ctx, struct snmp_value *val, + u_int sub, u_int __unused vindex, enum snmp_op op) +{ + asn_subid_t which = val->var.subs[sub - 1]; + + if (op == SNMP_OP_SET) + return (SNMP_ERR_NOT_WRITEABLE); + + if (op == SNMP_OP_GET) { + if (pfs_refresh() == -1) + return (SNMP_ERR_GENERR); + + switch (which) { + case LEAF_pfCounterMatch: + val->v.counter64 = pfs.counters[PFRES_MATCH]; + break; + case LEAF_pfCounterBadOffset: + val->v.counter64 = pfs.counters[PFRES_BADOFF]; + break; + case LEAF_pfCounterFragment: + val->v.counter64 = pfs.counters[PFRES_FRAG]; + break; + case LEAF_pfCounterShort: + val->v.counter64 = pfs.counters[PFRES_SHORT]; + break; + case LEAF_pfCounterNormalize: + val->v.counter64 = pfs.counters[PFRES_NORM]; + break; + case LEAF_pfCounterMemDrop: + val->v.counter64 = pfs.counters[PFRES_MEMORY]; + break; + + default: + return (SNMP_ERR_NOSUCHNAME); + } + + return (SNMP_ERR_NOERROR); + } + + abort(); +} + +int +pf_statetable(struct snmp_context __unused *ctx, struct snmp_value *val, + u_int sub, u_int __unused vindex, enum snmp_op op) +{ + asn_subid_t which = val->var.subs[sub - 1]; + + if (op == SNMP_OP_SET) + return (SNMP_ERR_NOT_WRITEABLE); + + if (op == SNMP_OP_GET) { + if (pfs_refresh() == -1) + return (SNMP_ERR_GENERR); + + switch (which) { + case LEAF_pfStateTableCount: + val->v.uint32 = pfs.states; + break; + case LEAF_pfStateTableSearches: + val->v.counter64 = + pfs.fcounters[FCNT_STATE_SEARCH]; + break; + case LEAF_pfStateTableInserts: + val->v.counter64 = + pfs.fcounters[FCNT_STATE_INSERT]; + break; + case LEAF_pfStateTableRemovals: + val->v.counter64 = + pfs.fcounters[FCNT_STATE_REMOVALS]; + break; + + default: + return (SNMP_ERR_NOSUCHNAME); + } + + return (SNMP_ERR_NOERROR); + } + + abort(); +} + +int +pf_srcnodes(struct snmp_context __unused *ctx, struct snmp_value *val, + u_int sub, u_int __unused vindex, enum snmp_op op) +{ + asn_subid_t which = val->var.subs[sub - 1]; + + if (op == SNMP_OP_SET) + return (SNMP_ERR_NOT_WRITEABLE); + + if (op == SNMP_OP_GET) { + if (pfs_refresh() == -1) + return (SNMP_ERR_GENERR); + + switch (which) { + case LEAF_pfSrcNodesCount: + val->v.uint32 = pfs.src_nodes; + break; + case LEAF_pfSrcNodesSearches: + val->v.counter64 = + pfs.scounters[SCNT_SRC_NODE_SEARCH]; + break; + case LEAF_pfSrcNodesInserts: + val->v.counter64 = + pfs.scounters[SCNT_SRC_NODE_INSERT]; + break; + case LEAF_pfSrcNodesRemovals: + val->v.counter64 = + pfs.scounters[SCNT_SRC_NODE_REMOVALS]; + break; + + default: + return (SNMP_ERR_NOSUCHNAME); + } + + return (SNMP_ERR_NOERROR); + } + + abort(); +} + +int +pf_limits(struct snmp_context __unused *ctx, struct snmp_value *val, + u_int sub, u_int __unused vindex, enum snmp_op op) +{ + asn_subid_t which = val->var.subs[sub - 1]; + struct pfioc_limit pl; + + if (op == SNMP_OP_SET) + return (SNMP_ERR_NOT_WRITEABLE); + + if (op == SNMP_OP_GET) { + bzero(&pl, sizeof(struct pfioc_limit)); + + switch (which) { + case LEAF_pfLimitsStates: + pl.index = PF_LIMIT_STATES; + break; + case LEAF_pfLimitsSrcNodes: + pl.index = PF_LIMIT_SRC_NODES; + break; + case LEAF_pfLimitsFrags: + pl.index = PF_LIMIT_FRAGS; + break; + + default: + return (SNMP_ERR_NOSUCHNAME); + } + + if (ioctl(dev, DIOCGETLIMIT, &pl)) { + syslog(LOG_ERR, "pf_limits(): ioctl(): %s", + strerror(errno)); + return (SNMP_ERR_GENERR); + } + + val->v.uint32 = pl.limit; + + return (SNMP_ERR_NOERROR); + } + + abort(); +} + +int +pf_timeouts(struct snmp_context __unused *ctx, struct snmp_value *val, + u_int sub, u_int __unused vindex, enum snmp_op op) +{ + asn_subid_t which = val->var.subs[sub - 1]; + struct pfioc_tm pt; + + if (op == SNMP_OP_SET) + return (SNMP_ERR_NOT_WRITEABLE); + + if (op == SNMP_OP_GET) { + bzero(&pt, sizeof(struct pfioc_tm)); + + switch (which) { + case LEAF_pfTimeoutsTcpFirst: + pt.timeout = PFTM_TCP_FIRST_PACKET; + break; + case LEAF_pfTimeoutsTcpOpening: + pt.timeout = PFTM_TCP_OPENING; + break; + case LEAF_pfTimeoutsTcpEstablished: + pt.timeout = PFTM_TCP_ESTABLISHED; + break; + case LEAF_pfTimeoutsTcpClosing: + pt.timeout = PFTM_TCP_CLOSING; + break; + case LEAF_pfTimeoutsTcpFinWait: + pt.timeout = PFTM_TCP_FIN_WAIT; + break; + case LEAF_pfTimeoutsTcpClosed: + pt.timeout = PFTM_TCP_CLOSED; + break; + case LEAF_pfTimeoutsUdpFirst: + pt.timeout = PFTM_UDP_FIRST_PACKET; + break; + case LEAF_pfTimeoutsUdpSingle: + pt.timeout = PFTM_UDP_SINGLE; + break; + case LEAF_pfTimeoutsUdpMultiple: + pt.timeout = PFTM_UDP_MULTIPLE; + break; + case LEAF_pfTimeoutsIcmpFirst: + pt.timeout = PFTM_ICMP_FIRST_PACKET; + break; + case LEAF_pfTimeoutsIcmpError: + pt.timeout = PFTM_ICMP_ERROR_REPLY; + break; + case LEAF_pfTimeoutsOtherFirst: + pt.timeout = PFTM_OTHER_FIRST_PACKET; + break; + case LEAF_pfTimeoutsOtherSingle: + pt.timeout = PFTM_OTHER_SINGLE; + break; + case LEAF_pfTimeoutsOtherMultiple: + pt.timeout = PFTM_OTHER_MULTIPLE; + break; + case LEAF_pfTimeoutsFragment: + pt.timeout = PFTM_FRAG; + break; + case LEAF_pfTimeoutsInterval: + pt.timeout = PFTM_INTERVAL; + break; + case LEAF_pfTimeoutsAdaptiveStart: + pt.timeout = PFTM_ADAPTIVE_START; + break; + case LEAF_pfTimeoutsAdaptiveEnd: + pt.timeout = PFTM_ADAPTIVE_END; + break; + case LEAF_pfTimeoutsSrcNode: + pt.timeout = PFTM_SRC_NODE; + break; + + default: + return (SNMP_ERR_NOSUCHNAME); + } + + if (ioctl(dev, DIOCGETTIMEOUT, &pt)) { + syslog(LOG_ERR, "pf_timeouts(): ioctl(): %s", + strerror(errno)); + return (SNMP_ERR_GENERR); + } + + val->v.integer = pt.seconds; + + return (SNMP_ERR_NOERROR); + } + + abort(); +} + +int +pf_logif(struct snmp_context __unused *ctx, struct snmp_value *val, + u_int sub, u_int __unused vindex, enum snmp_op op) +{ + asn_subid_t which = val->var.subs[sub - 1]; + unsigned char str[IFNAMSIZ]; + + if (op == SNMP_OP_SET) + return (SNMP_ERR_NOT_WRITEABLE); + + if (op == SNMP_OP_GET) { + if (pfs_refresh() == -1) + return (SNMP_ERR_GENERR); + + switch (which) { + case LEAF_pfLogInterfaceName: + strlcpy(str, pfs.ifname, sizeof str); + return (string_get(val, str, strlen(str))); + case LEAF_pfLogInterfaceIp4BytesIn: + val->v.counter64 = pfs.bcounters[IPV4][IN]; + break; + case LEAF_pfLogInterfaceIp4BytesOut: + val->v.counter64 = pfs.bcounters[IPV4][OUT]; + break; + case LEAF_pfLogInterfaceIp4PktsInPass: + val->v.counter64 = + pfs.pcounters[IPV4][IN][PF_PASS]; + break; + case LEAF_pfLogInterfaceIp4PktsInDrop: + val->v.counter64 = + pfs.pcounters[IPV4][IN][PF_DROP]; + break; + case LEAF_pfLogInterfaceIp4PktsOutPass: + val->v.counter64 = + pfs.pcounters[IPV4][OUT][PF_PASS]; + break; + case LEAF_pfLogInterfaceIp4PktsOutDrop: + val->v.counter64 = + pfs.pcounters[IPV4][OUT][PF_DROP]; + break; + case LEAF_pfLogInterfaceIp6BytesIn: + val->v.counter64 = pfs.bcounters[IPV6][IN]; + break; + case LEAF_pfLogInterfaceIp6BytesOut: + val->v.counter64 = pfs.bcounters[IPV6][OUT]; + break; + case LEAF_pfLogInterfaceIp6PktsInPass: + val->v.counter64 = + pfs.pcounters[IPV6][IN][PF_PASS]; + break; + case LEAF_pfLogInterfaceIp6PktsInDrop: + val->v.counter64 = + pfs.pcounters[IPV6][IN][PF_DROP]; + break; + case LEAF_pfLogInterfaceIp6PktsOutPass: + val->v.counter64 = + pfs.pcounters[IPV6][OUT][PF_PASS]; + break; + case LEAF_pfLogInterfaceIp6PktsOutDrop: + val->v.counter64 = + pfs.pcounters[IPV6][OUT][PF_DROP]; + break; + + default: + return (SNMP_ERR_NOSUCHNAME); + } + + return (SNMP_ERR_NOERROR); + } + + abort(); +} + +int +pf_interfaces(struct snmp_context __unused *ctx, struct snmp_value *val, + u_int sub, u_int __unused vindex, enum snmp_op op) +{ + asn_subid_t which = val->var.subs[sub - 1]; + + if (op == SNMP_OP_SET) + return (SNMP_ERR_NOT_WRITEABLE); + + if (op == SNMP_OP_GET) { + if ((time(NULL) - pfi_table_age) > PFI_TABLE_MAXAGE) + if (pfi_refresh() == -1) + return (SNMP_ERR_GENERR); + + switch (which) { + case LEAF_pfInterfacesIfNumber: + val->v.uint32 = pfi_table_count; + break; + + default: + return (SNMP_ERR_NOSUCHNAME); + } + + return (SNMP_ERR_NOERROR); + } + + abort(); +} + +int +pf_iftable(struct snmp_context __unused *ctx, struct snmp_value *val, + u_int sub, u_int __unused vindex, enum snmp_op op) +{ + asn_subid_t which = val->var.subs[sub - 1]; + struct pfi_entry *e = NULL; + + if ((time(NULL) - pfi_table_age) > PFI_TABLE_MAXAGE) + pfi_refresh(); + + switch (op) { + case SNMP_OP_SET: + return (SNMP_ERR_NOT_WRITEABLE); + case SNMP_OP_GETNEXT: + if ((e = NEXT_OBJECT_INT(&pfi_table, + &val->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + val->var.len = sub + 1; + val->var.subs[sub] = e->index; + break; + case SNMP_OP_GET: + if (val->var.len - sub != 1) + return (SNMP_ERR_NOSUCHNAME); + if ((e = pfi_table_find(val->var.subs[sub])) == NULL) + return (SNMP_ERR_NOSUCHNAME); + break; + + case SNMP_OP_COMMIT: + case SNMP_OP_ROLLBACK: + default: + abort(); + } + + switch (which) { + case LEAF_pfInterfacesIfDescr: + return (string_get(val, e->pfi.pfik_name, -1)); + case LEAF_pfInterfacesIfType: + val->v.integer = PFI_IFTYPE_INSTANCE; + break; + case LEAF_pfInterfacesIfTZero: + val->v.uint32 = + (time(NULL) - e->pfi.pfik_tzero) * 100; + break; + case LEAF_pfInterfacesIfRefsState: + val->v.uint32 = e->pfi.pfik_states; + break; + case LEAF_pfInterfacesIfRefsRule: + val->v.uint32 = e->pfi.pfik_rules; + break; + case LEAF_pfInterfacesIf4BytesInPass: + val->v.counter64 = + e->pfi.pfik_bytes[IPV4][IN][PASS]; + break; + case LEAF_pfInterfacesIf4BytesInBlock: + val->v.counter64 = + e->pfi.pfik_bytes[IPV4][IN][BLOCK]; + break; + case LEAF_pfInterfacesIf4BytesOutPass: + val->v.counter64 = + e->pfi.pfik_bytes[IPV4][OUT][PASS]; + break; + case LEAF_pfInterfacesIf4BytesOutBlock: + val->v.counter64 = + e->pfi.pfik_bytes[IPV4][OUT][BLOCK]; + break; + case LEAF_pfInterfacesIf4PktsInPass: + val->v.counter64 = + e->pfi.pfik_packets[IPV4][IN][PASS]; + break; + case LEAF_pfInterfacesIf4PktsInBlock: + val->v.counter64 = + e->pfi.pfik_packets[IPV4][IN][BLOCK]; + break; + case LEAF_pfInterfacesIf4PktsOutPass: + val->v.counter64 = + e->pfi.pfik_packets[IPV4][OUT][PASS]; + break; + case LEAF_pfInterfacesIf4PktsOutBlock: + val->v.counter64 = + e->pfi.pfik_packets[IPV4][OUT][BLOCK]; + break; + case LEAF_pfInterfacesIf6BytesInPass: + val->v.counter64 = + e->pfi.pfik_bytes[IPV6][IN][PASS]; + break; + case LEAF_pfInterfacesIf6BytesInBlock: + val->v.counter64 = + e->pfi.pfik_bytes[IPV6][IN][BLOCK]; + break; + case LEAF_pfInterfacesIf6BytesOutPass: + val->v.counter64 = + e->pfi.pfik_bytes[IPV6][OUT][PASS]; + break; + case LEAF_pfInterfacesIf6BytesOutBlock: + val->v.counter64 = + e->pfi.pfik_bytes[IPV6][OUT][BLOCK]; + break; + case LEAF_pfInterfacesIf6PktsInPass: + val->v.counter64 = + e->pfi.pfik_packets[IPV6][IN][PASS]; + break; + case LEAF_pfInterfacesIf6PktsInBlock: + val->v.counter64 = + e->pfi.pfik_packets[IPV6][IN][BLOCK]; + break; + case LEAF_pfInterfacesIf6PktsOutPass: + val->v.counter64 = + e->pfi.pfik_packets[IPV6][OUT][PASS]; + break; + case LEAF_pfInterfacesIf6PktsOutBlock: + val->v.counter64 = + e->pfi.pfik_packets[IPV6][OUT][BLOCK]; + break; + + default: + return (SNMP_ERR_NOSUCHNAME); + } + + return (SNMP_ERR_NOERROR); +} + +int +pf_tables(struct snmp_context __unused *ctx, struct snmp_value *val, + u_int sub, u_int __unused vindex, enum snmp_op op) +{ + asn_subid_t which = val->var.subs[sub - 1]; + + if (op == SNMP_OP_SET) + return (SNMP_ERR_NOT_WRITEABLE); + + if (op == SNMP_OP_GET) { + if ((time(NULL) - pft_table_age) > PFT_TABLE_MAXAGE) + if (pft_refresh() == -1) + return (SNMP_ERR_GENERR); + + switch (which) { + case LEAF_pfTablesTblNumber: + val->v.uint32 = pft_table_count; + break; + + default: + return (SNMP_ERR_NOSUCHNAME); + } + + return (SNMP_ERR_NOERROR); + } + + abort(); +} + +int +pf_tbltable(struct snmp_context __unused *ctx, struct snmp_value *val, + u_int sub, u_int __unused vindex, enum snmp_op op) +{ + asn_subid_t which = val->var.subs[sub - 1]; + struct pft_entry *e = NULL; + + if ((time(NULL) - pft_table_age) > PFT_TABLE_MAXAGE) + pft_refresh(); + + switch (op) { + case SNMP_OP_SET: + return (SNMP_ERR_NOT_WRITEABLE); + case SNMP_OP_GETNEXT: + if ((e = NEXT_OBJECT_INT(&pft_table, + &val->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + val->var.len = sub + 1; + val->var.subs[sub] = e->index; + break; + case SNMP_OP_GET: + if (val->var.len - sub != 1) + return (SNMP_ERR_NOSUCHNAME); + if ((e = pft_table_find(val->var.subs[sub])) == NULL) + return (SNMP_ERR_NOSUCHNAME); + break; + + case SNMP_OP_COMMIT: + case SNMP_OP_ROLLBACK: + default: + abort(); + } + + switch (which) { + case LEAF_pfTablesTblDescr: + return (string_get(val, e->pft.pfrts_name, -1)); + case LEAF_pfTablesTblCount: + val->v.integer = e->pft.pfrts_cnt; + break; + case LEAF_pfTablesTblTZero: + val->v.uint32 = + (time(NULL) - e->pft.pfrts_tzero) * 100; + break; + case LEAF_pfTablesTblRefsAnchor: + val->v.integer = + e->pft.pfrts_refcnt[PFR_REFCNT_ANCHOR]; + break; + case LEAF_pfTablesTblRefsRule: + val->v.integer = + e->pft.pfrts_refcnt[PFR_REFCNT_RULE]; + break; + case LEAF_pfTablesTblEvalMatch: + val->v.counter64 = e->pft.pfrts_match; + break; + case LEAF_pfTablesTblEvalNoMatch: + val->v.counter64 = e->pft.pfrts_nomatch; + break; + case LEAF_pfTablesTblBytesInPass: + val->v.counter64 = + e->pft.pfrts_bytes[PFR_DIR_IN][PFR_OP_PASS]; + break; + case LEAF_pfTablesTblBytesInBlock: + val->v.counter64 = + e->pft.pfrts_bytes[PFR_DIR_IN][PFR_OP_BLOCK]; + break; + case LEAF_pfTablesTblBytesInXPass: + val->v.counter64 = + e->pft.pfrts_bytes[PFR_DIR_IN][PFR_OP_XPASS]; + break; + case LEAF_pfTablesTblBytesOutPass: + val->v.counter64 = + e->pft.pfrts_bytes[PFR_DIR_OUT][PFR_OP_PASS]; + break; + case LEAF_pfTablesTblBytesOutBlock: + val->v.counter64 = + e->pft.pfrts_bytes[PFR_DIR_OUT][PFR_OP_BLOCK]; + break; + case LEAF_pfTablesTblBytesOutXPass: + val->v.counter64 = + e->pft.pfrts_bytes[PFR_DIR_OUT][PFR_OP_XPASS]; + break; + case LEAF_pfTablesTblPktsInPass: + val->v.counter64 = + e->pft.pfrts_packets[PFR_DIR_IN][PFR_OP_PASS]; + break; + case LEAF_pfTablesTblPktsInBlock: + val->v.counter64 = + e->pft.pfrts_packets[PFR_DIR_IN][PFR_OP_BLOCK]; + break; + case LEAF_pfTablesTblPktsInXPass: + val->v.counter64 = + e->pft.pfrts_packets[PFR_DIR_IN][PFR_OP_XPASS]; + break; + case LEAF_pfTablesTblPktsOutPass: + val->v.counter64 = + e->pft.pfrts_packets[PFR_DIR_OUT][PFR_OP_PASS]; + break; + case LEAF_pfTablesTblPktsOutBlock: + val->v.counter64 = + e->pft.pfrts_packets[PFR_DIR_OUT][PFR_OP_BLOCK]; + break; + case LEAF_pfTablesTblPktsOutXPass: + val->v.counter64 = + e->pft.pfrts_packets[PFR_DIR_OUT][PFR_OP_XPASS]; + break; + + default: + return (SNMP_ERR_NOSUCHNAME); + } + + return (SNMP_ERR_NOERROR); +} + +int +pf_tbladdr(struct snmp_context __unused *ctx, struct snmp_value __unused *val, + u_int __unused sub, u_int __unused vindex, enum snmp_op __unused op) +{ + asn_subid_t which = val->var.subs[sub - 1]; + struct pfa_entry *e = NULL; + + if ((time(NULL) - pfa_table_age) > PFA_TABLE_MAXAGE) + pfa_refresh(); + + switch (op) { + case SNMP_OP_SET: + return (SNMP_ERR_NOT_WRITEABLE); + case SNMP_OP_GETNEXT: + if ((e = NEXT_OBJECT_INT(&pfa_table, + &val->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + val->var.len = sub + 1; + val->var.subs[sub] = e->index; + break; + case SNMP_OP_GET: + if (val->var.len - sub != 1) + return (SNMP_ERR_NOSUCHNAME); + if ((e = pfa_table_find(val->var.subs[sub])) == NULL) + return (SNMP_ERR_NOSUCHNAME); + break; + + case SNMP_OP_COMMIT: + case SNMP_OP_ROLLBACK: + default: + abort(); + } + + switch (which) { + case LEAF_pfTablesAddrNetType: + if (e->pfas.pfras_a.pfra_af == AF_INET) + val->v.integer = pfTablesAddrNetType_ipv4; + else if (e->pfas.pfras_a.pfra_af == AF_INET6) + val->v.integer = pfTablesAddrNetType_ipv6; + else + return (SNMP_ERR_GENERR); + break; + case LEAF_pfTablesAddrNet: + if (e->pfas.pfras_a.pfra_af == AF_INET) { + return (string_get(val, + (u_char *)&e->pfas.pfras_a.pfra_ip4addr, 4)); + } else if (e->pfas.pfras_a.pfra_af == AF_INET6) + return (string_get(val, + (u_char *)&e->pfas.pfras_a.pfra_ip6addr, 16)); + else + return (SNMP_ERR_GENERR); + break; + case LEAF_pfTablesAddrPrefix: + val->v.integer = (int32_t) e->pfas.pfras_a.pfra_net; + break; + case LEAF_pfTablesAddrTZero: + val->v.uint32 = + (time(NULL) - e->pfas.pfras_tzero) * 100; + break; + case LEAF_pfTablesAddrBytesInPass: + val->v.counter64 = + e->pfas.pfras_bytes[PFR_DIR_IN][PFR_OP_PASS]; + break; + case LEAF_pfTablesAddrBytesInBlock: + val->v.counter64 = + e->pfas.pfras_bytes[PFR_DIR_IN][PFR_OP_BLOCK]; + break; + case LEAF_pfTablesAddrBytesOutPass: + val->v.counter64 = + e->pfas.pfras_bytes[PFR_DIR_OUT][PFR_OP_PASS]; + break; + case LEAF_pfTablesAddrBytesOutBlock: + val->v.counter64 = + e->pfas.pfras_bytes[PFR_DIR_OUT][PFR_OP_BLOCK]; + break; + case LEAF_pfTablesAddrPktsInPass: + val->v.counter64 = + e->pfas.pfras_packets[PFR_DIR_IN][PFR_OP_PASS]; + break; + case LEAF_pfTablesAddrPktsInBlock: + val->v.counter64 = + e->pfas.pfras_packets[PFR_DIR_IN][PFR_OP_BLOCK]; + break; + case LEAF_pfTablesAddrPktsOutPass: + val->v.counter64 = + e->pfas.pfras_packets[PFR_DIR_OUT][PFR_OP_PASS]; + break; + case LEAF_pfTablesAddrPktsOutBlock: + val->v.counter64 = + e->pfas.pfras_packets[PFR_DIR_OUT][PFR_OP_BLOCK]; + break; + default: + return (SNMP_ERR_NOSUCHNAME); + } + + return (SNMP_ERR_NOERROR); +} + +int +pf_altq(struct snmp_context __unused *ctx, struct snmp_value *val, + u_int sub, u_int __unused vindex, enum snmp_op op) +{ + asn_subid_t which = val->var.subs[sub - 1]; + + if (!altq_enabled) + return (SNMP_ERR_NOSUCHNAME); + + if (op == SNMP_OP_SET) + return (SNMP_ERR_NOT_WRITEABLE); + + if (op == SNMP_OP_GET) { + if ((time(NULL) - pfq_table_age) > PFQ_TABLE_MAXAGE) + if (pfq_refresh() == -1) + return (SNMP_ERR_GENERR); + + switch (which) { + case LEAF_pfAltqQueueNumber: + val->v.uint32 = pfq_table_count; + break; + + default: + return (SNMP_ERR_NOSUCHNAME); + } + + return (SNMP_ERR_NOERROR); + } + + abort(); + return (SNMP_ERR_GENERR); +} + +int +pf_altqq(struct snmp_context __unused *ctx, struct snmp_value *val, + u_int sub, u_int __unused vindex, enum snmp_op op) +{ + asn_subid_t which = val->var.subs[sub - 1]; + struct pfq_entry *e = NULL; + + if (!altq_enabled) + return (SNMP_ERR_NOSUCHNAME); + + if ((time(NULL) - pfq_table_age) > PFQ_TABLE_MAXAGE) + pfq_refresh(); + + switch (op) { + case SNMP_OP_SET: + return (SNMP_ERR_NOT_WRITEABLE); + case SNMP_OP_GETNEXT: + if ((e = NEXT_OBJECT_INT(&pfq_table, + &val->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + val->var.len = sub + 1; + val->var.subs[sub] = e->index; + break; + case SNMP_OP_GET: + if (val->var.len - sub != 1) + return (SNMP_ERR_NOSUCHNAME); + if ((e = pfq_table_find(val->var.subs[sub])) == NULL) + return (SNMP_ERR_NOSUCHNAME); + break; + + case SNMP_OP_COMMIT: + case SNMP_OP_ROLLBACK: + default: + abort(); + } + + switch (which) { + case LEAF_pfAltqQueueDescr: + return (string_get(val, e->altq.qname, -1)); + case LEAF_pfAltqQueueParent: + return (string_get(val, e->altq.parent, -1)); + case LEAF_pfAltqQueueScheduler: + val->v.integer = e->altq.scheduler; + break; + case LEAF_pfAltqQueueBandwidth: + val->v.uint32 = e->altq.bandwidth; + break; + case LEAF_pfAltqQueuePriority: + val->v.integer = e->altq.priority; + break; + case LEAF_pfAltqQueueLimit: + val->v.integer = e->altq.qlimit; + break; + + default: + return (SNMP_ERR_NOSUCHNAME); + } + + return (SNMP_ERR_NOERROR); +} + +int +pf_labels(struct snmp_context __unused *ctx, struct snmp_value *val, + u_int sub, u_int __unused vindex, enum snmp_op op) +{ + asn_subid_t which = val->var.subs[sub - 1]; + + if (op == SNMP_OP_SET) + return (SNMP_ERR_NOT_WRITEABLE); + + if (op == SNMP_OP_GET) { + if ((time(NULL) - pfl_table_age) > PFL_TABLE_MAXAGE) + if (pfl_refresh() == -1) + return (SNMP_ERR_GENERR); + + switch (which) { + case LEAF_pfLabelsLblNumber: + val->v.uint32 = pfl_table_count; + break; + + default: + return (SNMP_ERR_NOSUCHNAME); + } + + return (SNMP_ERR_NOERROR); + } + + abort(); + return (SNMP_ERR_GENERR); +} + +int +pf_lbltable(struct snmp_context __unused *ctx, struct snmp_value *val, + u_int sub, u_int __unused vindex, enum snmp_op op) +{ + asn_subid_t which = val->var.subs[sub - 1]; + struct pfl_entry *e = NULL; + + if ((time(NULL) - pfl_table_age) > PFL_TABLE_MAXAGE) + pfl_refresh(); + + switch (op) { + case SNMP_OP_SET: + return (SNMP_ERR_NOT_WRITEABLE); + case SNMP_OP_GETNEXT: + if ((e = NEXT_OBJECT_INT(&pfl_table, + &val->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + val->var.len = sub + 1; + val->var.subs[sub] = e->index; + break; + case SNMP_OP_GET: + if (val->var.len - sub != 1) + return (SNMP_ERR_NOSUCHNAME); + if ((e = pfl_table_find(val->var.subs[sub])) == NULL) + return (SNMP_ERR_NOSUCHNAME); + break; + + case SNMP_OP_COMMIT: + case SNMP_OP_ROLLBACK: + default: + abort(); + } + + switch (which) { + case LEAF_pfLabelsLblName: + return (string_get(val, e->name, -1)); + case LEAF_pfLabelsLblEvals: + val->v.counter64 = e->evals; + break; + case LEAF_pfLabelsLblBytesIn: + val->v.counter64 = e->bytes[IN]; + break; + case LEAF_pfLabelsLblBytesOut: + val->v.counter64 = e->bytes[OUT]; + break; + case LEAF_pfLabelsLblPktsIn: + val->v.counter64 = e->pkts[IN]; + break; + case LEAF_pfLabelsLblPktsOut: + val->v.counter64 = e->pkts[OUT]; + break; + default: + return (SNMP_ERR_NOSUCHNAME); + } + + return (SNMP_ERR_NOERROR); +} + +static struct pfi_entry * +pfi_table_find(u_int idx) +{ + struct pfi_entry *e; + + TAILQ_FOREACH(e, &pfi_table, link) + if (e->index == idx) + return (e); + return (NULL); +} + +static struct pfq_entry * +pfq_table_find(u_int idx) +{ + struct pfq_entry *e; + + TAILQ_FOREACH(e, &pfq_table, link) + if (e->index == idx) + return (e); + return (NULL); +} + +static struct pft_entry * +pft_table_find(u_int idx) +{ + struct pft_entry *e; + + TAILQ_FOREACH(e, &pft_table, link) + if (e->index == idx) + return (e); + return (NULL); +} + +static struct pfa_entry * +pfa_table_find(u_int idx) +{ + struct pfa_entry *e; + + TAILQ_FOREACH(e, &pfa_table, link) + if (e->index == idx) + return (e); + return (NULL); +} + +static struct pfl_entry * +pfl_table_find(u_int idx) +{ + struct pfl_entry *e; + + TAILQ_FOREACH(e, &pfl_table, link) + if (e->index == idx) + return (e); + + return (NULL); +} + +static int +pfi_refresh(void) +{ + struct pfioc_iface io; + struct pfi_kif *p = NULL; + struct pfi_entry *e; + int i, numifs = 1; + + if (started && this_tick <= pf_tick) + return (0); + + while (!TAILQ_EMPTY(&pfi_table)) { + e = TAILQ_FIRST(&pfi_table); + TAILQ_REMOVE(&pfi_table, e, link); + free(e); + } + + bzero(&io, sizeof(io)); + io.pfiio_esize = sizeof(struct pfi_kif); + + for (;;) { + p = reallocf(p, numifs * sizeof(struct pfi_kif)); + if (p == NULL) { + syslog(LOG_ERR, "pfi_refresh(): reallocf() numifs=%d: %s", + numifs, strerror(errno)); + goto err2; + } + io.pfiio_size = numifs; + io.pfiio_buffer = p; + + if (ioctl(dev, DIOCIGETIFACES, &io)) { + syslog(LOG_ERR, "pfi_refresh(): ioctl(): %s", + strerror(errno)); + goto err2; + } + + if (numifs >= io.pfiio_size) + break; + + numifs = io.pfiio_size; + } + + for (i = 0; i < numifs; i++) { + e = malloc(sizeof(struct pfi_entry)); + if (e == NULL) + goto err1; + e->index = i + 1; + memcpy(&e->pfi, p+i, sizeof(struct pfi_kif)); + TAILQ_INSERT_TAIL(&pfi_table, e, link); + } + + pfi_table_age = time(NULL); + pfi_table_count = numifs; + pf_tick = this_tick; + + free(p); + return (0); + +err1: + while (!TAILQ_EMPTY(&pfi_table)) { + e = TAILQ_FIRST(&pfi_table); + TAILQ_REMOVE(&pfi_table, e, link); + free(e); + } +err2: + free(p); + return(-1); +} + +static int +pfq_refresh(void) +{ + struct pfioc_altq pa; + struct pfq_entry *e; + int i, numqs, ticket; + + if (started && this_tick <= pf_tick) + return (0); + + while (!TAILQ_EMPTY(&pfq_table)) { + e = TAILQ_FIRST(&pfq_table); + TAILQ_REMOVE(&pfq_table, e, link); + free(e); + } + + bzero(&pa, sizeof(pa)); + + if (ioctl(dev, DIOCGETALTQS, &pa)) { + syslog(LOG_ERR, "pfq_refresh: ioctl(DIOCGETALTQS): %s", + strerror(errno)); + return (-1); + } + + numqs = pa.nr; + ticket = pa.ticket; + + for (i = 0; i < numqs; i++) { + e = malloc(sizeof(struct pfq_entry)); + if (e == NULL) { + syslog(LOG_ERR, "pfq_refresh(): " + "malloc(): %s", + strerror(errno)); + goto err; + } + pa.ticket = ticket; + pa.nr = i; + + if (ioctl(dev, DIOCGETALTQ, &pa)) { + syslog(LOG_ERR, "pfq_refresh(): " + "ioctl(DIOCGETALTQ): %s", + strerror(errno)); + goto err; + } + + if (pa.altq.qid > 0) { + memcpy(&e->altq, &pa.altq, sizeof(struct pf_altq)); + e->index = pa.altq.qid; + pfq_table_count = i; + INSERT_OBJECT_INT_LINK_INDEX(e, &pfq_table, link, index); + } + } + + pfq_table_age = time(NULL); + pf_tick = this_tick; + + return (0); +err: + free(e); + while (!TAILQ_EMPTY(&pfq_table)) { + e = TAILQ_FIRST(&pfq_table); + TAILQ_REMOVE(&pfq_table, e, link); + free(e); + } + return(-1); +} + +static int +pfs_refresh(void) +{ + if (started && this_tick <= pf_tick) + return (0); + + bzero(&pfs, sizeof(struct pf_status)); + + if (ioctl(dev, DIOCGETSTATUS, &pfs)) { + syslog(LOG_ERR, "pfs_refresh(): ioctl(): %s", + strerror(errno)); + return (-1); + } + + pf_tick = this_tick; + return (0); +} + +static int +pft_refresh(void) +{ + struct pfioc_table io; + struct pfr_tstats *t = NULL; + struct pft_entry *e; + int i, numtbls = 1; + + if (started && this_tick <= pf_tick) + return (0); + + while (!TAILQ_EMPTY(&pft_table)) { + e = TAILQ_FIRST(&pft_table); + TAILQ_REMOVE(&pft_table, e, link); + free(e); + } + + bzero(&io, sizeof(io)); + io.pfrio_esize = sizeof(struct pfr_tstats); + + for (;;) { + t = reallocf(t, numtbls * sizeof(struct pfr_tstats)); + if (t == NULL) { + syslog(LOG_ERR, "pft_refresh(): reallocf() numtbls=%d: %s", + numtbls, strerror(errno)); + goto err2; + } + io.pfrio_size = numtbls; + io.pfrio_buffer = t; + + if (ioctl(dev, DIOCRGETTSTATS, &io)) { + syslog(LOG_ERR, "pft_refresh(): ioctl(): %s", + strerror(errno)); + goto err2; + } + + if (numtbls >= io.pfrio_size) + break; + + numtbls = io.pfrio_size; + } + + for (i = 0; i < numtbls; i++) { + e = malloc(sizeof(struct pft_entry)); + if (e == NULL) + goto err1; + e->index = i + 1; + memcpy(&e->pft, t+i, sizeof(struct pfr_tstats)); + TAILQ_INSERT_TAIL(&pft_table, e, link); + } + + pft_table_age = time(NULL); + pft_table_count = numtbls; + pf_tick = this_tick; + + free(t); + return (0); +err1: + while (!TAILQ_EMPTY(&pft_table)) { + e = TAILQ_FIRST(&pft_table); + TAILQ_REMOVE(&pft_table, e, link); + free(e); + } +err2: + free(t); + return(-1); +} + +static int +pfa_table_addrs(u_int sidx, struct pfr_table *pt) +{ + struct pfioc_table io; + struct pfr_astats *t = NULL; + struct pfa_entry *e; + int i, numaddrs = 1; + + if (pt == NULL) + return (-1); + + memset(&io, 0, sizeof(io)); + strlcpy(io.pfrio_table.pfrt_name, pt->pfrt_name, + sizeof(io.pfrio_table.pfrt_name)); + + for (;;) { + t = reallocf(t, numaddrs * sizeof(struct pfr_astats)); + if (t == NULL) { + syslog(LOG_ERR, "pfa_table_addrs(): reallocf(): %s", + strerror(errno)); + numaddrs = -1; + goto error; + } + + memset(t, 0, sizeof(*t)); + io.pfrio_size = numaddrs; + io.pfrio_buffer = t; + io.pfrio_esize = sizeof(struct pfr_astats); + + if (ioctl(dev, DIOCRGETASTATS, &io)) { + syslog(LOG_ERR, "pfa_table_addrs(): ioctl() on %s: %s", + pt->pfrt_name, strerror(errno)); + numaddrs = -1; + break; + } + + if (numaddrs >= io.pfrio_size) + break; + + numaddrs = io.pfrio_size; + } + + for (i = 0; i < numaddrs; i++) { + if ((t + i)->pfras_a.pfra_af != AF_INET && + (t + i)->pfras_a.pfra_af != AF_INET6) { + numaddrs = i; + break; + } + + e = (struct pfa_entry *)malloc(sizeof(struct pfa_entry)); + if (e == NULL) { + syslog(LOG_ERR, "pfa_table_addrs(): malloc(): %s", + strerror(errno)); + numaddrs = -1; + break; + } + e->index = sidx + i; + memcpy(&e->pfas, t + i, sizeof(struct pfr_astats)); + TAILQ_INSERT_TAIL(&pfa_table, e, link); + } + + free(t); +error: + return (numaddrs); +} + +static int +pfa_refresh(void) +{ + struct pfioc_table io; + struct pfr_table *pt = NULL, *it = NULL; + struct pfa_entry *e; + int i, numtbls = 1, cidx, naddrs; + + if (started && this_tick <= pf_tick) + return (0); + + while (!TAILQ_EMPTY(&pfa_table)) { + e = TAILQ_FIRST(&pfa_table); + TAILQ_REMOVE(&pfa_table, e, link); + free(e); + } + + memset(&io, 0, sizeof(io)); + io.pfrio_esize = sizeof(struct pfr_table); + + for (;;) { + pt = reallocf(pt, numtbls * sizeof(struct pfr_table)); + if (pt == NULL) { + syslog(LOG_ERR, "pfa_refresh(): reallocf() %s", + strerror(errno)); + return (-1); + } + memset(pt, 0, sizeof(*pt)); + io.pfrio_size = numtbls; + io.pfrio_buffer = pt; + + if (ioctl(dev, DIOCRGETTABLES, &io)) { + syslog(LOG_ERR, "pfa_refresh(): ioctl(): %s", + strerror(errno)); + goto err2; + } + + if (numtbls >= io.pfrio_size) + break; + + numtbls = io.pfrio_size; + } + + cidx = 1; + + for (it = pt, i = 0; i < numtbls; it++, i++) { + /* + * Skip the table if not active - ioctl(DIOCRGETASTATS) will + * return ESRCH for this entry anyway. + */ + if (!(it->pfrt_flags & PFR_TFLAG_ACTIVE)) + continue; + + if ((naddrs = pfa_table_addrs(cidx, it)) < 0) + goto err1; + + cidx += naddrs; + } + + pfa_table_age = time(NULL); + pfa_table_count = cidx; + pf_tick = this_tick; + + free(pt); + return (0); +err1: + while (!TAILQ_EMPTY(&pfa_table)) { + e = TAILQ_FIRST(&pfa_table); + TAILQ_REMOVE(&pfa_table, e, link); + free(e); + } + +err2: + free(pt); + return (-1); +} + +static int +pfl_scan_ruleset(const char *path) +{ + struct pfioc_rule pr; + struct pfl_entry *e; + u_int32_t nr, i; + + bzero(&pr, sizeof(pr)); + strlcpy(pr.anchor, path, sizeof(pr.anchor)); + pr.rule.action = PF_PASS; + if (ioctl(dev, DIOCGETRULES, &pr)) { + syslog(LOG_ERR, "pfl_scan_ruleset: ioctl(DIOCGETRULES): %s", + strerror(errno)); + goto err; + } + + for (nr = pr.nr, i = 0; i < nr; i++) { + pr.nr = i; + if (ioctl(dev, DIOCGETRULE, &pr)) { + syslog(LOG_ERR, "pfl_scan_ruleset: ioctl(DIOCGETRULE):" + " %s", strerror(errno)); + goto err; + } + + if (pr.rule.label[0]) { + e = (struct pfl_entry *)malloc(sizeof(*e)); + if (e == NULL) + goto err; + + strlcpy(e->name, path, sizeof(e->name)); + if (path[0]) + strlcat(e->name, "/", sizeof(e->name)); + strlcat(e->name, pr.rule.label, sizeof(e->name)); + + e->evals = pr.rule.evaluations; + e->bytes[IN] = pr.rule.bytes[IN]; + e->bytes[OUT] = pr.rule.bytes[OUT]; + e->pkts[IN] = pr.rule.packets[IN]; + e->pkts[OUT] = pr.rule.packets[OUT]; + e->index = ++pfl_table_count; + + TAILQ_INSERT_TAIL(&pfl_table, e, link); + } + } + + return (0); + +err: + return (-1); +} + +static int +pfl_walk_rulesets(const char *path) +{ + struct pfioc_ruleset prs; + char newpath[MAXPATHLEN]; + u_int32_t nr, i; + + if (pfl_scan_ruleset(path)) + goto err; + + bzero(&prs, sizeof(prs)); + strlcpy(prs.path, path, sizeof(prs.path)); + if (ioctl(dev, DIOCGETRULESETS, &prs)) { + syslog(LOG_ERR, "pfl_walk_rulesets: ioctl(DIOCGETRULESETS): %s", + strerror(errno)); + goto err; + } + + for (nr = prs.nr, i = 0; i < nr; i++) { + prs.nr = i; + if (ioctl(dev, DIOCGETRULESET, &prs)) { + syslog(LOG_ERR, "pfl_walk_rulesets: ioctl(DIOCGETRULESET):" + " %s", strerror(errno)); + goto err; + } + + if (strcmp(prs.name, PF_RESERVED_ANCHOR) == 0) + continue; + + strlcpy(newpath, path, sizeof(newpath)); + if (path[0]) + strlcat(newpath, "/", sizeof(newpath)); + + strlcat(newpath, prs.name, sizeof(newpath)); + if (pfl_walk_rulesets(newpath)) + goto err; + } + + return (0); + +err: + return (-1); +} + +static int +pfl_refresh(void) +{ + struct pfl_entry *e; + + if (started && this_tick <= pf_tick) + return (0); + + while (!TAILQ_EMPTY(&pfl_table)) { + e = TAILQ_FIRST(&pfl_table); + TAILQ_REMOVE(&pfl_table, e, link); + free(e); + } + pfl_table_count = 0; + + if (pfl_walk_rulesets("")) + goto err; + + pfl_table_age = time(NULL); + pf_tick = this_tick; + + return (0); + +err: + while (!TAILQ_EMPTY(&pfl_table)) { + e = TAILQ_FIRST(&pfl_table); + TAILQ_REMOVE(&pfl_table, e, link); + free(e); + } + pfl_table_count = 0; + + return (-1); +} + +/* + * check whether altq support is enabled in kernel + */ + +static int +altq_is_enabled(int pfdev) +{ + struct pfioc_altq pa; + + errno = 0; + if (ioctl(pfdev, DIOCGETALTQS, &pa)) { + if (errno == ENODEV) { + syslog(LOG_INFO, "No ALTQ support in kernel\n" + "ALTQ related functions disabled\n"); + return (0); + } else + syslog(LOG_ERR, "DIOCGETALTQS returned an error: %s", + strerror(errno)); + return (-1); + } + return (1); +} + +/* + * Implement the bsnmpd module interface + */ +static int +pf_init(struct lmodule *mod, int __unused argc, char __unused *argv[]) +{ + module = mod; + + if ((dev = open("/dev/pf", O_RDONLY)) == -1) { + syslog(LOG_ERR, "pf_init(): open(): %s\n", + strerror(errno)); + return (-1); + } + + if ((altq_enabled = altq_is_enabled(dev)) == -1) { + syslog(LOG_ERR, "pf_init(): altq test failed"); + return (-1); + } + + /* Prepare internal state */ + TAILQ_INIT(&pfi_table); + TAILQ_INIT(&pfq_table); + TAILQ_INIT(&pft_table); + TAILQ_INIT(&pfa_table); + TAILQ_INIT(&pfl_table); + + pfi_refresh(); + if (altq_enabled) { + pfq_refresh(); + } + + pfs_refresh(); + pft_refresh(); + pfa_refresh(); + pfl_refresh(); + + started = 1; + + return (0); +} + +static int +pf_fini(void) +{ + struct pfi_entry *i1, *i2; + struct pfq_entry *q1, *q2; + struct pft_entry *t1, *t2; + struct pfa_entry *a1, *a2; + struct pfl_entry *l1, *l2; + + /* Empty the list of interfaces */ + i1 = TAILQ_FIRST(&pfi_table); + while (i1 != NULL) { + i2 = TAILQ_NEXT(i1, link); + free(i1); + i1 = i2; + } + + /* List of queues */ + q1 = TAILQ_FIRST(&pfq_table); + while (q1 != NULL) { + q2 = TAILQ_NEXT(q1, link); + free(q1); + q1 = q2; + } + + /* List of tables */ + t1 = TAILQ_FIRST(&pft_table); + while (t1 != NULL) { + t2 = TAILQ_NEXT(t1, link); + free(t1); + t1 = t2; + } + + /* List of table addresses */ + a1 = TAILQ_FIRST(&pfa_table); + while (a1 != NULL) { + a2 = TAILQ_NEXT(a1, link); + free(a1); + a1 = a2; + } + + /* And the list of labeled filter rules */ + l1 = TAILQ_FIRST(&pfl_table); + while (l1 != NULL) { + l2 = TAILQ_NEXT(l1, link); + free(l1); + l1 = l2; + } + + close(dev); + return (0); +} + +static void +pf_dump(void) +{ + pfi_refresh(); + if (altq_enabled) { + pfq_refresh(); + } + pft_refresh(); + pfa_refresh(); + pfl_refresh(); + + syslog(LOG_ERR, "Dump: pfi_table_age = %jd", + (intmax_t)pfi_table_age); + syslog(LOG_ERR, "Dump: pfi_table_count = %d", + pfi_table_count); + + syslog(LOG_ERR, "Dump: pfq_table_age = %jd", + (intmax_t)pfq_table_age); + syslog(LOG_ERR, "Dump: pfq_table_count = %d", + pfq_table_count); + + syslog(LOG_ERR, "Dump: pft_table_age = %jd", + (intmax_t)pft_table_age); + syslog(LOG_ERR, "Dump: pft_table_count = %d", + pft_table_count); + + syslog(LOG_ERR, "Dump: pfa_table_age = %jd", + (intmax_t)pfa_table_age); + syslog(LOG_ERR, "Dump: pfa_table_count = %d", + pfa_table_count); + + syslog(LOG_ERR, "Dump: pfl_table_age = %jd", + (intmax_t)pfl_table_age); + syslog(LOG_ERR, "Dump: pfl_table_count = %d", + pfl_table_count); +} + +const struct snmp_module config = { + .comment = "This module implements a MIB for the pf packet filter.", + .init = pf_init, + .fini = pf_fini, + .tree = pf_ctree, + .dump = pf_dump, + .tree_size = pf_CTREE_SIZE, +}; diff --git a/usr.sbin/bsnmpd/modules/snmp_pf/pf_tree.def b/usr.sbin/bsnmpd/modules/snmp_pf/pf_tree.def new file mode 100644 index 0000000..7b791b3 --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_pf/pf_tree.def @@ -0,0 +1,210 @@ +# +# Copyright (c) 2010 Philip Paeps <philip@FreeBSD.org> +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# + +(1 internet + (4 private + (1 enterprises + (12325 fokus + (1 begemot + (200 begemotPf + (1 begemotPfObjects + (1 pfStatus + (1 pfStatusRunning ENUM ( 1 true 2 false ) pf_status GET) + (2 pfStatusRuntime TIMETICKS pf_status GET) + (3 pfStatusDebug ENUM ( 0 none 1 urgent 2 misc 3 loud ) pf_status GET) + (4 pfStatusHostId OCTETSTRING pf_status GET) + ) + (2 pfCounter + (1 pfCounterMatch COUNTER64 pf_counter GET) + (2 pfCounterBadOffset COUNTER64 pf_counter GET) + (3 pfCounterFragment COUNTER64 pf_counter GET) + (4 pfCounterShort COUNTER64 pf_counter GET) + (5 pfCounterNormalize COUNTER64 pf_counter GET) + (6 pfCounterMemDrop COUNTER64 pf_counter GET) + ) + (3 pfStateTable + (1 pfStateTableCount UNSIGNED32 pf_statetable GET) + (2 pfStateTableSearches COUNTER64 pf_statetable GET) + (3 pfStateTableInserts COUNTER64 pf_statetable GET) + (4 pfStateTableRemovals COUNTER64 pf_statetable GET) + ) + (4 pfSrcNodes + (1 pfSrcNodesCount UNSIGNED32 pf_srcnodes GET) + (2 pfSrcNodesSearches COUNTER64 pf_srcnodes GET) + (3 pfSrcNodesInserts COUNTER64 pf_srcnodes GET) + (4 pfSrcNodesRemovals COUNTER64 pf_srcnodes GET) + ) + (5 pfLimits + (1 pfLimitsStates UNSIGNED32 pf_limits GET) + (2 pfLimitsSrcNodes UNSIGNED32 pf_limits GET) + (3 pfLimitsFrags UNSIGNED32 pf_limits GET) + ) + (6 pfTimeouts + (1 pfTimeoutsTcpFirst INTEGER32 pf_timeouts GET) + (2 pfTimeoutsTcpOpening INTEGER32 pf_timeouts GET) + (3 pfTimeoutsTcpEstablished INTEGER32 pf_timeouts GET) + (4 pfTimeoutsTcpClosing INTEGER32 pf_timeouts GET) + (5 pfTimeoutsTcpFinWait INTEGER32 pf_timeouts GET) + (6 pfTimeoutsTcpClosed INTEGER32 pf_timeouts GET) + (7 pfTimeoutsUdpFirst INTEGER32 pf_timeouts GET) + (8 pfTimeoutsUdpSingle INTEGER32 pf_timeouts GET) + (9 pfTimeoutsUdpMultiple INTEGER32 pf_timeouts GET) + (10 pfTimeoutsIcmpFirst INTEGER32 pf_timeouts GET) + (11 pfTimeoutsIcmpError INTEGER32 pf_timeouts GET) + (12 pfTimeoutsOtherFirst INTEGER32 pf_timeouts GET) + (13 pfTimeoutsOtherSingle INTEGER32 pf_timeouts GET) + (14 pfTimeoutsOtherMultiple INTEGER32 pf_timeouts GET) + (15 pfTimeoutsFragment INTEGER32 pf_timeouts GET) + (16 pfTimeoutsInterval INTEGER32 pf_timeouts GET) + (17 pfTimeoutsAdaptiveStart INTEGER32 pf_timeouts GET) + (18 pfTimeoutsAdaptiveEnd INTEGER32 pf_timeouts GET) + (19 pfTimeoutsSrcNode INTEGER32 pf_timeouts GET) + ) + (7 pfLogInterface + (1 pfLogInterfaceName OCTETSTRING pf_logif GET) + (2 pfLogInterfaceIp4BytesIn COUNTER64 pf_logif GET) + (3 pfLogInterfaceIp4BytesOut COUNTER64 pf_logif GET) + (4 pfLogInterfaceIp4PktsInPass COUNTER64 pf_logif GET) + (5 pfLogInterfaceIp4PktsInDrop COUNTER64 pf_logif GET) + (6 pfLogInterfaceIp4PktsOutPass COUNTER64 pf_logif GET) + (7 pfLogInterfaceIp4PktsOutDrop COUNTER64 pf_logif GET) + (8 pfLogInterfaceIp6BytesIn COUNTER64 pf_logif GET) + (9 pfLogInterfaceIp6BytesOut COUNTER64 pf_logif GET) + (10 pfLogInterfaceIp6PktsInPass COUNTER64 pf_logif GET) + (11 pfLogInterfaceIp6PktsInDrop COUNTER64 pf_logif GET) + (12 pfLogInterfaceIp6PktsOutPass COUNTER64 pf_logif GET) + (13 pfLogInterfaceIp6PktsOutDrop COUNTER64 pf_logif GET) + ) + (8 pfInterfaces + (1 pfInterfacesIfNumber INTEGER32 pf_interfaces GET) + (2 pfInterfacesIfTable + (1 pfInterfacesIfEntry : INTEGER32 pf_iftable + (1 pfInterfacesIfIndex INTEGER32) + (2 pfInterfacesIfDescr OCTETSTRING GET) + (3 pfInterfacesIfType ENUM ( 0 group 1 instance 2 detached ) GET) + (4 pfInterfacesIfTZero TIMETICKS GET) + (5 pfInterfacesIfRefsState UNSIGNED32 GET) + (6 pfInterfacesIfRefsRule UNSIGNED32 GET) + (7 pfInterfacesIf4BytesInPass COUNTER64 GET) + (8 pfInterfacesIf4BytesInBlock COUNTER64 GET) + (9 pfInterfacesIf4BytesOutPass COUNTER64 GET) + (10 pfInterfacesIf4BytesOutBlock COUNTER64 GET) + (11 pfInterfacesIf4PktsInPass COUNTER64 GET) + (12 pfInterfacesIf4PktsInBlock COUNTER64 GET) + (13 pfInterfacesIf4PktsOutPass COUNTER64 GET) + (14 pfInterfacesIf4PktsOutBlock COUNTER64 GET) + (15 pfInterfacesIf6BytesInPass COUNTER64 GET) + (16 pfInterfacesIf6BytesInBlock COUNTER64 GET) + (17 pfInterfacesIf6BytesOutPass COUNTER64 GET) + (18 pfInterfacesIf6BytesOutBlock COUNTER64 GET) + (19 pfInterfacesIf6PktsInPass COUNTER64 GET) + (20 pfInterfacesIf6PktsInBlock COUNTER64 GET) + (21 pfInterfacesIf6PktsOutPass COUNTER64 GET) + (22 pfInterfacesIf6PktsOutBlock COUNTER64 GET) + ) + ) + ) + (9 pfTables + (1 pfTablesTblNumber INTEGER32 pf_tables GET) + (2 pfTablesTblTable + (1 pfTablesTblEntry : INTEGER32 pf_tbltable + (1 pfTablesTblIndex INTEGER32) + (2 pfTablesTblDescr OCTETSTRING GET) + (3 pfTablesTblCount INTEGER32 GET) + (4 pfTablesTblTZero TIMETICKS GET) + (5 pfTablesTblRefsAnchor INTEGER32 GET) + (6 pfTablesTblRefsRule INTEGER32 GET) + (7 pfTablesTblEvalMatch COUNTER64 GET) + (8 pfTablesTblEvalNoMatch COUNTER64 GET) + (9 pfTablesTblBytesInPass COUNTER64 GET) + (10 pfTablesTblBytesInBlock COUNTER64 GET) + (11 pfTablesTblBytesInXPass COUNTER64 GET) + (12 pfTablesTblBytesOutPass COUNTER64 GET) + (13 pfTablesTblBytesOutBlock COUNTER64 GET) + (14 pfTablesTblBytesOutXPass COUNTER64 GET) + (15 pfTablesTblPktsInPass COUNTER64 GET) + (16 pfTablesTblPktsInBlock COUNTER64 GET) + (17 pfTablesTblPktsInXPass COUNTER64 GET) + (18 pfTablesTblPktsOutPass COUNTER64 GET) + (19 pfTablesTblPktsOutBlock COUNTER64 GET) + (20 pfTablesTblPktsOutXPass COUNTER64 GET) + ) + ) + (3 pfTablesAddrTable + (1 pfTablesAddrEntry : INTEGER32 pf_tbladdr + (1 pfTablesAddrIndex INTEGER32) + (2 pfTablesAddrNetType ENUM ( 0 unknown 1 ipv4 2 ipv6) GET) + (3 pfTablesAddrNet OCTETSTRING | InetAddress GET) + (4 pfTablesAddrPrefix UNSIGNED32 GET) + (5 pfTablesAddrTZero TIMETICKS GET) + (6 pfTablesAddrBytesInPass COUNTER64 GET) + (7 pfTablesAddrBytesInBlock COUNTER64 GET) + (8 pfTablesAddrBytesOutPass COUNTER64 GET) + (9 pfTablesAddrBytesOutBlock COUNTER64 GET) + (10 pfTablesAddrPktsInPass COUNTER64 GET) + (11 pfTablesAddrPktsInBlock COUNTER64 GET) + (12 pfTablesAddrPktsOutPass COUNTER64 GET) + (13 pfTablesAddrPktsOutBlock COUNTER64 GET) + ) + ) + ) + (10 pfAltq + (1 pfAltqQueueNumber INTEGER32 pf_altq GET) + (2 pfAltqQueueTable + (1 pfAltqQueueEntry : INTEGER32 pf_altqq + (1 pfAltqQueueIndex INTEGER32) + (2 pfAltqQueueDescr OCTETSTRING GET) + (3 pfAltqQueueParent OCTETSTRING GET) + (4 pfAltqQueueScheduler ENUM ( 1 cbq 8 hfsc 11 priq ) GET) + (5 pfAltqQueueBandwidth UNSIGNED32 GET) + (6 pfAltqQueuePriority INTEGER32 GET) + (7 pfAltqQueueLimit INTEGER32 GET) + ) + ) + ) + (11 pfLabels + (1 pfLabelsLblNumber INTEGER32 pf_labels GET) + (2 pfLabelsLblTable + (1 pfLabelsLblEntry : INTEGER pf_lbltable + (1 pfLabelsLblIndex INTEGER) + (2 pfLabelsLblName OCTETSTRING GET) + (3 pfLabelsLblEvals COUNTER64 GET) + (4 pfLabelsLblBytesIn COUNTER64 GET) + (5 pfLabelsLblBytesOut COUNTER64 GET) + (6 pfLabelsLblPktsIn COUNTER64 GET) + (7 pfLabelsLblPktsOut COUNTER64 GET) + ) + ) + ) + ) + ) + ) + ) + ) + ) +) diff --git a/usr.sbin/bsnmpd/modules/snmp_target/Makefile b/usr.sbin/bsnmpd/modules/snmp_target/Makefile new file mode 100755 index 0000000..10141e5 --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_target/Makefile @@ -0,0 +1,20 @@ +# $FreeBSD$ +# +# Author: Shteryana Shopova <syrinx@freebsd.org> + +CONTRIB= ${.CURDIR}/../../../../contrib/bsnmp +.PATH: ${CONTRIB}/snmp_target + +MOD= target +SRCS= target_snmp.c +XSYM= snmpTargetMIB snmpNotificationMIB snmpUDPDomain + +MAN= snmp_target.3 + +CFLAGS+= -I${CONTRIB}/lib -I${CONTRIB}/snmpd -DSNMPTREE_TYPES +CFLAGS+= -DHAVE_ERR_H -DHAVE_GETADDRINFO -DHAVE_STRLCPY -DHAVE_SYS_TREE_H + +DEFS= ${MOD}_tree.def +BMIBS= + +.include <bsd.snmpmod.mk> diff --git a/usr.sbin/bsnmpd/modules/snmp_usm/Makefile b/usr.sbin/bsnmpd/modules/snmp_usm/Makefile new file mode 100755 index 0000000..4ae818a --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_usm/Makefile @@ -0,0 +1,22 @@ +# $FreeBSD$ +# +# Author: Shteryana Shopova <syrinx@freebsd.org> + +CONTRIB= ${.CURDIR}/../../../../contrib/bsnmp +.PATH: ${CONTRIB}/snmp_usm + +MOD= usm +SRCS= usm_snmp.c +XSYM= snmpUsmMIB usmNoAuthProtocol usmHMACMD5AuthProtocol \ + usmHMACSHAAuthProtocol usmNoPrivProtocol usmDESPrivProtocol \ + usmAesCfb128Protocol usmUserSecurityName + +MAN= snmp_usm.3 + +CFLAGS+= -I${CONTRIB}/lib -I${CONTRIB}/snmpd -DSNMPTREE_TYPES +CFLAGS+= -DHAVE_ERR_H -DHAVE_GETADDRINFO -DHAVE_STRLCPY -DHAVE_SYS_TREE_H + +DEFS= ${MOD}_tree.def +BMIBS= + +.include <bsd.snmpmod.mk> diff --git a/usr.sbin/bsnmpd/modules/snmp_vacm/Makefile b/usr.sbin/bsnmpd/modules/snmp_vacm/Makefile new file mode 100755 index 0000000..1be8d62 --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_vacm/Makefile @@ -0,0 +1,20 @@ +# $FreeBSD$ +# +# Author: Shteryana Shopova <syrinx@freebsd.org> + +CONTRIB= ${.CURDIR}/../../../../contrib/bsnmp +.PATH: ${CONTRIB}/snmp_vacm + +MOD= vacm +SRCS= vacm_snmp.c +XSYM= snmpVacmMIB + +MAN= snmp_vacm.3 + +CFLAGS+= -I${CONTRIB}/lib -I${CONTRIB}/snmpd -DSNMPTREE_TYPES +CFLAGS+= -DHAVE_ERR_H -DHAVE_GETADDRINFO -DHAVE_STRLCPY -DHAVE_SYS_TREE_H + +DEFS= ${MOD}_tree.def +BMIBS= + +.include <bsd.snmpmod.mk> diff --git a/usr.sbin/bsnmpd/modules/snmp_wlan/BEGEMOT-WIRELESS-MIB.txt b/usr.sbin/bsnmpd/modules/snmp_wlan/BEGEMOT-WIRELESS-MIB.txt new file mode 100644 index 0000000..27f5be7 --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_wlan/BEGEMOT-WIRELESS-MIB.txt @@ -0,0 +1,3898 @@ +-- +-- Copyright (C) 2010 The FreeBSD Foundation +-- All rights reserved. +-- +-- This documentation was written by Shteryana Sotirova Shopova under +-- sponsorship from the FreeBSD Foundation. +-- +-- Redistribution and use in source and binary forms, with or without +-- modification, are permitted provided that the following conditions +-- are met: +-- 1. Redistributions of source code must retain the above copyright +-- notice, this list of conditions and the following disclaimer. +-- 2. Redistributions in binary form must reproduce the above copyright +-- notice, this list of conditions and the following disclaimer in the +-- documentation and/or other materials provided with the distribution. +-- +-- THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND +-- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +-- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +-- ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE +-- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +-- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +-- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +-- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +-- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +-- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +-- SUCH DAMAGE. +-- +-- $FreeBSD$ +-- + +BEGEMOT-WIRELESS-MIB DEFINITIONS ::= BEGIN + +IMPORTS + MODULE-IDENTITY, OBJECT-TYPE, NOTIFICATION-TYPE, + Counter32, Integer32, TimeTicks, Unsigned32, mib-2 + FROM SNMPv2-SMI + TEXTUAL-CONVENTION, MacAddress, TruthValue, RowStatus, + DisplayString + FROM SNMPv2-TC + InterfaceIndex, ifIndex FROM IF-MIB + begemot + FROM BEGEMOT-MIB; + +begemotWlan MODULE-IDENTITY + LAST-UPDATED "201005170000Z" + ORGANIZATION "The FreeBSD Foundation" + CONTACT-INFO + " Shteryana Shopova + + Postal: 12 Andrey Lyapchev Blvd. + block 2, ap.19 + 1797 Sofia + Bulgaria + + Fax: N/A + + E-Mail: syrinx@FreeBSD.org" + DESCRIPTION + "The Begemot MIB for managing IEEE802.11 interfaces." + REVISION "201005170000Z" + DESCRIPTION + "Initial revision." + ::= { begemot 210 } + +-- ---------------------------------------------------------- -- +-- Textual conventions +-- ---------------------------------------------------------- -- +WlanMgmtReasonCode ::= TEXTUAL-CONVENTION + STATUS current + DESCRIPTION + "Enumeration of reason and codes used in IEEE802.11 + management frames to indicate why an action took place." + SYNTAX INTEGER { + unspecified(1), + authenticationExpire(2), + authenticationLeave(3), + associationExpire(4), + associationTooMany(5), + notAuthenticated(6), + notAssociated(7), + associationLeave(8), + associationNotAuthenticated(9), +-- XXX: TODO - FIXME + dissassocPwrcapBad(10), + dissassocSuperchanBad(11), + ieInvalid(13), + micFailure(14), + fourWayHandshakeTimeout(15), + groupKeyUpdateTimeout(16), + ieIn4FourWayDiffers(17), + groupCipherInvalid(18), + pairwiseCiherInvalid(19), + akmpInvalid(20), + unsupportedRsnIeVersion(21), + invalidRsnIeCap(22), + dot1xAuthFailed(23), + cipherSuiteRejected(24), + unspeciffiedQos(32), + insufficientBw(33), + tooManyFrames(34), + outsideTxOp(35), + leavingQbss(36), + badMechanism(37), + setupNeeded(38), + timeout(39) + } + +WlanMgmtMeshReasonCode ::= TEXTUAL-CONVENTION + STATUS current + DESCRIPTION + "Enumeration of reason and codes used in IEEE802.11 + mesh routing management frames to indicate why an + action took place." + SYNTAX INTEGER { +-- XXX: TODO - FIXME + peerLinkCancelled(2), + maxPeers(3), + cpViolation(4), + closeRcvd(5), + maxRetries(6), + confirmTimeout(7), + invalidGtk(8), + inconsistentParams(9), + invalidSecurity(10), + perrUnspecified(11), + perrNoFI(12), + perrDestUnreach(13) + } + +WlanMgmtStatusCode ::= TEXTUAL-CONVENTION + STATUS current + DESCRIPTION + "Enumeration of reason and codes used in IEEE802.11 + management frames to indicate what the result of an + operation is." + SYNTAX INTEGER { +-- XXX: TODO - FIXME + success(0), + unspecified(1), + capabilitiesInfo(10), + notAssociated(11), + other(12), + algorithm(13), + sequence(14), + challenge(15), + timeout(16), + tooMany(17), + basicRate(18), + spRequired(19), + pbccRequired(20), + caRequired(21), + specMgmtRequired(22), + pwrcapRequire(23), + superchanRequired(24), + shortSlotRequired(25), + dssofdmRequired(26), + missingHTCaps(27), + invalidIE(40), + groupCipherInvalid(41), + pairwiseCipherInvalid(42), + akmpInvalid(43), + unsupportedRsnIEVersion(44), + invalidRsnIECap(45), + cipherSuiteRejected(46) + } + +WlanRegDomainCode ::= TEXTUAL-CONVENTION + STATUS current + DESCRIPTION + "Enumeration of regdomain codes." + SYNTAX INTEGER { + fcc(1), + ca(2), + etsi(3), + etsi2(4), + etsi3(5), + fcc3(6), + japan(7), + korea(8), + apac(9), + apac2(10), + apac3(11), + row(12), + none(13), + debug(14), + sr9(15), + xr9(16), + gz901(17) + } + +WlanIfaceDot11nPduType ::= TEXTUAL-CONVENTION + STATUS current + DESCRIPTION + "Enumeration of values for PDU transmit/receive enabled." + SYNTAX INTEGER { + disabled(0), + rxOnly(1), + txOnly(2), + txAndRx(3) + } + +WlanPeerCapabilityFlags ::= TEXTUAL-CONVENTION + STATUS current + DESCRIPTION + "A list of capability bits that may be advertised by a peer." + SYNTAX BITS { + ess(1), + ibss(2), + cfPollable(3), + cfPollRequest(4), + privacy(5), + shortPreamble(6), + pbcc(7), + channelAgility(8), + shortSlotTime(9), + rsn(10), + dsssofdm(11) + } + +WlanIfPhyMode ::= TEXTUAL-CONVENTION + STATUS current + DESCRIPTION + "A list of wireless PHY operating modes." + SYNTAX INTEGER { + auto(1), + dot11a(2), + dot11b(3), + dot11g(4), + fh(5), + turboA(6), + turboG(7), + sturboA(8), + dot11na(9), + dot11ng(10), + ofdmHalf(11), + ofdmQuarter(12) + } + +-- ---------------------------------------------------------- -- +-- Subtrees in the Begemot Wireless MIB +-- ---------------------------------------------------------- -- +begemotWlanNotifications OBJECT IDENTIFIER ::= { begemotWlan 0 } + +begemotWlanInterface OBJECT IDENTIFIER ::= { begemotWlan 1 } + +begemotWlanScanning OBJECT IDENTIFIER ::= { begemotWlan 2 } + +begemotWlanStatistics OBJECT IDENTIFIER ::= { begemotWlan 3 } + +begemotWlanWep OBJECT IDENTIFIER ::= { begemotWlan 4 } + +begemotWlanMACAccessControl OBJECT IDENTIFIER ::= { begemotWlan 5 } + +begemotWlanMeshRouting OBJECT IDENTIFIER ::= { begemotWlan 6 } + +-- ---------------------------------------------------------- -- +-- begemotWlanMultimedia OBJECT IDENTIFIER ::= { begemotWlan 7 } +-- ---------------------------------------------------------- -- + +-- ---------------------------------------------------------- -- +-- Cloned wireless interfaces' database +-- ---------------------------------------------------------- -- +wlanInterfaceTable OBJECT-TYPE + SYNTAX SEQUENCE OF WlanInterfaceEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A table that contains the list of cloned wireless + interfaces created on the system." + ::= { begemotWlanInterface 1 } + +wlanInterfaceEntry OBJECT-TYPE + SYNTAX WlanInterfaceEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Information for a cloned wireless interface." + INDEX { wlanIfaceName } + ::= { wlanInterfaceTable 1 } + +WlanInterfaceEntry ::= SEQUENCE { + wlanIfaceIndex InterfaceIndex, + wlanIfaceName DisplayString, + wlanParentIfName DisplayString, + wlanIfaceOperatingMode INTEGER, + wlanIfaceFlags BITS, + wlanIfaceBssid MacAddress, + wlanIfaceLocalAddress MacAddress, + wlanIfaceStatus RowStatus, + wlanIfaceState INTEGER +} + +wlanIfaceIndex OBJECT-TYPE + SYNTAX InterfaceIndex + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The ifIndex of this cloned wireless interface." + ::= { wlanInterfaceEntry 1 } + +wlanIfaceName OBJECT-TYPE + SYNTAX DisplayString (SIZE(1..32)) + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The name of this cloned wireless interface." + ::= { wlanInterfaceEntry 2 } + +wlanParentIfName OBJECT-TYPE + SYNTAX DisplayString (SIZE(1..32)) + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The name of this cloned wireless interface's parent hardware + interface." + ::= { wlanInterfaceEntry 3 } + +wlanIfaceOperatingMode OBJECT-TYPE + SYNTAX INTEGER { + ibss(0), + station(1), + wds(2), + adhocDemo(3), + hostAp(4), + monitor(5), + meshPoint(6), + tdma(7) + } + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The desired operating mode of the cloned wireless interface." + DEFVAL { station } + ::= { wlanInterfaceEntry 4 } + +wlanIfaceFlags OBJECT-TYPE + SYNTAX BITS { + uniqueBssid(1), + noBeacons(2), + wdsLegacy(3) + } + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "Flags per cloned wireless interface used during creation." + ::= { wlanInterfaceEntry 5 } + +wlanIfaceBssid OBJECT-TYPE + SYNTAX MacAddress + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The BSSID assigned to a cloned wireless interface operating in + WDS mode." + ::= { wlanInterfaceEntry 6 } + +wlanIfaceLocalAddress OBJECT-TYPE + SYNTAX MacAddress + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The unique local MAC address assigned to the cloned wireless + interface during creation." + ::= { wlanInterfaceEntry 7 } + +wlanIfaceStatus OBJECT-TYPE + SYNTAX RowStatus + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "This column allows creation or deletion of cloned wireless + interfaces." + ::= { wlanInterfaceEntry 8 } + +wlanIfaceState OBJECT-TYPE + SYNTAX INTEGER { + up(1), + down(2) + } + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The operating state of the interface." + ::= { wlanInterfaceEntry 9 } + +wlanIfParentTable OBJECT-TYPE + SYNTAX SEQUENCE OF WlanIfParentEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A table that contains information about the parent hardware + interface of every cloned wireless interface in the system." + ::= { begemotWlanInterface 2 } + +wlanIfParentEntry OBJECT-TYPE + SYNTAX WlanIfParentEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Information for the parent hardware interface of a cloned + wireless interface." + AUGMENTS { wlanInterfaceEntry } + ::= { wlanIfParentTable 1 } + +WlanIfParentEntry ::= SEQUENCE { + wlanIfParentDriverCapabilities BITS, + wlanIfParentCryptoCapabilities BITS, + wlanIfParentHTCapabilities BITS +} + +wlanIfParentDriverCapabilities OBJECT-TYPE + SYNTAX BITS { + station(1), + ieee8023encap(2), + athFastFrames(3), + athTurbo(4), + ibss(5), + pmgt(6), + hostAp(7), + ahDemo(8), + swRetry(9), + txPmgt(10), + shortSlot(11), + shortPreamble(12), + monitor(13), + dfs(14), + mbss(15), + wpa1(16), + wpa2(17), + burst(18), + wme(19), + wds(20), + bgScan(21), + txFrag(22), + tdma(23) + } + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The driver capabilities of this cloned interface's parent." + ::= { wlanIfParentEntry 1 } + +wlanIfParentCryptoCapabilities OBJECT-TYPE + SYNTAX BITS { + wep(1), + tkip(2), + aes(3), + aesCcm(4), + tkipMic(5), + ckip(6) + } + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The hardware cryptographic capabilities of this cloned + interface's parent." + ::= { wlanIfParentEntry 2 } + +wlanIfParentHTCapabilities OBJECT-TYPE + SYNTAX BITS { + ldpc(1), + chwidth40(2), + greenField(3), + shortGi20(4), + shortGi40(5), + txStbc(6), + delba(7), + amsdu7935(8), + dssscck40(9), + psmp(10), + fortyMHzIntolerant(11), + lsigTxOpProt(12), + htcAmpdu(13), + htcAmsdu(14), + htcHt(15), + htcSmps(16), + htcRifs(17) + } + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The hardware High Throughput capabilities of this cloned + interface's parent." + ::= { wlanIfParentEntry 3 } + +wlanIfaceConfigTable OBJECT-TYPE + SYNTAX SEQUENCE OF WlanIfaceConfigEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A table that contains a list of configuration parameters per + cloned wireless interface. Some of the parameters may not be + applicable depending on the underlying device's hardware + capabilities and operating mode of the virtual interface." + ::= { begemotWlanInterface 3 } + +wlanIfaceConfigEntry OBJECT-TYPE + SYNTAX WlanIfaceConfigEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A list of configuration parameters for a cloned wireless interface." + AUGMENTS { wlanInterfaceEntry } + ::= { wlanIfaceConfigTable 1 } + +WlanIfaceConfigEntry ::= SEQUENCE { + wlanIfacePacketBurst TruthValue, + wlanIfaceCountryCode OCTET STRING, + wlanIfaceRegDomain INTEGER, + wlanIfaceDesiredSsid OCTET STRING, + wlanIfaceDesiredChannel INTEGER, + wlanIfaceDynamicFreqSelection TruthValue, + wlanIfaceFastFrames TruthValue, + wlanIfaceDturbo TruthValue, + wlanIfaceTxPower INTEGER, + wlanIfaceFragmentThreshold INTEGER, + wlanIfaceRTSThreshold INTEGER, + wlanIfaceWlanPrivacySubscribe TruthValue, +-- Parameters for station mode + wlanIfaceBgScan TruthValue, + wlanIfaceBgScanIdle INTEGER, + wlanIfaceBgScanInterval INTEGER, + wlanIfaceBeaconMissedThreshold INTEGER, + wlanIfaceDesiredBssid MacAddress, + wlanIfaceRoamingMode INTEGER, +-- Additional parameters when operating in host-ap/ad-hoc mode + wlanIfaceDot11d TruthValue, + wlanIfaceDot11h TruthValue, + wlanIfaceDynamicWds TruthValue, + wlanIfacePowerSave TruthValue, + wlanIfaceApBridge TruthValue, + wlanIfaceBeaconInterval INTEGER, + wlanIfaceDtimPeriod INTEGER, + wlanIfaceHideSsid TruthValue, + wlanIfaceInactivityProccess TruthValue, + wlanIfaceDot11gProtMode INTEGER, + wlanIfaceDot11gPureMode TruthValue, + wlanIfaceDot11nPureMode TruthValue, + wlanIfaceDot11nAmpdu INTEGER, + wlanIfaceDot11nAmpduDensity INTEGER, + wlanIfaceDot11nAmpduLimit INTEGER, + wlanIfaceDot11nAmsdu INTEGER, + wlanIfaceDot11nAmsduLimit INTEGER, + wlanIfaceDot11nHighThroughput TruthValue, + wlanIfaceDot11nHTCompatible TruthValue, + wlanIfaceDot11nHTProtMode INTEGER, + wlanIfaceDot11nRIFS TruthValue, + wlanIfaceDot11nShortGI TruthValue, + wlanIfaceDot11nSMPSMode INTEGER, +-- Parameters when operating in tdma mode + wlanIfaceTdmaSlot INTEGER, + wlanIfaceTdmaSlotCount INTEGER, + wlanIfaceTdmaSlotLength INTEGER, + wlanIfaceTdmaBeaconInterval INTEGER +} + +wlanIfacePacketBurst OBJECT-TYPE + SYNTAX TruthValue + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The value of this object controls whether packet bursting is + enabled on the interface." + ::= { wlanIfaceConfigEntry 1 } + +wlanIfaceCountryCode OBJECT-TYPE + SYNTAX OCTET STRING (SIZE(3)) + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The value of this object controls the country regulatory + constraints for operation of this wireless interface. The first + two octets of this string is the two character country code as + described in ISO/IEC 3166-1. The third octet shall contain one + of the following: + + 1. an ASCII space character, if the regulations under which the + interface is operating include all environments in the specified + country. + + 2. an ASCII 'O' character, if the country's regulations are for + Outdoor environment only. + + 3. an ASCII 'I' character, if the country's regulations are for + Indoor environment only." + ::= { wlanIfaceConfigEntry 2 } + +wlanIfaceRegDomain OBJECT-TYPE + SYNTAX WlanRegDomainCode + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "This object specifies the regulatory domain to use when calculating + the regulatory constraints for operation of the interface." + ::= { wlanIfaceConfigEntry 3 } + +wlanIfaceDesiredSsid OBJECT-TYPE + SYNTAX OCTET STRING (SIZE(0..32)) + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The desired SSID for the interface as an ASCII string. + Specifying an empty string shall remove the current configured + SSID." + ::= { wlanIfaceConfigEntry 4 } + +wlanIfaceDesiredChannel OBJECT-TYPE + SYNTAX INTEGER + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The desired operating channel for this interface. The value of this + column is the channel index (wlanIfaceChannelId) of the corresponding + entry from the wlanIfaceChannelTable. The interface status must be + down so that the current operating channel may be set properly." + ::= { wlanIfaceConfigEntry 5 } + +wlanIfaceDynamicFreqSelection OBJECT-TYPE + SYNTAX TruthValue + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The value of this object specifies whether Dynamic Frequency + Selection (DFS) as specified in 802.11h is enabled on an + interface that supports 802.11h and DFS." + DEFVAL { false } + ::= { wlanIfaceConfigEntry 6 } + +wlanIfaceFastFrames OBJECT-TYPE + SYNTAX TruthValue + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The value of this object controls whether use of Atheros Fast + Frames is enabled when when communicating with another Fast + Frames-capable station. The value is only meaningful for + interfaces that support Atheros Fast Frames." + ::= { wlanIfaceConfigEntry 7 } + +wlanIfaceDturbo OBJECT-TYPE + SYNTAX TruthValue + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The value of this object controls whether use of Atheros Dynamic + Turbo mode is enabled when when communicating with another Dynamic + Turbo-capable station. The value is only meaningful for interfaces + that support Atheros Dynamic Turbo mode." + ::= { wlanIfaceConfigEntry 8 } + +wlanIfaceTxPower OBJECT-TYPE + SYNTAX INTEGER + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The value of this object controls the power used to transmit + frames. Accepted values are in units of one tenths of a dBm in + steps of .5 dBm, e.g setting the value of this object to 155 + results in 15.5 dBm transmit power configured on the interface." + ::= { wlanIfaceConfigEntry 9 } + +wlanIfaceFragmentThreshold OBJECT-TYPE + SYNTAX INTEGER (256..2346) + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The value of this object controls the threshold for which + transmitted frames are broken into fragments. Setting the value + of this object to 2346 will disable transmit fragmentation." + DEFVAL { 2346 } + ::= { wlanIfaceConfigEntry 10 } + +wlanIfaceRTSThreshold OBJECT-TYPE + SYNTAX INTEGER (1..2346) + UNITS "bytes" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The value of this object controls the threshold for which + transmitted frames are preceded by transmission of an RTS + control frame. Setting the value of this object to 2346 will + disable transmission of RTS frames." + DEFVAL { 2346 } + ::= { wlanIfaceConfigEntry 11 } + +wlanIfaceWlanPrivacySubscribe OBJECT-TYPE + SYNTAX TruthValue + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The value of this object specifies whether Wireless Privacy + Subscriber support is enabled on the interface." + ::= { wlanIfaceConfigEntry 12 } + +wlanIfaceBgScan OBJECT-TYPE + SYNTAX TruthValue + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The value of this object specifies whether background scanning + is enabled for an interface operating in station mode." + ::= { wlanIfaceConfigEntry 13 } + +wlanIfaceBgScanIdle OBJECT-TYPE + SYNTAX INTEGER + UNITS "milliseconds" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The value of this object specifies the minimum time a station must + be idle before a background scan is initiated on an interface + operating in station mode." + DEFVAL { 250 } + ::= { wlanIfaceConfigEntry 14 } + +wlanIfaceBgScanInterval OBJECT-TYPE + SYNTAX INTEGER + UNITS "seconds" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The value of this object specifies the interval at which background + scanning is attempted when operating in station mode." + DEFVAL { 300 } + ::= { wlanIfaceConfigEntry 15 } + +wlanIfaceBeaconMissedThreshold OBJECT-TYPE + SYNTAX INTEGER (1..255) + UNITS "frames" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The value of this object specifies the number of consecutive missed + beacons before an interface operating in station mode will attempt + to search for a new access point." + DEFVAL { 7 } + ::= { wlanIfaceConfigEntry 16 } + +wlanIfaceDesiredBssid OBJECT-TYPE + SYNTAX MacAddress + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The value of this object specifies the MAC address of the desired + access point to use when an interface is operating as a station." + ::= { wlanIfaceConfigEntry 17 } + +wlanIfaceRoamingMode OBJECT-TYPE + SYNTAX INTEGER { + device(1), + auto(2), + manual(3) + } + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The value of this object specifies the desired system behavior + when the interface is operating as a station and the communication + with the current access point is broken." + DEFVAL { auto } + ::= { wlanIfaceConfigEntry 18 } + +wlanIfaceDot11d OBJECT-TYPE + SYNTAX TruthValue + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The value of this object specifies whether 802.11d specification + support is enabled." + DEFVAL { false } + ::= { wlanIfaceConfigEntry 19 } + +wlanIfaceDot11h OBJECT-TYPE + SYNTAX TruthValue + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The value of this object specifies whether 802.11h support + including spectrum management is enabled. The value is only + meaningfull for interfaces that support 802.11h specification." + DEFVAL { false } + ::= { wlanIfaceConfigEntry 20 } + +wlanIfaceDynamicWds OBJECT-TYPE + SYNTAX TruthValue + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The value of this object specifies whether Dynamic WDS (DWDS) + support is enabled. The value is only meaningful for interfaces + that support Dynamic WDS." + ::= { wlanIfaceConfigEntry 21 } + +wlanIfacePowerSave OBJECT-TYPE + SYNTAX TruthValue + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The value of this object specifies whether powersave operation + is enabled. The value is only meaningful for interfaces that + support powersave operation." + ::= { wlanIfaceConfigEntry 22 } + +wlanIfaceApBridge OBJECT-TYPE + SYNTAX TruthValue + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The value of this object specifies whether packets between + wireless clients will be passed directly by an interface + operating in host ap mode. Disabling it may be useful in + situations when traffic between wireless clients needs to be + processed with packet filtering." + DEFVAL { true } + ::= { wlanIfaceConfigEntry 23 } + +wlanIfaceBeaconInterval OBJECT-TYPE + SYNTAX INTEGER (25..1000) + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The value of this object specifies the interval at with beacon + frames are sent when an interface is operating in ad-hoc or ap + mode. The beacon interval is specified in TU's (1024 usecs)." + DEFVAL { 100 } + ::= { wlanIfaceConfigEntry 24 } + +wlanIfaceDtimPeriod OBJECT-TYPE + SYNTAX INTEGER (1..15) + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The value of this object specifies the DTIM period at which + buffered multicast data frames are transmitted by an interface + operating in host ap mode. Its value indicates the number of + beacon intervals between DTIM." + DEFVAL { 1 } + ::= { wlanIfaceConfigEntry 25 } + +wlanIfaceHideSsid OBJECT-TYPE + SYNTAX TruthValue + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The value of this object specifies whether broadcasting of the + SSID in beacon frames and responding to undirected probe request + frames is enabled for an interface operating in ap mode." + DEFVAL { false } + ::= { wlanIfaceConfigEntry 26 } + +wlanIfaceInactivityProccess OBJECT-TYPE + SYNTAX TruthValue + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The value of this object specifies whether inactivity processing + for associated station on an interface operating in ap mode is + enabled." + DEFVAL { true } + ::= { wlanIfaceConfigEntry 27 } + +wlanIfaceDot11gProtMode OBJECT-TYPE + SYNTAX INTEGER { + off(1), + cts(2), + rtscts(3) + } + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The value of this object specifies the technique used for + protecting OFDM frames in a mixed 11b/11g network." + ::= { wlanIfaceConfigEntry 28 } + +wlanIfaceDot11gPureMode OBJECT-TYPE + SYNTAX TruthValue + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The value of this object specifies whether only 802.11g-capable + stations will be allowed to associate to an interface operating + as access point in 802.11g mode." + ::= { wlanIfaceConfigEntry 29 } + +wlanIfaceDot11nPureMode OBJECT-TYPE + SYNTAX TruthValue + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The value of this object specifies whether only HT-capable + stations will be allowed to associate to an interface operating + as access point in 802.11n mode." + ::= { wlanIfaceConfigEntry 30 } + +wlanIfaceDot11nAmpdu OBJECT-TYPE + SYNTAX WlanIfaceDot11nPduType + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The value of this object specifies whether sending and + receiving of AMPDU frames is enabled on an interface + operating in 802.11n mode." + DEFVAL { txAndRx } + ::= { wlanIfaceConfigEntry 31 } + +wlanIfaceDot11nAmpduDensity OBJECT-TYPE + SYNTAX INTEGER (0|25|50|100|200|400|800|1600) + UNITS "1/100ths-of-microsecond" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The value of this object specifies the AMPDU density parameter + for an interface operating in 802.11n mode." + ::= { wlanIfaceConfigEntry 32 } + +wlanIfaceDot11nAmpduLimit OBJECT-TYPE + SYNTAX INTEGER (8192|16384|32768|65536) + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The value of this object specifies a limit on the AMPDU packet + size for receiving AMPDU frames for an interface operating in + 802.11n mode." + ::= { wlanIfaceConfigEntry 33 } + +wlanIfaceDot11nAmsdu OBJECT-TYPE + SYNTAX WlanIfaceDot11nPduType + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The value of this object specifies whether sending and receiving + of AMSDU frames is enabled on an interface operating in 802.11n + mode." + DEFVAL { rxOnly } + ::= { wlanIfaceConfigEntry 34 } + +wlanIfaceDot11nAmsduLimit OBJECT-TYPE + SYNTAX INTEGER (3839|7935) + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The value of this object specifies a limit on the AMSDU packet + size when sending and receiving AMSDU frames for an interface + operating in 802.11n mode." + ::= { wlanIfaceConfigEntry 35 } + +wlanIfaceDot11nHighThroughput OBJECT-TYPE + SYNTAX TruthValue + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The value of this object specifies whether use of High Throughput + (HT) is enabled for an interface operating in 802.11n mode." + DEFVAL { true } + ::= { wlanIfaceConfigEntry 36 } + +wlanIfaceDot11nHTCompatible OBJECT-TYPE + SYNTAX TruthValue + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The value of this object specifies whether use of compatibility + support for pre-802.11n devices is enabled for an interface + operating in 802.11n mode." + DEFVAL { true } + ::= { wlanIfaceConfigEntry 37 } + +wlanIfaceDot11nHTProtMode OBJECT-TYPE + SYNTAX INTEGER { + off(1), + rts(2) + } + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The value of this object specifies the technique used for + protecting HT frames in a mixed legacy/HT network for interfaces + operating in 802.11n mode." + DEFVAL { rts } + ::= { wlanIfaceConfigEntry 38 } + +wlanIfaceDot11nRIFS OBJECT-TYPE + SYNTAX TruthValue + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The value of this object specifies whether Reduced InterFrame + Spacing (RIFS) is enabled for an interface operating in 802.11n + mode on an HT channel." + ::= { wlanIfaceConfigEntry 39 } + +wlanIfaceDot11nShortGI OBJECT-TYPE + SYNTAX TruthValue + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The value of this object specifies whether use of Short Guard + Interval is enabled on an interface operating in 802.11n mode + on an HT channel." + ::= { wlanIfaceConfigEntry 40 } + +wlanIfaceDot11nSMPSMode OBJECT-TYPE + SYNTAX INTEGER { + disabled(1), + static(2), + dynamic(3) + } + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The value of this object specifies whether use of Spatial Multiplexing + Power Save (SMPS) is enabled on an interface operating in 802.11n mode + and whether SMPS mode is set to Static or Dynamic. The value is only + meaningfull for interfaces that support SMPS." + ::= { wlanIfaceConfigEntry 41 } + +wlanIfaceTdmaSlot OBJECT-TYPE + SYNTAX INTEGER (0..2) + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The value of this object specifies the slot configuration to use + when operating in TDMA mode." + ::= { wlanIfaceConfigEntry 42 } + +wlanIfaceTdmaSlotCount OBJECT-TYPE + SYNTAX INTEGER (0..2) + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The value of this object specifies the number of slots to use to + setup a BSS for an interface operating in TDMA mode." + ::= { wlanIfaceConfigEntry 43 } + +wlanIfaceTdmaSlotLength OBJECT-TYPE + SYNTAX INTEGER (150..65000) + UNITS "microseconds" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The value of this object specifies a slot length that each station + has when a BSS is setup by an interface operating in TDMA mode." + DEFVAL { 10000 } + ::= { wlanIfaceConfigEntry 44 } + +wlanIfaceTdmaBeaconInterval OBJECT-TYPE + SYNTAX INTEGER + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The value of this object specifies the number of superframes at + which a beacon frame is sent to synchronize the TDMA slot timing + for interfaces operating in TDMA mode." + DEFVAL { 5 } + ::= { wlanIfaceConfigEntry 45 } + +wlanIfacePeerTable OBJECT-TYPE + SYNTAX SEQUENCE OF WlanIfacePeerEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A table that contains information about the associated stations + for an interface operating as an access point, or the stations + identified as neighbors in the IBSS for an interface operating in + adhoc mode." + ::= { begemotWlanInterface 4 } + +wlanIfacePeerEntry OBJECT-TYPE + SYNTAX WlanIfacePeerEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "An entry that contains information for the associated stations + for an interface operating as an access point, or the neighboring + stations of an interface in adhoc mode." + INDEX { wlanIfaceName, wlanIfacePeerAddress } + ::= { wlanIfacePeerTable 1 } + +WlanIfacePeerEntry ::= SEQUENCE { + wlanIfacePeerAddress MacAddress, + wlanIfacePeerAssociationId INTEGER, + wlanIfacePeerVlanTag INTEGER, + wlanIfacePeerFrequency INTEGER, + wlanIfacePeerCurrentTXRate INTEGER, + wlanIfacePeerRxSignalStrength INTEGER, + wlanIfacePeerIdleTimer INTEGER, + wlanIfacePeerTxSequenceNo INTEGER, + wlanIfacePeerRxSequenceNo INTEGER, + wlanIfacePeerTxPower INTEGER, + wlanIfacePeerCapabilities BITS, + wlanIfacePeerFlags BITS +} + +wlanIfacePeerAddress OBJECT-TYPE + SYNTAX MacAddress + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The MAC address of this peer." + ::= { wlanIfacePeerEntry 1 } + +wlanIfacePeerAssociationId OBJECT-TYPE + SYNTAX INTEGER + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The ID of the associacition with this peer." + ::= { wlanIfacePeerEntry 2 } + +wlanIfacePeerVlanTag OBJECT-TYPE + SYNTAX INTEGER (0..4096) + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The Vlan Tag for traffic to/from this peer." + ::= { wlanIfacePeerEntry 3 } + +wlanIfacePeerFrequency OBJECT-TYPE + SYNTAX INTEGER + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The operating frequency for the link with this peer." + ::= { wlanIfacePeerEntry 4 } + +wlanIfacePeerCurrentTXRate OBJECT-TYPE + SYNTAX INTEGER + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The current transmit rate for this peer." + ::= { wlanIfacePeerEntry 5 } + +wlanIfacePeerRxSignalStrength OBJECT-TYPE + SYNTAX INTEGER + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The average receive signal strength for this peer." + ::= { wlanIfacePeerEntry 6 } + +wlanIfacePeerIdleTimer OBJECT-TYPE + SYNTAX INTEGER + UNITS "seconds" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The value of this peer's inactivity timer." + ::= { wlanIfacePeerEntry 7 } + +wlanIfacePeerTxSequenceNo OBJECT-TYPE + SYNTAX INTEGER + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The last sequence number transmitted to this peer." + ::= { wlanIfacePeerEntry 8 } + +wlanIfacePeerRxSequenceNo OBJECT-TYPE + SYNTAX INTEGER + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The last sequence number received from this peer." + ::= { wlanIfacePeerEntry 9 } + +wlanIfacePeerTxPower OBJECT-TYPE + SYNTAX INTEGER + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The transmit power limit for this peer." + ::= { wlanIfacePeerEntry 10 } + +wlanIfacePeerCapabilities OBJECT-TYPE + SYNTAX WlanPeerCapabilityFlags + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The capabilities advertised by this peer." + ::= { wlanIfacePeerEntry 11 } + +wlanIfacePeerFlags OBJECT-TYPE + SYNTAX BITS { + authorizedForData(1), + qosEnabled(2), + erpEnabled(3), + powerSaveMode(4), + authRefHeld(5), + htEnabled(6), + htCompat(7), + wpsAssoc(8), + tsnAssoc(9), + ampduRx(10), + ampduTx(11), + mimoPowerSave(12), + sendRts(13), + rifs(14), + shortGiHT20(15), + shortGiHT40(16), + amsduRx(17), + amsduTx(18) + } + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The peer state flags." + ::= { wlanIfacePeerEntry 12 } + +wlanIfaceChannelTable OBJECT-TYPE + SYNTAX SEQUENCE OF WlanIfaceChannelEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A table that contains information about the active channels for + the cloned wireless interfaces in the system." + ::= { begemotWlanInterface 5 } + +wlanIfaceChannelEntry OBJECT-TYPE + SYNTAX WlanIfaceChannelEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "An entry that contains active channel information for the wireless + interface." + INDEX { wlanIfaceName, wlanIfaceChannelId } + ::= { wlanIfaceChannelTable 1 } + +WlanIfaceChannelEntry ::= SEQUENCE { + wlanIfaceChannelId INTEGER, + wlanIfaceChannelIeeeId INTEGER, + wlanIfaceChannelType INTEGER, + wlanIfaceChannelFlags BITS, + wlanIfaceChannelFrequency INTEGER, + wlanIfaceChannelMaxRegPower INTEGER, + wlanIfaceChannelMaxTxPower INTEGER, + wlanIfaceChannelMinTxPower INTEGER, + wlanIfaceChannelState BITS, + wlanIfaceChannelHTExtension INTEGER, + wlanIfaceChannelMaxAntennaGain INTEGER +} + +wlanIfaceChannelId OBJECT-TYPE + SYNTAX INTEGER (1..1536) + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The index of this channel entry." + ::= { wlanIfaceChannelEntry 1 } + +wlanIfaceChannelIeeeId OBJECT-TYPE + SYNTAX INTEGER (1..256) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The channel IEEE ID." + ::= { wlanIfaceChannelEntry 2 } + +wlanIfaceChannelType OBJECT-TYPE + SYNTAX INTEGER { + fhss(1), + dot11a(2), + dot11b(3), + dot11g(4), + tenMHz(5), + fiveMHz(6), + turbo(7), + ht(8) + } + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The operating channel type for this entry." + ::= { wlanIfaceChannelEntry 3 } + +wlanIfaceChannelFlags OBJECT-TYPE + SYNTAX BITS { + turbo(1), + cck(2), + ofdm(3), + spectrum2Ghz(4), + spectrum5Ghz(5), + passiveScan(6), + dynamicCckOfdm(7), + gfsk(8), + spectrum900Mhz(9), + dot11aStaticTurbo(10), + halfRate(11), + quarterRate(12), + ht20(13), + ht40u(14), + ht40d(15), + dfs(16), + xmit4ms(17), + noAdhoc(18), + noHostAp(19), + dot11d(20) + } + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The channel flags." + ::= { wlanIfaceChannelEntry 4 } + +wlanIfaceChannelFrequency OBJECT-TYPE + SYNTAX INTEGER + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The channel frequency setting in MHz." + ::= { wlanIfaceChannelEntry 5 } + +wlanIfaceChannelMaxRegPower OBJECT-TYPE + SYNTAX INTEGER + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The maximum regulatory tx power in dBm for this channel." + ::= { wlanIfaceChannelEntry 6 } + +wlanIfaceChannelMaxTxPower OBJECT-TYPE + SYNTAX INTEGER + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The maximum tx power in units of .5 dBm for this channel." + ::= { wlanIfaceChannelEntry 7 } + +wlanIfaceChannelMinTxPower OBJECT-TYPE + SYNTAX INTEGER + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The minimum tx power in units of .5 dBm for this channel." + ::= { wlanIfaceChannelEntry 8 } + +wlanIfaceChannelState OBJECT-TYPE + SYNTAX BITS { + radar(1), + cacDone(2), + interferenceDetected(3), + radarClear(4) + } + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The channel dynamic state." + ::= { wlanIfaceChannelEntry 9 } + +wlanIfaceChannelHTExtension OBJECT-TYPE + SYNTAX INTEGER + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The HT40 extension channel number." + ::= { wlanIfaceChannelEntry 10 } + +wlanIfaceChannelMaxAntennaGain OBJECT-TYPE + SYNTAX INTEGER + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The maximum antenna gain in units of .5 dBm." + ::= { wlanIfaceChannelEntry 11 } + +-- ---------------------------------------------------------- -- +-- The Scan requests/results for cloned wireless interfaces +-- ---------------------------------------------------------- -- + +wlanScanConfigTable OBJECT-TYPE + SYNTAX SEQUENCE OF WlanScanConfigEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A table that contains a configuration for channel scanning + initiated via SNMP." + ::= { begemotWlanScanning 1 } + +wlanScanConfigEntry OBJECT-TYPE + SYNTAX WlanScanConfigEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Per cloned wireless interface channel scan configuration data. + The entry will be empty if no scans were initiated via SNMP." + INDEX { wlanIfaceName } + ::= { wlanScanConfigTable 1 } + +WlanScanConfigEntry ::= SEQUENCE { + wlanScanFlags BITS, + wlanScanDuration INTEGER, + wlanScanMinChannelDwellTime INTEGER, + wlanScanMaxChannelDwellTime INTEGER, + wlanScanConfigStatus INTEGER +} + +wlanScanFlags OBJECT-TYPE + SYNTAX BITS { + noSelection(1), + activeScan(2), + pickFirst(3), + backgroundScan(4), + once(5), + noBroadcast(6), + noAutoSequencing(7), + flushCashe(8), + chechCashe(9) + } + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Desired flags for the channel scan." + ::= { wlanScanConfigEntry 1 } + +wlanScanDuration OBJECT-TYPE + SYNTAX INTEGER (1..2147483647) + UNITS "milliseconds" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The desired duration of the scan. Setting the value of this object + to the highest allowed value will initiate an infinite scan." + ::= { wlanScanConfigEntry 2 } + +wlanScanMinChannelDwellTime OBJECT-TYPE + SYNTAX INTEGER + UNITS "milliseconds" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The minimum channel dwelltime for this scan." + ::= { wlanScanConfigEntry 3 } + +wlanScanMaxChannelDwellTime OBJECT-TYPE + SYNTAX INTEGER + UNITS "milliseconds" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The maximum channel dwelltime for this scan." + ::= { wlanScanConfigEntry 4 } + +wlanScanConfigStatus OBJECT-TYPE + SYNTAX INTEGER { + unknown(0), + notStarted(1), + running(2), + finished(3), + cancel(4) + } + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "This object is used to initiate or cancel channel scanning on the cloned + interface via SNMP. Setting its value to running(2) will initiate channel + scanning on the cloned interface, while setting the value to cancel will + cancel the current ongoing scan. All other values should be returned in + GET operations only." + ::= { wlanScanConfigEntry 5 } + +wlanScanResultsTable OBJECT-TYPE + SYNTAX SEQUENCE OF WlanScanResultsEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A table that contains scan results for a virtual wireless interface." + ::= { begemotWlanScanning 2 } + +wlanScanResultsEntry OBJECT-TYPE + SYNTAX WlanScanResultsEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Per virtual wireless interface channel scan results data." + INDEX { wlanIfaceName, wlanScanResultID, wlanScanResultBssid } + ::= { wlanScanResultsTable 1 } + +WlanScanResultsEntry ::= SEQUENCE { + wlanScanResultID OCTET STRING, + wlanScanResultBssid MacAddress, + wlanScanResultChannel INTEGER, + wlanScanResultRate INTEGER, + wlanScanResultNoise INTEGER, + wlanScanResultBeaconInterval INTEGER, + wlanScanResultCapabilities BITS +} + +wlanScanResultID OBJECT-TYPE + SYNTAX OCTET STRING (SIZE(0..32)) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The SSID/MESH ID for this scan result entry." + ::= { wlanScanResultsEntry 1 } + +wlanScanResultBssid OBJECT-TYPE + SYNTAX MacAddress + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The BSSID for this scan result entry." + ::= { wlanScanResultsEntry 2 } + +wlanScanResultChannel OBJECT-TYPE + SYNTAX INTEGER + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The operating channel for this scan result entry." + ::= { wlanScanResultsEntry 3 } + +wlanScanResultRate OBJECT-TYPE + SYNTAX INTEGER + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The operating rate of this scan result entry." + ::= { wlanScanResultsEntry 4 } + +wlanScanResultNoise OBJECT-TYPE + SYNTAX INTEGER + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The channel noise for this scan result entry." + ::= { wlanScanResultsEntry 5 } + +wlanScanResultBeaconInterval OBJECT-TYPE + SYNTAX INTEGER + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The beacon interval reported for this scan result entry." + ::= { wlanScanResultsEntry 6 } + +wlanScanResultCapabilities OBJECT-TYPE + SYNTAX WlanPeerCapabilityFlags + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The capabilities advertised for this scan result entry." + ::= { wlanScanResultsEntry 7 } + +wlanIfRoamParamsTable OBJECT-TYPE + SYNTAX SEQUENCE OF WlanIfRoamParamsEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A table that contains the parameters that govern the roaming + operation on a wireless interface." + ::= { begemotWlanInterface 6 } + +wlanIfRoamParamsEntry OBJECT-TYPE + SYNTAX WlanIfRoamParamsEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "An entry that contains the roaming parameters of a wireless + interface." + INDEX { wlanIfaceName, wlanIfRoamPhyMode } + ::= { wlanIfRoamParamsTable 1 } + +WlanIfRoamParamsEntry ::= SEQUENCE { + wlanIfRoamPhyMode INTEGER, + wlanIfRoamRxSignalStrength INTEGER, + wlanIfRoamTxRateThreshold INTEGER +} + +wlanIfRoamPhyMode OBJECT-TYPE + SYNTAX WlanIfPhyMode + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The PHY mode for this roaming parameters entry." + ::= { wlanIfRoamParamsEntry 1 } + +wlanIfRoamRxSignalStrength OBJECT-TYPE + SYNTAX INTEGER + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The Receive Signal Strength for this roaming parameters entry." + ::= { wlanIfRoamParamsEntry 2 } + +wlanIfRoamTxRateThreshold OBJECT-TYPE + SYNTAX INTEGER + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The transmit rate threshold value for this roaming parameters + entry in Mb/s or MCS." + ::= { wlanIfRoamParamsEntry 3 } + +wlanIfTxParamsTable OBJECT-TYPE + SYNTAX SEQUENCE OF WlanIfTxParamsEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A table that contains the parameters that govern the transmit + operation on a wireless interface." + ::= { begemotWlanInterface 7 } + +wlanIfTxParamsEntry OBJECT-TYPE + SYNTAX WlanIfTxParamsEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "An entry that contains the transmit parameters of a wireless + interface." + INDEX { wlanIfaceName, wlanIfTxPhyMode } + ::= { wlanIfTxParamsTable 1 } + +WlanIfTxParamsEntry ::= SEQUENCE { + wlanIfTxPhyMode INTEGER, + wlanIfTxUnicastRate INTEGER, + wlanIfTxMcastRate INTEGER, + wlanIfTxMgmtRate INTEGER, + wlanIfTxMaxRetryCount INTEGER +} + +wlanIfTxPhyMode OBJECT-TYPE + SYNTAX WlanIfPhyMode + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The PHY mode for this entry." + ::= { wlanIfTxParamsEntry 1 } + +wlanIfTxUnicastRate OBJECT-TYPE + SYNTAX INTEGER + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The value of this object specifies a fixed rate for transmitting + unicast frames in this phy mode." + ::= { wlanIfTxParamsEntry 2 } + +wlanIfTxMcastRate OBJECT-TYPE + SYNTAX INTEGER + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The value of this object specifies a fixed rate for transmitting + broadcast and multicast frames in this phy mode." + ::= { wlanIfTxParamsEntry 3 } + +wlanIfTxMgmtRate OBJECT-TYPE + SYNTAX INTEGER + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The value of this object specifies a fixed rate for transmitting + management and/or control frames in this phy mode." + ::= { wlanIfTxParamsEntry 4 } + +wlanIfTxMaxRetryCount OBJECT-TYPE + SYNTAX INTEGER + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The maximum number of tries to use when sending unicast frames + in this phy mode." + ::= { wlanIfTxParamsEntry 5 } + +-- ---------------------------------------------------------- -- +-- The Statistics Database for Wireless interfaces +-- ---------------------------------------------------------- -- + +wlanIfaceStatisticsTable OBJECT-TYPE + SYNTAX SEQUENCE OF WlanIfaceStatisticsEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A table that contains summary statistics for each virtual wireless + interface on the managed device." + ::= { begemotWlanStatistics 1 } + +wlanIfaceStatisticsEntry OBJECT-TYPE + SYNTAX WlanIfaceStatisticsEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A list of statistics for each virtual wireless interface." + AUGMENTS { wlanInterfaceEntry } + ::= { wlanIfaceStatisticsTable 1 } + +WlanIfaceStatisticsEntry ::= SEQUENCE { + wlanStatsRxBadVersion Counter32, + wlanStatsRxTooShort Counter32, + wlanStatsRxWrongBssid Counter32, + wlanStatsRxDiscardedDups Counter32, + wlanStatsRxWrongDir Counter32, + wlanStatsRxDiscardMcastEcho Counter32, + wlanStatsRxDiscardNoAssoc Counter32, + wlanStatsRxWepNoPrivacy Counter32, + wlanStatsRxWepUnencrypted Counter32, + wlanStatsRxWepFailed Counter32, + wlanStatsRxDecapsulationFailed Counter32, + wlanStatsRxDiscardMgmt Counter32, + wlanStatsRxControl Counter32, + wlanStatsRxBeacon Counter32, + wlanStatsRxRateSetTooBig Counter32, + wlanStatsRxElemMissing Counter32, + wlanStatsRxElemTooBig Counter32, + wlanStatsRxElemTooSmall Counter32, + wlanStatsRxElemUnknown Counter32, + wlanStatsRxChannelMismatch Counter32, + wlanStatsRxDropped Counter32, + wlanStatsRxSsidMismatch Counter32, + wlanStatsRxAuthNotSupported Counter32, + wlanStatsRxAuthFailed Counter32, + wlanStatsRxAuthCM Counter32, + wlanStatsRxAssocWrongBssid Counter32, + wlanStatsRxAssocNoAuth Counter32, + wlanStatsRxAssocCapMismatch Counter32, + wlanStatsRxAssocNoRateMatch Counter32, + wlanStatsRxBadWpaIE Counter32, + wlanStatsRxDeauthenticate Counter32, + wlanStatsRxDisassociate Counter32, + wlanStatsRxUnknownSubtype Counter32, + wlanStatsRxFailedNoBuf Counter32, + wlanStatsRxBadAuthRequest Counter32, + wlanStatsRxUnAuthorized Counter32, + wlanStatsRxBadKeyId Counter32, + wlanStatsRxCCMPSeqViolation Counter32, + wlanStatsRxCCMPBadFormat Counter32, + wlanStatsRxCCMPFailedMIC Counter32, + wlanStatsRxTKIPSeqViolation Counter32, + wlanStatsRxTKIPBadFormat Counter32, + wlanStatsRxTKIPFailedMIC Counter32, + wlanStatsRxTKIPFailedICV Counter32, + wlanStatsRxDiscardACL Counter32, + wlanStatsTxFailedNoBuf Counter32, + wlanStatsTxFailedNoNode Counter32, + wlanStatsTxUnknownMgmt Counter32, + wlanStatsTxBadCipher Counter32, + wlanStatsTxNoDefKey Counter32, + wlanStatsTxFragmented Counter32, + wlanStatsTxFragmentsCreated Counter32, + wlanStatsActiveScans Counter32, + wlanStatsPassiveScans Counter32, + wlanStatsTimeoutInactivity Counter32, + wlanStatsCryptoNoMem Counter32, + wlanStatsSwCryptoTKIP Counter32, + wlanStatsSwCryptoTKIPEnMIC Counter32, + wlanStatsSwCryptoTKIPDeMIC Counter32, + wlanStatsCryptoTKIPCM Counter32, + wlanStatsSwCryptoCCMP Counter32, + wlanStatsSwCryptoWEP Counter32, + wlanStatsCryptoCipherKeyRejected Counter32, + wlanStatsCryptoNoKey Counter32, + wlanStatsCryptoDeleteKeyFailed Counter32, + wlanStatsCryptoUnknownCipher Counter32, + wlanStatsCryptoAttachFailed Counter32, + wlanStatsCryptoKeyFailed Counter32, + wlanStatsCryptoEnMICFailed Counter32, + wlanStatsIBSSCapMismatch Counter32, + wlanStatsUnassocStaPSPoll Counter32, + wlanStatsBadAidPSPoll Counter32, + wlanStatsEmptyPSPoll Counter32, + wlanStatsRxFFBadHdr Counter32, + wlanStatsRxFFTooShort Counter32, + wlanStatsRxFFSplitError Counter32, + wlanStatsRxFFDecap Counter32, + wlanStatsTxFFEncap Counter32, + wlanStatsRxBadBintval Counter32, + wlanStatsRxDemicFailed Counter32, + wlanStatsRxDefragFailed Counter32, + wlanStatsRxMgmt Counter32, + wlanStatsRxActionMgmt Counter32, + wlanStatsRxAMSDUTooShort Counter32, + wlanStatsRxAMSDUSplitError Counter32, + wlanStatsRxAMSDUDecap Counter32, + wlanStatsTxAMSDUEncap Counter32, + wlanStatsAMPDUBadBAR Counter32, + wlanStatsAMPDUOowBar Counter32, + wlanStatsAMPDUMovedBAR Counter32, + wlanStatsAMPDURxBAR Counter32, + wlanStatsAMPDURxOor Counter32, + wlanStatsAMPDURxCopied Counter32, + wlanStatsAMPDURxDropped Counter32, + wlanStatsTxDiscardBadState Counter32, + wlanStatsTxFailedNoAssoc Counter32, + wlanStatsTxClassifyFailed Counter32, + wlanStatsDwdsMcastDiscard Counter32, + wlanStatsHTAssocRejectNoHT Counter32, + wlanStatsHTAssocDowngrade Counter32, + wlanStatsHTAssocRateMismatch Counter32, + wlanStatsAMPDURxAge Counter32, + wlanStatsAMPDUMoved Counter32, + wlanStatsADDBADisabledReject Counter32, + wlanStatsADDBANoRequest Counter32, + wlanStatsADDBABadToken Counter32, + wlanStatsADDBABadPolicy Counter32, + wlanStatsAMPDUStopped Counter32, + wlanStatsAMPDUStopFailed Counter32, + wlanStatsAMPDURxReorder Counter32, + wlanStatsScansBackground Counter32, + wlanLastDeauthReason INTEGER, + wlanLastDissasocReason INTEGER, + wlanLastAuthFailReason INTEGER, + wlanStatsBeaconMissedEvents Counter32, + wlanStatsRxDiscardBadStates Counter32, + wlanStatsFFFlushed Counter32, + wlanStatsTxControlFrames Counter32, + wlanStatsAMPDURexmt Counter32, + wlanStatsAMPDURexmtFailed Counter32, + wlanStatsReset INTEGER +} + +wlanStatsRxBadVersion OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of frames received by this interface that had bad version." + ::= { wlanIfaceStatisticsEntry 1 } + +wlanStatsRxTooShort OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of frames received by this interface that were too short." + ::= { wlanIfaceStatisticsEntry 2 } + +wlanStatsRxWrongBssid OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of frames received by this interface with wrong BSSID." + ::= { wlanIfaceStatisticsEntry 3 } + +wlanStatsRxDiscardedDups OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of received discarded duplicate frames by this interface." + ::= { wlanIfaceStatisticsEntry 4 } + +wlanStatsRxWrongDir OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of received frames by this interface that were dropped + due to wrong direction." + ::= { wlanIfaceStatisticsEntry 5 } + +wlanStatsRxDiscardMcastEcho OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of received multicast echo frames discarded by this + interface." + ::= { wlanIfaceStatisticsEntry 6 } + +wlanStatsRxDiscardNoAssoc OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of frames received by this interface that were dropped + since no association existed with the sending station." + ::= { wlanIfaceStatisticsEntry 7 } + +wlanStatsRxWepNoPrivacy OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of frames received by this interface that were dropped + since they contained WEP information and WEP privacy was off." + ::= { wlanIfaceStatisticsEntry 8 } + +wlanStatsRxWepUnencrypted OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of frames received by this interface that were dropped + since they contained no WEP information and WEP privacy was on." + ::= { wlanIfaceStatisticsEntry 9 } + +wlanStatsRxWepFailed OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of frames received by this interface that were dropped + since processing of the WEP information contained in them failed." + ::= { wlanIfaceStatisticsEntry 10 } + +wlanStatsRxDecapsulationFailed OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of received frames that were discarded by this interface + due to decapsulation failure." + ::= { wlanIfaceStatisticsEntry 11 } + +wlanStatsRxDiscardMgmt OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of received management frames discarded by this interface." + ::= { wlanIfaceStatisticsEntry 12 } + +wlanStatsRxControl OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of control frames received by this interface." + ::= { wlanIfaceStatisticsEntry 13 } + +wlanStatsRxBeacon OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of beacon frames received by this interface." + ::= { wlanIfaceStatisticsEntry 14 } + +wlanStatsRxRateSetTooBig OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of frames received by this interface with extended + supported rate element." + ::= { wlanIfaceStatisticsEntry 15 } + +wlanStatsRxElemMissing OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of frames received by this interface that were missing + a required element." + ::= { wlanIfaceStatisticsEntry 16 } + +wlanStatsRxElemTooBig OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of frames received by this interface that contained an + information element whose size was too big." + ::= { wlanIfaceStatisticsEntry 17 } + +wlanStatsRxElemTooSmall OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of frames received by this interface that contained an + information element whose size was too small." + ::= { wlanIfaceStatisticsEntry 18 } + +wlanStatsRxElemUnknown OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of frames received by this interface that contained an + unknown information element." + ::= { wlanIfaceStatisticsEntry 19 } + +wlanStatsRxChannelMismatch OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of frames received by this interface, that were discarded + since they were received on a channel different from the one indicated + in the DS params element id." + ::= { wlanIfaceStatisticsEntry 20 } + +wlanStatsRxDropped OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of frames received by this interface that were dropped due + to unknown reason." + ::= { wlanIfaceStatisticsEntry 21 } + +wlanStatsRxSsidMismatch OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of frames received by this interface that had a bad SSID." + ::= { wlanIfaceStatisticsEntry 22 } + +wlanStatsRxAuthNotSupported OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of frames received by this interface that contained an + unknown authentication algorithm." + ::= { wlanIfaceStatisticsEntry 23 } + +wlanStatsRxAuthFailed OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of frames received by this interface for which the + authentication failed." + ::= { wlanIfaceStatisticsEntry 24 } + +wlanStatsRxAuthCM OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of frames received by this interface for which the + authentication failed due to TKIP countermeasures enabled." + ::= { wlanIfaceStatisticsEntry 25 } + +wlanStatsRxAssocWrongBssid OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of frames received by this interface with association + request that had a bad BSSID." + ::= { wlanIfaceStatisticsEntry 26 } + +wlanStatsRxAssocNoAuth OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of frames received by this interface with association + request that came from unauthentication node." + ::= { wlanIfaceStatisticsEntry 27 } + +wlanStatsRxAssocCapMismatch OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of frames received by this interface with association + request that had bad capabilities set." + ::= { wlanIfaceStatisticsEntry 28 } + +wlanStatsRxAssocNoRateMatch OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of frames received by this interface with association + request that had unsupported rate set." + ::= { wlanIfaceStatisticsEntry 29 } + +wlanStatsRxBadWpaIE OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of frames received by this interface with association + request that had no or invalid WPA information element." + ::= { wlanIfaceStatisticsEntry 30 } + +wlanStatsRxDeauthenticate OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of deauthentication requests received by this interface." + ::= { wlanIfaceStatisticsEntry 31 } + +wlanStatsRxDisassociate OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of deassociation requests received by this interface." + ::= { wlanIfaceStatisticsEntry 32 } + +wlanStatsRxUnknownSubtype OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of frames received by this interface that had unknown + subtype." + ::= { wlanIfaceStatisticsEntry 33 } + +wlanStatsRxFailedNoBuf OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of frames received by this interface that were dropped + due to lack of free buffers." + ::= { wlanIfaceStatisticsEntry 34 } + +wlanStatsRxBadAuthRequest OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of frames received by this interface for which + authentication failed." + ::= { wlanIfaceStatisticsEntry 35 } + +wlanStatsRxUnAuthorized OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of non-PAE frames received by this interface prior to + authorization." + ::= { wlanIfaceStatisticsEntry 36 } + +wlanStatsRxBadKeyId OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of frames received by this interface with bad key." + ::= { wlanIfaceStatisticsEntry 37 } + +wlanStatsRxCCMPSeqViolation OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of frames received by this interface that caused CCMP + sequence violation." + ::= { wlanIfaceStatisticsEntry 38 } + +wlanStatsRxCCMPBadFormat OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of frames received by this interface that had bad CCMP + format." + ::= { wlanIfaceStatisticsEntry 39 } + +wlanStatsRxCCMPFailedMIC OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of frames receivbed by this interface for which CCMP + decryption failed due to MIC mismatch." + ::= { wlanIfaceStatisticsEntry 40 } + +wlanStatsRxTKIPSeqViolation OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of frames received by this interface that caused TKIP + sequence violation.." + ::= { wlanIfaceStatisticsEntry 41 } + +wlanStatsRxTKIPBadFormat OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of frames received by this interface that were missing + TKIP ExtIV." + ::= { wlanIfaceStatisticsEntry 42 } + +wlanStatsRxTKIPFailedMIC OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of frames received by this interface for which TKIP + decryption failed due to MIC mismatch." + ::= { wlanIfaceStatisticsEntry 43 } + +wlanStatsRxTKIPFailedICV OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of frames received by this interface for which TKIP + decryption failed due to ICV mismatch." + ::= { wlanIfaceStatisticsEntry 44 } + +wlanStatsRxDiscardACL OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of frames received by this interface that were + disallowed by ACL." + ::= { wlanIfaceStatisticsEntry 45 } + +wlanStatsTxFailedNoBuf OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of frames that were not transmitted by this interface + due to lack of free buffers." + ::= { wlanIfaceStatisticsEntry 46 } + +wlanStatsTxFailedNoNode OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of frames that were queued for transmit on this interface + but were not sent since appropriate node for sending was not found." + ::= { wlanIfaceStatisticsEntry 47 } + +wlanStatsTxUnknownMgmt OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of unknown management frames transmitted by this interface." + ::= { wlanIfaceStatisticsEntry 48 } + +wlanStatsTxBadCipher OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of frames that were queued for transmit on this interface + but were not send since the specified key was not setup." + ::= { wlanIfaceStatisticsEntry 49 } + +wlanStatsTxNoDefKey OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of frames that were queued for transmit on this interface + but were not send since an appropriate key was not found." + ::= { wlanIfaceStatisticsEntry 50 } + +wlanStatsTxFragmented OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of fragmented frames transmitted by this interface." + ::= { wlanIfaceStatisticsEntry 51 } + +wlanStatsTxFragmentsCreated OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of created fragments transmitted by this interface." + ::= { wlanIfaceStatisticsEntry 52 } + +wlanStatsActiveScans OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of active scans performed by this interface." + ::= { wlanIfaceStatisticsEntry 53 } + +wlanStatsPassiveScans OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of passive scans performed by this interface." + ::= { wlanIfaceStatisticsEntry 54 } + +wlanStatsTimeoutInactivity OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of times a station/node was dropped by this interface + due to inactivity timeout." + ::= { wlanIfaceStatisticsEntry 55 } + +wlanStatsCryptoNoMem OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number times attaching a crypto protocol to this interface + failed due to lack of memory." + ::= { wlanIfaceStatisticsEntry 56 } + +wlanStatsSwCryptoTKIP OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of times TKIP encryption/decryption was handled in + software for frames received/transmitted by this interface." + ::= { wlanIfaceStatisticsEntry 57 } + +wlanStatsSwCryptoTKIPEnMIC OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of times TKIP MIC was added in software to frames + transmitted by this interface." + ::= { wlanIfaceStatisticsEntry 58 } + +wlanStatsSwCryptoTKIPDeMIC OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of times TKIP MIC was stripped in software from frames + received by this interface." + ::= { wlanIfaceStatisticsEntry 59 } + +wlanStatsCryptoTKIPCM OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of frames discarded by this interface due to TKIP + counter measures." + ::= { wlanIfaceStatisticsEntry 60 } + +wlanStatsSwCryptoCCMP OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of times CCMP encryption/decryption was handled in + software for frames received/transmitted by this interface." + ::= { wlanIfaceStatisticsEntry 61 } + +wlanStatsSwCryptoWEP OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of times WEP encryption/decryption was handled in + software for frames received/transmitted by this interface." + ::= { wlanIfaceStatisticsEntry 62 } + +wlanStatsCryptoCipherKeyRejected OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of times a key was rejected for this interface." + ::= { wlanIfaceStatisticsEntry 63 } + +wlanStatsCryptoNoKey OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of times key setup for this interface failed." + ::= { wlanIfaceStatisticsEntry 64 } + +wlanStatsCryptoDeleteKeyFailed OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of times key deletion from driver for this interface + failed." + ::= { wlanIfaceStatisticsEntry 65 } + +wlanStatsCryptoUnknownCipher OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of times key setup for this interface failed due to + invalid cipher." + ::= { wlanIfaceStatisticsEntry 66 } + +wlanStatsCryptoAttachFailed OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of times attaching a cipher for this interface failed." + ::= { wlanIfaceStatisticsEntry 67 } + +wlanStatsCryptoKeyFailed OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of times setting a cipher in the driver for this + interface failed." + ::= { wlanIfaceStatisticsEntry 68 } + +wlanStatsCryptoEnMICFailed OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of frames that were discarded by by this interface + due to failed enmic." + ::= { wlanIfaceStatisticsEntry 69 } + +wlanStatsIBSSCapMismatch OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of times a BSSID change failed for an interface operating + in ad hoc mode due to capabilities mismatch." + ::= { wlanIfaceStatisticsEntry 70 } + +wlanStatsUnassocStaPSPoll OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of ps-poll frames from unassociated station received + by this interface." + ::= { wlanIfaceStatisticsEntry 71 } + +wlanStatsBadAidPSPoll OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of ps-poll frames with incorrect aid received by this + interface." + ::= { wlanIfaceStatisticsEntry 72 } + +wlanStatsEmptyPSPoll OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of empty ps-poll frames received by this interface." + ::= { wlanIfaceStatisticsEntry 73 } + +wlanStatsRxFFBadHdr OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of fast frames with bad header received by this interface." + ::= { wlanIfaceStatisticsEntry 74 } + +wlanStatsRxFFTooShort OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of fast frames received by this interface, for which + decapsulation failed." + ::= { wlanIfaceStatisticsEntry 75 } + +wlanStatsRxFFSplitError OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of fast frames received by this interface, for which + decapsulation failed during split." + ::= { wlanIfaceStatisticsEntry 76 } + +wlanStatsRxFFDecap OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of fast frames received by this interface, that were + successfully decapsulated." + ::= { wlanIfaceStatisticsEntry 77 } + +wlanStatsTxFFEncap OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of encapsulated fast frames transmitted by this interface." + ::= { wlanIfaceStatisticsEntry 78 } + +wlanStatsRxBadBintval OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of frames with bogus beacon interval received by this + interface." + ::= { wlanIfaceStatisticsEntry 79 } + +wlanStatsRxDemicFailed OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of frames received by this interface for which + stripping of the MIC failed." + ::= { wlanIfaceStatisticsEntry 80 } + +wlanStatsRxDefragFailed OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of frames received by this interface for which + defragmentation failed." + ::= { wlanIfaceStatisticsEntry 81 } + +wlanStatsRxMgmt OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of management frames received by this interface." + ::= { wlanIfaceStatisticsEntry 82 } + +wlanStatsRxActionMgmt OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of action management frames received by this interface." + ::= { wlanIfaceStatisticsEntry 83 } + +wlanStatsRxAMSDUTooShort OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of A-MSDU frames received by this interface for which + decapsulaiton failed." + ::= { wlanIfaceStatisticsEntry 84 } + +wlanStatsRxAMSDUSplitError OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of A-MSDU frames received by this interface for which + split failed." + ::= { wlanIfaceStatisticsEntry 85 } + +wlanStatsRxAMSDUDecap OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of A-MSDU frames received by this interface which + were successfully decapsulaited." + ::= { wlanIfaceStatisticsEntry 86 } + +wlanStatsTxAMSDUEncap OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of encapsulated A-MSDU frames transmitted by this + interface." + ::= { wlanIfaceStatisticsEntry 87 } + +wlanStatsAMPDUBadBAR OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of A-MPDU frames that were dropped by this interface + source BAR frame processing was disabled." + ::= { wlanIfaceStatisticsEntry 88 } + +wlanStatsAMPDUOowBar OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of A-MPDU BAR before ADDBA frames received by this + interface." + ::= { wlanIfaceStatisticsEntry 89 } + +wlanStatsAMPDUMovedBAR OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of times a BAR moved window occurred." + ::= { wlanIfaceStatisticsEntry 90 } + +wlanStatsAMPDURxBAR OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of A-MPDU BAR frames received by this interface." + ::= { wlanIfaceStatisticsEntry 91 } + +wlanStatsAMPDURxOor OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of out-of-order A-MPDU frames by received this interface." + ::= { wlanIfaceStatisticsEntry 92 } + +wlanStatsAMPDURxCopied OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of A-MPDU frames by copied down this interface." + ::= { wlanIfaceStatisticsEntry 93 } + +wlanStatsAMPDURxDropped OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of A-MPDU frames by dropped this interface." + ::= { wlanIfaceStatisticsEntry 94 } + +wlanStatsTxDiscardBadState OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of frames queued for transmit on this interface that + were discarded due to interface state not ready for transmit." + ::= { wlanIfaceStatisticsEntry 95 } + +wlanStatsTxFailedNoAssoc OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of frames queued for transmit on this interface that + were discarded since the receiving station was not associated." + ::= { wlanIfaceStatisticsEntry 96 } + +wlanStatsTxClassifyFailed OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of frames queued for transmit on this interface that + were discarded since their priority was not determined." + ::= { wlanIfaceStatisticsEntry 97 } + +wlanStatsDwdsMcastDiscard OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of multicast over DWDS frames discarded by this interface." + ::= { wlanIfaceStatisticsEntry 98 } + +wlanStatsHTAssocRejectNoHT OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of management frames received from a non-HT stations + that were rejected by this interface." + ::= { wlanIfaceStatisticsEntry 99 } + +wlanStatsHTAssocDowngrade OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of times HT was disallowed for an association on + this interface due to WEP or TKIP requested." + ::= { wlanIfaceStatisticsEntry 100 } + +wlanStatsHTAssocRateMismatch OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of times rate mismatch occurred during HT rate set + handling on this interface." + ::= { wlanIfaceStatisticsEntry 101 } + +wlanStatsAMPDURxAge OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of A-MPDU frames sent by this interface due to aging out." + ::= { wlanIfaceStatisticsEntry 102 } + +wlanStatsAMPDUMoved OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of time A-MPDU MSDU moved window occurred for this + interface." + ::= { wlanIfaceStatisticsEntry 103 } + +wlanStatsADDBADisabledReject OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of received ADDBA frames that were discarded by this + interface since ADDBA was disabled." + ::= { wlanIfaceStatisticsEntry 104 } + +wlanStatsADDBANoRequest OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of received ADDBA responses frames that were discarded + by this interface due to no pending ADDBA." + ::= { wlanIfaceStatisticsEntry 105 } + +wlanStatsADDBABadToken OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of received ADDBA response frames that were discarded + by this interface since ADDBA response caused dialogtoken mismatch." + ::= { wlanIfaceStatisticsEntry 106 } + +wlanStatsADDBABadPolicy OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of received ADDBA response frames that were discarded + by this interface since ADDBA response caused policy mismatch." + ::= { wlanIfaceStatisticsEntry 107 } + +wlanStatsAMPDUStopped OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of times a A-MPDU stream stopped on this interface." + ::= { wlanIfaceStatisticsEntry 108 } + +wlanStatsAMPDUStopFailed OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of times a A-MPDU stream stop failed on this interface." + ::= { wlanIfaceStatisticsEntry 109 } + +wlanStatsAMPDURxReorder OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of received reordered A-MPDU frames on this interface." + ::= { wlanIfaceStatisticsEntry 110 } + +wlanStatsScansBackground OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of background scans started on this interface." + ::= { wlanIfaceStatisticsEntry 111 } + +wlanLastDeauthReason OBJECT-TYPE + SYNTAX WlanMgmtReasonCode + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The last received deauthenticate reason on this interface." + ::= { wlanIfaceStatisticsEntry 112 } + +wlanLastDissasocReason OBJECT-TYPE + SYNTAX WlanMgmtReasonCode + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The last received disassociate reason on this interface." + ::= { wlanIfaceStatisticsEntry 113 } + +wlanLastAuthFailReason OBJECT-TYPE + SYNTAX WlanMgmtReasonCode + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The last received authentication failed reason on this interface." + ::= { wlanIfaceStatisticsEntry 114 } + +wlanStatsBeaconMissedEvents OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of beacon miss notification events on this interface." + ::= { wlanIfaceStatisticsEntry 115 } + +wlanStatsRxDiscardBadStates OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of frames received on this interface that were discarded + due to interface state not ready for receive." + ::= { wlanIfaceStatisticsEntry 116 } + +wlanStatsFFFlushed OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of fast frames flushed from the stage queue on this + interface." + ::= { wlanIfaceStatisticsEntry 117 } + +wlanStatsTxControlFrames OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of control frames transmitted by this interface." + ::= { wlanIfaceStatisticsEntry 118 } + +wlanStatsAMPDURexmt OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of A-MPDU frames successfully retransmitted by this + interface." + ::= { wlanIfaceStatisticsEntry 119 } + +wlanStatsAMPDURexmtFailed OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of A-MPDU frames for which retransmission failed on + this interface." + ::= { wlanIfaceStatisticsEntry 120 } + +wlanStatsReset OBJECT-TYPE + SYNTAX INTEGER { + no-op(1), + clear(2) + } + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The value of this object is used to reset the statistics on this + interface." + ::= { wlanIfaceStatisticsEntry 121 } + +-- ---------------------------------------------------------- -- +-- The WEP Configuration Database for Wireless interfaces +-- ---------------------------------------------------------- -- + +wlanWepInterfaceTable OBJECT-TYPE + SYNTAX SEQUENCE OF WlanWepInterfaceEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A table that contains WEP configuration for the wireless interfaces + on the managed system." + ::= { begemotWlanWep 1 } + +wlanWepInterfaceEntry OBJECT-TYPE + SYNTAX WlanWepInterfaceEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "WEP Configuration for wireless interface." + INDEX { wlanIfaceName} + ::= { wlanWepInterfaceTable 1 } + +WlanWepInterfaceEntry ::= SEQUENCE { + wlanWepMode INTEGER, + wlanWepDefTxKey INTEGER +} + +wlanWepMode OBJECT-TYPE + SYNTAX INTEGER { + off(0), + on(1), + mixed(2) + } + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The WEP mode set on the interface." + DEFVAL { off } + ::= { wlanWepInterfaceEntry 1 } + +wlanWepDefTxKey OBJECT-TYPE + SYNTAX INTEGER + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The index of the default WEP key for the interface." + ::= { wlanWepInterfaceEntry 2 } + +wlanWepKeyTable OBJECT-TYPE + SYNTAX SEQUENCE OF WlanWepKeyEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A table that contains the configured WEP keys for a virtual + wireless interface." + ::= { begemotWlanWep 2 } + +wlanWepKeyEntry OBJECT-TYPE + SYNTAX WlanWepKeyEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A configured WEP Key entry." + INDEX { wlanIfaceName, wlanWepKeyID } + ::= { wlanWepKeyTable 1 } + +WlanWepKeyEntry ::= SEQUENCE { + wlanWepKeyID INTEGER, + wlanWepKeyLength INTEGER, + wlanWepKeySet OCTET STRING, + wlanWepKeyHash OCTET STRING, + wlanWepKeyStatus RowStatus +} + +wlanWepKeyID OBJECT-TYPE + SYNTAX INTEGER (1..4) + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The WEP Key ID." + ::= { wlanWepKeyEntry 1 } + +wlanWepKeyLength OBJECT-TYPE + SYNTAX INTEGER + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The WEP Key length." + ::= { wlanWepKeyEntry 2 } + +wlanWepKeySet OBJECT-TYPE + SYNTAX OCTET STRING + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The WEP Key String to configure for this key. When GET is attempted + for this column, an empty Octet String is returned." + ::= { wlanWepKeyEntry 3 } + +wlanWepKeyHash OBJECT-TYPE + SYNTAX OCTET STRING + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The SHA256 hash produced of the WEP Key String." + ::= { wlanWepKeyEntry 4 } + +wlanWepKeyStatus OBJECT-TYPE + SYNTAX RowStatus + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "This object is used for creating/deleting WEP keys." + ::= { wlanWepKeyEntry 5 } + +-- ---------------------------------------------------------- -- +-- The MAC Access Control Database for Wireless interfaces +-- ---------------------------------------------------------- -- + +wlanMACAccessControlTable OBJECT-TYPE + SYNTAX SEQUENCE OF WlanMACAccessControlEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A table that contains Access Control configuration for wireless + interfaces operating as an access point." + ::= { begemotWlanMACAccessControl 1 } + +wlanMACAccessControlEntry OBJECT-TYPE + SYNTAX WlanMACAccessControlEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The MAC Access Control configuration for a wireless interface + operating as an access point." + INDEX { wlanIfaceName} + ::= { wlanMACAccessControlTable 1 } + +WlanMACAccessControlEntry ::= SEQUENCE { + wlanMACAccessControlPolicy INTEGER, + wlanMACAccessControlNacl Counter32, + wlanMACAccessControlFlush INTEGER +} + +wlanMACAccessControlPolicy OBJECT-TYPE + SYNTAX INTEGER { + open(0), + allow(1), + deny(2), + radius(7) + } + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The value of this object specifies the MAC Access Control policy + for this Host AP interface." + DEFVAL { open } + ::= { wlanMACAccessControlEntry 1 } + +wlanMACAccessControlNacl OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of active MAC Access Control Entries in the Database + for this Host AP interface." + ::= { wlanMACAccessControlEntry 2 } + +wlanMACAccessControlFlush OBJECT-TYPE + SYNTAX INTEGER { + no-op(0), + flush(1) + } + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "This object is used to flush all entries from the MAC Access + Control Database for the specified virtual wireless interface." + ::= { wlanMACAccessControlEntry 3 } + +wlanMACAccessControlMACTable OBJECT-TYPE + SYNTAX SEQUENCE OF WlanMACAccessControlMACEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A table that contains Access Control MAC for virtual wireless + interfaces operating in Host AP mode." + ::= { begemotWlanMACAccessControl 2 } + +wlanMACAccessControlMACEntry OBJECT-TYPE + SYNTAX WlanMACAccessControlMACEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The MAC Access Control configuration database with MAC addresses + for a virtual wireless interface." + INDEX { wlanIfaceName, wlanMACAccessControlMAC } + ::= { wlanMACAccessControlMACTable 1 } + +WlanMACAccessControlMACEntry ::= SEQUENCE { + wlanMACAccessControlMAC MacAddress, + wlanMACAccessControlMACStatus RowStatus +} + +wlanMACAccessControlMAC OBJECT-TYPE + SYNTAX MacAddress + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The value of this object specifies the station's MAC to which + the Access Control policy will be applied." + ::= { wlanMACAccessControlMACEntry 1 } + +wlanMACAccessControlMACStatus OBJECT-TYPE + SYNTAX RowStatus + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The object is used to add or delete MAC entries from the Access + Control Database for this interface operating in Host AP mode. + To add an entry the value of this object should be set to createAndGo, + a value of destroy will remove an existing entry. A GET on this object + will always return value active." + ::= { wlanMACAccessControlMACEntry 2 } + +-- ---------------------------------------------------------- -- +-- The Mesh Routing Database for interfaces operating in mesh mode +-- ---------------------------------------------------------- -- + +wlanMeshRoutingConfig OBJECT IDENTIFIER ::= { begemotWlanMeshRouting 1 } + +wlanMeshInterface OBJECT IDENTIFIER ::= { begemotWlanMeshRouting 2 } + +wlanMeshRoute OBJECT IDENTIFIER ::= { begemotWlanMeshRouting 3 } + +wlanMeshStatistics OBJECT IDENTIFIER ::= { begemotWlanMeshRouting 4 } + +wlanMeshRouteProtocols OBJECT IDENTIFIER ::= { begemotWlanMeshRouting 5 } + +wlanMeshMaxRetries OBJECT-TYPE + SYNTAX INTEGER + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Maximum retries during peer link establishment for wireless mesh + routing operation." + DEFVAL { 2 } + ::= { wlanMeshRoutingConfig 1 } + +wlanMeshConfirmTimeout OBJECT-TYPE + SYNTAX INTEGER + UNITS "milliseconds" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Confirm state timeout for wireless mesh routing operation." + DEFVAL { 40 } + ::= { wlanMeshRoutingConfig 2 } + +wlanMeshHoldingTimeout OBJECT-TYPE + SYNTAX INTEGER + UNITS "milliseconds" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Holding state timeout for wireless mesh routing operation." + DEFVAL { 40 } + ::= { wlanMeshRoutingConfig 3 } + +wlanMeshRetryTimeout OBJECT-TYPE + SYNTAX INTEGER + UNITS "milliseconds" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Retry timeout for wireless mesh routing operation." + DEFVAL { 40 } + ::= { wlanMeshRoutingConfig 4 } + +wlanMeshInterfaceTable OBJECT-TYPE + SYNTAX SEQUENCE OF WlanMeshInterfaceEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A table that contains information for wireless interfaces operating + as wireless mesh points." + ::= { wlanMeshInterface 1 } + +wlanMeshInterfaceEntry OBJECT-TYPE + SYNTAX WlanMeshInterfaceEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Wireless Mesh Routing information for an interface operating as + mesh point." + INDEX { wlanIfaceName } + ::= { wlanMeshInterfaceTable 1 } + +WlanMeshInterfaceEntry ::= SEQUENCE { + wlanMeshId OCTET STRING, + wlanMeshTTL INTEGER, + wlanMeshPeeringEnabled TruthValue, + wlanMeshForwardingEnabled TruthValue, + wlanMeshMetric INTEGER, + wlanMeshPath INTEGER, + wlanMeshRoutesFlush INTEGER +} + +wlanMeshId OBJECT-TYPE + SYNTAX OCTET STRING (SIZE(1..32)) + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The desired Mesh Identifier for the interface." + ::= { wlanMeshInterfaceEntry 1 } + +wlanMeshTTL OBJECT-TYPE + SYNTAX INTEGER + UNITS "hops" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The number of hops a packet may be forwarded before it is discarded." + DEFVAL { 31 } + ::= { wlanMeshInterfaceEntry 2 } + +wlanMeshPeeringEnabled OBJECT-TYPE + SYNTAX TruthValue + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Enable or disable peering with neighbor mesh stations for this + interface." + DEFVAL { true } + ::= { wlanMeshInterfaceEntry 3 } + +wlanMeshForwardingEnabled OBJECT-TYPE + SYNTAX TruthValue + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Enable or disable forwarding packets by this interface." + DEFVAL { true } + ::= { wlanMeshInterfaceEntry 4 } + +wlanMeshMetric OBJECT-TYPE + SYNTAX INTEGER { + unknown(0), + airtime(1) + } + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The link metric protocol used by the interface." + DEFVAL { airtime } + ::= { wlanMeshInterfaceEntry 5 } + +wlanMeshPath OBJECT-TYPE + SYNTAX INTEGER { + unknown(0), + hwmp(1) + } + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The path selection protocol used by the interface." + DEFVAL { hwmp } + ::= { wlanMeshInterfaceEntry 6 } + +wlanMeshRoutesFlush OBJECT-TYPE + SYNTAX INTEGER { + no-op(0), + flush(1) + } + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "This object is used to flush all mesh route entries from the mesh + routing table for the specified interface." + ::= { wlanMeshInterfaceEntry 7 } + +wlanMeshNeighborTable OBJECT-TYPE + SYNTAX SEQUENCE OF WlanMeshNeighborEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A table that contains information for the neighbors of wireless + interfaces operating in mesh mode." + ::= { wlanMeshInterface 2 } + +wlanMeshNeighborEntry OBJECT-TYPE + SYNTAX WlanMeshNeighborEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Information for all neighbors of a wireless interface operating as + a mesh point." + INDEX { wlanIfaceName, wlanMeshNeighborAddress } + ::= { wlanMeshNeighborTable 1 } + +WlanMeshNeighborEntry ::= SEQUENCE { + wlanMeshNeighborAddress MacAddress, + wlanMeshNeighborFrequency INTEGER, + wlanMeshNeighborLocalId INTEGER, + wlanMeshNeighborPeerId INTEGER, + wlanMeshNeighborPeerState INTEGER, + wlanMeshNeighborCurrentTXRate INTEGER, + wlanMeshNeighborRxSignalStrength INTEGER, + wlanMeshNeighborIdleTimer INTEGER, + wlanMeshNeighborTxSequenceNo INTEGER, + wlanMeshNeighborRxSequenceNo INTEGER +} + +wlanMeshNeighborAddress OBJECT-TYPE + SYNTAX MacAddress + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The Ethernet address of this neighbor." + ::= { wlanMeshNeighborEntry 1 } + +wlanMeshNeighborFrequency OBJECT-TYPE + SYNTAX INTEGER + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The operating frequency for the link with this neighbor." + ::= { wlanMeshNeighborEntry 2 } + +wlanMeshNeighborLocalId OBJECT-TYPE + SYNTAX INTEGER + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The local mesh id for this neighbor." + ::= { wlanMeshNeighborEntry 3 } + +wlanMeshNeighborPeerId OBJECT-TYPE + SYNTAX INTEGER + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The mesh peer id of this neighbor." + ::= { wlanMeshNeighborEntry 4 } + +wlanMeshNeighborPeerState OBJECT-TYPE + SYNTAX INTEGER { + idle(0), + openTx(1), + openRx(2), + confirmRx(3), + established(4), + closing(5) + } + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The current link state for this neighbor." + ::= { wlanMeshNeighborEntry 5 } + +wlanMeshNeighborCurrentTXRate OBJECT-TYPE + SYNTAX INTEGER + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The current transmit rate for this neighbor." + ::= { wlanMeshNeighborEntry 6 } + +wlanMeshNeighborRxSignalStrength OBJECT-TYPE + SYNTAX INTEGER + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The average receive signal strength for this neighbor." + ::= { wlanMeshNeighborEntry 7 } + +wlanMeshNeighborIdleTimer OBJECT-TYPE + SYNTAX INTEGER + UNITS "seconds" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The value of this neighbor's inactivity timer." + ::= { wlanMeshNeighborEntry 8 } + +wlanMeshNeighborTxSequenceNo OBJECT-TYPE + SYNTAX INTEGER + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The last sequence number transmitted to this neighbor." + ::= { wlanMeshNeighborEntry 9 } + +wlanMeshNeighborRxSequenceNo OBJECT-TYPE + SYNTAX INTEGER + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The last sequence number received from this neighbor." + ::= { wlanMeshNeighborEntry 10 } + +wlanMeshRouteTable OBJECT-TYPE + SYNTAX SEQUENCE OF WlanMeshRouteEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A table that contains the mesh routing table for interfaces operating + as mesh points, used for forwarding packets on a mesh network." + ::= { wlanMeshRoute 1 } + +wlanMeshRouteEntry OBJECT-TYPE + SYNTAX WlanMeshRouteEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Wireless Mesh Routing Table entries for virtual wireless interfaces." + INDEX { wlanIfaceName, wlanMeshRouteDestination } + ::= { wlanMeshRouteTable 1 } + +WlanMeshRouteEntry ::= SEQUENCE { + wlanMeshRouteDestination MacAddress, + wlanMeshRouteNextHop MacAddress, + wlanMeshRouteHops INTEGER, + wlanMeshRouteMetric Unsigned32, + wlanMeshRouteLifeTime Unsigned32, + wlanMeshRouteLastMseq Unsigned32, + wlanMeshRouteFlags BITS, + wlanMeshRouteStatus RowStatus +} + +wlanMeshRouteDestination OBJECT-TYPE + SYNTAX MacAddress + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The mesh route entry's destination address." + ::= { wlanMeshRouteEntry 1 } + +wlanMeshRouteNextHop OBJECT-TYPE + SYNTAX MacAddress + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The mesh route entry's next hop address." + ::= { wlanMeshRouteEntry 2 } + +wlanMeshRouteHops OBJECT-TYPE + SYNTAX INTEGER + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of hops for this mesh route entry." + ::= { wlanMeshRouteEntry 3 } + +wlanMeshRouteMetric OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The metric of this mesh route entry." + ::= { wlanMeshRouteEntry 4 } + +wlanMeshRouteLifeTime OBJECT-TYPE + SYNTAX Unsigned32 + UNITS "seconds" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The life time of this mesh route entry." + ::= { wlanMeshRouteEntry 5 } + +wlanMeshRouteLastMseq OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The last sequence number seen from this destination." + ::= { wlanMeshRouteEntry 6 } + +wlanMeshRouteFlags OBJECT-TYPE + SYNTAX BITS { + valid(1), + proxy(2) + } + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The Mesh Route entry's flags." + ::= { wlanMeshRouteEntry 7 } + +wlanMeshRouteStatus OBJECT-TYPE + SYNTAX RowStatus + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The object is used to add or delete entries from the mesh routing + table for the virtual wireless interface." + ::= { wlanMeshRouteEntry 8 } + +wlanMeshStatsTable OBJECT-TYPE + SYNTAX SEQUENCE OF WlanMeshStatsEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A table that contains summary statistics for each virtual wireless + interface operating as mesh point." + ::= { wlanMeshStatistics 1 } + +wlanMeshStatsEntry OBJECT-TYPE + SYNTAX WlanMeshStatsEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A list of statistics for each virtual wireless interface operating + as mesh point." + INDEX { wlanIfaceName } + ::= { wlanMeshStatsTable 1 } + +WlanMeshStatsEntry ::= SEQUENCE { + wlanMeshDroppedBadSta Counter32, + wlanMeshDroppedNoLink Counter32, + wlanMeshNoFwdTtl Counter32, + wlanMeshNoFwdBuf Counter32, + wlanMeshNoFwdTooShort Counter32, + wlanMeshNoFwdDisabled Counter32, + wlanMeshNoFwdPathUnknown Counter32, + wlanMeshDroppedBadAE Counter32, + wlanMeshRouteAddFailed Counter32, + wlanMeshDroppedNoProxy Counter32, + wlanMeshDroppedMisaligned Counter32 +} + +wlanMeshDroppedBadSta OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of frames dropped by this interface since they were + received from a non-mesh station." + ::= { wlanMeshStatsEntry 1 } + +wlanMeshDroppedNoLink OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of frames dropped by this interface since no link had + been established." + ::= { wlanMeshStatsEntry 2 } + +wlanMeshNoFwdTtl OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of frames that were not forwarded by this interface + because of a zero TTL." + ::= { wlanMeshStatsEntry 3 } + +wlanMeshNoFwdBuf OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of frames that were not forwarded by this interface + due to lack of free buffers." + ::= { wlanMeshStatsEntry 4 } + +wlanMeshNoFwdTooShort OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of frames that were not forwarded by this interface + due to missing headers." + ::= { wlanMeshStatsEntry 5 } + +wlanMeshNoFwdDisabled OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of frames that were not forwarded by this interface + since forwarding was disabled." + ::= { wlanMeshStatsEntry 6 } + +wlanMeshNoFwdPathUnknown OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of frames that were not forwarded by this interface + since the path was unknown." + ::= { wlanMeshStatsEntry 7 } + +wlanMeshDroppedBadAE OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of frames that were dropped by this interface since + the AE was invalid." + ::= { wlanMeshStatsEntry 8 } + +wlanMeshRouteAddFailed OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of times an addition of a route to the mesh routing + table for this interface failed." + ::= { wlanMeshStatsEntry 9 } + +wlanMeshDroppedNoProxy OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of frames that were dropped by this interface since + proxying was not enabled on the interface." + ::= { wlanMeshStatsEntry 10 } + +wlanMeshDroppedMisaligned OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of frames that were dropped by this interface due to + bad alignment." + ::= { wlanMeshStatsEntry 11 } + +-- ---------------------------------------------------------- -- +-- Subtrees containing data for each supported mesh routing protocol. +-- ---------------------------------------------------------- -- + +wlanMeshProtoHWMP OBJECT IDENTIFIER ::= { wlanMeshRouteProtocols 1 } + +-- ---------------------------------------------------------- -- +-- Hybrid Wireless Mesh Protocol database. +-- ---------------------------------------------------------- -- +wlanMeshHWMPConfig OBJECT IDENTIFIER ::= { wlanMeshProtoHWMP 1 } + +wlanMeshHWMPInterface OBJECT IDENTIFIER ::= { wlanMeshProtoHWMP 2 } + +wlanMeshHWMPStatistics OBJECT IDENTIFIER ::= { wlanMeshProtoHWMP 3 } + +wlanHWMPRouteInactiveTimeout OBJECT-TYPE + SYNTAX INTEGER + UNITS "milliseconds" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The HWMP Route inactivity timeout." + DEFVAL { 5000 } + ::= { wlanMeshHWMPConfig 1 } + +wlanHWMPRootAnnounceInterval OBJECT-TYPE + SYNTAX INTEGER + UNITS "milliseconds" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The HWMP Root Announcement interval." + DEFVAL { 1000 } + ::= { wlanMeshHWMPConfig 2 } + +wlanHWMPRootInterval OBJECT-TYPE + SYNTAX INTEGER + UNITS "milliseconds" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The HWMP Root interval." + DEFVAL { 2000 } + ::= { wlanMeshHWMPConfig 3 } + +wlanHWMPRootTimeout OBJECT-TYPE + SYNTAX INTEGER + UNITS "milliseconds" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The root PREQ timeout." + DEFVAL { 5000 } + ::= { wlanMeshHWMPConfig 4 } + +wlanHWMPPathLifetime OBJECT-TYPE + SYNTAX INTEGER + UNITS "milliseconds" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The HWMP path entry lifetime." + DEFVAL { 500 } + ::= { wlanMeshHWMPConfig 5 } + +wlanHWMPReplyForwardBit OBJECT-TYPE + SYNTAX INTEGER + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "A non-zero value for this object specifies that RF bit shall be + set on generated PREQs." + DEFVAL { 1 } + ::= { wlanMeshHWMPConfig 6 } + +wlanHWMPTargetOnlyBit OBJECT-TYPE + SYNTAX INTEGER + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "A non-zero value for this object specifies that TO bit shall be + set on generated PREQs." + DEFVAL { 0 } + ::= { wlanMeshHWMPConfig 7 } + +wlanHWMPInterfaceTable OBJECT-TYPE + SYNTAX SEQUENCE OF WlanHWMPInterfaceEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A table that contains information for wireless interfaces + operating in mesh mode." + ::= { wlanMeshHWMPInterface 1 } + +wlanHWMPInterfaceEntry OBJECT-TYPE + SYNTAX WlanHWMPInterfaceEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Wireless Mesh Routing HWMP information for a wireless interface." + INDEX { wlanIfaceName } + ::= { wlanHWMPInterfaceTable 1 } + +WlanHWMPInterfaceEntry ::= SEQUENCE { + wlanHWMPRootMode INTEGER, + wlanHWMPMaxHops INTEGER +} + +wlanHWMPRootMode OBJECT-TYPE + SYNTAX INTEGER { + disabled(1), + normal(2), + proactive(3), + rann(4) + } + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "This object is used to configure whether the interface will operate + as root node and specify root node mode." + DEFVAL { disabled } + ::= { wlanHWMPInterfaceEntry 1 } + +wlanHWMPMaxHops OBJECT-TYPE + SYNTAX INTEGER + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The maximum number of hops allowed on an HMWP path for this interface." + DEFVAL { 31 } + ::= { wlanHWMPInterfaceEntry 2 } + +wlanMeshHWMPStatsTable OBJECT-TYPE + SYNTAX SEQUENCE OF WlanMeshHWMPStatsEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A table that contains summary statistics for HWMP operation on an + interface operating as mesh point." + ::= { wlanMeshHWMPStatistics 1 } + +wlanMeshHWMPStatsEntry OBJECT-TYPE + SYNTAX WlanMeshHWMPStatsEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A list of HWMP statistics for each wlan interface operating as HWMP + mesh point." + INDEX { wlanIfaceName } + ::= { wlanMeshHWMPStatsTable 1 } + +WlanMeshHWMPStatsEntry ::= SEQUENCE { + wlanMeshHWMPWrongSeqNo Counter32, + wlanMeshHWMPTxRootPREQ Counter32, + wlanMeshHWMPTxRootRANN Counter32, + wlanMeshHWMPProxy Counter32 +} + +wlanMeshHWMPWrongSeqNo OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of HWMP frames with wrong sequence number received by + this interface." + ::= { wlanMeshHWMPStatsEntry 1 } + +wlanMeshHWMPTxRootPREQ OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of HWMP Root PREQ frames sent by this interface." + ::= { wlanMeshHWMPStatsEntry 2 } + +wlanMeshHWMPTxRootRANN OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of HWMP Root RANN frames sent by this interface." + ::= { wlanMeshHWMPStatsEntry 3 } + +wlanMeshHWMPProxy OBJECT-TYPE + SYNTAX Counter32 + UNITS "frames" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of HWMP PREP frames discarded by this interface due to + the HWMP route being marked as proxy." + ::= { wlanMeshHWMPStatsEntry 4 } + +END diff --git a/usr.sbin/bsnmpd/modules/snmp_wlan/Makefile b/usr.sbin/bsnmpd/modules/snmp_wlan/Makefile new file mode 100644 index 0000000..6a53d46 --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_wlan/Makefile @@ -0,0 +1,15 @@ +# +# $FreeBSD$ +# + +MOD= wlan +SRCS= wlan_snmp.c wlan_sys.c +CFLAGS+= -DSNMPTREE_TYPES + +XSYM= begemotWlan + +BMIBS= BEGEMOT-WIRELESS-MIB.txt +MAN= snmp_${MOD}.3 +DEFS= ${MOD}_tree.def + +.include <bsd.snmpmod.mk> diff --git a/usr.sbin/bsnmpd/modules/snmp_wlan/snmp_wlan.3 b/usr.sbin/bsnmpd/modules/snmp_wlan/snmp_wlan.3 new file mode 100644 index 0000000..6c6af47 --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_wlan/snmp_wlan.3 @@ -0,0 +1,160 @@ +.\"- +.\" Copyright (C) 2010 The FreeBSD Foundation +.\" All rights reserved. +.\" +.\" This documentation was written by Shteryana Sotirova Shopova under +.\" sponsorship from the FreeBSD Foundation. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd June 28, 2010 +.Dt SNMP_WLAN 3 +.Os +.Sh NAME +.Nm snmp_wlan +.Nd "wireless networking module for" +.Xr bsnmpd 1 +.Sh LIBRARY +.Pq begemotSnmpdModulePath."wlan" = "/usr/lib/snmp_wlan.so" +.Sh DESCRIPTION +The +.Nm snmp_wlan +module implements a private BEGEMOT-WIRELESS-MIB, which allows +management of virtual wireless interfaces. The MIB defines objects similar to the +state data and configuration capabilities of +.Xr ifconfig 8 +for configuring virtual wireless interfaces. +Therefore one should consider adding write communities or loading the +.Nm +module on systems where security is crucial. +.Sh IMPLEMENTATION NOTES +A short description of the Tables and interesting objects in the MIB follows. +.Bl -tag -width "XXXXXXXXX" +.It Va wlanInterfaceTable +The table is used for creation and deletion of virtual wireless interfaces. To +add a new interface, a SET should be executed on the +.Va wlanIfaceName +column with +value the desired name of the interface. Next the parent interface must be set +via +.Va wlanParentIfName +column. Any optional parameters may be set +via the +.Va wlanIfaceOperatingMode , +.Va wlanIfaceFlags , +.Va wlanIfaceBssid +and +.Va wlanIfaceLocalAddress +columns. +To finally create the interface in the system, a SET with value of active(1) to +.Va wlanIfaceStatus +column should be executed. +To destroy a wireless interface a SET with value of destroy(6) to the relevant +.Va wlanIfaceStatus +column should be executed. +.It Va wlanIfParentTable +The table contains information about the hardware capabilities of the parent of +a wireless interface. +.It Va wlanIfaceConfigTable +The table is used to get or set various configuration parameters for a virtual +wireless interface. Depending on the operating mode of the interface and the +hardware capabilities of the underlying hardware interface, not all parameters +and values may be supported. +.It Va wlanIfacePeerTable +The table contains information about the associated stations for interfaces +operating as access points, or the stations identified as neighbors in the IBSS +for interfaces operating in adhoc mode. +.It Va wlanIfaceChannelTable +Information about the active channels for the wireless interfaces in the system. +.It Va wlanIfRoamParamsTable +The parameters that govern the roaming operation on the wireless interfaces. +.It Va wlanIfTxParamsTable +The parameters that govern the transmit operation on the wireless interfaces. +.It Va wlanScanConfigTable +The table that contains a configuration for channel scanning initiated via SNMP. +.It Va wlanScanResultsTable +The table contains the scan results from the last scan for each wireless +interface on the system. +.It Va wlanIfaceStatisticsTable +Summary statistics for each wireless interface on the system. +.It Va wlanWepInterfaceTable +WEP configuration for the wireless interfaces on the system. +.It Va wlanMACAccessControlTable +Access Control configuration for wireless interfaces operating as access points. +.It Va wlanMACAccessControlMACTable +The table with Access Control MAC entries for which the configured Access +Control Policy on wireless interfaces operating in Host AP mode is applied. +.Va wlanMACAccessControlMACStatus +column is used to add or delete MAC ACL entries. A set with value createAndGo(4) +will add new entry, while with value destroy(6) will delete an existing one. +.It Va wlanMeshRoutingConfig +The subtree contains system configuration related to Wireless Mesh Routing. +.It Va wlanMeshInterfaceTable +The table contains information for wireless interfaces operating as wireless +mesh points. +.It Va wlanMeshNeighborTable +The table contains information for the neighbors of wireless interfaces +operating in mesh mode. +.It Va wlanMeshRouteTable +The mesh routing table for interfaces operating as mesh points, used for +forwarding packets on a mesh network. +.Va wlanMeshRouteStatus +column is used to add or delete entries in the mesh routing table for an +interface. A set with value createAndGo(4) will add new entry, while with value +destroy(6) will delete an existing one. +.It Va wlanMeshStatsTable +Summary statistics for each virtual wireless interface operating as mesh point. +.It Va wlanMeshHWMPConfig +The subtree contains system configuration related to Hybrid Wireless Mesh +Protocol. +.It Va wlanHWMPInterfaceTable +The table contains HWMP information for wireless interfaces operating in mesh +mode. +.It Va wlanMeshHWMPStatsTable +Summary statistics for HWMP operation on interfaces operating as mesh points. +.El +.Sh RESTRICTIONS +Not all information or configuration in the MIBs is currently available in FreeBSD. +The values of the following variables carry no information: +.Bl -tag -width "XXXXXXXXX" +.It Va wlanStatsReset +.El +.Sh FILES +.Bl -tag -width "XXXXXXXXX" +.It Pa /usr/share/snmp/defs/wlan_tree.def +The description of the MIB tree implemented by +.Nm . +.It Pa /usr/share/snmp/mibs/BEGEMOT-WIRELESS-MIB.txt +The private BEGEMOT-WIRELESS-MIB that is implemented by this module. +.El +.Sh SEE ALSO +.Xr bsnmpd 1 , +.Xr gensnmptree 1 , +.Xr wlan 4 , +.Xr wlan_acl 4 , +.Xr wlan_wep 4 , +.Xr ifconfig 8 , +.Xr snmpmod 3 +.Sh AUTHORS +.An Shteryana Shopova Aq syrinx@FreeBSD.org diff --git a/usr.sbin/bsnmpd/modules/snmp_wlan/wlan_snmp.c b/usr.sbin/bsnmpd/modules/snmp_wlan/wlan_snmp.c new file mode 100644 index 0000000..ec94ac6 --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_wlan/wlan_snmp.c @@ -0,0 +1,4513 @@ +/*- + * Copyright (c) 2010 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Shteryana Sotirova Shopova under + * sponsorship from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include <sys/queue.h> +#include <sys/socket.h> +#include <sys/types.h> + +#include <net/if.h> +#include <net/if_media.h> +#include <net/if_mib.h> +#include <net/if_types.h> +#include <net80211/ieee80211.h> +#include <net80211/ieee80211_ioctl.h> + +#include <errno.h> +#include <stdarg.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <syslog.h> + +#include <bsnmp/snmpmod.h> +#include <bsnmp/snmp_mibII.h> + +#include "wlan_tree.h" +#include "wlan_snmp.h" +#include "wlan_oid.h" + +static struct lmodule *wlan_module; + +/* For the registration. */ +static const struct asn_oid oid_wlan = OIDX_begemotWlan; +/* The registration. */ +static uint reg_wlan; + +/* Periodic timer for polling the module's data. */ +static void *wlan_data_timer; + +/* + * Poll data from kernel every 15 minutes unless explicitly requested by an + * SNMP client. + * XXX: make that configurable. + */ +static int wlan_poll_ticks = (15 * 60) * 100; + +/* The age of each table. */ +#define WLAN_LIST_MAXAGE 5 + +static time_t wlan_iflist_age; +static time_t wlan_peerlist_age; +static time_t wlan_chanlist_age; +static time_t wlan_roamlist_age; +static time_t wlan_tx_paramlist_age; +static time_t wlan_scanlist_age; +static time_t wlan_maclist_age; +static time_t wlan_mrlist_age; + +/* + * The list of all virtual wireless interfaces - sorted by name. + */ +SLIST_HEAD(wlan_ifaces, wlan_iface); +static struct wlan_ifaces wlan_ifaces = SLIST_HEAD_INITIALIZER(wlan_ifaces); + +static struct wlan_config wlan_config; + +/* Forward declarations */ +static int bits_get(struct snmp_value *, const u_char *, ssize_t); + +static int wlan_add_wif(struct wlan_iface *); +static void wlan_delete_wif(struct wlan_iface *); +static int wlan_attach_newif(struct mibif *); +static int wlan_iface_create(struct wlan_iface *); +static int wlan_iface_destroy(struct wlan_iface *); +static struct wlan_iface * wlan_new_wif(char *); + +static void wlan_free_interface(struct wlan_iface *); +static void wlan_free_iflist(void); +static void wlan_free_peerlist(struct wlan_iface *); +static void wlan_scan_free_results(struct wlan_iface *); +static void wlan_mac_free_maclist(struct wlan_iface *); +static void wlan_mesh_free_routes(struct wlan_iface *); + +static int wlan_update_interface(struct wlan_iface *); +static void wlan_update_interface_list(void); +static void wlan_update_peers(void); +static void wlan_update_channels(void); +static void wlan_update_roam_params(void); +static void wlan_update_tx_params(void); +static void wlan_scan_update_results(void); +static void wlan_mac_update_aclmacs(void); +static void wlan_mesh_update_routes(void); + +static struct wlan_iface * wlan_find_interface(const char *); +static struct wlan_peer * wlan_find_peer(struct wlan_iface *, uint8_t *); +static struct ieee80211_channel* wlan_find_channel(struct wlan_iface *, + uint32_t); +static struct wlan_scan_result * wlan_scan_find_result(struct wlan_iface *, + uint8_t *, uint8_t *); +static struct wlan_mac_mac * wlan_mac_find_mac(struct wlan_iface *, + uint8_t *); +static struct wlan_mesh_route * wlan_mesh_find_route(struct wlan_iface *, + uint8_t *); + +static struct wlan_iface * wlan_first_interface(void); +static struct wlan_iface * wlan_next_interface(struct wlan_iface *); +static struct wlan_iface * wlan_mesh_first_interface(void); +static struct wlan_iface * wlan_mesh_next_interface(struct wlan_iface *); + +static struct wlan_iface * wlan_get_interface(const struct asn_oid *, uint); +static struct wlan_iface * wlan_get_snmp_interface(const struct asn_oid *, + uint); +static struct wlan_peer * wlan_get_peer(const struct asn_oid *, uint, + struct wlan_iface **); +static struct ieee80211_channel *wlan_get_channel(const struct asn_oid *, uint, + struct wlan_iface **); +static struct ieee80211_roamparam *wlan_get_roam_param(const struct asn_oid *, + uint, struct wlan_iface **); +static struct ieee80211_txparam *wlan_get_tx_param(const struct asn_oid *, + uint, struct wlan_iface **, uint32_t *); +static struct wlan_scan_result *wlan_get_scanr(const struct asn_oid *, uint, + struct wlan_iface **); +static struct wlan_mac_mac * wlan_get_acl_mac(const struct asn_oid *, + uint, struct wlan_iface **); +static struct wlan_iface * wlan_mesh_get_iface(const struct asn_oid *, uint); +static struct wlan_peer * wlan_mesh_get_peer(const struct asn_oid *, uint, + struct wlan_iface **); +static struct wlan_mesh_route * wlan_mesh_get_route(const struct asn_oid *, + uint, struct wlan_iface **); + +static struct wlan_iface * wlan_get_next_interface(const struct asn_oid *, + uint); +static struct wlan_iface * wlan_get_next_snmp_interface(const struct + asn_oid *, uint); +static struct wlan_peer * wlan_get_next_peer(const struct asn_oid *, uint, + struct wlan_iface **); +static struct ieee80211_channel *wlan_get_next_channel(const struct asn_oid *, + uint, struct wlan_iface **); +static struct ieee80211_roamparam *wlan_get_next_roam_param(const struct + asn_oid *, uint sub, struct wlan_iface **, uint32_t *); +static struct ieee80211_txparam *wlan_get_next_tx_param(const struct asn_oid *, + uint, struct wlan_iface **, uint32_t *); +static struct wlan_scan_result *wlan_get_next_scanr(const struct asn_oid *, + uint , struct wlan_iface **); +static struct wlan_mac_mac * wlan_get_next_acl_mac(const struct asn_oid *, + uint, struct wlan_iface **); +static struct wlan_iface * wlan_mesh_get_next_iface(const struct asn_oid *, + uint); +static struct wlan_peer * wlan_mesh_get_next_peer(const struct asn_oid *, + uint, struct wlan_iface **); +static struct wlan_mesh_route * wlan_mesh_get_next_route(const struct asn_oid *, + uint sub, struct wlan_iface **); + +static uint8_t *wlan_get_ifname(const struct asn_oid *, uint, uint8_t *); +static int wlan_mac_index_decode(const struct asn_oid *, uint, char *, + uint8_t *); +static int wlan_channel_index_decode(const struct asn_oid *, uint, + char *, uint32_t *); +static int wlan_phy_index_decode(const struct asn_oid *, uint, char *, + uint32_t *); +static int wlan_scanr_index_decode(const struct asn_oid *oid, uint sub, + char *wname, uint8_t *ssid, uint8_t *bssid); + +static void wlan_append_ifindex(struct asn_oid *, uint, + const struct wlan_iface *); +static void wlan_append_mac_index(struct asn_oid *, uint, char *, uint8_t *); +static void wlan_append_channel_index(struct asn_oid *, uint, + const struct wlan_iface *, const struct ieee80211_channel *); +static void wlan_append_phy_index(struct asn_oid *, uint, char *, uint32_t); +static void wlan_append_scanr_index(struct asn_oid *, uint, char *, + uint8_t *, uint8_t *); + +static int wlan_acl_mac_set_status(struct snmp_context *, + struct snmp_value *, uint); +static int wlan_mesh_route_set_status(struct snmp_context *, + struct snmp_value *, uint); + +static int32_t wlan_get_channel_type(struct ieee80211_channel *); +static int wlan_scan_compare_result(struct wlan_scan_result *, + struct wlan_scan_result *); +static int wlan_mac_delete_mac(struct wlan_iface *, struct wlan_mac_mac *); +static int wlan_mesh_delete_route(struct wlan_iface *, + struct wlan_mesh_route *); + +/* + * The module's GET/SET data hooks per each table or group of objects as + * required by bsnmpd(1). + */ +int +op_wlan_iface(struct snmp_context *ctx, struct snmp_value *val, uint32_t sub, + uint32_t iidx __unused, enum snmp_op op) +{ + int rc; + char wname[IFNAMSIZ]; + struct wlan_iface *wif; + + wlan_update_interface_list(); + + switch (op) { + case SNMP_OP_GET: + if ((wif = wlan_get_snmp_interface(&val->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + break; + + case SNMP_OP_GETNEXT: + if ((wif = wlan_get_next_snmp_interface(&val->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + wlan_append_ifindex(&val->var, sub, wif); + break; + + case SNMP_OP_SET: + if ((wif = wlan_get_snmp_interface(&val->var, sub)) == NULL) { + if (val->var.subs[sub - 1] != LEAF_wlanIfaceName) + return (SNMP_ERR_NOSUCHNAME); + if (wlan_get_ifname(&val->var, sub, wname) == NULL) + return (SNMP_ERR_INCONS_VALUE); + if ((wif = wlan_new_wif(wname)) == NULL) + return (SNMP_ERR_GENERR); + wif->internal = 1; + } + if (wif->status == RowStatus_active && + val->var.subs[sub - 1] != LEAF_wlanIfaceStatus && + val->var.subs[sub - 1] != LEAF_wlanIfaceState) + return (SNMP_ERR_INCONS_VALUE); + + switch (val->var.subs[sub - 1]) { + case LEAF_wlanIfaceIndex: + return (SNMP_ERR_NOT_WRITEABLE); + + case LEAF_wlanIfaceName: + if (val->v.octetstring.len >= IFNAMSIZ) + return (SNMP_ERR_INCONS_VALUE); + if ((ctx->scratch->ptr1 = malloc(IFNAMSIZ)) == NULL) + return (SNMP_ERR_GENERR); + strlcpy(ctx->scratch->ptr1, wif->wname, IFNAMSIZ); + memcpy(wif->wname, val->v.octetstring.octets, + val->v.octetstring.len); + wif->wname[val->v.octetstring.len] = '\0'; + return (SNMP_ERR_NOERROR); + + case LEAF_wlanParentIfName: + if (val->v.octetstring.len >= IFNAMSIZ) + return (SNMP_ERR_INCONS_VALUE); + if ((ctx->scratch->ptr1 = malloc(IFNAMSIZ)) == NULL) + return (SNMP_ERR_GENERR); + strlcpy(ctx->scratch->ptr1, wif->pname, IFNAMSIZ); + memcpy(wif->pname, val->v.octetstring.octets, + val->v.octetstring.len); + wif->pname[val->v.octetstring.len] = '\0'; + return (SNMP_ERR_NOERROR); + + case LEAF_wlanIfaceOperatingMode: + ctx->scratch->int1 = wif->mode; + wif->mode = val->v.integer; + return (SNMP_ERR_NOERROR); + + case LEAF_wlanIfaceFlags: + if (val->v.octetstring.len > sizeof(wif->flags)) + return (SNMP_ERR_INCONS_VALUE); + ctx->scratch->ptr1 = malloc(sizeof(wif->flags)); + if (ctx->scratch->ptr1 == NULL) + return (SNMP_ERR_GENERR); + memcpy(ctx->scratch->ptr1, (uint8_t *)&wif->flags, + sizeof(wif->flags)); + memcpy((uint8_t *)&wif->flags, val->v.octetstring.octets, + sizeof(wif->flags)); + return (SNMP_ERR_NOERROR); + + case LEAF_wlanIfaceBssid: + if (val->v.octetstring.len != IEEE80211_ADDR_LEN) + return (SNMP_ERR_INCONS_VALUE); + ctx->scratch->ptr1 = malloc(IEEE80211_ADDR_LEN); + if (ctx->scratch->ptr1 == NULL) + return (SNMP_ERR_GENERR); + memcpy(ctx->scratch->ptr1, wif->dbssid, + IEEE80211_ADDR_LEN); + memcpy(wif->dbssid, val->v.octetstring.octets, + IEEE80211_ADDR_LEN); + return (SNMP_ERR_NOERROR); + + case LEAF_wlanIfaceLocalAddress: + if (val->v.octetstring.len != IEEE80211_ADDR_LEN) + return (SNMP_ERR_INCONS_VALUE); + ctx->scratch->ptr1 = malloc(IEEE80211_ADDR_LEN); + if (ctx->scratch->ptr1 == NULL) + return (SNMP_ERR_GENERR); + memcpy(ctx->scratch->ptr1, wif->dlmac, + IEEE80211_ADDR_LEN); + memcpy(wif->dlmac, val->v.octetstring.octets, + IEEE80211_ADDR_LEN); + return (SNMP_ERR_NOERROR); + + case LEAF_wlanIfaceStatus: + ctx->scratch->int1 = wif->status; + wif->status = val->v.integer; + if (wif->status == RowStatus_active) { + rc = wlan_iface_create(wif); /* XXX */ + if (rc != SNMP_ERR_NOERROR) { + wif->status = ctx->scratch->int1; + return (rc); + } + } else if (wif->status == RowStatus_destroy) + return (wlan_iface_destroy(wif)); + else + wif->status = RowStatus_notReady; + return (SNMP_ERR_NOERROR); + + case LEAF_wlanIfaceState: + ctx->scratch->int1 = wif->state; + wif->state = val->v.integer; + if (wif->status == RowStatus_active) + if (wlan_config_state(wif, 1) < 0) + return (SNMP_ERR_GENERR); + return (SNMP_ERR_NOERROR); + } + abort(); + + case SNMP_OP_ROLLBACK: + if ((wif = wlan_get_snmp_interface(&val->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + switch (val->var.subs[sub - 1]) { + case LEAF_wlanIfaceName: + strlcpy(wif->wname, ctx->scratch->ptr1, IFNAMSIZ); + free(ctx->scratch->ptr1); + break; + + case LEAF_wlanParentIfName: + strlcpy(wif->pname, ctx->scratch->ptr1, IFNAMSIZ); + free(ctx->scratch->ptr1); + break; + + case LEAF_wlanIfaceOperatingMode: + wif->mode = ctx->scratch->int1; + break; + + case LEAF_wlanIfaceFlags: + memcpy((uint8_t *)&wif->flags, ctx->scratch->ptr1, + sizeof(wif->flags)); + free(ctx->scratch->ptr1); + break; + + case LEAF_wlanIfaceBssid: + memcpy(wif->dbssid, ctx->scratch->ptr1, + IEEE80211_ADDR_LEN); + free(ctx->scratch->ptr1); + break; + + case LEAF_wlanIfaceLocalAddress: + memcpy(wif->dlmac, ctx->scratch->ptr1, + IEEE80211_ADDR_LEN); + free(ctx->scratch->ptr1); + break; + + case LEAF_wlanIfaceStatus: + wif->status = ctx->scratch->int1; + if (ctx->scratch->int1 == RowStatus_active) + return (SNMP_ERR_GENERR); /* XXX: FIXME */ + else if (wif->internal != 0) + return (wlan_iface_destroy(wif)); + break; + + case LEAF_wlanIfaceState: + wif->state = ctx->scratch->int1; + if (wif->status == RowStatus_active) + if (wlan_config_state(wif, 1) < 0) + return (SNMP_ERR_GENERR); + break; + } + return (SNMP_ERR_NOERROR); + + case SNMP_OP_COMMIT: + switch (val->var.subs[sub - 1]) { + case LEAF_wlanIfaceName: + case LEAF_wlanParentIfName: + case LEAF_wlanIfaceFlags: + case LEAF_wlanIfaceBssid: + case LEAF_wlanIfaceLocalAddress: + free(ctx->scratch->ptr1); + /* FALLTHROUGH */ + default: + return (SNMP_ERR_NOERROR); + } + default: + abort(); + } + + switch (val->var.subs[sub - 1]) { + case LEAF_wlanIfaceIndex: + val->v.integer = wif->index; + return (SNMP_ERR_NOERROR); + case LEAF_wlanIfaceName: + return (string_get(val, wif->wname, -1)); + case LEAF_wlanParentIfName: + return (string_get(val, wif->pname, -1)); + case LEAF_wlanIfaceOperatingMode: + val->v.integer = wif->mode; + return (SNMP_ERR_NOERROR); + case LEAF_wlanIfaceFlags: + return (bits_get(val, (uint8_t *)&wif->flags, + sizeof(wif->flags))); + case LEAF_wlanIfaceBssid: + return (string_get(val, wif->dbssid, IEEE80211_ADDR_LEN)); + case LEAF_wlanIfaceLocalAddress: + return (string_get(val, wif->dlmac, IEEE80211_ADDR_LEN)); + case LEAF_wlanIfaceStatus: + val->v.integer = wif->status; + return (SNMP_ERR_NOERROR); + case LEAF_wlanIfaceState: + val->v.integer = wif->state; + return (SNMP_ERR_NOERROR); + } + + abort(); +} + +int +op_wlan_if_parent(struct snmp_context *ctx __unused, struct snmp_value *val, + uint32_t sub, uint32_t iidx __unused, enum snmp_op op) +{ + struct wlan_iface *wif; + + wlan_update_interface_list(); + + switch (op) { + case SNMP_OP_GET: + if ((wif = wlan_get_interface(&val->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + break; + case SNMP_OP_GETNEXT: + if ((wif = wlan_get_next_interface(&val->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + wlan_append_ifindex(&val->var, sub, wif); + break; + case SNMP_OP_SET: + return (SNMP_ERR_NOT_WRITEABLE); + case SNMP_OP_COMMIT: + /* FALLTHROUGH */ + case SNMP_OP_ROLLBACK: + /* FALLTHROUGH */ + default: + abort(); + } + + switch (val->var.subs[sub - 1]) { + case LEAF_wlanIfParentDriverCapabilities: + return (bits_get(val, (uint8_t *)&wif->drivercaps, + sizeof(wif->drivercaps))); + case LEAF_wlanIfParentCryptoCapabilities: + return (bits_get(val, (uint8_t *)&wif->cryptocaps, + sizeof(wif->cryptocaps))); + case LEAF_wlanIfParentHTCapabilities: + return (bits_get(val, (uint8_t *)&wif->htcaps, + sizeof(wif->htcaps))); + } + + abort(); +} + +int +op_wlan_iface_config(struct snmp_context *ctx, struct snmp_value *val, + uint32_t sub, uint32_t iidx __unused, enum snmp_op op) +{ + int intval, vlen, rc; + char *strval; + struct wlan_iface *wif; + + wlan_update_interface_list(); + + switch (op) { + case SNMP_OP_GET: + if ((wif = wlan_get_interface(&val->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + goto get_config; + + case SNMP_OP_GETNEXT: + if ((wif = wlan_get_next_interface(&val->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + wlan_append_ifindex(&val->var, sub, wif); + goto get_config; + + case SNMP_OP_SET: + if ((wif = wlan_get_interface(&val->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + + intval = val->v.integer; + strval = NULL; + vlen = 0; + + /* Simple sanity checks & save old data. */ + switch (val->var.subs[sub - 1]) { + case LEAF_wlanIfaceCountryCode: + if (val->v.octetstring.len != WLAN_COUNTRY_CODE_SIZE) + return (SNMP_ERR_INCONS_VALUE); + break; + case LEAF_wlanIfaceDesiredSsid: + if (val->v.octetstring.len > IEEE80211_NWID_LEN) + return (SNMP_ERR_INCONS_VALUE); + break; + case LEAF_wlanIfaceDesiredBssid: + if (val->v.octetstring.len != IEEE80211_ADDR_LEN) + return (SNMP_ERR_INCONS_VALUE); + break; + case LEAF_wlanIfacePacketBurst: + ctx->scratch->int1 = wif->packet_burst; + break; + case LEAF_wlanIfaceRegDomain: + ctx->scratch->int1 = wif->reg_domain; + break; + case LEAF_wlanIfaceDesiredChannel: + ctx->scratch->int1 = wif->desired_channel; + break; + case LEAF_wlanIfaceDynamicFreqSelection: + ctx->scratch->int1 = wif->dyn_frequency; + break; + case LEAF_wlanIfaceFastFrames: + ctx->scratch->int1 = wif->fast_frames; + break; + case LEAF_wlanIfaceDturbo: + ctx->scratch->int1 = wif->dturbo; + break; + case LEAF_wlanIfaceTxPower: + ctx->scratch->int1 = wif->tx_power; + break; + case LEAF_wlanIfaceFragmentThreshold: + ctx->scratch->int1 = wif->frag_threshold; + break; + case LEAF_wlanIfaceRTSThreshold: + ctx->scratch->int1 = wif->rts_threshold; + break; + case LEAF_wlanIfaceWlanPrivacySubscribe: + ctx->scratch->int1 = wif->priv_subscribe; + break; + case LEAF_wlanIfaceBgScan: + ctx->scratch->int1 = wif->bg_scan; + break; + case LEAF_wlanIfaceBgScanIdle: + ctx->scratch->int1 = wif->bg_scan_idle; + break; + case LEAF_wlanIfaceBgScanInterval: + ctx->scratch->int1 = wif->bg_scan_interval; + break; + case LEAF_wlanIfaceBeaconMissedThreshold: + ctx->scratch->int1 = wif->beacons_missed; + break; + case LEAF_wlanIfaceRoamingMode: + ctx->scratch->int1 = wif->roam_mode; + break; + case LEAF_wlanIfaceDot11d: + ctx->scratch->int1 = wif->dot11d; + break; + case LEAF_wlanIfaceDot11h: + ctx->scratch->int1 = wif->dot11h; + break; + case LEAF_wlanIfaceDynamicWds: + ctx->scratch->int1 = wif->dynamic_wds; + break; + case LEAF_wlanIfacePowerSave: + ctx->scratch->int1 = wif->power_save; + break; + case LEAF_wlanIfaceApBridge: + ctx->scratch->int1 = wif->ap_bridge; + break; + case LEAF_wlanIfaceBeaconInterval: + ctx->scratch->int1 = wif->beacon_interval; + break; + case LEAF_wlanIfaceDtimPeriod: + ctx->scratch->int1 = wif->dtim_period; + break; + case LEAF_wlanIfaceHideSsid: + ctx->scratch->int1 = wif->hide_ssid; + break; + case LEAF_wlanIfaceInactivityProccess: + ctx->scratch->int1 = wif->inact_process; + break; + case LEAF_wlanIfaceDot11gProtMode: + ctx->scratch->int1 = wif->do11g_protect; + break; + case LEAF_wlanIfaceDot11gPureMode: + ctx->scratch->int1 = wif->dot11g_pure; + break; + case LEAF_wlanIfaceDot11nPureMode: + ctx->scratch->int1 = wif->dot11n_pure; + break; + case LEAF_wlanIfaceDot11nAmpdu: + ctx->scratch->int1 = wif->ampdu; + break; + case LEAF_wlanIfaceDot11nAmpduDensity: + ctx->scratch->int1 = wif->ampdu_density; + break; + case LEAF_wlanIfaceDot11nAmpduLimit: + ctx->scratch->int1 = wif->ampdu_limit; + break; + case LEAF_wlanIfaceDot11nAmsdu: + ctx->scratch->int1 = wif->amsdu; + break; + case LEAF_wlanIfaceDot11nAmsduLimit: + ctx->scratch->int1 = wif->amsdu_limit; + break; + case LEAF_wlanIfaceDot11nHighThroughput: + ctx->scratch->int1 = wif->ht_enabled; + break; + case LEAF_wlanIfaceDot11nHTCompatible: + ctx->scratch->int1 = wif->ht_compatible; + break; + case LEAF_wlanIfaceDot11nHTProtMode: + ctx->scratch->int1 = wif->ht_prot_mode; + break; + case LEAF_wlanIfaceDot11nRIFS: + ctx->scratch->int1 = wif->rifs; + break; + case LEAF_wlanIfaceDot11nShortGI: + ctx->scratch->int1 = wif->short_gi; + break; + case LEAF_wlanIfaceDot11nSMPSMode: + ctx->scratch->int1 = wif->smps_mode; + break; + case LEAF_wlanIfaceTdmaSlot: + ctx->scratch->int1 = wif->tdma_slot; + break; + case LEAF_wlanIfaceTdmaSlotCount: + ctx->scratch->int1 = wif->tdma_slot_count; + break; + case LEAF_wlanIfaceTdmaSlotLength: + ctx->scratch->int1 = wif->tdma_slot_length; + break; + case LEAF_wlanIfaceTdmaBeaconInterval: + ctx->scratch->int1 = wif->tdma_binterval; + break; + default: + abort(); + } + + if (val->syntax != SNMP_SYNTAX_OCTETSTRING) + goto set_config; + + ctx->scratch->int1 = val->v.octetstring.len; + ctx->scratch->ptr1 = malloc(val->v.octetstring.len + 1); + if (ctx->scratch->ptr1 == NULL) + return (SNMP_ERR_GENERR); /* XXX */ + if (val->var.subs[sub - 1] == LEAF_wlanIfaceDesiredSsid) + strlcpy(ctx->scratch->ptr1, val->v.octetstring.octets, + val->v.octetstring.len + 1); + else + memcpy(ctx->scratch->ptr1, val->v.octetstring.octets, + val->v.octetstring.len); + strval = val->v.octetstring.octets; + vlen = val->v.octetstring.len; + goto set_config; + + case SNMP_OP_ROLLBACK: + intval = ctx->scratch->int1; + strval = NULL; + vlen = 0; + + if ((wif = wlan_get_interface(&val->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + switch (val->var.subs[sub - 1]) { + case LEAF_wlanIfaceCountryCode: + case LEAF_wlanIfaceDesiredSsid: + case LEAF_wlanIfaceDesiredBssid: + strval = ctx->scratch->ptr1; + vlen = ctx->scratch->int1; + break; + default: + break; + } + goto set_config; + + case SNMP_OP_COMMIT: + switch (val->var.subs[sub - 1]) { + case LEAF_wlanIfaceCountryCode: + case LEAF_wlanIfaceDesiredSsid: + case LEAF_wlanIfaceDesiredBssid: + free(ctx->scratch->ptr1); + /* FALLTHROUGH */ + default: + return (SNMP_ERR_NOERROR); + } + } + abort(); + +get_config: + + if (wlan_config_get_ioctl(wif, val->var.subs[sub - 1]) < 0) + return (SNMP_ERR_GENERR); + + switch (val->var.subs[sub - 1]) { + case LEAF_wlanIfacePacketBurst: + val->v.integer = wif->packet_burst; + break; + case LEAF_wlanIfaceCountryCode: + return (string_get(val, wif->country_code, + WLAN_COUNTRY_CODE_SIZE)); + case LEAF_wlanIfaceRegDomain: + val->v.integer = wif->reg_domain; + break; + case LEAF_wlanIfaceDesiredSsid: + return (string_get(val, wif->desired_ssid, -1)); + case LEAF_wlanIfaceDesiredChannel: + val->v.integer = wif->desired_channel; + break; + case LEAF_wlanIfaceDynamicFreqSelection: + val->v.integer = wif->dyn_frequency; + break; + case LEAF_wlanIfaceFastFrames: + val->v.integer = wif->fast_frames; + break; + case LEAF_wlanIfaceDturbo: + val->v.integer = wif->dturbo; + break; + case LEAF_wlanIfaceTxPower: + val->v.integer = wif->tx_power; + break; + case LEAF_wlanIfaceFragmentThreshold: + val->v.integer = wif->frag_threshold; + break; + case LEAF_wlanIfaceRTSThreshold: + val->v.integer = wif->rts_threshold; + break; + case LEAF_wlanIfaceWlanPrivacySubscribe: + val->v.integer = wif->priv_subscribe; + break; + case LEAF_wlanIfaceBgScan: + val->v.integer = wif->bg_scan; + break; + case LEAF_wlanIfaceBgScanIdle: + val->v.integer = wif->bg_scan_idle; + break; + case LEAF_wlanIfaceBgScanInterval: + val->v.integer = wif->bg_scan_interval; + break; + case LEAF_wlanIfaceBeaconMissedThreshold: + val->v.integer = wif->beacons_missed; + break; + case LEAF_wlanIfaceDesiredBssid: + return (string_get(val, wif->desired_bssid, + IEEE80211_ADDR_LEN)); + case LEAF_wlanIfaceRoamingMode: + val->v.integer = wif->roam_mode; + break; + case LEAF_wlanIfaceDot11d: + val->v.integer = wif->dot11d; + break; + case LEAF_wlanIfaceDot11h: + val->v.integer = wif->dot11h; + break; + case LEAF_wlanIfaceDynamicWds: + val->v.integer = wif->dynamic_wds; + break; + case LEAF_wlanIfacePowerSave: + val->v.integer = wif->power_save; + break; + case LEAF_wlanIfaceApBridge: + val->v.integer = wif->ap_bridge; + break; + case LEAF_wlanIfaceBeaconInterval: + val->v.integer = wif->beacon_interval; + break; + case LEAF_wlanIfaceDtimPeriod: + val->v.integer = wif->dtim_period; + break; + case LEAF_wlanIfaceHideSsid: + val->v.integer = wif->hide_ssid; + break; + case LEAF_wlanIfaceInactivityProccess: + val->v.integer = wif->inact_process; + break; + case LEAF_wlanIfaceDot11gProtMode: + val->v.integer = wif->do11g_protect; + break; + case LEAF_wlanIfaceDot11gPureMode: + val->v.integer = wif->dot11g_pure; + break; + case LEAF_wlanIfaceDot11nPureMode: + val->v.integer = wif->dot11n_pure; + break; + case LEAF_wlanIfaceDot11nAmpdu: + val->v.integer = wif->ampdu; + break; + case LEAF_wlanIfaceDot11nAmpduDensity: + val->v.integer = wif->ampdu_density; + break; + case LEAF_wlanIfaceDot11nAmpduLimit: + val->v.integer = wif->ampdu_limit; + break; + case LEAF_wlanIfaceDot11nAmsdu: + val->v.integer = wif->amsdu; + break; + case LEAF_wlanIfaceDot11nAmsduLimit: + val->v.integer = wif->amsdu_limit; + break; + case LEAF_wlanIfaceDot11nHighThroughput: + val->v.integer = wif->ht_enabled; + break; + case LEAF_wlanIfaceDot11nHTCompatible: + val->v.integer = wif->ht_compatible; + break; + case LEAF_wlanIfaceDot11nHTProtMode: + val->v.integer = wif->ht_prot_mode; + break; + case LEAF_wlanIfaceDot11nRIFS: + val->v.integer = wif->rifs; + break; + case LEAF_wlanIfaceDot11nShortGI: + val->v.integer = wif->short_gi; + break; + case LEAF_wlanIfaceDot11nSMPSMode: + val->v.integer = wif->smps_mode; + break; + case LEAF_wlanIfaceTdmaSlot: + val->v.integer = wif->tdma_slot; + break; + case LEAF_wlanIfaceTdmaSlotCount: + val->v.integer = wif->tdma_slot_count; + break; + case LEAF_wlanIfaceTdmaSlotLength: + val->v.integer = wif->tdma_slot_length; + break; + case LEAF_wlanIfaceTdmaBeaconInterval: + val->v.integer = wif->tdma_binterval; + break; + } + + return (SNMP_ERR_NOERROR); + +set_config: + rc = wlan_config_set_ioctl(wif, val->var.subs[sub - 1], intval, + strval, vlen); + + if (op == SNMP_OP_ROLLBACK) { + switch (val->var.subs[sub - 1]) { + case LEAF_wlanIfaceCountryCode: + case LEAF_wlanIfaceDesiredSsid: + case LEAF_wlanIfaceDesiredBssid: + free(ctx->scratch->ptr1); + /* FALLTHROUGH */ + default: + break; + } + } + + if (rc < 0) + return (SNMP_ERR_GENERR); + + return (SNMP_ERR_NOERROR); +} + +int +op_wlan_if_peer(struct snmp_context *ctx, struct snmp_value *val, uint32_t sub, + uint32_t iidx __unused, enum snmp_op op) +{ + struct wlan_peer *wip; + struct wlan_iface *wif; + + wlan_update_interface_list(); + wlan_update_peers(); + + switch (op) { + case SNMP_OP_GET: + if ((wip = wlan_get_peer(&val->var, sub, &wif)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + break; + case SNMP_OP_GETNEXT: + if ((wip = wlan_get_next_peer(&val->var, sub, &wif)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + wlan_append_mac_index(&val->var, sub, wif->wname, wip->pmac); + break; + case SNMP_OP_SET: + if ((wip = wlan_get_peer(&val->var, sub, &wif)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + if (val->var.subs[sub - 1] != LEAF_wlanIfacePeerVlanTag) + return (SNMP_ERR_GENERR); + ctx->scratch->int1 = wip->vlan; + if (wlan_peer_set_vlan(wif, wip, val->v.integer) < 0) + return (SNMP_ERR_GENERR); + return (SNMP_ERR_NOERROR); + case SNMP_OP_COMMIT: + return (SNMP_ERR_NOERROR); + case SNMP_OP_ROLLBACK: + if ((wip = wlan_get_peer(&val->var, sub, &wif)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + if (val->var.subs[sub - 1] != LEAF_wlanIfacePeerVlanTag) + return (SNMP_ERR_GENERR); + if (wlan_peer_set_vlan(wif, wip, ctx->scratch->int1) < 0) + return (SNMP_ERR_GENERR); + return (SNMP_ERR_NOERROR); + default: + abort(); + } + + switch (val->var.subs[sub - 1]) { + case LEAF_wlanIfacePeerAddress: + return (string_get(val, wip->pmac, IEEE80211_ADDR_LEN)); + case LEAF_wlanIfacePeerAssociationId: + val->v.integer = wip->associd; + break; + case LEAF_wlanIfacePeerVlanTag: + val->v.integer = wip->vlan; + break; + case LEAF_wlanIfacePeerFrequency: + val->v.integer = wip->frequency; + break; + case LEAF_wlanIfacePeerCurrentTXRate: + val->v.integer = wip->txrate; + break; + case LEAF_wlanIfacePeerRxSignalStrength: + val->v.integer = wip->rssi; + break; + case LEAF_wlanIfacePeerIdleTimer: + val->v.integer = wip->idle; + break; + case LEAF_wlanIfacePeerTxSequenceNo: + val->v.integer = wip->txseqs; + break; + case LEAF_wlanIfacePeerRxSequenceNo: + val->v.integer = wip->rxseqs; + break; + case LEAF_wlanIfacePeerTxPower: + val->v.integer = wip->txpower; + break; + case LEAF_wlanIfacePeerCapabilities: + return (bits_get(val, (uint8_t *)&wip->capinfo, + sizeof(wip->capinfo))); + case LEAF_wlanIfacePeerFlags: + return (bits_get(val, (uint8_t *)&wip->state, + sizeof(wip->state))); + default: + abort(); + } + + return (SNMP_ERR_NOERROR); +} + +int +op_wlan_channels(struct snmp_context *ctx __unused, struct snmp_value *val, + uint32_t sub, uint32_t iidx __unused, enum snmp_op op) +{ + int32_t bits; + struct ieee80211_channel *channel; + struct wlan_iface *wif; + + wlan_update_interface_list(); + wlan_update_channels(); + + switch (op) { + case SNMP_OP_GET: + if ((channel = wlan_get_channel(&val->var, sub, &wif)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + break; + case SNMP_OP_GETNEXT: + channel = wlan_get_next_channel(&val->var, sub, &wif); + if (channel == NULL || wif == NULL) + return (SNMP_ERR_NOSUCHNAME); + wlan_append_channel_index(&val->var, sub, wif, channel); + break; + case SNMP_OP_SET: + return (SNMP_ERR_NOT_WRITEABLE); + case SNMP_OP_COMMIT: + /* FALLTHROUGH */ + case SNMP_OP_ROLLBACK: + /* FALLTHROUGH */ + default: + abort(); + } + + switch (val->var.subs[sub - 1]) { + case LEAF_wlanIfaceChannelIeeeId: + val->v.integer = channel->ic_ieee; + break; + case LEAF_wlanIfaceChannelType: + val->v.integer = wlan_get_channel_type(channel); + break; + case LEAF_wlanIfaceChannelFlags: + bits = wlan_channel_flags_to_snmp(channel->ic_flags); + return (bits_get(val, (uint8_t *)&bits, sizeof(bits))); + case LEAF_wlanIfaceChannelFrequency: + val->v.integer = channel->ic_freq; + break; + case LEAF_wlanIfaceChannelMaxRegPower: + val->v.integer = channel->ic_maxregpower; + break; + case LEAF_wlanIfaceChannelMaxTxPower: + val->v.integer = channel->ic_maxpower; + break; + case LEAF_wlanIfaceChannelMinTxPower: + val->v.integer = channel->ic_minpower; + break; + case LEAF_wlanIfaceChannelState: + bits = wlan_channel_state_to_snmp(channel->ic_state); + return (bits_get(val, (uint8_t *)&bits, sizeof(bits))); + case LEAF_wlanIfaceChannelHTExtension: + val->v.integer = channel->ic_extieee; + break; + case LEAF_wlanIfaceChannelMaxAntennaGain: + val->v.integer = channel->ic_maxantgain; + break; + } + + return (SNMP_ERR_NOERROR); +} + +int +op_wlan_roam_params(struct snmp_context *ctx __unused, struct snmp_value *val, + uint32_t sub, uint32_t iidx __unused, enum snmp_op op) +{ + uint32_t phy; + struct ieee80211_roamparam *rparam; + struct wlan_iface *wif; + + wlan_update_interface_list(); + wlan_update_roam_params(); + + switch (op) { + case SNMP_OP_GET: + rparam = wlan_get_roam_param(&val->var, sub, &wif); + if (rparam == NULL) + return (SNMP_ERR_NOSUCHNAME); + break; + case SNMP_OP_GETNEXT: + rparam = wlan_get_next_roam_param(&val->var, sub, &wif, &phy); + if (rparam == NULL || wif == NULL) + return (SNMP_ERR_NOSUCHNAME); + wlan_append_phy_index(&val->var, sub, wif->wname, phy); + break; + case SNMP_OP_SET: + return (SNMP_ERR_NOT_WRITEABLE); + case SNMP_OP_COMMIT: + /* FALLTHROUGH */ + case SNMP_OP_ROLLBACK: + /* FALLTHROUGH */ + default: + abort(); + } + + switch (val->var.subs[sub - 1]) { + case LEAF_wlanIfRoamRxSignalStrength: + val->v.integer = rparam->rssi/2; + break; + case LEAF_wlanIfRoamTxRateThreshold: + val->v.integer = rparam->rate/2; + break; + default: + abort(); + } + + return (SNMP_ERR_NOERROR); +} + +int +op_wlan_tx_params(struct snmp_context *ctx, struct snmp_value *val, + uint32_t sub, uint32_t iidx __unused, enum snmp_op op) +{ + uint32_t phy; + struct ieee80211_txparam *txparam; + struct wlan_iface *wif; + + wlan_update_interface_list(); + wlan_update_tx_params(); + + switch (op) { + case SNMP_OP_GET: + txparam = wlan_get_tx_param(&val->var, sub, &wif, &phy); + if (txparam == NULL) + return (SNMP_ERR_NOSUCHNAME); + goto get_txparams; + + case SNMP_OP_GETNEXT: + txparam = wlan_get_next_tx_param(&val->var, sub, &wif, &phy); + if (txparam == NULL || wif == NULL) + return (SNMP_ERR_NOSUCHNAME); + wlan_append_phy_index(&val->var, sub, wif->wname, phy); + goto get_txparams; + + case SNMP_OP_SET: + txparam = wlan_get_tx_param(&val->var, sub, &wif, &phy); + if (txparam == NULL || wif == NULL) + return (SNMP_ERR_NOSUCHNAME); + switch (val->var.subs[sub - 1]) { + case LEAF_wlanIfTxUnicastRate: + ctx->scratch->int1 = txparam->ucastrate; + txparam->ucastrate = val->v.integer * 2; + break; + case LEAF_wlanIfTxMcastRate: + ctx->scratch->int1 = txparam->mcastrate; + txparam->mcastrate = val->v.integer * 2; + break; + case LEAF_wlanIfTxMgmtRate: + ctx->scratch->int1 = txparam->mgmtrate; + txparam->mgmtrate = val->v.integer * 2; + break; + case LEAF_wlanIfTxMaxRetryCount: + ctx->scratch->int1 = txparam->maxretry; + txparam->maxretry = val->v.integer; + break; + default: + abort(); + } + if (wlan_set_tx_params(wif, phy) < 0) + return (SNMP_ERR_GENERR); + return (SNMP_ERR_NOERROR); + + case SNMP_OP_COMMIT: + return (SNMP_ERR_NOERROR); + + case SNMP_OP_ROLLBACK: + txparam = wlan_get_tx_param(&val->var, sub, &wif, &phy); + if (txparam == NULL || wif == NULL) + return (SNMP_ERR_NOSUCHNAME); + switch (val->var.subs[sub - 1]) { + case LEAF_wlanIfTxUnicastRate: + txparam->ucastrate = ctx->scratch->int1; + break; + case LEAF_wlanIfTxMcastRate: + txparam->mcastrate = ctx->scratch->int1; + break; + case LEAF_wlanIfTxMgmtRate: + txparam->mgmtrate = ctx->scratch->int1; + break; + case LEAF_wlanIfTxMaxRetryCount: + txparam->maxretry = ctx->scratch->int1; + break; + default: + abort(); + } + if (wlan_set_tx_params(wif, phy) < 0) + return (SNMP_ERR_GENERR); + return (SNMP_ERR_NOERROR); + default: + abort(); + } + +get_txparams: + switch (val->var.subs[sub - 1]) { + case LEAF_wlanIfTxUnicastRate: + val->v.integer = txparam->ucastrate / 2; + break; + case LEAF_wlanIfTxMcastRate: + val->v.integer = txparam->mcastrate / 2; + break; + case LEAF_wlanIfTxMgmtRate: + val->v.integer = txparam->mgmtrate / 2; + break; + case LEAF_wlanIfTxMaxRetryCount: + val->v.integer = txparam->maxretry; + break; + default: + abort(); + } + + return (SNMP_ERR_NOERROR); +} + +int +op_wlan_scan_config(struct snmp_context *ctx, struct snmp_value *val, + uint32_t sub, uint32_t iidx __unused, enum snmp_op op) +{ + struct wlan_iface *wif; + + wlan_update_interface_list(); + + switch (op) { + case SNMP_OP_GET: + if ((wif = wlan_get_interface(&val->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + break; + + case SNMP_OP_GETNEXT: + if ((wif = wlan_get_next_interface(&val->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + wlan_append_ifindex(&val->var, sub, wif); + break; + + case SNMP_OP_SET: + if ((wif = wlan_get_interface(&val->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + if (wif->scan_status == wlanScanConfigStatus_running + && val->var.subs[sub - 1] != LEAF_wlanScanConfigStatus) + return (SNMP_ERR_INCONS_VALUE); + switch (val->var.subs[sub - 1]) { + case LEAF_wlanScanFlags: + ctx->scratch->int1 = wif->scan_flags; + wif->scan_flags = val->v.integer; + break; + case LEAF_wlanScanDuration: + ctx->scratch->int1 = wif->scan_duration; + wif->scan_duration = val->v.integer; + break; + case LEAF_wlanScanMinChannelDwellTime: + ctx->scratch->int1 = wif->scan_mindwell; + wif->scan_mindwell = val->v.integer; + break; + case LEAF_wlanScanMaxChannelDwellTime: + ctx->scratch->int1 = wif->scan_maxdwell; + wif->scan_maxdwell = val->v.integer; + break; + case LEAF_wlanScanConfigStatus: + if (val->v.integer == wlanScanConfigStatus_running || + val->v.integer == wlanScanConfigStatus_cancel) { + ctx->scratch->int1 = wif->scan_status; + wif->scan_status = val->v.integer; + break; + } + return (SNMP_ERR_INCONS_VALUE); + } + return (SNMP_ERR_NOERROR); + + case SNMP_OP_COMMIT: + if ((wif = wlan_get_interface(&val->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + if (val->var.subs[sub - 1] == LEAF_wlanScanConfigStatus) + if (wif->scan_status == wlanScanConfigStatus_running) + (void)wlan_set_scan_config(wif); /* XXX */ + return (SNMP_ERR_NOERROR); + + case SNMP_OP_ROLLBACK: + if ((wif = wlan_get_interface(&val->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + switch (val->var.subs[sub - 1]) { + case LEAF_wlanScanFlags: + wif->scan_flags = ctx->scratch->int1; + break; + case LEAF_wlanScanDuration: + wif->scan_duration = ctx->scratch->int1; + break; + case LEAF_wlanScanMinChannelDwellTime: + wif->scan_mindwell = ctx->scratch->int1; + break; + case LEAF_wlanScanMaxChannelDwellTime: + wif->scan_maxdwell = ctx->scratch->int1; + break; + case LEAF_wlanScanConfigStatus: + wif->scan_status = ctx->scratch->int1; + break; + } + return (SNMP_ERR_NOERROR); + default: + abort(); + } + + switch (val->var.subs[sub - 1]) { + case LEAF_wlanScanFlags: + val->v.integer = wif->scan_flags; + break; + case LEAF_wlanScanDuration: + val->v.integer = wif->scan_duration; + break; + case LEAF_wlanScanMinChannelDwellTime: + val->v.integer = wif->scan_mindwell; + break; + case LEAF_wlanScanMaxChannelDwellTime: + val->v.integer = wif->scan_maxdwell; + break; + case LEAF_wlanScanConfigStatus: + val->v.integer = wif->scan_status; + break; + } + + return (SNMP_ERR_NOERROR); +} + +int +op_wlan_scan_results(struct snmp_context *ctx __unused, struct snmp_value *val, + uint32_t sub, uint32_t iidx __unused, enum snmp_op op) +{ + struct wlan_scan_result *sr; + struct wlan_iface *wif; + + wlan_update_interface_list(); + wlan_scan_update_results(); + + switch (op) { + case SNMP_OP_GET: + if ((sr = wlan_get_scanr(&val->var, sub, &wif)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + break; + + case SNMP_OP_GETNEXT: + if ((sr = wlan_get_next_scanr(&val->var, sub, &wif)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + wlan_append_scanr_index(&val->var, sub, wif->wname, sr->ssid, + sr->bssid); + break; + + case SNMP_OP_SET: + return (SNMP_ERR_NOT_WRITEABLE); + case SNMP_OP_COMMIT: + /* FALLTHROUGH */ + case SNMP_OP_ROLLBACK: + /* FALLTHROUGH */ + default: + abort(); + } + + switch (val->var.subs[sub - 1]) { + case LEAF_wlanScanResultID: + return (string_get(val, sr->ssid, -1)); + case LEAF_wlanScanResultBssid: + return (string_get(val, sr->bssid, IEEE80211_ADDR_LEN)); + case LEAF_wlanScanResultChannel: + val->v.integer = sr->opchannel; /* XXX */ + break; + case LEAF_wlanScanResultRate: + val->v.integer = sr->rssi; + break; + case LEAF_wlanScanResultNoise: + val->v.integer = sr->noise; + break; + case LEAF_wlanScanResultBeaconInterval: + val->v.integer = sr->bintval; + break; + case LEAF_wlanScanResultCapabilities: + return (bits_get(val, &sr->capinfo, sizeof(sr->capinfo))); + default: + abort(); + } + + return (SNMP_ERR_NOERROR); +} + +int +op_wlan_iface_stats(struct snmp_context *ctx __unused, struct snmp_value *val, + uint32_t sub, uint32_t iidx __unused, enum snmp_op op) +{ + struct wlan_iface *wif; + + wlan_update_interface_list(); + + switch (op) { + case SNMP_OP_GET: + if ((wif = wlan_get_interface(&val->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + break; + case SNMP_OP_GETNEXT: + if ((wif = wlan_get_next_interface(&val->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + wlan_append_ifindex(&val->var, sub, wif); + break; + case SNMP_OP_SET: + /* XXX: LEAF_wlanStatsReset */ + return (SNMP_ERR_NOT_WRITEABLE); + case SNMP_OP_COMMIT: + /* FALLTHROUGH */ + case SNMP_OP_ROLLBACK: + /* FALLTHROUGH */ + default: + abort(); + } + + if (wlan_get_stats(wif) < 0) + return (SNMP_ERR_GENERR); + + switch (val->var.subs[sub - 1]) { + case LEAF_wlanStatsRxBadVersion: + val->v.uint32 = wif->stats.is_rx_badversion; + break; + case LEAF_wlanStatsRxTooShort: + val->v.uint32 = wif->stats.is_rx_tooshort; + break; + case LEAF_wlanStatsRxWrongBssid: + val->v.uint32 = wif->stats.is_rx_wrongbss; + break; + case LEAF_wlanStatsRxDiscardedDups: + val->v.uint32 = wif->stats.is_rx_dup; + break; + case LEAF_wlanStatsRxWrongDir: + val->v.uint32 = wif->stats.is_rx_wrongdir; + break; + case LEAF_wlanStatsRxDiscardMcastEcho: + val->v.uint32 = wif->stats.is_rx_mcastecho; + break; + case LEAF_wlanStatsRxDiscardNoAssoc: + val->v.uint32 = wif->stats.is_rx_notassoc; + break; + case LEAF_wlanStatsRxWepNoPrivacy: + val->v.uint32 = wif->stats.is_rx_noprivacy; + break; + case LEAF_wlanStatsRxWepUnencrypted: + val->v.uint32 = wif->stats.is_rx_unencrypted; + break; + case LEAF_wlanStatsRxWepFailed: + val->v.uint32 = wif->stats.is_rx_wepfail; + break; + case LEAF_wlanStatsRxDecapsulationFailed: + val->v.uint32 = wif->stats.is_rx_decap; + break; + case LEAF_wlanStatsRxDiscardMgmt: + val->v.uint32 = wif->stats.is_rx_mgtdiscard; + break; + case LEAF_wlanStatsRxControl: + val->v.uint32 = wif->stats.is_rx_ctl; + break; + case LEAF_wlanStatsRxBeacon: + val->v.uint32 = wif->stats.is_rx_beacon; + break; + case LEAF_wlanStatsRxRateSetTooBig: + val->v.uint32 = wif->stats.is_rx_rstoobig; + break; + case LEAF_wlanStatsRxElemMissing: + val->v.uint32 = wif->stats.is_rx_elem_missing; + break; + case LEAF_wlanStatsRxElemTooBig: + val->v.uint32 = wif->stats.is_rx_elem_toobig; + break; + case LEAF_wlanStatsRxElemTooSmall: + val->v.uint32 = wif->stats.is_rx_elem_toosmall; + break; + case LEAF_wlanStatsRxElemUnknown: + val->v.uint32 = wif->stats.is_rx_elem_unknown; + break; + case LEAF_wlanStatsRxChannelMismatch: + val->v.uint32 = wif->stats.is_rx_chanmismatch; + break; + case LEAF_wlanStatsRxDropped: + val->v.uint32 = wif->stats.is_rx_nodealloc; + break; + case LEAF_wlanStatsRxSsidMismatch: + val->v.uint32 = wif->stats.is_rx_ssidmismatch; + break; + case LEAF_wlanStatsRxAuthNotSupported: + val->v.uint32 = wif->stats.is_rx_auth_unsupported; + break; + case LEAF_wlanStatsRxAuthFailed: + val->v.uint32 = wif->stats.is_rx_auth_fail; + break; + case LEAF_wlanStatsRxAuthCM: + val->v.uint32 = wif->stats.is_rx_auth_countermeasures; + break; + case LEAF_wlanStatsRxAssocWrongBssid: + val->v.uint32 = wif->stats.is_rx_assoc_bss; + break; + case LEAF_wlanStatsRxAssocNoAuth: + val->v.uint32 = wif->stats.is_rx_assoc_notauth; + break; + case LEAF_wlanStatsRxAssocCapMismatch: + val->v.uint32 = wif->stats.is_rx_assoc_capmismatch; + break; + case LEAF_wlanStatsRxAssocNoRateMatch: + val->v.uint32 = wif->stats.is_rx_assoc_norate; + break; + case LEAF_wlanStatsRxBadWpaIE: + val->v.uint32 = wif->stats.is_rx_assoc_badwpaie; + break; + case LEAF_wlanStatsRxDeauthenticate: + val->v.uint32 = wif->stats.is_rx_deauth; + break; + case LEAF_wlanStatsRxDisassociate: + val->v.uint32 = wif->stats.is_rx_disassoc; + break; + case LEAF_wlanStatsRxUnknownSubtype: + val->v.uint32 = wif->stats.is_rx_badsubtype; + break; + case LEAF_wlanStatsRxFailedNoBuf: + val->v.uint32 = wif->stats.is_rx_nobuf; + break; + case LEAF_wlanStatsRxBadAuthRequest: + val->v.uint32 = wif->stats.is_rx_bad_auth; + break; + case LEAF_wlanStatsRxUnAuthorized: + val->v.uint32 = wif->stats.is_rx_unauth; + break; + case LEAF_wlanStatsRxBadKeyId: + val->v.uint32 = wif->stats.is_rx_badkeyid; + break; + case LEAF_wlanStatsRxCCMPSeqViolation: + val->v.uint32 = wif->stats.is_rx_ccmpreplay; + break; + case LEAF_wlanStatsRxCCMPBadFormat: + val->v.uint32 = wif->stats.is_rx_ccmpformat; + break; + case LEAF_wlanStatsRxCCMPFailedMIC: + val->v.uint32 = wif->stats.is_rx_ccmpmic; + break; + case LEAF_wlanStatsRxTKIPSeqViolation: + val->v.uint32 = wif->stats.is_rx_tkipreplay; + break; + case LEAF_wlanStatsRxTKIPBadFormat: + val->v.uint32 = wif->stats.is_rx_tkipformat; + break; + case LEAF_wlanStatsRxTKIPFailedMIC: + val->v.uint32 = wif->stats.is_rx_tkipmic; + break; + case LEAF_wlanStatsRxTKIPFailedICV: + val->v.uint32 = wif->stats.is_rx_tkipicv; + break; + case LEAF_wlanStatsRxDiscardACL: + val->v.uint32 = wif->stats.is_rx_acl; + break; + case LEAF_wlanStatsTxFailedNoBuf: + val->v.uint32 = wif->stats.is_tx_nobuf; + break; + case LEAF_wlanStatsTxFailedNoNode: + val->v.uint32 = wif->stats.is_tx_nonode; + break; + case LEAF_wlanStatsTxUnknownMgmt: + val->v.uint32 = wif->stats.is_tx_unknownmgt; + break; + case LEAF_wlanStatsTxBadCipher: + val->v.uint32 = wif->stats.is_tx_badcipher; + break; + case LEAF_wlanStatsTxNoDefKey: + val->v.uint32 = wif->stats.is_tx_nodefkey; + break; + case LEAF_wlanStatsTxFragmented: + val->v.uint32 = wif->stats.is_tx_fragframes; + break; + case LEAF_wlanStatsTxFragmentsCreated: + val->v.uint32 = wif->stats.is_tx_frags; + break; + case LEAF_wlanStatsActiveScans: + val->v.uint32 = wif->stats.is_scan_active; + break; + case LEAF_wlanStatsPassiveScans: + val->v.uint32 = wif->stats.is_scan_passive; + break; + case LEAF_wlanStatsTimeoutInactivity: + val->v.uint32 = wif->stats.is_node_timeout; + break; + case LEAF_wlanStatsCryptoNoMem: + val->v.uint32 = wif->stats.is_crypto_nomem; + break; + case LEAF_wlanStatsSwCryptoTKIP: + val->v.uint32 = wif->stats.is_crypto_tkip; + break; + case LEAF_wlanStatsSwCryptoTKIPEnMIC: + val->v.uint32 = wif->stats.is_crypto_tkipenmic; + break; + case LEAF_wlanStatsSwCryptoTKIPDeMIC: + val->v.uint32 = wif->stats.is_crypto_tkipdemic; + break; + case LEAF_wlanStatsCryptoTKIPCM: + val->v.uint32 = wif->stats.is_crypto_tkipcm; + break; + case LEAF_wlanStatsSwCryptoCCMP: + val->v.uint32 = wif->stats.is_crypto_ccmp; + break; + case LEAF_wlanStatsSwCryptoWEP: + val->v.uint32 = wif->stats.is_crypto_wep; + break; + case LEAF_wlanStatsCryptoCipherKeyRejected: + val->v.uint32 = wif->stats.is_crypto_setkey_cipher; + break; + case LEAF_wlanStatsCryptoNoKey: + val->v.uint32 = wif->stats.is_crypto_setkey_nokey; + break; + case LEAF_wlanStatsCryptoDeleteKeyFailed: + val->v.uint32 = wif->stats.is_crypto_delkey; + break; + case LEAF_wlanStatsCryptoUnknownCipher: + val->v.uint32 = wif->stats.is_crypto_badcipher; + break; + case LEAF_wlanStatsCryptoAttachFailed: + val->v.uint32 = wif->stats.is_crypto_attachfail; + break; + case LEAF_wlanStatsCryptoKeyFailed: + val->v.uint32 = wif->stats.is_crypto_keyfail; + break; + case LEAF_wlanStatsCryptoEnMICFailed: + val->v.uint32 = wif->stats.is_crypto_enmicfail; + break; + case LEAF_wlanStatsIBSSCapMismatch: + val->v.uint32 = wif->stats.is_ibss_capmismatch; + break; + case LEAF_wlanStatsUnassocStaPSPoll: + val->v.uint32 = wif->stats.is_ps_unassoc; + break; + case LEAF_wlanStatsBadAidPSPoll: + val->v.uint32 = wif->stats.is_ps_badaid; + break; + case LEAF_wlanStatsEmptyPSPoll: + val->v.uint32 = wif->stats.is_ps_qempty; + break; + case LEAF_wlanStatsRxFFBadHdr: + val->v.uint32 = wif->stats.is_ff_badhdr; + break; + case LEAF_wlanStatsRxFFTooShort: + val->v.uint32 = wif->stats.is_ff_tooshort; + break; + case LEAF_wlanStatsRxFFSplitError: + val->v.uint32 = wif->stats.is_ff_split; + break; + case LEAF_wlanStatsRxFFDecap: + val->v.uint32 = wif->stats.is_ff_decap; + break; + case LEAF_wlanStatsTxFFEncap: + val->v.uint32 = wif->stats.is_ff_encap; + break; + case LEAF_wlanStatsRxBadBintval: + val->v.uint32 = wif->stats.is_rx_badbintval; + break; + case LEAF_wlanStatsRxDemicFailed: + val->v.uint32 = wif->stats.is_rx_demicfail; + break; + case LEAF_wlanStatsRxDefragFailed: + val->v.uint32 = wif->stats.is_rx_defrag; + break; + case LEAF_wlanStatsRxMgmt: + val->v.uint32 = wif->stats.is_rx_mgmt; + break; + case LEAF_wlanStatsRxActionMgmt: + val->v.uint32 = wif->stats.is_rx_action; + break; + case LEAF_wlanStatsRxAMSDUTooShort: + val->v.uint32 = wif->stats.is_amsdu_tooshort; + break; + case LEAF_wlanStatsRxAMSDUSplitError: + val->v.uint32 = wif->stats.is_amsdu_split; + break; + case LEAF_wlanStatsRxAMSDUDecap: + val->v.uint32 = wif->stats.is_amsdu_decap; + break; + case LEAF_wlanStatsTxAMSDUEncap: + val->v.uint32 = wif->stats.is_amsdu_encap; + break; + case LEAF_wlanStatsAMPDUBadBAR: + val->v.uint32 = wif->stats.is_ampdu_bar_bad; + break; + case LEAF_wlanStatsAMPDUOowBar: + val->v.uint32 = wif->stats.is_ampdu_bar_oow; + break; + case LEAF_wlanStatsAMPDUMovedBAR: + val->v.uint32 = wif->stats.is_ampdu_bar_move; + break; + case LEAF_wlanStatsAMPDURxBAR: + val->v.uint32 = wif->stats.is_ampdu_bar_rx; + break; + case LEAF_wlanStatsAMPDURxOor: + val->v.uint32 = wif->stats.is_ampdu_rx_oor; + break; + case LEAF_wlanStatsAMPDURxCopied: + val->v.uint32 = wif->stats.is_ampdu_rx_copy; + break; + case LEAF_wlanStatsAMPDURxDropped: + val->v.uint32 = wif->stats.is_ampdu_rx_drop; + break; + case LEAF_wlanStatsTxDiscardBadState: + val->v.uint32 = wif->stats.is_tx_badstate; + break; + case LEAF_wlanStatsTxFailedNoAssoc: + val->v.uint32 = wif->stats.is_tx_notassoc; + break; + case LEAF_wlanStatsTxClassifyFailed: + val->v.uint32 = wif->stats.is_tx_classify; + break; + case LEAF_wlanStatsDwdsMcastDiscard: + val->v.uint32 = wif->stats.is_dwds_mcast; + break; + case LEAF_wlanStatsHTAssocRejectNoHT: + val->v.uint32 = wif->stats.is_ht_assoc_nohtcap; + break; + case LEAF_wlanStatsHTAssocDowngrade: + val->v.uint32 = wif->stats.is_ht_assoc_downgrade; + break; + case LEAF_wlanStatsHTAssocRateMismatch: + val->v.uint32 = wif->stats.is_ht_assoc_norate; + break; + case LEAF_wlanStatsAMPDURxAge: + val->v.uint32 = wif->stats.is_ampdu_rx_age; + break; + case LEAF_wlanStatsAMPDUMoved: + val->v.uint32 = wif->stats.is_ampdu_rx_move; + break; + case LEAF_wlanStatsADDBADisabledReject: + val->v.uint32 = wif->stats.is_addba_reject; + break; + case LEAF_wlanStatsADDBANoRequest: + val->v.uint32 = wif->stats.is_addba_norequest; + break; + case LEAF_wlanStatsADDBABadToken: + val->v.uint32 = wif->stats.is_addba_badtoken; + break; + case LEAF_wlanStatsADDBABadPolicy: + val->v.uint32 = wif->stats.is_addba_badpolicy; + break; + case LEAF_wlanStatsAMPDUStopped: + val->v.uint32 = wif->stats.is_ampdu_stop; + break; + case LEAF_wlanStatsAMPDUStopFailed: + val->v.uint32 = wif->stats.is_ampdu_stop_failed; + break; + case LEAF_wlanStatsAMPDURxReorder: + val->v.uint32 = wif->stats.is_ampdu_rx_reorder; + break; + case LEAF_wlanStatsScansBackground: + val->v.uint32 = wif->stats.is_scan_bg; + break; + case LEAF_wlanLastDeauthReason: + val->v.uint32 = wif->stats.is_rx_deauth_code; + break; + case LEAF_wlanLastDissasocReason: + val->v.uint32 = wif->stats.is_rx_disassoc_code; + break; + case LEAF_wlanLastAuthFailReason: + val->v.uint32 = wif->stats.is_rx_authfail_code; + break; + case LEAF_wlanStatsBeaconMissedEvents: + val->v.uint32 = wif->stats.is_beacon_miss; + break; + case LEAF_wlanStatsRxDiscardBadStates: + val->v.uint32 = wif->stats.is_rx_badstate; + break; + case LEAF_wlanStatsFFFlushed: + val->v.uint32 = wif->stats.is_ff_flush; + break; + case LEAF_wlanStatsTxControlFrames: + val->v.uint32 = wif->stats.is_tx_ctl; + break; + case LEAF_wlanStatsAMPDURexmt: + val->v.uint32 = wif->stats.is_ampdu_rexmt; + break; + case LEAF_wlanStatsAMPDURexmtFailed: + val->v.uint32 = wif->stats.is_ampdu_rexmt_fail; + break; + case LEAF_wlanStatsReset: + val->v.uint32 = wlanStatsReset_no_op; + break; + default: + abort(); + } + + return (SNMP_ERR_NOERROR); +} + +int +op_wlan_wep_iface(struct snmp_context *ctx, struct snmp_value *val, + uint32_t sub, uint32_t iidx __unused, enum snmp_op op) +{ + struct wlan_iface *wif; + + wlan_update_interface_list(); + + switch (op) { + case SNMP_OP_GET: + if ((wif = wlan_get_interface(&val->var, sub)) == NULL || + !wif->wepsupported) + return (SNMP_ERR_NOSUCHNAME); + break; + + case SNMP_OP_GETNEXT: + /* XXX: filter wif->wepsupported */ + if ((wif = wlan_get_next_interface(&val->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + wlan_append_ifindex(&val->var, sub, wif); + break; + + case SNMP_OP_SET: + if ((wif = wlan_get_interface(&val->var, sub)) == NULL || + !wif->wepsupported) + return (SNMP_ERR_NOSUCHNAME); + switch (val->var.subs[sub - 1]) { + case LEAF_wlanWepMode: + if (val->v.integer < wlanWepMode_off || + val->v.integer > wlanWepMode_mixed) + return (SNMP_ERR_INCONS_VALUE); + ctx->scratch->int1 = wif->wepmode; + wif->wepmode = val->v.integer; + if (wlan_set_wepmode(wif) < 0) { + wif->wepmode = ctx->scratch->int1; + return (SNMP_ERR_GENERR); + } + break; + case LEAF_wlanWepDefTxKey: + if (val->v.integer < 0 || + val->v.integer > IEEE80211_WEP_NKID) + return (SNMP_ERR_INCONS_VALUE); + ctx->scratch->int1 = wif->weptxkey; + wif->weptxkey = val->v.integer; + if (wlan_set_weptxkey(wif) < 0) { + wif->weptxkey = ctx->scratch->int1; + return (SNMP_ERR_GENERR); + } + break; + default: + abort(); + } + return (SNMP_ERR_NOERROR); + + case SNMP_OP_COMMIT: + return (SNMP_ERR_NOERROR); + + case SNMP_OP_ROLLBACK: + if ((wif = wlan_get_interface(&val->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + switch (val->var.subs[sub - 1]) { + case LEAF_wlanWepMode: + wif->wepmode = ctx->scratch->int1; + if (wlan_set_wepmode(wif) < 0) + return (SNMP_ERR_GENERR); + break; + case LEAF_wlanWepDefTxKey: + wif->weptxkey = ctx->scratch->int1; + if (wlan_set_weptxkey(wif) < 0) + return (SNMP_ERR_GENERR); + break; + default: + abort(); + } + return (SNMP_ERR_NOERROR); + + default: + abort(); + } + + switch (val->var.subs[sub - 1]) { + case LEAF_wlanWepMode: + if (wlan_get_wepmode(wif) < 0) + return (SNMP_ERR_GENERR); + val->v.integer = wif->wepmode; + break; + case LEAF_wlanWepDefTxKey: + if (wlan_get_weptxkey(wif) < 0) + return (SNMP_ERR_GENERR); + val->v.integer = wif->weptxkey; + break; + default: + abort(); + } + + return (SNMP_ERR_NOERROR); +} + +int +op_wlan_wep_key(struct snmp_context *ctx __unused, + struct snmp_value *val __unused, uint32_t sub __unused, + uint32_t iidx __unused, enum snmp_op op __unused) +{ + return (SNMP_ERR_NOSUCHNAME); +} + +int +op_wlan_mac_access_control(struct snmp_context *ctx, struct snmp_value *val, + uint32_t sub, uint32_t iidx __unused, enum snmp_op op) +{ + struct wlan_iface *wif; + + wlan_update_interface_list(); + + switch (op) { + case SNMP_OP_GET: + if ((wif = wlan_get_interface(&val->var, sub)) == NULL || + !wif->macsupported) + return (SNMP_ERR_NOSUCHNAME); + break; + + case SNMP_OP_GETNEXT: + /* XXX: filter wif->macsupported */ + if ((wif = wlan_get_next_interface(&val->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + wlan_append_ifindex(&val->var, sub, wif); + break; + + case SNMP_OP_SET: + if ((wif = wlan_get_interface(&val->var, sub)) == NULL || + !wif->macsupported) + return (SNMP_ERR_NOSUCHNAME); + switch (val->var.subs[sub - 1]) { + case LEAF_wlanMACAccessControlPolicy: + ctx->scratch->int1 = wif->mac_policy; + wif->mac_policy = val->v.integer; + break; + case LEAF_wlanMACAccessControlNacl: + return (SNMP_ERR_NOT_WRITEABLE); + case LEAF_wlanMACAccessControlFlush: + break; + default: + abort(); + } + return (SNMP_ERR_NOERROR); + + case SNMP_OP_COMMIT: + if ((wif = wlan_get_interface(&val->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + switch (val->var.subs[sub - 1]) { + case LEAF_wlanMACAccessControlPolicy: + if (wlan_set_mac_policy(wif) < 0) { + wif->mac_policy = ctx->scratch->int1; + return (SNMP_ERR_GENERR); + } + break; + case LEAF_wlanMACAccessControlFlush: + if (wlan_flush_mac_mac(wif) < 0) + return (SNMP_ERR_GENERR); + break; + default: + abort(); + } + return (SNMP_ERR_NOERROR); + + case SNMP_OP_ROLLBACK: + if ((wif = wlan_get_interface(&val->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + if (val->var.subs[sub - 1] == LEAF_wlanMACAccessControlPolicy) + wif->mac_policy = ctx->scratch->int1; + return (SNMP_ERR_NOERROR); + + default: + abort(); + } + + if (wlan_get_mac_policy(wif) < 0) + return (SNMP_ERR_GENERR); + + switch (val->var.subs[sub - 1]) { + case LEAF_wlanMACAccessControlPolicy: + val->v.integer = wif->mac_policy; + break; + case LEAF_wlanMACAccessControlNacl: + val->v.integer = wif->mac_nacls; + break; + case LEAF_wlanMACAccessControlFlush: + val->v.integer = wlanMACAccessControlFlush_no_op; + break; + default: + abort(); + } + + return (SNMP_ERR_NOERROR); +} + +int +op_wlan_mac_acl_mac(struct snmp_context *ctx, struct snmp_value *val, + uint32_t sub, uint32_t iidx __unused, enum snmp_op op) +{ + struct wlan_iface *wif; + struct wlan_mac_mac *macl; + + wlan_update_interface_list(); + wlan_mac_update_aclmacs(); + + switch (op) { + case SNMP_OP_GET: + if ((macl = wlan_get_acl_mac(&val->var, sub, &wif)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + break; + + case SNMP_OP_GETNEXT: + if ((macl = wlan_get_next_acl_mac(&val->var, sub, &wif)) + == NULL) + return (SNMP_ERR_NOSUCHNAME); + wlan_append_mac_index(&val->var, sub, wif->wname, macl->mac); + break; + + case SNMP_OP_SET: + switch (val->var.subs[sub - 1]) { + case LEAF_wlanMACAccessControlMAC: + return (SNMP_ERR_INCONS_NAME); + case LEAF_wlanMACAccessControlMACStatus: + return(wlan_acl_mac_set_status(ctx, val, sub)); + default: + abort(); + } + + case SNMP_OP_COMMIT: + if ((macl = wlan_get_acl_mac(&val->var, sub, &wif)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + if (val->v.integer == RowStatus_destroy && + wlan_mac_delete_mac(wif, macl) < 0) + return (SNMP_ERR_GENERR); + return (SNMP_ERR_NOERROR); + + case SNMP_OP_ROLLBACK: + if ((macl = wlan_get_acl_mac(&val->var, sub, &wif)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + if (ctx->scratch->int1 == RowStatus_destroy && + wlan_mac_delete_mac(wif, macl) < 0) + return (SNMP_ERR_GENERR); + return (SNMP_ERR_NOERROR); + + default: + abort(); + } + + switch (val->var.subs[sub - 1]) { + case LEAF_wlanMACAccessControlMAC: + return (string_get(val, macl->mac, IEEE80211_ADDR_LEN)); + case LEAF_wlanMACAccessControlMACStatus: + val->v.integer = macl->mac_status; + break; + default: + abort(); + } + + return (SNMP_ERR_NOERROR); +} + +int +op_wlan_mesh_config(struct snmp_context *ctx, struct snmp_value *val, + uint32_t sub, uint32_t iidx __unused, enum snmp_op op) +{ + int which; + + switch (val->var.subs[sub - 1]) { + case LEAF_wlanMeshMaxRetries: + which = WLAN_MESH_MAX_RETRIES; + break; + case LEAF_wlanMeshHoldingTimeout: + which = WLAN_MESH_HOLDING_TO; + break; + case LEAF_wlanMeshConfirmTimeout: + which = WLAN_MESH_CONFIRM_TO; + break; + case LEAF_wlanMeshRetryTimeout: + which = WLAN_MESH_RETRY_TO; + break; + default: + abort(); + } + + switch (op) { + case SNMP_OP_GET: + if (wlan_do_sysctl(&wlan_config, which, 0) < 0) + return (SNMP_ERR_GENERR); + break; + + case SNMP_OP_GETNEXT: + abort(); + + case SNMP_OP_SET: + switch (val->var.subs[sub - 1]) { + case LEAF_wlanMeshRetryTimeout : + ctx->scratch->int1 = wlan_config.mesh_retryto; + wlan_config.mesh_retryto = val->v.integer; + break; + case LEAF_wlanMeshHoldingTimeout: + ctx->scratch->int1 = wlan_config.mesh_holdingto; + wlan_config.mesh_holdingto = val->v.integer; + break; + case LEAF_wlanMeshConfirmTimeout: + ctx->scratch->int1 = wlan_config.mesh_confirmto; + wlan_config.mesh_confirmto = val->v.integer; + break; + case LEAF_wlanMeshMaxRetries: + ctx->scratch->int1 = wlan_config.mesh_maxretries; + wlan_config.mesh_maxretries = val->v.integer; + break; + } + if (wlan_do_sysctl(&wlan_config, which, 1) < 0) + return (SNMP_ERR_GENERR); + return (SNMP_ERR_NOERROR); + + case SNMP_OP_COMMIT: + return (SNMP_ERR_NOERROR); + + case SNMP_OP_ROLLBACK: + switch (val->var.subs[sub - 1]) { + case LEAF_wlanMeshRetryTimeout: + wlan_config.mesh_retryto = ctx->scratch->int1; + break; + case LEAF_wlanMeshConfirmTimeout: + wlan_config.mesh_confirmto = ctx->scratch->int1; + break; + case LEAF_wlanMeshHoldingTimeout: + wlan_config.mesh_holdingto= ctx->scratch->int1; + break; + case LEAF_wlanMeshMaxRetries: + wlan_config.mesh_maxretries = ctx->scratch->int1; + break; + } + if (wlan_do_sysctl(&wlan_config, which, 1) < 0) + return (SNMP_ERR_GENERR); + return (SNMP_ERR_NOERROR); + + default: + abort(); + } + + switch (val->var.subs[sub - 1]) { + case LEAF_wlanMeshRetryTimeout: + val->v.integer = wlan_config.mesh_retryto; + break; + case LEAF_wlanMeshHoldingTimeout: + val->v.integer = wlan_config.mesh_holdingto; + break; + case LEAF_wlanMeshConfirmTimeout: + val->v.integer = wlan_config.mesh_confirmto; + break; + case LEAF_wlanMeshMaxRetries: + val->v.integer = wlan_config.mesh_maxretries; + break; + } + + return (SNMP_ERR_NOERROR); +} + +int +op_wlan_mesh_iface(struct snmp_context *ctx, struct snmp_value *val, + uint32_t sub, uint32_t iidx __unused, enum snmp_op op) +{ + int rc; + struct wlan_iface *wif; + + wlan_update_interface_list(); + + switch (op) { + case SNMP_OP_GET: + if ((wif = wlan_mesh_get_iface(&val->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + break; + + case SNMP_OP_GETNEXT: + if ((wif = wlan_mesh_get_next_iface(&val->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + wlan_append_ifindex(&val->var, sub, wif); + break; + + case SNMP_OP_SET: + if ((wif = wlan_mesh_get_iface(&val->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + switch (val->var.subs[sub - 1]) { + case LEAF_wlanMeshId: + if (val->v.octetstring.len > IEEE80211_NWID_LEN) + return (SNMP_ERR_INCONS_VALUE); + ctx->scratch->ptr1 = malloc(val->v.octetstring.len + 1); + if (ctx->scratch->ptr1 == NULL) + return (SNMP_ERR_GENERR); + strlcpy(ctx->scratch->ptr1, wif->desired_ssid, + val->v.octetstring.len + 1); + ctx->scratch->int1 = strlen(wif->desired_ssid); + memcpy(wif->desired_ssid, val->v.octetstring.octets, + val->v.octetstring.len); + wif->desired_ssid[val->v.octetstring.len] = '\0'; + break; + case LEAF_wlanMeshTTL: + ctx->scratch->int1 = wif->mesh_ttl; + wif->mesh_ttl = val->v.integer; + break; + case LEAF_wlanMeshPeeringEnabled: + ctx->scratch->int1 = wif->mesh_peering; + wif->mesh_peering = val->v.integer; + break; + case LEAF_wlanMeshForwardingEnabled: + ctx->scratch->int1 = wif->mesh_forwarding; + wif->mesh_forwarding = val->v.integer; + break; + case LEAF_wlanMeshMetric: + ctx->scratch->int1 = wif->mesh_metric; + wif->mesh_metric = val->v.integer; + break; + case LEAF_wlanMeshPath: + ctx->scratch->int1 = wif->mesh_path; + wif->mesh_path = val->v.integer; + break; + case LEAF_wlanMeshRoutesFlush: + if (val->v.integer != wlanMeshRoutesFlush_flush) + return (SNMP_ERR_INCONS_VALUE); + return (SNMP_ERR_NOERROR); + default: + abort(); + } + if (val->var.subs[sub - 1] == LEAF_wlanMeshId) + rc = wlan_config_set_dssid(wif, + val->v.octetstring.octets, val->v.octetstring.len); + else + rc = wlan_mesh_config_set(wif, val->var.subs[sub - 1]); + if (rc < 0) + return (SNMP_ERR_GENERR); + return (SNMP_ERR_NOERROR); + + case SNMP_OP_COMMIT: + if ((wif = wlan_mesh_get_iface(&val->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + if (val->var.subs[sub - 1] == LEAF_wlanMeshRoutesFlush && + wlan_mesh_flush_routes(wif) < 0) + return (SNMP_ERR_GENERR); + if (val->var.subs[sub - 1] == LEAF_wlanMeshId) + free(ctx->scratch->ptr1); + return (SNMP_ERR_NOERROR); + + case SNMP_OP_ROLLBACK: + if ((wif = wlan_mesh_get_iface(&val->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + switch (val->var.subs[sub - 1]) { + case LEAF_wlanMeshId: + strlcpy(wif->desired_ssid, ctx->scratch->ptr1, + IEEE80211_NWID_LEN); + free(ctx->scratch->ptr1); + break; + case LEAF_wlanMeshTTL: + wif->mesh_ttl = ctx->scratch->int1; + break; + case LEAF_wlanMeshPeeringEnabled: + wif->mesh_peering = ctx->scratch->int1; + break; + case LEAF_wlanMeshForwardingEnabled: + wif->mesh_forwarding = ctx->scratch->int1; + break; + case LEAF_wlanMeshMetric: + wif->mesh_metric = ctx->scratch->int1; + break; + case LEAF_wlanMeshPath: + wif->mesh_path = ctx->scratch->int1; + break; + case LEAF_wlanMeshRoutesFlush: + return (SNMP_ERR_NOERROR); + default: + abort(); + } + if (val->var.subs[sub - 1] == LEAF_wlanMeshId) + rc = wlan_config_set_dssid(wif, wif->desired_ssid, + strlen(wif->desired_ssid)); + else + rc = wlan_mesh_config_set(wif, val->var.subs[sub - 1]); + if (rc < 0) + return (SNMP_ERR_GENERR); + return (SNMP_ERR_NOERROR); + + default: + abort(); + } + + if (val->var.subs[sub - 1] == LEAF_wlanMeshId) + rc = wlan_config_get_dssid(wif); + else + rc = wlan_mesh_config_get(wif, val->var.subs[sub - 1]); + if (rc < 0) + return (SNMP_ERR_GENERR); + + switch (val->var.subs[sub - 1]) { + case LEAF_wlanMeshId: + return (string_get(val, wif->desired_ssid, -1)); + case LEAF_wlanMeshTTL: + val->v.integer = wif->mesh_ttl; + break; + case LEAF_wlanMeshPeeringEnabled: + val->v.integer = wif->mesh_peering; + break; + case LEAF_wlanMeshForwardingEnabled: + val->v.integer = wif->mesh_forwarding; + break; + case LEAF_wlanMeshMetric: + val->v.integer = wif->mesh_metric; + break; + case LEAF_wlanMeshPath: + val->v.integer = wif->mesh_path; + break; + case LEAF_wlanMeshRoutesFlush: + val->v.integer = wlanMeshRoutesFlush_no_op; + break; + default: + abort(); + } + + return (SNMP_ERR_NOERROR); +} + +int +op_wlan_mesh_neighbor(struct snmp_context *ctx __unused, struct snmp_value *val, + uint32_t sub, uint32_t iidx __unused, enum snmp_op op) +{ + struct wlan_peer *wip; + struct wlan_iface *wif; + + wlan_update_interface_list(); + wlan_update_peers(); + + switch (op) { + case SNMP_OP_GET: + if ((wip = wlan_mesh_get_peer(&val->var, sub, &wif)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + break; + case SNMP_OP_GETNEXT: + wip = wlan_mesh_get_next_peer(&val->var, sub, &wif); + if (wip == NULL) + return (SNMP_ERR_NOSUCHNAME); + wlan_append_mac_index(&val->var, sub, wif->wname, + wip->pmac); + break; + case SNMP_OP_SET: + return (SNMP_ERR_NOT_WRITEABLE); + case SNMP_OP_COMMIT: + /* FALLTHROUGH */ + case SNMP_OP_ROLLBACK: + /* FALLTHROUGH */ + default: + abort(); + } + + switch (val->var.subs[sub - 1]) { + case LEAF_wlanMeshNeighborAddress: + return (string_get(val, wip->pmac, IEEE80211_ADDR_LEN)); + case LEAF_wlanMeshNeighborFrequency: + val->v.integer = wip->frequency; + break; + case LEAF_wlanMeshNeighborLocalId: + val->v.integer = wip->local_id; + break; + case LEAF_wlanMeshNeighborPeerId: + val->v.integer = wip->peer_id; + break; + case LEAF_wlanMeshNeighborPeerState: + return (bits_get(val, (uint8_t *)&wip->state, + sizeof(wip->state))); + case LEAF_wlanMeshNeighborCurrentTXRate: + val->v.integer = wip->txrate; + break; + case LEAF_wlanMeshNeighborRxSignalStrength: + val->v.integer = wip->rssi; + break; + case LEAF_wlanMeshNeighborIdleTimer: + val->v.integer = wip->idle; + break; + case LEAF_wlanMeshNeighborTxSequenceNo: + val->v.integer = wip->txseqs; + break; + case LEAF_wlanMeshNeighborRxSequenceNo: + val->v.integer = wip->rxseqs; + break; + default: + abort(); + } + + return (SNMP_ERR_NOERROR); +} + +int +op_wlan_mesh_route(struct snmp_context *ctx, struct snmp_value *val, + uint32_t sub, uint32_t iidx __unused, enum snmp_op op) +{ + struct wlan_mesh_route *wmr; + struct wlan_iface *wif; + + wlan_update_interface_list(); + wlan_mesh_update_routes(); + + switch (op) { + case SNMP_OP_GET: + if ((wmr = wlan_mesh_get_route(&val->var, sub, &wif)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + break; + + case SNMP_OP_GETNEXT: + wmr = wlan_mesh_get_next_route(&val->var, sub, &wif); + if (wmr == NULL) + return (SNMP_ERR_NOSUCHNAME); + wlan_append_mac_index(&val->var, sub, wif->wname, + wmr->imroute.imr_dest); + break; + + case SNMP_OP_SET: + switch (val->var.subs[sub - 1]) { + case LEAF_wlanMeshRouteDestination: + return (SNMP_ERR_INCONS_NAME); + case LEAF_wlanMeshRouteStatus: + return(wlan_mesh_route_set_status(ctx, val, sub)); + default: + return (SNMP_ERR_NOT_WRITEABLE); + } + abort(); + + case SNMP_OP_COMMIT: + if ((wmr = wlan_mesh_get_route(&val->var, sub, &wif)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + if (val->v.integer == RowStatus_destroy && + wlan_mesh_delete_route(wif, wmr) < 0) + return (SNMP_ERR_GENERR); + return (SNMP_ERR_NOERROR); + + case SNMP_OP_ROLLBACK: + if ((wmr = wlan_mesh_get_route(&val->var, sub, &wif)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + if (ctx->scratch->int1 == RowStatus_destroy && + wlan_mesh_delete_route(wif, wmr) < 0) + return (SNMP_ERR_GENERR); + return (SNMP_ERR_NOERROR); + + default: + abort(); + } + + switch (val->var.subs[sub - 1]) { + case LEAF_wlanMeshRouteDestination: + return (string_get(val, wmr->imroute.imr_dest, + IEEE80211_ADDR_LEN)); + case LEAF_wlanMeshRouteNextHop: + return (string_get(val, wmr->imroute.imr_nexthop, + IEEE80211_ADDR_LEN)); + case LEAF_wlanMeshRouteHops: + val->v.integer = wmr->imroute.imr_nhops; + break; + case LEAF_wlanMeshRouteMetric: + val->v.integer = wmr->imroute.imr_metric; + break; + case LEAF_wlanMeshRouteLifeTime: + val->v.integer = wmr->imroute.imr_lifetime; + break; + case LEAF_wlanMeshRouteLastMseq: + val->v.integer = wmr->imroute.imr_lastmseq; + break; + case LEAF_wlanMeshRouteFlags: + val->v.integer = 0; + if ((wmr->imroute.imr_flags & + IEEE80211_MESHRT_FLAGS_VALID) != 0) + val->v.integer |= (0x1 << wlanMeshRouteFlags_valid); + if ((wmr->imroute.imr_flags & + IEEE80211_MESHRT_FLAGS_PROXY) != 0) + val->v.integer |= (0x1 << wlanMeshRouteFlags_proxy); + return (bits_get(val, (uint8_t *)&val->v.integer, + sizeof(val->v.integer))); + case LEAF_wlanMeshRouteStatus: + val->v.integer = wmr->mroute_status; + break; + } + + return (SNMP_ERR_NOERROR); +} + +int +op_wlan_mesh_stats(struct snmp_context *ctx __unused, struct snmp_value *val, + uint32_t sub, uint32_t iidx __unused, enum snmp_op op) +{ + struct wlan_iface *wif; + + wlan_update_interface_list(); + + switch (op) { + case SNMP_OP_GET: + if ((wif = wlan_mesh_get_iface(&val->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + break; + case SNMP_OP_GETNEXT: + if ((wif = wlan_mesh_get_next_iface(&val->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + wlan_append_ifindex(&val->var, sub, wif); + break; + case SNMP_OP_SET: + return (SNMP_ERR_NOT_WRITEABLE); + case SNMP_OP_COMMIT: + /* FALLTHROUGH */ + case SNMP_OP_ROLLBACK: + /* FALLTHROUGH */ + default: + abort(); + } + + if (wlan_get_stats(wif) < 0) + return (SNMP_ERR_GENERR); + + switch (val->var.subs[sub - 1]) { + case LEAF_wlanMeshDroppedBadSta: + val->v.uint32 = wif->stats.is_mesh_wrongmesh; + break; + case LEAF_wlanMeshDroppedNoLink: + val->v.uint32 = wif->stats.is_mesh_nolink; + break; + case LEAF_wlanMeshNoFwdTtl: + val->v.uint32 = wif->stats.is_mesh_fwd_ttl; + break; + case LEAF_wlanMeshNoFwdBuf: + val->v.uint32 = wif->stats.is_mesh_fwd_nobuf; + break; + case LEAF_wlanMeshNoFwdTooShort: + val->v.uint32 = wif->stats.is_mesh_fwd_tooshort; + break; + case LEAF_wlanMeshNoFwdDisabled: + val->v.uint32 = wif->stats.is_mesh_fwd_disabled; + break; + case LEAF_wlanMeshNoFwdPathUnknown: + val->v.uint32 = wif->stats.is_mesh_fwd_nopath; + break; + case LEAF_wlanMeshDroppedBadAE: + val->v.uint32 = wif->stats.is_mesh_badae; + break; + case LEAF_wlanMeshRouteAddFailed: + val->v.uint32 = wif->stats.is_mesh_rtaddfailed; + break; + case LEAF_wlanMeshDroppedNoProxy: + val->v.uint32 = wif->stats.is_mesh_notproxy; + break; + case LEAF_wlanMeshDroppedMisaligned: + val->v.uint32 = wif->stats.is_rx_badalign; + break; + default: + abort(); + } + + return (SNMP_ERR_NOERROR); +} + +int +op_wlan_hwmp_config(struct snmp_context *ctx, struct snmp_value *val, + uint32_t sub, uint32_t iidx __unused, enum snmp_op op) +{ + int which; + + switch (val->var.subs[sub - 1]) { + case LEAF_wlanHWMPRouteInactiveTimeout: + which = WLAN_HWMP_INACTIVITY_TO; + break; + case LEAF_wlanHWMPRootAnnounceInterval: + which = WLAN_HWMP_RANN_INT; + break; + case LEAF_wlanHWMPRootInterval: + which = WLAN_HWMP_ROOT_INT; + break; + case LEAF_wlanHWMPRootTimeout: + which = WLAN_HWMP_ROOT_TO; + break; + case LEAF_wlanHWMPPathLifetime: + which = WLAN_HWMP_PATH_LIFETIME; + break; + case LEAF_wlanHWMPReplyForwardBit: + which = WLAN_HWMP_REPLY_FORWARD; + break; + case LEAF_wlanHWMPTargetOnlyBit: + which = WLAN_HWMP_TARGET_ONLY; + break; + default: + abort(); + } + + switch (op) { + case SNMP_OP_GET: + if (wlan_do_sysctl(&wlan_config, which, 0) < 0) + return (SNMP_ERR_GENERR); + break; + + case SNMP_OP_GETNEXT: + abort(); + + case SNMP_OP_SET: + switch (val->var.subs[sub - 1]) { + case LEAF_wlanHWMPRouteInactiveTimeout: + ctx->scratch->int1 = wlan_config.hwmp_inact; + wlan_config.hwmp_inact = val->v.integer; + break; + case LEAF_wlanHWMPRootAnnounceInterval: + ctx->scratch->int1 = wlan_config.hwmp_rannint; + wlan_config.hwmp_rannint = val->v.integer; + break; + case LEAF_wlanHWMPRootInterval: + ctx->scratch->int1 = wlan_config.hwmp_rootint; + wlan_config.hwmp_rootint = val->v.integer; + break; + case LEAF_wlanHWMPRootTimeout: + ctx->scratch->int1 = wlan_config.hwmp_roottimeout; + wlan_config.hwmp_roottimeout = val->v.integer; + break; + case LEAF_wlanHWMPPathLifetime: + ctx->scratch->int1 = wlan_config.hwmp_pathlifetime; + wlan_config.hwmp_pathlifetime = val->v.integer; + break; + case LEAF_wlanHWMPReplyForwardBit: + ctx->scratch->int1 = wlan_config.hwmp_replyforward; + wlan_config.hwmp_replyforward = val->v.integer; + break; + case LEAF_wlanHWMPTargetOnlyBit: + ctx->scratch->int1 = wlan_config.hwmp_targetonly; + wlan_config.hwmp_targetonly = val->v.integer; + break; + } + if (wlan_do_sysctl(&wlan_config, which, 1) < 0) + return (SNMP_ERR_GENERR); + return (SNMP_ERR_NOERROR); + + case SNMP_OP_COMMIT: + return (SNMP_ERR_NOERROR); + + case SNMP_OP_ROLLBACK: + switch (val->var.subs[sub - 1]) { + case LEAF_wlanHWMPRouteInactiveTimeout: + wlan_config.hwmp_inact = ctx->scratch->int1; + break; + case LEAF_wlanHWMPRootAnnounceInterval: + wlan_config.hwmp_rannint = ctx->scratch->int1; + break; + case LEAF_wlanHWMPRootInterval: + wlan_config.hwmp_rootint = ctx->scratch->int1; + break; + case LEAF_wlanHWMPRootTimeout: + wlan_config.hwmp_roottimeout = ctx->scratch->int1; + break; + case LEAF_wlanHWMPPathLifetime: + wlan_config.hwmp_pathlifetime = ctx->scratch->int1; + break; + case LEAF_wlanHWMPReplyForwardBit: + wlan_config.hwmp_replyforward = ctx->scratch->int1; + break; + case LEAF_wlanHWMPTargetOnlyBit: + wlan_config.hwmp_targetonly = ctx->scratch->int1; + break; + } + if (wlan_do_sysctl(&wlan_config, which, 1) < 0) + return (SNMP_ERR_GENERR); + return (SNMP_ERR_NOERROR); + + default: + abort(); + } + + switch (val->var.subs[sub - 1]) { + case LEAF_wlanHWMPRouteInactiveTimeout: + val->v.integer = wlan_config.hwmp_inact; + break; + case LEAF_wlanHWMPRootAnnounceInterval: + val->v.integer = wlan_config.hwmp_rannint; + break; + case LEAF_wlanHWMPRootInterval: + val->v.integer = wlan_config.hwmp_rootint; + break; + case LEAF_wlanHWMPRootTimeout: + val->v.integer = wlan_config.hwmp_roottimeout; + break; + case LEAF_wlanHWMPPathLifetime: + val->v.integer = wlan_config.hwmp_pathlifetime; + break; + case LEAF_wlanHWMPReplyForwardBit: + val->v.integer = wlan_config.hwmp_replyforward; + break; + case LEAF_wlanHWMPTargetOnlyBit: + val->v.integer = wlan_config.hwmp_targetonly; + break; + } + + return (SNMP_ERR_NOERROR); +} + +int +op_wlan_hwmp_iface(struct snmp_context *ctx, struct snmp_value *val, + uint32_t sub, uint32_t iidx __unused, enum snmp_op op) +{ + struct wlan_iface *wif; + + wlan_update_interface_list(); + + switch (op) { + case SNMP_OP_GET: + if ((wif = wlan_mesh_get_iface(&val->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + break; + + case SNMP_OP_GETNEXT: + if ((wif = wlan_mesh_get_next_iface(&val->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + wlan_append_ifindex(&val->var, sub, wif); + break; + + case SNMP_OP_SET: + if ((wif = wlan_mesh_get_iface(&val->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + switch (val->var.subs[sub - 1]) { + case LEAF_wlanHWMPRootMode: + ctx->scratch->int1 = wif->hwmp_root_mode; + wif->hwmp_root_mode = val->v.integer; + break; + case LEAF_wlanHWMPMaxHops: + ctx->scratch->int1 = wif->hwmp_max_hops; + wif->hwmp_max_hops = val->v.integer; + break; + default: + abort(); + } + if (wlan_hwmp_config_set(wif, val->var.subs[sub - 1]) < 0) + return (SNMP_ERR_GENERR); + return (SNMP_ERR_NOERROR); + + case SNMP_OP_COMMIT: + return (SNMP_ERR_NOERROR); + + case SNMP_OP_ROLLBACK: + if ((wif = wlan_mesh_get_iface(&val->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + switch (val->var.subs[sub - 1]) { + case LEAF_wlanHWMPRootMode: + wif->hwmp_root_mode = ctx->scratch->int1; + break; + case LEAF_wlanHWMPMaxHops: + wif->hwmp_max_hops = ctx->scratch->int1; + break; + default: + abort(); + } + if (wlan_hwmp_config_set(wif, val->var.subs[sub - 1]) < 0) + return (SNMP_ERR_GENERR); + return (SNMP_ERR_NOERROR); + + default: + abort(); + } + + if (wlan_hwmp_config_get(wif, val->var.subs[sub - 1]) < 0) + return (SNMP_ERR_GENERR); + + switch (val->var.subs[sub - 1]) { + case LEAF_wlanHWMPRootMode: + val->v.integer = wif->hwmp_root_mode; + break; + case LEAF_wlanHWMPMaxHops: + val->v.integer = wif->hwmp_max_hops; + break; + default: + abort(); + } + + return (SNMP_ERR_NOERROR); +} + +int +op_wlan_hwmp_stats(struct snmp_context *ctx __unused, struct snmp_value *val, + uint32_t sub, uint32_t iidx __unused, enum snmp_op op) +{ + struct wlan_iface *wif; + + wlan_update_interface_list(); + + switch (op) { + case SNMP_OP_GET: + if ((wif = wlan_mesh_get_iface(&val->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + break; + case SNMP_OP_GETNEXT: + if ((wif = wlan_mesh_get_next_iface(&val->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + wlan_append_ifindex(&val->var, sub, wif); + break; + case SNMP_OP_SET: + return (SNMP_ERR_NOT_WRITEABLE); + case SNMP_OP_COMMIT: + /* FALLTHROUGH */ + case SNMP_OP_ROLLBACK: + /* FALLTHROUGH */ + default: + abort(); + } + + if (wlan_get_stats(wif) < 0) + return (SNMP_ERR_GENERR); + + switch (val->var.subs[sub - 1]) { + case LEAF_wlanMeshHWMPWrongSeqNo: + val->v.uint32 = wif->stats.is_hwmp_wrongseq; + break; + case LEAF_wlanMeshHWMPTxRootPREQ: + val->v.uint32 = wif->stats.is_hwmp_rootreqs; + break; + case LEAF_wlanMeshHWMPTxRootRANN: + val->v.uint32 = wif->stats.is_hwmp_rootrann; + break; + case LEAF_wlanMeshHWMPProxy: + val->v.uint32 = wif->stats.is_hwmp_proxy; + break; + default: + abort(); + } + + return (SNMP_ERR_NOERROR); +} + +/* + * Encode BITS type for a response packet - XXX: this belongs to the snmp lib. + */ +static int +bits_get(struct snmp_value *value, const u_char *ptr, ssize_t len) +{ + int size; + + if (ptr == NULL) { + value->v.octetstring.len = 0; + value->v.octetstring.octets = NULL; + return (SNMP_ERR_NOERROR); + } + + /* Determine length - up to 8 octets supported so far. */ + for (size = len; size > 0; size--) + if (ptr[size - 1] != 0) + break; + if (size == 0) + size = 1; + + value->v.octetstring.len = (u_long)size; + if ((value->v.octetstring.octets = malloc((size_t)size)) == NULL) + return (SNMP_ERR_RES_UNAVAIL); + memcpy(value->v.octetstring.octets, ptr, (size_t)size); + return (SNMP_ERR_NOERROR); +} + +/* + * Calls for adding/updating/freeing/etc of wireless interfaces. + */ +static void +wlan_free_interface(struct wlan_iface *wif) +{ + wlan_free_peerlist(wif); + free(wif->chanlist); + wlan_scan_free_results(wif); + wlan_mac_free_maclist(wif); + wlan_mesh_free_routes(wif); + free(wif); +} + +static void +wlan_free_iflist(void) +{ + struct wlan_iface *w; + + while ((w = SLIST_FIRST(&wlan_ifaces)) != NULL) { + SLIST_REMOVE_HEAD(&wlan_ifaces, w_if); + wlan_free_interface(w); + } +} + +static struct wlan_iface * +wlan_find_interface(const char *wname) +{ + struct wlan_iface *wif; + + SLIST_FOREACH(wif, &wlan_ifaces, w_if) + if (strcmp(wif->wname, wname) == 0) { + if (wif->status != RowStatus_active) + return (NULL); + break; + } + + return (wif); +} + +static struct wlan_iface * +wlan_first_interface(void) +{ + return (SLIST_FIRST(&wlan_ifaces)); +} + +static struct wlan_iface * +wlan_next_interface(struct wlan_iface *wif) +{ + if (wif == NULL) + return (NULL); + + return (SLIST_NEXT(wif, w_if)); +} + +/* + * Add a new interface to the list - sorted by name. + */ +static int +wlan_add_wif(struct wlan_iface *wif) +{ + int cmp; + struct wlan_iface *temp, *prev; + + if ((prev = SLIST_FIRST(&wlan_ifaces)) == NULL || + strcmp(wif->wname, prev->wname) < 0) { + SLIST_INSERT_HEAD(&wlan_ifaces, wif, w_if); + return (0); + } + + SLIST_FOREACH(temp, &wlan_ifaces, w_if) { + if ((cmp = strcmp(wif->wname, temp->wname)) <= 0) + break; + prev = temp; + } + + if (temp == NULL) + SLIST_INSERT_AFTER(prev, wif, w_if); + else if (cmp > 0) + SLIST_INSERT_AFTER(temp, wif, w_if); + else { + syslog(LOG_ERR, "Wlan iface %s already in list", wif->wname); + return (-1); + } + + return (0); +} + +static struct wlan_iface * +wlan_new_wif(char *wname) +{ + struct wlan_iface *wif; + + /* Make sure it's not in the list. */ + for (wif = wlan_first_interface(); wif != NULL; + wif = wlan_next_interface(wif)) + if (strcmp(wname, wif->wname) == 0) { + wif->internal = 0; + return (wif); + } + + if ((wif = (struct wlan_iface *)malloc(sizeof(*wif))) == NULL) + return (NULL); + + memset(wif, 0, sizeof(struct wlan_iface)); + strlcpy(wif->wname, wname, IFNAMSIZ); + wif->status = RowStatus_notReady; + wif->state = wlanIfaceState_down; + wif->mode = WlanIfaceOperatingModeType_station; + + if (wlan_add_wif(wif) < 0) { + free(wif); + return (NULL); + } + + return (wif); +} + +static void +wlan_delete_wif(struct wlan_iface *wif) +{ + SLIST_REMOVE(&wlan_ifaces, wif, wlan_iface, w_if); + wlan_free_interface(wif); +} + +static int +wlan_attach_newif(struct mibif *mif) +{ + struct wlan_iface *wif; + + if (mif->mib.ifmd_data.ifi_type != IFT_ETHER || + wlan_check_media(mif->name) != IFM_IEEE80211) + return (0); + + if ((wif = wlan_new_wif(mif->name)) == NULL) + return (-1); + + (void)wlan_get_opmode(wif); + wif->index = mif->index; + wif->status = RowStatus_active; + (void)wlan_update_interface(wif); + + return (0); +} + +static int +wlan_iface_create(struct wlan_iface *wif) +{ + int rc; + + if ((rc = wlan_clone_create(wif)) == SNMP_ERR_NOERROR) { + /* + * The rest of the info will be updated once the + * snmp_mibII module notifies us of the interface. + */ + wif->status = RowStatus_active; + if (wif->state == wlanIfaceState_up) + (void)wlan_config_state(wif, 1); + } + + return (rc); +} + +static int +wlan_iface_destroy(struct wlan_iface *wif) +{ + int rc = SNMP_ERR_NOERROR; + + if (wif->internal == 0) + rc = wlan_clone_destroy(wif); + + if (rc == SNMP_ERR_NOERROR) + wlan_delete_wif(wif); + + return (rc); +} + +static int +wlan_update_interface(struct wlan_iface *wif) +{ + int i; + + (void)wlan_config_state(wif, 0); + (void)wlan_get_driver_caps(wif); + for (i = LEAF_wlanIfacePacketBurst; + i <= LEAF_wlanIfaceTdmaBeaconInterval; i++) + (void)wlan_config_get_ioctl(wif, i); + (void)wlan_get_stats(wif); + /* + * XXX: wlan_get_channel_list() not needed - + * fetched with wlan_get_driver_caps() + */ + (void)wlan_get_channel_list(wif); + (void)wlan_get_roam_params(wif); + (void)wlan_get_tx_params(wif); + (void)wlan_get_scan_results(wif); + (void)wlan_get_wepmode(wif); + (void)wlan_get_weptxkey(wif); + (void)wlan_get_mac_policy(wif); + (void)wlan_get_mac_acl_macs(wif); + (void)wlan_get_peerinfo(wif); + + if (wif->mode == WlanIfaceOperatingModeType_meshPoint) { + for (i = LEAF_wlanMeshTTL; i <= LEAF_wlanMeshPath; i++) + (void)wlan_mesh_config_get(wif, i); + (void)wlan_mesh_get_routelist(wif); + for (i = LEAF_wlanHWMPRootMode; i <= LEAF_wlanHWMPMaxHops; i++) + (void)wlan_hwmp_config_get(wif, i); + } + + return (0); +} + +static void +wlan_update_interface_list(void) +{ + struct wlan_iface *wif, *twif; + + if ((time(NULL) - wlan_iflist_age) <= WLAN_LIST_MAXAGE) + return; + + /* + * The snmp_mibII module would have notified us for new interfaces, + * so only check if any have been deleted. + */ + SLIST_FOREACH_SAFE(wif, &wlan_ifaces, w_if, twif) + if (wif->status == RowStatus_active && wlan_get_opmode(wif) < 0) + wlan_delete_wif(wif); + + wlan_iflist_age = time(NULL); +} + +static void +wlan_append_ifindex(struct asn_oid *oid, uint sub, const struct wlan_iface *w) +{ + uint32_t i; + + oid->len = sub + strlen(w->wname) + 1; + oid->subs[sub] = strlen(w->wname); + for (i = 1; i <= strlen(w->wname); i++) + oid->subs[sub + i] = w->wname[i - 1]; +} + +static uint8_t * +wlan_get_ifname(const struct asn_oid *oid, uint sub, uint8_t *wname) +{ + uint32_t i; + + memset(wname, 0, IFNAMSIZ); + + if (oid->len - sub != oid->subs[sub] + 1 || oid->subs[sub] >= IFNAMSIZ) + return (NULL); + + for (i = 0; i < oid->subs[sub]; i++) + wname[i] = oid->subs[sub + i + 1]; + wname[i] = '\0'; + + return (wname); +} + +static struct wlan_iface * +wlan_get_interface(const struct asn_oid *oid, uint sub) +{ + uint8_t wname[IFNAMSIZ]; + + if (wlan_get_ifname(oid, sub, wname) == NULL) + return (NULL); + + return (wlan_find_interface(wname)); +} + +static struct wlan_iface * +wlan_get_next_interface(const struct asn_oid *oid, uint sub) +{ + uint32_t i; + uint8_t wname[IFNAMSIZ]; + struct wlan_iface *wif; + + if (oid->len - sub == 0) { + for (wif = wlan_first_interface(); wif != NULL; + wif = wlan_next_interface(wif)) + if (wif->status == RowStatus_active) + break; + return (wif); + } + + if (oid->len - sub != oid->subs[sub] + 1 || oid->subs[sub] >= IFNAMSIZ) + return (NULL); + + memset(wname, 0, IFNAMSIZ); + for (i = 0; i < oid->subs[sub]; i++) + wname[i] = oid->subs[sub + i + 1]; + wname[i] = '\0'; + if ((wif = wlan_find_interface(wname)) == NULL) + return (NULL); + + while ((wif = wlan_next_interface(wif)) != NULL) + if (wif->status == RowStatus_active) + break; + + return (wif); +} + +static struct wlan_iface * +wlan_get_snmp_interface(const struct asn_oid *oid, uint sub) +{ + uint8_t wname[IFNAMSIZ]; + struct wlan_iface *wif; + + if (wlan_get_ifname(oid, sub, wname) == NULL) + return (NULL); + + for (wif = wlan_first_interface(); wif != NULL; + wif = wlan_next_interface(wif)) + if (strcmp(wif->wname, wname) == 0) + break; + + return (wif); +} + +static struct wlan_iface * +wlan_get_next_snmp_interface(const struct asn_oid *oid, uint sub) +{ + uint32_t i; + uint8_t wname[IFNAMSIZ]; + struct wlan_iface *wif; + + if (oid->len - sub == 0) + return (wlan_first_interface()); + + if (oid->len - sub != oid->subs[sub] + 1 || oid->subs[sub] >= IFNAMSIZ) + return (NULL); + + memset(wname, 0, IFNAMSIZ); + for (i = 0; i < oid->subs[sub]; i++) + wname[i] = oid->subs[sub + i + 1]; + wname[i] = '\0'; + + for (wif = wlan_first_interface(); wif != NULL; + wif = wlan_next_interface(wif)) + if (strcmp(wif->wname, wname) == 0) + break; + + return (wlan_next_interface(wif)); +} + +/* + * Decode/Append an index for tables indexed by the wireless interface + * name and a MAC address - ACL MACs and Mesh Routes. + */ +static int +wlan_mac_index_decode(const struct asn_oid *oid, uint sub, + char *wname, uint8_t *mac) +{ + uint32_t i; + int mac_off; + + if (oid->len - sub != oid->subs[sub] + 2 + IEEE80211_ADDR_LEN + || oid->subs[sub] >= IFNAMSIZ) + return (-1); + + for (i = 0; i < oid->subs[sub]; i++) + wname[i] = oid->subs[sub + i + 1]; + wname[i] = '\0'; + + mac_off = sub + oid->subs[sub] + 1; + if (oid->subs[mac_off] != IEEE80211_ADDR_LEN) + return (-1); + for (i = 0; i < IEEE80211_ADDR_LEN; i++) + mac[i] = oid->subs[mac_off + i + 1]; + + return (0); +} + +static void +wlan_append_mac_index(struct asn_oid *oid, uint sub, char *wname, uint8_t *mac) +{ + uint32_t i; + + oid->len = sub + strlen(wname) + IEEE80211_ADDR_LEN + 2; + oid->subs[sub] = strlen(wname); + for (i = 1; i <= strlen(wname); i++) + oid->subs[sub + i] = wname[i - 1]; + + sub += strlen(wname) + 1; + oid->subs[sub] = IEEE80211_ADDR_LEN; + for (i = 1; i <= IEEE80211_ADDR_LEN; i++) + oid->subs[sub + i] = mac[i - 1]; +} + +/* + * Decode/Append an index for tables indexed by the wireless interface + * name and the PHY mode - Roam and TX params. + */ +static int +wlan_phy_index_decode(const struct asn_oid *oid, uint sub, char *wname, + uint32_t *phy) +{ + uint32_t i; + + if (oid->len - sub != oid->subs[sub] + 2 || oid->subs[sub] >= IFNAMSIZ) + return (-1); + + for (i = 0; i < oid->subs[sub]; i++) + wname[i] = oid->subs[sub + i + 1]; + wname[i] = '\0'; + + *phy = oid->subs[sub + oid->subs[sub] + 1]; + return (0); +} + +static void +wlan_append_phy_index(struct asn_oid *oid, uint sub, char *wname, uint32_t phy) +{ + uint32_t i; + + oid->len = sub + strlen(wname) + 2; + oid->subs[sub] = strlen(wname); + for (i = 1; i <= strlen(wname); i++) + oid->subs[sub + i] = wname[i - 1]; + oid->subs[sub + strlen(wname) + 1] = phy; +} + +/* + * Calls for manipulating the peerlist of a wireless interface. + */ +static void +wlan_free_peerlist(struct wlan_iface *wif) +{ + struct wlan_peer *wip; + + while ((wip = SLIST_FIRST(&wif->peerlist)) != NULL) { + SLIST_REMOVE_HEAD(&wif->peerlist, wp); + free(wip); + } + + SLIST_INIT(&wif->peerlist); +} + +static struct wlan_peer * +wlan_find_peer(struct wlan_iface *wif, uint8_t *peermac) +{ + struct wlan_peer *wip; + + SLIST_FOREACH(wip, &wif->peerlist, wp) + if (memcmp(wip->pmac, peermac, IEEE80211_ADDR_LEN) == 0) + break; + + return (wip); +} + +struct wlan_peer * +wlan_new_peer(const uint8_t *pmac) +{ + struct wlan_peer *wip; + + if ((wip = (struct wlan_peer *)malloc(sizeof(*wip))) == NULL) + return (NULL); + + memset(wip, 0, sizeof(struct wlan_peer)); + memcpy(wip->pmac, pmac, IEEE80211_ADDR_LEN); + + return (wip); +} + +void +wlan_free_peer(struct wlan_peer *wip) +{ + free(wip); +} + +int +wlan_add_peer(struct wlan_iface *wif, struct wlan_peer *wip) +{ + struct wlan_peer *temp, *prev; + + SLIST_FOREACH(temp, &wif->peerlist, wp) + if (memcmp(temp->pmac, wip->pmac, IEEE80211_ADDR_LEN) == 0) + return (-1); + + if ((prev = SLIST_FIRST(&wif->peerlist)) == NULL || + memcmp(wip->pmac, prev->pmac, IEEE80211_ADDR_LEN) < 0) { + SLIST_INSERT_HEAD(&wif->peerlist, wip, wp); + return (0); + } + + SLIST_FOREACH(temp, &wif->peerlist, wp) { + if (memcmp(wip->pmac, temp->pmac, IEEE80211_ADDR_LEN) < 0) + break; + prev = temp; + } + + SLIST_INSERT_AFTER(prev, wip, wp); + return (0); +} + +static void +wlan_update_peers(void) +{ + struct wlan_iface *wif; + + if ((time(NULL) - wlan_peerlist_age) <= WLAN_LIST_MAXAGE) + return; + + for (wif = wlan_first_interface(); wif != NULL; + wif = wlan_next_interface(wif)) { + if (wif->status != RowStatus_active) + continue; + wlan_free_peerlist(wif); + (void)wlan_get_peerinfo(wif); + } + wlan_peerlist_age = time(NULL); +} + +static struct wlan_peer * +wlan_get_peer(const struct asn_oid *oid, uint sub, struct wlan_iface **wif) +{ + char wname[IFNAMSIZ]; + uint8_t pmac[IEEE80211_ADDR_LEN]; + + if (wlan_mac_index_decode(oid, sub, wname, pmac) < 0) + return (NULL); + + if ((*wif = wlan_find_interface(wname)) == NULL) + return (NULL); + + return (wlan_find_peer(*wif, pmac)); +} + +static struct wlan_peer * +wlan_get_next_peer(const struct asn_oid *oid, uint sub, struct wlan_iface **wif) +{ + char wname[IFNAMSIZ]; + char pmac[IEEE80211_ADDR_LEN]; + struct wlan_peer *wip; + + if (oid->len - sub == 0) { + for (*wif = wlan_first_interface(); *wif != NULL; + *wif = wlan_next_interface(*wif)) { + if ((*wif)->mode == + WlanIfaceOperatingModeType_meshPoint) + continue; + wip = SLIST_FIRST(&(*wif)->peerlist); + if (wip != NULL) + return (wip); + } + return (NULL); + } + + if (wlan_mac_index_decode(oid, sub, wname, pmac) < 0 || + (*wif = wlan_find_interface(wname)) == NULL || + (wip = wlan_find_peer(*wif, pmac)) == NULL) + return (NULL); + + if ((wip = SLIST_NEXT(wip, wp)) != NULL) + return (wip); + + while ((*wif = wlan_next_interface(*wif)) != NULL) { + if ((*wif)->mode == WlanIfaceOperatingModeType_meshPoint) + continue; + if ((wip = SLIST_FIRST(&(*wif)->peerlist)) != NULL) + break; + } + + return (wip); +} + +/* + * Calls for manipulating the active channel list of a wireless interface. + */ +static void +wlan_update_channels(void) +{ + struct wlan_iface *wif; + + if ((time(NULL) - wlan_chanlist_age) <= WLAN_LIST_MAXAGE) + return; + + for (wif = wlan_first_interface(); wif != NULL; + wif = wlan_next_interface(wif)) { + if (wif->status != RowStatus_active) + continue; + (void)wlan_get_channel_list(wif); + } + wlan_chanlist_age = time(NULL); +} + +static int +wlan_channel_index_decode(const struct asn_oid *oid, uint sub, char *wname, + uint32_t *cindex) +{ + uint32_t i; + if (oid->len - sub != oid->subs[sub] + 2 || oid->subs[sub] >= IFNAMSIZ) + return (-1); + + for (i = 0; i < oid->subs[sub]; i++) + wname[i] = oid->subs[sub + i + 1]; + wname[i] = '\0'; + + *cindex = oid->subs[sub + oid->subs[sub] + 1]; + + return (0); +} + +static void +wlan_append_channel_index(struct asn_oid *oid, uint sub, + const struct wlan_iface *wif, const struct ieee80211_channel *channel) +{ + uint32_t i; + + oid->len = sub + strlen(wif->wname) + 2; + oid->subs[sub] = strlen(wif->wname); + for (i = 1; i <= strlen(wif->wname); i++) + oid->subs[sub + i] = wif->wname[i - 1]; + oid->subs[sub + strlen(wif->wname) + 1] = (channel - wif->chanlist) + 1; +} + +static int32_t +wlan_get_channel_type(struct ieee80211_channel *c) +{ + if (IEEE80211_IS_CHAN_FHSS(c)) + return (WlanChannelType_fhss); + if (IEEE80211_IS_CHAN_A(c)) + return (WlanChannelType_dot11a); + if (IEEE80211_IS_CHAN_B(c)) + return (WlanChannelType_dot11b); + if (IEEE80211_IS_CHAN_ANYG(c)) + return (WlanChannelType_dot11g); + if (IEEE80211_IS_CHAN_HALF(c)) + return (WlanChannelType_tenMHz); + if (IEEE80211_IS_CHAN_QUARTER(c)) + return (WlanChannelType_fiveMHz); + if (IEEE80211_IS_CHAN_TURBO(c)) + return (WlanChannelType_turbo); + if (IEEE80211_IS_CHAN_HT(c)) + return (WlanChannelType_ht); + + return (-1); +} + +static struct ieee80211_channel * +wlan_find_channel(struct wlan_iface *wif, uint32_t cindex) +{ + if (wif->chanlist == NULL || cindex > wif->nchannels) + return (NULL); + + return (wif->chanlist + cindex - 1); +} + +static struct ieee80211_channel * +wlan_get_channel(const struct asn_oid *oid, uint sub, struct wlan_iface **wif) +{ + uint32_t cindex; + char wname[IFNAMSIZ]; + + if (wlan_channel_index_decode(oid, sub, wname, &cindex) < 0) + return (NULL); + + if ((*wif = wlan_find_interface(wname)) == NULL) + return (NULL); + + return (wlan_find_channel(*wif, cindex)); +} + +static struct ieee80211_channel * +wlan_get_next_channel(const struct asn_oid *oid, uint sub, + struct wlan_iface **wif) +{ + uint32_t cindex; + char wname[IFNAMSIZ]; + + if (oid->len - sub == 0) { + for (*wif = wlan_first_interface(); *wif != NULL; + *wif = wlan_next_interface(*wif)) { + if ((*wif)->status != RowStatus_active) + continue; + if ((*wif)->nchannels != 0 && (*wif)->chanlist != NULL) + return ((*wif)->chanlist); + } + return (NULL); + } + + if (wlan_channel_index_decode(oid, sub, wname, &cindex) < 0) + return (NULL); + + if ((*wif = wlan_find_interface(wname)) == NULL) + return (NULL); + + if (cindex < (*wif)->nchannels) + return ((*wif)->chanlist + cindex); + + while ((*wif = wlan_next_interface(*wif)) != NULL) + if ((*wif)->status == RowStatus_active) + if ((*wif)->nchannels != 0 && (*wif)->chanlist != NULL) + return ((*wif)->chanlist); + + return (NULL); +} + +/* + * Calls for manipulating the roam params of a wireless interface. + */ +static void +wlan_update_roam_params(void) +{ + struct wlan_iface *wif; + + if ((time(NULL) - wlan_roamlist_age) <= WLAN_LIST_MAXAGE) + return; + + for (wif = wlan_first_interface(); wif != NULL; + wif = wlan_next_interface(wif)) { + if (wif->status != RowStatus_active) + continue; + (void)wlan_get_roam_params(wif); + } + wlan_roamlist_age = time(NULL); +} + +static struct ieee80211_roamparam * +wlan_get_roam_param(const struct asn_oid *oid, uint sub, struct wlan_iface **wif) +{ + uint32_t phy; + char wname[IFNAMSIZ]; + + if (wlan_phy_index_decode(oid, sub, wname, &phy) < 0) + return (NULL); + + if ((*wif = wlan_find_interface(wname)) == NULL) + return (NULL); + + if (phy == 0 || phy > IEEE80211_MODE_MAX) + return (NULL); + + return ((*wif)->roamparams.params + phy - 1); +} + +static struct ieee80211_roamparam * +wlan_get_next_roam_param(const struct asn_oid *oid, uint sub, + struct wlan_iface **wif, uint32_t *phy) +{ + char wname[IFNAMSIZ]; + + if (oid->len - sub == 0) { + for (*wif = wlan_first_interface(); *wif != NULL; + *wif = wlan_next_interface(*wif)) { + if ((*wif)->status != RowStatus_active) + continue; + *phy = 1; + return ((*wif)->roamparams.params); + } + return (NULL); + } + + if (wlan_phy_index_decode(oid, sub, wname, phy) < 0) + return (NULL); + + if (*phy == 0 || (*wif = wlan_find_interface(wname)) == NULL) + return (NULL); + + if (++(*phy) <= IEEE80211_MODE_MAX) + return ((*wif)->roamparams.params + *phy - 1); + + *phy = 1; + while ((*wif = wlan_next_interface(*wif)) != NULL) + if ((*wif)->status == RowStatus_active) + return ((*wif)->roamparams.params); + + return (NULL); +} + +/* + * Calls for manipulating the tx params of a wireless interface. + */ +static void +wlan_update_tx_params(void) +{ + struct wlan_iface *wif; + + if ((time(NULL) - wlan_tx_paramlist_age) <= WLAN_LIST_MAXAGE) + return; + + for (wif = wlan_first_interface(); wif != NULL; + wif = wlan_next_interface(wif)) { + if (wif->status != RowStatus_active) + continue; + (void)wlan_get_tx_params(wif); + } + + wlan_tx_paramlist_age = time(NULL); +} + +static struct ieee80211_txparam * +wlan_get_tx_param(const struct asn_oid *oid, uint sub, struct wlan_iface **wif, + uint32_t *phy) +{ + char wname[IFNAMSIZ]; + + if (wlan_phy_index_decode(oid, sub, wname, phy) < 0) + return (NULL); + + if ((*wif = wlan_find_interface(wname)) == NULL) + return (NULL); + + if (*phy == 0 || *phy > IEEE80211_MODE_MAX) + return (NULL); + + return ((*wif)->txparams.params + *phy - 1); +} + +static struct ieee80211_txparam * +wlan_get_next_tx_param(const struct asn_oid *oid, uint sub, + struct wlan_iface **wif, uint32_t *phy) +{ + char wname[IFNAMSIZ]; + + if (oid->len - sub == 0) { + for (*wif = wlan_first_interface(); *wif != NULL; + *wif = wlan_next_interface(*wif)) { + if ((*wif)->status != RowStatus_active) + continue; + *phy = 1; + return ((*wif)->txparams.params); + } + return (NULL); + } + + if (wlan_phy_index_decode(oid, sub, wname, phy) < 0) + return (NULL); + + if (*phy == 0 || (*wif = wlan_find_interface(wname)) == NULL) + return (NULL); + + if (++(*phy) <= IEEE80211_MODE_MAX) + return ((*wif)->txparams.params + *phy - 1); + + *phy = 1; + while ((*wif = wlan_next_interface(*wif)) != NULL) + if ((*wif)->status == RowStatus_active) + return ((*wif)->txparams.params); + + return (NULL); +} + +/* + * Calls for manipulating the scan results for a wireless interface. + */ +static void +wlan_scan_free_results(struct wlan_iface *wif) +{ + struct wlan_scan_result *sr; + + while ((sr = SLIST_FIRST(&wif->scanlist)) != NULL) { + SLIST_REMOVE_HEAD(&wif->scanlist, wsr); + free(sr); + } + + SLIST_INIT(&wif->scanlist); +} + +static struct wlan_scan_result * +wlan_scan_find_result(struct wlan_iface *wif, uint8_t *ssid, uint8_t *bssid) +{ + struct wlan_scan_result *sr; + + SLIST_FOREACH(sr, &wif->scanlist, wsr) + if (strlen(ssid) == strlen(sr->ssid) && + strcmp(sr->ssid, ssid) == 0 && + memcmp(sr->bssid, bssid, IEEE80211_ADDR_LEN) == 0) + break; + + return (sr); +} + +struct wlan_scan_result * +wlan_scan_new_result(const uint8_t *ssid, const uint8_t *bssid) +{ + struct wlan_scan_result *sr; + + sr = (struct wlan_scan_result *)malloc(sizeof(*sr)); + if (sr == NULL) + return (NULL); + + memset(sr, 0, sizeof(*sr)); + if (ssid[0] != '\0') + strlcpy(sr->ssid, ssid, IEEE80211_NWID_LEN + 1); + memcpy(sr->bssid, bssid, IEEE80211_ADDR_LEN); + + return (sr); +} + +void +wlan_scan_free_result(struct wlan_scan_result *sr) +{ + free(sr); +} + +static int +wlan_scan_compare_result(struct wlan_scan_result *sr1, + struct wlan_scan_result *sr2) +{ + uint32_t i; + + if (strlen(sr1->ssid) < strlen(sr2->ssid)) + return (-1); + if (strlen(sr1->ssid) > strlen(sr2->ssid)) + return (1); + + for (i = 0; i < strlen(sr1->ssid) && i < strlen(sr2->ssid); i++) { + if (sr1->ssid[i] < sr2->ssid[i]) + return (-1); + if (sr1->ssid[i] > sr2->ssid[i]) + return (1); + } + + for (i = 0; i < IEEE80211_ADDR_LEN; i++) { + if (sr1->bssid[i] < sr2->bssid[i]) + return (-1); + if (sr1->bssid[i] > sr2->bssid[i]) + return (1); + } + + return (0); +} + +int +wlan_scan_add_result(struct wlan_iface *wif, struct wlan_scan_result *sr) +{ + struct wlan_scan_result *prev, *temp; + + SLIST_FOREACH(temp, &wif->scanlist, wsr) + if (strlen(temp->ssid) == strlen(sr->ssid) && + strcmp(sr->ssid, temp->ssid) == 0 && + memcmp(sr->bssid, temp->bssid, IEEE80211_ADDR_LEN) == 0) + return (-1); + + if ((prev = SLIST_FIRST(&wif->scanlist)) == NULL || + wlan_scan_compare_result(sr, prev) < 0) { + SLIST_INSERT_HEAD(&wif->scanlist, sr, wsr); + return (0); + } + + SLIST_FOREACH(temp, &wif->scanlist, wsr) { + if (wlan_scan_compare_result(sr, temp) < 0) + break; + prev = temp; + } + + SLIST_INSERT_AFTER(prev, sr, wsr); + return (0); +} + +static void +wlan_scan_update_results(void) +{ + struct wlan_iface *wif; + + if ((time(NULL) - wlan_scanlist_age) <= WLAN_LIST_MAXAGE) + return; + + for (wif = wlan_first_interface(); wif != NULL; + wif = wlan_next_interface(wif)) { + if (wif->status != RowStatus_active) + continue; + wlan_scan_free_results(wif); + (void)wlan_get_scan_results(wif); + } + wlan_scanlist_age = time(NULL); +} + +static int +wlan_scanr_index_decode(const struct asn_oid *oid, uint sub, + char *wname, uint8_t *ssid, uint8_t *bssid) +{ + uint32_t i; + int offset; + + if (oid->subs[sub] >= IFNAMSIZ) + return (-1); + for (i = 0; i < oid->subs[sub]; i++) + wname[i] = oid->subs[sub + i + 1]; + wname[oid->subs[sub]] = '\0'; + + offset = sub + oid->subs[sub] + 1; + if (oid->subs[offset] > IEEE80211_NWID_LEN) + return (-1); + for (i = 0; i < oid->subs[offset]; i++) + ssid[i] = oid->subs[offset + i + 1]; + ssid[i] = '\0'; + + offset = sub + oid->subs[sub] + oid->subs[offset] + 2; + if (oid->subs[offset] != IEEE80211_ADDR_LEN) + return (-1); + for (i = 0; i < IEEE80211_ADDR_LEN; i++) + bssid[i] = oid->subs[offset + i + 1]; + + return (0); +} + +static void +wlan_append_scanr_index(struct asn_oid *oid, uint sub, char *wname, + uint8_t *ssid, uint8_t *bssid) +{ + uint32_t i; + + oid->len = sub + strlen(wname) + strlen(ssid) + IEEE80211_ADDR_LEN + 3; + oid->subs[sub] = strlen(wname); + for (i = 1; i <= strlen(wname); i++) + oid->subs[sub + i] = wname[i - 1]; + + sub += strlen(wname) + 1; + oid->subs[sub] = strlen(ssid); + for (i = 1; i <= strlen(ssid); i++) + oid->subs[sub + i] = ssid[i - 1]; + + sub += strlen(ssid) + 1; + oid->subs[sub] = IEEE80211_ADDR_LEN; + for (i = 1; i <= IEEE80211_ADDR_LEN; i++) + oid->subs[sub + i] = bssid[i - 1]; +} + +static struct wlan_scan_result * +wlan_get_scanr(const struct asn_oid *oid, uint sub, struct wlan_iface **wif) +{ + char wname[IFNAMSIZ]; + uint8_t ssid[IEEE80211_NWID_LEN + 1]; + uint8_t bssid[IEEE80211_ADDR_LEN]; + + if (wlan_scanr_index_decode(oid, sub, wname, ssid, bssid) < 0) + return (NULL); + + if ((*wif = wlan_find_interface(wname)) == NULL) + return (NULL); + + return (wlan_scan_find_result(*wif, ssid, bssid)); +} + +static struct wlan_scan_result * +wlan_get_next_scanr(const struct asn_oid *oid, uint sub, + struct wlan_iface **wif) +{ + char wname[IFNAMSIZ]; + uint8_t ssid[IEEE80211_NWID_LEN + 1]; + uint8_t bssid[IEEE80211_ADDR_LEN]; + struct wlan_scan_result *sr; + + if (oid->len - sub == 0) { + for (*wif = wlan_first_interface(); *wif != NULL; + *wif = wlan_next_interface(*wif)) { + sr = SLIST_FIRST(&(*wif)->scanlist); + if (sr != NULL) + return (sr); + } + return (NULL); + } + + if (wlan_scanr_index_decode(oid, sub, wname, ssid, bssid) < 0 || + (*wif = wlan_find_interface(wname)) == NULL || + (sr = wlan_scan_find_result(*wif, ssid, bssid)) == NULL) + return (NULL); + + if ((sr = SLIST_NEXT(sr, wsr)) != NULL) + return (sr); + + while ((*wif = wlan_next_interface(*wif)) != NULL) + if ((sr = SLIST_FIRST(&(*wif)->scanlist)) != NULL) + break; + + return (sr); +} + +/* + * MAC Access Control. + */ +static void +wlan_mac_free_maclist(struct wlan_iface *wif) +{ + struct wlan_mac_mac *wmm; + + while ((wmm = SLIST_FIRST(&wif->mac_maclist)) != NULL) { + SLIST_REMOVE_HEAD(&wif->mac_maclist, wm); + free(wmm); + } + + SLIST_INIT(&wif->mac_maclist); +} + +static struct wlan_mac_mac * +wlan_mac_find_mac(struct wlan_iface *wif, uint8_t *mac) +{ + struct wlan_mac_mac *wmm; + + SLIST_FOREACH(wmm, &wif->mac_maclist, wm) + if (memcmp(wmm->mac, mac, IEEE80211_ADDR_LEN) == 0) + break; + + return (wmm); +} + +struct wlan_mac_mac * +wlan_mac_new_mac(const uint8_t *mac) +{ + struct wlan_mac_mac *wmm; + + if ((wmm = (struct wlan_mac_mac *)malloc(sizeof(*wmm))) == NULL) + return (NULL); + + memset(wmm, 0, sizeof(*wmm)); + memcpy(wmm->mac, mac, IEEE80211_ADDR_LEN); + wmm->mac_status = RowStatus_notReady; + + return (wmm); +} + +void +wlan_mac_free_mac(struct wlan_mac_mac *wmm) +{ + free(wmm); +} + +int +wlan_mac_add_mac(struct wlan_iface *wif, struct wlan_mac_mac *wmm) +{ + struct wlan_mac_mac *temp, *prev; + + SLIST_FOREACH(temp, &wif->mac_maclist, wm) + if (memcmp(temp->mac, wmm->mac, IEEE80211_ADDR_LEN) == 0) + return (-1); + + if ((prev = SLIST_FIRST(&wif->mac_maclist)) == NULL || + memcmp(wmm->mac, prev->mac,IEEE80211_ADDR_LEN) < 0) { + SLIST_INSERT_HEAD(&wif->mac_maclist, wmm, wm); + return (0); + } + + SLIST_FOREACH(temp, &wif->mac_maclist, wm) { + if (memcmp(wmm->mac, temp->mac, IEEE80211_ADDR_LEN) < 0) + break; + prev = temp; + } + + SLIST_INSERT_AFTER(prev, wmm, wm); + return (0); +} + +static int +wlan_mac_delete_mac(struct wlan_iface *wif, struct wlan_mac_mac *wmm) +{ + if (wmm->mac_status == RowStatus_active && + wlan_del_mac_acl_mac(wif, wmm) < 0) + return (-1); + + SLIST_REMOVE(&wif->mac_maclist, wmm, wlan_mac_mac, wm); + free(wmm); + + return (0); +} + +static void +wlan_mac_update_aclmacs(void) +{ + struct wlan_iface *wif; + struct wlan_mac_mac *wmm, *twmm; + + if ((time(NULL) - wlan_maclist_age) <= WLAN_LIST_MAXAGE) + return; + + for (wif = wlan_first_interface(); wif != NULL; + wif = wlan_next_interface(wif)) { + if (wif->status != RowStatus_active) + continue; + /* + * Nuke old entries - XXX - they are likely not to + * change often - reconsider. + */ + SLIST_FOREACH_SAFE(wmm, &wif->mac_maclist, wm, twmm) + if (wmm->mac_status == RowStatus_active) { + SLIST_REMOVE(&wif->mac_maclist, wmm, + wlan_mac_mac, wm); + wlan_mac_free_mac(wmm); + } + (void)wlan_get_mac_acl_macs(wif); + } + wlan_maclist_age = time(NULL); +} + +static struct wlan_mac_mac * +wlan_get_acl_mac(const struct asn_oid *oid, uint sub, struct wlan_iface **wif) +{ + char wname[IFNAMSIZ]; + char mac[IEEE80211_ADDR_LEN]; + + if (wlan_mac_index_decode(oid, sub, wname, mac) < 0) + return (NULL); + + if ((*wif = wlan_find_interface(wname)) == NULL) + return (NULL); + + return (wlan_mac_find_mac(*wif, mac)); +} + +static struct wlan_mac_mac * +wlan_get_next_acl_mac(const struct asn_oid *oid, uint sub, + struct wlan_iface **wif) +{ + char wname[IFNAMSIZ]; + char mac[IEEE80211_ADDR_LEN]; + struct wlan_mac_mac *wmm; + + if (oid->len - sub == 0) { + for (*wif = wlan_first_interface(); *wif != NULL; + *wif = wlan_next_interface(*wif)) { + wmm = SLIST_FIRST(&(*wif)->mac_maclist); + if (wmm != NULL) + return (wmm); + } + return (NULL); + } + + if (wlan_mac_index_decode(oid, sub, wname, mac) < 0 || + (*wif = wlan_find_interface(wname)) == NULL || + (wmm = wlan_mac_find_mac(*wif, mac)) == NULL) + return (NULL); + + if ((wmm = SLIST_NEXT(wmm, wm)) != NULL) + return (wmm); + + while ((*wif = wlan_next_interface(*wif)) != NULL) + if ((wmm = SLIST_FIRST(&(*wif)->mac_maclist)) != NULL) + break; + + return (wmm); +} + +static int +wlan_acl_mac_set_status(struct snmp_context *ctx, struct snmp_value *val, + uint sub) +{ + char wname[IFNAMSIZ]; + uint8_t mac[IEEE80211_ADDR_LEN]; + struct wlan_iface *wif; + struct wlan_mac_mac *macl; + + if (wlan_mac_index_decode(&val->var, sub, wname, mac) < 0) + return (SNMP_ERR_GENERR); + macl = wlan_get_acl_mac(&val->var, sub, &wif); + + switch (val->v.integer) { + case RowStatus_createAndGo: + if (macl != NULL) + return (SNMP_ERR_INCONS_NAME); + break; + case RowStatus_destroy: + if (macl == NULL) + return (SNMP_ERR_NOSUCHNAME); + ctx->scratch->int1 = RowStatus_active; + return (SNMP_ERR_NOERROR); + default: + return (SNMP_ERR_INCONS_VALUE); + } + + + if (wif == NULL || !wif->macsupported) + return (SNMP_ERR_INCONS_VALUE); + + if ((macl = wlan_mac_new_mac((const uint8_t *)mac)) == NULL) + return (SNMP_ERR_GENERR); + + ctx->scratch->int1 = RowStatus_destroy; + + if (wlan_mac_add_mac(wif, macl) < 0) { + wlan_mac_free_mac(macl); + return (SNMP_ERR_GENERR); + } + + ctx->scratch->int1 = RowStatus_destroy; + if (wlan_add_mac_acl_mac(wif, macl) < 0) { + (void)wlan_mac_delete_mac(wif, macl); + return (SNMP_ERR_GENERR); + } + + return (SNMP_ERR_NOERROR); +} + +/* + * Wireless interfaces operating as mesh points. + */ +static struct wlan_iface * +wlan_mesh_first_interface(void) +{ + struct wlan_iface *wif; + + SLIST_FOREACH(wif, &wlan_ifaces, w_if) + if (wif->mode == WlanIfaceOperatingModeType_meshPoint && + wif->status == RowStatus_active) + break; + + return (wif); +} + +static struct wlan_iface * +wlan_mesh_next_interface(struct wlan_iface *wif) +{ + struct wlan_iface *nwif; + + while ((nwif = wlan_next_interface(wif)) != NULL) { + if (nwif->mode == WlanIfaceOperatingModeType_meshPoint && + nwif->status == RowStatus_active) + break; + wif = nwif; + } + + return (nwif); +} + +static struct wlan_iface * +wlan_mesh_get_iface(const struct asn_oid *oid, uint sub) +{ + struct wlan_iface *wif; + + if ((wif = wlan_get_interface(oid, sub)) == NULL) + return (NULL); + + if (wif->mode != WlanIfaceOperatingModeType_meshPoint) + return (NULL); + + return (wif); +} + +static struct wlan_iface * +wlan_mesh_get_next_iface(const struct asn_oid *oid, uint sub) +{ + uint32_t i; + uint8_t wname[IFNAMSIZ]; + struct wlan_iface *wif; + + if (oid->len - sub == 0) + return (wlan_mesh_first_interface()); + + if (oid->len - sub != oid->subs[sub] + 1 || oid->subs[sub] >= IFNAMSIZ) + return (NULL); + + memset(wname, 0, IFNAMSIZ); + for (i = 0; i < oid->subs[sub]; i++) + wname[i] = oid->subs[sub + i + 1]; + wname[i] = '\0'; + + if ((wif = wlan_find_interface(wname)) == NULL) + return (NULL); + + return (wlan_mesh_next_interface(wif)); +} + +/* + * The neighbors of wireless interfaces operating as mesh points. + */ +static struct wlan_peer * +wlan_mesh_get_peer(const struct asn_oid *oid, uint sub, struct wlan_iface **wif) +{ + char wname[IFNAMSIZ]; + uint8_t pmac[IEEE80211_ADDR_LEN]; + + if (wlan_mac_index_decode(oid, sub, wname, pmac) < 0) + return (NULL); + + if ((*wif = wlan_find_interface(wname)) == NULL || + (*wif)->mode != WlanIfaceOperatingModeType_meshPoint) + return (NULL); + + return (wlan_find_peer(*wif, pmac)); +} + +static struct wlan_peer * +wlan_mesh_get_next_peer(const struct asn_oid *oid, uint sub, struct wlan_iface **wif) +{ + char wname[IFNAMSIZ]; + char pmac[IEEE80211_ADDR_LEN]; + struct wlan_peer *wip; + + if (oid->len - sub == 0) { + for (*wif = wlan_mesh_first_interface(); *wif != NULL; + *wif = wlan_mesh_next_interface(*wif)) { + wip = SLIST_FIRST(&(*wif)->peerlist); + if (wip != NULL) + return (wip); + } + return (NULL); + } + + if (wlan_mac_index_decode(oid, sub, wname, pmac) < 0 || + (*wif = wlan_find_interface(wname)) == NULL || + (*wif)->mode != WlanIfaceOperatingModeType_meshPoint || + (wip = wlan_find_peer(*wif, pmac)) == NULL) + return (NULL); + + if ((wip = SLIST_NEXT(wip, wp)) != NULL) + return (wip); + + while ((*wif = wlan_mesh_next_interface(*wif)) != NULL) + if ((wip = SLIST_FIRST(&(*wif)->peerlist)) != NULL) + break; + + return (wip); +} + +/* + * Mesh routing table. + */ +static void +wlan_mesh_free_routes(struct wlan_iface *wif) +{ + struct wlan_mesh_route *wmr; + + while ((wmr = SLIST_FIRST(&wif->mesh_routelist)) != NULL) { + SLIST_REMOVE_HEAD(&wif->mesh_routelist, wr); + free(wmr); + } + + SLIST_INIT(&wif->mesh_routelist); +} + +static struct wlan_mesh_route * +wlan_mesh_find_route(struct wlan_iface *wif, uint8_t *dstmac) +{ + struct wlan_mesh_route *wmr; + + if (wif->mode != WlanIfaceOperatingModeType_meshPoint) + return (NULL); + + SLIST_FOREACH(wmr, &wif->mesh_routelist, wr) + if (memcmp(wmr->imroute.imr_dest, dstmac, + IEEE80211_ADDR_LEN) == 0) + break; + + return (wmr); +} + +struct wlan_mesh_route * +wlan_mesh_new_route(const uint8_t *dstmac) +{ + struct wlan_mesh_route *wmr; + + if ((wmr = (struct wlan_mesh_route *)malloc(sizeof(*wmr))) == NULL) + return (NULL); + + memset(wmr, 0, sizeof(*wmr)); + memcpy(wmr->imroute.imr_dest, dstmac, IEEE80211_ADDR_LEN); + wmr->mroute_status = RowStatus_notReady; + + return (wmr); +} + +void +wlan_mesh_free_route(struct wlan_mesh_route *wmr) +{ + free(wmr); +} + +int +wlan_mesh_add_rtentry(struct wlan_iface *wif, struct wlan_mesh_route *wmr) +{ + struct wlan_mesh_route *temp, *prev; + + SLIST_FOREACH(temp, &wif->mesh_routelist, wr) + if (memcmp(temp->imroute.imr_dest, wmr->imroute.imr_dest, + IEEE80211_ADDR_LEN) == 0) + return (-1); + + if ((prev = SLIST_FIRST(&wif->mesh_routelist)) == NULL || + memcmp(wmr->imroute.imr_dest, prev->imroute.imr_dest, + IEEE80211_ADDR_LEN) < 0) { + SLIST_INSERT_HEAD(&wif->mesh_routelist, wmr, wr); + return (0); + } + + SLIST_FOREACH(temp, &wif->mesh_routelist, wr) { + if (memcmp(wmr->imroute.imr_dest, temp->imroute.imr_dest, + IEEE80211_ADDR_LEN) < 0) + break; + prev = temp; + } + + SLIST_INSERT_AFTER(prev, wmr, wr); + return (0); +} + +static int +wlan_mesh_delete_route(struct wlan_iface *wif, struct wlan_mesh_route *wmr) +{ + if (wmr->mroute_status == RowStatus_active && + wlan_mesh_del_route(wif, wmr) < 0) + return (-1); + + SLIST_REMOVE(&wif->mesh_routelist, wmr, wlan_mesh_route, wr); + free(wmr); + + return (0); +} + +static void +wlan_mesh_update_routes(void) +{ + struct wlan_iface *wif; + struct wlan_mesh_route *wmr, *twmr; + + if ((time(NULL) - wlan_mrlist_age) <= WLAN_LIST_MAXAGE) + return; + + for (wif = wlan_mesh_first_interface(); wif != NULL; + wif = wlan_mesh_next_interface(wif)) { + /* + * Nuke old entries - XXX - they are likely not to + * change often - reconsider. + */ + SLIST_FOREACH_SAFE(wmr, &wif->mesh_routelist, wr, twmr) + if (wmr->mroute_status == RowStatus_active) { + SLIST_REMOVE(&wif->mesh_routelist, wmr, + wlan_mesh_route, wr); + wlan_mesh_free_route(wmr); + } + (void)wlan_mesh_get_routelist(wif); + } + wlan_mrlist_age = time(NULL); +} + +static struct wlan_mesh_route * +wlan_mesh_get_route(const struct asn_oid *oid, uint sub, struct wlan_iface **wif) +{ + char wname[IFNAMSIZ]; + char dstmac[IEEE80211_ADDR_LEN]; + + if (wlan_mac_index_decode(oid, sub, wname, dstmac) < 0) + return (NULL); + + if ((*wif = wlan_find_interface(wname)) == NULL) + return (NULL); + + return (wlan_mesh_find_route(*wif, dstmac)); +} + +static struct wlan_mesh_route * +wlan_mesh_get_next_route(const struct asn_oid *oid, uint sub, + struct wlan_iface **wif) +{ + char wname[IFNAMSIZ]; + char dstmac[IEEE80211_ADDR_LEN]; + struct wlan_mesh_route *wmr; + + if (oid->len - sub == 0) { + for (*wif = wlan_mesh_first_interface(); *wif != NULL; + *wif = wlan_mesh_next_interface(*wif)) { + wmr = SLIST_FIRST(&(*wif)->mesh_routelist); + if (wmr != NULL) + return (wmr); + } + return (NULL); + } + + if (wlan_mac_index_decode(oid, sub, wname, dstmac) < 0 || + (*wif = wlan_find_interface(wname)) == NULL || + (wmr = wlan_mesh_find_route(*wif, dstmac)) == NULL) + return (NULL); + + if ((wmr = SLIST_NEXT(wmr, wr)) != NULL) + return (wmr); + + while ((*wif = wlan_mesh_next_interface(*wif)) != NULL) + if ((wmr = SLIST_FIRST(&(*wif)->mesh_routelist)) != NULL) + break; + + return (wmr); +} + +static int +wlan_mesh_route_set_status(struct snmp_context *ctx, struct snmp_value *val, + uint sub) +{ + char wname[IFNAMSIZ]; + char mac[IEEE80211_ADDR_LEN]; + struct wlan_mesh_route *wmr; + struct wlan_iface *wif; + + if (wlan_mac_index_decode(&val->var, sub, wname, mac) < 0) + return (SNMP_ERR_GENERR); + wmr = wlan_mesh_get_route(&val->var, sub, &wif); + + switch (val->v.integer) { + case RowStatus_createAndGo: + if (wmr != NULL) + return (SNMP_ERR_INCONS_NAME); + break; + case RowStatus_destroy: + if (wmr == NULL) + return (SNMP_ERR_NOSUCHNAME); + ctx->scratch->int1 = RowStatus_active; + return (SNMP_ERR_NOERROR); + default: + return (SNMP_ERR_INCONS_VALUE); + } + + if ((wif = wlan_find_interface(wname)) == NULL) + return (SNMP_ERR_INCONS_NAME); + + if ((wmr = wlan_mesh_new_route(mac)) == NULL) + return (SNMP_ERR_GENERR); + + if (wlan_mesh_add_rtentry(wif, wmr) < 0) { + wlan_mesh_free_route(wmr); + return (SNMP_ERR_GENERR); + } + + ctx->scratch->int1 = RowStatus_destroy; + if (wlan_mesh_add_route(wif, wmr) < 0) { + (void)wlan_mesh_delete_route(wif, wmr); + return (SNMP_ERR_GENERR); + } + + return (SNMP_ERR_NOERROR); +} + +/* + * Wlan snmp module initialization hook. + * Returns 0 on success, < 0 on error. + */ +static int +wlan_init(struct lmodule * mod __unused, int argc __unused, + char *argv[] __unused) +{ + if (wlan_kmodules_load() < 0) + return (-1); + + if (wlan_ioctl_init() < 0) + return (-1); + + /* Register for new interface creation notifications. */ + if (mib_register_newif(wlan_attach_newif, wlan_module)) { + syslog(LOG_ERR, "Cannot register newif function: %s", + strerror(errno)); + return (-1); + } + + return (0); +} + +/* + * Wlan snmp module finalization hook. + */ +static int +wlan_fini(void) +{ + mib_unregister_newif(wlan_module); + or_unregister(reg_wlan); + + /* XXX: Cleanup! */ + wlan_free_iflist(); + + return (0); +} + +/* + * Refetch all available data from the kernel. + */ +static void +wlan_update_data(void *arg __unused) +{ +} + +/* + * Wlan snmp module start operation. + */ +static void +wlan_start(void) +{ + struct mibif *ifp; + + reg_wlan = or_register(&oid_wlan, + "The MIB module for managing wireless networking.", wlan_module); + + /* Add the existing wlan interfaces. */ + for (ifp = mib_first_if(); ifp != NULL; ifp = mib_next_if(ifp)) + wlan_attach_newif(ifp); + + wlan_data_timer = timer_start_repeat(wlan_poll_ticks, + wlan_poll_ticks, wlan_update_data, NULL, wlan_module); +} + +/* + * Dump the Wlan snmp module data on SIGUSR1. + */ +static void +wlan_dump(void) +{ + /* XXX: Print some debug info to syslog. */ + struct wlan_iface *wif; + + for (wif = wlan_first_interface(); wif != NULL; + wif = wlan_next_interface(wif)) + syslog(LOG_ERR, "wlan iface %s", wif->wname); +} + +const char wlan_comment[] = \ +"This module implements the BEGEMOT MIB for wireless networking."; + +const struct snmp_module config = { + .comment = wlan_comment, + .init = wlan_init, + .fini = wlan_fini, + .start = wlan_start, + .tree = wlan_ctree, + .dump = wlan_dump, + .tree_size = wlan_CTREE_SIZE, +}; diff --git a/usr.sbin/bsnmpd/modules/snmp_wlan/wlan_snmp.h b/usr.sbin/bsnmpd/modules/snmp_wlan/wlan_snmp.h new file mode 100644 index 0000000..9e184f5 --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_wlan/wlan_snmp.h @@ -0,0 +1,286 @@ +/*- + * Copyright (c) 2010 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Shteryana Sotirova Shopova under + * sponsorship from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#define WLAN_IFMODE_MAX WlanIfaceOperatingModeType_tdma +#define WLAN_COUNTRY_CODE_SIZE 3 +#define WLAN_BGSCAN_IDLE_MIN 100 /* XXX */ +#define WLAN_SCAN_VALID_MIN 10 /* XXX */ +#define WLAN_TDMA_MAXSLOTS 2 /* XXX */ + +struct wlan_iface; + +struct wlan_peer { + uint8_t pmac[IEEE80211_ADDR_LEN]; /* key */ + uint16_t associd; + uint16_t vlan; + uint16_t frequency; + uint32_t fflags; + uint8_t txrate; + int8_t rssi; + uint16_t idle; + uint16_t txseqs; + uint16_t rxseqs; + uint16_t txpower; + uint8_t capinfo; + uint32_t state; + uint16_t local_id; + uint16_t peer_id; + SLIST_ENTRY(wlan_peer) wp; +}; + +SLIST_HEAD(wlan_peerlist, wlan_peer); + +struct wlan_scan_result { + uint8_t ssid[IEEE80211_NWID_LEN + 1]; + uint8_t bssid[IEEE80211_ADDR_LEN]; + uint8_t opchannel; + int8_t rssi; + uint16_t frequency; + int8_t noise; + uint16_t bintval; + uint8_t capinfo; + struct wlan_iface *pwif; + SLIST_ENTRY(wlan_scan_result) wsr; +}; + +SLIST_HEAD(wlan_scanlist, wlan_scan_result); + +struct wlan_mac_mac { + uint8_t mac[IEEE80211_ADDR_LEN]; + enum RowStatus mac_status; + SLIST_ENTRY(wlan_mac_mac) wm; +}; + +SLIST_HEAD(wlan_maclist, wlan_mac_mac); + +struct wlan_mesh_route { + struct ieee80211req_mesh_route imroute; + enum RowStatus mroute_status; + SLIST_ENTRY(wlan_mesh_route) wr; +}; + +SLIST_HEAD(wlan_mesh_routes, wlan_mesh_route); + +struct wlan_iface { + char wname[IFNAMSIZ]; + uint32_t index; + char pname[IFNAMSIZ]; + enum WlanIfaceOperatingModeType mode; + uint32_t flags; + uint8_t dbssid[IEEE80211_ADDR_LEN]; + uint8_t dlmac[IEEE80211_ADDR_LEN]; + enum RowStatus status; + enum wlanIfaceState state; + uint8_t internal; + + uint32_t drivercaps; + uint32_t cryptocaps; + uint32_t htcaps; + + uint32_t packet_burst; + uint8_t country_code[WLAN_COUNTRY_CODE_SIZE]; + enum WlanRegDomainCode reg_domain; + uint8_t desired_ssid[IEEE80211_NWID_LEN + 1]; + uint32_t desired_channel; + enum TruthValue dyn_frequency; + enum TruthValue fast_frames; + enum TruthValue dturbo; + int32_t tx_power; + int32_t frag_threshold; + int32_t rts_threshold; + enum TruthValue priv_subscribe; + enum TruthValue bg_scan; + int32_t bg_scan_idle; + int32_t bg_scan_interval; + int32_t beacons_missed; + uint8_t desired_bssid[IEEE80211_ADDR_LEN]; + enum wlanIfaceRoamingMode roam_mode; + enum TruthValue dot11d; + enum TruthValue dot11h; + enum TruthValue dynamic_wds; + enum TruthValue power_save; + enum TruthValue ap_bridge; + int32_t beacon_interval; + int32_t dtim_period; + enum TruthValue hide_ssid; + enum TruthValue inact_process; + enum wlanIfaceDot11gProtMode do11g_protect; + enum TruthValue dot11g_pure; + enum TruthValue dot11n_pure; + enum WlanIfaceDot11nPduType ampdu; + int32_t ampdu_density; + int32_t ampdu_limit; + enum WlanIfaceDot11nPduType amsdu; + int32_t amsdu_limit; + enum TruthValue ht_enabled; + enum TruthValue ht_compatible; + enum wlanIfaceDot11nHTProtMode ht_prot_mode; + enum TruthValue rifs; + enum TruthValue short_gi; + enum wlanIfaceDot11nSMPSMode smps_mode; + int32_t tdma_slot; + int32_t tdma_slot_count; + int32_t tdma_slot_length; + int32_t tdma_binterval; + + struct wlan_peerlist peerlist; + struct ieee80211_stats stats; + uint32_t nchannels; + struct ieee80211_channel *chanlist; + struct ieee80211_roamparams_req roamparams; + struct ieee80211_txparams_req txparams; + + uint32_t scan_flags; + uint32_t scan_duration; + uint32_t scan_mindwell; + uint32_t scan_maxdwell; + enum wlanScanConfigStatus scan_status; + struct wlan_scanlist scanlist; + + uint8_t wepsupported; + enum wlanWepMode wepmode; + int32_t weptxkey; + + uint8_t macsupported; + enum wlanMACAccessControlPolicy mac_policy; + uint32_t mac_nacls; + struct wlan_maclist mac_maclist; + + uint32_t mesh_ttl; + enum wlanMeshPeeringEnabled mesh_peering; + enum wlanMeshForwardingEnabled mesh_forwarding; + enum wlanMeshMetric mesh_metric; + enum wlanMeshPath mesh_path; + enum wlanHWMPRootMode hwmp_root_mode; + uint32_t hwmp_max_hops; + struct wlan_mesh_routes mesh_routelist; + + SLIST_ENTRY(wlan_iface) w_if; +}; + +enum wlan_syscl { + WLAN_MESH_RETRY_TO = 0, + WLAN_MESH_HOLDING_TO, + WLAN_MESH_CONFIRM_TO, + WLAN_MESH_MAX_RETRIES, + WLAN_HWMP_TARGET_ONLY, + WLAN_HWMP_REPLY_FORWARD, + WLAN_HWMP_PATH_LIFETIME, + WLAN_HWMP_ROOT_TO, + WLAN_HWMP_ROOT_INT, + WLAN_HWMP_RANN_INT, + WLAN_HWMP_INACTIVITY_TO, + WLAN_SYSCTL_MAX +}; + +struct wlan_config { + int32_t mesh_retryto; + int32_t mesh_holdingto; + int32_t mesh_confirmto; + int32_t mesh_maxretries; + int32_t hwmp_targetonly; + int32_t hwmp_replyforward; + int32_t hwmp_pathlifetime; + int32_t hwmp_roottimeout; + int32_t hwmp_rootint; + int32_t hwmp_rannint; + int32_t hwmp_inact; +}; + +int wlan_ioctl_init(void); +int wlan_kmodules_load(void); +int wlan_check_media(char *); +int wlan_config_state(struct wlan_iface *, uint8_t); +int wlan_get_opmode(struct wlan_iface *wif); +int wlan_get_local_addr(struct wlan_iface *wif); +int wlan_get_parent(struct wlan_iface *wif); +int wlan_get_driver_caps(struct wlan_iface *wif); +uint8_t wlan_channel_state_to_snmp(uint8_t cstate); +uint32_t wlan_channel_flags_to_snmp(uint32_t cflags); +int wlan_get_channel_list(struct wlan_iface *wif); +int wlan_get_roam_params(struct wlan_iface *wif); +int wlan_get_tx_params(struct wlan_iface *wif); +int wlan_set_tx_params(struct wlan_iface *wif, int32_t pmode); +int wlan_clone_create(struct wlan_iface *); +int wlan_clone_destroy(struct wlan_iface *wif); +int wlan_config_get_dssid(struct wlan_iface *wif); +int wlan_config_set_dssid(struct wlan_iface *wif, char *ssid, int slen); +int wlan_config_get_ioctl(struct wlan_iface *wif, int which); +int wlan_config_set_ioctl(struct wlan_iface *wif, int which, int val, + char *strval, int len); +int wlan_set_scan_config(struct wlan_iface *wif); +int wlan_get_scan_results(struct wlan_iface *wif); +int wlan_get_stats(struct wlan_iface *wif); +int wlan_get_wepmode(struct wlan_iface *wif); +int wlan_set_wepmode(struct wlan_iface *wif); +int wlan_get_weptxkey(struct wlan_iface *wif); +int wlan_set_weptxkey(struct wlan_iface *wif); +int wlan_get_wepkeys(struct wlan_iface *wif); +int wlan_set_wepkeys(struct wlan_iface *wif); +int wlan_get_mac_policy(struct wlan_iface *wif); +int wlan_set_mac_policy(struct wlan_iface *wif); +int wlan_flush_mac_mac(struct wlan_iface *wif); +int wlan_get_mac_acl_macs(struct wlan_iface *wif); +int wlan_add_mac_acl_mac(struct wlan_iface *wif, struct wlan_mac_mac *mmac); +int wlan_del_mac_acl_mac(struct wlan_iface *wif, struct wlan_mac_mac *mmac); + +int32_t wlan_do_sysctl(struct wlan_config *cfg, enum wlan_syscl which, int set); +int wlan_mesh_config_get(struct wlan_iface *wif, int which); +int wlan_mesh_config_set(struct wlan_iface *wif, int which); +int wlan_mesh_flush_routes(struct wlan_iface *wif); +int wlan_mesh_add_route(struct wlan_iface *wif, struct wlan_mesh_route *wmr); +int wlan_mesh_del_route(struct wlan_iface *wif, struct wlan_mesh_route *wmr); +int wlan_mesh_get_routelist(struct wlan_iface *wif); +int wlan_hwmp_config_get(struct wlan_iface *wif, int which); +int wlan_hwmp_config_set(struct wlan_iface *wif, int which); + +/* XXX: static */ + +int wlan_peer_set_vlan(struct wlan_iface *wif, struct wlan_peer *wip, int vlan); +int wlan_get_peerinfo(struct wlan_iface *wif); + +/* XXX*/ +struct wlan_peer *wlan_new_peer(const uint8_t *pmac); +void wlan_free_peer(struct wlan_peer *wip); +int wlan_add_peer(struct wlan_iface *wif, struct wlan_peer *wip); + +struct wlan_scan_result * wlan_scan_new_result(const uint8_t *ssid, + const uint8_t *bssid); +void wlan_scan_free_result(struct wlan_scan_result *sr); +int wlan_scan_add_result(struct wlan_iface *wif, struct wlan_scan_result *sr); + +struct wlan_mac_mac *wlan_mac_new_mac(const uint8_t *mac); +void wlan_mac_free_mac(struct wlan_mac_mac *wmm); +int wlan_mac_add_mac(struct wlan_iface *wif, struct wlan_mac_mac *wmm); + +struct wlan_mesh_route *wlan_mesh_new_route(const uint8_t *dstmac); +int wlan_mesh_add_rtentry(struct wlan_iface *wif, struct wlan_mesh_route *wmr); +void wlan_mesh_free_route(struct wlan_mesh_route *wmr); diff --git a/usr.sbin/bsnmpd/modules/snmp_wlan/wlan_sys.c b/usr.sbin/bsnmpd/modules/snmp_wlan/wlan_sys.c new file mode 100644 index 0000000..739f11f --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_wlan/wlan_sys.c @@ -0,0 +1,3145 @@ +/*- + * Copyright (c) 2010 The FreeBSD Foundation + * All rights reserved. + + * This software was developed by Shteryana Sotirova Shopova under + * sponsorship from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include <sys/ioctl.h> +#include <sys/param.h> +#include <sys/module.h> +#include <sys/linker.h> +#include <sys/socket.h> +#include <sys/sysctl.h> + +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_media.h> +#include <net/if_mib.h> +#include <net/if_types.h> +#include <net80211/ieee80211.h> +#include <net80211/ieee80211_ioctl.h> +#include <net80211/ieee80211_regdomain.h> + +#include <errno.h> +#include <ifaddrs.h> +#include <stdarg.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <syslog.h> + +#include <bsnmp/snmpmod.h> +#include <bsnmp/snmp_mibII.h> + +#include "wlan_tree.h" +#include "wlan_snmp.h" + +static int sock = -1; + +static int wlan_ioctl(char *, uint16_t, int *, void *, size_t *, int); +static int wlan_kmod_load(const char *); +static uint32_t wlan_drivercaps_to_snmp(uint32_t); +static uint32_t wlan_cryptocaps_to_snmp(uint32_t); +static uint32_t wlan_htcaps_to_snmp(uint32_t); +static uint32_t wlan_peerstate_to_snmp(uint32_t); +static uint32_t wlan_peercaps_to_snmp(uint32_t ); +static uint32_t wlan_channel_flags_to_snmp_phy(uint32_t); +static uint32_t wlan_regdomain_to_snmp(int); +static uint32_t wlan_snmp_to_scan_flags(int); +static int wlan_config_snmp2ioctl(int); +static int wlan_snmp_to_regdomain(enum WlanRegDomainCode); +static int wlan_config_get_country(struct wlan_iface *); +static int wlan_config_set_country(struct wlan_iface *, char *, int); +static int wlan_config_get_dchannel(struct wlan_iface *wif); +static int wlan_config_set_dchannel(struct wlan_iface *wif, uint32_t); +static int wlan_config_get_bssid(struct wlan_iface *); +static int wlan_config_set_bssid(struct wlan_iface *, uint8_t *); +static void wlan_config_set_snmp_intval(struct wlan_iface *, int, int); +static int wlan_config_snmp2value(int, int, int *); +static int wlan_config_check(struct wlan_iface *, int); +static int wlan_config_get_intval(struct wlan_iface *, int); +static int wlan_config_set_intval(struct wlan_iface *, int, int); +static int wlan_add_new_scan_result(struct wlan_iface *, + const struct ieee80211req_scan_result *, uint8_t *); +static int wlan_add_mac_macinfo(struct wlan_iface *, + const struct ieee80211req_maclist *); +static struct wlan_peer *wlan_add_peerinfo(const struct ieee80211req_sta_info *); + +int +wlan_ioctl_init(void) +{ + if ((sock = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { + syslog(LOG_ERR, "cannot open socket : %s", strerror(errno)); + return (-1); + } + + return (0); +} +/* + * Load the needed modules in kernel if not already there. + */ +enum wlan_kmodules { + WLAN_KMOD = 0, + WLAN_KMOD_ACL, + WLAN_KMOD_WEP, + WLAN_KMODS_MAX +}; + +static const char *wmod_names[] = { + "wlan", + "wlan_wlan_acl", + "wlan_wep", + NULL +}; + +static int +wlan_kmod_load(const char *modname) +{ + int fileid, modid; + struct module_stat mstat; + + mstat.version = sizeof(struct module_stat); + for (fileid = kldnext(0); fileid > 0; fileid = kldnext(fileid)) { + for (modid = kldfirstmod(fileid); modid > 0; + modid = modfnext(modid)) { + if (modstat(modid, &mstat) < 0) + continue; + if (strcmp(modname, mstat.name) == 0) + return (0); + } + } + + /* Not present - load it. */ + if (kldload(modname) < 0) { + syslog(LOG_ERR, "failed to load %s kernel module - %s", modname, + strerror(errno)); + return (-1); + } + + return (1); +} + +int +wlan_kmodules_load(void) +{ + if (wlan_kmod_load(wmod_names[WLAN_KMOD]) < 0) + return (-1); + + if (wlan_kmod_load(wmod_names[WLAN_KMOD_ACL]) > 0) + syslog(LOG_NOTICE, "SNMP wlan loaded %s module", + wmod_names[WLAN_KMOD_ACL]); + + if (wlan_kmod_load(wmod_names[WLAN_KMOD_WEP]) > 0) + syslog(LOG_NOTICE, "SNMP wlan loaded %s module", + wmod_names[WLAN_KMOD_WEP]); + + return (0); +} + +/* XXX: FIXME */ +static int +wlan_ioctl(char *wif_name, uint16_t req_type, int *val, void *arg, + size_t *argsize, int set) +{ + struct ieee80211req ireq; + + memset(&ireq, 0, sizeof(struct ieee80211req)); + strlcpy(ireq.i_name, wif_name, IFNAMSIZ); + + ireq.i_type = req_type; + ireq.i_val = *val; + ireq.i_len = *argsize; + ireq.i_data = arg; + + if (ioctl(sock, set ? SIOCS80211 : SIOCG80211, &ireq) < 0) { + syslog(LOG_ERR, "iface %s - %s param: ioctl(%d) " + "failed: %s", wif_name, set ? "set" : "get", + req_type, strerror(errno)); + return (-1); + } + + *argsize = ireq.i_len; + *val = ireq.i_val; + + return (0); +} + +int +wlan_check_media(char *ifname) +{ + struct ifmediareq ifmr; + + memset(&ifmr, 0, sizeof(struct ifmediareq)); + strlcpy(ifmr.ifm_name, ifname, sizeof(ifmr.ifm_name)); + + if (ioctl(sock, SIOCGIFMEDIA, &ifmr) < 0 || ifmr.ifm_count == 0) + return (0); /* Interface doesn't support SIOCGIFMEDIA. */ + + if ((ifmr.ifm_status & IFM_AVALID) == 0) + return (0); + + return (IFM_TYPE(ifmr.ifm_active)); +} + +int +wlan_get_opmode(struct wlan_iface *wif) +{ + struct ifmediareq ifmr; + + memset(&ifmr, 0, sizeof(struct ifmediareq)); + strlcpy(ifmr.ifm_name, wif->wname, sizeof(ifmr.ifm_name)); + + if (ioctl(sock, SIOCGIFMEDIA, &ifmr) < 0) { + if (errno == ENXIO) + return (-1); + wif->mode = WlanIfaceOperatingModeType_station; + return (0); + } + + if (ifmr.ifm_current & IFM_IEEE80211_ADHOC) { + if (ifmr.ifm_current & IFM_FLAG0) + wif->mode = WlanIfaceOperatingModeType_adhocDemo; + else + wif->mode = WlanIfaceOperatingModeType_ibss; + } else if (ifmr.ifm_current & IFM_IEEE80211_HOSTAP) + wif->mode = WlanIfaceOperatingModeType_hostAp; + else if (ifmr.ifm_current & IFM_IEEE80211_MONITOR) + wif->mode = WlanIfaceOperatingModeType_monitor; + else if (ifmr.ifm_current & IFM_IEEE80211_MBSS) + wif->mode = WlanIfaceOperatingModeType_meshPoint; + else if (ifmr.ifm_current & IFM_IEEE80211_WDS) + wif->mode = WlanIfaceOperatingModeType_wds; + + return (0); +} + +int +wlan_config_state(struct wlan_iface *wif, uint8_t set) +{ + int flags; + struct ifreq ifr; + + memset(&ifr, 0, sizeof(ifr)); + strcpy(ifr.ifr_name, wif->wname); + + if (ioctl(sock, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) { + syslog(LOG_ERR, "set %s status: ioctl(SIOCGIFFLAGS) " + "failed: %s", wif->wname, strerror(errno)); + return (-1); + } + + if (set == 0) { + if ((ifr.ifr_flags & IFF_UP) != 0) + wif->state = wlanIfaceState_up; + else + wif->state = wlanIfaceState_down; + return (0); + } + + flags = (ifr.ifr_flags & 0xffff) | (ifr.ifr_flagshigh << 16); + + if (wif->state == wlanIfaceState_up) + flags |= IFF_UP; + else + flags &= ~IFF_UP; + + ifr.ifr_flags = flags & 0xffff; + ifr.ifr_flagshigh = flags >> 16; + if (ioctl(sock, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) { + syslog(LOG_ERR, "set %s %s: ioctl(SIOCSIFFLAGS) failed: %s", + wif->wname, wif->state == wlanIfaceState_up?"up":"down", + strerror(errno)); + return (-1); + } + + return (0); +} + +int +wlan_get_local_addr(struct wlan_iface *wif) +{ + int len; + char ifname[IFNAMSIZ]; + struct ifaddrs *ifap, *ifa; + struct sockaddr_dl sdl; + + if (getifaddrs(&ifap) != 0) { + syslog(LOG_ERR, "wlan get mac: getifaddrs() failed - %s", + strerror(errno)); + return (-1); + } + + for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) { + if (ifa->ifa_addr->sa_family != AF_LINK) + continue; + memcpy(&sdl, ifa->ifa_addr, sizeof(struct sockaddr_dl)); + if (sdl.sdl_alen > IEEE80211_ADDR_LEN) + continue; + if ((len = sdl.sdl_nlen) >= IFNAMSIZ) + len = IFNAMSIZ - 1; + memcpy(ifname, sdl.sdl_data, len); + ifname[len] = '\0'; + if (strcmp(wif->wname, ifname) == 0) + break; + } + + freeifaddrs(ifap); + return (0); +} + +int +wlan_get_parent(struct wlan_iface *wif __unused) +{ + /* XXX: There's no way to fetch this from the kernel. */ + return (0); +} + +/* XXX */ +#define IEEE80211_C_STA 0x00000001 /* CAPABILITY: STA available */ +#define IEEE80211_C_8023ENCAP 0x00000002 /* CAPABILITY: 802.3 encap */ +#define IEEE80211_C_FF 0x00000040 /* CAPABILITY: ATH FF avail */ +#define IEEE80211_C_TURBOP 0x00000080 /* CAPABILITY: ATH Turbo avail*/ +#define IEEE80211_C_IBSS 0x00000100 /* CAPABILITY: IBSS available */ +#define IEEE80211_C_PMGT 0x00000200 /* CAPABILITY: Power mgmt */ +#define IEEE80211_C_HOSTAP 0x00000400 /* CAPABILITY: HOSTAP avail */ +#define IEEE80211_C_AHDEMO 0x00000800 /* CAPABILITY: Old Adhoc Demo */ +#define IEEE80211_C_SWRETRY 0x00001000 /* CAPABILITY: sw tx retry */ +#define IEEE80211_C_TXPMGT 0x00002000 /* CAPABILITY: tx power mgmt */ +#define IEEE80211_C_SHSLOT 0x00004000 /* CAPABILITY: short slottime */ +#define IEEE80211_C_SHPREAMBLE 0x00008000 /* CAPABILITY: short preamble */ +#define IEEE80211_C_MONITOR 0x00010000 /* CAPABILITY: monitor mode */ +#define IEEE80211_C_DFS 0x00020000 /* CAPABILITY: DFS/radar avail*/ +#define IEEE80211_C_MBSS 0x00040000 /* CAPABILITY: MBSS available */ +/* 0x7c0000 available */ +#define IEEE80211_C_WPA1 0x00800000 /* CAPABILITY: WPA1 avail */ +#define IEEE80211_C_WPA2 0x01000000 /* CAPABILITY: WPA2 avail */ +#define IEEE80211_C_WPA 0x01800000 /* CAPABILITY: WPA1+WPA2 avail*/ +#define IEEE80211_C_BURST 0x02000000 /* CAPABILITY: frame bursting */ +#define IEEE80211_C_WME 0x04000000 /* CAPABILITY: WME avail */ +#define IEEE80211_C_WDS 0x08000000 /* CAPABILITY: 4-addr support */ +/* 0x10000000 reserved */ +#define IEEE80211_C_BGSCAN 0x20000000 /* CAPABILITY: bg scanning */ +#define IEEE80211_C_TXFRAG 0x40000000 /* CAPABILITY: tx fragments */ +#define IEEE80211_C_TDMA 0x80000000 /* CAPABILITY: TDMA avail */ + +static uint32_t +wlan_drivercaps_to_snmp(uint32_t dcaps) +{ + uint32_t scaps = 0; + + if ((dcaps & IEEE80211_C_STA) != 0) + scaps |= (0x1 << WlanDriverCaps_station); + if ((dcaps & IEEE80211_C_8023ENCAP) != 0) + scaps |= (0x1 << WlanDriverCaps_ieee8023encap); + if ((dcaps & IEEE80211_C_FF) != 0) + scaps |= (0x1 << WlanDriverCaps_athFastFrames); + if ((dcaps & IEEE80211_C_TURBOP) != 0) + scaps |= (0x1 << WlanDriverCaps_athTurbo); + if ((dcaps & IEEE80211_C_IBSS) != 0) + scaps |= (0x1 << WlanDriverCaps_ibss); + if ((dcaps & IEEE80211_C_PMGT) != 0) + scaps |= (0x1 << WlanDriverCaps_pmgt); + if ((dcaps & IEEE80211_C_HOSTAP) != 0) + scaps |= (0x1 << WlanDriverCaps_hostAp); + if ((dcaps & IEEE80211_C_AHDEMO) != 0) + scaps |= (0x1 << WlanDriverCaps_ahDemo); + if ((dcaps & IEEE80211_C_SWRETRY) != 0) + scaps |= (0x1 << WlanDriverCaps_swRetry); + if ((dcaps & IEEE80211_C_TXPMGT) != 0) + scaps |= (0x1 << WlanDriverCaps_txPmgt); + if ((dcaps & IEEE80211_C_SHSLOT) != 0) + scaps |= (0x1 << WlanDriverCaps_shortSlot); + if ((dcaps & IEEE80211_C_SHPREAMBLE) != 0) + scaps |= (0x1 << WlanDriverCaps_shortPreamble); + if ((dcaps & IEEE80211_C_MONITOR) != 0) + scaps |= (0x1 << WlanDriverCaps_monitor); + if ((dcaps & IEEE80211_C_DFS) != 0) + scaps |= (0x1 << WlanDriverCaps_dfs); + if ((dcaps & IEEE80211_C_MBSS) != 0) + scaps |= (0x1 << WlanDriverCaps_mbss); + if ((dcaps & IEEE80211_C_WPA1) != 0) + scaps |= (0x1 << WlanDriverCaps_wpa1); + if ((dcaps & IEEE80211_C_WPA2) != 0) + scaps |= (0x1 << WlanDriverCaps_wpa2); + if ((dcaps & IEEE80211_C_BURST) != 0) + scaps |= (0x1 << WlanDriverCaps_burst); + if ((dcaps & IEEE80211_C_WME) != 0) + scaps |= (0x1 << WlanDriverCaps_wme); + if ((dcaps & IEEE80211_C_WDS) != 0) + scaps |= (0x1 << WlanDriverCaps_wds); + if ((dcaps & IEEE80211_C_BGSCAN) != 0) + scaps |= (0x1 << WlanDriverCaps_bgScan); + if ((dcaps & IEEE80211_C_TXFRAG) != 0) + scaps |= (0x1 << WlanDriverCaps_txFrag); + if ((dcaps & IEEE80211_C_TDMA) != 0) + scaps |= (0x1 << WlanDriverCaps_tdma); + + return (scaps); +} + +static uint32_t +wlan_cryptocaps_to_snmp(uint32_t ccaps) +{ + uint32_t scaps = 0; + +#if NOT_YET + if ((ccaps & IEEE80211_CRYPTO_WEP) != 0) + scaps |= (0x1 << wlanCryptoCaps_wep); + if ((ccaps & IEEE80211_CRYPTO_TKIP) != 0) + scaps |= (0x1 << wlanCryptoCaps_tkip); + if ((ccaps & IEEE80211_CRYPTO_AES_OCB) != 0) + scaps |= (0x1 << wlanCryptoCaps_aes); + if ((ccaps & IEEE80211_CRYPTO_AES_CCM) != 0) + scaps |= (0x1 << wlanCryptoCaps_aesCcm); + if ((ccaps & IEEE80211_CRYPTO_TKIPMIC) != 0) + scaps |= (0x1 << wlanCryptoCaps_tkipMic); + if ((ccaps & IEEE80211_CRYPTO_CKIP) != 0) + scaps |= (0x1 << wlanCryptoCaps_ckip); +#else /* !NOT_YET */ + scaps = ccaps; +#endif + return (scaps); +} + +#define IEEE80211_HTC_AMPDU 0x00010000 /* CAPABILITY: A-MPDU tx */ +#define IEEE80211_HTC_AMSDU 0x00020000 /* CAPABILITY: A-MSDU tx */ +/* NB: HT40 is implied by IEEE80211_HTCAP_CHWIDTH40 */ +#define IEEE80211_HTC_HT 0x00040000 /* CAPABILITY: HT operation */ +#define IEEE80211_HTC_SMPS 0x00080000 /* CAPABILITY: MIMO power save*/ +#define IEEE80211_HTC_RIFS 0x00100000 /* CAPABILITY: RIFS support */ + +static uint32_t +wlan_htcaps_to_snmp(uint32_t hcaps) +{ + uint32_t scaps = 0; + + if ((hcaps & IEEE80211_HTCAP_LDPC) != 0) + scaps |= (0x1 << WlanHTCaps_ldpc); + if ((hcaps & IEEE80211_HTCAP_CHWIDTH40) != 0) + scaps |= (0x1 << WlanHTCaps_chwidth40); + if ((hcaps & IEEE80211_HTCAP_GREENFIELD) != 0) + scaps |= (0x1 << WlanHTCaps_greenField); + if ((hcaps & IEEE80211_HTCAP_SHORTGI20) != 0) + scaps |= (0x1 << WlanHTCaps_shortGi20); + if ((hcaps & IEEE80211_HTCAP_SHORTGI40) != 0) + scaps |= (0x1 << WlanHTCaps_shortGi40); + if ((hcaps & IEEE80211_HTCAP_TXSTBC) != 0) + scaps |= (0x1 << WlanHTCaps_txStbc); + if ((hcaps & IEEE80211_HTCAP_DELBA) != 0) + scaps |= (0x1 << WlanHTCaps_delba); + if ((hcaps & IEEE80211_HTCAP_MAXAMSDU_7935) != 0) + scaps |= (0x1 << WlanHTCaps_amsdu7935); + if ((hcaps & IEEE80211_HTCAP_DSSSCCK40) != 0) + scaps |= (0x1 << WlanHTCaps_dssscck40); + if ((hcaps & IEEE80211_HTCAP_PSMP) != 0) + scaps |= (0x1 << WlanHTCaps_psmp); + if ((hcaps & IEEE80211_HTCAP_40INTOLERANT) != 0) + scaps |= (0x1 << WlanHTCaps_fortyMHzIntolerant); + if ((hcaps & IEEE80211_HTCAP_LSIGTXOPPROT) != 0) + scaps |= (0x1 << WlanHTCaps_lsigTxOpProt); + if ((hcaps & IEEE80211_HTC_AMPDU) != 0) + scaps |= (0x1 << WlanHTCaps_htcAmpdu); + if ((hcaps & IEEE80211_HTC_AMSDU) != 0) + scaps |= (0x1 << WlanHTCaps_htcAmsdu); + if ((hcaps & IEEE80211_HTC_HT) != 0) + scaps |= (0x1 << WlanHTCaps_htcHt); + if ((hcaps & IEEE80211_HTC_SMPS) != 0) + scaps |= (0x1 << WlanHTCaps_htcSmps); + if ((hcaps & IEEE80211_HTC_RIFS) != 0) + scaps |= (0x1 << WlanHTCaps_htcRifs); + + return (scaps); +} + +/* XXX: Not here? */ +#define WLAN_SET_TDMA_OPMODE(w) do { \ + if ((w)->mode == WlanIfaceOperatingModeType_adhocDemo && \ + ((w)->drivercaps & WlanDriverCaps_tdma) != 0) \ + (w)->mode = WlanIfaceOperatingModeType_tdma; \ +} while (0) +int +wlan_get_driver_caps(struct wlan_iface *wif) +{ + int val = 0; + size_t argsize; + struct ieee80211_devcaps_req dc; + + memset(&dc, 0, sizeof(struct ieee80211_devcaps_req)); + argsize = sizeof(struct ieee80211_devcaps_req); + + if (wlan_ioctl(wif->wname, IEEE80211_IOC_DEVCAPS, &val, &dc, + &argsize, 0) < 0) + return (-1); + + wif->drivercaps = wlan_drivercaps_to_snmp(dc.dc_drivercaps); + wif->cryptocaps = wlan_cryptocaps_to_snmp(dc.dc_cryptocaps); + wif->htcaps = wlan_htcaps_to_snmp(dc.dc_htcaps); + + WLAN_SET_TDMA_OPMODE(wif); + + argsize = dc.dc_chaninfo.ic_nchans * sizeof(struct ieee80211_channel); + wif->chanlist = (struct ieee80211_channel *)malloc(argsize); + if (wif->chanlist == NULL) + return (0); + + memcpy(wif->chanlist, dc.dc_chaninfo.ic_chans, argsize); + wif->nchannels = dc.dc_chaninfo.ic_nchans; + + return (0); +} + +uint8_t +wlan_channel_state_to_snmp(uint8_t cstate) +{ + uint8_t cs = 0; + + if ((cstate & IEEE80211_CHANSTATE_RADAR) != 0) + cs |= (0x1 << WlanIfaceChannelStateType_radar); + if ((cstate & IEEE80211_CHANSTATE_CACDONE) != 0) + cs |= (0x1 << WlanIfaceChannelStateType_cacDone); + if ((cstate & IEEE80211_CHANSTATE_CWINT) != 0) + cs |= (0x1 << WlanIfaceChannelStateType_interferenceDetected); + if ((cstate & IEEE80211_CHANSTATE_NORADAR) != 0) + cs |= (0x1 << WlanIfaceChannelStateType_radarClear); + + return (cs); +} + +uint32_t +wlan_channel_flags_to_snmp(uint32_t cflags) +{ + uint32_t cf = 0; + + if ((cflags & IEEE80211_CHAN_TURBO) != 0) + cf |= (0x1 << WlanIfaceChannelFlagsType_turbo); + if ((cflags & IEEE80211_CHAN_CCK) != 0) + cf |= (0x1 << WlanIfaceChannelFlagsType_cck); + if ((cflags & IEEE80211_CHAN_OFDM) != 0) + cf |= (0x1 << WlanIfaceChannelFlagsType_ofdm); + if ((cflags & IEEE80211_CHAN_2GHZ) != 0) + cf |= (0x1 << WlanIfaceChannelFlagsType_spectrum2Ghz); + if ((cflags & IEEE80211_CHAN_5GHZ) != 0) + cf |= (0x1 << WlanIfaceChannelFlagsType_spectrum5Ghz); + if ((cflags & IEEE80211_CHAN_PASSIVE) != 0) + cf |= (0x1 << WlanIfaceChannelFlagsType_passiveScan); + if ((cflags & IEEE80211_CHAN_DYN) != 0) + cf |= (0x1 << WlanIfaceChannelFlagsType_dynamicCckOfdm); + if ((cflags & IEEE80211_CHAN_GFSK) != 0) + cf |= (0x1 << WlanIfaceChannelFlagsType_gfsk); + if ((cflags & IEEE80211_CHAN_GSM) != 0) + cf |= (0x1 << WlanIfaceChannelFlagsType_spectrum900Mhz); + if ((cflags & IEEE80211_CHAN_STURBO) != 0) + cf |= (0x1 << WlanIfaceChannelFlagsType_dot11aStaticTurbo); + if ((cflags & IEEE80211_CHAN_HALF) != 0) + cf |= (0x1 << WlanIfaceChannelFlagsType_halfRate); + if ((cflags & IEEE80211_CHAN_QUARTER) != 0) + cf |= (0x1 << WlanIfaceChannelFlagsType_quarterRate); + if ((cflags & IEEE80211_CHAN_HT20) != 0) + cf |= (0x1 << WlanIfaceChannelFlagsType_ht20); + if ((cflags & IEEE80211_CHAN_HT40U) != 0) + cf |= (0x1 << WlanIfaceChannelFlagsType_ht40u); + if ((cflags & IEEE80211_CHAN_HT40D) != 0) + cf |= (0x1 << WlanIfaceChannelFlagsType_ht40d); + if ((cflags & IEEE80211_CHAN_DFS) != 0) + cf |= (0x1 << WlanIfaceChannelFlagsType_dfs); + if ((cflags & IEEE80211_CHAN_4MSXMIT) != 0) + cf |= (0x1 << WlanIfaceChannelFlagsType_xmit4ms); + if ((cflags & IEEE80211_CHAN_NOADHOC) != 0) + cf |= (0x1 << WlanIfaceChannelFlagsType_noAdhoc); + if ((cflags & IEEE80211_CHAN_NOHOSTAP) != 0) + cf |= (0x1 << WlanIfaceChannelFlagsType_noHostAp); + if ((cflags & IEEE80211_CHAN_11D) != 0) + cf |= (0x1 << WlanIfaceChannelFlagsType_dot11d); + + return (cf); +} + +/* XXX: */ +#define WLAN_SNMP_MAX_CHANS 256 +int +wlan_get_channel_list(struct wlan_iface *wif) +{ + int val = 0; + uint32_t i, nchans; + size_t argsize; + struct ieee80211req_chaninfo *chaninfo; + struct ieee80211req_chanlist active; + const struct ieee80211_channel *c; + + argsize = sizeof(struct ieee80211req_chaninfo) + + sizeof(struct ieee80211_channel) * WLAN_SNMP_MAX_CHANS; + chaninfo = (struct ieee80211req_chaninfo *)malloc(argsize); + if (chaninfo == NULL) + return (-1); + + if (wlan_ioctl(wif->wname, IEEE80211_IOC_CHANINFO, &val, chaninfo, + &argsize, 0) < 0) + return (-1); + + argsize = sizeof(active); + if (wlan_ioctl(wif->wname, IEEE80211_IOC_CHANLIST, &val, &active, + &argsize, 0) < 0) + goto error; + + for (i = 0, nchans = 0; i < chaninfo->ic_nchans; i++) { + c = &chaninfo->ic_chans[i]; + if (!isset(active.ic_channels, c->ic_ieee)) + continue; + nchans++; + } + wif->chanlist = (struct ieee80211_channel *)reallocf(wif->chanlist, + nchans * sizeof(*c)); + if (wif->chanlist == NULL) + goto error; + wif->nchannels = nchans; + for (i = 0, nchans = 0; i < chaninfo->ic_nchans; i++) { + c = &chaninfo->ic_chans[i]; + if (!isset(active.ic_channels, c->ic_ieee)) + continue; + memcpy(wif->chanlist + nchans, c, sizeof (*c)); + nchans++; + } + + free(chaninfo); + return (0); +error: + wif->nchannels = 0; + free(chaninfo); + return (-1); +} + +static enum WlanIfPhyMode +wlan_channel_flags_to_snmp_phy(uint32_t cflags) +{ + /* XXX: recheck */ + if ((cflags & IEEE80211_CHAN_A) != 0) + return (WlanIfPhyMode_dot11a); + if ((cflags & IEEE80211_CHAN_B) != 0) + return (WlanIfPhyMode_dot11b); + if ((cflags & IEEE80211_CHAN_G) != 0 || + (cflags & IEEE80211_CHAN_PUREG) != 0) + return (WlanIfPhyMode_dot11g); + if ((cflags & IEEE80211_CHAN_FHSS) != 0) + return (WlanIfPhyMode_fh); + if ((cflags & IEEE80211_CHAN_TURBO) != 0 && + (cflags & IEEE80211_CHAN_A) != 0) + return (WlanIfPhyMode_turboA); + if ((cflags & IEEE80211_CHAN_TURBO) != 0 && + (cflags & IEEE80211_CHAN_G) != 0) + return (WlanIfPhyMode_turboG); + if ((cflags & IEEE80211_CHAN_STURBO) != 0) + return (WlanIfPhyMode_sturboA); + if ((cflags & IEEE80211_CHAN_HALF) != 0) + return (WlanIfPhyMode_ofdmHalf); + if ((cflags & IEEE80211_CHAN_QUARTER) != 0) + return (WlanIfPhyMode_ofdmQuarter); + + return (WlanIfPhyMode_auto); +} + +int +wlan_get_roam_params(struct wlan_iface *wif) +{ + int val = 0; + size_t argsize; + + argsize = sizeof(struct ieee80211_roamparams_req); + if (wlan_ioctl(wif->wname, IEEE80211_IOC_ROAM, &val, + &wif->roamparams, &argsize, 0) < 0) + return (-1); + + return (0); +} + +int +wlan_get_tx_params(struct wlan_iface *wif) +{ + int val = 0; + size_t argsize; + + /* + * XXX: Reset IEEE80211_RATE_MCS bit on IEEE80211_MODE_11NA + * and IEEE80211_MODE_11NG modes. + */ + argsize = sizeof(struct ieee80211_txparams_req); + if (wlan_ioctl(wif->wname, IEEE80211_IOC_TXPARAMS, &val, + &wif->txparams, &argsize, 0) < 0) + return (-1); + + return (0); +} + +int +wlan_set_tx_params(struct wlan_iface *wif, int32_t pmode __unused) +{ + int val = 0; + size_t argsize; + + /* + * XXX: Set IEEE80211_RATE_MCS bit on IEEE80211_MODE_11NA + * and IEEE80211_MODE_11NG modes. + */ + argsize = sizeof(struct ieee80211_txparams_req); + if (wlan_ioctl(wif->wname, IEEE80211_IOC_TXPARAMS, &val, + &wif->txparams, &argsize, 1) < 0) + return (-1); + + return (0); +} + +int +wlan_clone_create(struct wlan_iface *wif) +{ + struct ifreq ifr; + struct ieee80211_clone_params wcp; + static const uint8_t zerobssid[IEEE80211_ADDR_LEN]; + + memset(&wcp, 0, sizeof(wcp)); + memset(&ifr, 0, sizeof(ifr)); + + /* Sanity checks. */ + if (wif == NULL || wif->pname[0] == '\0' || wif->mode > WLAN_IFMODE_MAX) + return (SNMP_ERR_INCONS_VALUE); + + if (wif->mode == WlanIfaceOperatingModeType_wds && + memcmp(wif->dbssid, zerobssid, IEEE80211_ADDR_LEN) == 0) + return (SNMP_ERR_INCONS_VALUE); + + strlcpy(wcp.icp_parent, wif->pname, IFNAMSIZ); + if ((wif->flags & WlanIfaceFlagsType_uniqueBssid) != 0) + wcp.icp_flags |= IEEE80211_CLONE_BSSID; + if ((wif->flags & WlanIfaceFlagsType_noBeacons) != 0) + wcp.icp_flags |= IEEE80211_CLONE_NOBEACONS; + if (wif->mode == WlanIfaceOperatingModeType_wds && + (wif->flags & WlanIfaceFlagsType_wdsLegacy) != 0) + wcp.icp_flags |= IEEE80211_CLONE_WDSLEGACY; + + switch (wif->mode) { + case WlanIfaceOperatingModeType_ibss: + wcp.icp_opmode = IEEE80211_M_IBSS; + break; + case WlanIfaceOperatingModeType_station: + wcp.icp_opmode = IEEE80211_M_STA; + break; + case WlanIfaceOperatingModeType_wds: + wcp.icp_opmode = IEEE80211_M_WDS; + break; + case WlanIfaceOperatingModeType_adhocDemo: + wcp.icp_opmode = IEEE80211_M_AHDEMO; + break; + case WlanIfaceOperatingModeType_hostAp: + wcp.icp_opmode = IEEE80211_M_HOSTAP; + break; + case WlanIfaceOperatingModeType_monitor: + wcp.icp_opmode = IEEE80211_M_MONITOR; + break; + case WlanIfaceOperatingModeType_meshPoint: + wcp.icp_opmode = IEEE80211_M_MBSS; + break; + case WlanIfaceOperatingModeType_tdma: + wcp.icp_opmode = IEEE80211_M_AHDEMO; + wcp.icp_flags |= IEEE80211_CLONE_TDMA; + break; + } + + memcpy(wcp.icp_bssid, wif->dbssid, IEEE80211_ADDR_LEN); + if (memcmp(wif->dlmac, zerobssid, IEEE80211_ADDR_LEN) != 0) { + memcpy(wcp.icp_macaddr, wif->dlmac, IEEE80211_ADDR_LEN); + wcp.icp_flags |= IEEE80211_CLONE_MACADDR; + } + + strlcpy(ifr.ifr_name, wif->wname, IFNAMSIZ); + ifr.ifr_data = (caddr_t) &wcp; + + if (ioctl(sock, SIOCIFCREATE2, (caddr_t) &ifr) < 0) { + syslog(LOG_ERR, "wlan clone create: ioctl(SIOCIFCREATE2) " + "failed: %s", strerror(errno)); + return (SNMP_ERR_GENERR); + } + + return (SNMP_ERR_NOERROR); +} + +int +wlan_clone_destroy(struct wlan_iface *wif) +{ + struct ifreq ifr; + + if (wif == NULL) + return (SNMP_ERR_INCONS_VALUE); + + memset(&ifr, 0, sizeof(ifr)); + strcpy(ifr.ifr_name, wif->wname); + + if (ioctl(sock, SIOCIFDESTROY, &ifr) < 0) { + syslog(LOG_ERR, "wlan clone destroy: ioctl(SIOCIFDESTROY) " + "failed: %s", strerror(errno)); + return (SNMP_ERR_GENERR); + } + + return (SNMP_ERR_NOERROR); +} + +static int +wlan_config_snmp2ioctl(int which) +{ + int op; + + switch (which) { + case LEAF_wlanIfacePacketBurst: + op = IEEE80211_IOC_BURST; + break; + case LEAF_wlanIfaceCountryCode: + op = IEEE80211_IOC_REGDOMAIN; + break; + case LEAF_wlanIfaceRegDomain: + op = IEEE80211_IOC_REGDOMAIN; + break; + case LEAF_wlanIfaceDesiredSsid: + op = IEEE80211_IOC_SSID; + break; + case LEAF_wlanIfaceDesiredChannel: + op = IEEE80211_IOC_CURCHAN; + break; + case LEAF_wlanIfaceDynamicFreqSelection: + op = IEEE80211_IOC_DFS; + break; + case LEAF_wlanIfaceFastFrames: + op = IEEE80211_IOC_FF; + break; + case LEAF_wlanIfaceDturbo: + op = IEEE80211_IOC_TURBOP; + break; + case LEAF_wlanIfaceTxPower: + op = IEEE80211_IOC_TXPOWER; + break; + case LEAF_wlanIfaceFragmentThreshold: + op = IEEE80211_IOC_FRAGTHRESHOLD; + break; + case LEAF_wlanIfaceRTSThreshold: + op = IEEE80211_IOC_RTSTHRESHOLD; + break; + case LEAF_wlanIfaceWlanPrivacySubscribe: + op = IEEE80211_IOC_WPS; + break; + case LEAF_wlanIfaceBgScan: + op = IEEE80211_IOC_BGSCAN; + break; + case LEAF_wlanIfaceBgScanIdle: + op = IEEE80211_IOC_BGSCAN_IDLE; + break; + case LEAF_wlanIfaceBgScanInterval: + op = IEEE80211_IOC_BGSCAN_INTERVAL; + break; + case LEAF_wlanIfaceBeaconMissedThreshold: + op = IEEE80211_IOC_BMISSTHRESHOLD; + break; + case LEAF_wlanIfaceDesiredBssid: + op = IEEE80211_IOC_BSSID; + break; + case LEAF_wlanIfaceRoamingMode: + op = IEEE80211_IOC_ROAMING; + break; + case LEAF_wlanIfaceDot11d: + op = IEEE80211_IOC_DOTD; + break; + case LEAF_wlanIfaceDot11h: + op = IEEE80211_IOC_DOTH; + break; + case LEAF_wlanIfaceDynamicWds: + op = IEEE80211_IOC_DWDS; + break; + case LEAF_wlanIfacePowerSave: + op = IEEE80211_IOC_POWERSAVE; + break; + case LEAF_wlanIfaceApBridge: + op = IEEE80211_IOC_APBRIDGE; + break; + case LEAF_wlanIfaceBeaconInterval: + op = IEEE80211_IOC_BEACON_INTERVAL; + break; + case LEAF_wlanIfaceDtimPeriod: + op = IEEE80211_IOC_DTIM_PERIOD; + break; + case LEAF_wlanIfaceHideSsid: + op = IEEE80211_IOC_HIDESSID; + break; + case LEAF_wlanIfaceInactivityProccess: + op = IEEE80211_IOC_INACTIVITY; + break; + case LEAF_wlanIfaceDot11gProtMode: + op = IEEE80211_IOC_PROTMODE; + break; + case LEAF_wlanIfaceDot11gPureMode: + op = IEEE80211_IOC_PUREG; + break; + case LEAF_wlanIfaceDot11nPureMode: + op = IEEE80211_IOC_PUREN; + break; + case LEAF_wlanIfaceDot11nAmpdu: + op = IEEE80211_IOC_AMPDU; + break; + case LEAF_wlanIfaceDot11nAmpduDensity: + op = IEEE80211_IOC_AMPDU_DENSITY; + break; + case LEAF_wlanIfaceDot11nAmpduLimit: + op = IEEE80211_IOC_AMPDU_LIMIT; + break; + case LEAF_wlanIfaceDot11nAmsdu: + op = IEEE80211_IOC_AMSDU; + break; + case LEAF_wlanIfaceDot11nAmsduLimit: + op = IEEE80211_IOC_AMSDU_LIMIT; + break; + case LEAF_wlanIfaceDot11nHighThroughput: + op = IEEE80211_IOC_HTCONF; + break; + case LEAF_wlanIfaceDot11nHTCompatible: + op = IEEE80211_IOC_HTCOMPAT; + break; + case LEAF_wlanIfaceDot11nHTProtMode: + op = IEEE80211_IOC_HTPROTMODE; + break; + case LEAF_wlanIfaceDot11nRIFS: + op = IEEE80211_IOC_RIFS; + break; + case LEAF_wlanIfaceDot11nShortGI: + op = IEEE80211_IOC_SHORTGI; + break; + case LEAF_wlanIfaceDot11nSMPSMode: + op = IEEE80211_IOC_SMPS; + break; + case LEAF_wlanIfaceTdmaSlot: + op = IEEE80211_IOC_TDMA_SLOT; + break; + case LEAF_wlanIfaceTdmaSlotCount: + op = IEEE80211_IOC_TDMA_SLOTCNT; + break; + case LEAF_wlanIfaceTdmaSlotLength: + op = IEEE80211_IOC_TDMA_SLOTLEN; + break; + case LEAF_wlanIfaceTdmaBeaconInterval: + op = IEEE80211_IOC_TDMA_BINTERVAL; + break; + default: + op = -1; + } + + return (op); +} + +static enum WlanRegDomainCode +wlan_regdomain_to_snmp(int which) +{ + enum WlanRegDomainCode reg_domain; + + switch (which) { + case SKU_FCC: + reg_domain = WlanRegDomainCode_fcc; + break; + case SKU_CA: + reg_domain = WlanRegDomainCode_ca; + break; + case SKU_ETSI: + reg_domain = WlanRegDomainCode_etsi; + break; + case SKU_ETSI2: + reg_domain = WlanRegDomainCode_etsi2; + break; + case SKU_ETSI3: + reg_domain = WlanRegDomainCode_etsi3; + break; + case SKU_FCC3: + reg_domain = WlanRegDomainCode_fcc3; + break; + case SKU_JAPAN: + reg_domain = WlanRegDomainCode_japan; + break; + case SKU_KOREA: + reg_domain = WlanRegDomainCode_korea; + break; + case SKU_APAC: + reg_domain = WlanRegDomainCode_apac; + break; + case SKU_APAC2: + reg_domain = WlanRegDomainCode_apac2; + break; + case SKU_APAC3: + reg_domain = WlanRegDomainCode_apac3; + break; + case SKU_ROW: + reg_domain = WlanRegDomainCode_row; + break; + case SKU_NONE: + reg_domain = WlanRegDomainCode_none; + break; + case SKU_DEBUG: + reg_domain = WlanRegDomainCode_debug; + break; + case SKU_SR9: + reg_domain = WlanRegDomainCode_sr9; + break; + case SKU_XR9: + reg_domain = WlanRegDomainCode_xr9; + break; + case SKU_GZ901: + reg_domain = WlanRegDomainCode_gz901; + break; + case 0: + reg_domain = WlanRegDomainCode_none; + break; + default: + syslog(LOG_ERR, "unknown regdomain (0x%x) ", which); + reg_domain = WlanRegDomainCode_none; + break; + } + + return (reg_domain); +} + +static int +wlan_snmp_to_regdomain(enum WlanRegDomainCode regdomain) +{ + int which; + + switch (regdomain) { + case WlanRegDomainCode_fcc: + which = SKU_FCC; + break; + case WlanRegDomainCode_ca: + which = SKU_CA; + break; + case WlanRegDomainCode_etsi: + which = SKU_ETSI; + break; + case WlanRegDomainCode_etsi2: + which = SKU_ETSI2; + break; + case WlanRegDomainCode_etsi3: + which = SKU_ETSI3; + break; + case WlanRegDomainCode_fcc3: + which = SKU_FCC3; + break; + case WlanRegDomainCode_japan: + which = SKU_JAPAN; + break; + case WlanRegDomainCode_korea: + which = SKU_KOREA; + break; + case WlanRegDomainCode_apac: + which = SKU_APAC; + break; + case WlanRegDomainCode_apac2: + which = SKU_APAC2; + break; + case WlanRegDomainCode_apac3: + which = SKU_APAC3; + break; + case WlanRegDomainCode_row: + which = SKU_ROW; + break; + case WlanRegDomainCode_none: + which = SKU_NONE; + break; + case WlanRegDomainCode_debug: + which = SKU_DEBUG; + break; + case WlanRegDomainCode_sr9: + which = SKU_SR9; + break; + case WlanRegDomainCode_xr9: + which = SKU_XR9; + break; + case WlanRegDomainCode_gz901: + which = SKU_GZ901; + break; + default: + syslog(LOG_ERR, "unknown snmp regdomain (0x%x) ", regdomain); + which = SKU_NONE; + break; + } + + return (which); +} + +static int +wlan_config_get_country(struct wlan_iface *wif) +{ + int val = 0; + size_t argsize; + struct ieee80211_regdomain regdomain; + + memset(®domain, 0, sizeof(regdomain)); + argsize = sizeof(regdomain); + + if (wlan_ioctl(wif->wname, IEEE80211_IOC_REGDOMAIN, &val, ®domain, + &argsize, 0) < 0) + return (-1); + + wif->reg_domain = wlan_regdomain_to_snmp(regdomain.regdomain); + wif->country_code[0] = regdomain.isocc[0]; + wif->country_code[1] = regdomain.isocc[1]; + wif->country_code[2] = regdomain.location; + + return (0); +} + +static int +wlan_config_set_country(struct wlan_iface *wif, char *ccode, int rdomain) +{ + int val = 0, txpowermax; + uint32_t i; + size_t argsize = 0; + struct ieee80211_regdomain_req *regdomain; + + if (wlan_get_channel_list(wif) < 0) + return (-1); + + if (wif->nchannels == 0) { + syslog(LOG_ERR, "iface %s - set regdomain failed", wif->wname); + return (-1); + } + + if (wlan_ioctl(wif->wname, IEEE80211_IOC_TXPOWMAX, &txpowermax, 0, + &argsize, 0) < 0) + return (-1); + + regdomain = malloc(IEEE80211_REGDOMAIN_SIZE(wif->nchannels)); + if (regdomain == NULL) + return (-1); + memset(regdomain, 0, IEEE80211_REGDOMAIN_SIZE(wif->nchannels)); + argsize = IEEE80211_REGDOMAIN_SIZE(wif->nchannels); + + /* XXX: recheck with how this is done by ifconfig(8) */ + regdomain->rd.regdomain = wlan_snmp_to_regdomain(rdomain); + regdomain->rd.isocc[0] = ccode[0]; + regdomain->rd.isocc[1] = ccode[1]; + regdomain->rd.location = ccode[2]; + + /* XXX: fill the channel list properly */ + regdomain->chaninfo.ic_nchans = wif->nchannels; + memcpy(regdomain->chaninfo.ic_chans, wif->chanlist, + wif->nchannels * sizeof(struct ieee80211_channel)); + for (i = 0; i < wif->nchannels; i++) + regdomain->chaninfo.ic_chans[i].ic_maxregpower = txpowermax; + + wif->state = wlanIfaceState_down; + if (wlan_config_state(wif, 1) < 0 || + wlan_ioctl(wif->wname, IEEE80211_IOC_REGDOMAIN, &val, regdomain, + &argsize, 1) < 0) { + free(regdomain); + return (-1); + } + + wif->state = wlanIfaceState_up; + (void)wlan_config_state(wif, 1); + wif->reg_domain = wlan_regdomain_to_snmp(regdomain->rd.regdomain); + wif->country_code[0] = regdomain->rd.isocc[0]; + wif->country_code[1] = regdomain->rd.isocc[1]; + wif->country_code[2] = regdomain->rd.location; + free(regdomain); + + return (0); +} + +int +wlan_config_get_dssid(struct wlan_iface *wif) +{ + int val = -1; + size_t argsize = IEEE80211_NWID_LEN + 1; + char ssid[IEEE80211_NWID_LEN + 1]; + + memset(ssid, 0, IEEE80211_NWID_LEN + 1); + + if (wlan_ioctl(wif->wname, + (wif->mode == WlanIfaceOperatingModeType_meshPoint) ? + IEEE80211_IOC_MESH_ID : IEEE80211_IOC_SSID, &val, ssid, + &argsize, 0) < 0) + return (-1); + + if (argsize > IEEE80211_NWID_LEN) + argsize = IEEE80211_NWID_LEN; + memcpy(wif->desired_ssid, ssid, argsize); + wif->desired_ssid[argsize] = '\0'; + + return (0); +} + +int +wlan_config_set_dssid(struct wlan_iface *wif, char *ssid, int slen) +{ + int val = 0; + size_t argsize = slen; + + if (wlan_ioctl(wif->wname, + (wif->mode == WlanIfaceOperatingModeType_meshPoint) ? + IEEE80211_IOC_MESH_ID : IEEE80211_IOC_SSID, &val, ssid, + &argsize, 1) < 0) + return (-1); + + if (argsize > IEEE80211_NWID_LEN) + argsize = IEEE80211_NWID_LEN; + memcpy(wif->desired_ssid, ssid, argsize); + wif->desired_ssid[argsize] = '\0'; + + return (0); +} + +static int +wlan_config_get_dchannel(struct wlan_iface *wif) +{ + uint32_t i = 0; + int val = 0; + size_t argsize = sizeof(struct ieee80211_channel); + struct ieee80211_channel chan; + + if (wlan_get_channel_list(wif) < 0) + return (-1); + + memset(&chan, 0, sizeof(chan)); + if (wlan_ioctl(wif->wname, IEEE80211_IOC_CURCHAN, &val, &chan, + &argsize, 0) < 0) + return (-1); + + for (i = 0; i < wif->nchannels; i++) + if (chan.ic_ieee == wif->chanlist[i].ic_ieee && + chan.ic_flags == wif->chanlist[i].ic_flags) { + wif->desired_channel = i + 1; + break; + } + + return (0); +} + +static int +wlan_config_set_dchannel(struct wlan_iface *wif, uint32_t dchannel) +{ + int val = 0; + size_t argsize = sizeof(struct ieee80211_channel); + struct ieee80211_channel chan; + + if (wlan_get_channel_list(wif) < 0) + return (-1); + + if (dchannel > wif->nchannels) + return (-1); + + memcpy(&chan, wif->chanlist + dchannel - 1, sizeof(chan)); + if (wlan_ioctl(wif->wname, IEEE80211_IOC_CURCHAN, &val, &chan, + &argsize, 1) < 0) + return (-1); + + wif->desired_channel = dchannel; + + return (0); +} + +static int +wlan_config_get_bssid(struct wlan_iface *wif) +{ + int val = 0; + size_t argsize = IEEE80211_ADDR_LEN; + char bssid[IEEE80211_ADDR_LEN]; + + memset(bssid, 0, IEEE80211_ADDR_LEN); + + if (wlan_ioctl(wif->wname, IEEE80211_IOC_BSSID, &val, bssid, + &argsize, 0) < 0 || argsize != IEEE80211_ADDR_LEN) + return (-1); + + memcpy(wif->desired_bssid, bssid, IEEE80211_ADDR_LEN); + + return (0); +} + +static int +wlan_config_set_bssid(struct wlan_iface *wif, uint8_t *bssid) +{ + int val = 0; + size_t argsize = IEEE80211_ADDR_LEN; + + if (wlan_ioctl(wif->wname, IEEE80211_IOC_BSSID, &val, bssid, + &argsize, 1) < 0 || argsize != IEEE80211_ADDR_LEN) + return (-1); + + memcpy(wif->desired_bssid, bssid, IEEE80211_ADDR_LEN); + + return (0); +} + +/* + * Convert the value returned by the kernel to the appropriate SNMP + * representation and set the corresponding interface member accordingly. + */ +static void +wlan_config_set_snmp_intval(struct wlan_iface *wif, int op, int val) +{ + switch (op) { + case IEEE80211_IOC_BURST: + if (val == 0) + wif->packet_burst = TruthValue_false; + else + wif->packet_burst = TruthValue_true; + break; + case IEEE80211_IOC_DFS: + if (val == 0) + wif->dyn_frequency = TruthValue_false; + else + wif->dyn_frequency = TruthValue_true; + break; + case IEEE80211_IOC_FF: + if (val == 0) + wif->fast_frames = TruthValue_false; + else + wif->fast_frames = TruthValue_true; + break; + case IEEE80211_IOC_TURBOP: + if (val == 0) + wif->dturbo = TruthValue_false; + else + wif->dturbo = TruthValue_true; + break; + case IEEE80211_IOC_TXPOWER: + wif->tx_power = val / 2; + break; + case IEEE80211_IOC_FRAGTHRESHOLD: + wif->frag_threshold = val; + break; + case IEEE80211_IOC_RTSTHRESHOLD: + wif->rts_threshold = val; + break; + case IEEE80211_IOC_WPS: + if (val == 0) + wif->priv_subscribe = TruthValue_false; + else + wif->priv_subscribe = TruthValue_true; + break; + case IEEE80211_IOC_BGSCAN: + if (val == 0) + wif->bg_scan = TruthValue_false; + else + wif->bg_scan = TruthValue_true; + break; + case IEEE80211_IOC_BGSCAN_IDLE: + wif->bg_scan_idle = val; + break; + case IEEE80211_IOC_BGSCAN_INTERVAL: + wif->bg_scan_interval = val; + break; + case IEEE80211_IOC_BMISSTHRESHOLD: + wif->beacons_missed = val; + break; + case IEEE80211_IOC_ROAMING: + switch (val) { + case IEEE80211_ROAMING_DEVICE: + wif->roam_mode = wlanIfaceRoamingMode_device; + break; + case IEEE80211_ROAMING_MANUAL: + wif->roam_mode = wlanIfaceRoamingMode_manual; + break; + case IEEE80211_ROAMING_AUTO: + /* FALTHROUGH */ + default: + wif->roam_mode = wlanIfaceRoamingMode_auto; + break; + } + break; + case IEEE80211_IOC_DOTD: + if (val == 0) + wif->dot11d = TruthValue_false; + else + wif->dot11d = TruthValue_true; + break; + case IEEE80211_IOC_DOTH: + if (val == 0) + wif->dot11h = TruthValue_false; + else + wif->dot11h = TruthValue_true; + break; + case IEEE80211_IOC_DWDS: + if (val == 0) + wif->dynamic_wds = TruthValue_false; + else + wif->dynamic_wds = TruthValue_true; + break; + case IEEE80211_IOC_POWERSAVE: + if (val == 0) + wif->power_save = TruthValue_false; + else + wif->power_save = TruthValue_true; + break; + case IEEE80211_IOC_APBRIDGE: + if (val == 0) + wif->ap_bridge = TruthValue_false; + else + wif->ap_bridge = TruthValue_true; + break; + case IEEE80211_IOC_BEACON_INTERVAL: + wif->beacon_interval = val; + break; + case IEEE80211_IOC_DTIM_PERIOD: + wif->dtim_period = val; + break; + case IEEE80211_IOC_HIDESSID: + if (val == 0) + wif->hide_ssid = TruthValue_false; + else + wif->hide_ssid = TruthValue_true; + break; + case IEEE80211_IOC_INACTIVITY: + if (val == 0) + wif->inact_process = TruthValue_false; + else + wif->inact_process = TruthValue_true; + break; + case IEEE80211_IOC_PROTMODE: + switch (val) { + case IEEE80211_PROTMODE_CTS: + wif->do11g_protect = wlanIfaceDot11gProtMode_cts; + break; + case IEEE80211_PROTMODE_RTSCTS: + wif->do11g_protect = wlanIfaceDot11gProtMode_rtscts; + break; + case IEEE80211_PROTMODE_OFF: + /* FALLTHROUGH */ + default: + wif->do11g_protect = wlanIfaceDot11gProtMode_off; + break; + } + break; + case IEEE80211_IOC_PUREG: + if (val == 0) + wif->dot11g_pure = TruthValue_false; + else + wif->dot11g_pure = TruthValue_true; + break; + case IEEE80211_IOC_PUREN: + if (val == 0) + wif->dot11n_pure = TruthValue_false; + else + wif->dot11n_pure = TruthValue_true; + break; + case IEEE80211_IOC_AMPDU: + switch (val) { + case 0: + wif->ampdu = WlanIfaceDot11nPduType_disabled; + break; + case 1: + wif->ampdu = WlanIfaceDot11nPduType_txOnly; + break; + case 2: + wif->ampdu = WlanIfaceDot11nPduType_rxOnly; + break; + case 3: + /* FALLTHROUGH */ + default: + wif->ampdu = WlanIfaceDot11nPduType_txAndRx; + break; + } + break; + case IEEE80211_IOC_AMPDU_DENSITY: + switch (val) { + case IEEE80211_HTCAP_MPDUDENSITY_025: + wif->ampdu_density = 25; + break; + case IEEE80211_HTCAP_MPDUDENSITY_05: + wif->ampdu_density = 50; + break; + case IEEE80211_HTCAP_MPDUDENSITY_1: + wif->ampdu_density = 100; + break; + case IEEE80211_HTCAP_MPDUDENSITY_2: + wif->ampdu_density = 200; + break; + case IEEE80211_HTCAP_MPDUDENSITY_4: + wif->ampdu_density = 400; + break; + case IEEE80211_HTCAP_MPDUDENSITY_8: + wif->ampdu_density = 800; + break; + case IEEE80211_HTCAP_MPDUDENSITY_16: + wif->ampdu_density = 1600; + break; + case IEEE80211_HTCAP_MPDUDENSITY_NA: + default: + wif->ampdu_density = 0; + break; + } + break; + case IEEE80211_IOC_AMPDU_LIMIT: + switch (val) { + case IEEE80211_HTCAP_MAXRXAMPDU_8K: + wif->ampdu_limit = 8192; + break; + case IEEE80211_HTCAP_MAXRXAMPDU_16K: + wif->ampdu_limit = 16384; + break; + case IEEE80211_HTCAP_MAXRXAMPDU_32K: + wif->ampdu_limit = 32768; + break; + case IEEE80211_HTCAP_MAXRXAMPDU_64K: + default: + wif->ampdu_limit = 65536; + break; + } + break; + case IEEE80211_IOC_AMSDU: + switch (val) { + case 0: + wif->amsdu = WlanIfaceDot11nPduType_disabled; + break; + case 1: + wif->amsdu = WlanIfaceDot11nPduType_txOnly; + break; + case 3: + wif->amsdu = WlanIfaceDot11nPduType_txAndRx; + break; + case 2: + default: + /* FALLTHROUGH */ + wif->amsdu = WlanIfaceDot11nPduType_rxOnly; + break; + } + break; + case IEEE80211_IOC_AMSDU_LIMIT: + wif->amsdu_limit = val; + break; + case IEEE80211_IOC_HTCONF: + if (val == 0) /* XXX */ + wif->ht_enabled = TruthValue_false; + else + wif->ht_enabled = TruthValue_true; + break; + case IEEE80211_IOC_HTCOMPAT: + if (val == 0) + wif->ht_compatible = TruthValue_false; + else + wif->ht_compatible = TruthValue_true; + break; + case IEEE80211_IOC_HTPROTMODE: + if (val == IEEE80211_PROTMODE_RTSCTS) + wif->ht_prot_mode = wlanIfaceDot11nHTProtMode_rts; + else + wif->ht_prot_mode = wlanIfaceDot11nHTProtMode_off; + break; + case IEEE80211_IOC_RIFS: + if (val == 0) + wif->rifs = TruthValue_false; + else + wif->rifs = TruthValue_true; + break; + case IEEE80211_IOC_SHORTGI: + if (val == 0) + wif->short_gi = TruthValue_false; + else + wif->short_gi = TruthValue_true; + break; + case IEEE80211_IOC_SMPS: + switch (val) { + case IEEE80211_HTCAP_SMPS_DYNAMIC: + wif->smps_mode = wlanIfaceDot11nSMPSMode_dynamic; + break; + case IEEE80211_HTCAP_SMPS_ENA: + wif->smps_mode = wlanIfaceDot11nSMPSMode_static; + break; + case IEEE80211_HTCAP_SMPS_OFF: + /* FALLTHROUGH */ + default: + wif->smps_mode = wlanIfaceDot11nSMPSMode_disabled; + break; + } + break; + case IEEE80211_IOC_TDMA_SLOT: + wif->tdma_slot = val; + break; + case IEEE80211_IOC_TDMA_SLOTCNT: + wif->tdma_slot_count = val; + break; + case IEEE80211_IOC_TDMA_SLOTLEN: + wif->tdma_slot_length = val; + break; + case IEEE80211_IOC_TDMA_BINTERVAL: + wif->tdma_binterval = val; + break; + default: + break; + } +} + +/* + * Convert an SNMP value to the kernel equivalent and also do sanity check + * for each specific type. + */ +static int +wlan_config_snmp2value(int which, int sval, int *value) +{ + *value = 0; + + switch (which) { + case IEEE80211_IOC_BURST: + case IEEE80211_IOC_DFS: + case IEEE80211_IOC_FF: + case IEEE80211_IOC_TURBOP: + case IEEE80211_IOC_WPS: + case IEEE80211_IOC_BGSCAN: + case IEEE80211_IOC_DOTD: + case IEEE80211_IOC_DOTH: + case IEEE80211_IOC_DWDS: + case IEEE80211_IOC_POWERSAVE: + case IEEE80211_IOC_APBRIDGE: + case IEEE80211_IOC_HIDESSID: + case IEEE80211_IOC_INACTIVITY: + case IEEE80211_IOC_PUREG: + case IEEE80211_IOC_PUREN: + case IEEE80211_IOC_HTCONF: + case IEEE80211_IOC_HTCOMPAT: + case IEEE80211_IOC_RIFS: + if (sval == TruthValue_true) + *value = 1; + else if (sval != TruthValue_false) + return (SNMP_ERR_INCONS_VALUE); + break; + case IEEE80211_IOC_REGDOMAIN: + break; + case IEEE80211_IOC_SSID: + break; + case IEEE80211_IOC_CURCHAN: + break; + case IEEE80211_IOC_TXPOWER: + *value = sval * 2; + break; + case IEEE80211_IOC_FRAGTHRESHOLD: + if (sval < IEEE80211_FRAG_MIN || sval > IEEE80211_FRAG_MAX) + return (SNMP_ERR_INCONS_VALUE); + *value = sval; + break; + case IEEE80211_IOC_RTSTHRESHOLD: + if (sval < IEEE80211_RTS_MIN || sval > IEEE80211_RTS_MAX) + return (SNMP_ERR_INCONS_VALUE); + *value = sval; + break; + case IEEE80211_IOC_BGSCAN_IDLE: + if (sval < WLAN_BGSCAN_IDLE_MIN) + return (SNMP_ERR_INCONS_VALUE); + *value = sval; + break; + case IEEE80211_IOC_BGSCAN_INTERVAL: + if (sval < WLAN_SCAN_VALID_MIN) + return (SNMP_ERR_INCONS_VALUE); + *value = sval; + break; + case IEEE80211_IOC_BMISSTHRESHOLD: + if (sval < IEEE80211_HWBMISS_MIN || sval > IEEE80211_HWBMISS_MAX) + return (SNMP_ERR_INCONS_VALUE); + *value = sval; + break; + case IEEE80211_IOC_BSSID: + break; + case IEEE80211_IOC_ROAMING: + switch (sval) { + case wlanIfaceRoamingMode_device: + *value = IEEE80211_ROAMING_DEVICE; + break; + case wlanIfaceRoamingMode_manual: + *value = IEEE80211_ROAMING_MANUAL; + break; + case wlanIfaceRoamingMode_auto: + *value = IEEE80211_ROAMING_AUTO; + break; + default: + return (SNMP_ERR_INCONS_VALUE); + } + break; + case IEEE80211_IOC_BEACON_INTERVAL: + if (sval < IEEE80211_BINTVAL_MIN || sval > IEEE80211_BINTVAL_MAX) + return (SNMP_ERR_INCONS_VALUE); + *value = sval; + break; + case IEEE80211_IOC_DTIM_PERIOD: + if (sval < IEEE80211_DTIM_MIN || sval > IEEE80211_DTIM_MAX) + return (SNMP_ERR_INCONS_VALUE); + *value = sval; + break; + case IEEE80211_IOC_PROTMODE: + switch (sval) { + case wlanIfaceDot11gProtMode_cts: + *value = IEEE80211_PROTMODE_CTS; + break; + case wlanIfaceDot11gProtMode_rtscts: + *value = IEEE80211_PROTMODE_RTSCTS; + break; + case wlanIfaceDot11gProtMode_off: + *value = IEEE80211_PROTMODE_OFF; + break; + default: + return (SNMP_ERR_INCONS_VALUE); + } + break; + case IEEE80211_IOC_AMPDU: + switch (sval) { + case WlanIfaceDot11nPduType_disabled: + break; + case WlanIfaceDot11nPduType_txOnly: + *value = 1; + break; + case WlanIfaceDot11nPduType_rxOnly: + *value = 2; + break; + case WlanIfaceDot11nPduType_txAndRx: + *value = 3; + break; + default: + return (SNMP_ERR_INCONS_VALUE); + } + break; + case IEEE80211_IOC_AMPDU_DENSITY: + switch (sval) { + case 0: + *value = IEEE80211_HTCAP_MPDUDENSITY_NA; + break; + case 25: + *value = IEEE80211_HTCAP_MPDUDENSITY_025; + break; + case 50: + *value = IEEE80211_HTCAP_MPDUDENSITY_05; + break; + case 100: + *value = IEEE80211_HTCAP_MPDUDENSITY_1; + break; + case 200: + *value = IEEE80211_HTCAP_MPDUDENSITY_2; + break; + case 400: + *value = IEEE80211_HTCAP_MPDUDENSITY_4; + break; + case 800: + *value = IEEE80211_HTCAP_MPDUDENSITY_8; + break; + case 1600: + *value = IEEE80211_HTCAP_MPDUDENSITY_16; + break; + default: + return (SNMP_ERR_INCONS_VALUE); + } + break; + case IEEE80211_IOC_AMPDU_LIMIT: + switch (sval) { + case 8192: + *value = IEEE80211_HTCAP_MAXRXAMPDU_8K; + break; + case 16384: + *value = IEEE80211_HTCAP_MAXRXAMPDU_16K; + break; + case 32768: + *value = IEEE80211_HTCAP_MAXRXAMPDU_32K; + break; + case 65536: + *value = IEEE80211_HTCAP_MAXRXAMPDU_64K; + break; + default: + return (SNMP_ERR_INCONS_VALUE); + } + break; + case IEEE80211_IOC_AMSDU: + switch (sval) { + case WlanIfaceDot11nPduType_disabled: + break; + case WlanIfaceDot11nPduType_txOnly: + *value = 1; + break; + case WlanIfaceDot11nPduType_rxOnly: + *value = 2; + break; + case WlanIfaceDot11nPduType_txAndRx: + *value = 3; + break; + default: + return (SNMP_ERR_INCONS_VALUE); + } + break; + case IEEE80211_IOC_AMSDU_LIMIT: + if (sval == 3839 || sval == 0) + *value = IEEE80211_HTCAP_MAXAMSDU_3839; + else if (sval == 7935) + *value = IEEE80211_HTCAP_MAXAMSDU_7935; + else + return (SNMP_ERR_INCONS_VALUE); + break; + case IEEE80211_IOC_HTPROTMODE: + switch (sval) { + case wlanIfaceDot11nHTProtMode_rts: + *value = IEEE80211_PROTMODE_RTSCTS; + break; + case wlanIfaceDot11nHTProtMode_off: + break; + default: + return (SNMP_ERR_INCONS_VALUE); + } + break; + case IEEE80211_IOC_SHORTGI: + if (sval == TruthValue_true) + *value = IEEE80211_HTCAP_SHORTGI20 | + IEEE80211_HTCAP_SHORTGI40; + else if (sval != TruthValue_false) + return (SNMP_ERR_INCONS_VALUE); + break; + case IEEE80211_IOC_SMPS: + switch (sval) { + case wlanIfaceDot11nSMPSMode_disabled: + *value = IEEE80211_HTCAP_SMPS_OFF; + break; + case wlanIfaceDot11nSMPSMode_static: + *value = IEEE80211_HTCAP_SMPS_ENA; + break; + case wlanIfaceDot11nSMPSMode_dynamic: + *value = IEEE80211_HTCAP_SMPS_DYNAMIC; + break; + default: + return (SNMP_ERR_INCONS_VALUE); + } + break; + case IEEE80211_IOC_TDMA_SLOT: + if (sval < 0 || sval > WLAN_TDMA_MAXSLOTS) /* XXX */ + return (SNMP_ERR_INCONS_VALUE); + *value = sval; + break; + case IEEE80211_IOC_TDMA_SLOTCNT: + if (sval < 0 || sval > WLAN_TDMA_MAXSLOTS) /* XXX */ + return (SNMP_ERR_INCONS_VALUE); + *value = sval; + break; + case IEEE80211_IOC_TDMA_SLOTLEN: + if (sval < 2*100 || sval > 0xfffff) /* XXX */ + return (SNMP_ERR_INCONS_VALUE); + *value = sval; + break; + case IEEE80211_IOC_TDMA_BINTERVAL: + if (sval < 1) /* XXX */ + return (SNMP_ERR_INCONS_VALUE); + *value = sval; + break; + default: + return (SNMP_ERR_INCONS_VALUE); + } + + return (SNMP_ERR_NOERROR); +} + +/* + * Sanity checks for the wlanIfaceConfigTable. + */ +static int +wlan_config_check(struct wlan_iface *wif, int op) +{ + switch (op) { + case IEEE80211_IOC_BURST: + if ((wif->drivercaps & (0x1 << WlanDriverCaps_burst)) == 0) { + wif->packet_burst = TruthValue_false; + return (-1); + } + break; + case IEEE80211_IOC_DFS: + if ((wif->drivercaps & (0x1 << WlanDriverCaps_dfs)) == 0) { + wif->dyn_frequency = TruthValue_false; + return (-1); + } + break; + case IEEE80211_IOC_FF: + if ((wif->drivercaps & (0x1 << WlanDriverCaps_athFastFrames)) + == 0) { + wif->fast_frames = TruthValue_false; + return (-1); + } + break; + case IEEE80211_IOC_TURBOP: + if ((wif->drivercaps & (0x1 << WlanDriverCaps_athTurbo)) == 0) { + wif->dturbo = TruthValue_false; + return (-1); + } + break; + case IEEE80211_IOC_TXPOWER: + if ((wif->drivercaps & (0x1 << WlanDriverCaps_txPmgt)) == 0) { + wif->tx_power = 0; + return (-1); + } + break; + case IEEE80211_IOC_FRAGTHRESHOLD: + if ((wif->drivercaps & (0x1 << WlanDriverCaps_txFrag)) == 0) { + wif->frag_threshold = IEEE80211_FRAG_MAX; + return (-1); + } + break; + case IEEE80211_IOC_DWDS: + if ((wif->drivercaps & (0x1 << WlanDriverCaps_wds)) == 0) { + wif->dynamic_wds = TruthValue_false; + return (-1); + } + break; + case IEEE80211_IOC_POWERSAVE: + if ((wif->drivercaps & (0x1 << WlanDriverCaps_pmgt)) == 0) { + wif->power_save = TruthValue_false; + return (-1); + } + break; + case IEEE80211_IOC_BEACON_INTERVAL: + if (wif->mode != WlanIfaceOperatingModeType_hostAp && + wif->mode != WlanIfaceOperatingModeType_meshPoint && + wif->mode != WlanIfaceOperatingModeType_ibss) { + wif->beacon_interval = 100; /* XXX */ + return (-1); + } + break; + case IEEE80211_IOC_DTIM_PERIOD: + if (wif->mode != WlanIfaceOperatingModeType_hostAp && + wif->mode != WlanIfaceOperatingModeType_meshPoint && + wif->mode != WlanIfaceOperatingModeType_ibss) { + wif->dtim_period = 1; /* XXX */ + return (-1); + } + break; + case IEEE80211_IOC_PUREN: + if ((wif->htcaps & (0x1 << WlanHTCaps_htcHt)) == 0) { + wif->dot11n_pure = TruthValue_false; + return (-1); + } + break; + case IEEE80211_IOC_AMPDU: + if ((wif->htcaps & (0x1 << WlanHTCaps_htcAmpdu)) == 0) { + wif->ampdu = WlanIfaceDot11nPduType_disabled; + return (-1); + } + break; + case IEEE80211_IOC_AMSDU: + if ((wif->htcaps & (0x1 << WlanHTCaps_htcAmsdu)) == 0) { + wif->amsdu = WlanIfaceDot11nPduType_disabled; + return (-1); + } + break; + case IEEE80211_IOC_RIFS: + if ((wif->htcaps & (0x1 << WlanHTCaps_htcRifs)) == 0) { + wif->rifs = TruthValue_false; + return (-1); + } + break; + case IEEE80211_IOC_SHORTGI: + if ((wif->htcaps & (0x1 << WlanHTCaps_shortGi20 | + 0x1 << WlanHTCaps_shortGi40)) == 0) { + wif->short_gi = TruthValue_false; + return (-1); + } + break; + case IEEE80211_IOC_SMPS: + if ((wif->htcaps & (0x1 << WlanHTCaps_htcSmps)) == 0) { + wif->smps_mode = wlanIfaceDot11nSMPSMode_disabled; + return (-1); + } + break; + case IEEE80211_IOC_TDMA_SLOT: + if ((wif->drivercaps & (0x1 << WlanDriverCaps_tdma)) == 0) { + wif->tdma_slot = 0; + return (-1); + } + break; + case IEEE80211_IOC_TDMA_SLOTCNT: + if ((wif->drivercaps & (0x1 << WlanDriverCaps_tdma)) == 0) { + wif->tdma_slot_count = 0; + return (-1); + } + break; + case IEEE80211_IOC_TDMA_SLOTLEN: + if ((wif->drivercaps & (0x1 << WlanDriverCaps_tdma)) == 0) { + wif->tdma_slot_length = 0; + return (-1); + } + break; + case IEEE80211_IOC_TDMA_BINTERVAL: + if ((wif->drivercaps & (0x1 << WlanDriverCaps_tdma)) == 0) { + wif->tdma_binterval = 0; + return (-1); + } + break; + default: + break; + } + + return (0); +} + +static int +wlan_config_get_intval(struct wlan_iface *wif, int op) +{ + int val = 0; + size_t argsize = 0; + + if (wlan_config_check(wif, op) < 0) + return (0); + if (wlan_ioctl(wif->wname, op, &val, NULL, &argsize, 0) < 0) + return (-1); + wlan_config_set_snmp_intval(wif, op, val); + + return (0); +} + +static int +wlan_config_set_intval(struct wlan_iface *wif, int op, int sval) +{ + size_t argsize = 0; + int val; + + if (wlan_config_check(wif, op) < 0) + return (-1); + if (wlan_config_snmp2value(op, sval, &val) != SNMP_ERR_NOERROR) + return (-1); + if (wlan_ioctl(wif->wname, op, &val, NULL, &argsize, 1) < 0) + return (-1); + wlan_config_set_snmp_intval(wif, op, val); + + return (0); +} + +int +wlan_config_get_ioctl(struct wlan_iface *wif, int which) +{ + int op; + + switch (which) { + case LEAF_wlanIfaceCountryCode: + /* FALLTHROUGH */ + case LEAF_wlanIfaceRegDomain: + return (wlan_config_get_country(wif)); + case LEAF_wlanIfaceDesiredSsid: + return (wlan_config_get_dssid(wif)); + case LEAF_wlanIfaceDesiredChannel: + return (wlan_config_get_dchannel(wif)); + case LEAF_wlanIfaceDesiredBssid: + return (wlan_config_get_bssid(wif)); + default: + op = wlan_config_snmp2ioctl(which); + return (wlan_config_get_intval(wif, op)); + } + + return (-1); +} + +int +wlan_config_set_ioctl(struct wlan_iface *wif, int which, int val, + char *strval, int len) +{ + int op; + + switch (which) { + case LEAF_wlanIfaceCountryCode: + return (wlan_config_set_country(wif, strval, + wif->reg_domain)); + case LEAF_wlanIfaceRegDomain: + return (wlan_config_set_country(wif, wif->country_code, + val)); + case LEAF_wlanIfaceDesiredSsid: + return (wlan_config_set_dssid(wif, strval, len)); + case LEAF_wlanIfaceDesiredChannel: + return (wlan_config_set_dchannel(wif, val)); + case LEAF_wlanIfaceDesiredBssid: + return (wlan_config_set_bssid(wif, strval)); + default: + op = wlan_config_snmp2ioctl(which); + return (wlan_config_set_intval(wif, op, val)); + } + + return (-1); +} + +static uint32_t +wlan_snmp_to_scan_flags(int flags) +{ + int sr_flags = 0; + + if ((flags & (0x1 << WlanScanFlagsType_noSelection)) != 0) + sr_flags |= IEEE80211_IOC_SCAN_NOPICK; + if ((flags & (0x1 << WlanScanFlagsType_activeScan)) != 0) + sr_flags |= IEEE80211_IOC_SCAN_ACTIVE; + if ((flags & (0x1 << WlanScanFlagsType_pickFirst)) != 0) + sr_flags |= IEEE80211_IOC_SCAN_PICK1ST; + if ((flags & (0x1 << WlanScanFlagsType_backgroundScan)) != 0) + sr_flags |= IEEE80211_IOC_SCAN_BGSCAN; + if ((flags & (0x1 << WlanScanFlagsType_once)) != 0) + sr_flags |= IEEE80211_IOC_SCAN_ONCE; + if ((flags & (0x1 << WlanScanFlagsType_noBroadcast)) != 0) + sr_flags |= IEEE80211_IOC_SCAN_NOBCAST; + if ((flags & (0x1 << WlanScanFlagsType_noAutoSequencing)) != 0) + sr_flags |= IEEE80211_IOC_SCAN_NOJOIN; + if ((flags & (0x1 << WlanScanFlagsType_flushCashe)) != 0) + sr_flags |= IEEE80211_IOC_SCAN_FLUSH; + if ((flags & (0x1 << WlanScanFlagsType_chechCashe)) != 0) + sr_flags |= IEEE80211_IOC_SCAN_CHECK; + + return (sr_flags); +} + +int +wlan_set_scan_config(struct wlan_iface *wif) +{ + int val = 0; + size_t argsize; + struct ieee80211_scan_req sr; + + + memset(&sr, 0, sizeof(sr)); + argsize = sizeof(struct ieee80211_scan_req); + sr.sr_flags = wlan_snmp_to_scan_flags(wif->scan_flags); + sr.sr_flags |= IEEE80211_IOC_SCAN_BGSCAN; + sr.sr_duration = wif->scan_duration; + sr.sr_mindwell = wif->scan_mindwell; + sr.sr_maxdwell = wif->scan_maxdwell; + sr.sr_nssid = 0; + + if (wlan_ioctl(wif->wname, IEEE80211_IOC_SCAN_REQ, + &val, &sr, &argsize, 1) < 0) + return (-1); + + wif->scan_status = wlanScanConfigStatus_running; + return (0); +} + +static uint32_t +wlan_peercaps_to_snmp(uint32_t pcaps) +{ + uint32_t scaps = 0; + + if ((pcaps & IEEE80211_CAPINFO_ESS) != 0) + scaps |= (0x1 << WlanPeerCapabilityFlags_ess); + if ((pcaps & IEEE80211_CAPINFO_IBSS) != 0) + scaps |= (0x1 << WlanPeerCapabilityFlags_ibss); + if ((pcaps & IEEE80211_CAPINFO_CF_POLLABLE) != 0) + scaps |= (0x1 << WlanPeerCapabilityFlags_cfPollable); + if ((pcaps & IEEE80211_CAPINFO_CF_POLLREQ) != 0) + scaps |= (0x1 << WlanPeerCapabilityFlags_cfPollRequest); + if ((pcaps & IEEE80211_CAPINFO_PRIVACY) != 0) + scaps |= (0x1 << WlanPeerCapabilityFlags_privacy); + if ((pcaps & IEEE80211_CAPINFO_SHORT_PREAMBLE) != 0) + scaps |= (0x1 << WlanPeerCapabilityFlags_shortPreamble); + if ((pcaps & IEEE80211_CAPINFO_PBCC) != 0) + scaps |= (0x1 << WlanPeerCapabilityFlags_pbcc); + if ((pcaps & IEEE80211_CAPINFO_CHNL_AGILITY) != 0) + scaps |= (0x1 << WlanPeerCapabilityFlags_channelAgility); + if ((pcaps & IEEE80211_CAPINFO_SHORT_SLOTTIME) != 0) + scaps |= (0x1 << WlanPeerCapabilityFlags_shortSlotTime); + if ((pcaps & IEEE80211_CAPINFO_RSN) != 0) + scaps |= (0x1 << WlanPeerCapabilityFlags_rsn); + if ((pcaps & IEEE80211_CAPINFO_DSSSOFDM) != 0) + scaps |= (0x1 << WlanPeerCapabilityFlags_dsssofdm); + + return (scaps); +} + +static int +wlan_add_new_scan_result(struct wlan_iface *wif, + const struct ieee80211req_scan_result *isr, uint8_t *ssid) +{ + struct wlan_scan_result *sr; + + if ((sr = wlan_scan_new_result(ssid, isr->isr_bssid)) == NULL) + return (-1); + + sr->opchannel = wlan_channel_flags_to_snmp_phy(isr->isr_flags); + sr->rssi = isr->isr_rssi; + sr->frequency = isr->isr_freq; + sr->noise = isr->isr_noise; + sr->bintval = isr->isr_intval; + sr->capinfo = wlan_peercaps_to_snmp(isr->isr_capinfo); + + if (wlan_scan_add_result(wif, sr) < 0) { + wlan_scan_free_result(sr); + return (-1); + } + + return (0); +} + +int +wlan_get_scan_results(struct wlan_iface *wif) +{ + int ssidlen, val = 0; + uint8_t buf[24 * 1024]; + size_t argsize; + const uint8_t *cp, *idp; + uint8_t ssid[IEEE80211_NWID_LEN + 1]; + struct ieee80211req_scan_result isr; + + argsize = sizeof(buf); + if (wlan_ioctl(wif->wname, IEEE80211_IOC_SCAN_RESULTS, &val, &buf, + &argsize, 0) < 0) + return (-1); + + if (argsize < sizeof(struct ieee80211req_scan_result)) + return (0); + + cp = buf; + do { + memcpy(&isr, cp, sizeof(struct ieee80211req_scan_result)); + memset(ssid, 0, IEEE80211_NWID_LEN + 1); + + if (isr.isr_meshid_len) { + idp = cp + isr.isr_ie_off + isr.isr_ssid_len; + ssidlen = isr.isr_meshid_len; + } else { + idp = cp + isr.isr_ie_off; + ssidlen = isr.isr_ssid_len; + } + if (ssidlen > IEEE80211_NWID_LEN) + ssidlen = IEEE80211_NWID_LEN; + memcpy(ssid, idp, ssidlen); + ssid[IEEE80211_NWID_LEN] = '\0'; + (void)wlan_add_new_scan_result(wif, &isr, ssid); + cp += isr.isr_len; + argsize -= isr.isr_len; + } while (argsize >= sizeof(struct ieee80211req_scan_result)); + + return (0); +} + +int +wlan_get_stats(struct wlan_iface *wif) +{ + struct ifreq ifr; + + memset(&ifr, 0, sizeof(struct ifreq)); + strlcpy(ifr.ifr_name, wif->wname, IFNAMSIZ); + + ifr.ifr_data = (caddr_t) &wif->stats; + + if (ioctl(sock, SIOCG80211STATS, &ifr) < 0) { + syslog(LOG_ERR, "iface %s - ioctl(SIOCG80211STATS) failed: %s", + wif->wname, strerror(errno)); + return (-1); + } + + return (0); +} + +int +wlan_get_wepmode(struct wlan_iface *wif) +{ + int val = 0; + size_t argsize = 0; + + if (wlan_ioctl(wif->wname, IEEE80211_IOC_WEP, &val, NULL, + &argsize, 0) < 0 || val == IEEE80211_WEP_NOSUP) { + wif->wepsupported = 0; /* XXX */ + wif->wepmode = wlanWepMode_off; + wif->weptxkey = 0; + return (-1); + } + + wif->wepsupported = 1; + + switch (val) { + case IEEE80211_WEP_ON: + wif->wepmode = wlanWepMode_on; + break; + case IEEE80211_WEP_MIXED: + wif->wepmode = wlanWepMode_mixed; + break; + case IEEE80211_WEP_OFF: + /* FALLTHROUGH */ + default: + wif->wepmode = wlanWepMode_off; + break; + } + + return (0); +} + +int +wlan_set_wepmode(struct wlan_iface *wif) +{ + int val; + size_t argsize = 0; + + if (!wif->wepsupported) + return (-1); + + switch (wif->wepmode) { + case wlanWepMode_off: + val = IEEE80211_WEP_OFF; + break; + case wlanWepMode_on: + val = IEEE80211_WEP_ON; + break; + case wlanWepMode_mixed: + val = IEEE80211_WEP_MIXED; + break; + default: + return (-1); + } + + if (wlan_ioctl(wif->wname, IEEE80211_IOC_WEP, &val, NULL, + &argsize, 1) < 0) + return (-1); + + return (0); +} + +int +wlan_get_weptxkey(struct wlan_iface *wif) +{ + int val; + size_t argsize = 0; + + if (!wif->wepsupported) + return (0); + + if (wlan_ioctl(wif->wname, IEEE80211_IOC_WEPTXKEY, &val, NULL, + &argsize, 0) < 0) + return (-1); + + if (val == IEEE80211_KEYIX_NONE) + wif->weptxkey = 0; + else + wif->weptxkey = val + 1; + + return (0); +} + +int +wlan_set_weptxkey(struct wlan_iface *wif) +{ + int val; + size_t argsize = 0; + + if (!wif->wepsupported) + return (0); + + if (wif->weptxkey >= IEEE80211_WEP_NKID) + return (-1); + + if (wif->weptxkey == 0) + val = IEEE80211_KEYIX_NONE; + else + val = wif->weptxkey - 1; + if (wlan_ioctl(wif->wname, IEEE80211_IOC_WEPTXKEY, &val, NULL, + &argsize, 1) < 0) + return (-1); + + return (0); +} + +int +wlan_get_wepkeys(struct wlan_iface *wif __unused) +{ + /* XXX: should they be visible via SNMP */ + return (0); +} + +int +wlan_set_wepkeys(struct wlan_iface *wif __unused) +{ + /* XXX: should they be configurable via SNMP */ + return (0); +} + +int +wlan_get_mac_policy(struct wlan_iface *wif) +{ + int val = IEEE80211_MACCMD_POLICY; + size_t argsize = 0; + struct ieee80211req ireq; + + memset(&ireq, 0, sizeof(struct ieee80211req)); + strlcpy(ireq.i_name, wif->wname, IFNAMSIZ); + ireq.i_type = IEEE80211_IOC_MACCMD; + ireq.i_val = IEEE80211_MACCMD_POLICY; + + if (ioctl(sock, SIOCG80211, &ireq) < 0) { + if (errno != EINVAL) { + syslog(LOG_ERR, "iface %s - get param: ioctl(%d) " + "failed: %s", wif->wname, ireq.i_type, + strerror(errno)); + wif->macsupported = 0; + return (-1); + } else { + wif->macsupported = 1; + wif->mac_policy = wlanMACAccessControlPolicy_open; + return (0); + } + + } + + wif->macsupported = 1; + + switch (val) { + case IEEE80211_MACCMD_POLICY_ALLOW: + wif->mac_policy = wlanMACAccessControlPolicy_allow; + break; + case IEEE80211_MACCMD_POLICY_DENY: + wif->mac_policy = wlanMACAccessControlPolicy_deny; + break; + case IEEE80211_MACCMD_POLICY_RADIUS: + wif->mac_policy = wlanMACAccessControlPolicy_radius; + break; + case IEEE80211_MACCMD_POLICY_OPEN: + /* FALLTHROUGH */ + default: + wif->mac_policy = wlanMACAccessControlPolicy_open; + break; + } + + argsize = 0; + val = IEEE80211_MACCMD_LIST; + if (wlan_ioctl(wif->wname, IEEE80211_IOC_MACCMD, &val, NULL, + &argsize, 0) < 0) + return (-1); + + wif->mac_nacls = argsize / sizeof(struct ieee80211req_maclist *); + return (0); +} + +int +wlan_set_mac_policy(struct wlan_iface *wif) +{ + int val; + size_t argsize = 0; + + if (!wif->macsupported) + return (-1); + + switch (wif->mac_policy) { + case wlanMACAccessControlPolicy_allow: + val = IEEE80211_MACCMD_POLICY_ALLOW; + break; + case wlanMACAccessControlPolicy_deny: + val = IEEE80211_MACCMD_POLICY_DENY; + break; + case wlanMACAccessControlPolicy_radius: + val = IEEE80211_MACCMD_POLICY_RADIUS; + break; + case wlanMACAccessControlPolicy_open: + val = IEEE80211_MACCMD_POLICY_OPEN; + break; + default: + return (-1); + } + + if (wlan_ioctl(wif->wname, IEEE80211_IOC_MACCMD, &val, NULL, + &argsize, 1) < 0) + return (-1); + + return (0); +} + +int +wlan_flush_mac_mac(struct wlan_iface *wif) +{ + int val = IEEE80211_MACCMD_FLUSH; + size_t argsize = 0; + + if (wlan_ioctl(wif->wname, IEEE80211_IOC_MACCMD, &val, NULL, + &argsize, 1) < 0) + return (-1); + + return (0); +} + +static int +wlan_add_mac_macinfo(struct wlan_iface *wif, + const struct ieee80211req_maclist *ml) +{ + struct wlan_mac_mac *mmac; + + if ((mmac = wlan_mac_new_mac(ml->ml_macaddr)) == NULL) + return (-1); + + mmac->mac_status = RowStatus_active; + if (wlan_mac_add_mac(wif, mmac) < 0) { + wlan_mac_free_mac(mmac); + return (-1); + } + + return (0); +} + +int +wlan_get_mac_acl_macs(struct wlan_iface *wif) +{ + int i, nacls, val = IEEE80211_MACCMD_LIST; + size_t argsize = 0; + uint8_t *data; + struct ieee80211req ireq; + const struct ieee80211req_maclist *acllist; + + if (wif->mac_policy == wlanMACAccessControlPolicy_radius) { + wif->mac_nacls = 0; + return (0); + } + + memset(&ireq, 0, sizeof(struct ieee80211req)); + strlcpy(ireq.i_name, wif->wname, IFNAMSIZ); + ireq.i_type = IEEE80211_IOC_MACCMD; + ireq.i_val = IEEE80211_MACCMD_LIST; + + + if (ioctl(sock, SIOCG80211, &ireq) < 0) { + if (errno != EINVAL) { + syslog(LOG_ERR, "iface %s - get param: ioctl(%d) " + "failed: %s", wif->wname, ireq.i_type, + strerror(errno)); + wif->macsupported = 0; + return (-1); + } + } + + if (argsize == 0) { + wif->mac_nacls = 0; + return (0); + } + + if ((data = (uint8_t *)malloc(argsize)) == NULL) + return (-1); + + if (wlan_ioctl(wif->wname, IEEE80211_IOC_MACCMD, &val, data, + &argsize, 0) < 0) + return (-1); + + nacls = argsize / sizeof(*acllist); + acllist = (struct ieee80211req_maclist *) data; + for (i = 0; i < nacls; i++) + (void)wlan_add_mac_macinfo(wif, acllist + i); + + wif->mac_nacls = nacls; + return (0); +} + +int +wlan_add_mac_acl_mac(struct wlan_iface *wif, struct wlan_mac_mac *mmac) +{ + int val = 0; + size_t argsize = IEEE80211_ADDR_LEN; + struct ieee80211req_mlme mlme; + + if (wlan_ioctl(wif->wname, IEEE80211_IOC_ADDMAC, &val, + mmac->mac, &argsize, 1) < 0) + return (-1); + + mmac->mac_status = RowStatus_active; + + /* If policy is deny, try to kick the station just in case. */ + if (wif->mac_policy != wlanMACAccessControlPolicy_deny) + return (0); + + memset(&mlme, 0, sizeof(mlme)); + mlme.im_op = IEEE80211_MLME_DEAUTH; + mlme.im_reason = IEEE80211_REASON_AUTH_EXPIRE; + memcpy(mlme.im_macaddr, mmac->mac, IEEE80211_ADDR_LEN); + argsize = sizeof(struct ieee80211req_mlme); + + if (wlan_ioctl(wif->wname, IEEE80211_IOC_MLME, &val, &mlme, + &argsize, 1) < 0 && errno != ENOENT) + return (-1); + + return (0); +} + +int +wlan_del_mac_acl_mac(struct wlan_iface *wif, struct wlan_mac_mac *mmac) +{ + int val = 0; + size_t argsize = IEEE80211_ADDR_LEN; + struct ieee80211req_mlme mlme; + + if (wlan_ioctl(wif->wname, IEEE80211_IOC_DELMAC, &val, + mmac->mac, &argsize, 1) < 0) + return (-1); + + mmac->mac_status = RowStatus_active; + + /* If policy is allow, try to kick the station just in case. */ + if (wif->mac_policy != wlanMACAccessControlPolicy_allow) + return (0); + + memset(&mlme, 0, sizeof(mlme)); + mlme.im_op = IEEE80211_MLME_DEAUTH; + mlme.im_reason = IEEE80211_REASON_AUTH_EXPIRE; + memcpy(mlme.im_macaddr, mmac->mac, IEEE80211_ADDR_LEN); + argsize = sizeof(struct ieee80211req_mlme); + + if (wlan_ioctl(wif->wname, IEEE80211_IOC_MLME, &val, &mlme, + &argsize, 1) < 0 && errno != ENOENT) + return (-1); + + return (0); +} + +int +wlan_peer_set_vlan(struct wlan_iface *wif, struct wlan_peer *wip, int vlan) +{ + int val = 0; + size_t argsize; + struct ieee80211req_sta_vlan vreq; + + memcpy(vreq.sv_macaddr, wip->pmac, IEEE80211_ADDR_LEN); + vreq.sv_vlan = vlan; + argsize = sizeof(struct ieee80211req_sta_vlan); + + if (wlan_ioctl(wif->wname, IEEE80211_IOC_STA_VLAN, + &val, &vreq, &argsize, 1) < 0) + return (-1); + + wip->vlan = vlan; + + return (0); +} + +/* XXX */ +#ifndef IEEE80211_NODE_AUTH +#define IEEE80211_NODE_AUTH 0x000001 /* authorized for data */ +#define IEEE80211_NODE_QOS 0x000002 /* QoS enabled */ +#define IEEE80211_NODE_ERP 0x000004 /* ERP enabled */ +#define IEEE80211_NODE_PWR_MGT 0x000010 /* power save mode enabled */ +#define IEEE80211_NODE_AREF 0x000020 /* authentication ref held */ +#define IEEE80211_NODE_HT 0x000040 /* HT enabled */ +#define IEEE80211_NODE_HTCOMPAT 0x000080 /* HT setup w/ vendor OUI's */ +#define IEEE80211_NODE_WPS 0x000100 /* WPS association */ +#define IEEE80211_NODE_TSN 0x000200 /* TSN association */ +#define IEEE80211_NODE_AMPDU_RX 0x000400 /* AMPDU rx enabled */ +#define IEEE80211_NODE_AMPDU_TX 0x000800 /* AMPDU tx enabled */ +#define IEEE80211_NODE_MIMO_PS 0x001000 /* MIMO power save enabled */ +#define IEEE80211_NODE_MIMO_RTS 0x002000 /* send RTS in MIMO PS */ +#define IEEE80211_NODE_RIFS 0x004000 /* RIFS enabled */ +#define IEEE80211_NODE_SGI20 0x008000 /* Short GI in HT20 enabled */ +#define IEEE80211_NODE_SGI40 0x010000 /* Short GI in HT40 enabled */ +#define IEEE80211_NODE_ASSOCID 0x020000 /* xmit requires associd */ +#define IEEE80211_NODE_AMSDU_RX 0x040000 /* AMSDU rx enabled */ +#define IEEE80211_NODE_AMSDU_TX 0x080000 /* AMSDU tx enabled */ +#endif + +static uint32_t +wlan_peerstate_to_snmp(uint32_t pstate) +{ + uint32_t sstate = 0; + + if ((pstate & IEEE80211_NODE_AUTH) != 0) + sstate |= (0x1 << WlanIfacePeerFlagsType_authorizedForData); + if ((pstate & IEEE80211_NODE_QOS) != 0) + sstate |= (0x1 << WlanIfacePeerFlagsType_qosEnabled); + if ((pstate & IEEE80211_NODE_ERP) != 0) + sstate |= (0x1 << WlanIfacePeerFlagsType_erpEnabled); + if ((pstate & IEEE80211_NODE_PWR_MGT) != 0) + sstate |= (0x1 << WlanIfacePeerFlagsType_powerSaveMode); + if ((pstate & IEEE80211_NODE_AREF) != 0) + sstate |= (0x1 << WlanIfacePeerFlagsType_authRefHeld); + if ((pstate & IEEE80211_NODE_HT) != 0) + sstate |= (0x1 << WlanIfacePeerFlagsType_htEnabled); + if ((pstate & IEEE80211_NODE_HTCOMPAT) != 0) + sstate |= (0x1 << WlanIfacePeerFlagsType_htCompat); + if ((pstate & IEEE80211_NODE_WPS) != 0) + sstate |= (0x1 << WlanIfacePeerFlagsType_wpsAssoc); + if ((pstate & IEEE80211_NODE_TSN) != 0) + sstate |= (0x1 << WlanIfacePeerFlagsType_tsnAssoc); + if ((pstate & IEEE80211_NODE_AMPDU_RX) != 0) + sstate |= (0x1 << WlanIfacePeerFlagsType_ampduRx); + if ((pstate & IEEE80211_NODE_AMPDU_TX) != 0) + sstate |= (0x1 << WlanIfacePeerFlagsType_ampduTx); + if ((pstate & IEEE80211_NODE_MIMO_PS) != 0) + sstate |= (0x1 << WlanIfacePeerFlagsType_mimoPowerSave); + if ((pstate & IEEE80211_NODE_MIMO_RTS) != 0) + sstate |= (0x1 << WlanIfacePeerFlagsType_sendRts); + if ((pstate & IEEE80211_NODE_RIFS) != 0) + sstate |= (0x1 << WlanIfacePeerFlagsType_rifs); + if ((pstate & IEEE80211_NODE_SGI20) != 0) + sstate |= (0x1 << WlanIfacePeerFlagsType_shortGiHT20); + if ((pstate & IEEE80211_NODE_SGI40) != 0) + sstate |= (0x1 << WlanIfacePeerFlagsType_shortGiHT40); + if ((pstate & IEEE80211_NODE_AMSDU_RX) != 0) + sstate |= (0x1 << WlanIfacePeerFlagsType_amsduRx); + if ((pstate & IEEE80211_NODE_AMSDU_TX) != 0) + sstate |= (0x1 << WlanIfacePeerFlagsType_amsduTx); + + return (sstate); +} + +static struct wlan_peer * +wlan_add_peerinfo(const struct ieee80211req_sta_info *si) +{ + struct wlan_peer *wip; + + if ((wip = wlan_new_peer(si->isi_macaddr))== NULL) + return (NULL); + + wip->associd = IEEE80211_AID(si->isi_associd); + wip->vlan = si->isi_vlan; + wip->frequency = si->isi_freq; + wip->fflags = si->isi_flags; + wip->txrate = si->isi_txrate; + wip->rssi = si->isi_rssi; + wip->idle = si->isi_inact; + wip->txseqs = si->isi_txseqs[0]; /* XXX */ + wip->rxseqs = si->isi_rxseqs[0]; /* XXX */ + wip->txpower = si->isi_txpower; + wip->capinfo = wlan_peercaps_to_snmp(si->isi_capinfo); + wip->state = wlan_peerstate_to_snmp(si->isi_state); + wip->local_id = si->isi_localid; + wip->peer_id = si->isi_peerid; + + return (wip); +} + +int +wlan_get_peerinfo(struct wlan_iface *wif) +{ + union { + struct ieee80211req_sta_req req; + uint8_t buf[24 * 1024]; + } u; + const uint8_t *cp; + int val = 0; + size_t len; + struct ieee80211req_sta_info si; + struct wlan_peer *wip; + + /* Get all stations - broadcast address */ + (void) memset(u.req.is_u.macaddr, 0xff, IEEE80211_ADDR_LEN); + len = sizeof(u); + + if (wlan_ioctl(wif->wname, IEEE80211_IOC_STA_INFO, + & val, &u, &len, 0) < 0) + return (-1); + + if (len < sizeof(struct ieee80211req_sta_info)) + return (-1); + + cp = (const uint8_t *) u.req.info; + do { + memcpy(&si, cp, sizeof(struct ieee80211req_sta_info)); + if ((wip = wlan_add_peerinfo(&si)) != NULL && + wlan_add_peer(wif, wip) < 0) + wlan_free_peer(wip); + cp += si.isi_len, len -= si.isi_len; + } while (len >= sizeof(struct ieee80211req_sta_info)); + + return (0); +} + +/************************************************************************ + * Wireless MESH & HWMP sysctl config. + */ +const char wlan_sysctl_name[] = "net.wlan."; + +static const char *wlan_sysctl[] = { + "mesh.retrytimeout", + "mesh.holdingtimeout", + "mesh.confirmtimeout", + "mesh.maxretries", + "hwmp.targetonly", + "hwmp.replyforward", + "hwmp.pathlifetime", + "hwmp.roottimeout", + "hwmp.rootint", + "hwmp.rannint", + "hwmp.inact", +}; + +int32_t +wlan_do_sysctl(struct wlan_config *cfg, enum wlan_syscl which, int set) +{ + char mib_name[100]; + int val, sval; + size_t len, vlen; + + if (set) { + vlen = sizeof(sval); + switch (which) { + case WLAN_MESH_RETRY_TO: + sval = cfg->mesh_retryto; + break; + case WLAN_MESH_HOLDING_TO: + sval = cfg->mesh_holdingto; + break; + case WLAN_MESH_CONFIRM_TO: + sval = cfg->mesh_confirmto; + break; + case WLAN_MESH_MAX_RETRIES: + sval = cfg->mesh_maxretries; + break; + case WLAN_HWMP_TARGET_ONLY: + sval = cfg->hwmp_targetonly; + break; + case WLAN_HWMP_REPLY_FORWARD: + sval = cfg->hwmp_replyforward; + break; + case WLAN_HWMP_PATH_LIFETIME: + sval = cfg->hwmp_pathlifetime; + break; + case WLAN_HWMP_ROOT_TO: + sval = cfg->hwmp_roottimeout; + break; + case WLAN_HWMP_ROOT_INT: + sval = cfg->hwmp_rootint; + break; + case WLAN_HWMP_RANN_INT: + sval = cfg->hwmp_rannint; + break; + case WLAN_HWMP_INACTIVITY_TO: + sval = cfg->hwmp_inact; + break; + default: + return (-1); + } + } else { + if (which >= WLAN_SYSCTL_MAX) + return (-1); + vlen = 0; + } + + strlcpy(mib_name, wlan_sysctl_name, sizeof(mib_name)); + strlcat(mib_name, wlan_sysctl[which], sizeof(mib_name)); + len = sizeof (val); + + if (sysctlbyname(mib_name, &val, &len, (set? &sval : NULL), vlen) < 0) { + syslog(LOG_ERR, "sysctl(%s) failed - %s", mib_name, + strerror(errno)); + return (-1); + } + + switch (which) { + case WLAN_MESH_RETRY_TO: + cfg->mesh_retryto = val; + break; + case WLAN_MESH_HOLDING_TO: + cfg->mesh_holdingto = val; + break; + case WLAN_MESH_CONFIRM_TO: + cfg->mesh_confirmto = val; + break; + case WLAN_MESH_MAX_RETRIES: + cfg->mesh_maxretries = val; + break; + case WLAN_HWMP_TARGET_ONLY: + cfg->hwmp_targetonly = val; + break; + case WLAN_HWMP_REPLY_FORWARD: + cfg->hwmp_replyforward = val; + break; + case WLAN_HWMP_PATH_LIFETIME: + cfg->hwmp_pathlifetime = val; + break; + case WLAN_HWMP_ROOT_TO: + cfg->hwmp_roottimeout = val; + break; + case WLAN_HWMP_ROOT_INT: + cfg->hwmp_rootint = val; + break; + case WLAN_HWMP_RANN_INT: + cfg->hwmp_rannint = val; + break; + case WLAN_HWMP_INACTIVITY_TO: + cfg->hwmp_inact = val; + break; + default: + /* NOTREACHED */ + abort(); + } + + return (0); +} + +int +wlan_mesh_config_get(struct wlan_iface *wif, int which) +{ + int op, val = 0; + size_t argsize = 0; + uint8_t data[32], *pd = NULL; + + switch (which) { + case LEAF_wlanMeshTTL: + op = IEEE80211_IOC_MESH_TTL; + break; + case LEAF_wlanMeshPeeringEnabled: + op = IEEE80211_IOC_MESH_AP; + break; + case LEAF_wlanMeshForwardingEnabled: + op = IEEE80211_IOC_MESH_FWRD; + break; + case LEAF_wlanMeshMetric: + op = IEEE80211_IOC_MESH_PR_METRIC; + pd = data; + argsize = sizeof(data); + break; + case LEAF_wlanMeshPath: + op = IEEE80211_IOC_MESH_PR_PATH; + pd = data; + argsize = sizeof(data); + break; + case LEAF_wlanMeshRoutesFlush: + return (0); + default: + return (-1); + } + + if (wlan_ioctl(wif->wname, op, &val, pd, &argsize, 0) < 0) + return (-1); + + switch (which) { + case LEAF_wlanMeshTTL: + wif->mesh_ttl = val; + break; + case LEAF_wlanMeshPeeringEnabled: + if (val) + wif->mesh_peering = wlanMeshPeeringEnabled_true; + else + wif->mesh_peering = wlanMeshPeeringEnabled_false; + break; + case LEAF_wlanMeshForwardingEnabled: + if (val) + wif->mesh_forwarding = wlanMeshForwardingEnabled_true; + else + wif->mesh_forwarding = wlanMeshForwardingEnabled_false; + break; + case LEAF_wlanMeshMetric: + data[argsize] = '\0'; + if (strcmp(data, "AIRTIME") == 0) + wif->mesh_metric = wlanMeshMetric_airtime; + else + wif->mesh_metric = wlanMeshMetric_unknown; + break; + case LEAF_wlanMeshPath: + data[argsize] = '\0'; + if (strcmp(data, "HWMP") == 0) + wif->mesh_path = wlanMeshPath_hwmp; + else + wif->mesh_path = wlanMeshPath_unknown; + } + + return (0); +} + +int +wlan_mesh_config_set(struct wlan_iface *wif, int which) +{ + int op, val = 0; + size_t argsize = 0; + uint8_t data[32], *pd = NULL; + + switch (which) { + case LEAF_wlanMeshTTL: + op = IEEE80211_IOC_MESH_TTL; + val = wif->mesh_ttl; + break; + case LEAF_wlanMeshPeeringEnabled: + op = IEEE80211_IOC_MESH_AP; + if (wif->mesh_peering == wlanMeshPeeringEnabled_true) + val = 1; + break; + case LEAF_wlanMeshForwardingEnabled: + if (wif->mesh_forwarding == wlanMeshForwardingEnabled_true) + val = 1; + op = IEEE80211_IOC_MESH_FWRD; + break; + case LEAF_wlanMeshMetric: + op = IEEE80211_IOC_MESH_PR_METRIC; + if (wif->mesh_metric == wlanMeshMetric_airtime) + strcpy(data, "AIRTIME"); + else + return (-1); + pd = data; + argsize = sizeof(data); + break; + case LEAF_wlanMeshPath: + op = IEEE80211_IOC_MESH_PR_PATH; + if (wif->mesh_path == wlanMeshPath_hwmp) + strcpy(data, "HWMP"); + else + return (-1); + pd = data; + argsize = sizeof(data); + break; + default: + return (-1); + } + + if (wlan_ioctl(wif->wname, op, &val, pd, &argsize, 1) < 0) + return (-1); + + return(0); +} + +int +wlan_mesh_flush_routes(struct wlan_iface *wif) +{ + int val = IEEE80211_MESH_RTCMD_FLUSH; + size_t argsize = 0; + + if (wlan_ioctl(wif->wname, IEEE80211_IOC_MESH_RTCMD, &val, NULL, + &argsize, 1) < 0) + return (-1); + + return (0); +} + +int +wlan_mesh_add_route(struct wlan_iface *wif, struct wlan_mesh_route *wmr) +{ + int val = IEEE80211_MESH_RTCMD_ADD; + size_t argsize = IEEE80211_ADDR_LEN; + + if (wlan_ioctl(wif->wname, IEEE80211_IOC_MESH_RTCMD, &val, + wmr->imroute.imr_dest, &argsize, 1) < 0) + return (-1); + + wmr->mroute_status = RowStatus_active; + + return (0); +} + +int +wlan_mesh_del_route(struct wlan_iface *wif, struct wlan_mesh_route *wmr) +{ + int val = IEEE80211_MESH_RTCMD_DELETE; + size_t argsize = IEEE80211_ADDR_LEN; + + if (wlan_ioctl(wif->wname, IEEE80211_IOC_MESH_RTCMD, &val, + wmr->imroute.imr_dest, &argsize, 1) < 0) + return (-1); + + wmr->mroute_status = RowStatus_destroy; + + return (0); +} + +int +wlan_mesh_get_routelist(struct wlan_iface *wif) +{ + int i, nroutes, val = IEEE80211_MESH_RTCMD_LIST; + size_t argsize; + struct ieee80211req_mesh_route routes[128]; + struct ieee80211req_mesh_route *rt; + struct wlan_mesh_route *wmr; + + argsize = sizeof(routes); + if (wlan_ioctl(wif->wname, IEEE80211_IOC_MESH_RTCMD, &val, routes, + &argsize, 0) < 0) /* XXX: ENOMEM? */ + return (-1); + + nroutes = argsize / sizeof(*rt); + for (i = 0; i < nroutes; i++) { + rt = routes + i; + if ((wmr = wlan_mesh_new_route(rt->imr_dest)) == NULL) + return (-1); + memcpy(&wmr->imroute, rt, sizeof(*rt)); + wmr->mroute_status = RowStatus_active; + if (wlan_mesh_add_rtentry(wif, wmr) < 0) + wlan_mesh_free_route(wmr); + } + + return (0); +} + +int +wlan_hwmp_config_get(struct wlan_iface *wif, int which) +{ + int op, val = 0; + size_t argsize = 0; + + switch (which) { + case LEAF_wlanHWMPRootMode: + op = IEEE80211_IOC_HWMP_ROOTMODE; + break; + case LEAF_wlanHWMPMaxHops: + op = IEEE80211_IOC_HWMP_MAXHOPS; + break; + default: + return (-1); + } + + if (wlan_ioctl(wif->wname, op, &val, NULL, &argsize, 0) < 0) + return (-1); + + switch (which) { + case LEAF_wlanHWMPRootMode: + switch (val) { + case IEEE80211_HWMP_ROOTMODE_NORMAL: + wif->hwmp_root_mode = wlanHWMPRootMode_normal; + break; + case IEEE80211_HWMP_ROOTMODE_PROACTIVE: + wif->hwmp_root_mode = wlanHWMPRootMode_proactive; + break; + case IEEE80211_HWMP_ROOTMODE_RANN: + wif->hwmp_root_mode = wlanHWMPRootMode_rann; + break; + case IEEE80211_HWMP_ROOTMODE_DISABLED: + default: + wif->hwmp_root_mode = wlanHWMPRootMode_disabled; + break; + } + break; + case LEAF_wlanHWMPMaxHops: + wif->hwmp_max_hops = val; + break; + } + + return (0); +} + +int +wlan_hwmp_config_set(struct wlan_iface *wif, int which) +{ + int op, val = 0; + size_t argsize = 0; + + switch (which) { + case LEAF_wlanHWMPRootMode: + op = IEEE80211_IOC_HWMP_ROOTMODE; + switch (wif->hwmp_root_mode) { + case wlanHWMPRootMode_disabled: + val = IEEE80211_HWMP_ROOTMODE_DISABLED; + break; + case wlanHWMPRootMode_normal: + val = IEEE80211_HWMP_ROOTMODE_NORMAL; + break; + case wlanHWMPRootMode_proactive: + val = IEEE80211_HWMP_ROOTMODE_PROACTIVE; + break; + case wlanHWMPRootMode_rann: + val = IEEE80211_HWMP_ROOTMODE_RANN; + break; + default: + return (-1); + } + break; + case LEAF_wlanHWMPMaxHops: + op = IEEE80211_IOC_HWMP_MAXHOPS; + val = wif->hwmp_max_hops; + break; + default: + return (-1); + } + + if (wlan_ioctl(wif->wname, op, &val, NULL, &argsize, 1) < 0) + return (-1); + + return (0); +} diff --git a/usr.sbin/bsnmpd/modules/snmp_wlan/wlan_tree.def b/usr.sbin/bsnmpd/modules/snmp_wlan/wlan_tree.def new file mode 100644 index 0000000..e0ae2a0 --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_wlan/wlan_tree.def @@ -0,0 +1,677 @@ +#- +# Copyright (C) 2010 The FreeBSD Foundation +# All rights reserved. +# +# This software was developed by Shteryana Sotirova Shopova under +# sponsorship from the FreeBSD Foundation. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# + +#include "tc.def" + +typedef RowStatus ENUM ( + 1 active + 2 notInService + 3 notReady + 4 createAndGo + 5 createAndWait + 6 destroy +) + +typedef TruthValue ENUM ( + 1 true + 2 false +) + +typedef WlanRegDomainCode ENUM ( + 1 fcc + 2 ca + 3 etsi + 4 etsi2 + 5 etsi3 + 6 fcc3 + 7 japan + 8 korea + 9 apac + 10 apac2 + 11 apac3 + 12 row + 13 none + 14 debug + 15 sr9 + 16 xr9 + 17 gz901 +) + +typedef WlanMgmtReasonCode ENUM ( + 1 unspecified + 2 authenticationExpire + 3 authenticationLeave + 4 associationExpire + 5 associationTooMany + 6 notAuthenticated + 7 notAssociated + 8 associationLeave + 9 associationNotAuthenticated + 10 dissassocPwrcapBad + 11 dissassocSuperchanBad + 13 ieInvalid + 14 micFailure + 15 fourWayHandshakeTimeout + 16 groupKeyUpdateTimeout + 17 ieIn4FourWayDiffers + 18 groupCipherInvalid + 19 pairwiseCiherInvalid + 20 akmpInvalid + 21 unsupportedRsnIeVersion + 22 invalidRsnIeCap + 23 dot1xAuthFailed + 24 cipherSuiteRejected + 32 unspeciffiedQos + 33 insufficientBw + 34 tooManyFrames + 35 outsideTxOp + 36 leavingQbss + 37 badMechanism + 38 setupNeeded + 39 timeout +) + +typedef WlanIfaceOperatingModeType ENUM ( + 0 ibss + 1 station + 2 wds + 3 adhocDemo + 4 hostAp + 5 monitor + 6 meshPoint + 7 tdma +) + +typedef WlanIfaceFlagsType BITS ( + 1 uniqueBssid + 2 noBeacons + 3 wdsLegacy +) + +typedef WlanDriverCaps BITS ( + 1 station + 2 ieee8023encap + 3 athFastFrames + 4 athTurbo + 5 ibss + 6 pmgt + 7 hostAp + 8 ahDemo + 9 swRetry + 10 txPmgt + 11 shortSlot + 12 shortPreamble + 13 monitor + 14 dfs + 15 mbss + 16 wpa1 + 17 wpa2 + 18 burst + 19 wme + 20 wds + 21 bgScan + 22 txFrag + 23 tdma +) + +typedef WlanCryptoCaps BITS ( + 1 wep + 2 tkip + 3 aes + 4 aesCcm + 5 tkipMic + 6 ckip +) + +typedef WlanHTCaps BITS ( + 1 ldpc + 2 chwidth40 + 3 greenField + 4 shortGi20 + 5 shortGi40 + 6 txStbc + 7 delba + 8 amsdu7935 + 9 dssscck40 + 10 psmp + 11 fortyMHzIntolerant + 12 lsigTxOpProt + 13 htcAmpdu + 14 htcAmsdu + 15 htcHt + 16 htcSmps + 17 htcRifs +) + +typedef WlanIfaceDot11nPduType ENUM ( + 0 disabled + 1 rxOnly + 2 txOnly + 3 txAndRx +) + +typedef WlanPeerCapabilityFlags BITS ( + 1 ess + 2 ibss + 3 cfPollable + 4 cfPollRequest + 5 privacy + 6 shortPreamble + 7 pbcc + 8 channelAgility + 9 shortSlotTime + 10 rsn + 11 dsssofdm +) + +typedef WlanIfacePeerFlagsType BITS ( + 1 authorizedForData + 2 qosEnabled + 3 erpEnabled + 4 powerSaveMode + 5 authRefHeld + 6 htEnabled + 7 htCompat + 8 wpsAssoc + 9 tsnAssoc + 10 ampduRx + 11 ampduTx + 12 mimoPowerSave + 13 sendRts + 14 rifs + 15 shortGiHT20 + 16 shortGiHT40 + 17 amsduRx + 18 amsduTx +) + +typedef WlanIfaceChannelFlagsType BITS ( + 1 turbo + 2 cck + 3 ofdm + 4 spectrum2Ghz + 5 spectrum5Ghz + 6 passiveScan + 7 dynamicCckOfdm + 8 gfsk + 9 spectrum900Mhz + 10 dot11aStaticTurbo + 11 halfRate + 12 quarterRate + 13 ht20 + 14 ht40u + 15 ht40d + 16 dfs + 17 xmit4ms + 18 noAdhoc + 19 noHostAp + 20 dot11d +) + +typedef WlanIfaceChannelStateType BITS ( + 1 radar + 2 cacDone + 3 interferenceDetected + 4 radarClear +) + +typedef WlanIfPhyMode ENUM ( + 1 auto + 2 dot11a + 3 dot11b + 4 dot11g + 5 fh + 6 turboA + 7 turboG + 8 sturboA + 9 dot11na + 10 dot11ng + 11 ofdmHalf + 12 ofdmQuarter +) + +typedef WlanChannelType ENUM ( + 1 fhss + 2 dot11a + 3 dot11b + 4 dot11g + 5 tenMHz + 6 fiveMHz + 7 turbo + 8 ht +) + +typedef WlanScanFlagsType BITS ( + 1 noSelection + 2 activeScan + 3 pickFirst + 4 backgroundScan + 5 once + 6 noBroadcast + 7 noAutoSequencing + 8 flushCashe + 9 chechCashe +) + +typedef WlanMeshNeighborPeerStateType ENUM ( + 0 idle + 1 openTx + 2 openRx + 3 confirmRx + 4 established + 5 closing +) + +(1 internet + (4 private + (1 enterprises + (12325 fokus + (1 begemot + (210 begemotWlan + (0 begemotWlanNotifications + ) + (1 begemotWlanInterface + (1 wlanInterfaceTable + (1 wlanInterfaceEntry : OCTETSTRING op_wlan_iface + (1 wlanIfaceIndex INTEGER GET) + (2 wlanIfaceName OCTETSTRING GET SET) + (3 wlanParentIfName OCTETSTRING GET SET) + (4 wlanIfaceOperatingMode WlanIfaceOperatingModeType GET SET) + (5 wlanIfaceFlags WlanIfaceFlagsType GET SET) + (6 wlanIfaceBssid OCTETSTRING | MacAddress GET SET) + (7 wlanIfaceLocalAddress OCTETSTRING | MacAddress GET SET) + (8 wlanIfaceStatus RowStatus GET SET) + (9 wlanIfaceState ENUM ( 1 up 2 down ) GET SET) + )) + (2 wlanIfParentTable + (1 wlanIfParentEntry : OCTETSTRING op_wlan_if_parent + (1 wlanIfParentDriverCapabilities WlanDriverCaps GET) + (2 wlanIfParentCryptoCapabilities WlanCryptoCaps GET) + (3 wlanIfParentHTCapabilities WlanHTCaps GET) + )) + (3 wlanIfaceConfigTable + (1 wlanIfaceConfigEntry : OCTETSTRING op_wlan_iface_config + (1 wlanIfacePacketBurst ENUM ( 1 true 2 false ) GET SET) + (2 wlanIfaceCountryCode OCTETSTRING GET SET) + (3 wlanIfaceRegDomain WlanRegDomainCode GET SET) + (4 wlanIfaceDesiredSsid OCTETSTRING GET SET) + (5 wlanIfaceDesiredChannel INTEGER32 GET SET) + (6 wlanIfaceDynamicFreqSelection ENUM ( 1 true 2 false ) GET SET) + (7 wlanIfaceFastFrames ENUM ( 1 true 2 false ) GET SET) + (8 wlanIfaceDturbo ENUM ( 1 true 2 false ) GET SET) + (9 wlanIfaceTxPower INTEGER32 GET SET) + (10 wlanIfaceFragmentThreshold INTEGER GET SET) + (11 wlanIfaceRTSThreshold INTEGER GET SET) + (12 wlanIfaceWlanPrivacySubscribe ENUM ( 1 true 2 false ) GET SET) + (13 wlanIfaceBgScan ENUM ( 1 true 2 false ) GET SET) + (14 wlanIfaceBgScanIdle INTEGER32 GET SET) + (15 wlanIfaceBgScanInterval INTEGER32 GET SET) + (16 wlanIfaceBeaconMissedThreshold INTEGER GET SET) + (17 wlanIfaceDesiredBssid OCTETSTRING | MacAddress GET SET) + (18 wlanIfaceRoamingMode ENUM ( 1 device 2 auto 3 manual ) GET SET) + (19 wlanIfaceDot11d ENUM ( 1 true 2 false ) GET SET) + (20 wlanIfaceDot11h ENUM ( 1 true 2 false ) GET SET) + (21 wlanIfaceDynamicWds ENUM ( 1 true 2 false ) GET SET) + (22 wlanIfacePowerSave ENUM ( 1 true 2 false ) GET SET) + (23 wlanIfaceApBridge ENUM ( 1 true 2 false ) GET SET) + (24 wlanIfaceBeaconInterval INTEGER GET SET) + (25 wlanIfaceDtimPeriod INTEGER GET SET) + (26 wlanIfaceHideSsid ENUM ( 1 true 2 false ) GET SET) + (27 wlanIfaceInactivityProccess ENUM ( 1 true 2 false ) GET SET) + (28 wlanIfaceDot11gProtMode ENUM ( 1 off 2 cts 3 rtscts ) GET SET) + (29 wlanIfaceDot11gPureMode ENUM ( 1 true 2 false ) GET SET) + (30 wlanIfaceDot11nPureMode ENUM ( 1 true 2 false ) GET SET) + (31 wlanIfaceDot11nAmpdu WlanIfaceDot11nPduType GET SET) + (32 wlanIfaceDot11nAmpduDensity INTEGER GET SET) + (33 wlanIfaceDot11nAmpduLimit INTEGER GET SET) + (34 wlanIfaceDot11nAmsdu WlanIfaceDot11nPduType GET SET) + (35 wlanIfaceDot11nAmsduLimit INTEGER GET SET) + (36 wlanIfaceDot11nHighThroughput ENUM ( 1 true 2 false ) GET SET) + (37 wlanIfaceDot11nHTCompatible ENUM ( 1 true 2 false ) GET SET) + (38 wlanIfaceDot11nHTProtMode ENUM ( 1 off 2 rts ) GET SET) + (39 wlanIfaceDot11nRIFS ENUM ( 1 true 2 false ) GET SET) + (40 wlanIfaceDot11nShortGI ENUM ( 1 true 2 false ) GET SET) + (41 wlanIfaceDot11nSMPSMode ENUM ( 1 disabled 2 static 3 dynamic ) GET SET) + (42 wlanIfaceTdmaSlot INTEGER GET SET) + (43 wlanIfaceTdmaSlotCount INTEGER GET SET) + (44 wlanIfaceTdmaSlotLength INTEGER GET SET) + (45 wlanIfaceTdmaBeaconInterval INTEGER32 GET SET) + )) + (4 wlanIfacePeerTable + (1 wlanIfacePeerEntry : OCTETSTRING OCTETSTRING | MacAddress op_wlan_if_peer + (1 wlanIfacePeerAddress OCTETSTRING | MacAddress GET) + (2 wlanIfacePeerAssociationId INTEGER32 GET) + (3 wlanIfacePeerVlanTag INTEGER GET SET) + (4 wlanIfacePeerFrequency INTEGER32 GET) + (5 wlanIfacePeerCurrentTXRate INTEGER32 GET) + (6 wlanIfacePeerRxSignalStrength INTEGER32 GET) + (7 wlanIfacePeerIdleTimer INTEGER32 GET) + (8 wlanIfacePeerTxSequenceNo INTEGER32 GET) + (9 wlanIfacePeerRxSequenceNo INTEGER32 GET) + (10 wlanIfacePeerTxPower INTEGER32 GET) + (11 wlanIfacePeerCapabilities WlanPeerCapabilityFlags GET) + (12 wlanIfacePeerFlags WlanIfacePeerFlagsType GET) + )) + (5 wlanIfaceChannelTable + (1 wlanIfaceChannelEntry : OCTETSTRING INTEGER op_wlan_channels + (1 wlanIfaceChannelId INTEGER) + (2 wlanIfaceChannelIeeeId INTEGER GET) + (3 wlanIfaceChannelType WlanChannelType GET) + (4 wlanIfaceChannelFlags WlanIfaceChannelFlagsType GET) + (5 wlanIfaceChannelFrequency INTEGER32 GET) + (6 wlanIfaceChannelMaxRegPower INTEGER32 GET) + (7 wlanIfaceChannelMaxTxPower INTEGER32 GET) + (8 wlanIfaceChannelMinTxPower INTEGER32 GET) + (9 wlanIfaceChannelState WlanIfaceChannelStateType GET) + (10 wlanIfaceChannelHTExtension INTEGER32 GET) + (11 wlanIfaceChannelMaxAntennaGain INTEGER32 GET) + )) + (6 wlanIfRoamParamsTable + (1 wlanIfRoamParamsEntry : OCTETSTRING WlanIfPhyMode op_wlan_roam_params + (1 wlanIfRoamPhyMode WlanIfPhyMode) + (2 wlanIfRoamRxSignalStrength INTEGER32 GET) + (3 wlanIfRoamTxRateThreshold INTEGER32 GET) + )) + (7 wlanIfTxParamsTable + (1 wlanIfTxParamsEntry : OCTETSTRING WlanIfPhyMode op_wlan_tx_params + (1 wlanIfTxPhyMode WlanIfPhyMode) + (2 wlanIfTxUnicastRate INTEGER32 GET SET) + (3 wlanIfTxMcastRate INTEGER32 GET SET) + (4 wlanIfTxMgmtRate INTEGER32 GET SET) + (5 wlanIfTxMaxRetryCount INTEGER32 GET SET) + )) + ) + (2 begemotWlanScanning + (1 wlanScanConfigTable + (1 wlanScanConfigEntry : OCTETSTRING op_wlan_scan_config + (1 wlanScanFlags WlanScanFlagsType GET SET) + (2 wlanScanDuration INTEGER GET SET) + (3 wlanScanMinChannelDwellTime INTEGER32 GET SET) + (4 wlanScanMaxChannelDwellTime INTEGER32 GET SET) + (5 wlanScanConfigStatus ENUM ( 0 unknown 1 notStarted 2 running 3 finished 4 cancel ) GET SET) + )) + (2 wlanScanResultsTable + (1 wlanScanResultsEntry : OCTETSTRING OCTETSTRING OCTETSTRING | MacAddress op_wlan_scan_results + (1 wlanScanResultID OCTETSTRING GET) + (2 wlanScanResultBssid OCTETSTRING | MacAddress GET) + (3 wlanScanResultChannel INTEGER32 GET) + (4 wlanScanResultRate INTEGER32 GET) + (5 wlanScanResultNoise INTEGER32 GET) + (6 wlanScanResultBeaconInterval INTEGER32 GET) + (7 wlanScanResultCapabilities WlanPeerCapabilityFlags GET) + )) + ) + (3 begemotWlanStatistics + (1 wlanIfaceStatisticsTable + (1 wlanIfaceStatisticsEntry : OCTETSTRING op_wlan_iface_stats + (1 wlanStatsRxBadVersion COUNTER GET) + (2 wlanStatsRxTooShort COUNTER GET) + (3 wlanStatsRxWrongBssid COUNTER GET) + (4 wlanStatsRxDiscardedDups COUNTER GET) + (5 wlanStatsRxWrongDir COUNTER GET) + (6 wlanStatsRxDiscardMcastEcho COUNTER GET) + (7 wlanStatsRxDiscardNoAssoc COUNTER GET) + (8 wlanStatsRxWepNoPrivacy COUNTER GET) + (9 wlanStatsRxWepUnencrypted COUNTER GET) + (10 wlanStatsRxWepFailed COUNTER GET) + (11 wlanStatsRxDecapsulationFailed COUNTER GET) + (12 wlanStatsRxDiscardMgmt COUNTER GET) + (13 wlanStatsRxControl COUNTER GET) + (14 wlanStatsRxBeacon COUNTER GET) + (15 wlanStatsRxRateSetTooBig COUNTER GET) + (16 wlanStatsRxElemMissing COUNTER GET) + (17 wlanStatsRxElemTooBig COUNTER GET) + (18 wlanStatsRxElemTooSmall COUNTER GET) + (19 wlanStatsRxElemUnknown COUNTER GET) + (20 wlanStatsRxChannelMismatch COUNTER GET) + (21 wlanStatsRxDropped COUNTER GET) + (22 wlanStatsRxSsidMismatch COUNTER GET) + (23 wlanStatsRxAuthNotSupported COUNTER GET) + (24 wlanStatsRxAuthFailed COUNTER GET) + (25 wlanStatsRxAuthCM COUNTER GET) + (26 wlanStatsRxAssocWrongBssid COUNTER GET) + (27 wlanStatsRxAssocNoAuth COUNTER GET) + (28 wlanStatsRxAssocCapMismatch COUNTER GET) + (29 wlanStatsRxAssocNoRateMatch COUNTER GET) + (30 wlanStatsRxBadWpaIE COUNTER GET) + (31 wlanStatsRxDeauthenticate COUNTER GET) + (32 wlanStatsRxDisassociate COUNTER GET) + (33 wlanStatsRxUnknownSubtype COUNTER GET) + (34 wlanStatsRxFailedNoBuf COUNTER GET) + (35 wlanStatsRxBadAuthRequest COUNTER GET) + (36 wlanStatsRxUnAuthorized COUNTER GET) + (37 wlanStatsRxBadKeyId COUNTER GET) + (38 wlanStatsRxCCMPSeqViolation COUNTER GET) + (39 wlanStatsRxCCMPBadFormat COUNTER GET) + (40 wlanStatsRxCCMPFailedMIC COUNTER GET) + (41 wlanStatsRxTKIPSeqViolation COUNTER GET) + (42 wlanStatsRxTKIPBadFormat COUNTER GET) + (43 wlanStatsRxTKIPFailedMIC COUNTER GET) + (44 wlanStatsRxTKIPFailedICV COUNTER GET) + (45 wlanStatsRxDiscardACL COUNTER GET) + (46 wlanStatsTxFailedNoBuf COUNTER GET) + (47 wlanStatsTxFailedNoNode COUNTER GET) + (48 wlanStatsTxUnknownMgmt COUNTER GET) + (49 wlanStatsTxBadCipher COUNTER GET) + (50 wlanStatsTxNoDefKey COUNTER GET) + (51 wlanStatsTxFragmented COUNTER GET) + (52 wlanStatsTxFragmentsCreated COUNTER GET) + (53 wlanStatsActiveScans COUNTER GET) + (54 wlanStatsPassiveScans COUNTER GET) + (55 wlanStatsTimeoutInactivity COUNTER GET) + (56 wlanStatsCryptoNoMem COUNTER GET) + (57 wlanStatsSwCryptoTKIP COUNTER GET) + (58 wlanStatsSwCryptoTKIPEnMIC COUNTER GET) + (59 wlanStatsSwCryptoTKIPDeMIC COUNTER GET) + (60 wlanStatsCryptoTKIPCM COUNTER GET) + (61 wlanStatsSwCryptoCCMP COUNTER GET) + (62 wlanStatsSwCryptoWEP COUNTER GET) + (63 wlanStatsCryptoCipherKeyRejected COUNTER GET) + (64 wlanStatsCryptoNoKey COUNTER GET) + (65 wlanStatsCryptoDeleteKeyFailed COUNTER GET) + (66 wlanStatsCryptoUnknownCipher COUNTER GET) + (67 wlanStatsCryptoAttachFailed COUNTER GET) + (68 wlanStatsCryptoKeyFailed COUNTER GET) + (69 wlanStatsCryptoEnMICFailed COUNTER GET) + (70 wlanStatsIBSSCapMismatch COUNTER GET) + (71 wlanStatsUnassocStaPSPoll COUNTER GET) + (72 wlanStatsBadAidPSPoll COUNTER GET) + (73 wlanStatsEmptyPSPoll COUNTER GET) + (74 wlanStatsRxFFBadHdr COUNTER GET) + (75 wlanStatsRxFFTooShort COUNTER GET) + (76 wlanStatsRxFFSplitError COUNTER GET) + (77 wlanStatsRxFFDecap COUNTER GET) + (78 wlanStatsTxFFEncap COUNTER GET) + (79 wlanStatsRxBadBintval COUNTER GET) + (80 wlanStatsRxDemicFailed COUNTER GET) + (81 wlanStatsRxDefragFailed COUNTER GET) + (82 wlanStatsRxMgmt COUNTER GET) + (83 wlanStatsRxActionMgmt COUNTER GET) + (84 wlanStatsRxAMSDUTooShort COUNTER GET) + (85 wlanStatsRxAMSDUSplitError COUNTER GET) + (86 wlanStatsRxAMSDUDecap COUNTER GET) + (87 wlanStatsTxAMSDUEncap COUNTER GET) + (88 wlanStatsAMPDUBadBAR COUNTER GET) + (89 wlanStatsAMPDUOowBar COUNTER GET) + (90 wlanStatsAMPDUMovedBAR COUNTER GET) + (91 wlanStatsAMPDURxBAR COUNTER GET) + (92 wlanStatsAMPDURxOor COUNTER GET) + (93 wlanStatsAMPDURxCopied COUNTER GET) + (94 wlanStatsAMPDURxDropped COUNTER GET) + (95 wlanStatsTxDiscardBadState COUNTER GET) + (96 wlanStatsTxFailedNoAssoc COUNTER GET) + (97 wlanStatsTxClassifyFailed COUNTER GET) + (98 wlanStatsDwdsMcastDiscard COUNTER GET) + (99 wlanStatsHTAssocRejectNoHT COUNTER GET) + (100 wlanStatsHTAssocDowngrade COUNTER GET) + (101 wlanStatsHTAssocRateMismatch COUNTER GET) + (102 wlanStatsAMPDURxAge COUNTER GET) + (103 wlanStatsAMPDUMoved COUNTER GET) + (104 wlanStatsADDBADisabledReject COUNTER GET) + (105 wlanStatsADDBANoRequest COUNTER GET) + (106 wlanStatsADDBABadToken COUNTER GET) + (107 wlanStatsADDBABadPolicy COUNTER GET) + (108 wlanStatsAMPDUStopped COUNTER GET) + (109 wlanStatsAMPDUStopFailed COUNTER GET) + (110 wlanStatsAMPDURxReorder COUNTER GET) + (111 wlanStatsScansBackground COUNTER GET) + (112 wlanLastDeauthReason WlanMgmtReasonCode GET) + (113 wlanLastDissasocReason WlanMgmtReasonCode GET) + (114 wlanLastAuthFailReason WlanMgmtReasonCode GET) + (115 wlanStatsBeaconMissedEvents COUNTER GET) + (116 wlanStatsRxDiscardBadStates COUNTER GET) + (117 wlanStatsFFFlushed COUNTER GET) + (118 wlanStatsTxControlFrames COUNTER GET) + (119 wlanStatsAMPDURexmt COUNTER GET) + (120 wlanStatsAMPDURexmtFailed COUNTER GET) + (121 wlanStatsReset ENUM ( 1 no-op 2 clear ) GET SET) + )) + ) + (4 begemotWlanWep + (1 wlanWepInterfaceTable + (1 wlanWepInterfaceEntry : OCTETSTRING op_wlan_wep_iface + (1 wlanWepMode ENUM ( 0 off 1 on 2 mixed ) GET SET) + (2 wlanWepDefTxKey INTEGER32 GET SET) + )) + (2 wlanWepKeyTable + (1 wlanWepKeyEntry : OCTETSTRING INTEGER op_wlan_wep_key + (1 wlanWepKeyID INTEGER GET SET) + (2 wlanWepKeyLength INTEGER32 GET) + (3 wlanWepKeySet OCTETSTRING | OctetString GET SET) + (4 wlanWepKeyHash OCTETSTRING | OctetString GET) + (5 wlanWepKeyStatus RowStatus GET SET) + )) + ) + (5 begemotWlanMACAccessControl + (1 wlanMACAccessControlTable + (1 wlanMACAccessControlEntry : OCTETSTRING op_wlan_mac_access_control + (1 wlanMACAccessControlPolicy ENUM ( 0 open 1 allow 2 deny 7 radius ) GET SET) + (2 wlanMACAccessControlNacl COUNTER GET) + (3 wlanMACAccessControlFlush ENUM ( 0 no-op 1 flush ) GET SET) + )) + (2 wlanMACAccessControlMACTable + (1 wlanMACAccessControlMACEntry : OCTETSTRING OCTETSTRING | MacAddress op_wlan_mac_acl_mac + (1 wlanMACAccessControlMAC OCTETSTRING | MacAddress GET SET) + (2 wlanMACAccessControlMACStatus RowStatus GET SET) + )) + ) + (6 begemotWlanMeshRouting + (1 wlanMeshRoutingConfig + (1 wlanMeshMaxRetries INTEGER32 op_wlan_mesh_config GET SET) + (2 wlanMeshConfirmTimeout INTEGER32 op_wlan_mesh_config GET SET) + (3 wlanMeshHoldingTimeout INTEGER32 op_wlan_mesh_config GET SET) + (4 wlanMeshRetryTimeout INTEGER32 op_wlan_mesh_config GET SET) + ) + (2 wlanMeshInterface + (1 wlanMeshInterfaceTable + (1 wlanMeshInterfaceEntry : OCTETSTRING op_wlan_mesh_iface + (1 wlanMeshId OCTETSTRING GET SET) + (2 wlanMeshTTL INTEGER32 GET SET) + (3 wlanMeshPeeringEnabled ENUM ( 1 true 2 false ) GET SET) + (4 wlanMeshForwardingEnabled ENUM ( 1 true 2 false ) GET SET) + (5 wlanMeshMetric ENUM ( 0 unknown 1 airtime ) GET SET) + (6 wlanMeshPath ENUM ( 0 unknown 1 hwmp ) GET SET) + (7 wlanMeshRoutesFlush ENUM ( 0 no-op 1 flush ) GET SET) + )) + (2 wlanMeshNeighborTable + (1 wlanMeshNeighborEntry : OCTETSTRING OCTETSTRING | MacAddress op_wlan_mesh_neighbor + (1 wlanMeshNeighborAddress OCTETSTRING | MacAddress GET) + (2 wlanMeshNeighborFrequency INTEGER32 GET) + (3 wlanMeshNeighborLocalId INTEGER32 GET) + (4 wlanMeshNeighborPeerId INTEGER32 GET) + (5 wlanMeshNeighborPeerState WlanMeshNeighborPeerStateType GET) + (6 wlanMeshNeighborCurrentTXRate INTEGER32 GET) + (7 wlanMeshNeighborRxSignalStrength INTEGER32 GET) + (8 wlanMeshNeighborIdleTimer INTEGER32 GET) + (9 wlanMeshNeighborTxSequenceNo INTEGER32 GET) + (10 wlanMeshNeighborRxSequenceNo INTEGER32 GET) + )) + ) + (3 wlanMeshRoute + (1 wlanMeshRouteTable + (1 wlanMeshRouteEntry : OCTETSTRING OCTETSTRING | MacAddress op_wlan_mesh_route + (1 wlanMeshRouteDestination OCTETSTRING | MacAddress GET SET) + (2 wlanMeshRouteNextHop OCTETSTRING | MacAddress GET) + (3 wlanMeshRouteHops INTEGER32 GET) + (4 wlanMeshRouteMetric UNSIGNED32 GET) + (5 wlanMeshRouteLifeTime UNSIGNED32 GET) + (6 wlanMeshRouteLastMseq UNSIGNED32 GET) + (7 wlanMeshRouteFlags BITS ( 1 valid 2 proxy ) GET) + (8 wlanMeshRouteStatus RowStatus GET SET) + )) + ) + (4 wlanMeshStatistics + (1 wlanMeshStatsTable + (1 wlanMeshStatsEntry : OCTETSTRING op_wlan_mesh_stats + (1 wlanMeshDroppedBadSta COUNTER GET) + (2 wlanMeshDroppedNoLink COUNTER GET) + (3 wlanMeshNoFwdTtl COUNTER GET) + (4 wlanMeshNoFwdBuf COUNTER GET) + (5 wlanMeshNoFwdTooShort COUNTER GET) + (6 wlanMeshNoFwdDisabled COUNTER GET) + (7 wlanMeshNoFwdPathUnknown COUNTER GET) + (8 wlanMeshDroppedBadAE COUNTER GET) + (9 wlanMeshRouteAddFailed COUNTER GET) + (10 wlanMeshDroppedNoProxy COUNTER GET) + (11 wlanMeshDroppedMisaligned COUNTER GET) + )) + ) + (5 wlanMeshRouteProtocols + (1 wlanMeshProtoHWMP + (1 wlanMeshHWMPConfig + (1 wlanHWMPRouteInactiveTimeout INTEGER32 op_wlan_hwmp_config GET SET) + (2 wlanHWMPRootAnnounceInterval INTEGER32 op_wlan_hwmp_config GET SET) + (3 wlanHWMPRootInterval INTEGER32 op_wlan_hwmp_config GET SET) + (4 wlanHWMPRootTimeout INTEGER32 op_wlan_hwmp_config GET SET) + (5 wlanHWMPPathLifetime INTEGER32 op_wlan_hwmp_config GET SET) + (6 wlanHWMPReplyForwardBit INTEGER32 op_wlan_hwmp_config GET SET) + (7 wlanHWMPTargetOnlyBit INTEGER32 op_wlan_hwmp_config GET SET) + ) + (2 wlanMeshHWMPInterface + (1 wlanHWMPInterfaceTable + (1 wlanHWMPInterfaceEntry : OCTETSTRING op_wlan_hwmp_iface + (1 wlanHWMPRootMode ENUM ( 1 disabled 2 normal 3 proactive 4 rann ) GET SET) + (2 wlanHWMPMaxHops INTEGER32 GET SET) + )) + ) + (3 wlanMeshHWMPStatistics + (1 wlanMeshHWMPStatsTable + (1 wlanMeshHWMPStatsEntry : OCTETSTRING op_wlan_hwmp_stats + (1 wlanMeshHWMPWrongSeqNo COUNTER GET) + (2 wlanMeshHWMPTxRootPREQ COUNTER GET) + (3 wlanMeshHWMPTxRootRANN COUNTER GET) + (4 wlanMeshHWMPProxy COUNTER GET) + )) + ) + ) + ) + )))))) +) diff --git a/usr.sbin/bsnmpd/tools/Makefile b/usr.sbin/bsnmpd/tools/Makefile new file mode 100644 index 0000000..3ffc01e --- /dev/null +++ b/usr.sbin/bsnmpd/tools/Makefile @@ -0,0 +1,7 @@ +# $FreeBSD$ +# Author: Shteryana Shopova <syrinx@FreeBSD.org> + +SUBDIR= libbsnmptools \ + bsnmptools + +.include <bsd.subdir.mk> diff --git a/usr.sbin/bsnmpd/tools/Makefile.inc b/usr.sbin/bsnmpd/tools/Makefile.inc new file mode 100644 index 0000000..e08fe26 --- /dev/null +++ b/usr.sbin/bsnmpd/tools/Makefile.inc @@ -0,0 +1,13 @@ +# $FreeBSD$ +# Author: Shteryana Shopova <syrinx@FreeBSD.org> + +BINDIR?= /usr/bin + +CFLAGS+= -I. -I${.CURDIR} + +.if exists(${.OBJDIR}/../libbsnmptools) +LIBBSNMPTOOLSDIR= ${.OBJDIR}/../libbsnmptools +.else +LIBBSNMPTOOLSDIR= ${.CURDIR}/../libbsnmptools +.endif +LIBBSNMPTOOLS= ${LIBBSNMPTOOLSDIR}/libbsnmptools.a diff --git a/usr.sbin/bsnmpd/tools/bsnmptools/Makefile b/usr.sbin/bsnmpd/tools/bsnmptools/Makefile new file mode 100644 index 0000000..94a1cea --- /dev/null +++ b/usr.sbin/bsnmpd/tools/bsnmptools/Makefile @@ -0,0 +1,28 @@ +# $FreeBSD$ +# Author: Shteryana Shopova <syrinx@FreeBSD.org> + +.include <bsd.own.mk> + +.PATH: ${.CURDIR} + +PROG= bsnmpget + +DPADD+= ${LIBBSNMP} ${LIBBSNMPTOOLS} +LDADD+= -lbsnmp -lbsnmptools +CFLAGS+= -I${.CURDIR}/../libbsnmptools +LDFLAGS+= -L${LIBBSNMPTOOLSDIR} + +.if ${MK_OPENSSL} != "no" +DPADD+= ${LIBCRYPTO} +LDADD+= -lcrypto +.endif + +LINKS= ${BINDIR}/bsnmpget ${BINDIR}/bsnmpwalk +LINKS+= ${BINDIR}/bsnmpget ${BINDIR}/bsnmpset + +MAN= bsnmpget.1 + +MLINKS= bsnmpget.1 bsnmpwalk.1 +MLINKS+= bsnmpget.1 bsnmpset.1 + +.include <bsd.prog.mk> diff --git a/usr.sbin/bsnmpd/tools/bsnmptools/bsnmpget.1 b/usr.sbin/bsnmpd/tools/bsnmptools/bsnmpget.1 new file mode 100644 index 0000000..57a85d6 --- /dev/null +++ b/usr.sbin/bsnmpd/tools/bsnmptools/bsnmpget.1 @@ -0,0 +1,426 @@ +.\" +.\" Copyright (c) 2010 The FreeBSD Foundation +.\" All rights reserved. +.\" +.\" Portions of this documentation were written by Shteryana Sotirova Shopova +.\" under sponsorship from the FreeBSD Foundation. +.\" +.\" Copyright (c) 2005-2007 The FreeBSD Project. +.\" All rights reserved. +.\" +.\" Author: Shteryana Shopova <syrinx@FreeBSD.org> +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd January 10, 2012 +.Dt BSNMPGET 1 +.Os +.Sh NAME +.Nm bsnmpget , +.Nm bsnmpwalk , +.Nm bsnmpset +.Nd "simple tools for querying SNMP agents" +.Sh SYNOPSIS +.Nm +.Op Fl aDdehnK +.Op Fl A Ar options +.Op Fl b Ar buffersize +.Op Fl C Ar options +.Op Fl I Ar options +.Op Fl i Ar filelist +.Op Fl l Ar filename +.Op Fl M Ar max-repetitions +.Op Fl N Ar non-repeaters +.Op Fl o Ar output +.Op Fl P Ar options +.Op Fl p Ar pdu +.Op Fl r Ar retries +.Op Fl s Ar [trans::][community@][server][:port] +.Op Fl t Ar timeout +.Op Fl U Ar options +.Op Fl v Ar version +.Op Ar OID ... +.Pp +.Nm bsnmpwalk +.Op Fl dhnK +.Op Fl A Ar options +.Op Fl b Ar buffersize +.Op Fl C Ar options +.Op Fl I Ar options +.Op Fl i Ar filelist +.Op Fl l Ar filename +.Op Fl o Ar output +.Op Fl P Ar options +.Op Fl r Ar retries +.Op Fl s Ar [trans::][community@][server][:port] +.Op Fl t Ar timeout +.Op Fl U Ar options +.Op Fl v Ar version +.Op Ar OID ... +.Pp +.Nm bsnmpset +.Op Fl adehnK +.Op Fl A Ar options +.Op Fl b Ar buffersize +.Op Fl C Ar options +.Op Fl I Ar options +.Op Fl i Ar filelist +.Op Fl l Ar filename +.Op Fl o Ar output +.Op Fl P Ar options +.Op Fl r Ar retries +.Op Fl s Ar [trans::][community@][server][:port] +.Op Fl t Ar timeout +.Op Fl U Ar options +.Op Fl v Ar version +.Ar OID Ns = Ar syntax Ns : Ns Ar value +.Op Ar OID Ns = Ar syntax Ns : Ns Ar value ... +.Sh DESCRIPTION +.Nm , +.Nm bsnmpwalk +and +.Nm bsnmpset +are simple tools for retrieving management information from and setting +management information to a Simple Network Management Protocol (SNMP) agent. +.Pp +Depending on the options +.Nm bsnmpget +constructs either a SMNP GetRequest, GetNextRequest +or a GetBulkRequest packet, fills in the object identifiers (OIDs) of the +objects whose values will be retrived, waits for a response and prints it if +received successfully. +.Pp +.Nm Bsnmpwalk +queries an agent with ether SMNP GetNextRequest or GetBulkRequest packets, +asking for values of OID instances that are a part of the object subtree +rooted at the provided OIDs. +.Pp +.Nm Bsnmpset +constructs a SMNP SetRequest packet, fills in the OIDs (object identifiers), +syntaxes and values of the objects whose values are to be set and waits for a +response from server. +.Sh OPTIONS +The options are as follows (not all apply to all three programs): +.Bl -tag -width ".It Fl D Ar options" +.It Fl A Ar options +Authentication options to use with SNMPv3 PDUs +.Bl -tag -width \& +.It Cm proto=[md5|sha] +The protocol to use when calculating the PDU message digest. +.It Cm key=authkey +A binary localized authentication key to use when calculating the PDU message +digest. +.El +.Pp +By default SNMPv3 PDUs are sent unauthenticated. +.It Fl a +Skip any sanity checks when adding OIDs to a Protocol Data Unit (PDU): +ingore syntax/access type, allow adding of non-leaf objects for GetPdu and +read-only objects to a SetPDU. +.It Fl b Ar buffersize +Tune the size of buffers used to send and receive packets. +The default size is 10000 bytes which should be enough unless an agent sends +a really large octetstring. +The maximum allowed length is 65535 according to the Structure of Management +Information (SMIv2). +.It Fl C Ar options +The context to query with SNMPv3 PDUs. +.Bl -tag -width \& +.It Cm context=name +The context name. Default is "" (empty). +.It Cm context-engine=engine-id +The SNMP Engine ID of the context to query with SNMPv3 PDUs, represented as +binary octet string. By default, this is set to the Engine ID of the SNMP agent. +.El +.It Fl D +Perform SNMP USM Engine Discovery, rather than sending a request for the value +of a specific object. +.It Fl d +Turn on debugging. +This option will cause the packets sent and received to be dumped to the +terminal. +.It Fl e +Retry on error. +If an error is returned in the response PDU, resend the request removing the +variable that caused the error until a valid response is received. +This is only useful for a GetRequest- and a GetNextRequest-PDU. +.It Fl h +Print a short help text with default values for various options. +.It Fl I Ar options +Load each MIB description file from the given list to translate symbolic +object names to their numerical representation and vice versa. +Use the other options to obtain a non-default behaviour: +.Bl -tag -width \& +.It Cm cut=OID +Specifies the initial OID that was cut by +.Xr gensnmpdef 1 +when producing the MIB description file. +The default value is .iso(1).org(3).dod(6) which is what should have been +used for all the files installed under /usr/share/snmp/defs. +Use this only if you generated your own files, providing a +.Fl c +option to +.Xr gensnmpdef 1 . +.It Cm path=filedir +The directory where files in the list will be searched. +The default is +.Pa /usr/share/snmp/defs Ns . +.It Cm file=filelist +A comma separated list of files to which the two options above will apply. +.El +.Pp +The file suboption has to come after the other suboptions so that their +non-default values will be applied to the list of files. +The order of the other suboptions before each file suboption can be random. +Suboptions may be separated either by commas or by spaces. +If using spaces make sure the entire option string is one argument, for +example using quotes. +.It Fl i Ar filelist +List of MIB description files produced by +.Xr gensnmpdef 1 which +.Nm bsnmpget , +.Nm bsnmpwalk +or +.Nm bsnmpset +will search to translate numerical OIDs to their symbolic object names. +Multiple files can be provided either giving this option multiple times +or a comma separated list of file names. +If a filename begins with a letter the default directory, +/usr/share/snmp/defs, +will be searched. +.It Fl K +Calculate and display the localized authentication and privacy keys +corresponding to a plain text password. The password is obtain via the +environment. Additionally, if one or more OIDs are specified, the calculated +keys are used when processing the SNMPv3 requests. +.It Fl l Ar filename +The path of the posix local (unix domain) socket if local +transport is used. +.It Fl M Ar max-repetitions +The value for the max-repetitions field in a GetBulk PDU. +Default is 10. +.It Fl N Ar non-repeaters +The value for the non-repeaters field in a GetBulk PDU. +Default is 0. +.It Fl n +Only use numerical representations for input and output OIDs and do not +try to resolve symbolic object names. +Note that +.Nm bsnmpget , +.Nm bsnmpwalk +and +.Nm bsnmpset +will print numerical OIDs anyway if the corresponding string representation +is not found in the MIB description files. +.It Fl o Ar [quiet|short|verbose] +The format used to print the received response. +Quiet only prints values, short (default) prints an abbreviated OID +representation and the value. +In addition to the short output verbose prints the type before the value. +.It Fl P Ar options +Privacy options to use with SNMPv3 PDUs +.Bl -tag -width \& +.It Cm proto=[aes|des] +The protocol to use when encypting/decrypting SNMPv3 PDU data. +.It Cm key=privkey +A binary localized privacy key to use when encypting/decrypting SNMPv3 PDU data. +.El +.Pp +By default plain text SNMPv3 PDUs are sent. +.It Fl p Ar [get|getnext|getbulk] +The PDU type to send by +.Nm bsmpget +and +.Nm bsnmpwalk . +Default is get +for +.Nm bsmpget +and getnext for +.Nm bsnmpwalk . +Getbulk allows executing the so called SNMP "bulkwalks" allowing the values of +multiple columns to be retrived in a single PDU by +.Nm bsnmpwalk . +.It Fl r Ar retries +Number of resends of request packets before giving up if the agent does +not respond after the first try. +Default is 3. +.It Fl s Ar [trans::] Ns Ar [community@] Ns Ar [server] Ns Ar [:port] +Each of the server specification components is optional but at least one +has to be provided if +.Ar s +option is used. +The server specification is constructed in the following manner: +.Bl -tag -width \& +.It Cm trans:: +Transport type may be one of udp, stream or dgram. +If this option is not provided an udp inet/inet6 socket will be used, which +is the most common. +Stream stands for a posix local stream socket and a posix local datagram +socket will be used if dgram is specified. +.It Cm community@ +Specify an SNMP community string to be used when sending packets. +If the option is skipped the default "public" will be used for +.Nm +and +.Nm bsnmpwalk +and the default "private" community string will be used for +.Nm bsnmpset . +.It Cm server +This might be either the IP address or the hostname where the agent is +listening. +The default is +.Qq localhost . +.It Cm port +The destination port to send the requests to. +This is useful if the SNMP agent listens on a non-default port. +Default is given by the +.Qq snmp +entry in +.Pa /etc/services , +port 161. +.El +.It Fl t Ar timeout +Number of seconds before resending a request packet if the agent does +not respond. +The default value is 3 seconds. +.It Fl U Ar options +User credentials when sending SNMPv3 PDUs. +.Bl -tag -width \& +.It Cm engine=id +The Engine ID of the SNMP agent represented as a binary octet string. +.It Cm engine-boots=value +The value of the snmpEngineBoots of the SNMP agent. +.It Cm engine-time=value +The value of the snmpEngineTime of the SNMP agent. +.Pp +If any of the above is not specified, SNMP USM Engine Discovery is attempted. +This is also the default behavior. +.It Cm name=username +The USM user name to include in the SNMPv3 PDUs. By default, the user name is +obtain via the environment +.El +.It Fl v Ar version +The SNMP protocol version to use when sending requests. SNMP versions 1, 2 and +3 are supported. +If no version option is provided +.Nm bsnmpget , +.Nm bsnmpwalk +and +.Nm bsnmpset +will use version 2. +Note that GetBulkRequest-PDUs were introduced in SNMPv2 thus setting the +version to 1 is incompatiable with sending a GetBulk PDU. +.It OID +The object identifier whose value to retrive. +At least one OID should be provided for +.Nm bsnmpget +to be able to send a request. +.Pp +For +.Nm bsnmpwalk +this is the root object identifier of the subtree whose values are to be +retrived. +If no OID is provided +.Nm bsnmpwalk +will walk the mib2 subtree rooted +at .iso(1).org(3).dod(6).internet(1).mgmt(2).mib2(1) . +.Pp +Any of the formats used to print a single variable +is valid as input OID: +.Bl -tag -width \& +.It 1.3.6.1.2.1.25.1.1.0 +.It sysDescr +.It ifPhysAddress.1 +.It ifRcvAddressStatus.2.6.255.255.255.255.255.255 +.It ifRcvAddressType[2,ff:ff:ff:ff:ff:ff] +.It ifRcvAddressStatus[Integer:1,OctetString:ff:ff:ff:ff:ff:ff] +(requires +.Fl o Ar verbose +option) +.El +.Pp +Square brackets are used to denote an entry's indexes. +When used in an input OID, the square brackets may have to be +escaped or the OID has to be quoted to protect it from the shell. +Note there is no difference between ifName.1 and "ifName[1]". +.It OID Ns = Ns Ar [syntax Ns :] Ns Ar value +The object identifier with its syntax type and value that is to be set. +At least one such string OID=[syntax:]value should be provided to +.Nm bsnmpset +to be able to send a request. +.Bl -tag -width \& +.It Cm OID +OID may be input as a string, a string followed by a random number of integers +(suboids) separated by dots, a sequence of integers separated by dots - that is +if +.Ar n +options is used - and in such case a syntax is required for every value, +or a string followed by square brackets (used to denote an entry's indexes) and +corresponding indexes. +Any of formats used to print a single variable by +.Nm bsnmpset is +valid for inpit OID as well: +.Bl -tag -width \& +.It 1.3.6.1.2.1.25.1.1.0=TimeTicks:537615486 +.It sysLocation=OctetString:"@ Home" (with Fl o Ar verbose No option) +.It sysLocation.0="@ Home" +.It 1.3.6.1.2.1.2.2.1.6.1=OctetString:ffffffffffff +.It ifPhysAddress.1="00:02:b3:1d:1c:a3" +.It ifRcvAddressStatus.1.6.255.255.255.255.255.255=1 +.It "ifRcvAddressStatus[Integer:1,OctetString:ff:ff:ff:ff:ff:ff]=Integer:1" +(with +.Fl o Ar verbose +option) +.El +.It Cm syntax +where syntax string is one of: +Integer, OctetString, OID, IpAddress, Counter32, Gauge, TimeTicks, Counter64. +.It Cm value +The value to be set - IP address in form of u.u.u.u - for example +1.3.1.6.1.2.0=IpAddress:192.168.0.1, strings require inverted-commas if they +contain any special characters or spaces, all other numeric types don't. +.El +.El +.Sh ENVIRONMENT +.Nm , +.Nm bsnmpwalk +and +.Nm bsnmpset +use the following environment variables: +.Bl -tag -width SNMPAUTH +.It Ev SNMPAUTH +Specifies a default SNMP USM authentication protocol. +.It Ev SNMPPRIV +Specifies a default SNMP USM privacy protocol. +.It Ev SNMPUSER +Specifies a default SNMP USM user name. +.It Ev SNMPPASSWD +Specifies the SNMP USM plain text password to use when calculating localized +authentication and privacy keys. If this variable exists in the environment, +SMNPv3 is the default version to use for outgoing requests. +.El +.Sh SEE ALSO +.Xr gensnmpdef 1 +.Sh AUTHORS +.An Shteryana Shopova Aq syrinx@FreeBSD.org diff --git a/usr.sbin/bsnmpd/tools/bsnmptools/bsnmpget.c b/usr.sbin/bsnmpd/tools/bsnmptools/bsnmpget.c new file mode 100644 index 0000000..fb0c7e5 --- /dev/null +++ b/usr.sbin/bsnmpd/tools/bsnmptools/bsnmpget.c @@ -0,0 +1,1289 @@ +/*- + * Copyright (c) 2005-2006 The FreeBSD Project + * All rights reserved. + * + * Author: Shteryana Shopova <syrinx@FreeBSD.org> + * + * Redistribution of this software and documentation and use in source and + * binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code or documentation must retain the above + * copyright notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Bsnmpget and bsnmpwalk are simple tools for querying SNMP agents, + * bsnmpset can be used to set MIB objects in an agent. + * + * $FreeBSD$ + */ + +#include <sys/queue.h> +#include <sys/types.h> + +#include <assert.h> +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <unistd.h> + +#include <bsnmp/asn1.h> +#include <bsnmp/snmp.h> +#include <bsnmp/snmpclient.h> +#include "bsnmptc.h" +#include "bsnmptools.h" + +static const char *program_name = NULL; +static enum program_e { + BSNMPGET, + BSNMPWALK, + BSNMPSET +} program; + +/* ***************************************************************************** + * Common bsnmptools functions. + */ +static void +usage(void) +{ + fprintf(stderr, +"Usage:\n" +"%s %s [-A options] [-b buffersize] [-C options] [-I options]\n" +"\t[-i filelist] [-l filename]%s [-o output] [-P options]\n" +"\t%s[-r retries] [-s [trans::][community@][server][:port]]\n" +"\t[-t timeout] [-U options] [-v version]%s\n", + program_name, + (program == BSNMPGET) ? "[-aDdehnK]" : + (program == BSNMPWALK) ? "[-dhnK]" : + (program == BSNMPSET) ? "[-adehnK]" : + "", + (program == BSNMPGET || program == BSNMPWALK) ? + " [-M max-repetitions] [-N non-repeaters]" : "", + (program == BSNMPGET || program == BSNMPWALK) ? "[-p pdu] " : "", + (program == BSNMPGET) ? " OID [OID ...]" : + (program == BSNMPWALK || program == BSNMPSET) ? " [OID ...]" : + "" + ); +} + +static int32_t +parse_max_repetitions(struct snmp_toolinfo *snmptoolctx, char *opt_arg) +{ + uint32_t v; + + assert(opt_arg != NULL); + + v = strtoul(opt_arg, (void *) NULL, 10); + + if (v > SNMP_MAX_BINDINGS) { + warnx("Max repetitions value greater than %d maximum allowed.", + SNMP_MAX_BINDINGS); + return (-1); + } + + SET_MAXREP(snmptoolctx, v); + return (2); +} + +static int32_t +parse_non_repeaters(struct snmp_toolinfo *snmptoolctx, char *opt_arg) +{ + uint32_t v; + + assert(opt_arg != NULL); + + v = strtoul(opt_arg, (void *) NULL, 10); + + if (v > SNMP_MAX_BINDINGS) { + warnx("Non repeaters value greater than %d maximum allowed.", + SNMP_MAX_BINDINGS); + return (-1); + } + + SET_NONREP(snmptoolctx, v); + return (2); +} + +static int32_t +parse_pdu_type(struct snmp_toolinfo *snmptoolctx, char *opt_arg) +{ + assert(opt_arg != NULL); + + if (strcasecmp(opt_arg, "getbulk") == 0) + SET_PDUTYPE(snmptoolctx, SNMP_PDU_GETBULK); + else if (strcasecmp(opt_arg, "getnext") == 0) + SET_PDUTYPE(snmptoolctx, SNMP_PDU_GETNEXT); + else if (strcasecmp(opt_arg, "get") == 0) + SET_PDUTYPE(snmptoolctx, SNMP_PDU_GET); + else { + warnx("PDU type '%s' not supported.", opt_arg); + return (-1); + } + + return (2); +} + +static int32_t +snmptool_parse_options(struct snmp_toolinfo *snmptoolctx, int argc, char **argv) +{ + int32_t count, optnum = 0; + int ch; + const char *opts; + + switch (program) { + case BSNMPWALK: + opts = "dhnKA:b:C:I:i:l:M:N:o:P:p:r:s:t:U:v:"; + break; + case BSNMPGET: + opts = "aDdehnKA:b:C:I:i:l:M:N:o:P:p:r:s:t:U:v:"; + break; + case BSNMPSET: + opts = "adehnKA:b:C:I:i:l:o:P:r:s:t:U:v:"; + break; + default: + return (-1); + } + + while ((ch = getopt(argc, argv, opts)) != EOF) { + switch (ch) { + case 'A': + count = parse_authentication(snmptoolctx, optarg); + break; + case 'a': + count = parse_skip_access(snmptoolctx); + break; + case 'b': + count = parse_buflen(optarg); + break; + case 'D': + count = parse_discovery(snmptoolctx); + break; + case 'd': + count = parse_debug(); + break; + case 'e': + count = parse_errors(snmptoolctx); + break; + case 'h': + usage(); + return (-2); + case 'C': + count = parse_context(snmptoolctx, optarg); + break; + case 'I': + count = parse_include(snmptoolctx, optarg); + break; + case 'i': + count = parse_file(snmptoolctx, optarg); + break; + case 'K': + count = parse_local_key(snmptoolctx); + break; + case 'l': + count = parse_local_path(optarg); + break; + case 'M': + count = parse_max_repetitions(snmptoolctx, optarg); + break; + case 'N': + count = parse_non_repeaters(snmptoolctx, optarg); + break; + case 'n': + count = parse_num_oids(snmptoolctx); + break; + case 'o': + count = parse_output(snmptoolctx, optarg); + break; + case 'P': + count = parse_privacy(snmptoolctx, optarg); + break; + case 'p': + count = parse_pdu_type(snmptoolctx, optarg); + break; + case 'r': + count = parse_retry(optarg); + break; + case 's': + count = parse_server(optarg); + break; + case 't': + count = parse_timeout(optarg); + break; + case 'U': + count = parse_user_security(snmptoolctx, optarg); + break; + case 'v': + count = parse_version(optarg); + break; + case '?': + default: + usage(); + return (-1); + } + if (count < 0) + return (-1); + optnum += count; + } + + return (optnum); +} + +/* + * Read user input OID - one of following formats: + * 1) 1.2.1.1.2.1.0 - that is if option numeric was given; + * 2) string - in such case append .0 to the asn_oid subs; + * 3) string.1 - no additional processing required in such case. + */ +static char * +snmptools_parse_stroid(struct snmp_toolinfo *snmptoolctx, + struct snmp_object *obj, char *argv) +{ + char string[MAXSTR], *str; + int32_t i = 0; + struct asn_oid in_oid; + + str = argv; + + if (*str == '.') + str++; + + while (isalpha(*str) || *str == '_' || (i != 0 && isdigit(*str))) { + str++; + i++; + } + + if (i <= 0 || i >= MAXSTR) + return (NULL); + + memset(&in_oid, 0, sizeof(struct asn_oid)); + if ((str = snmp_parse_suboid((argv + i), &in_oid)) == NULL) { + warnx("Invalid OID - %s", argv); + return (NULL); + } + + strlcpy(string, argv, i + 1); + if (snmp_lookup_oidall(snmptoolctx, obj, string) < 0) { + warnx("No entry for %s in mapping lists", string); + return (NULL); + } + + /* If OID given on command line append it. */ + if (in_oid.len > 0) + asn_append_oid(&(obj->val.var), &in_oid); + else if (*str == '[') { + if ((str = snmp_parse_index(snmptoolctx, str + 1, obj)) == NULL) + return (NULL); + } else if (obj->val.syntax > 0 && GET_PDUTYPE(snmptoolctx) == + SNMP_PDU_GET) { + if (snmp_suboid_append(&(obj->val.var), (asn_subid_t) 0) < 0) + return (NULL); + } + + return (str); +} + +static int32_t +snmptools_parse_oid(struct snmp_toolinfo *snmptoolctx, + struct snmp_object *obj, char *argv) +{ + if (argv == NULL) + return (-1); + + if (ISSET_NUMERIC(snmptoolctx)) { + if (snmp_parse_numoid(argv, &(obj->val.var)) < 0) + return (-1); + } else { + if (snmptools_parse_stroid(snmptoolctx, obj, argv) == NULL && + snmp_parse_numoid(argv, &(obj->val.var)) < 0) + return (-1); + } + + return (1); +} + +static int32_t +snmptool_add_vbind(struct snmp_pdu *pdu, struct snmp_object *obj) +{ + if (obj->error > 0) + return (0); + + asn_append_oid(&(pdu->bindings[pdu->nbindings].var), &(obj->val.var)); + pdu->nbindings++; + + return (pdu->nbindings); +} + +/* ***************************************************************************** + * bsnmpget private functions. + */ +static int32_t +snmpget_verify_vbind(struct snmp_toolinfo *snmptoolctx, struct snmp_pdu *pdu, + struct snmp_object *obj) +{ + if (pdu->version == SNMP_V1 && obj->val.syntax == + SNMP_SYNTAX_COUNTER64) { + warnx("64-bit counters are not supported in SNMPv1 PDU"); + return (-1); + } + + if (ISSET_NUMERIC(snmptoolctx) || pdu->type == SNMP_PDU_GETNEXT || + pdu->type == SNMP_PDU_GETBULK) + return (1); + + if (pdu->type == SNMP_PDU_GET && obj->val.syntax == SNMP_SYNTAX_NULL) { + warnx("Only leaf object values can be added to GET PDU"); + return (-1); + } + + return (1); +} + +/* + * In case of a getbulk PDU, the error_status and error_index fields are used by + * libbsnmp to hold the values of the non-repeaters and max-repetitions fields + * that are present only in the getbulk - so before sending the PDU make sure + * these have correct values as well. + */ +static void +snmpget_fix_getbulk(struct snmp_pdu *pdu, uint32_t max_rep, uint32_t non_rep) +{ + assert(pdu != NULL); + + if (pdu->nbindings < non_rep) + pdu->error_status = pdu->nbindings; + else + pdu->error_status = non_rep; + + if (max_rep > 0) + pdu->error_index = max_rep; + else + pdu->error_index = 1; +} + +static int +snmptool_get(struct snmp_toolinfo *snmptoolctx) +{ + struct snmp_pdu req, resp; + + snmp_pdu_create(&req, GET_PDUTYPE(snmptoolctx)); + + while ((snmp_pdu_add_bindings(snmptoolctx, snmpget_verify_vbind, + snmptool_add_vbind, &req, SNMP_MAX_BINDINGS)) > 0) { + + if (GET_PDUTYPE(snmptoolctx) == SNMP_PDU_GETBULK) + snmpget_fix_getbulk(&req, GET_MAXREP(snmptoolctx), + GET_NONREP(snmptoolctx)); + + if (snmp_dialog(&req, &resp) == -1) { + warnx("Snmp dialog - %s", strerror(errno)); + break; + } + + if (snmp_parse_resp(&resp, &req) >= 0) { + snmp_output_resp(snmptoolctx, &resp, NULL); + break; + } + + snmp_output_err_resp(snmptoolctx, &resp); + if (GET_PDUTYPE(snmptoolctx) == SNMP_PDU_GETBULK || + !ISSET_RETRY(snmptoolctx)) + break; + + /* + * Loop through the object list and set object->error to the + * varbinding that caused the error. + */ + if (snmp_object_seterror(snmptoolctx, + &(resp.bindings[resp.error_index - 1]), + resp.error_status) <= 0) + break; + + fprintf(stderr, "Retrying...\n"); + snmp_pdu_free(&resp); + snmp_pdu_create(&req, GET_PDUTYPE(snmptoolctx)); + } + + snmp_pdu_free(&resp); + + return (0); +} + + +/* ***************************************************************************** + * bsnmpwalk private functions. + */ +/* The default tree to walk. */ +static const struct asn_oid snmp_mibII_OID = { + 6 , { 1, 3, 6, 1, 2, 1 } +}; + +static int32_t +snmpwalk_add_default(struct snmp_toolinfo *snmptoolctx __unused, + struct snmp_object *obj, char *string __unused) +{ + asn_append_oid(&(obj->val.var), &snmp_mibII_OID); + return (1); +} + +/* + * Prepare the next GetNext/Get PDU to send. + */ +static void +snmpwalk_nextpdu_create(uint32_t op, struct asn_oid *var, struct snmp_pdu *pdu) +{ + snmp_pdu_create(pdu, op); + asn_append_oid(&(pdu->bindings[0].var), var); + pdu->nbindings = 1; +} + +static int +snmptool_walk(struct snmp_toolinfo *snmptoolctx) +{ + struct snmp_pdu req, resp; + struct asn_oid root; /* Keep the initial oid. */ + int32_t outputs, rc; + uint32_t op; + + if (GET_PDUTYPE(snmptoolctx) == SNMP_PDU_GETBULK) + op = SNMP_PDU_GETBULK; + else + op = SNMP_PDU_GETNEXT; + + snmp_pdu_create(&req, op); + + while ((rc = snmp_pdu_add_bindings(snmptoolctx, NULL, + snmptool_add_vbind, &req, 1)) > 0) { + + /* Remember the root where the walk started from. */ + memset(&root, 0, sizeof(struct asn_oid)); + asn_append_oid(&root, &(req.bindings[0].var)); + + if (op == SNMP_PDU_GETBULK) + snmpget_fix_getbulk(&req, GET_MAXREP(snmptoolctx), + GET_NONREP(snmptoolctx)); + + outputs = 0; + while (snmp_dialog(&req, &resp) >= 0) { + if ((snmp_parse_resp(&resp, &req)) < 0) { + snmp_output_err_resp(snmptoolctx, &resp); + snmp_pdu_free(&resp); + outputs = -1; + break; + } + + rc = snmp_output_resp(snmptoolctx, &resp, &root); + if (rc < 0) { + snmp_pdu_free(&resp); + outputs = -1; + break; + } + + outputs += rc; + snmp_pdu_free(&resp); + + if (rc < resp.nbindings) + break; + + snmpwalk_nextpdu_create(op, + &(resp.bindings[resp.nbindings - 1].var), &req); + if (op == SNMP_PDU_GETBULK) + snmpget_fix_getbulk(&req, GET_MAXREP(snmptoolctx), + GET_NONREP(snmptoolctx)); + } + + /* Just in case our root was a leaf. */ + if (outputs == 0) { + snmpwalk_nextpdu_create(SNMP_PDU_GET, &root, &req); + if (snmp_dialog(&req, &resp) == SNMP_CODE_OK) { + if (snmp_parse_resp(&resp,&req) < 0) + snmp_output_err_resp(snmptoolctx, &resp); + else + snmp_output_resp(snmptoolctx, &(resp), NULL); + + snmp_pdu_free(&resp); + } else + warnx("Snmp dialog - %s", strerror(errno)); + } + + if (snmp_object_remove(snmptoolctx, &root) < 0) { + warnx("snmp_object_remove"); + break; + } + + snmp_pdu_create(&req, op); + } + + if (rc == 0) + return (0); + else + return (1); +} + +/* ***************************************************************************** + * bsnmpset private functions. + */ + +static int32_t +parse_oid_numeric(struct snmp_value *value, char *val) +{ + char *endptr; + int32_t saved_errno; + asn_subid_t suboid; + + do { + saved_errno = errno; + errno = 0; + suboid = strtoul(val, &endptr, 10); + if (errno != 0) { + warnx("Value %s not supported - %s", val, + strerror(errno)); + errno = saved_errno; + return (-1); + } + errno = saved_errno; + if ((asn_subid_t) suboid > ASN_MAXID) { + warnx("Suboid %u > ASN_MAXID", suboid); + return (-1); + } + if (snmp_suboid_append(&(value->v.oid), suboid) < 0) + return (-1); + val = endptr + 1; + } while (*endptr == '.'); + + if (*endptr != '\0') + warnx("OID value %s not supported", val); + + value->syntax = SNMP_SYNTAX_OID; + return (0); +} + +/* + * Allow OID leaf in both forms: + * 1) 1.3.6.1.2... -> in such case call directly the function reading raw OIDs; + * 2) begemotSnmpdAgentFreeBSD -> lookup the ASN OID corresponding to that. + */ +static int32_t +parse_oid_string(struct snmp_toolinfo *snmptoolctx, + struct snmp_value *value, char *string) +{ + struct snmp_object obj; + + if (isdigit(string[0])) + return (parse_oid_numeric(value, string)); + + memset(&obj, 0, sizeof(struct snmp_object)); + if (snmp_lookup_enumoid(snmptoolctx, &obj, string) < 0) { + warnx("Unknown OID enum string - %s", string); + return (-1); + } + + asn_append_oid(&(value->v.oid), &(obj.val.var)); + return (1); +} + +static int32_t +parse_ip(struct snmp_value * value, char * val) +{ + uint32_t v; + int32_t i; + char *endptr, *str; + + str = val; + for (i = 0; i < 4; i++) { + v = strtoul(str, &endptr, 10); + if (v > 0xff) + return (-1); + if (*endptr != '.' && *endptr != '\0' && i != 3) + break; + str = endptr + 1; + value->v.ipaddress[i] = (uint8_t) v; + } + + value->syntax = SNMP_SYNTAX_IPADDRESS; + return (0); +} + +static int32_t +parse_int(struct snmp_value *value, char *val) +{ + char *endptr; + int32_t v, saved_errno; + + saved_errno = errno; + errno = 0; + + v = strtol(val, &endptr, 10); + + if (errno != 0) { + warnx("Value %s not supported - %s", val, strerror(errno)); + errno = saved_errno; + return (-1); + } + + value->syntax = SNMP_SYNTAX_INTEGER; + value->v.integer = v; + errno = saved_errno; + + return (0); +} + +static int32_t +parse_int_string(struct snmp_object *object, char *val) +{ + int32_t v; + + if (isdigit(val[0])) + return ((parse_int(&(object->val), val))); + + if (object->info == NULL) { + warnx("Unknown enumerated integer type - %s", val); + return (-1); + } + if ((v = enum_number_lookup(object->info->snmp_enum, val)) < 0) + warnx("Unknown enumerated integer type - %s", val); + + object->val.v.integer = v; + return (1); +} + +/* + * Here syntax may be one of SNMP_SYNTAX_COUNTER, SNMP_SYNTAX_GAUGE, + * SNMP_SYNTAX_TIMETICKS. + */ +static int32_t +parse_uint(struct snmp_value *value, char *val) +{ + char *endptr; + uint32_t v = 0; + int32_t saved_errno; + + saved_errno = errno; + errno = 0; + + v = strtoul(val, &endptr, 10); + + if (errno != 0) { + warnx("Value %s not supported - %s", val, strerror(errno)); + errno = saved_errno; + return (-1); + } + + value->v.uint32 = v; + errno = saved_errno; + + return (0); +} + +static int32_t +parse_ticks(struct snmp_value *value, char *val) +{ + if (parse_uint(value, val) < 0) + return (-1); + + value->syntax = SNMP_SYNTAX_TIMETICKS; + return (0); +} + +static int32_t +parse_gauge(struct snmp_value *value, char *val) +{ + if (parse_uint(value, val) < 0) + return (-1); + + value->syntax = SNMP_SYNTAX_GAUGE; + return (0); +} + +static int32_t +parse_counter(struct snmp_value *value, char *val) +{ + if (parse_uint(value, val) < 0) + return (-1); + + value->syntax = SNMP_SYNTAX_COUNTER; + return (0); +} + +static int32_t +parse_uint64(struct snmp_value *value, char *val) +{ + char *endptr; + int32_t saved_errno; + uint64_t v; + + saved_errno = errno; + errno = 0; + + v = strtoull(val, &endptr, 10); + + if (errno != 0) { + warnx("Value %s not supported - %s", val, strerror(errno)); + errno = saved_errno; + return (-1); + } + + value->syntax = SNMP_SYNTAX_COUNTER64; + value->v.counter64 = v; + errno = saved_errno; + + return (0); +} + +static int32_t +parse_syntax_val(struct snmp_value *value, enum snmp_syntax syntax, char *val) +{ + switch (syntax) { + case SNMP_SYNTAX_INTEGER: + return (parse_int(value, val)); + case SNMP_SYNTAX_IPADDRESS: + return (parse_ip(value, val)); + case SNMP_SYNTAX_COUNTER: + return (parse_counter(value, val)); + case SNMP_SYNTAX_GAUGE: + return (parse_gauge(value, val)); + case SNMP_SYNTAX_TIMETICKS: + return (parse_ticks(value, val)); + case SNMP_SYNTAX_COUNTER64: + return (parse_uint64(value, val)); + case SNMP_SYNTAX_OCTETSTRING: + return (snmp_tc2oct(SNMP_STRING, value, val)); + case SNMP_SYNTAX_OID: + return (parse_oid_numeric(value, val)); + default: + /* NOTREACHED */ + break; + } + + return (-1); +} + +/* + * Parse a command line argument of type OID=syntax:value and fill in whatever + * fields can be derived from the input into snmp_value structure. Reads numeric + * OIDs. + */ +static int32_t +parse_pair_numoid_val(char *str, struct snmp_value *snmp_val) +{ + int32_t cnt; + char *ptr; + enum snmp_syntax syntax; + char oid_str[ASN_OIDSTRLEN]; + + ptr = str; + for (cnt = 0; cnt < ASN_OIDSTRLEN; cnt++) + if (ptr[cnt] == '=') + break; + + if (cnt >= ASN_OIDSTRLEN) { + warnx("OID too long - %s", str); + return (-1); + } + strlcpy(oid_str, ptr, (size_t) (cnt + 1)); + + ptr = str + cnt + 1; + for (cnt = 0; cnt < MAX_CMD_SYNTAX_LEN; cnt++) + if(ptr[cnt] == ':') + break; + + if (cnt >= MAX_CMD_SYNTAX_LEN) { + warnx("Unknown syntax in OID - %s", str); + return (-1); + } + + if ((syntax = parse_syntax(ptr)) <= SNMP_SYNTAX_NULL) { + warnx("Unknown syntax in OID - %s", ptr); + return (-1); + } + + ptr = ptr + cnt + 1; + for (cnt = 0; cnt < MAX_OCTSTRING_LEN; cnt++) + if (ptr[cnt] == '\0') + break; + + if (ptr[cnt] != '\0') { + warnx("Value string too long - %s",ptr); + return (-1); + } + + /* + * Here try parsing the OIDs and syntaxes and then check values - have + * to know syntax to check value boundaries. + */ + if (snmp_parse_numoid(oid_str, &(snmp_val->var)) < 0) { + warnx("Error parsing OID %s",oid_str); + return (-1); + } + + if (parse_syntax_val(snmp_val, syntax, ptr) < 0) + return (-1); + + return (1); +} + +/* XXX-BZ aruments should be swapped. */ +static int32_t +parse_syntax_strval(struct snmp_toolinfo *snmptoolctx, char *str, + struct snmp_object *object) +{ + uint32_t len; + enum snmp_syntax syn; + + /* + * Syntax string here not required - still may be present. + */ + + if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE) { + for (len = 0 ; *(str + len) != ':'; len++) { + if (*(str + len) == '\0') { + warnx("Syntax missing in value - %s", str); + return (-1); + } + } + if ((syn = parse_syntax(str)) <= SNMP_SYNTAX_NULL) { + warnx("Unknown syntax in - %s", str); + return (-1); + } + if (syn != object->val.syntax) { + if (!ISSET_ERRIGNORE(snmptoolctx)) { + warnx("Bad syntax in - %s", str); + return (-1); + } else + object->val.syntax = syn; + } + len++; + } else + len = 0; + + switch (object->val.syntax) { + case SNMP_SYNTAX_INTEGER: + return (parse_int_string(object, str + len)); + case SNMP_SYNTAX_IPADDRESS: + return (parse_ip(&(object->val), str + len)); + case SNMP_SYNTAX_COUNTER: + return (parse_counter(&(object->val), str + len)); + case SNMP_SYNTAX_GAUGE: + return (parse_gauge(&(object->val), str + len)); + case SNMP_SYNTAX_TIMETICKS: + return (parse_ticks(&(object->val), str + len)); + case SNMP_SYNTAX_COUNTER64: + return (parse_uint64(&(object->val), str + len)); + case SNMP_SYNTAX_OCTETSTRING: + return (snmp_tc2oct(object->info->tc, &(object->val), + str + len)); + case SNMP_SYNTAX_OID: + return (parse_oid_string(snmptoolctx, &(object->val), + str + len)); + default: + /* NOTREACHED */ + break; + } + + return (-1); +} + +static int32_t +parse_pair_stroid_val(struct snmp_toolinfo *snmptoolctx, + struct snmp_object *obj, char *argv) +{ + char *ptr; + + if ((ptr = snmptools_parse_stroid(snmptoolctx, obj, argv)) == NULL) + return (-1); + + if (*ptr != '=') { + warnx("Value to set expected after OID"); + return (-1); + } + + if (parse_syntax_strval(snmptoolctx, ptr + 1, obj) < 0) + return (-1); + + return (1); +} + + +static int32_t +snmpset_parse_oid(struct snmp_toolinfo *snmptoolctx, + struct snmp_object *obj, char *argv) +{ + if (argv == NULL) + return (-1); + + if (ISSET_NUMERIC(snmptoolctx)) { + if (parse_pair_numoid_val(argv, &(obj->val)) < 0) + return (-1); + } else { + if (parse_pair_stroid_val(snmptoolctx, obj, argv) < 0) + return (-1); + } + + return (1); +} + +static int32_t +add_ip_syntax(struct snmp_value *dst, struct snmp_value *src) +{ + int8_t i; + + dst->syntax = SNMP_SYNTAX_IPADDRESS; + for (i = 0; i < 4; i++) + dst->v.ipaddress[i] = src->v.ipaddress[i]; + + return (1); +} + +static int32_t +add_octstring_syntax(struct snmp_value *dst, struct snmp_value *src) +{ + if (src->v.octetstring.len > ASN_MAXOCTETSTRING) { + warnx("OctetString len too big - %u",src->v.octetstring.len); + return (-1); + } + + if ((dst->v.octetstring.octets = malloc(src->v.octetstring.len)) == + NULL) { + syslog(LOG_ERR, "malloc() failed - %s", strerror(errno)); + return (-1); + } + + memcpy(dst->v.octetstring.octets, src->v.octetstring.octets, + src->v.octetstring.len); + dst->syntax = SNMP_SYNTAX_OCTETSTRING; + dst->v.octetstring.len = src->v.octetstring.len; + + return(0); +} + +static int32_t +add_oid_syntax(struct snmp_value *dst, struct snmp_value *src) +{ + asn_append_oid(&(dst->v.oid), &(src->v.oid)); + dst->syntax = SNMP_SYNTAX_OID; + return (0); +} + +/* + * Check syntax - if one of SNMP_SYNTAX_NULL, SNMP_SYNTAX_NOSUCHOBJECT, + * SNMP_SYNTAX_NOSUCHINSTANCE, SNMP_SYNTAX_ENDOFMIBVIEW or anything not known - + * return error. + */ +static int32_t +snmpset_add_value(struct snmp_value *dst, struct snmp_value *src) +{ + if (dst == NULL || src == NULL) + return (-1); + + switch (src->syntax) { + case SNMP_SYNTAX_INTEGER: + dst->v.integer = src->v.integer; + dst->syntax = SNMP_SYNTAX_INTEGER; + break; + case SNMP_SYNTAX_TIMETICKS: + dst->v.uint32 = src->v.uint32; + dst->syntax = SNMP_SYNTAX_TIMETICKS; + break; + case SNMP_SYNTAX_GAUGE: + dst->v.uint32 = src->v.uint32; + dst->syntax = SNMP_SYNTAX_GAUGE; + break; + case SNMP_SYNTAX_COUNTER: + dst->v.uint32 = src->v.uint32; + dst->syntax = SNMP_SYNTAX_COUNTER; + break; + case SNMP_SYNTAX_COUNTER64: + dst->v.counter64 = src->v.counter64; + dst->syntax = SNMP_SYNTAX_COUNTER64; + break; + case SNMP_SYNTAX_IPADDRESS: + add_ip_syntax(dst, src); + break; + case SNMP_SYNTAX_OCTETSTRING: + add_octstring_syntax(dst, src); + break; + case SNMP_SYNTAX_OID: + add_oid_syntax(dst, src); + break; + default: + warnx("Unknown syntax %d", src->syntax); + return (-1); + } + + return (0); +} + +static int32_t +snmpset_verify_vbind(struct snmp_toolinfo *snmptoolctx, struct snmp_pdu *pdu, + struct snmp_object *obj) +{ + if (pdu->version == SNMP_V1 && obj->val.syntax == + SNMP_SYNTAX_COUNTER64) { + warnx("64-bit counters are not supported in SNMPv1 PDU"); + return (-1); + } + + if (ISSET_NUMERIC(snmptoolctx) || ISSET_ERRIGNORE(snmptoolctx)) + return (1); + + if (obj->info->access < SNMP_ACCESS_SET) { + warnx("Object %s not accessible for set - try 'bsnmpset -a'", + obj->info->string); + return (-1); + } + + return (1); +} + +static int32_t +snmpset_add_vbind(struct snmp_pdu *pdu, struct snmp_object *obj) +{ + if (pdu->nbindings > SNMP_MAX_BINDINGS) { + warnx("Too many OIDs for one PDU"); + return (-1); + } + + if (obj->error > 0) + return (0); + + if (snmpset_add_value(&(pdu->bindings[pdu->nbindings]), &(obj->val)) + < 0) + return (-1); + + asn_append_oid(&(pdu->bindings[pdu->nbindings].var), &(obj->val.var)); + pdu->nbindings++; + + return (pdu->nbindings); +} + +static int +snmptool_set(struct snmp_toolinfo *snmptoolctx) +{ + struct snmp_pdu req, resp; + + snmp_pdu_create(&req, SNMP_PDU_SET); + + while ((snmp_pdu_add_bindings(snmptoolctx, snmpset_verify_vbind, + snmpset_add_vbind, &req, SNMP_MAX_BINDINGS)) > 0) { + if (snmp_dialog(&req, &resp)) { + warnx("Snmp dialog - %s", strerror(errno)); + break; + } + + if (snmp_pdu_check(&req, &resp) > 0) { + if (GET_OUTPUT(snmptoolctx) != OUTPUT_QUIET) + snmp_output_resp(snmptoolctx, &resp, NULL); + break; + } + + snmp_output_err_resp(snmptoolctx, &resp); + if (!ISSET_RETRY(snmptoolctx)) + break; + + if (snmp_object_seterror(snmptoolctx, + &(resp.bindings[resp.error_index - 1]), + resp.error_status) <= 0) + break; + + fprintf(stderr, "Retrying...\n"); + snmp_pdu_free(&req); + snmp_pdu_free(&resp); + snmp_pdu_create(&req, SNMP_PDU_SET); + } + + snmp_pdu_free(&resp); + + return (0); +} + +/* ***************************************************************************** + * main + */ +/* + * According to command line options prepare SNMP Get | GetNext | GetBulk PDU. + * Wait for a response and print it. + */ +/* + * Do a 'snmp walk' - according to command line options request for values + * lexicographically subsequent and subrooted at a common node. Send a GetNext + * PDU requesting the value for each next variable and print the response. Stop + * when a Response PDU is received that contains the value of a variable not + * subrooted at the variable the walk started. + */ +int +main(int argc, char ** argv) +{ + struct snmp_toolinfo snmptoolctx; + int32_t oid_cnt, last_oid, opt_num; + int rc = 0; + + /* Make sure program_name is set and valid. */ + if (*argv == NULL) + program_name = "snmptool"; + else { + program_name = strrchr(*argv, '/'); + if (program_name != NULL) + program_name++; + else + program_name = *argv; + } + + if (program_name == NULL) { + fprintf(stderr, "Error: No program name?\n"); + exit (1); + } else if (strcmp(program_name, "bsnmpget") == 0) + program = BSNMPGET; + else if (strcmp(program_name, "bsnmpwalk") == 0) + program = BSNMPWALK; + else if (strcmp(program_name, "bsnmpset") == 0) + program = BSNMPSET; + else { + fprintf(stderr, "Unknown snmp tool name '%s'.\n", program_name); + exit (1); + } + + /* Initialize. */ + if (snmptool_init(&snmptoolctx) < 0) + exit (1); + + if ((opt_num = snmptool_parse_options(&snmptoolctx, argc, argv)) < 0) { + snmp_tool_freeall(&snmptoolctx); + /* On -h (help) exit without error. */ + if (opt_num == -2) + exit(0); + else + exit(1); + } + + oid_cnt = argc - opt_num - 1; + if (oid_cnt == 0) { + switch (program) { + case BSNMPGET: + if (!ISSET_EDISCOVER(&snmptoolctx) && + !ISSET_LOCALKEY(&snmptoolctx)) { + fprintf(stderr, "No OID given.\n"); + usage(); + snmp_tool_freeall(&snmptoolctx); + exit(1); + } + break; + + case BSNMPWALK: + if (snmp_object_add(&snmptoolctx, snmpwalk_add_default, + NULL) < 0) { + fprintf(stderr, + "Error setting default subtree.\n"); + snmp_tool_freeall(&snmptoolctx); + exit(1); + } + break; + + case BSNMPSET: + fprintf(stderr, "No OID given.\n"); + usage(); + snmp_tool_freeall(&snmptoolctx); + exit(1); + } + } + + if (snmp_import_all(&snmptoolctx) < 0) { + snmp_tool_freeall(&snmptoolctx); + exit(1); + } + + /* A simple sanity check - can not send GETBULK when using SNMPv1. */ + if (program == BSNMPGET && snmp_client.version == SNMP_V1 && + GET_PDUTYPE(&snmptoolctx) == SNMP_PDU_GETBULK) { + fprintf(stderr, "Cannot send GETBULK PDU with SNMPv1.\n"); + snmp_tool_freeall(&snmptoolctx); + exit(1); + } + + for (last_oid = argc - 1; oid_cnt > 0; last_oid--, oid_cnt--) { + if ((snmp_object_add(&snmptoolctx, (program == BSNMPSET) ? + snmpset_parse_oid : snmptools_parse_oid, + argv[last_oid])) < 0) { + fprintf(stderr, "Error parsing OID string '%s'.\n", + argv[last_oid]); + snmp_tool_freeall(&snmptoolctx); + exit(1); + } + } + + if (snmp_open(NULL, NULL, NULL, NULL)) { + warnx("Failed to open snmp session: %s.", strerror(errno)); + snmp_tool_freeall(&snmptoolctx); + exit(1); + } + + if (snmp_client.version == SNMP_V3 && snmp_client.engine.engine_len == 0) + SET_EDISCOVER(&snmptoolctx); + + if (ISSET_EDISCOVER(&snmptoolctx) && + snmp_discover_engine(snmptoolctx.passwd) < 0) { + warnx("Unknown SNMP Engine ID: %s.", strerror(errno)); + rc = 1; + goto cleanup; + } + + if (GET_OUTPUT(&snmptoolctx) == OUTPUT_VERBOSE || + ISSET_EDISCOVER(&snmptoolctx)) + snmp_output_engine(); + + if (snmp_client.version == SNMP_V3 && ISSET_LOCALKEY(&snmptoolctx) && + !ISSET_EDISCOVER(&snmptoolctx)) { + if (snmp_passwd_to_keys(&snmp_client.user, + snmptoolctx.passwd) != SNMP_CODE_OK || + snmp_get_local_keys(&snmp_client.user, + snmp_client.engine.engine_id, + snmp_client.engine.engine_len) != SNMP_CODE_OK) { + warnx("Failed to get keys: %s.", strerror(errno)); + rc = 1; + goto cleanup; + } + } + + if (GET_OUTPUT(&snmptoolctx) == OUTPUT_VERBOSE || + ISSET_EDISCOVER(&snmptoolctx)) + snmp_output_keys(); + + if (ISSET_EDISCOVER(&snmptoolctx) && snmptoolctx.objects == 0) + goto cleanup; + + switch (program) { + case BSNMPGET: + rc = snmptool_get(&snmptoolctx); + break; + case BSNMPWALK: + rc = snmptool_walk(&snmptoolctx); + break; + case BSNMPSET: + rc = snmptool_set(&snmptoolctx); + break; + } + + +cleanup: + snmp_tool_freeall(&snmptoolctx); + snmp_close(); + + exit(rc); +} diff --git a/usr.sbin/bsnmpd/tools/libbsnmptools/Makefile b/usr.sbin/bsnmpd/tools/libbsnmptools/Makefile new file mode 100644 index 0000000..3551464 --- /dev/null +++ b/usr.sbin/bsnmpd/tools/libbsnmptools/Makefile @@ -0,0 +1,13 @@ +# +# $FreeBSD$ +# + +.PATH: ${.CURDIR} + +LIB= bsnmptools +#INTERNALLIB= +SRCS= bsnmpimport.c bsnmpmap.c bsnmptools.c bsnmptc.c + +SHLIB_MAJOR= 0 + +.include <bsd.lib.mk> diff --git a/usr.sbin/bsnmpd/tools/libbsnmptools/bsnmpimport.c b/usr.sbin/bsnmpd/tools/libbsnmptools/bsnmpimport.c new file mode 100644 index 0000000..b92532f --- /dev/null +++ b/usr.sbin/bsnmpd/tools/libbsnmptools/bsnmpimport.c @@ -0,0 +1,971 @@ +/*- + * Copyright (c) 2006 The FreeBSD Project + * All rights reserved. + * + * Author: Shteryana Shopova <syrinx@FreeBSD.org> + * + * Redistribution of this software and documentation and use in source and + * binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code or documentation must retain the above + * copyright notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * Read file containing table description - reuse magic from gensnmptree.c. + * Hopefully one day most of the code here will be part of libbsnmp and + * this duplication won't be necessary. + * + * Syntax is: + * --------- + * file := top | top file + * + * top := tree | typedef | include + * + * tree := head elements ')' + * + * entry := head ':' index STRING elements ')' + * + * leaf := head type STRING ACCESS ')' + * + * column := head type ACCESS ')' + * + * type := BASETYPE | BASETYPE '|' subtype | enum | bits + * + * subtype := STRING + * + * enum := ENUM '(' value ')' + * + * bits := BITS '(' value ')' + * + * value := INT STRING | INT STRING value + * + * head := '(' INT STRING + * + * elements := EMPTY | elements element + * + * element := tree | leaf | column + * + * index := type | index type + * + * typedef := 'typedef' STRING type + * + * include := 'include' filespec + * + * filespec := '"' STRING '"' | '<' STRING '>' + */ + +#include <sys/param.h> +#include <sys/queue.h> +#include <sys/uio.h> + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <unistd.h> + +#include <bsnmp/asn1.h> +#include <bsnmp/snmp.h> +#include <bsnmp/snmpagent.h> /* SNMP_INDEXES_MAX */ +#include "bsnmptc.h" +#include "bsnmptools.h" + +enum snmp_tbl_entry { + ENTRY_NONE = 0, + ENTRY_INDEX, + ENTRY_DATA +}; + +enum { + FL_GET = 0x01, + FL_SET = 0x02, +}; + +/************************************************************ + * + * Allocate memory and panic just in the case... + */ +static void * +xalloc(size_t size) +{ + void *ptr; + + if ((ptr = malloc(size)) == NULL) + err(1, "allocing %zu bytes", size); + + return (ptr); +} + +static char * +savestr(const char *s) +{ + if (s == NULL) + return (NULL); + + return (strcpy(xalloc(strlen(s) + 1), s)); +} + +/************************************************************ + * + * Input stack + */ +struct input { + FILE *fp; + uint32_t lno; + char *fname; + char *path; + LIST_ENTRY(input) link; +}; + +LIST_HEAD(, input) inputs = LIST_HEAD_INITIALIZER(inputs); +struct input *input = NULL; +int32_t pbchar = -1; + +#define MAX_PATHS 100 + +static const char *paths[MAX_PATHS + 1] = { + "/usr/share/snmp/defs", + "/usr/local/share/snmp/defs", + NULL +}; + +static void +input_new(FILE *fp, const char *path, const char *fname) +{ + struct input *ip; + + ip = xalloc(sizeof(*ip)); + ip->fp = fp; + ip->lno = 1; + ip->fname = savestr(fname); + ip->path = savestr(path); + LIST_INSERT_HEAD(&inputs, ip, link); + + input = ip; +} + +static void +input_close(void) +{ + if (input == NULL) + return; + + fclose(input->fp); + free(input->fname); + free(input->path); + LIST_REMOVE(input, link); + free(input); + + input = LIST_FIRST(&inputs); +} + +static FILE * +tryopen(const char *path, const char *fname) +{ + char *fn; + FILE *fp; + + if (path == NULL) + fn = savestr(fname); + else { + fn = xalloc(strlen(path) + strlen(fname) + 2); + sprintf(fn, "%s/%s", path, fname); + } + fp = fopen(fn, "r"); + free(fn); + return (fp); +} + +static int32_t +input_fopen(const char *fname) +{ + FILE *fp; + u_int p; + + if (fname[0] == '/' || fname[0] == '.' || fname[0] == '~') { + if ((fp = tryopen(NULL, fname)) != NULL) { + input_new(fp, NULL, fname); + return (0); + } + + } else { + + for (p = 0; paths[p] != NULL; p++) + if ((fp = tryopen(paths[p], fname)) != NULL) { + input_new(fp, paths[p], fname); + return (0); + } + } + + warnx("cannot open '%s'", fname); + return (-1); +} + +static int32_t +tgetc(void) +{ + int c; + + if (pbchar != -1) { + c = pbchar; + pbchar = -1; + return (c); + } + + for (;;) { + if (input == NULL) + return (EOF); + + if ((c = getc(input->fp)) != EOF) + return (c); + + input_close(); + } +} + +static int32_t +tungetc(int c) +{ + + if (pbchar != -1) + return (-1); + + pbchar = c; + return (1); +} + +/************************************************************ + * + * Parsing input + */ +enum tok { + TOK_EOF = 0200, /* end-of-file seen */ + TOK_NUM, /* number */ + TOK_STR, /* string */ + TOK_ACCESS, /* access operator */ + TOK_TYPE, /* type operator */ + TOK_ENUM, /* enum token (kind of a type) */ + TOK_TYPEDEF, /* typedef directive */ + TOK_DEFTYPE, /* defined type */ + TOK_INCLUDE, /* include directive */ + TOK_FILENAME, /* filename ("foo.bar" or <foo.bar>) */ + TOK_BITS, /* bits token (kind of a type) */ + TOK_ERR /* unexpected char - exit */ +}; + +static const struct { + const char *str; + enum tok tok; + uint32_t val; +} keywords[] = { + { "GET", TOK_ACCESS, FL_GET }, + { "SET", TOK_ACCESS, FL_SET }, + { "NULL", TOK_TYPE, SNMP_SYNTAX_NULL }, + { "INTEGER", TOK_TYPE, SNMP_SYNTAX_INTEGER }, + { "INTEGER32", TOK_TYPE, SNMP_SYNTAX_INTEGER }, + { "UNSIGNED32", TOK_TYPE, SNMP_SYNTAX_GAUGE }, + { "OCTETSTRING", TOK_TYPE, SNMP_SYNTAX_OCTETSTRING }, + { "IPADDRESS", TOK_TYPE, SNMP_SYNTAX_IPADDRESS }, + { "OID", TOK_TYPE, SNMP_SYNTAX_OID }, + { "TIMETICKS", TOK_TYPE, SNMP_SYNTAX_TIMETICKS }, + { "COUNTER", TOK_TYPE, SNMP_SYNTAX_COUNTER }, + { "GAUGE", TOK_TYPE, SNMP_SYNTAX_GAUGE }, + { "COUNTER64", TOK_TYPE, SNMP_SYNTAX_COUNTER64 }, + { "ENUM", TOK_ENUM, SNMP_SYNTAX_INTEGER }, + { "BITS", TOK_BITS, SNMP_SYNTAX_OCTETSTRING }, + { "typedef", TOK_TYPEDEF, 0 }, + { "include", TOK_INCLUDE, 0 }, + { NULL, 0, 0 } +}; + +struct { + /* Current OID type, regarding table membership. */ + enum snmp_tbl_entry tbl_type; + /* A pointer to a structure in table list to add to its members. */ + struct snmp_index_entry *table_idx; +} table_data; + +struct asn_oid current_oid; +char nexttok[MAXSTR]; +u_long val; /* integer values */ +int32_t all_cond; /* all conditions are true */ +int32_t saved_token = -1; + +/* Prepare the global data before parsing a new file. */ +static void +snmp_import_init(struct asn_oid *append) +{ + memset(&table_data, 0, sizeof(table_data)); + memset(¤t_oid, 0, sizeof(struct asn_oid)); + memset(nexttok, 0, MAXSTR); + + if (append != NULL) + asn_append_oid(¤t_oid, append); + + all_cond = 0; + val = 0; + saved_token = -1; +} + +static int32_t +gettoken(struct snmp_toolinfo *snmptoolctx) +{ + int c; + struct enum_type *t; + + if (saved_token != -1) { + c = saved_token; + saved_token = -1; + return (c); + } + + again: + /* + * Skip any whitespace before the next token. + */ + while ((c = tgetc()) != EOF) { + if (c == '\n') + input->lno++; + if (!isspace(c)) + break; + } + if (c == EOF) + return (TOK_EOF); + + if (!isascii(c)) { + warnx("unexpected character %#2x", (u_int) c); + return (TOK_ERR); + } + + /* + * Skip comments. + */ + if (c == '#') { + while ((c = tgetc()) != EOF) { + if (c == '\n') { + input->lno++; + goto again; + } + } + warnx("unexpected EOF in comment"); + return (TOK_ERR); + } + + /* + * Single character tokens. + */ + if (strchr("():|", c) != NULL) + return (c); + + if (c == '"' || c == '<') { + int32_t end = c; + size_t n = 0; + + val = 1; + if (c == '<') { + val = 0; + end = '>'; + } + + while ((c = tgetc()) != EOF) { + if (c == end) + break; + if (n == sizeof(nexttok) - 1) { + nexttok[n++] = '\0'; + warnx("filename too long '%s...'", nexttok); + return (TOK_ERR); + } + nexttok[n++] = c; + } + nexttok[n++] = '\0'; + return (TOK_FILENAME); + } + + /* + * Sort out numbers. + */ + if (isdigit(c)) { + size_t n = 0; + nexttok[n++] = c; + while ((c = tgetc()) != EOF) { + if (!isdigit(c)) { + if (tungetc(c) < 0) + return (TOK_ERR); + break; + } + if (n == sizeof(nexttok) - 1) { + nexttok[n++] = '\0'; + warnx("number too long '%s...'", nexttok); + return (TOK_ERR); + } + nexttok[n++] = c; + } + nexttok[n++] = '\0'; + sscanf(nexttok, "%lu", &val); + return (TOK_NUM); + } + + /* + * So that has to be a string. + */ + if (isalpha(c) || c == '_' || c == '-') { + size_t n = 0; + nexttok[n++] = c; + while ((c = tgetc()) != EOF) { + if (!isalnum(c) && c != '_' && c != '-') { + if (tungetc (c) < 0) + return (TOK_ERR); + break; + } + if (n == sizeof(nexttok) - 1) { + nexttok[n++] = '\0'; + warnx("string too long '%s...'", nexttok); + return (TOK_ERR); + } + nexttok[n++] = c; + } + nexttok[n++] = '\0'; + + /* + * Keywords. + */ + for (c = 0; keywords[c].str != NULL; c++) + if (strcmp(keywords[c].str, nexttok) == 0) { + val = keywords[c].val; + return (keywords[c].tok); + } + + if ((t = snmp_enumtc_lookup(snmptoolctx, nexttok)) != NULL) { + val = t->syntax; + return (TOK_DEFTYPE); + } + + return (TOK_STR); + } + + if (isprint(c)) + warnx("%u: unexpected character '%c'", input->lno, c); + else + warnx("%u: unexpected character 0x%02x", input->lno, (u_int) c); + + return (TOK_ERR); +} + +/* + * Update table information. + */ +static struct snmp_index_entry * +snmp_import_update_table(enum snmp_tbl_entry te, struct snmp_index_entry *tbl) +{ + switch (te) { + case ENTRY_NONE: + if (table_data.tbl_type == ENTRY_NONE) + return (NULL); + if (table_data.tbl_type == ENTRY_INDEX) + table_data.table_idx = NULL; + table_data.tbl_type--; + return (NULL); + + case ENTRY_INDEX: + if (tbl == NULL) + warnx("No table_index to add!!!"); + table_data.table_idx = tbl; + table_data.tbl_type = ENTRY_INDEX; + return (tbl); + + case ENTRY_DATA: + if (table_data.tbl_type == ENTRY_INDEX) { + table_data.tbl_type = ENTRY_DATA; + return (table_data.table_idx); + } + return (NULL); + + default: + /* NOTREACHED */ + warnx("Unknown table entry type!!!"); + break; + } + + return (NULL); +} + +static int32_t +parse_enum(struct snmp_toolinfo *snmptoolctx, enum tok *tok, + struct enum_pairs *enums) +{ + while ((*tok = gettoken(snmptoolctx)) == TOK_STR) { + if (enum_pair_insert(enums, val, nexttok) < 0) + return (-1); + if ((*tok = gettoken(snmptoolctx)) != TOK_NUM) + break; + } + + if (*tok != ')') { + warnx("')' at end of enums"); + return (-1); + } + + return (1); +} + +static int32_t +parse_subtype(struct snmp_toolinfo *snmptoolctx, enum tok *tok, + enum snmp_tc *tc) +{ + if ((*tok = gettoken(snmptoolctx)) != TOK_STR) { + warnx("subtype expected after '|'"); + return (-1); + } + + *tc = snmp_get_tc(nexttok); + *tok = gettoken(snmptoolctx); + + return (1); +} + +static int32_t +parse_type(struct snmp_toolinfo *snmptoolctx, enum tok *tok, + enum snmp_tc *tc, struct enum_pairs **snmp_enum) +{ + int32_t syntax, mem; + + syntax = val; + *tc = 0; + + if (*tok == TOK_ENUM || *tok == TOK_BITS) { + if (*snmp_enum == NULL) { + if ((*snmp_enum = enum_pairs_init()) == NULL) + return (-1); + mem = 1; + *tc = SNMP_TC_OWN; + } else + mem = 0; + + if (gettoken(snmptoolctx) != '(') { + warnx("'(' expected after ENUM/BITS"); + return (-1); + } + + if ((*tok = gettoken(snmptoolctx)) != TOK_NUM) { + warnx("need value for ENUM//BITS"); + if (mem == 1) { + free(*snmp_enum); + *snmp_enum = NULL; + } + return (-1); + } + + if (parse_enum(snmptoolctx, tok, *snmp_enum) < 0) { + enum_pairs_free(*snmp_enum); + *snmp_enum = NULL; + return (-1); + } + + *tok = gettoken(snmptoolctx); + + } else if (*tok == TOK_DEFTYPE) { + struct enum_type *t; + + *tc = 0; + t = snmp_enumtc_lookup(snmptoolctx, nexttok); + if (t != NULL) + *snmp_enum = t->snmp_enum; + + *tok = gettoken(snmptoolctx); + + } else { + if ((*tok = gettoken(snmptoolctx)) == '|') { + if (parse_subtype(snmptoolctx, tok, tc) < 0) + return (-1); + } + } + + return (syntax); +} + +static int32_t +snmp_import_head(struct snmp_toolinfo *snmptoolctx) +{ + enum tok tok; + + if ((tok = gettoken(snmptoolctx)) == '(') + tok = gettoken(snmptoolctx); + + if (tok != TOK_NUM || val > ASN_MAXID ) { + warnx("Suboid expected - line %d", input->lno); + return (-1); + } + + if (gettoken(snmptoolctx) != TOK_STR) { + warnx("Node name expected at line %d", input->lno); + return (-1); + } + + return (1); +} + +static int32_t +snmp_import_table(struct snmp_toolinfo *snmptoolctx, struct snmp_oid2str *obj) +{ + int32_t i; + enum snmp_tc tc; + enum tok tok; + struct snmp_index_entry *entry; + + if ((entry = malloc(sizeof(struct snmp_index_entry))) == NULL) { + syslog(LOG_ERR, "malloc() failed: %s", strerror(errno)); + return (-1); + } + + memset(entry, 0, sizeof(struct snmp_index_entry)); + STAILQ_INIT(&(entry->index_list)); + + for (i = 0, tok = gettoken(snmptoolctx); i < SNMP_INDEXES_MAX; i++) { + int32_t syntax; + struct enum_pairs *enums = NULL; + + if (tok != TOK_TYPE && tok != TOK_DEFTYPE && tok != TOK_ENUM && + tok != TOK_BITS) + break; + + if ((syntax = parse_type(snmptoolctx, &tok, &tc, &enums)) < 0) { + enum_pairs_free(enums); + snmp_index_listfree(&(entry->index_list)); + free(entry); + return (-1); + } + + if (snmp_syntax_insert(&(entry->index_list), enums, syntax, + tc) < 0) { + snmp_index_listfree(&(entry->index_list)); + enum_pairs_free(enums); + free(entry); + return (-1); + } + } + + if (i == 0 || i > SNMP_INDEXES_MAX) { + warnx("Bad number of indexes at line %d", input->lno); + snmp_index_listfree(&(entry->index_list)); + free(entry); + return (-1); + } + + if (tok != TOK_STR) { + warnx("String expected after indexes at line %d", input->lno); + snmp_index_listfree(&(entry->index_list)); + free(entry); + return (-1); + } + + entry->string = obj->string; + entry->strlen = obj->strlen; + asn_append_oid(&(entry->var), &(obj->var)); + + if ((i = snmp_table_insert(snmptoolctx, entry)) < 0) { + snmp_index_listfree(&(entry->index_list)); + free(entry); + return (-1); + } else if (i == 0) { + /* Same entry already present in lists. */ + free(entry->string); + free(entry); + } + + (void) snmp_import_update_table(ENTRY_INDEX, entry); + + return (1); +} + +/* + * Read everything after the syntax type that is certainly a leaf OID info. + */ +static int32_t +snmp_import_leaf(struct snmp_toolinfo *snmptoolctx, enum tok *tok, + struct snmp_oid2str *oid2str) +{ + int32_t i, syntax; + + if ((syntax = parse_type(snmptoolctx, tok, &(oid2str->tc), &(oid2str->snmp_enum))) + < 0) + return(-1); + + oid2str->syntax = syntax; + /* + * That is the name of the function, corresponding to the entry. + * It is used by bsnmpd, but is not interesting for us. + */ + if (*tok == TOK_STR) + *tok = gettoken(snmptoolctx); + + for (i = 0; i < SNMP_ACCESS_GETSET && *tok == TOK_ACCESS; i++) { + oid2str->access |= (uint32_t) val; + *tok = gettoken(snmptoolctx); + } + + if (*tok != ')') { + warnx("')' expected at end of line %d", input->lno); + return (-1); + } + + oid2str->table_idx = snmp_import_update_table(ENTRY_DATA, NULL); + + if ((i = snmp_leaf_insert(snmptoolctx, oid2str)) < 0) { + warnx("Error adding leaf %s to list", oid2str->string); + return (-1); + } + + /* + * Same entry is already present in the mapping lists and + * the new one was not inserted. + */ + if (i == 0) { + free(oid2str->string); + free(oid2str); + } + + (void) snmp_import_update_table(ENTRY_NONE, NULL); + + return (1); +} + +static int32_t +snmp_import_object(struct snmp_toolinfo *snmptoolctx) +{ + char *string; + int i; + enum tok tok; + struct snmp_oid2str *oid2str; + + if (snmp_import_head(snmptoolctx) < 0) + return (-1); + + if ((oid2str = malloc(sizeof(struct snmp_oid2str))) == NULL) { + syslog(LOG_ERR, "malloc() failed: %s", strerror(errno)); + return (-1); + } + + if ((string = malloc(strlen(nexttok) + 1)) == NULL) { + syslog(LOG_ERR, "malloc() failed: %s", strerror(errno)); + free(oid2str); + return (-1); + } + + memset(oid2str, 0, sizeof(struct snmp_oid2str)); + strlcpy(string, nexttok, strlen(nexttok) + 1); + oid2str->string = string; + oid2str->strlen = strlen(nexttok); + + asn_append_oid(&(oid2str->var), &(current_oid)); + if (snmp_suboid_append(&(oid2str->var), (asn_subid_t) val) < 0) + goto error; + + /* + * Prepared the entry - now figure out where to insert it. + * After the object we have following options: + * 1) new line, blank, ) - then it is an enum oid -> snmp_enumlist; + * 2) new line , ( - nonleaf oid -> snmp_nodelist; + * 2) ':' - table entry - a variable length SYNTAX_TYPE (one or more) + * may follow and second string must end line -> snmp_tablelist; + * 3) OID , string ) - this is a trap entry or a leaf -> snmp_oidlist; + * 4) SYNTAX_TYPE, string (not always), get/set modifier - always last + * and )- this is definitely a leaf. + */ + + switch (tok = gettoken(snmptoolctx)) { + case ')': + if ((i = snmp_enum_insert(snmptoolctx, oid2str)) < 0) + goto error; + if (i == 0) { + free(oid2str->string); + free(oid2str); + } + return (1); + + case '(': + if (snmp_suboid_append(¤t_oid, (asn_subid_t) val) < 0) + goto error; + + /* + * Ignore the error for nodes since the .def files currently + * contain different strings for 1.3.6.1.2.1 - mibII. Only make + * sure the memory is freed and don't complain. + */ + if ((i = snmp_node_insert(snmptoolctx, oid2str)) <= 0) { + free(string); + free(oid2str); + } + return (snmp_import_object(snmptoolctx)); + + case ':': + if (snmp_suboid_append(¤t_oid, (asn_subid_t) val) < 0) + goto error; + if (snmp_import_table(snmptoolctx, oid2str) < 0) + goto error; + /* + * A different table entry type was malloced and the data is + * contained there. + */ + free(oid2str); + return (1); + + case TOK_TYPE: + /* FALLTHROUGH */ + case TOK_DEFTYPE: + /* FALLTHROUGH */ + case TOK_ENUM: + /* FALLTHROUGH */ + case TOK_BITS: + if (snmp_import_leaf(snmptoolctx, &tok, oid2str) < 0) + goto error; + return (1); + + default: + warnx("Unexpected token at line %d - %s", input->lno, + input->fname); + break; + } + +error: + snmp_mapping_entryfree(oid2str); + + return (-1); +} + +static int32_t +snmp_import_tree(struct snmp_toolinfo *snmptoolctx, enum tok *tok) +{ + while (*tok != TOK_EOF) { + switch (*tok) { + case TOK_ERR: + return (-1); + case '(': + if (snmp_import_object(snmptoolctx) < 0) + return (-1); + break; + case ')': + if (snmp_suboid_pop(¤t_oid) < 0) + return (-1); + (void) snmp_import_update_table(ENTRY_NONE, NULL); + break; + default: + /* Anything else here would be illegal. */ + return (-1); + } + *tok = gettoken(snmptoolctx); + } + + return (0); +} + +static int32_t +snmp_import_top(struct snmp_toolinfo *snmptoolctx, enum tok *tok) +{ + enum snmp_tc tc; + struct enum_type *t; + + if (*tok == '(') + return (snmp_import_tree(snmptoolctx, tok)); + + if (*tok == TOK_TYPEDEF) { + if ((*tok = gettoken(snmptoolctx)) != TOK_STR) { + warnx("type name expected after typedef - %s", + input->fname); + return (-1); + } + + t = snmp_enumtc_init(nexttok); + + *tok = gettoken(snmptoolctx); + t->is_enum = (*tok == TOK_ENUM); + t->is_bits = (*tok == TOK_BITS); + t->syntax = parse_type(snmptoolctx, tok, &tc, &(t->snmp_enum)); + snmp_enumtc_insert(snmptoolctx, t); + + return (1); + } + + if (*tok == TOK_INCLUDE) { + int i; + + *tok = gettoken(snmptoolctx); + if (*tok != TOK_FILENAME) { + warnx("filename expected in include directive - %s", + nexttok); + return (-1); + } + + if (( i = add_filename(snmptoolctx, nexttok, NULL, 1)) == 0) { + *tok = gettoken(snmptoolctx); + return (1); + } + + if (i == -1) + return (-1); + + input_fopen(nexttok); + *tok = gettoken(snmptoolctx); + return (1); + } + + warnx("'(' or 'typedef' expected - %s", nexttok); + return (-1); +} + +static int32_t +snmp_import(struct snmp_toolinfo *snmptoolctx) +{ + int i; + enum tok tok; + + tok = gettoken(snmptoolctx); + + do + i = snmp_import_top(snmptoolctx, &tok); + while (i > 0); + + return (i); +} + +/* + * Read a .def file and import oid<->string mapping. + * Mappings are inserted into a global structure containing list for each OID + * syntax type. + */ +int32_t +snmp_import_file(struct snmp_toolinfo *snmptoolctx, struct fname *file) +{ + int idx; + + snmp_import_init(&(file->cut)); + input_fopen(file->name); + if ((idx = snmp_import(snmptoolctx)) < 0) + warnx("Failed to read mappings from file %s", file->name); + + input_close(); + + return (idx); +} diff --git a/usr.sbin/bsnmpd/tools/libbsnmptools/bsnmpmap.c b/usr.sbin/bsnmpd/tools/libbsnmptools/bsnmpmap.c new file mode 100644 index 0000000..4f71c58 --- /dev/null +++ b/usr.sbin/bsnmpd/tools/libbsnmptools/bsnmpmap.c @@ -0,0 +1,1018 @@ +/*- + * Copyright (c) 2006 The FreeBSD Project + * All rights reserved. + * + * Author: Shteryana Shopova <syrinx@FreeBSD.org> + * + * Redistribution of this software and documentation and use in source and + * binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code or documentation must retain the above + * copyright notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include <sys/param.h> +#include <sys/queue.h> +#include <sys/uio.h> + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <unistd.h> + +#include <bsnmp/asn1.h> +#include <bsnmp/snmp.h> +#include "bsnmptc.h" +#include "bsnmptools.h" + +extern int _bsnmptools_debug; +#define DEBUG if (_bsnmptools_debug) fprintf + +/* Allocate memory and initialize list. */ +struct snmp_mappings * +snmp_mapping_init(void) +{ + struct snmp_mappings *m; + + if ((m = malloc(sizeof(struct snmp_mappings))) == NULL) { + syslog(LOG_ERR, "malloc() failed: %s", strerror(errno)); + return (NULL); + } + + memset(m, 0, sizeof(struct snmp_mappings)); + return (m); +} + +#define snmp_nodelist mappings->nodelist +#define snmp_intlist mappings->intlist +#define snmp_octlist mappings->octlist +#define snmp_oidlist mappings->oidlist +#define snmp_iplist mappings->iplist +#define snmp_ticklist mappings->ticklist +#define snmp_cntlist mappings->cntlist +#define snmp_gaugelist mappings->gaugelist +#define snmp_cnt64list mappings->cnt64list +#define snmp_enumlist mappings->enumlist +#define snmp_tablelist mappings->tablelist +#define snmp_tclist mappings->tclist + +void +enum_pairs_free(struct enum_pairs *headp) +{ + struct enum_pair *e; + + if (headp == NULL) + return; + + while ((e = STAILQ_FIRST(headp)) != NULL) { + STAILQ_REMOVE_HEAD(headp, link); + + if (e->enum_str) + free(e->enum_str); + free(e); + } + + free(headp); +} + +void +snmp_mapping_entryfree(struct snmp_oid2str *entry) +{ + if (entry->string) + free(entry->string); + + if (entry->tc == SNMP_TC_OWN) + enum_pairs_free(entry->snmp_enum); + + free(entry); +} + +static void +snmp_mapping_listfree(struct snmp_mapping *headp) +{ + struct snmp_oid2str *p; + + while ((p = SLIST_FIRST(headp)) != NULL) { + SLIST_REMOVE_HEAD(headp, link); + + if (p->string) + free(p->string); + + if (p->tc == SNMP_TC_OWN) + enum_pairs_free(p->snmp_enum); + free(p); + } + + SLIST_INIT(headp); +} + +void +snmp_index_listfree(struct snmp_idxlist *headp) +{ + struct index *i; + + while ((i = STAILQ_FIRST(headp)) != NULL) { + STAILQ_REMOVE_HEAD(headp, link); + if (i->tc == SNMP_TC_OWN) + enum_pairs_free(i->snmp_enum); + free(i); + } + + STAILQ_INIT(headp); +} + +static void +snmp_mapping_table_listfree(struct snmp_table_index *headp) +{ + struct snmp_index_entry *t; + + while ((t = SLIST_FIRST(headp)) != NULL) { + SLIST_REMOVE_HEAD(headp, link); + + if (t->string) + free(t->string); + + snmp_index_listfree(&(t->index_list)); + free(t); + } +} + +static void +snmp_enumtc_listfree(struct snmp_enum_tc *headp) +{ + struct enum_type *t; + + while ((t = SLIST_FIRST(headp)) != NULL) { + SLIST_REMOVE_HEAD(headp, link); + + if (t->name) + free(t->name); + enum_pairs_free(t->snmp_enum); + free(t); + } +} + +int +snmp_mapping_free(struct snmp_toolinfo *snmptoolctx) +{ + if (snmptoolctx == NULL || snmptoolctx->mappings == NULL) + return (-1); + + snmp_mapping_listfree(&snmptoolctx->snmp_nodelist); + snmp_mapping_listfree(&snmptoolctx->snmp_intlist); + snmp_mapping_listfree(&snmptoolctx->snmp_octlist); + snmp_mapping_listfree(&snmptoolctx->snmp_oidlist); + snmp_mapping_listfree(&snmptoolctx->snmp_iplist); + snmp_mapping_listfree(&snmptoolctx->snmp_ticklist); + snmp_mapping_listfree(&snmptoolctx->snmp_cntlist); + snmp_mapping_listfree(&snmptoolctx->snmp_gaugelist); + snmp_mapping_listfree(&snmptoolctx->snmp_cnt64list); + snmp_mapping_listfree(&snmptoolctx->snmp_enumlist); + snmp_mapping_table_listfree(&snmptoolctx->snmp_tablelist); + snmp_enumtc_listfree(&snmptoolctx->snmp_tclist); + free(snmptoolctx->mappings); + + return (0); +} + +static void +snmp_dump_enumpairs(struct enum_pairs *headp) +{ + struct enum_pair *entry; + + if (headp == NULL) + return; + + fprintf(stderr,"enums: "); + STAILQ_FOREACH(entry, headp, link) + fprintf(stderr,"%d - %s, ", entry->enum_val, + (entry->enum_str == NULL)?"NULL":entry->enum_str); + + fprintf(stderr,"; "); +} + +void +snmp_dump_oid2str(struct snmp_oid2str *entry) +{ + char buf[ASN_OIDSTRLEN]; + + if (entry != NULL) { + memset(buf, 0, sizeof(buf)); + asn_oid2str_r(&(entry->var), buf); + DEBUG(stderr, "%s - %s - %d - %d - %d", buf, entry->string, + entry->syntax, entry->access, entry->strlen); + snmp_dump_enumpairs(entry->snmp_enum); + DEBUG(stderr,"%s \n", (entry->table_idx == NULL)?"No table": + entry->table_idx->string); + } +} + +static void +snmp_dump_indexlist(struct snmp_idxlist *headp) +{ + struct index *entry; + + if (headp == NULL) + return; + + STAILQ_FOREACH(entry, headp, link) { + fprintf(stderr,"%d, ", entry->syntax); + snmp_dump_enumpairs(entry->snmp_enum); + } + + fprintf(stderr,"\n"); +} + +/* Initialize the enum pairs list of a oid2str entry. */ +struct enum_pairs * +enum_pairs_init(void) +{ + struct enum_pairs *snmp_enum; + + if ((snmp_enum = malloc(sizeof(struct enum_pairs))) == NULL) { + syslog(LOG_ERR, "malloc() failed: %s", strerror(errno)); + return (NULL); + } + + STAILQ_INIT(snmp_enum); + return (snmp_enum); +} + +/* + * Given a number and string, allocate memory for a (int, string) pair and add + * it to the given oid2str mapping entry's enum pairs list. + */ +int32_t +enum_pair_insert(struct enum_pairs *headp, int32_t enum_val, char *enum_str) +{ + struct enum_pair *e_new; + + if ((e_new = malloc(sizeof(struct enum_pair))) == NULL) { + syslog(LOG_ERR, "malloc() failed: %s", strerror(errno)); + return (-1); + } + + memset(e_new, 0, sizeof(struct enum_pair)); + + if ((e_new->enum_str = malloc(strlen(enum_str) + 1)) == NULL) { + syslog(LOG_ERR, "malloc() failed: %s", strerror(errno)); + free(e_new); + return (-1); + } + + e_new->enum_val = enum_val; + strlcpy(e_new->enum_str, enum_str, strlen(enum_str) + 1); + STAILQ_INSERT_TAIL(headp, e_new, link); + + return (1); + +} + +/* + * Insert an entry in a list - entries are lexicographicaly order by asn_oid. + * Returns 1 on success, -1 if list is not initialized, 0 if a matching oid already + * exists. Error cheking is left to calling function. + */ +static int +snmp_mapping_insert(struct snmp_mapping *headp, struct snmp_oid2str *entry) +{ + int32_t rc; + struct snmp_oid2str *temp, *prev; + + if (entry == NULL) + return(-1); + + if ((prev = SLIST_FIRST(headp)) == NULL || + asn_compare_oid(&(entry->var), &(prev->var)) < 0) { + SLIST_INSERT_HEAD(headp, entry, link); + return (1); + } else + rc = -1; /* Make the compiler happy. */ + + SLIST_FOREACH(temp, headp, link) { + if ((rc = asn_compare_oid(&(entry->var), &(temp->var))) <= 0) + break; + prev = temp; + rc = -1; + } + + switch (rc) { + case 0: + /* Ops, matching OIDs - hope the rest info also matches. */ + if (strncmp(temp->string, entry->string, entry->strlen)) { + syslog(LOG_INFO, "Matching OIDs with different string " + "mappings: old - %s, new - %s", temp->string, + entry->string); + return (-1); + } + /* + * Ok, we have that already. + * As long as the strings match - don't complain. + */ + return (0); + + case 1: + SLIST_INSERT_AFTER(temp, entry, link); + break; + + case -1: + SLIST_INSERT_AFTER(prev, entry, link); + break; + + default: + /* NOTREACHED */ + return (-1); + } + + return (1); +} + +int32_t +snmp_node_insert(struct snmp_toolinfo *snmptoolctx, struct snmp_oid2str *entry) +{ + if (snmptoolctx != NULL && snmptoolctx->mappings) + return (snmp_mapping_insert(&snmptoolctx->snmp_nodelist,entry)); + + return (-1); +} + +static int32_t +snmp_int_insert(struct snmp_toolinfo *snmptoolctx, struct snmp_oid2str *entry) +{ + if (snmptoolctx != NULL && snmptoolctx->mappings) + return (snmp_mapping_insert(&snmptoolctx->snmp_intlist,entry)); + + return (-1); +} + +static int32_t +snmp_oct_insert(struct snmp_toolinfo *snmptoolctx, struct snmp_oid2str *entry) +{ + if (snmptoolctx != NULL && snmptoolctx->mappings) + return (snmp_mapping_insert(&snmptoolctx->snmp_octlist,entry)); + + return (-1); +} + +static int32_t +snmp_oid_insert(struct snmp_toolinfo *snmptoolctx, struct snmp_oid2str *entry) +{ + if (snmptoolctx != NULL && snmptoolctx->mappings) + return (snmp_mapping_insert(&snmptoolctx->snmp_oidlist,entry)); + + return (-1); +} + +static int32_t +snmp_ip_insert(struct snmp_toolinfo *snmptoolctx, struct snmp_oid2str *entry) +{ + if (snmptoolctx != NULL && snmptoolctx->mappings) + return (snmp_mapping_insert(&snmptoolctx->snmp_iplist,entry)); + + return (-1); +} + +static int32_t +snmp_tick_insert(struct snmp_toolinfo *snmptoolctx, struct snmp_oid2str *entry) +{ + if (snmptoolctx != NULL && snmptoolctx->mappings) + return (snmp_mapping_insert(&snmptoolctx->snmp_ticklist,entry)); + + return (-1); +} + +static int32_t +snmp_cnt_insert(struct snmp_toolinfo *snmptoolctx, struct snmp_oid2str *entry) +{ + if (snmptoolctx != NULL && snmptoolctx->mappings) + return (snmp_mapping_insert(&snmptoolctx->snmp_cntlist,entry)); + + return (-1); +} + +static int32_t +snmp_gauge_insert(struct snmp_toolinfo *snmptoolctx, struct snmp_oid2str *entry) +{ + if (snmptoolctx != NULL && snmptoolctx->mappings) + return (snmp_mapping_insert(&snmptoolctx->snmp_gaugelist,entry)); + + return (-1); +} + +static int32_t +snmp_cnt64_insert(struct snmp_toolinfo *snmptoolctx, struct snmp_oid2str *entry) +{ + if (snmptoolctx != NULL && snmptoolctx->mappings) + return (snmp_mapping_insert(&snmptoolctx->snmp_cnt64list,entry)); + + return (-1); +} + +int32_t +snmp_enum_insert(struct snmp_toolinfo *snmptoolctx, struct snmp_oid2str *entry) +{ + if (snmptoolctx != NULL && snmptoolctx->mappings) + return (snmp_mapping_insert(&snmptoolctx->snmp_enumlist,entry)); + + return (-1); +} + +int32_t +snmp_leaf_insert(struct snmp_toolinfo *snmptoolctx, struct snmp_oid2str *entry) +{ + switch (entry->syntax) { + case SNMP_SYNTAX_INTEGER: + return (snmp_int_insert(snmptoolctx, entry)); + case SNMP_SYNTAX_OCTETSTRING: + return (snmp_oct_insert(snmptoolctx, entry)); + case SNMP_SYNTAX_OID: + return (snmp_oid_insert(snmptoolctx, entry)); + case SNMP_SYNTAX_IPADDRESS: + return (snmp_ip_insert(snmptoolctx, entry)); + case SNMP_SYNTAX_COUNTER: + return (snmp_cnt_insert(snmptoolctx, entry)); + case SNMP_SYNTAX_GAUGE: + return (snmp_gauge_insert(snmptoolctx, entry)); + case SNMP_SYNTAX_TIMETICKS: + return (snmp_tick_insert(snmptoolctx, entry)); + case SNMP_SYNTAX_COUNTER64: + return (snmp_cnt64_insert(snmptoolctx, entry)); + default: + break; + } + + return (-1); +} + +static int32_t +snmp_index_insert(struct snmp_idxlist *headp, struct index *idx) +{ + if (headp == NULL || idx == NULL) + return (-1); + + STAILQ_INSERT_TAIL(headp, idx, link); + return (1); +} + +int32_t +snmp_syntax_insert(struct snmp_idxlist *headp, struct enum_pairs *enums, + enum snmp_syntax syntax, enum snmp_tc tc) +{ + struct index *idx; + + if ((idx = malloc(sizeof(struct index))) == NULL) { + syslog(LOG_ERR, "malloc() failed: %s", strerror(errno)); + return (-1); + } + + memset(idx, 0, sizeof(struct index)); + + if (snmp_index_insert(headp, idx) < 0) { + free(idx); + return (-1); + } + + idx->syntax = syntax; + idx->snmp_enum = enums; + idx->tc = tc; + + return (1); +} + +int32_t +snmp_table_insert(struct snmp_toolinfo *snmptoolctx, + struct snmp_index_entry *entry) +{ + int32_t rc; + struct snmp_index_entry *temp, *prev; + + if (snmptoolctx == NULL || snmptoolctx->mappings == NULL || + entry == NULL) + return(-1); + + if ((prev = SLIST_FIRST(&snmptoolctx->snmp_tablelist)) == NULL || + asn_compare_oid(&(entry->var), &(prev->var)) < 0) { + SLIST_INSERT_HEAD(&snmptoolctx->snmp_tablelist, entry, link); + return (1); + } else + rc = -1; /* Make the compiler happy. */ + + SLIST_FOREACH(temp, &snmptoolctx->snmp_tablelist, link) { + if ((rc = asn_compare_oid(&(entry->var), &(temp->var))) <= 0) + break; + prev = temp; + rc = -1; + } + + switch (rc) { + case 0: + /* Ops, matching OIDs - hope the rest info also matches. */ + if (strncmp(temp->string, entry->string, entry->strlen)) { + syslog(LOG_INFO, "Matching OIDs with different string " + "mapping - old - %s, new - %s", temp->string, + entry->string); + return (-1); + } + return(0); + + case 1: + SLIST_INSERT_AFTER(temp, entry, link); + break; + + case -1: + SLIST_INSERT_AFTER(prev, entry, link); + break; + + default: + /* NOTREACHED */ + return (-1); + } + + return (1); +} + +struct enum_type * +snmp_enumtc_init(char *name) +{ + struct enum_type *enum_tc; + + if ((enum_tc = malloc(sizeof(struct enum_type))) == NULL) { + syslog(LOG_ERR, "malloc() failed: %s", strerror(errno)); + return (NULL); + } + + memset(enum_tc, 0, sizeof(struct enum_type)); + if ((enum_tc->name = malloc(strlen(name) + 1)) == NULL) { + syslog(LOG_ERR, "malloc() failed: %s", strerror(errno)); + free(enum_tc); + return (NULL); + } + strlcpy(enum_tc->name, name, strlen(name) + 1); + + return (enum_tc); +} + +void +snmp_enumtc_free(struct enum_type *tc) +{ + if (tc->name) + free(tc->name); + if (tc->snmp_enum) + enum_pairs_free(tc->snmp_enum); + free(tc); +} + +void +snmp_enumtc_insert(struct snmp_toolinfo *snmptoolctx, struct enum_type *entry) +{ + if (snmptoolctx == NULL || snmptoolctx->mappings == NULL) + return; /* XXX no error handling? */ + + SLIST_INSERT_HEAD(&snmptoolctx->snmp_tclist, entry, link); +} + +struct enum_type * +snmp_enumtc_lookup(struct snmp_toolinfo *snmptoolctx, char *name) +{ + struct enum_type *temp; + + if (snmptoolctx == NULL || snmptoolctx->mappings == NULL) + return (NULL); + + SLIST_FOREACH(temp, &snmptoolctx->snmp_tclist, link) { + if (strcmp(temp->name, name) == 0) + return (temp); + } + return (NULL); +} + +static void +snmp_mapping_dumplist(struct snmp_mapping *headp) +{ + char buf[ASN_OIDSTRLEN]; + struct snmp_oid2str *entry; + + if (headp == NULL) + return; + + SLIST_FOREACH(entry,headp,link) { + memset(buf, 0, sizeof(buf)); + asn_oid2str_r(&(entry->var), buf); + fprintf(stderr, "%s - %s - %d - %d - %d", buf, entry->string, + entry->syntax, entry->access ,entry->strlen); + fprintf(stderr," - %s \n", (entry->table_idx == NULL)? + "No table":entry->table_idx->string); + } +} + +static void +snmp_mapping_dumptable(struct snmp_table_index *headp) +{ + char buf[ASN_OIDSTRLEN]; + struct snmp_index_entry *entry; + + if (headp == NULL) + return; + + SLIST_FOREACH(entry, headp, link) { + memset(buf, 0, sizeof(buf)); + asn_oid2str_r(&(entry->var), buf); + fprintf(stderr,"%s - %s - %d - ", buf, entry->string, + entry->strlen); + snmp_dump_indexlist(&(entry->index_list)); + } +} + +void +snmp_mapping_dump(struct snmp_toolinfo *snmptoolctx /* int bits */) +{ + if (!_bsnmptools_debug) + return; + + if (snmptoolctx == NULL) { + fprintf(stderr,"No snmptool context!\n"); + return; + } + + if (snmptoolctx->mappings == NULL) { + fprintf(stderr,"No mappings!\n"); + return; + } + + fprintf(stderr,"snmp_nodelist:\n"); + snmp_mapping_dumplist(&snmptoolctx->snmp_nodelist); + + fprintf(stderr,"snmp_intlist:\n"); + snmp_mapping_dumplist(&snmptoolctx->snmp_intlist); + + fprintf(stderr,"snmp_octlist:\n"); + snmp_mapping_dumplist(&snmptoolctx->snmp_octlist); + + fprintf(stderr,"snmp_oidlist:\n"); + snmp_mapping_dumplist(&snmptoolctx->snmp_oidlist); + + fprintf(stderr,"snmp_iplist:\n"); + snmp_mapping_dumplist(&snmptoolctx->snmp_iplist); + + fprintf(stderr,"snmp_ticklist:\n"); + snmp_mapping_dumplist(&snmptoolctx->snmp_ticklist); + + fprintf(stderr,"snmp_cntlist:\n"); + snmp_mapping_dumplist(&snmptoolctx->snmp_cntlist); + + fprintf(stderr,"snmp_gaugelist:\n"); + snmp_mapping_dumplist(&snmptoolctx->snmp_gaugelist); + + fprintf(stderr,"snmp_cnt64list:\n"); + snmp_mapping_dumplist(&snmptoolctx->snmp_cnt64list); + + fprintf(stderr,"snmp_enumlist:\n"); + snmp_mapping_dumplist(&snmptoolctx->snmp_enumlist); + + fprintf(stderr,"snmp_tablelist:\n"); + snmp_mapping_dumptable(&snmptoolctx->snmp_tablelist); +} + +char * +enum_string_lookup(struct enum_pairs *headp, int32_t enum_val) +{ + struct enum_pair *temp; + + if (headp == NULL) + return (NULL); + + STAILQ_FOREACH(temp, headp, link) { + if (temp->enum_val == enum_val) + return (temp->enum_str); + } + + return (NULL); +} + +int32_t +enum_number_lookup(struct enum_pairs *headp, char *e_str) +{ + struct enum_pair *tmp; + + if (headp == NULL) + return (-1); + + STAILQ_FOREACH(tmp, headp, link) + if (strncmp(tmp->enum_str, e_str, strlen(tmp->enum_str)) == 0) + return (tmp->enum_val); + + return (-1); +} + +static int32_t +snmp_lookuplist_string(struct snmp_mapping *headp, struct snmp_object *s) +{ + struct snmp_oid2str *temp; + + if (headp == NULL) + return (-1); + + SLIST_FOREACH(temp, headp, link) + if (asn_compare_oid(&(temp->var), &(s->val.var)) == 0) + break; + + if ((s->info = temp) == NULL) + return (-1); + + return (1); +} + +/* provided an asn_oid find the corresponding string for it */ +static int32_t +snmp_lookup_leaf(struct snmp_mapping *headp, struct snmp_object *s) +{ + struct snmp_oid2str *temp; + + if (headp == NULL) + return (-1); + + SLIST_FOREACH(temp,headp,link) { + if ((asn_compare_oid(&(temp->var), &(s->val.var)) == 0) || + (asn_is_suboid(&(temp->var), &(s->val.var)))) { + s->info = temp; + return (1); + } + } + + return (-1); +} + +int32_t +snmp_lookup_leafstring(struct snmp_toolinfo *snmptoolctx, struct snmp_object *s) +{ + if (snmptoolctx == NULL || snmptoolctx->mappings == NULL || s == NULL) + return (-1); + + switch (s->val.syntax) { + case SNMP_SYNTAX_INTEGER: + return (snmp_lookup_leaf(&snmptoolctx->snmp_intlist, s)); + case SNMP_SYNTAX_OCTETSTRING: + return (snmp_lookup_leaf(&snmptoolctx->snmp_octlist, s)); + case SNMP_SYNTAX_OID: + return (snmp_lookup_leaf(&snmptoolctx->snmp_oidlist, s)); + case SNMP_SYNTAX_IPADDRESS: + return (snmp_lookup_leaf(&snmptoolctx->snmp_iplist, s)); + case SNMP_SYNTAX_COUNTER: + return (snmp_lookup_leaf(&snmptoolctx->snmp_cntlist, s)); + case SNMP_SYNTAX_GAUGE: + return (snmp_lookup_leaf( + &snmptoolctx->snmp_gaugelist, s)); + case SNMP_SYNTAX_TIMETICKS: + return (snmp_lookup_leaf( + &snmptoolctx->snmp_ticklist, s)); + case SNMP_SYNTAX_COUNTER64: + return (snmp_lookup_leaf( + &snmptoolctx->snmp_cnt64list, s)); + case SNMP_SYNTAX_NOSUCHOBJECT: + /* FALLTHROUGH */ + case SNMP_SYNTAX_NOSUCHINSTANCE: + /* FALLTHROUGH */ + case SNMP_SYNTAX_ENDOFMIBVIEW: + return (snmp_lookup_allstring(snmptoolctx, s)); + default: + warnx("Unknown syntax - %d", s->val.syntax); + break; + } + + return (-1); +} + +int32_t +snmp_lookup_enumstring(struct snmp_toolinfo *snmptoolctx, struct snmp_object *s) +{ + if (snmptoolctx == NULL || snmptoolctx->mappings == NULL || s == NULL) + return (-1); + + return (snmp_lookuplist_string(&snmptoolctx->snmp_enumlist, s)); +} + +int32_t +snmp_lookup_oidstring(struct snmp_toolinfo *snmptoolctx, struct snmp_object *s) +{ + if (snmptoolctx == NULL || snmptoolctx->mappings == NULL || s == NULL) + return (-1); + + return (snmp_lookuplist_string(&snmptoolctx->snmp_oidlist, s)); +} + +int32_t +snmp_lookup_nodestring(struct snmp_toolinfo *snmptoolctx, struct snmp_object *s) +{ + if (snmptoolctx == NULL || snmptoolctx->mappings == NULL || s == NULL) + return (-1); + + return (snmp_lookuplist_string(&snmptoolctx->snmp_nodelist, s)); +} + +int32_t +snmp_lookup_allstring(struct snmp_toolinfo *snmptoolctx, struct snmp_object *s) +{ + if (snmptoolctx == NULL || snmptoolctx->mappings == NULL) + return (-1); + + if (snmp_lookup_leaf(&snmptoolctx->snmp_intlist, s) > 0) + return (1); + if (snmp_lookup_leaf(&snmptoolctx->snmp_octlist, s) > 0) + return (1); + if (snmp_lookup_leaf(&snmptoolctx->snmp_oidlist, s) > 0) + return (1); + if (snmp_lookup_leaf(&snmptoolctx->snmp_iplist, s) > 0) + return (1); + if (snmp_lookup_leaf(&snmptoolctx->snmp_cntlist, s) > 0) + return (1); + if (snmp_lookup_leaf(&snmptoolctx->snmp_gaugelist, s) > 0) + return (1); + if (snmp_lookup_leaf(&snmptoolctx->snmp_ticklist, s) > 0) + return (1); + if (snmp_lookup_leaf(&snmptoolctx->snmp_cnt64list, s) > 0) + return (1); + if (snmp_lookuplist_string(&snmptoolctx->snmp_enumlist, s) > 0) + return (1); + if (snmp_lookuplist_string(&snmptoolctx->snmp_nodelist, s) > 0) + return (1); + + return (-1); +} + +int32_t +snmp_lookup_nonleaf_string(struct snmp_toolinfo *snmptoolctx, + struct snmp_object *s) +{ + if (snmptoolctx == NULL) + return (-1); + + if (snmp_lookuplist_string(&snmptoolctx->snmp_nodelist, s) > 0) + return (1); + if (snmp_lookuplist_string(&snmptoolctx->snmp_enumlist, s) > 0) + return (1); + + return (-1); +} + +static int32_t +snmp_lookup_oidlist(struct snmp_mapping *hp, struct snmp_object *s, char *oid) +{ + struct snmp_oid2str *temp; + + if (hp == NULL) + return (-1); + + SLIST_FOREACH(temp, hp, link) { + if (temp->strlen != strlen(oid)) + continue; + + if (strncmp(temp->string, oid, temp->strlen)) + continue; + + s->val.syntax = temp->syntax; + s->info = temp; + asn_append_oid(&(s->val.var), &(temp->var)); + return (1); + } + + return (-1); +} + +static int32_t +snmp_lookup_tablelist(struct snmp_toolinfo *snmptoolctx, + struct snmp_table_index *headp, struct snmp_object *s, char *oid) +{ + struct snmp_index_entry *temp; + + if (snmptoolctx == NULL || headp == NULL) + return (-1); + + SLIST_FOREACH(temp, headp, link) { + if (temp->strlen != strlen(oid)) + continue; + + if (strncmp(temp->string, oid, temp->strlen)) + continue; + + /* + * Another hack here - if we were given a table name + * return the corresponding pointer to it's entry. + * That should not change the reponce we'll get. + */ + s->val.syntax = SNMP_SYNTAX_NULL; + asn_append_oid(&(s->val.var), &(temp->var)); + if (snmp_lookup_leaf(&snmptoolctx->snmp_nodelist, s) > 0) + return (1); + else + return (-1); + } + + return (-1); +} + +int32_t +snmp_lookup_oidall(struct snmp_toolinfo *snmptoolctx, struct snmp_object *s, + char *oid) +{ + if (snmptoolctx == NULL || s == NULL || oid == NULL) + return (-1); + + if (snmp_lookup_oidlist(&snmptoolctx->snmp_intlist, s, oid) > 0) + return (1); + if (snmp_lookup_oidlist(&snmptoolctx->snmp_octlist, s, oid) > 0) + return (1); + if (snmp_lookup_oidlist(&snmptoolctx->snmp_oidlist, s, oid) > 0) + return (1); + if (snmp_lookup_oidlist(&snmptoolctx->snmp_iplist, s, oid) > 0) + return (1); + if (snmp_lookup_oidlist(&snmptoolctx->snmp_ticklist, s, oid) > 0) + return (1); + if (snmp_lookup_oidlist(&snmptoolctx->snmp_cntlist, s, oid) > 0) + return (1); + if (snmp_lookup_oidlist(&snmptoolctx->snmp_gaugelist, s, oid) > 0) + return (1); + if (snmp_lookup_oidlist(&snmptoolctx->snmp_cnt64list, s, oid) > 0) + return (1); + if (snmp_lookup_oidlist(&snmptoolctx->snmp_nodelist, s, oid) > 0) + return (1); + if (snmp_lookup_tablelist(snmptoolctx, &snmptoolctx->snmp_tablelist, + s, oid) > 0) + return (1); + + return (-1); +} + +int32_t +snmp_lookup_enumoid(struct snmp_toolinfo *snmptoolctx, struct snmp_object *s, + char *oid) +{ + if (snmptoolctx == NULL || s == NULL) + return (-1); + + return (snmp_lookup_oidlist(&snmptoolctx->snmp_enumlist, s, oid)); +} + +int32_t +snmp_lookup_oid(struct snmp_toolinfo *snmptoolctx, struct snmp_object *s, + char *oid) +{ + if (snmptoolctx == NULL || s == NULL) + return (-1); + + switch (s->val.syntax) { + case SNMP_SYNTAX_INTEGER: + return (snmp_lookup_oidlist(&snmptoolctx->snmp_intlist, + s, oid)); + case SNMP_SYNTAX_OCTETSTRING: + return (snmp_lookup_oidlist(&snmptoolctx->snmp_octlist, + s, oid)); + case SNMP_SYNTAX_OID: + return (snmp_lookup_oidlist(&snmptoolctx->snmp_oidlist, + s, oid)); + case SNMP_SYNTAX_IPADDRESS: + return (snmp_lookup_oidlist(&snmptoolctx->snmp_iplist, + s, oid)); + case SNMP_SYNTAX_COUNTER: + return (snmp_lookup_oidlist(&snmptoolctx->snmp_cntlist, + s, oid)); + case SNMP_SYNTAX_GAUGE: + return (snmp_lookup_oidlist(&snmptoolctx->snmp_gaugelist, + s, oid)); + case SNMP_SYNTAX_TIMETICKS: + return (snmp_lookup_oidlist(&snmptoolctx->snmp_ticklist, + s, oid)); + case SNMP_SYNTAX_COUNTER64: + return (snmp_lookup_oidlist(&snmptoolctx->snmp_cnt64list, + s, oid)); + case SNMP_SYNTAX_NULL: + return (snmp_lookup_oidlist(&snmptoolctx->snmp_nodelist, + s, oid)); + default: + warnx("Unknown syntax - %d", s->val.syntax); + break; + } + + return (-1); +} diff --git a/usr.sbin/bsnmpd/tools/libbsnmptools/bsnmptc.c b/usr.sbin/bsnmpd/tools/libbsnmptools/bsnmptc.c new file mode 100644 index 0000000..dc22c69 --- /dev/null +++ b/usr.sbin/bsnmpd/tools/libbsnmptools/bsnmptc.c @@ -0,0 +1,1287 @@ +/*- + * Copyright (c) 2006 The FreeBSD Project + * All rights reserved. + * + * Author: Shteryana Shopova <syrinx@FreeBSD.org> + * + * Redistribution of this software and documentation and use in source and + * binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code or documentation must retain the above + * copyright notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Textual conventions for OctetStrings + * + * $FreeBSD$ + */ + +#include <sys/param.h> +#include <sys/queue.h> +#include <sys/socket.h> +#include <sys/uio.h> + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <unistd.h> + +#include <arpa/inet.h> +#include <netinet/in.h> + +#include <bsnmp/asn1.h> +#include <bsnmp/snmp.h> +#include "bsnmptc.h" +#include "bsnmptools.h" + +/* OctetString, DisplayString */ +static char *snmp_oct2str(uint32_t, char *, char *); +static char *snmp_str2asn_oid(char *, struct asn_oid *); +static int parse_octetstring(struct snmp_value *, char *); + +/* DateAndTime */ +static char *snmp_octstr2date(uint32_t, char *, char *); +static char *snmp_date2asn_oid(char * , struct asn_oid *); +static int parse_dateandtime(struct snmp_value *, char *); + +/* PhysAddress */ +static char *snmp_oct2physAddr(uint32_t, char *, char *); +static char *snmp_addr2asn_oid(char *, struct asn_oid *); +static int parse_physaddress(struct snmp_value *, char *); + +/* NTPTimeStamp */ +static char *snmp_oct2ntp_ts(uint32_t, char *, char *); +static char *snmp_ntp_ts2asn_oid(char *, struct asn_oid *); +static int parse_ntp_ts(struct snmp_value *, char *); + +/* BridgeId */ +static char *snmp_oct2bridgeid(uint32_t, char *, char *); +static char *snmp_bridgeid2oct(char *, struct asn_oid *); +static int parse_bridge_id(struct snmp_value *, char *); + +/* BridgePortId */ +static char *snmp_oct2bport_id(uint32_t, char *, char *); +static char *snmp_bport_id2oct(char *, struct asn_oid *); +static int parse_bport_id(struct snmp_value *, char *); + +/* InetAddress */ +static char *snmp_oct2inetaddr(uint32_t len, char *octets, char *buf); +static char *snmp_inetaddr2oct(char *str, struct asn_oid *oid); +static int32_t parse_inetaddr(struct snmp_value *value, char *string); + +static char *snmp_oct2bits(uint32_t len, char *octets, char *buf); +static char *snmp_bits2oct(char *str, struct asn_oid *oid); +static int32_t parse_bits(struct snmp_value *value, char *string); + +struct snmp_text_conv { + enum snmp_tc tc; + const char *tc_str; + int32_t len; + snmp_oct2tc_f oct2tc; + snmp_tc2oid_f tc2oid; + snmp_tc2oct_f tc2oct; +} text_convs[] = { + { SNMP_STRING, "OctetString", SNMP_VAR_STRSZ, + snmp_oct2str, snmp_str2asn_oid, parse_octetstring }, + + { SNMP_DISPLAYSTRING, "DisplayString" , SNMP_VAR_STRSZ, + snmp_oct2str, snmp_str2asn_oid, parse_octetstring }, + + { SNMP_DATEANDTIME, "DateAndTime", SNMP_DATETIME_STRSZ, + snmp_octstr2date, snmp_date2asn_oid, parse_dateandtime }, + + { SNMP_PHYSADDR, "PhysAddress", SNMP_PHYSADDR_STRSZ, + snmp_oct2physAddr, snmp_addr2asn_oid, parse_physaddress }, + + { SNMP_ATMESI, "AtmESI", SNMP_PHYSADDR_STRSZ, + snmp_oct2physAddr, snmp_addr2asn_oid, parse_physaddress }, + + { SNMP_NTP_TIMESTAMP, "NTPTimeStamp", SNMP_NTP_TS_STRSZ, + snmp_oct2ntp_ts, snmp_ntp_ts2asn_oid, parse_ntp_ts }, + + { SNMP_MACADDRESS, "MacAddress", SNMP_PHYSADDR_STRSZ, + snmp_oct2physAddr, snmp_addr2asn_oid, parse_physaddress }, + + { SNMP_BRIDGE_ID, "BridgeId", SNMP_BRIDGEID_STRSZ, + snmp_oct2bridgeid, snmp_bridgeid2oct, parse_bridge_id }, + + { SNMP_BPORT_ID, "BridgePortId", SNMP_BPORT_STRSZ, + snmp_oct2bport_id, snmp_bport_id2oct, parse_bport_id }, + + { SNMP_INETADDRESS, "InetAddress", SNMP_INADDRS_STRSZ, + snmp_oct2inetaddr, snmp_inetaddr2oct, parse_inetaddr }, + + { SNMP_TC_OWN, "BITS", SNMP_VAR_STRSZ, + snmp_oct2bits, snmp_bits2oct, parse_bits }, + + { SNMP_UNKNOWN, "Unknown", SNMP_VAR_STRSZ, snmp_oct2str, + snmp_str2asn_oid, parse_octetstring } /* keep last */ +}; + +/* Common API */ +enum snmp_tc +snmp_get_tc(char *str) +{ + int i; + for (i = 0; i < SNMP_UNKNOWN; i++) { + if (!strncmp(text_convs[i].tc_str, str, + strlen(text_convs[i].tc_str))) + return (text_convs[i].tc); + } + + return (SNMP_STRING); +} + +char * +snmp_oct2tc(enum snmp_tc tc, uint32_t len, char *octets) +{ + uint32_t tc_len; + char * buf; + + if (tc < 0 || tc > SNMP_UNKNOWN) + tc = SNMP_UNKNOWN; + + if (text_convs[tc].len > 0) + tc_len = text_convs[tc].len; + else + tc_len = 2 * len + 3; + + if ((buf = malloc(tc_len)) == NULL ) { + syslog(LOG_ERR, "malloc failed - %s", strerror(errno)); + return (NULL); + } + + memset(buf, 0, tc_len); + if (text_convs[tc].oct2tc(len, octets, buf) == NULL) { + free(buf); + return (NULL); + } + + return (buf); +} + +char * +snmp_tc2oid(enum snmp_tc tc, char *str, struct asn_oid *oid) +{ + if (tc < 0 || tc > SNMP_UNKNOWN) + tc = SNMP_UNKNOWN; + + return (text_convs[tc].tc2oid(str, oid)); +} + +int32_t +snmp_tc2oct(enum snmp_tc tc, struct snmp_value *value, char *string) +{ + if (tc < 0 || tc > SNMP_UNKNOWN) + tc = SNMP_UNKNOWN; + + return (text_convs[tc].tc2oct(value, string)); +} + +/***************************************************** +* Basic OctetString type. +*/ +static char * +snmp_oct2str(uint32_t len, char *octets, char *buf) +{ + uint8_t binary = 0; + uint32_t i; + char *ptr; + + if (len > MAX_OCTSTRING_LEN || octets == NULL || buf == NULL) + return (NULL); + + for (ptr = buf, i = 0; i < len; i++) + if (!isprint(octets[i])) { + binary = 1; + buf += sprintf(buf, "0x"); + break; + } + + for (ptr = buf, i = 0; i < len; i++) + if (!binary) + ptr += sprintf(ptr, "%c", octets[i]); + else + ptr += sprintf(ptr, "%2.2x", (u_char)octets[i]); + + return (buf); +} + +static char * +snmp_str2asn_oid(char *str, struct asn_oid *oid) +{ + uint32_t i, len = 0; + + /* + * OctetStrings are allowed max length of ASN_MAXOCTETSTRING, + * but trying to index an entry with such a long OctetString + * will fail anyway. + */ + for (len = 0; len < ASN_MAXOIDLEN; len++) { + if (strchr(",]", *(str + len)) != NULL) + break; + } + + if (len >= ASN_MAXOIDLEN) + return (NULL); + + if (snmp_suboid_append(oid, (asn_subid_t) len) < 0) + return (NULL); + + for (i = 0; i < len; i++) + if (snmp_suboid_append(oid, (asn_subid_t) *(str + i)) < 0) + return (NULL); + + return (str + len); +} + +static int32_t +parse_octetstring(struct snmp_value *value, char *val) +{ + size_t len; + + if ((len = strlen(val)) >= MAX_OCTSTRING_LEN) { + warnx("Octetstring too long - %d is max allowed", + MAX_OCTSTRING_LEN - 1); + return (-1); + } + + value->v.octetstring.len = len; + + if((value->v.octetstring.octets = malloc(len)) == NULL) { + syslog(LOG_ERR,"malloc failed: %s", strerror(errno)); + return (-1); + } + + memcpy(value->v.octetstring.octets, val, len); + value->syntax = SNMP_SYNTAX_OCTETSTRING; + + return (0); +} + +/************************************************************* + * DateAndTime + ************************************************************* + * rfc 2579 specification: + * DateAndTime ::= TEXTUAL-CONVENTION + * DISPLAY-HINT "2d-1d-1d,1d:1d:1d.1d,1a1d:1d" + * STATUS current + * DESCRIPTION + * "A date-time specification. + * + * field octets contents range + * ----- ------ -------- ----- + * 1 1-2 year* 0..65536 + * 2 3 month 1..12 + * 3 4 day 1..31 + * 4 5 hour 0..23 + * 5 6 minutes 0..59 + * 6 7 seconds 0..60 + * (use 60 for leap-second) + * 7 8 deci-seconds 0..9 + * 8 9 direction from UTC '+' / '-' + * 9 10 hours from UTC* 0..13 + * 10 11 minutes from UTC 0..59 + * + * * Notes: + * - the value of year is in network-byte order + * - daylight saving time in New Zealand is +13 + * + * For example, Tuesday May 26, 1992 at 1:30:15 PM EDT would be + * displayed as: + * + * 1992-5-26,13:30:15.0,-4:0 + */ +static char * +snmp_octstr2date(uint32_t len, char *octets, char *buf) +{ + int year; + char *ptr; + + if (len != SNMP_DATETIME_OCTETS || octets == NULL || buf == NULL) + return (NULL); + + buf[0]= '\0'; + year = (octets[0] << 8); + year += (octets[1]); + + ptr = buf; + ptr += sprintf(ptr, "%4.4d-%.2d-%.2d, ", year, octets[2],octets[3]); + ptr += sprintf(ptr, "%2.2d:%2.2d:%2.2d.%.2d, ", octets[4],octets[5], + octets[6],octets[7]); + ptr += sprintf(ptr, "%c%.2d:%.2d", octets[8],octets[9],octets[10]); + + return (buf); +} + +static char * +snmp_date2asn_oid(char *str, struct asn_oid *oid) +{ + char *endptr, *ptr; + uint32_t v; + int32_t saved_errno; + + if (snmp_suboid_append(oid, (asn_subid_t) SNMP_DATETIME_OCTETS) < 0) + return (NULL); + + /* Read 'YYYY-' and write it in two subs. */ + ptr = str; + saved_errno = errno; + errno = 0; + v = strtoul(ptr, &endptr, 10); + if (v > 0xffff) + goto error; + else + errno = saved_errno; + if (*endptr != '-') + goto error1; + if (snmp_suboid_append(oid, (asn_subid_t) ((v & 0xff00) >> 8)) < 0) + return (NULL); + if (snmp_suboid_append(oid, (asn_subid_t) (v & 0xff)) < 0) + return (NULL); + + /* 'MM-' */ + ptr = endptr + 1; + saved_errno = errno; + v = strtoul(ptr, &endptr, 10); + if (errno != 0) + goto error; + else + errno = saved_errno; + if (*endptr != '-') + goto error1; + if (snmp_suboid_append(oid, (asn_subid_t) v) < 0) + return (NULL); + + /* 'DD,' */ + ptr = endptr + 1; + saved_errno = errno; + v = strtoul(ptr, &endptr, 10); + if (errno != 0) + goto error; + else + errno = saved_errno; + if (*endptr != '-') + goto error1; + if (snmp_suboid_append(oid, (asn_subid_t) v) < 0) + return (NULL); + + /* 'HH:' */ + ptr = endptr + 1; + saved_errno = errno; + v = strtoul(ptr, &endptr, 10); + if (errno != 0) + goto error; + else + errno = saved_errno; + if (*endptr != ':') + goto error1; + if (snmp_suboid_append(oid, (asn_subid_t) v) < 0) + return (NULL); + + /* 'MM:' */ + ptr = endptr + 1; + saved_errno = errno; + v = strtoul(ptr, &endptr, 10); + if (errno != 0) + goto error; + else + errno = saved_errno; + if (*endptr != ':') + goto error1; + if (snmp_suboid_append(oid, (asn_subid_t) v) < 0) + return (NULL); + + /* 'SS.' */ + ptr = endptr + 1; + saved_errno = errno; + v = strtoul(ptr, &endptr, 10); + if (errno != 0) + goto error; + else + errno = saved_errno; + if (*endptr != '.') + goto error1; + if (snmp_suboid_append(oid, (asn_subid_t) v) < 0) + return (NULL); + + /* 'M(mseconds),' */ + ptr = endptr + 1; + saved_errno = errno; + v = strtoul(ptr, &endptr, 10); + if (errno != 0) + goto error; + else + errno = saved_errno; + if (*endptr != ',') + goto error1; + if (snmp_suboid_append(oid, (asn_subid_t) v) < 0) + return (NULL); + + /* 'UTC' - optional */ + ptr = endptr + 1; + if (*ptr == 'U' && *(ptr + 1) == 'T' && *(ptr + 1) == 'C') + ptr += 3; + + /* '+/-' */ + if (*ptr == '-' || *ptr == '+') { + if (snmp_suboid_append(oid, (asn_subid_t) (*ptr)) < 0) + return (NULL); + } else + goto error1; + + /* 'HH:' */ + ptr = endptr + 1; + saved_errno = errno; + v = strtoul(ptr, &endptr, 10); + if (errno != 0) + goto error; + else + errno = saved_errno; + if (*endptr != ':') + goto error1; + if (snmp_suboid_append(oid, (asn_subid_t) v) < 0) + return (NULL); + + /* 'MM' - last one - ignore endptr here. */ + ptr = endptr + 1; + saved_errno = errno; + v = strtoul(ptr, &endptr, 10); + if (errno != 0) + goto error; + else + errno = saved_errno; + if (snmp_suboid_append(oid, (asn_subid_t) v) < 0) + return (NULL); + + return (endptr); + + error: + errno = saved_errno; + error1: + warnx("Date value %s not supported", str); + return (NULL); +} + +/* Read a DateAndTime string eg. 1992-5-26,13:30:15.0,-4:0. */ +static int32_t +parse_dateandtime(struct snmp_value *sv, char *val) +{ + char *endptr; + uint32_t v; + uint8_t date[SNMP_DATETIME_OCTETS]; + + /* 'YYYY-' */ + v = strtoul(val, &endptr, 10); + if (v > 0xffff || *endptr != '-') + goto error; + date[0] = ((v & 0xff00) >> 8); + date[1] = (v & 0xff); + val = endptr + 1; + + /* 'MM-' */ + v = strtoul(val, &endptr, 10); + if (v == 0 || v > 12 || *endptr != '-') + goto error; + date[2] = v; + val = endptr + 1; + + /* 'DD,' */ + v = strtoul(val, &endptr, 10); + if (v == 0 || v > 31 || *endptr != ',') + goto error; + date[3] = v; + val = endptr + 1; + + /* 'HH:' */ + v = strtoul(val, &endptr, 10); + if (v > 23 || *endptr != ':') + goto error; + date[4] = v; + val = endptr + 1; + + /* 'MM:' */ + v = strtoul(val, &endptr, 10); + if (v > 59 || *endptr != ':') + goto error; + date[5] = v; + val = endptr + 1; + + /* 'SS.' */ + v = strtoul(val, &endptr, 10); + if (v > 60 || *endptr != '.') + goto error; + date[6] = v; + val = endptr + 1; + + /* '(deci-)s,' */ + v = strtoul(val, &endptr, 10); + if (v > 9 || *endptr != ',') + goto error; + date[7] = v; + val = endptr + 1; + + /* offset - '+/-' */ + if (*val != '-' && *val != '+') + goto error; + date[8] = (uint8_t) *val; + val = endptr + 1; + + /* 'HH:' - offset from UTC */ + v = strtoul(val, &endptr, 10); + if (v > 13 || *endptr != ':') + goto error; + date[9] = v; + val = endptr + 1; + + /* 'MM'\0'' offset from UTC */ + v = strtoul(val, &endptr, 10); + if (v > 59 || *endptr != '\0') + goto error; + date[10] = v; + + if ((sv->v.octetstring.octets = malloc(SNMP_DATETIME_OCTETS)) == NULL) { + warnx("malloc() failed - %s", strerror(errno)); + return (-1); + } + + sv->v.octetstring.len = SNMP_DATETIME_OCTETS; + memcpy(sv->v.octetstring.octets, date, SNMP_DATETIME_OCTETS); + sv->syntax = SNMP_SYNTAX_OCTETSTRING; + return (1); + + error: + warnx("Date value %s not supported", val); + return (-1); +} + +/************************************************************** + * PhysAddress + */ +static char * +snmp_oct2physAddr(uint32_t len, char *octets, char *buf) +{ + char *ptr; + uint32_t i; + + if (len != SNMP_PHYSADDR_OCTETS || octets == NULL || buf == NULL) + return (NULL); + + buf[0]= '\0'; + + ptr = buf; + ptr += sprintf(ptr, "%2.2x", octets[0]); + for (i = 1; i < 6; i++) + ptr += sprintf(ptr, ":%2.2x", octets[i]); + + return (buf); +} + +static char * +snmp_addr2asn_oid(char *str, struct asn_oid *oid) +{ + char *endptr, *ptr; + uint32_t v, i; + int saved_errno; + + if (snmp_suboid_append(oid, (asn_subid_t) SNMP_PHYSADDR_OCTETS) < 0) + return (NULL); + + ptr = str; + for (i = 0; i < 5; i++) { + saved_errno = errno; + v = strtoul(ptr, &endptr, 16); + errno = saved_errno; + if (v > 0xff) { + warnx("Integer value %s not supported", str); + return (NULL); + } + if (*endptr != ':') { + warnx("Failed adding oid - %s",str); + return (NULL); + } + if (snmp_suboid_append(oid, (asn_subid_t) v) < 0) + return (NULL); + ptr = endptr + 1; + } + + /* The last one - don't check the ending char here. */ + saved_errno = errno; + v = strtoul(ptr, &endptr, 16); + errno = saved_errno; + if (v > 0xff) { + warnx("Integer value %s not supported", str); + return (NULL); + } + if (snmp_suboid_append(oid, (asn_subid_t) v) < 0) + return (NULL); + + return (endptr); +} + +static int32_t +parse_physaddress(struct snmp_value *sv, char *val) +{ + char *endptr; + int32_t i; + uint32_t v; + uint8_t phys_addr[SNMP_PHYSADDR_OCTETS]; + + for (i = 0; i < 5; i++) { + v = strtoul(val, &endptr, 16); + if (v > 0xff) { + warnx("Integer value %s not supported", val); + return (-1); + } + if(*endptr != ':') { + warnx("Failed reading octet - %s", val); + return (-1); + } + phys_addr[i] = v; + val = endptr + 1; + } + + /* The last one - don't check the ending char here. */ + v = strtoul(val, &endptr, 16); + if (v > 0xff) { + warnx("Integer value %s not supported", val); + return (-1); + } + phys_addr[5] = v; + + if ((sv->v.octetstring.octets = malloc(SNMP_PHYSADDR_OCTETS)) == NULL) { + syslog(LOG_ERR,"malloc failed: %s", strerror(errno)); + return (-1); + } + + sv->v.octetstring.len = SNMP_PHYSADDR_OCTETS; + memcpy(sv->v.octetstring.octets, phys_addr, SNMP_PHYSADDR_OCTETS); + sv->syntax = SNMP_SYNTAX_OCTETSTRING; + return (1); +} + +/************************************************************** + * NTPTimeStamp + ************************************************************** + * NTP MIB, Revision 0.2, 7/25/97: + * NTPTimeStamp ::= TEXTUAL-CONVENTION + * DISPLAY-HINT "4x.4x" + * STATUS current + * DESCRIPTION + * "" + * SYNTAX OCTET STRING (SIZE(8)) + */ +static char * +snmp_oct2ntp_ts(uint32_t len, char *octets, char *buf) +{ + char *ptr; + uint32_t i; + + if (len != SNMP_NTP_TS_OCTETS || octets == NULL || buf == NULL) + return (NULL); + + buf[0]= '\0'; + + ptr = buf; + i = octets[0] * 1000 + octets[1] * 100 + octets[2] * 10 + octets[3]; + ptr += sprintf(ptr, "%4.4d", i); + i = octets[4] * 1000 + octets[5] * 100 + octets[6] * 10 + octets[7]; + ptr += sprintf(ptr, ".%4.4d", i); + + return (buf); +} + +static char * +snmp_ntp_ts2asn_oid(char *str, struct asn_oid *oid) +{ + char *endptr, *ptr; + uint32_t v, i, d; + struct asn_oid suboid; + int saved_errno; + + if (snmp_suboid_append(oid, (asn_subid_t) SNMP_NTP_TS_OCTETS) < 0) + return (NULL); + + ptr = str; + saved_errno = errno; + v = strtoul(ptr, &endptr, 10); + if (errno != 0 || (v / 1000) > 9) { + warnx("Integer value %s not supported", str); + errno = saved_errno; + return (NULL); + } else + errno = saved_errno; + + if (*endptr != '.') { + warnx("Failed adding oid - %s",str); + return (NULL); + } + + memset(&suboid, 0, sizeof(struct asn_oid)); + suboid.len = SNMP_NTP_TS_OCTETS; + + for (i = 0, d = 1000; i < 4; i++) { + suboid.subs[i] = v / d; + v = v % d; + d = d / 10; + } + + ptr = endptr + 1; + saved_errno = errno; + v = strtoul(ptr, &endptr, 10); + if (errno != 0 || (v / 1000) > 9) { + warnx("Integer value %s not supported", str); + errno = saved_errno; + return (NULL); + } else + errno = saved_errno; + + for (i = 0, d = 1000; i < 4; i++) { + suboid.subs[i + 4] = v / d; + v = v % d; + d = d / 10; + } + + asn_append_oid(oid, &suboid); + return (endptr); +} + +static int32_t +parse_ntp_ts(struct snmp_value *sv, char *val) +{ + char *endptr; + int32_t i, d, saved_errno; + uint32_t v; + uint8_t ntp_ts[SNMP_NTP_TS_OCTETS]; + + saved_errno = errno; + v = strtoul(val, &endptr, 10); + if (errno != 0 || (v / 1000) > 9) { + saved_errno = errno; + warnx("Integer value %s not supported", val); + return (-1); + } else + saved_errno = errno; + + if (*endptr != '.') { + warnx("Failed reading octet - %s", val); + return (-1); + } + + for (i = 0, d = 1000; i < 4; i++) { + ntp_ts[i] = v / d; + v = v % d; + d = d / 10; + } + val = endptr + 1; + + saved_errno = errno; + v = strtoul(val, &endptr, 10); + if (errno != 0 || (v / 1000) > 9) { + saved_errno = errno; + warnx("Integer value %s not supported", val); + return (-1); + } else + saved_errno = errno; + + for (i = 0, d = 1000; i < 4; i++) { + ntp_ts[i + 4] = v / d; + v = v % d; + d = d / 10; + } + + if ((sv->v.octetstring.octets = malloc(SNMP_NTP_TS_OCTETS)) == NULL) { + syslog(LOG_ERR,"malloc failed: %s", strerror(errno)); + return (-1); + } + + sv->v.octetstring.len = SNMP_NTP_TS_OCTETS; + memcpy(sv->v.octetstring.octets, ntp_ts, SNMP_NTP_TS_OCTETS); + sv->syntax = SNMP_SYNTAX_OCTETSTRING; + return (1); +} + +/************************************************************** + * BridgeId + ************************************************************** + * BRIDGE-MIB, REVISION "200509190000Z" + * BridgeId ::= TEXTUAL-CONVENTION + * STATUS current + * DESCRIPTION + * "The Bridge-Identifier, as used in the Spanning Tree + * Protocol, to uniquely identify a bridge. Its first two + * octets (in network byte order) contain a priority value, + * and its last 6 octets contain the MAC address used to + * refer to a bridge in a unique fashion (typically, the + * numerically smallest MAC address of all ports on the + * bridge)." + * SYNTAX OCTET STRING (SIZE (8)) + */ +static char * +snmp_oct2bridgeid(uint32_t len, char *octets, char *buf) +{ + char *ptr; + uint32_t i, priority; + + if (len != SNMP_BRIDGEID_OCTETS || octets == NULL || buf == NULL) + return (NULL); + + buf[0]= '\0'; + ptr = buf; + + priority = octets[0] << 8; + priority += octets[1]; + if (priority > SNMP_MAX_BRIDGE_PRIORITY) { + warnx("Invalid bridge priority %d", priority); + return (NULL); + } else + ptr += sprintf(ptr, "%d.", octets[0]); + + ptr += sprintf(ptr, "%2.2x", octets[2]); + + for (i = 1; i < 6; i++) + ptr += sprintf(ptr, ":%2.2x", octets[i + 2]); + + return (buf); +} + +static char * +snmp_bridgeid2oct(char *str, struct asn_oid *oid) +{ + char *endptr, *ptr; + uint32_t v, i; + int32_t saved_errno; + + if (snmp_suboid_append(oid, (asn_subid_t) SNMP_BRIDGEID_OCTETS) < 0) + return (NULL); + + ptr = str; + /* Read the priority. */ + saved_errno = errno; + v = strtoul(ptr, &endptr, 10); + errno = 0; + + if (v > SNMP_MAX_BRIDGE_PRIORITY || errno != 0 || *endptr != '.') { + errno = saved_errno; + warnx("Bad bridge priority value %d", v); + return (NULL); + } + + if (snmp_suboid_append(oid, (asn_subid_t) (v & 0xff00)) < 0) + return (NULL); + + if (snmp_suboid_append(oid, (asn_subid_t) (v & 0xff)) < 0) + return (NULL); + + ptr = endptr + 1; + for (i = 0; i < 5; i++) { + saved_errno = errno; + v = strtoul(ptr, &endptr, 16); + errno = saved_errno; + if (v > 0xff) { + warnx("Integer value %s not supported", str); + return (NULL); + } + if (*endptr != ':') { + warnx("Failed adding oid - %s",str); + return (NULL); + } + if (snmp_suboid_append(oid, (asn_subid_t) v) < 0) + return (NULL); + ptr = endptr + 1; + } + + /* The last one - don't check the ending char here. */ + saved_errno = errno; + v = strtoul(ptr, &endptr, 16); + errno = saved_errno; + if (v > 0xff) { + warnx("Integer value %s not supported", str); + return (NULL); + } + if (snmp_suboid_append(oid, (asn_subid_t) v) < 0) + return (NULL); + + return (endptr); +} + +static int32_t +parse_bridge_id(struct snmp_value *sv, char *string) +{ + char *ptr, *endptr; + int32_t i, saved_errno; + uint32_t v; + uint8_t bridge_id[SNMP_BRIDGEID_OCTETS]; + + ptr = string; + /* Read the priority. */ + saved_errno = errno; + errno = 0; + v = strtoul(string, &endptr, 10); + errno = saved_errno; + + if (v > SNMP_MAX_BRIDGE_PRIORITY || errno != 0 || *endptr != '.') { + errno = saved_errno; + warnx("Bad bridge priority value %d", v); + return (-1); + } + + bridge_id[0] = (v & 0xff00); + bridge_id[1] = (v & 0xff); + + string = endptr + 1; + + for (i = 0; i < 5; i++) { + v = strtoul(string, &endptr, 16); + if (v > 0xff) { + warnx("Integer value %s not supported", string); + return (-1); + } + if(*endptr != ':') { + warnx("Failed reading octet - %s", string); + return (-1); + } + bridge_id[i + 2] = v; + string = endptr + 1; + } + + /* The last one - don't check the ending char here. */ + v = strtoul(string, &endptr, 16); + if (v > 0xff) { + warnx("Integer value %s not supported", string); + return (-1); + } + bridge_id[7] = v; + + if ((sv->v.octetstring.octets = malloc(SNMP_BRIDGEID_OCTETS)) == NULL) { + syslog(LOG_ERR,"malloc failed: %s", strerror(errno)); + return (-1); + } + + sv->v.octetstring.len = SNMP_BRIDGEID_OCTETS; + memcpy(sv->v.octetstring.octets, bridge_id, SNMP_BRIDGEID_OCTETS); + sv->syntax = SNMP_SYNTAX_OCTETSTRING; + return (1); +} + +/************************************************************** + * BridgePortId + ************************************************************** + * BEGEMOT-BRIDGE-MIB, LAST-UPDATED "200608100000Z" + * BridgePortId ::= TEXTUAL-CONVENTION + * DISPLAY-HINT "1x.1x" + * STATUS current + * DESCRIPTION + * "A port identifier that contains a bridge port's STP priority + * in the first octet and the port number in the second octet." + * SYNTAX OCTET STRING (SIZE(2)) + */ +static char * +snmp_oct2bport_id(uint32_t len, char *octets, char *buf) +{ + char *ptr; + + if (len != SNMP_BPORT_OCTETS || octets == NULL || buf == NULL) + return (NULL); + + buf[0]= '\0'; + ptr = buf; + + ptr += sprintf(ptr, "%d.", octets[0]); + ptr += sprintf(ptr, "%d", octets[1]); + + return (buf); +} + +static char * +snmp_bport_id2oct(char *str, struct asn_oid *oid) +{ + char *endptr, *ptr; + uint32_t v; + int saved_errno; + + if (snmp_suboid_append(oid, (asn_subid_t) SNMP_BPORT_OCTETS) < 0) + return (NULL); + + ptr = str; + /* Read the priority. */ + saved_errno = errno; + v = strtoul(ptr, &endptr, 10); + errno = 0; + + if (v > SNMP_MAX_BPORT_PRIORITY || errno != 0 || *endptr != '.') { + errno = saved_errno; + warnx("Bad bridge port priority value %d", v); + return (NULL); + } + + if (snmp_suboid_append(oid, (asn_subid_t) v) < 0) + return (NULL); + + saved_errno = errno; + v = strtoul(ptr, &endptr, 16); + errno = saved_errno; + + if (v > 0xff) { + warnx("Bad port number - %d", v); + return (NULL); + } + + if (snmp_suboid_append(oid, (asn_subid_t) v) < 0) + return (NULL); + + return (endptr); +} + +static int32_t +parse_bport_id(struct snmp_value *value, char *string) +{ + char *ptr, *endptr; + int saved_errno; + uint32_t v; + uint8_t bport_id[SNMP_BPORT_OCTETS]; + + ptr = string; + /* Read the priority. */ + saved_errno = errno; + errno = 0; + v = strtoul(string, &endptr, 10); + errno = saved_errno; + + if (v > SNMP_MAX_BPORT_PRIORITY || errno != 0 || *endptr != '.') { + errno = saved_errno; + warnx("Bad bridge port priority value %d", v); + return (-1); + } + + bport_id[0] = v; + + string = endptr + 1; + v = strtoul(string, &endptr, 16); + if (v > 0xff) { + warnx("Bad port number - %d", v); + return (-1); + } + + bport_id[1] = v; + + if ((value->v.octetstring.octets = malloc(SNMP_BPORT_OCTETS)) == NULL) { + syslog(LOG_ERR,"malloc failed: %s", strerror(errno)); + return (-1); + } + + value->v.octetstring.len = SNMP_BPORT_OCTETS; + memcpy(value->v.octetstring.octets, bport_id, SNMP_BPORT_OCTETS); + value->syntax = SNMP_SYNTAX_OCTETSTRING; + return (1); +} +/************************************************************** + * InetAddress + ************************************************************** + * INET-ADDRESS-MIB, REVISION "200502040000Z" + * InetAddress ::= TEXTUAL-CONVENTION + * STATUS current + * DESCRIPTION + * "Denotes a generic Internet address. + * + * An InetAddress value is always interpreted within the context + * of an InetAddressType value. Every usage of the InetAddress + * textual convention is required to specify the InetAddressType + * object that provides the context. It is suggested that the + * InetAddressType object be logically registered before the + * object(s) that use the InetAddress textual convention, if + * they appear in the same logical row. + * + * The value of an InetAddress object must always be + * consistent with the value of the associated InetAddressType + * object. Attempts to set an InetAddress object to a value + * inconsistent with the associated InetAddressType + * must fail with an inconsistentValue error. + * + * When this textual convention is used as the syntax of an + * index object, there may be issues with the limit of 128 + * sub-identifiers specified in SMIv2, STD 58. In this case, + * the object definition MUST include a 'SIZE' clause to + * limit the number of potential instance sub-identifiers; + * otherwise the applicable constraints MUST be stated in + * the appropriate conceptual row DESCRIPTION clauses, or + * in the surrounding documentation if there is no single + * DESCRIPTION clause that is appropriate." + * SYNTAX OCTET STRING (SIZE (0..255)) + ************************************************************** + * TODO: FIXME!!! syrinx: Since we do not support checking the + * consistency of a varbinding based on the value of a previous + * one, try to guess the type of address based on the + * OctetString SIZE - 4 for IPv4, 16 for IPv6, others currently + * not supported. + */ +static char * +snmp_oct2inetaddr(uint32_t len, char *octets, char *buf) +{ + int af; + void *ip; + struct in_addr ipv4; + struct in6_addr ipv6; + + if (len > MAX_OCTSTRING_LEN || octets == NULL || buf == NULL) + return (NULL); + + switch (len) { + /* XXX: FIXME - IPv4*/ + case 4: + memcpy(&ipv4.s_addr, octets, sizeof(ipv4.s_addr)); + af = AF_INET; + ip = &ipv4; + break; + + /* XXX: FIXME - IPv4*/ + case 16: + memcpy(ipv6.s6_addr, octets, sizeof(ipv6.s6_addr)); + af = AF_INET6; + ip = &ipv6; + break; + + default: + return (NULL); + } + + if (inet_ntop(af, ip, buf, SNMP_INADDRS_STRSZ) == NULL) { + warnx("inet_ntop failed - %s", strerror(errno)); + return (NULL); + } + + return (buf); +} + +static char * +snmp_inetaddr2oct(char *str, struct asn_oid *oid) +{ + return (NULL); +} + +static int32_t +parse_inetaddr(struct snmp_value *value, char *string) +{ + return (-1); +} + +/************************************************************** + * SNMP BITS type - XXX: FIXME + **************************************************************/ +static char * +snmp_oct2bits(uint32_t len, char *octets, char *buf) +{ + int i, bits; + uint64_t value; + + if (len > sizeof(value) || octets == NULL || buf == NULL) + return (NULL); + + for (i = len, value = 0, bits = 0; i > 0; i--, bits += 8) + value += octets[i] << bits; + + buf[0]= '\0'; + sprintf(buf, "0x%llx.",(long long unsigned) value); + + return (buf); +} + +static char * +snmp_bits2oct(char *str, struct asn_oid *oid) +{ + char *endptr; + int i, size, bits, saved_errno; + uint64_t v, mask = 0xFF00000000000000; + + saved_errno = errno; + errno = 0; + + v = strtoull(str, &endptr, 16); + if (errno != 0) { + warnx("Bad BITS value %s - %s", str, strerror(errno)); + errno = saved_errno; + return (NULL); + } + + bits = 8; + /* Determine length - up to 8 octets supported so far. */ + for (size = sizeof(v); size > 0; size--) { + if ((v & mask) != 0) + break; + mask = mask >> bits; + } + + if (size == 0) + size = 1; + + if (snmp_suboid_append(oid, (asn_subid_t) size) < 0) + return (NULL); + + for (i = 0, bits = 0; i < size; i++, bits += 8) + if (snmp_suboid_append(oid, + (asn_subid_t)((v & mask) >> bits)) < 0) + return (NULL); + + return (endptr); +} + +static int32_t +parse_bits(struct snmp_value *value, char *string) +{ + char *endptr; + int i, size, bits, saved_errno; + uint64_t v, mask = 0xFF00000000000000; + + saved_errno = errno; + errno = 0; + + v = strtoull(string, &endptr, 16); + + if (errno != 0) { + warnx("Bad BITS value %s - %s", string, strerror(errno)); + errno = saved_errno; + return (-1); + } + + bits = 8; + /* Determine length - up to 8 octets supported so far. */ + for (size = sizeof(v); size > 0; size--) { + if ((v & mask) != 0) + break; + mask = mask >> bits; + } + + if (size == 0) + size = 1; + + if ((value->v.octetstring.octets = malloc(size)) == NULL) { + syslog(LOG_ERR, "malloc failed: %s", strerror(errno)); + return (-1); + } + + value->v.octetstring.len = size; + for (i = 0, bits = 0; i < size; i++, bits += 8) + value->v.octetstring.octets[i] = (v & mask) >> bits; + value->syntax = SNMP_SYNTAX_OCTETSTRING; + return (1); +} diff --git a/usr.sbin/bsnmpd/tools/libbsnmptools/bsnmptc.h b/usr.sbin/bsnmpd/tools/libbsnmptools/bsnmptc.h new file mode 100644 index 0000000..fd06676 --- /dev/null +++ b/usr.sbin/bsnmpd/tools/libbsnmptools/bsnmptc.h @@ -0,0 +1,95 @@ +/*- + * Copyright (c) 2006 The FreeBSD Project + * All rights reserved. + * + * Author: Shteryana Shopova <syrinx@FreeBSD.org> + * + * Redistribution of this software and documentation and use in source and + * binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code or documentation must retain the above + * copyright notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Textual conventions for snmp + * + * $FreeBSD$ + */ + +#ifndef _BSNMP_TEXT_CONV_H_ +#define _BSNMP_TEXT_CONV_H_ + +/* Variable display length string. */ +#define SNMP_VAR_STRSZ -1 + +/* + * 11 bytes - octets that represent DateAndTime Textual convention + * and the size of string used to diplay that. + */ +#define SNMP_DATETIME_OCTETS 11 +#define SNMP_DATETIME_STRSZ 32 + +/* + * 6 bytes - octets that represent PhysAddress Textual convention + * and the size of string used to diplay that. + */ +#define SNMP_PHYSADDR_OCTETS 6 +#define SNMP_PHYSADDR_STRSZ 19 + +/* NTPTimeStamp. */ +#define SNMP_NTP_TS_OCTETS 8 +#define SNMP_NTP_TS_STRSZ 10 + +/* BridgeId. */ +#define SNMP_BRIDGEID_OCTETS 8 +#define SNMP_BRIDGEID_STRSZ 25 +#define SNMP_MAX_BRIDGE_PRIORITY 65535 + +/* BridgePortId. */ +#define SNMP_BPORT_OCTETS 2 +#define SNMP_BPORT_STRSZ 7 +#define SNMP_MAX_BPORT_PRIORITY 255 + +/* InetAddress. */ +#define SNMP_INADDRS_STRSZ INET6_ADDRSTRLEN + +enum snmp_tc { + SNMP_STRING = 0, + SNMP_DISPLAYSTRING = 1, + SNMP_DATEANDTIME = 2, + SNMP_PHYSADDR = 3, + SNMP_ATMESI = 4, + SNMP_NTP_TIMESTAMP = 5, + SNMP_MACADDRESS = 6, + SNMP_BRIDGE_ID = 7, + SNMP_BPORT_ID = 8, + SNMP_INETADDRESS = 9, + SNMP_TC_OWN = 10, + SNMP_UNKNOWN, /* keep last */ +}; + +typedef char * (*snmp_oct2tc_f) (uint32_t len, char *octs, char *buf); +typedef char * (*snmp_tc2oid_f) (char *str, struct asn_oid *oid); +typedef int32_t (*snmp_tc2oct_f) (struct snmp_value *value, char *string); + +enum snmp_tc snmp_get_tc(char *str); +char *snmp_oct2tc(enum snmp_tc tc, uint32_t len, char *octets); +char *snmp_tc2oid(enum snmp_tc tc, char *str, struct asn_oid *oid); +int32_t snmp_tc2oct(enum snmp_tc tc, struct snmp_value *value, char *string); + +#endif /* _BSNMP_TEXT_CONV_H_ */ diff --git a/usr.sbin/bsnmpd/tools/libbsnmptools/bsnmptools.c b/usr.sbin/bsnmpd/tools/libbsnmptools/bsnmptools.c new file mode 100755 index 0000000..52aa1a9 --- /dev/null +++ b/usr.sbin/bsnmpd/tools/libbsnmptools/bsnmptools.c @@ -0,0 +1,2132 @@ +/*- + * Copyright (c) 2005-2006 The FreeBSD Project + * All rights reserved. + * + * Author: Shteryana Shopova <syrinx@FreeBSD.org> + * + * Redistribution of this software and documentation and use in source and + * binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code or documentation must retain the above + * copyright notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Helper functions for snmp client tools + * + * $FreeBSD$ + */ + +#include <sys/param.h> +#include <sys/queue.h> +#include <sys/uio.h> + +#include <assert.h> +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <unistd.h> + +#include <bsnmp/asn1.h> +#include <bsnmp/snmp.h> +#include <bsnmp/snmpclient.h> +#include "bsnmptc.h" +#include "bsnmptools.h" + +/* Internal varibale to turn on library debugging for testing and to + * find bugs. It is not exported via the header file. + * XXX should we cover it by some #ifdef BSNMPTOOLS_DEBUG? */ +int _bsnmptools_debug = 0; + +/* Default files to import mapping from if none explicitly provided. */ +#define bsnmpd_defs "/usr/share/snmp/defs/tree.def" +#define mibII_defs "/usr/share/snmp/defs/mibII_tree.def" + +/* + * The .iso.org.dod oid that has to be prepended to every OID when requesting + * a value. + */ +const struct asn_oid IsoOrgDod_OID = { + 3, { 1, 3, 6 } +}; + + +#define SNMP_ERR_UNKNOWN 0 + +/* + * An array of error strings corresponding to error definitions from libbsnmp. + */ +static const struct { + const char *str; + int32_t error; +} error_strings[] = { + { "Unknown", SNMP_ERR_UNKNOWN }, + { "Too big ", SNMP_ERR_TOOBIG }, + { "No such Name", SNMP_ERR_NOSUCHNAME }, + { "Bad Value", SNMP_ERR_BADVALUE }, + { "Readonly", SNMP_ERR_READONLY }, + { "General error", SNMP_ERR_GENERR }, + { "No access", SNMP_ERR_NO_ACCESS }, + { "Wrong type", SNMP_ERR_WRONG_TYPE }, + { "Wrong length", SNMP_ERR_WRONG_LENGTH }, + { "Wrong encoding", SNMP_ERR_WRONG_ENCODING }, + { "Wrong value", SNMP_ERR_WRONG_VALUE }, + { "No creation", SNMP_ERR_NO_CREATION }, + { "Inconsistent value", SNMP_ERR_INCONS_VALUE }, + { "Resource unavailable", SNMP_ERR_RES_UNAVAIL }, + { "Commit failed", SNMP_ERR_COMMIT_FAILED }, + { "Undo failed", SNMP_ERR_UNDO_FAILED }, + { "Authorization error", SNMP_ERR_AUTH_ERR }, + { "Not writable", SNMP_ERR_NOT_WRITEABLE }, + { "Inconsistent name", SNMP_ERR_INCONS_NAME }, + { NULL, 0 } +}; + +/* This one and any following are exceptions. */ +#define SNMP_SYNTAX_UNKNOWN SNMP_SYNTAX_NOSUCHOBJECT + +static const struct { + const char *str; + enum snmp_syntax stx; +} syntax_strings[] = { + { "Null", SNMP_SYNTAX_NULL }, + { "Integer", SNMP_SYNTAX_INTEGER }, + { "OctetString", SNMP_SYNTAX_OCTETSTRING }, + { "OID", SNMP_SYNTAX_OID }, + { "IpAddress", SNMP_SYNTAX_IPADDRESS }, + { "Counter32", SNMP_SYNTAX_COUNTER }, + { "Gauge", SNMP_SYNTAX_GAUGE }, + { "TimeTicks", SNMP_SYNTAX_TIMETICKS }, + { "Counter64", SNMP_SYNTAX_COUNTER64 }, + { "Unknown", SNMP_SYNTAX_UNKNOWN }, +}; + +int +snmptool_init(struct snmp_toolinfo *snmptoolctx) +{ + char *str; + size_t slen; + + memset(snmptoolctx, 0, sizeof(struct snmp_toolinfo)); + snmptoolctx->objects = 0; + snmptoolctx->mappings = NULL; + snmptoolctx->flags = SNMP_PDU_GET; /* XXX */ + SLIST_INIT(&snmptoolctx->filelist); + snmp_client_init(&snmp_client); + SET_MAXREP(snmptoolctx, SNMP_MAX_REPETITIONS); + + if (add_filename(snmptoolctx, bsnmpd_defs, &IsoOrgDod_OID, 0) < 0) + warnx("Error adding file %s to list", bsnmpd_defs); + + if (add_filename(snmptoolctx, mibII_defs, &IsoOrgDod_OID, 0) < 0) + warnx("Error adding file %s to list", mibII_defs); + + /* Read the environment */ + if ((str = getenv("SNMPAUTH")) != NULL) { + slen = strlen(str); + if (slen == strlen("md5") && strcasecmp(str, "md5") == 0) + snmp_client.user.auth_proto = SNMP_AUTH_HMAC_MD5; + else if (slen == strlen("sha")&& strcasecmp(str, "sha") == 0) + snmp_client.user.auth_proto = SNMP_AUTH_HMAC_SHA; + else if (slen != 0) + warnx("Bad authentication type - %s in SNMPAUTH", str); + } + + if ((str = getenv("SNMPPRIV")) != NULL) { + slen = strlen(str); + if (slen == strlen("des") && strcasecmp(str, "des") == 0) + snmp_client.user.priv_proto = SNMP_PRIV_DES; + else if (slen == strlen("aes")&& strcasecmp(str, "aes") == 0) + snmp_client.user.priv_proto = SNMP_PRIV_AES; + else if (slen != 0) + warnx("Bad privacy type - %s in SNMPPRIV", str); + } + + if ((str = getenv("SNMPUSER")) != NULL) { + if ((slen = strlen(str)) > sizeof(snmp_client.user.sec_name)) { + warnx("Username too long - %s in SNMPUSER", str); + return (-1); + } + if (slen > 0) { + strlcpy(snmp_client.user.sec_name, str, + sizeof(snmp_client.user.sec_name)); + snmp_client.version = SNMP_V3; + } + } + + if ((str = getenv("SNMPPASSWD")) != NULL) { + if ((slen = strlen(str)) > MAXSTR) + slen = MAXSTR - 1; + if ((snmptoolctx->passwd = malloc(slen + 1)) == NULL) { + warnx("malloc() failed - %s", strerror(errno)); + return (-1); + } + if (slen > 0) + strlcpy(snmptoolctx->passwd, str, slen + 1); + } + + return (0); +} + +#define OBJECT_IDX_LIST(o) o->info->table_idx->index_list + +/* + * Walk through the file list and import string<->oid mappings from each file. + */ +int32_t +snmp_import_all(struct snmp_toolinfo *snmptoolctx) +{ + int32_t fc; + struct fname *tmp; + + if (snmptoolctx == NULL) + return (-1); + + if (ISSET_NUMERIC(snmptoolctx)) + return (0); + + if ((snmptoolctx->mappings = snmp_mapping_init()) == NULL) + return (-1); + + fc = 0; + if (SLIST_EMPTY(&snmptoolctx->filelist)) { + warnx("No files to read OID <-> string conversions from"); + return (-1); + } else { + SLIST_FOREACH(tmp, &snmptoolctx->filelist, link) { + if (tmp->done) + continue; + if (snmp_import_file(snmptoolctx, tmp) < 0) { + fc = -1; + break; + } + fc++; + } + } + + snmp_mapping_dump(snmptoolctx); + return (fc); +} + +/* + * Add a filename to the file list - the initial idea of keeping a list with all + * files to read OIDs from was that an application might want to have loaded in + * memory the OIDs from a single file only and when done with them read the OIDs + * from another file. This is not used yet but might be a good idea at some + * point. Size argument is number of bytes in string including trailing '\0', + * not string length. + */ +int32_t +add_filename(struct snmp_toolinfo *snmptoolctx, const char *filename, + const struct asn_oid *cut, int32_t done) +{ + char *fstring; + struct fname *entry; + + if (snmptoolctx == NULL) + return (-1); + + /* Make sure file was not in list. */ + SLIST_FOREACH(entry, &snmptoolctx->filelist, link) { + if (strncmp(entry->name, filename, strlen(entry->name)) == 0) + return (0); + } + + if ((fstring = malloc(strlen(filename) + 1)) == NULL) { + warnx("malloc() failed - %s", strerror(errno)); + return (-1); + } + + if ((entry = malloc(sizeof(struct fname))) == NULL) { + warnx("malloc() failed - %s", strerror(errno)); + free(fstring); + return (-1); + } + + memset(entry, 0, sizeof(struct fname)); + + if (cut != NULL) + asn_append_oid(&(entry->cut), cut); + strlcpy(fstring, filename, strlen(filename) + 1); + entry->name = fstring; + entry->done = done; + SLIST_INSERT_HEAD(&snmptoolctx->filelist, entry, link); + + return (1); +} + +void +free_filelist(struct snmp_toolinfo *snmptoolctx) +{ + struct fname *f; + + if (snmptoolctx == NULL) + return; /* XXX error handling */ + + while ((f = SLIST_FIRST(&snmptoolctx->filelist)) != NULL) { + SLIST_REMOVE_HEAD(&snmptoolctx->filelist, link); + if (f->name) + free(f->name); + free(f); + } +} + +static char +isvalid_fchar(char c, int pos) +{ + if (isalpha(c)|| c == '/'|| c == '_' || c == '.' || c == '~' || + (pos != 0 && isdigit(c))){ + return (c); + } + + if (c == '\0') + return (0); + + if (!isascii(c) || !isprint(c)) + warnx("Unexpected character %#2x", (u_int) c); + else + warnx("Illegal character '%c'", c); + + return (-1); +} + +/* + * Re-implement getsubopt from scratch, because the second argument is broken + * and will not compile with WARNS=5. + * Copied from src/contrib/bsnmp/snmpd/main.c. + */ +static int +getsubopt1(char **arg, const char *const *options, char **valp, char **optp) +{ + static const char *const delim = ",\t "; + u_int i; + char *ptr; + + *optp = NULL; + + /* Skip leading junk. */ + for (ptr = *arg; *ptr != '\0'; ptr++) + if (strchr(delim, *ptr) == NULL) + break; + if (*ptr == '\0') { + *arg = ptr; + return (-1); + } + *optp = ptr; + + /* Find the end of the option. */ + while (*++ptr != '\0') + if (strchr(delim, *ptr) != NULL || *ptr == '=') + break; + + if (*ptr != '\0') { + if (*ptr == '=') { + *ptr++ = '\0'; + *valp = ptr; + while (*ptr != '\0' && strchr(delim, *ptr) == NULL) + ptr++; + if (*ptr != '\0') + *ptr++ = '\0'; + } else + *ptr++ = '\0'; + } + + *arg = ptr; + + for (i = 0; *options != NULL; options++, i++) + if (strcmp(*optp, *options) == 0) + return (i); + return (-1); +} + +static int32_t +parse_path(char *value) +{ + int32_t i, len; + + if (value == NULL) + return (-1); + + for (len = 0; len < MAXPATHLEN; len++) { + i = isvalid_fchar(*(value + len), len) ; + + if (i == 0) + break; + else if (i < 0) + return (-1); + } + + if (len >= MAXPATHLEN || value[len] != '\0') { + warnx("Bad pathname - '%s'", value); + return (-1); + } + + return (len); +} + +static int32_t +parse_flist(struct snmp_toolinfo *snmptoolctx, char *value, char *path, + const struct asn_oid *cut) +{ + int32_t namelen; + char filename[MAXPATHLEN + 1]; + + if (value == NULL) + return (-1); + + do { + memset(filename, 0, MAXPATHLEN + 1); + + if (isalpha(*value) && (path == NULL || path[0] == '\0')) { + strlcpy(filename, SNMP_DEFS_DIR, MAXPATHLEN + 1); + namelen = strlen(SNMP_DEFS_DIR); + } else if (path != NULL){ + strlcpy(filename, path, MAXPATHLEN + 1); + namelen = strlen(path); + } else + namelen = 0; + + for ( ; namelen < MAXPATHLEN; value++) { + if (isvalid_fchar(*value, namelen) > 0) { + filename[namelen++] = *value; + continue; + } + + if (*value == ',' ) + value++; + else if (*value == '\0') + ; + else { + if (!isascii(*value) || !isprint(*value)) + warnx("Unexpected character %#2x in" + " filename", (u_int) *value); + else + warnx("Illegal character '%c' in" + " filename", *value); + return (-1); + } + + filename[namelen]='\0'; + break; + } + + if ((namelen == MAXPATHLEN) && (filename[MAXPATHLEN] != '\0')) { + warnx("Filename %s too long", filename); + return (-1); + } + + if (add_filename(snmptoolctx, filename, cut, 0) < 0) { + warnx("Error adding file %s to list", filename); + return (-1); + } + } while (*value != '\0'); + + return(1); +} + +static int32_t +parse_ascii(char *ascii, uint8_t *binstr, size_t binlen) +{ + int32_t alen, count, saved_errno, i; + uint32_t val; + char dptr[3]; + + /* Filter 0x at the beginning */ + if ((alen = strlen(ascii)) > 2 && ascii[0] == '0' && ascii[1] == 'x') + i = 2; + else + i = 0; + + saved_errno = errno; + errno = 0; + for (count = 0; i < alen; i += 2) { + /* XXX: consider strlen(ascii) % 2 != 0 */ + dptr[0] = ascii[i]; + dptr[1] = ascii[i + 1]; + dptr[2] = '\0'; + if ((val = strtoul(dptr, NULL, 16)) > 0xFF || errno != 0) { + errno = saved_errno; + return (-1); + } + binstr[count] = (uint8_t) val; + if (++count >= binlen) { + warnx("Key %s too long - truncating to %zu octets", + ascii, binlen); + break; + } + } + + return (count); +} + +/* + * Functions to parse common input options for client tools and fill in the + * snmp_client structure. + */ +int32_t +parse_authentication(struct snmp_toolinfo *snmptoolctx, char *opt_arg) +{ + int32_t count, subopt; + char *val, *option; + const char *const subopts[] = { + "proto", + "key", + NULL + }; + + assert(opt_arg != NULL); + count = 1; + while ((subopt = getsubopt1(&opt_arg, subopts, &val, &option)) != EOF) { + switch (subopt) { + case 0: + if (val == NULL) { + warnx("Suboption 'proto' requires an argument"); + return (-1); + } + if (strlen(val) != 3) { + warnx("Unknown auth protocol - %s", val); + return (-1); + } + if (strncasecmp("md5", val, strlen("md5")) == 0) + snmp_client.user.auth_proto = + SNMP_AUTH_HMAC_MD5; + else if (strncasecmp("sha", val, strlen("sha")) == 0) + snmp_client.user.auth_proto = + SNMP_AUTH_HMAC_SHA; + else { + warnx("Unknown auth protocol - %s", val); + return (-1); + } + break; + case 1: + if (val == NULL) { + warnx("Suboption 'key' requires an argument"); + return (-1); + } + if (parse_ascii(val, snmp_client.user.auth_key, + SNMP_AUTH_KEY_SIZ) < 0) { + warnx("Bad authentication key- %s", val); + return (-1); + } + break; + default: + warnx("Unknown suboption - '%s'", suboptarg); + return (-1); + } + count += 1; + } + return (2/* count */); +} + +int32_t +parse_privacy(struct snmp_toolinfo *snmptoolctx, char *opt_arg) +{ + int32_t count, subopt; + char *val, *option; + const char *const subopts[] = { + "proto", + "key", + NULL + }; + + assert(opt_arg != NULL); + count = 1; + while ((subopt = getsubopt1(&opt_arg, subopts, &val, &option)) != EOF) { + switch (subopt) { + case 0: + if (val == NULL) { + warnx("Suboption 'proto' requires an argument"); + return (-1); + } + if (strlen(val) != 3) { + warnx("Unknown privacy protocol - %s", val); + return (-1); + } + if (strncasecmp("aes", val, strlen("aes")) == 0) + snmp_client.user.priv_proto = SNMP_PRIV_AES; + else if (strncasecmp("des", val, strlen("des")) == 0) + snmp_client.user.priv_proto = SNMP_PRIV_DES; + else { + warnx("Unknown privacy protocol - %s", val); + return (-1); + } + break; + case 1: + if (val == NULL) { + warnx("Suboption 'key' requires an argument"); + return (-1); + } + if (parse_ascii(val, snmp_client.user.priv_key, + SNMP_PRIV_KEY_SIZ) < 0) { + warnx("Bad privacy key- %s", val); + return (-1); + } + break; + default: + warnx("Unknown suboption - '%s'", suboptarg); + return (-1); + } + count += 1; + } + return (2/* count */); +} + +int32_t +parse_context(struct snmp_toolinfo *snmptoolctx, char *opt_arg) +{ + int32_t count, subopt; + char *val, *option; + const char *const subopts[] = { + "context", + "context-engine", + NULL + }; + + assert(opt_arg != NULL); + count = 1; + while ((subopt = getsubopt1(&opt_arg, subopts, &val, &option)) != EOF) { + switch (subopt) { + case 0: + if (val == NULL) { + warnx("Suboption 'context' - no argument"); + return (-1); + } + strlcpy(snmp_client.cname, val, SNMP_CONTEXT_NAME_SIZ); + break; + case 1: + if (val == NULL) { + warnx("Suboption 'context-engine' - no argument"); + return (-1); + } + if ((snmp_client.clen = parse_ascii(val, + snmp_client.cengine, SNMP_ENGINE_ID_SIZ)) < 0) { + warnx("Bad EngineID - %s", val); + return (-1); + } + break; + default: + warnx("Unknown suboption - '%s'", suboptarg); + return (-1); + } + count += 1; + } + return (2/* count */); +} + +int32_t +parse_user_security(struct snmp_toolinfo *snmptoolctx, char *opt_arg) +{ + int32_t count, subopt, saved_errno; + char *val, *option; + const char *const subopts[] = { + "engine", + "engine-boots", + "engine-time", + "name", + NULL + }; + + assert(opt_arg != NULL); + count = 1; + while ((subopt = getsubopt1(&opt_arg, subopts, &val, &option)) != EOF) { + switch (subopt) { + case 0: + if (val == NULL) { + warnx("Suboption 'engine' - no argument"); + return (-1); + } + snmp_client.engine.engine_len = parse_ascii(val, + snmp_client.engine.engine_id, SNMP_ENGINE_ID_SIZ); + if (snmp_client.engine.engine_len < 0) { + warnx("Bad EngineID - %s", val); + return (-1); + } + break; + case 1: + if (val == NULL) { + warnx("Suboption 'engine-boots' - no argument"); + return (-1); + } + saved_errno = errno; + errno = 0; + snmp_client.engine.engine_boots = strtoul(val, NULL, 10); + if (errno != 0) { + warnx("Bad 'engine-boots' value %s - %s", val, + strerror(errno)); + errno = saved_errno; + return (-1); + } + errno = saved_errno; + break; + case 2: + if (val == NULL) { + warnx("Suboption 'engine-time' - no argument"); + return (-1); + } + saved_errno = errno; + errno = 0; + snmp_client.engine.engine_time = strtoul(val, NULL, 10); + if (errno != 0) { + warnx("Bad 'engine-time' value %s - %s", val, + strerror(errno)); + errno = saved_errno; + return (-1); + } + errno = saved_errno; + break; + case 3: + strlcpy(snmp_client.user.sec_name, val, + SNMP_ADM_STR32_SIZ); + break; + default: + warnx("Unknown suboption - '%s'", suboptarg); + return (-1); + } + count += 1; + } + return (2/* count */); +} + +int32_t +parse_file(struct snmp_toolinfo *snmptoolctx, char *opt_arg) +{ + assert(opt_arg != NULL); + + if (parse_flist(snmptoolctx, opt_arg, NULL, &IsoOrgDod_OID) < 0) + return (-1); + + return (2); +} + +int32_t +parse_include(struct snmp_toolinfo *snmptoolctx, char *opt_arg) +{ + char path[MAXPATHLEN + 1]; + int32_t cut_dflt, len, subopt; + struct asn_oid cut; + char *val, *option; + const char *const subopts[] = { + "cut", + "path", + "file", + NULL + }; + +#define INC_CUT 0 +#define INC_PATH 1 +#define INC_LIST 2 + + assert(opt_arg != NULL); + + /* if (opt == 'i') + free_filelist(snmptoolctx, ); */ + /* + * This function should be called only after getopt(3) - otherwise if + * no previous validation of opt_arg strlen() may not return what is + * expected. + */ + + path[0] = '\0'; + memset(&cut, 0, sizeof(struct asn_oid)); + cut_dflt = -1; + + while ((subopt = getsubopt1(&opt_arg, subopts, &val, &option)) != EOF) { + switch (subopt) { + case INC_CUT: + if (val == NULL) { + warnx("Suboption 'cut' requires an argument"); + return (-1); + } else { + if (snmp_parse_numoid(val, &cut) < 0) + return (-1); + } + cut_dflt = 1; + break; + + case INC_PATH: + if ((len = parse_path(val)) < 0) + return (-1); + strlcpy(path, val, len + 1); + break; + + case INC_LIST: + if (val == NULL) + return (-1); + if (cut_dflt == -1) + len = parse_flist(snmptoolctx, val, path, &IsoOrgDod_OID); + else + len = parse_flist(snmptoolctx, val, path, &cut); + if (len < 0) + return (-1); + break; + + default: + warnx("Unknown suboption - '%s'", suboptarg); + return (-1); + } + } + + /* XXX: Fix me - returning two is wrong here */ + return (2); +} + +int32_t +parse_server(char *opt_arg) +{ + assert(opt_arg != NULL); + + if (snmp_parse_server(&snmp_client, opt_arg) < 0) + return (-1); + + if (snmp_client.trans > SNMP_TRANS_UDP && snmp_client.chost == NULL) { + if ((snmp_client.chost = malloc(strlen(SNMP_DEFAULT_LOCAL + 1))) + == NULL) { + syslog(LOG_ERR, "malloc() failed: %s", strerror(errno)); + return (-1); + } + strcpy(snmp_client.chost, SNMP_DEFAULT_LOCAL); + } + + return (2); +} + +int32_t +parse_timeout(char *opt_arg) +{ + int32_t v, saved_errno; + + assert(opt_arg != NULL); + + saved_errno = errno; + errno = 0; + + v = strtol(opt_arg, NULL, 10); + if (errno != 0) { + warnx( "Error parsing timeout value - %s", strerror(errno)); + errno = saved_errno; + return (-1); + } + + snmp_client.timeout.tv_sec = v; + errno = saved_errno; + return (2); +} + +int32_t +parse_retry(char *opt_arg) +{ + uint32_t v; + int32_t saved_errno; + + assert(opt_arg != NULL); + + saved_errno = errno; + errno = 0; + + v = strtoul(opt_arg, NULL, 10); + if (errno != 0) { + warnx("Error parsing retries count - %s", strerror(errno)); + errno = saved_errno; + return (-1); + } + + snmp_client.retries = v; + errno = saved_errno; + return (2); +} + +int32_t +parse_version(char *opt_arg) +{ + uint32_t v; + int32_t saved_errno; + + assert(opt_arg != NULL); + + saved_errno = errno; + errno = 0; + + v = strtoul(opt_arg, NULL, 10); + if (errno != 0) { + warnx("Error parsing version - %s", strerror(errno)); + errno = saved_errno; + return (-1); + } + + switch (v) { + case 1: + snmp_client.version = SNMP_V1; + break; + case 2: + snmp_client.version = SNMP_V2c; + break; + case 3: + snmp_client.version = SNMP_V3; + break; + default: + warnx("Unsupported SNMP version - %u", v); + errno = saved_errno; + return (-1); + } + + errno = saved_errno; + return (2); +} + +int32_t +parse_local_path(char *opt_arg) +{ + assert(opt_arg != NULL); + + if (sizeof(opt_arg) > sizeof(SNMP_LOCAL_PATH)) { + warnx("Filename too long - %s", opt_arg); + return (-1); + } + + strlcpy(snmp_client.local_path, opt_arg, sizeof(SNMP_LOCAL_PATH)); + return (2); +} + +int32_t +parse_buflen(char *opt_arg) +{ + uint32_t size; + int32_t saved_errno; + + assert(opt_arg != NULL); + + saved_errno = errno; + errno = 0; + + size = strtoul(opt_arg, NULL, 10); + if (errno != 0) { + warnx("Error parsing buffer size - %s", strerror(errno)); + errno = saved_errno; + return (-1); + } + + if (size > MAX_BUFF_SIZE) { + warnx("Buffer size too big - %d max allowed", MAX_BUFF_SIZE); + errno = saved_errno; + return (-1); + } + + snmp_client.txbuflen = snmp_client.rxbuflen = size; + errno = saved_errno; + return (2); +} + +int32_t +parse_debug(void) +{ + snmp_client.dump_pdus = 1; + return (1); +} + +int32_t +parse_discovery(struct snmp_toolinfo *snmptoolctx) +{ + SET_EDISCOVER(snmptoolctx); + snmp_client.version = SNMP_V3; + return (1); +} + +int32_t +parse_local_key(struct snmp_toolinfo *snmptoolctx) +{ + SET_LOCALKEY(snmptoolctx); + snmp_client.version = SNMP_V3; + return (1); +} + +int32_t +parse_num_oids(struct snmp_toolinfo *snmptoolctx) +{ + SET_NUMERIC(snmptoolctx); + return (1); +} + +int32_t +parse_output(struct snmp_toolinfo *snmptoolctx, char *opt_arg) +{ + assert(opt_arg != NULL); + + if (strlen(opt_arg) > strlen("verbose")) { + warnx( "Invalid output option - %s",opt_arg); + return (-1); + } + + if (strncasecmp(opt_arg, "short", strlen(opt_arg)) == 0) + SET_OUTPUT(snmptoolctx, OUTPUT_SHORT); + else if (strncasecmp(opt_arg, "verbose", strlen(opt_arg)) == 0) + SET_OUTPUT(snmptoolctx, OUTPUT_VERBOSE); + else if (strncasecmp(opt_arg,"tabular", strlen(opt_arg)) == 0) + SET_OUTPUT(snmptoolctx, OUTPUT_TABULAR); + else if (strncasecmp(opt_arg, "quiet", strlen(opt_arg)) == 0) + SET_OUTPUT(snmptoolctx, OUTPUT_QUIET); + else { + warnx( "Invalid output option - %s", opt_arg); + return (-1); + } + + return (2); +} + +int32_t +parse_errors(struct snmp_toolinfo *snmptoolctx) +{ + SET_RETRY(snmptoolctx); + return (1); +} + +int32_t +parse_skip_access(struct snmp_toolinfo *snmptoolctx) +{ + SET_ERRIGNORE(snmptoolctx); + return (1); +} + +char * +snmp_parse_suboid(char *str, struct asn_oid *oid) +{ + char *endptr; + asn_subid_t suboid; + + if (*str == '.') + str++; + + if (*str < '0' || *str > '9') + return (str); + + do { + suboid = strtoul(str, &endptr, 10); + if ((asn_subid_t) suboid > ASN_MAXID) { + warnx("Suboid %u > ASN_MAXID", suboid); + return (NULL); + } + if (snmp_suboid_append(oid, suboid) < 0) + return (NULL); + str = endptr + 1; + } while (*endptr == '.'); + + return (endptr); +} + +static char * +snmp_int2asn_oid(char *str, struct asn_oid *oid) +{ + char *endptr; + int32_t v, saved_errno; + + saved_errno = errno; + errno = 0; + + v = strtol(str, &endptr, 10); + if (errno != 0) { + warnx("Integer value %s not supported - %s", str, + strerror(errno)); + errno = saved_errno; + return (NULL); + } + errno = saved_errno; + + if (snmp_suboid_append(oid, (asn_subid_t) v) < 0) + return (NULL); + + return (endptr); +} + +/* It is a bit weird to have a table indexed by OID but still... */ +static char * +snmp_oid2asn_oid(struct snmp_toolinfo *snmptoolctx, char *str, + struct asn_oid *oid) +{ + int32_t i; + char string[MAXSTR], *endptr; + struct snmp_object obj; + + for (i = 0; i < MAXSTR; i++) + if (isalpha (*(str + i)) == 0) + break; + + endptr = str + i; + memset(&obj, 0, sizeof(struct snmp_object)); + if (i == 0) { + if ((endptr = snmp_parse_suboid(str, &(obj.val.var))) == NULL) + return (NULL); + if (snmp_suboid_append(oid, (asn_subid_t) obj.val.var.len) < 0) + return (NULL); + } else { + strlcpy(string, str, i + 1); + string[i] = '\0'; + if (snmp_lookup_enumoid(snmptoolctx, &obj, string) < 0) { + warnx("Unknown string - %s",string); + return (NULL); + } + free(string); + } + + asn_append_oid(oid, &(obj.val.var)); + return (endptr); +} + +static char * +snmp_ip2asn_oid(char *str, struct asn_oid *oid) +{ + uint32_t v; + int32_t i; + char *endptr, *ptr; + + ptr = str; + for (i = 0; i < 4; i++) { + v = strtoul(ptr, &endptr, 10); + if (v > 0xff) + return (NULL); + if (*endptr != '.' && strchr("],\0", *endptr) == NULL && i != 3) + return (NULL); + if (snmp_suboid_append(oid, (asn_subid_t) v) < 0) + return (NULL); + ptr = endptr + 1; + } + + return (endptr); +} + +/* 32-bit counter, gauge, timeticks. */ +static char * +snmp_uint2asn_oid(char *str, struct asn_oid *oid) +{ + char *endptr; + uint32_t v; + int32_t saved_errno; + + saved_errno = errno; + errno = 0; + + v = strtoul(str, &endptr, 10); + if (errno != 0) { + warnx("Integer value %s not supported - %s\n", str, + strerror(errno)); + errno = saved_errno; + return (NULL); + } + errno = saved_errno; + if (snmp_suboid_append(oid, (asn_subid_t) v) < 0) + return (NULL); + + return (endptr); +} + +static char * +snmp_cnt64_2asn_oid(char *str, struct asn_oid *oid) +{ + char *endptr; + uint64_t v; + int32_t saved_errno; + + saved_errno = errno; + errno = 0; + + v = strtoull(str, &endptr, 10); + + if (errno != 0) { + warnx("Integer value %s not supported - %s", str, + strerror(errno)); + errno = saved_errno; + return (NULL); + } + errno = saved_errno; + if (snmp_suboid_append(oid, (asn_subid_t) (v & 0xffffffff)) < 0) + return (NULL); + + if (snmp_suboid_append(oid, (asn_subid_t) (v >> 32)) < 0) + return (NULL); + + return (endptr); +} + +enum snmp_syntax +parse_syntax(char *str) +{ + int32_t i; + + for (i = 0; i < SNMP_SYNTAX_UNKNOWN; i++) { + if (strncmp(syntax_strings[i].str, str, + strlen(syntax_strings[i].str)) == 0) + return (syntax_strings[i].stx); + } + + return (SNMP_SYNTAX_NULL); +} + +static char * +snmp_parse_subindex(struct snmp_toolinfo *snmptoolctx, char *str, + struct index *idx, struct snmp_object *object) +{ + char *ptr; + int32_t i; + enum snmp_syntax stx; + char syntax[MAX_CMD_SYNTAX_LEN]; + + ptr = str; + if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE) { + for (i = 0; i < MAX_CMD_SYNTAX_LEN ; i++) { + if (*(ptr + i) == ':') + break; + } + + if (i >= MAX_CMD_SYNTAX_LEN) { + warnx("Unknown syntax in OID - %s", str); + return (NULL); + } + /* Expect a syntax string here. */ + if ((stx = parse_syntax(str)) <= SNMP_SYNTAX_NULL) { + warnx("Invalid syntax - %s",syntax); + return (NULL); + } + + if (stx != idx->syntax && !ISSET_ERRIGNORE(snmptoolctx)) { + warnx("Syntax mismatch - %d expected, %d given", + idx->syntax, stx); + return (NULL); + } + /* + * That is where the suboid started + the syntax length + one + * character for ':'. + */ + ptr = str + i + 1; + } else + stx = idx->syntax; + + switch (stx) { + case SNMP_SYNTAX_INTEGER: + return (snmp_int2asn_oid(ptr, &(object->val.var))); + case SNMP_SYNTAX_OID: + return (snmp_oid2asn_oid(snmptoolctx, ptr, + &(object->val.var))); + case SNMP_SYNTAX_IPADDRESS: + return (snmp_ip2asn_oid(ptr, &(object->val.var))); + case SNMP_SYNTAX_COUNTER: + /* FALLTHROUGH */ + case SNMP_SYNTAX_GAUGE: + /* FALLTHROUGH */ + case SNMP_SYNTAX_TIMETICKS: + return (snmp_uint2asn_oid(ptr, &(object->val.var))); + case SNMP_SYNTAX_COUNTER64: + return (snmp_cnt64_2asn_oid(ptr, &(object->val.var))); + case SNMP_SYNTAX_OCTETSTRING: + return (snmp_tc2oid(idx->tc, ptr, &(object->val.var))); + default: + /* NOTREACHED */ + break; + } + + return (NULL); +} + +char * +snmp_parse_index(struct snmp_toolinfo *snmptoolctx, char *str, + struct snmp_object *object) +{ + char *ptr; + struct index *temp; + + if (object->info->table_idx == NULL) + return (NULL); + + ptr = NULL; + STAILQ_FOREACH(temp, &(OBJECT_IDX_LIST(object)), link) { + if ((ptr = snmp_parse_subindex(snmptoolctx, str, temp, object)) + == NULL) + return (NULL); + + if (*ptr != ',' && *ptr != ']') + return (NULL); + str = ptr + 1; + } + + if (ptr == NULL || *ptr != ']') { + warnx("Mismatching index - %s", str); + return (NULL); + } + + return (ptr + 1); +} + +/* + * Fill in the struct asn_oid member of snmp_value with suboids from input. + * If an error occurs - print message on stderr and return (-1). + * If all is ok - return the length of the oid. + */ +int32_t +snmp_parse_numoid(char *argv, struct asn_oid *var) +{ + char *endptr, *str; + asn_subid_t suboid; + + str = argv; + + if (*str == '.') + str++; + + do { + if (var->len == ASN_MAXOIDLEN) { + warnx("Oid too long - %u", var->len); + return (-1); + } + + suboid = strtoul(str, &endptr, 10); + if (suboid > ASN_MAXID) { + warnx("Oid too long - %u", var->len); + return (-1); + } + + var->subs[var->len++] = suboid; + str = endptr + 1; + } while ( *endptr == '.'); + + if (*endptr != '\0') { + warnx("Invalid oid string - %s", argv); + return (-1); + } + + return (var->len); +} + +/* Append a length 1 suboid to an asn_oid structure. */ +int32_t +snmp_suboid_append(struct asn_oid *var, asn_subid_t suboid) +{ + if (var == NULL) + return (-1); + + if (var->len >= ASN_MAXOIDLEN) { + warnx("Oid too long - %u", var->len); + return (-1); + } + + var->subs[var->len++] = suboid; + + return (1); +} + +/* Pop the last suboid from an asn_oid structure. */ +int32_t +snmp_suboid_pop(struct asn_oid *var) +{ + asn_subid_t suboid; + + if (var == NULL) + return (-1); + + if (var->len < 1) + return (-1); + + suboid = var->subs[--(var->len)]; + var->subs[var->len] = 0; + + return (suboid); +} + +/* + * Parse the command-line provided string into an OID - alocate memory for a new + * snmp object, fill in its fields and insert it in the object list. A + * (snmp_verify_inoid_f) function must be provided to validate the input string. + */ +int32_t +snmp_object_add(struct snmp_toolinfo *snmptoolctx, snmp_verify_inoid_f func, + char *string) +{ + struct snmp_object *obj; + + if (snmptoolctx == NULL) + return (-1); + + /* XXX-BZ does that chack make sense? */ + if (snmptoolctx->objects >= SNMP_MAX_BINDINGS) { + warnx("Too many bindings in PDU - %u", snmptoolctx->objects + 1); + return (-1); + } + + if ((obj = malloc(sizeof(struct snmp_object))) == NULL) { + syslog(LOG_ERR, "malloc() failed: %s", strerror(errno)); + return (-1); + } + + memset(obj, 0, sizeof(struct snmp_object)); + if (func(snmptoolctx, obj, string) < 0) { + warnx("Invalid OID - %s", string); + free(obj); + return (-1); + } + + snmptoolctx->objects++; + SLIST_INSERT_HEAD(&snmptoolctx->snmp_objectlist, obj, link); + + return (1); +} + +/* Given an OID, find it in the object list and remove it. */ +int32_t +snmp_object_remove(struct snmp_toolinfo *snmptoolctx, struct asn_oid *oid) +{ + struct snmp_object *temp; + + if (SLIST_EMPTY(&snmptoolctx->snmp_objectlist)) { + warnx("Object list already empty"); + return (-1); + } + + + SLIST_FOREACH(temp, &snmptoolctx->snmp_objectlist, link) + if (asn_compare_oid(&(temp->val.var), oid) == 0) + break; + + if (temp == NULL) { + warnx("No such object in list"); + return (-1); + } + + SLIST_REMOVE(&snmptoolctx->snmp_objectlist, temp, snmp_object, link); + if (temp->val.syntax == SNMP_SYNTAX_OCTETSTRING && + temp->val.v.octetstring.octets != NULL) + free(temp->val.v.octetstring.octets); + free(temp); + + return (1); +} + +static void +snmp_object_freeall(struct snmp_toolinfo *snmptoolctx) +{ + struct snmp_object *o; + + while ((o = SLIST_FIRST(&snmptoolctx->snmp_objectlist)) != NULL) { + SLIST_REMOVE_HEAD(&snmptoolctx->snmp_objectlist, link); + + if (o->val.syntax == SNMP_SYNTAX_OCTETSTRING && + o->val.v.octetstring.octets != NULL) + free(o->val.v.octetstring.octets); + free(o); + } +} + +/* Do all possible memory release before exit. */ +void +snmp_tool_freeall(struct snmp_toolinfo *snmptoolctx) +{ + if (snmp_client.chost != NULL) { + free(snmp_client.chost); + snmp_client.chost = NULL; + } + + if (snmp_client.cport != NULL) { + free(snmp_client.cport); + snmp_client.cport = NULL; + } + + snmp_mapping_free(snmptoolctx); + free_filelist(snmptoolctx); + snmp_object_freeall(snmptoolctx); + + if (snmptoolctx->passwd != NULL) { + free(snmptoolctx->passwd); + snmptoolctx->passwd = NULL; + } +} + +/* + * Fill all variables from the object list into a PDU. (snmp_verify_vbind_f) + * function should check whether the variable is consistent in this PDU + * (e.g do not add non-leaf OIDs to a GET PDU, or OIDs with read access only to + * a SET PDU) - might be NULL though. (snmp_add_vbind_f) function is the + * function actually adds the variable to the PDU and must not be NULL. + */ +int32_t +snmp_pdu_add_bindings(struct snmp_toolinfo *snmptoolctx, + snmp_verify_vbind_f vfunc, snmp_add_vbind_f afunc, + struct snmp_pdu *pdu, int32_t maxcount) +{ + int32_t nbindings, abind; + struct snmp_object *obj; + + if (pdu == NULL || afunc == NULL) + return (-1); + + /* Return 0 in case of no more work todo. */ + if (SLIST_EMPTY(&snmptoolctx->snmp_objectlist)) + return (0); + + if (maxcount < 0 || maxcount > SNMP_MAX_BINDINGS) { + warnx("maxcount out of range: <0 || >SNMP_MAX_BINDINGS"); + return (-1); + } + + nbindings = 0; + SLIST_FOREACH(obj, &snmptoolctx->snmp_objectlist, link) { + if ((vfunc != NULL) && (vfunc(snmptoolctx, pdu, obj) < 0)) { + nbindings = -1; + break; + } + if ((abind = afunc(pdu, obj)) < 0) { + nbindings = -1; + break; + } + + if (abind > 0) { + /* Do not put more varbindings than requested. */ + if (++nbindings >= maxcount) + break; + } + } + + return (nbindings); +} + +/* + * Locate an object in the object list and set a corresponding error status. + */ +int32_t +snmp_object_seterror(struct snmp_toolinfo *snmptoolctx, + struct snmp_value *err_value, int32_t error_status) +{ + struct snmp_object *obj; + + if (SLIST_EMPTY(&snmptoolctx->snmp_objectlist) || err_value == NULL) + return (-1); + + SLIST_FOREACH(obj, &snmptoolctx->snmp_objectlist, link) + if (asn_compare_oid(&(err_value->var), &(obj->val.var)) == 0) { + obj->error = error_status; + return (1); + } + + return (0); +} + +/* + * Check a PDU received in response to a SNMP_PDU_GET/SNMP_PDU_GETBULK request + * but don't compare syntaxes - when sending a request PDU they must be null. + * This is a (almost) complete copy of snmp_pdu_check() - with matching syntaxes + * checks and some other checks skipped. + */ +int32_t +snmp_parse_get_resp(struct snmp_pdu *resp, struct snmp_pdu *req) +{ + uint32_t i; + + for (i = 0; i < req->nbindings; i++) { + if (asn_compare_oid(&req->bindings[i].var, + &resp->bindings[i].var) != 0) { + warnx("Bad OID in response"); + return (-1); + } + + if (snmp_client.version != SNMP_V1 && (resp->bindings[i].syntax + == SNMP_SYNTAX_NOSUCHOBJECT || resp->bindings[i].syntax == + SNMP_SYNTAX_NOSUCHINSTANCE)) + return (0); + } + + return (1); +} + +int32_t +snmp_parse_getbulk_resp(struct snmp_pdu *resp, struct snmp_pdu *req) +{ + int32_t N, R, M, r; + + if (req->error_status > (int32_t) resp->nbindings) { + warnx("Bad number of bindings in response"); + return (-1); + } + + for (N = 0; N < req->error_status; N++) { + if (asn_is_suboid(&req->bindings[N].var, + &resp->bindings[N].var) == 0) + return (0); + if (resp->bindings[N].syntax == SNMP_SYNTAX_ENDOFMIBVIEW) + return (0); + } + + for (R = N , r = N; R < (int32_t) req->nbindings; R++) { + for (M = 0; M < req->error_index && (r + M) < + (int32_t) resp->nbindings; M++) { + if (asn_is_suboid(&req->bindings[R].var, + &resp->bindings[r + M].var) == 0) + return (0); + + if (resp->bindings[r + M].syntax == + SNMP_SYNTAX_ENDOFMIBVIEW) { + M++; + break; + } + } + r += M; + } + + return (0); +} + +int32_t +snmp_parse_getnext_resp(struct snmp_pdu *resp, struct snmp_pdu *req) +{ + uint32_t i; + + for (i = 0; i < req->nbindings; i++) { + if (asn_is_suboid(&req->bindings[i].var, &resp->bindings[i].var) + == 0) + return (0); + + if (resp->version != SNMP_V1 && resp->bindings[i].syntax == + SNMP_SYNTAX_ENDOFMIBVIEW) + return (0); + } + + return (1); +} + +/* + * Should be called to check a response to get/getnext/getbulk. + */ +int32_t +snmp_parse_resp(struct snmp_pdu *resp, struct snmp_pdu *req) +{ + if (resp == NULL || req == NULL) + return (-2); + + if (resp->version != req->version) { + warnx("Response has wrong version"); + return (-1); + } + + if (resp->error_status == SNMP_ERR_NOSUCHNAME) { + warnx("Error - No Such Name"); + return (0); + } + + if (resp->error_status != SNMP_ERR_NOERROR) { + warnx("Error %d in response", resp->error_status); + return (-1); + } + + if (resp->nbindings != req->nbindings && req->type != SNMP_PDU_GETBULK){ + warnx("Bad number of bindings in response"); + return (-1); + } + + switch (req->type) { + case SNMP_PDU_GET: + return (snmp_parse_get_resp(resp,req)); + case SNMP_PDU_GETBULK: + return (snmp_parse_getbulk_resp(resp,req)); + case SNMP_PDU_GETNEXT: + return (snmp_parse_getnext_resp(resp,req)); + default: + /* NOTREACHED */ + break; + } + + return (-2); +} + +static void +snmp_output_octetstring(struct snmp_toolinfo *snmptoolctx, enum snmp_tc tc, + uint32_t len, uint8_t *octets) +{ + char *buf; + + if (len == 0 || octets == NULL) + return; + + if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE) + fprintf(stdout, "%s : ", + syntax_strings[SNMP_SYNTAX_OCTETSTRING].str); + + if ((buf = snmp_oct2tc(tc, len, (char *) octets)) != NULL) { + fprintf(stdout, "%s", buf); + free(buf); + } +} + +static void +snmp_output_octetindex(struct snmp_toolinfo *snmptoolctx, enum snmp_tc tc, + struct asn_oid *oid) +{ + uint32_t i; + uint8_t *s; + + if ((s = malloc(oid->subs[0] + 1)) == NULL) + syslog(LOG_ERR, "malloc failed - %s", strerror(errno)); + else { + for (i = 0; i < oid->subs[0]; i++) + s[i] = (u_char) (oid->subs[i + 1]); + + snmp_output_octetstring(snmptoolctx, tc, oid->subs[0], s); + free(s); + } +} + +/* + * Check and output syntax type and value. + */ +static void +snmp_output_oid_value(struct snmp_toolinfo *snmptoolctx, struct asn_oid *oid) +{ + char oid_string[ASN_OIDSTRLEN]; + struct snmp_object obj; + + if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE) + fprintf(stdout, "%s : ", syntax_strings[SNMP_SYNTAX_OID].str); + + if(!ISSET_NUMERIC(snmptoolctx)) { + memset(&obj, 0, sizeof(struct snmp_object)); + asn_append_oid(&(obj.val.var), oid); + + if (snmp_lookup_enumstring(snmptoolctx, &obj) > 0) + fprintf(stdout, "%s" , obj.info->string); + else if (snmp_lookup_oidstring(snmptoolctx, &obj) > 0) + fprintf(stdout, "%s" , obj.info->string); + else if (snmp_lookup_nodestring(snmptoolctx, &obj) > 0) + fprintf(stdout, "%s" , obj.info->string); + else { + (void) asn_oid2str_r(oid, oid_string); + fprintf(stdout, "%s", oid_string); + } + } else { + (void) asn_oid2str_r(oid, oid_string); + fprintf(stdout, "%s", oid_string); + } +} + +static void +snmp_output_int(struct snmp_toolinfo *snmptoolctx, struct enum_pairs *enums, + int32_t int_val) +{ + char *string; + + if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE) + fprintf(stdout, "%s : ", + syntax_strings[SNMP_SYNTAX_INTEGER].str); + + if (enums != NULL && (string = enum_string_lookup(enums, int_val)) + != NULL) + fprintf(stdout, "%s", string); + else + fprintf(stdout, "%d", int_val); +} + +static void +snmp_output_ipaddress(struct snmp_toolinfo *snmptoolctx, uint8_t *ip) +{ + if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE) + fprintf(stdout, "%s : ", + syntax_strings[SNMP_SYNTAX_IPADDRESS].str); + + fprintf(stdout, "%u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]); +} + +static void +snmp_output_counter(struct snmp_toolinfo *snmptoolctx, uint32_t counter) +{ + if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE) + fprintf(stdout, "%s : ", + syntax_strings[SNMP_SYNTAX_COUNTER].str); + + fprintf(stdout, "%u", counter); +} + +static void +snmp_output_gauge(struct snmp_toolinfo *snmptoolctx, uint32_t gauge) +{ + if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE) + fprintf(stdout, "%s : ", syntax_strings[SNMP_SYNTAX_GAUGE].str); + + fprintf(stdout, "%u", gauge); +} + +static void +snmp_output_ticks(struct snmp_toolinfo *snmptoolctx, uint32_t ticks) +{ + if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE) + fprintf(stdout, "%s : ", + syntax_strings[SNMP_SYNTAX_TIMETICKS].str); + + fprintf(stdout, "%u", ticks); +} + +static void +snmp_output_counter64(struct snmp_toolinfo *snmptoolctx, uint64_t counter64) +{ + if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE) + fprintf(stdout, "%s : ", + syntax_strings[SNMP_SYNTAX_COUNTER64].str); + + fprintf(stdout,"%ju", counter64); +} + +int32_t +snmp_output_numval(struct snmp_toolinfo *snmptoolctx, struct snmp_value *val, + struct snmp_oid2str *entry) +{ + if (val == NULL) + return (-1); + + if (GET_OUTPUT(snmptoolctx) != OUTPUT_QUIET) + fprintf(stdout, " = "); + + switch (val->syntax) { + case SNMP_SYNTAX_INTEGER: + if (entry != NULL) + snmp_output_int(snmptoolctx, entry->snmp_enum, + val->v.integer); + else + snmp_output_int(snmptoolctx, NULL, val->v.integer); + break; + + case SNMP_SYNTAX_OCTETSTRING: + if (entry != NULL) + snmp_output_octetstring(snmptoolctx, entry->tc, + val->v.octetstring.len, val->v.octetstring.octets); + else + snmp_output_octetstring(snmptoolctx, SNMP_STRING, + val->v.octetstring.len, val->v.octetstring.octets); + break; + + case SNMP_SYNTAX_OID: + snmp_output_oid_value(snmptoolctx, &(val->v.oid)); + break; + + case SNMP_SYNTAX_IPADDRESS: + snmp_output_ipaddress(snmptoolctx, val->v.ipaddress); + break; + + case SNMP_SYNTAX_COUNTER: + snmp_output_counter(snmptoolctx, val->v.uint32); + break; + + case SNMP_SYNTAX_GAUGE: + snmp_output_gauge(snmptoolctx, val->v.uint32); + break; + + case SNMP_SYNTAX_TIMETICKS: + snmp_output_ticks(snmptoolctx, val->v.uint32); + break; + + case SNMP_SYNTAX_COUNTER64: + snmp_output_counter64(snmptoolctx, val->v.counter64); + break; + + case SNMP_SYNTAX_NOSUCHOBJECT: + fprintf(stdout, "No Such Object\n"); + return (val->syntax); + + case SNMP_SYNTAX_NOSUCHINSTANCE: + fprintf(stdout, "No Such Instance\n"); + return (val->syntax); + + case SNMP_SYNTAX_ENDOFMIBVIEW: + fprintf(stdout, "End of Mib View\n"); + return (val->syntax); + + case SNMP_SYNTAX_NULL: + /* NOTREACHED */ + fprintf(stdout, "agent returned NULL Syntax\n"); + return (val->syntax); + + default: + /* NOTREACHED - If here - then all went completely wrong. */ + fprintf(stdout, "agent returned unknown syntax\n"); + return (-1); + } + + fprintf(stdout, "\n"); + + return (0); +} + +static int32_t +snmp_fill_object(struct snmp_toolinfo *snmptoolctx, struct snmp_object *obj, + struct snmp_value *val) +{ + int32_t rc; + asn_subid_t suboid; + + if (obj == NULL || val == NULL) + return (-1); + + if ((suboid = snmp_suboid_pop(&(val->var))) > ASN_MAXID) + return (-1); + + memset(obj, 0, sizeof(struct snmp_object)); + asn_append_oid(&(obj->val.var), &(val->var)); + obj->val.syntax = val->syntax; + + if (obj->val.syntax > 0) + rc = snmp_lookup_leafstring(snmptoolctx, obj); + else + rc = snmp_lookup_nonleaf_string(snmptoolctx, obj); + + (void) snmp_suboid_append(&(val->var), suboid); + (void) snmp_suboid_append(&(obj->val.var), suboid); + + return (rc); +} + +static int32_t +snmp_output_index(struct snmp_toolinfo *snmptoolctx, struct index *stx, + struct asn_oid *oid) +{ + uint8_t ip[4]; + uint32_t bytes = 1; + uint64_t cnt64; + struct asn_oid temp, out; + + if (oid->len < bytes) + return (-1); + + memset(&temp, 0, sizeof(struct asn_oid)); + asn_append_oid(&temp, oid); + + switch (stx->syntax) { + case SNMP_SYNTAX_INTEGER: + snmp_output_int(snmptoolctx, stx->snmp_enum, temp.subs[0]); + break; + + case SNMP_SYNTAX_OCTETSTRING: + if ((temp.subs[0] > temp.len -1 ) || (temp.subs[0] > + ASN_MAXOCTETSTRING)) + return (-1); + snmp_output_octetindex(snmptoolctx, stx->tc, &temp); + bytes += temp.subs[0]; + break; + + case SNMP_SYNTAX_OID: + if ((temp.subs[0] > temp.len -1) || (temp.subs[0] > + ASN_MAXOIDLEN)) + return (-1); + + bytes += temp.subs[0]; + memset(&out, 0, sizeof(struct asn_oid)); + asn_slice_oid(&out, &temp, 1, bytes); + snmp_output_oid_value(snmptoolctx, &out); + break; + + case SNMP_SYNTAX_IPADDRESS: + if (temp.len < 4) + return (-1); + for (bytes = 0; bytes < 4; bytes++) + ip[bytes] = temp.subs[bytes]; + + snmp_output_ipaddress(snmptoolctx, ip); + bytes = 4; + break; + + case SNMP_SYNTAX_COUNTER: + snmp_output_counter(snmptoolctx, temp.subs[0]); + break; + + case SNMP_SYNTAX_GAUGE: + snmp_output_gauge(snmptoolctx, temp.subs[0]); + break; + + case SNMP_SYNTAX_TIMETICKS: + snmp_output_ticks(snmptoolctx, temp.subs[0]); + break; + + case SNMP_SYNTAX_COUNTER64: + if (oid->len < 2) + return (-1); + bytes = 2; + memcpy(&cnt64, temp.subs, bytes); + snmp_output_counter64(snmptoolctx, cnt64); + break; + + default: + return (-1); + } + + return (bytes); +} + +static int32_t +snmp_output_object(struct snmp_toolinfo *snmptoolctx, struct snmp_object *o) +{ + int32_t i, first, len; + struct asn_oid oid; + struct index *temp; + + if (ISSET_NUMERIC(snmptoolctx)) + return (-1); + + if (o->info->table_idx == NULL) { + fprintf(stdout,"%s.%d", o->info->string, + o->val.var.subs[o->val.var.len - 1]); + return (1); + } + + fprintf(stdout,"%s[", o->info->string); + memset(&oid, 0, sizeof(struct asn_oid)); + + len = 1; + asn_slice_oid(&oid, &(o->val.var), (o->info->table_idx->var.len + len), + o->val.var.len); + + first = 1; + STAILQ_FOREACH(temp, &(OBJECT_IDX_LIST(o)), link) { + if(first) + first = 0; + else + fprintf(stdout, ", "); + if ((i = snmp_output_index(snmptoolctx, temp, &oid)) < 0) + break; + len += i; + memset(&oid, 0, sizeof(struct asn_oid)); + asn_slice_oid(&oid, &(o->val.var), + (o->info->table_idx->var.len + len), o->val.var.len + 1); + } + + fprintf(stdout,"]"); + return (1); +} + +void +snmp_output_err_resp(struct snmp_toolinfo *snmptoolctx, struct snmp_pdu *pdu) +{ + char buf[ASN_OIDSTRLEN]; + struct snmp_object object; + + if (pdu == NULL || (pdu->error_index > (int32_t) pdu->nbindings)) { + fprintf(stdout,"Invalid error index in PDU\n"); + return; + } + + fprintf(stdout, "Agent %s:%s returned error \n", snmp_client.chost, + snmp_client.cport); + + if (!ISSET_NUMERIC(snmptoolctx) && (snmp_fill_object(snmptoolctx, &object, + &(pdu->bindings[pdu->error_index - 1])) > 0)) + snmp_output_object(snmptoolctx, &object); + else { + asn_oid2str_r(&(pdu->bindings[pdu->error_index - 1].var), buf); + fprintf(stdout,"%s", buf); + } + + fprintf(stdout," caused error - "); + if ((pdu->error_status > 0) && (pdu->error_status <= + SNMP_ERR_INCONS_NAME)) + fprintf(stdout, "%s\n", error_strings[pdu->error_status].str); + else + fprintf(stdout,"%s\n", error_strings[SNMP_ERR_UNKNOWN].str); +} + +int32_t +snmp_output_resp(struct snmp_toolinfo *snmptoolctx, struct snmp_pdu *pdu, + struct asn_oid *root) +{ + int32_t error; + char p[ASN_OIDSTRLEN]; + uint32_t i; + struct snmp_object object; + + i = error = 0; + while (i < pdu->nbindings) { + if (root != NULL && !(asn_is_suboid(root, + &(pdu->bindings[i].var)))) + break; + + if (GET_OUTPUT(snmptoolctx) != OUTPUT_QUIET) { + if (!ISSET_NUMERIC(snmptoolctx) && + (snmp_fill_object(snmptoolctx, &object, + &(pdu->bindings[i])) > 0)) + snmp_output_object(snmptoolctx, &object); + else { + asn_oid2str_r(&(pdu->bindings[i].var), p); + fprintf(stdout, "%s", p); + } + } + error |= snmp_output_numval(snmptoolctx, &(pdu->bindings[i]), object.info); + i++; + } + + if (error) + return (-1); + + return (i); +} + +void +snmp_output_engine(void) +{ + uint32_t i; + char *cptr, engine[2 * SNMP_ENGINE_ID_SIZ + 2]; + + cptr = engine; + for (i = 0; i < snmp_client.engine.engine_len; i++) + cptr += sprintf(cptr, "%.2x", snmp_client.engine.engine_id[i]); + *cptr++ = '\0'; + + fprintf(stdout, "Engine ID 0x%s\n", engine); + fprintf(stdout, "Boots : %u\t\tTime : %d\n", + snmp_client.engine.engine_boots, + snmp_client.engine.engine_time); +} + +void +snmp_output_keys(void) +{ + uint32_t i, keylen = 0; + char *cptr, extkey[2 * SNMP_AUTH_KEY_SIZ + 2]; + + fprintf(stdout, "Localized keys for %s\n", snmp_client.user.sec_name); + if (snmp_client.user.auth_proto == SNMP_AUTH_HMAC_MD5) { + fprintf(stdout, "MD5 : 0x"); + keylen = SNMP_AUTH_HMACMD5_KEY_SIZ; + } else if (snmp_client.user.auth_proto == SNMP_AUTH_HMAC_SHA) { + fprintf(stdout, "SHA : 0x"); + keylen = SNMP_AUTH_HMACSHA_KEY_SIZ; + } + if (snmp_client.user.auth_proto != SNMP_AUTH_NOAUTH) { + cptr = extkey; + for (i = 0; i < keylen; i++) + cptr += sprintf(cptr, "%.2x", + snmp_client.user.auth_key[i]); + *cptr++ = '\0'; + fprintf(stdout, "%s\n", extkey); + } + + if (snmp_client.user.priv_proto == SNMP_PRIV_DES) { + fprintf(stdout, "DES : 0x"); + keylen = SNMP_PRIV_DES_KEY_SIZ; + } else if (snmp_client.user.priv_proto == SNMP_PRIV_AES) { + fprintf(stdout, "AES : 0x"); + keylen = SNMP_PRIV_AES_KEY_SIZ; + } + if (snmp_client.user.priv_proto != SNMP_PRIV_NOPRIV) { + cptr = extkey; + for (i = 0; i < keylen; i++) + cptr += sprintf(cptr, "%.2x", + snmp_client.user.priv_key[i]); + *cptr++ = '\0'; + fprintf(stdout, "%s\n", extkey); + } +} diff --git a/usr.sbin/bsnmpd/tools/libbsnmptools/bsnmptools.h b/usr.sbin/bsnmpd/tools/libbsnmptools/bsnmptools.h new file mode 100644 index 0000000..c14fe52 --- /dev/null +++ b/usr.sbin/bsnmpd/tools/libbsnmptools/bsnmptools.h @@ -0,0 +1,333 @@ +/*- + * Copyright (c) 2005-2006 The FreeBSD Project + * All rights reserved. + * + * Author: Shteryana Shopova <syrinx@FreeBSD.org> + * + * Redistribution of this software and documentation and use in source and + * binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code or documentation must retain the above + * copyright notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Helper functions common for all tools. + * + * $FreeBSD$ + */ + +#ifndef _BSNMP_TOOLS_H_ +#define _BSNMP_TOOLS_H_ + +/* From asn1.h + 1 byte for trailing zero. */ +#define MAX_OCTSTRING_LEN ASN_MAXOCTETSTRING + 1 +#define MAX_CMD_SYNTAX_LEN 12 + +/* Arbitrary upper limit on node names and function names - gensnmptree.c. */ +#define MAXSTR 1000 + +/* Should be enough to fetch the biggest allowed octet string. */ +#define MAX_BUFF_SIZE (ASN_MAXOCTETSTRING + 50) + +#define SNMP_DEFS_DIR "/usr/share/snmp/defs/" +#define SNMP_DEFAULT_LOCAL "/var/run/snmpd.sock" + +#define SNMP_MAX_REPETITIONS 10 + +enum snmp_access { + SNMP_ACCESS_NONE = 0, + SNMP_ACCESS_GET, + SNMP_ACCESS_SET, + SNMP_ACCESS_GETSET, +}; + +/* A structure for integer-string enumerations. */ +struct enum_pair { + int32_t enum_val; + char *enum_str; + STAILQ_ENTRY(enum_pair) link; +}; + +STAILQ_HEAD(enum_pairs, enum_pair); + +struct enum_type { + char *name; + uint32_t syntax; + int32_t is_enum; + int32_t is_bits; + struct enum_pairs *snmp_enum; + SLIST_ENTRY(enum_type) link; +}; + +SLIST_HEAD(snmp_enum_tc, enum_type); + +struct index { + enum snmp_tc tc; + enum snmp_syntax syntax; + struct enum_pairs *snmp_enum; + STAILQ_ENTRY(index) link; +}; + +STAILQ_HEAD(snmp_idxlist, index); + +struct snmp_index_entry { + char *string; + uint32_t strlen; + struct asn_oid var; + struct snmp_idxlist index_list; + SLIST_ENTRY(snmp_index_entry) link; +}; + +/* Information needed for oid to string conversion. */ +struct snmp_oid2str { + char *string; + uint32_t strlen; + enum snmp_tc tc; + enum snmp_syntax syntax; + enum snmp_access access; + struct asn_oid var; + /* A pointer to a entry from the table list - OK if NULL. */ + struct snmp_index_entry *table_idx; + /* + * A singly-linked tail queue of all (int, string) pairs - + * for INTEGER syntax only. + */ + struct enum_pairs *snmp_enum; + SLIST_ENTRY(snmp_oid2str) link; +}; + +/* A structure to hold each oid input by user. */ +struct snmp_object { + /* Flag - if set, the variable caused error in a previous request. */ + int32_t error; + /* + * A pointer in the mapping lists - not used if OIDs are input as + * numericals. + */ + struct snmp_oid2str *info; + /* A snmp value to hold the actual oid, syntax and value. */ + struct snmp_value val; + SLIST_ENTRY(snmp_object) link; +}; + +struct fname { + char *name; + int32_t done; + struct asn_oid cut; + SLIST_ENTRY(fname) link; +}; + +SLIST_HEAD(snmp_mapping, snmp_oid2str); +SLIST_HEAD(fname_list, fname); +SLIST_HEAD(snmp_table_index, snmp_index_entry); + +/* + * Keep a list for every syntax type. + */ +struct snmp_mappings { + /* The list containing all non-leaf nodes. */ + struct snmp_mapping nodelist; + /* INTEGER/INTEGER32 types. */ + struct snmp_mapping intlist; + /* OCTETSTRING types. */ + struct snmp_mapping octlist; + /* OID types. */ + struct snmp_mapping oidlist; + /* IPADDRESS types. */ + struct snmp_mapping iplist; + /* TIMETICKS types. */ + struct snmp_mapping ticklist; + /* COUNTER types. */ + struct snmp_mapping cntlist; + /* GAUGE types. */ + struct snmp_mapping gaugelist; + /* COUNTER64 types. */ + struct snmp_mapping cnt64list; + /* ENUM values for oid types. */ + struct snmp_mapping enumlist; + /* Description of all table entry types. */ + struct snmp_table_index tablelist; + /* Defined enumerated textual conventions. */ + struct snmp_enum_tc tclist; +}; + +struct snmp_toolinfo { + uint32_t flags; + /* Number of initially input OIDs. */ + int32_t objects; + /* List of all input OIDs. */ + SLIST_HEAD(snmp_objectlist, snmp_object) snmp_objectlist; + /* All known OID to string mapping data. */ + struct snmp_mappings *mappings; + /* A list of .defs filenames to search oid<->string mapping. */ + struct fname_list filelist; + /* SNMPv3 USM user credentials */ + char *passwd; +}; + +/* XXX we might want to get away with this and will need to touch + * XXX the MACROS then too */ +extern struct snmp_toolinfo snmptool; + +/* Definitions for some flags' bits. */ +#define OUTPUT_BITS 0x00000003 /* bits 0-1 for output type */ +#define NUMERIC_BIT 0x00000004 /* bit 2 for numeric oids */ +#define RETRY_BIT 0x00000008 /* bit 3 for retry on error response */ +#define ERRIGNORE_BIT 0x00000010 /* bit 4 for skip sanity checking */ +#define ERRIGNORE_BIT 0x00000010 /* bit 4 for skip sanity checking */ +#define EDISCOVER_BIT 0x00000020 /* bit 5 for SNMP Engine Discovery */ +#define LOCALKEY_BIT 0x00000040 /* bit 6 for using localized key */ + /* 0x00000080 */ /* bit 7 reserved */ +#define PDUTYPE_BITS 0x00000f00 /* bits 8-11 for pdu type */ + /* 0x0000f000 */ /* bit 12-15 reserved */ +#define MAXREP_BITS 0x00ff0000 /* bits 16-23 for max-repetit. value */ +#define NONREP_BITS 0xff000000 /* bits 24-31 for non-repeaters value */ + +#define OUTPUT_SHORT 0x0 +#define OUTPUT_VERBOSE 0x1 +#define OUTPUT_TABULAR 0x2 +#define OUTPUT_QUIET 0x3 + +/* Macros for playing with flags' bits. */ +#define SET_OUTPUT(ctx, type) ((ctx)->flags |= ((type) & OUTPUT_BITS)) +#define GET_OUTPUT(ctx) ((ctx)->flags & OUTPUT_BITS) + +#define SET_NUMERIC(ctx) ((ctx)->flags |= NUMERIC_BIT) +#define ISSET_NUMERIC(ctx) ((ctx)->flags & NUMERIC_BIT) + +#define SET_RETRY(ctx) ((ctx)->flags |= RETRY_BIT) +#define ISSET_RETRY(ctx) ((ctx)->flags & RETRY_BIT) + +#define SET_ERRIGNORE(ctx) ((ctx)->flags |= ERRIGNORE_BIT) +#define ISSET_ERRIGNORE(ctx) ((ctx)->flags & ERRIGNORE_BIT) + +#define SET_EDISCOVER(ctx) ((ctx)->flags |= EDISCOVER_BIT) +#define ISSET_EDISCOVER(ctx) ((ctx)->flags & EDISCOVER_BIT) + +#define SET_LOCALKEY(ctx) ((ctx)->flags |= LOCALKEY_BIT) +#define ISSET_LOCALKEY(ctx) ((ctx)->flags & LOCALKEY_BIT) + +#define SET_PDUTYPE(ctx, type) (((ctx)->flags |= (((type) & 0xf) << 8))) +#define GET_PDUTYPE(ctx) (((ctx)->flags & PDUTYPE_BITS) >> 8) + +#define SET_MAXREP(ctx, i) (((ctx)->flags |= (((i) & 0xff) << 16))) +#define GET_MAXREP(ctx) (((ctx)->flags & MAXREP_BITS) >> 16) + +#define SET_NONREP(ctx, i) (((ctx)->flags |= (((i) & 0xff) << 24))) +#define GET_NONREP(ctx) (((ctx)->flags & NONREP_BITS) >> 24) + + +extern const struct asn_oid IsoOrgDod_OID; + +int snmptool_init(struct snmp_toolinfo *); +int32_t snmp_import_file(struct snmp_toolinfo *, struct fname *); +int32_t snmp_import_all(struct snmp_toolinfo *); +int32_t add_filename(struct snmp_toolinfo *, const char *, + const struct asn_oid *, int32_t); +void free_filelist(struct snmp_toolinfo *); +void snmp_tool_freeall(struct snmp_toolinfo *); +void snmp_import_dump(int); + +/* bsnmpmap.c */ +struct snmp_mappings *snmp_mapping_init(void); +int32_t snmp_mapping_free(struct snmp_toolinfo *); +void snmp_index_listfree(struct snmp_idxlist *); +void snmp_dump_oid2str(struct snmp_oid2str *); +int32_t snmp_node_insert(struct snmp_toolinfo *, struct snmp_oid2str *); +int32_t snmp_leaf_insert(struct snmp_toolinfo *, struct snmp_oid2str *); +int32_t snmp_enum_insert(struct snmp_toolinfo *, struct snmp_oid2str *); +struct enum_pairs *enum_pairs_init(void); +void enum_pairs_free(struct enum_pairs *); +void snmp_mapping_entryfree(struct snmp_oid2str *); +int32_t enum_pair_insert(struct enum_pairs *, int32_t, char *); +char *enum_string_lookup(struct enum_pairs *, int32_t); +int32_t enum_number_lookup(struct enum_pairs *, char *); +int32_t snmp_syntax_insert(struct snmp_idxlist *, struct enum_pairs *, + enum snmp_syntax, enum snmp_tc); +int32_t snmp_table_insert(struct snmp_toolinfo *, struct snmp_index_entry *); + +struct enum_type *snmp_enumtc_init(char *); +void snmp_enumtc_free(struct enum_type *); +void snmp_enumtc_insert(struct snmp_toolinfo *, struct enum_type *); +struct enum_type *snmp_enumtc_lookup(struct snmp_toolinfo *, char *); + +void snmp_mapping_dump(struct snmp_toolinfo *); +int32_t snmp_lookup_leafstring(struct snmp_toolinfo *, struct snmp_object *); +int32_t snmp_lookup_enumstring(struct snmp_toolinfo *, struct snmp_object *); +int32_t snmp_lookup_oidstring(struct snmp_toolinfo *, struct snmp_object *); +int32_t snmp_lookup_nonleaf_string(struct snmp_toolinfo *, struct snmp_object *); +int32_t snmp_lookup_allstring(struct snmp_toolinfo *, struct snmp_object *); +int32_t snmp_lookup_nodestring(struct snmp_toolinfo *, struct snmp_object *); +int32_t snmp_lookup_oidall(struct snmp_toolinfo *, struct snmp_object *, char *); +int32_t snmp_lookup_enumoid(struct snmp_toolinfo *, struct snmp_object *, char *); +int32_t snmp_lookup_oid(struct snmp_toolinfo *, struct snmp_object *, char *); + +/* Functions parsing common options for all tools. */ +int32_t parse_server(char *); +int32_t parse_timeout(char *); +int32_t parse_retry(char *); +int32_t parse_version(char *); +int32_t parse_local_path(char *); +int32_t parse_buflen(char *); +int32_t parse_debug(void); +int32_t parse_discovery(struct snmp_toolinfo *); +int32_t parse_local_key(struct snmp_toolinfo *); +int32_t parse_num_oids(struct snmp_toolinfo *); +int32_t parse_file(struct snmp_toolinfo *, char *); +int32_t parse_include(struct snmp_toolinfo *, char *); +int32_t parse_output(struct snmp_toolinfo *, char *); +int32_t parse_errors(struct snmp_toolinfo *); +int32_t parse_skip_access(struct snmp_toolinfo *); +int32_t parse_authentication(struct snmp_toolinfo *, char *); +int32_t parse_privacy(struct snmp_toolinfo *, char *); +int32_t parse_context(struct snmp_toolinfo *, char *); +int32_t parse_user_security(struct snmp_toolinfo *, char *); + +typedef int32_t (*snmp_verify_inoid_f) (struct snmp_toolinfo *, + struct snmp_object *, char *); +int32_t snmp_object_add(struct snmp_toolinfo *, snmp_verify_inoid_f, char *); +int32_t snmp_object_remove(struct snmp_toolinfo *, struct asn_oid *oid); +int32_t snmp_object_seterror(struct snmp_toolinfo *, struct snmp_value *, + int32_t); + +enum snmp_syntax parse_syntax(char *); +char *snmp_parse_suboid(char *, struct asn_oid *); +char *snmp_parse_index(struct snmp_toolinfo *, char *, struct snmp_object *); +int32_t snmp_parse_numoid(char *, struct asn_oid *); +int32_t snmp_suboid_append(struct asn_oid *, asn_subid_t); +int32_t snmp_suboid_pop(struct asn_oid *); + +typedef int32_t (*snmp_verify_vbind_f) (struct snmp_toolinfo *, + struct snmp_pdu *, struct snmp_object *); +typedef int32_t (*snmp_add_vbind_f) (struct snmp_pdu *, struct snmp_object *); +int32_t snmp_pdu_add_bindings(struct snmp_toolinfo *, snmp_verify_vbind_f, + snmp_add_vbind_f, struct snmp_pdu *, int32_t); + +int32_t snmp_parse_get_resp(struct snmp_pdu *, struct snmp_pdu *); +int32_t snmp_parse_getbulk_resp(struct snmp_pdu *, struct snmp_pdu *); +int32_t snmp_parse_getnext_resp(struct snmp_pdu *, struct snmp_pdu *); +int32_t snmp_parse_resp(struct snmp_pdu *, struct snmp_pdu *); +int32_t snmp_output_numval(struct snmp_toolinfo *, struct snmp_value *, + struct snmp_oid2str *); +void snmp_output_val(struct snmp_value *); +int32_t snmp_output_resp(struct snmp_toolinfo *, struct snmp_pdu *, struct asn_oid *); +void snmp_output_err_resp(struct snmp_toolinfo *, struct snmp_pdu *); +void snmp_output_engine(void); +void snmp_output_keys(void); + +#endif /* _BSNMP_TOOLS_H_ */ |