summaryrefslogtreecommitdiffstats
path: root/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_if.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/bsnmpd/modules/snmp_bridge/bridge_if.c')
-rw-r--r--usr.sbin/bsnmpd/modules/snmp_bridge/bridge_if.c1479
1 files changed, 1479 insertions, 0 deletions
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();
+}
OpenPOWER on IntegriCloud