diff options
Diffstat (limited to 'usr.sbin/bsnmpd/modules/snmp_bridge/bridge_port.c')
| -rw-r--r-- | usr.sbin/bsnmpd/modules/snmp_bridge/bridge_port.c | 1514 | 
1 files changed, 1514 insertions, 0 deletions
| 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 000000000000..11d9a12e281e --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_port.c @@ -0,0 +1,1514 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * 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. + */ + +#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> + +#define	SNMPTREE_TYPES +#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 != +			    (int32_t)begemotBridgeStpPortEnable_enabled || +			    val->v.integer != +			    (int32_t)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(); +} | 
