freebsd-dev/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_port.c
Shteryana Shopova 24988ed32d Keep the snmp_bridge(3) module up to date with if_bridge(4) and add an
object to control the value of the new 'PRIVATE' bridge members' flag.
While here, remove stale '__unused' compiler directives.

Reviewed by:	bz
Approved by:	re (bmah), bz (mentor)
2007-08-08 19:27:50 +00:00

1514 lines
38 KiB
C

/*-
* 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 neccessary.
*/
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 possition 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();
}