bridge_port.c revision 330449
1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2006 Shteryana Shopova <syrinx@FreeBSD.org>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 *
28 * Bridge MIB implementation for SNMPd.
29 * Bridge ports.
30 *
31 * $FreeBSD: stable/11/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_port.c 330449 2018-03-05 07:26:05Z eadler $
32 */
33
34#include <sys/queue.h>
35#include <sys/socket.h>
36#include <sys/types.h>
37
38#include <net/ethernet.h>
39#include <net/if.h>
40#include <net/if_mib.h>
41
42#include <assert.h>
43#include <errno.h>
44#include <stdarg.h>
45#include <string.h>
46#include <stdlib.h>
47#include <syslog.h>
48
49#include <bsnmp/snmpmod.h>
50#include <bsnmp/snmp_mibII.h>
51
52#include "bridge_tree.h"
53#include "bridge_snmp.h"
54
55TAILQ_HEAD(bridge_ports, bridge_port);
56
57/*
58 * Free the bridge base ports list.
59 */
60static void
61bridge_ports_free(struct bridge_ports *headp)
62{
63	struct bridge_port *bp;
64
65	while ((bp = TAILQ_FIRST(headp)) != NULL) {
66		TAILQ_REMOVE(headp, bp, b_p);
67		free(bp);
68	}
69}
70
71/*
72 * Free the bridge base ports from the base ports list,
73 * members of a specified bridge interface only.
74 */
75static void
76bridge_port_memif_free(struct bridge_ports *headp,
77	struct bridge_if *bif)
78{
79	struct bridge_port *bp;
80
81	while (bif->f_bp != NULL && bif->sysindex == bif->f_bp->sysindex) {
82		bp = TAILQ_NEXT(bif->f_bp, b_p);
83		TAILQ_REMOVE(headp, bif->f_bp, b_p);
84		free(bif->f_bp);
85		bif->f_bp = bp;
86	}
87}
88
89/*
90 * Insert a port entry in the base port TAILQ starting to search
91 * for its place from the position of the first bridge port for the bridge
92 * interface. Update the first bridge port if necessary.
93 */
94static void
95bridge_port_insert_at(struct bridge_ports *headp,
96	struct bridge_port *bp, struct bridge_port **f_bp)
97{
98	struct bridge_port *t1;
99
100	assert(f_bp != NULL);
101
102	for (t1 = *f_bp;
103	    t1 != NULL && bp->sysindex == t1->sysindex;
104	    t1 = TAILQ_NEXT(t1, b_p)) {
105		if (bp->if_idx < t1->if_idx) {
106			TAILQ_INSERT_BEFORE(t1, bp, b_p);
107			if (*f_bp == t1)
108				*f_bp = bp;
109			return;
110		}
111	}
112
113	/*
114	 * Handle the case when our first port was actually the
115	 * last element of the TAILQ.
116	 */
117	if (t1 == NULL)
118		TAILQ_INSERT_TAIL(headp, bp, b_p);
119	else
120		TAILQ_INSERT_BEFORE(t1, bp, b_p);
121}
122
123/*
124 * Find a port entry's position in the ports list according
125 * to it's parent bridge interface name. Returns a NULL if
126 * we should be at the TAILQ head, otherwise the entry after
127 * which we should be inserted.
128 */
129static struct bridge_port *
130bridge_port_find_pos(struct bridge_ports *headp, uint32_t b_idx)
131{
132	uint32_t t_idx;
133	struct bridge_port *t1;
134
135	if ((t1 = TAILQ_FIRST(headp)) == NULL ||
136	    bridge_compare_sysidx(b_idx, t1->sysindex) < 0)
137		return (NULL);
138
139	t_idx = t1->sysindex;
140
141	for (t1 = TAILQ_NEXT(t1, b_p); t1 != NULL; t1 = TAILQ_NEXT(t1, b_p)) {
142		if (t1->sysindex != t_idx) {
143			if (bridge_compare_sysidx(b_idx, t1->sysindex) < 0)
144				return (TAILQ_PREV(t1, bridge_ports, b_p));
145			else
146				t_idx = t1->sysindex;
147		}
148	}
149
150	if (t1 == NULL)
151		t1 = TAILQ_LAST(headp, bridge_ports);
152
153	return (t1);
154}
155
156/*
157 * Insert a bridge member interface in the ports TAILQ.
158 */
159static void
160bridge_port_memif_insert(struct bridge_ports *headp,
161	struct bridge_port *bp, struct bridge_port **f_bp)
162{
163	struct bridge_port *temp;
164
165	if (*f_bp != NULL)
166		bridge_port_insert_at(headp, bp, f_bp);
167	else {
168		temp = bridge_port_find_pos(headp, bp->sysindex);
169
170		if (temp == NULL)
171			TAILQ_INSERT_HEAD(headp, bp, b_p);
172		else
173			TAILQ_INSERT_AFTER(headp, temp, bp, b_p);
174		*f_bp = bp;
175	}
176}
177
178/* The global ports list. */
179static struct bridge_ports bridge_ports = TAILQ_HEAD_INITIALIZER(bridge_ports);
180static time_t ports_list_age;
181
182void
183bridge_ports_update_listage(void)
184{
185	ports_list_age = time(NULL);
186}
187
188void
189bridge_ports_fini(void)
190{
191	bridge_ports_free(&bridge_ports);
192}
193
194void
195bridge_members_free(struct bridge_if *bif)
196{
197	bridge_port_memif_free(&bridge_ports, bif);
198}
199
200/*
201 * Find the first port in the ports list.
202 */
203static struct bridge_port *
204bridge_port_first(void)
205{
206	return (TAILQ_FIRST(&bridge_ports));
207}
208
209/*
210 * Find the next port in the ports list.
211 */
212static struct bridge_port *
213bridge_port_next(struct bridge_port *bp)
214{
215	return (TAILQ_NEXT(bp, b_p));
216}
217
218/*
219 * Find the first member of the specified bridge interface.
220 */
221struct bridge_port *
222bridge_port_bif_first(struct bridge_if *bif)
223{
224	return (bif->f_bp);
225}
226
227/*
228 * Find the next member of the specified bridge interface.
229 */
230struct bridge_port *
231bridge_port_bif_next(struct bridge_port *bp)
232{
233	struct bridge_port *bp_next;
234
235	if ((bp_next = TAILQ_NEXT(bp, b_p)) == NULL ||
236	    bp_next->sysindex != bp->sysindex)
237		return (NULL);
238
239	return (bp_next);
240}
241
242/*
243 * Remove a bridge port from the ports list.
244 */
245void
246bridge_port_remove(struct bridge_port *bp, struct bridge_if *bif)
247{
248	if (bif->f_bp == bp)
249		bif->f_bp = bridge_port_bif_next(bp);
250
251	TAILQ_REMOVE(&bridge_ports, bp, b_p);
252	free(bp);
253}
254
255/*
256 * Allocate memory for a new bridge port and insert it
257 * in the base ports list. Return a pointer to the port's
258 * structure in case we want to do anything else with it.
259 */
260struct bridge_port *
261bridge_new_port(struct mibif *mif, struct bridge_if *bif)
262{
263	struct bridge_port *bp;
264
265	if ((bp = (struct bridge_port *) malloc(sizeof(*bp))) == NULL) {
266		syslog(LOG_ERR, "bridge new member: failed: %s",
267			strerror(errno));
268		return (NULL);
269	}
270
271	bzero(bp, sizeof(*bp));
272
273	bp->sysindex = bif->sysindex;
274	bp->if_idx = mif->index;
275	bp->port_no = mif->sysindex;
276	strlcpy(bp->p_name, mif->name, IFNAMSIZ);
277	bp->circuit = oid_zeroDotZero;
278
279	/*
280	 * Initialize all rstpMib specific values to false/default.
281	 * These will be set to their true values later if the bridge
282	 * supports RSTP.
283	 */
284	bp->proto_migr = TruthValue_false;
285	bp->admin_edge = TruthValue_false;
286	bp->oper_edge = TruthValue_false;
287	bp->oper_ptp = TruthValue_false;
288	bp->admin_ptp = StpPortAdminPointToPointType_auto;
289
290	bridge_port_memif_insert(&bridge_ports, bp, &(bif->f_bp));
291
292	return (bp);
293}
294
295/*
296 * Update our info from the corresponding mibII interface info.
297 */
298void
299bridge_port_getinfo_mibif(struct mibif *m_if, struct bridge_port *bp)
300{
301	bp->max_info = m_if->mib.ifmd_data.ifi_mtu;
302	bp->in_frames = m_if->mib.ifmd_data.ifi_ipackets;
303	bp->out_frames = m_if->mib.ifmd_data.ifi_opackets;
304	bp->in_drops = m_if->mib.ifmd_data.ifi_iqdrops;
305}
306
307/*
308 * Find a port, whose SNMP's mibII ifIndex matches one of the ports,
309 * members of the specified bridge interface.
310 */
311struct bridge_port *
312bridge_port_find(int32_t if_idx, struct bridge_if *bif)
313{
314	struct bridge_port *bp;
315
316	for (bp = bif->f_bp; bp != NULL; bp = TAILQ_NEXT(bp, b_p)) {
317		if (bp->sysindex != bif->sysindex) {
318			bp = NULL;
319			break;
320		}
321
322		if (bp->if_idx == if_idx)
323			break;
324	}
325
326	return (bp);
327}
328
329void
330bridge_ports_dump(struct bridge_if *bif)
331{
332	struct bridge_port *bp;
333
334	for (bp = bridge_port_bif_first(bif); bp != NULL;
335	    bp = bridge_port_bif_next(bp)) {
336		syslog(LOG_ERR, "memif - %s, index - %d",
337		bp->p_name, bp->port_no);
338	}
339}
340
341/*
342 * RFC4188 specifics.
343 */
344int
345op_dot1d_base_port(struct snmp_context *c __unused, struct snmp_value *val,
346	uint sub, uint iidx __unused, enum snmp_op op)
347{
348	struct bridge_if *bif;
349	struct bridge_port *bp;
350
351	if ((bif = bridge_get_default()) == NULL)
352		return (SNMP_ERR_NOSUCHNAME);
353
354	if (time(NULL) - bif->ports_age > bridge_get_data_maxage() &&
355	    bridge_update_memif(bif) <= 0)
356		return (SNMP_ERR_NOSUCHNAME);
357
358	switch (op) {
359		case SNMP_OP_GET:
360		    if (val->var.len - sub != 1)
361			return (SNMP_ERR_NOSUCHNAME);
362		    if ((bp = bridge_port_find(val->var.subs[sub],
363			bif)) == NULL)
364			    return (SNMP_ERR_NOSUCHNAME);
365		    goto get;
366
367		case SNMP_OP_GETNEXT:
368		    if (val->var.len - sub == 0) {
369			if ((bp = bridge_port_bif_first(bif)) == NULL)
370			    return (SNMP_ERR_NOSUCHNAME);
371		    } else {
372			if ((bp = bridge_port_find(val->var.subs[sub],
373			    bif)) == NULL ||
374			    (bp = bridge_port_bif_next(bp)) == NULL)
375				return (SNMP_ERR_NOSUCHNAME);
376		    }
377		    val->var.len = sub + 1;
378		    val->var.subs[sub] = bp->port_no;
379		    goto get;
380
381		case SNMP_OP_SET:
382		    return (SNMP_ERR_NOT_WRITEABLE);
383
384		case SNMP_OP_ROLLBACK:
385		case SNMP_OP_COMMIT:
386		    break;
387	}
388	abort();
389
390get:
391	switch (val->var.subs[sub - 1]) {
392	    case LEAF_dot1dBasePort:
393		val->v.integer = bp->port_no;
394		return (SNMP_ERR_NOERROR);
395
396	    case LEAF_dot1dBasePortIfIndex:
397		val->v.integer = bp->if_idx;
398		return (SNMP_ERR_NOERROR);
399
400	    case LEAF_dot1dBasePortCircuit:
401		val->v.oid = bp->circuit;
402		return (SNMP_ERR_NOERROR);
403
404	    case LEAF_dot1dBasePortDelayExceededDiscards:
405		val->v.uint32 = bp->dly_ex_drops;
406		return (SNMP_ERR_NOERROR);
407
408	    case LEAF_dot1dBasePortMtuExceededDiscards:
409		val->v.uint32 = bp->dly_mtu_drops;
410		return (SNMP_ERR_NOERROR);
411	}
412
413	abort();
414}
415
416int
417op_dot1d_stp_port(struct snmp_context *ctx, struct snmp_value *val,
418	 uint sub, uint iidx __unused, enum snmp_op op)
419{
420	struct bridge_if *bif;
421	struct bridge_port *bp;
422
423	if ((bif = bridge_get_default()) == NULL)
424		return (SNMP_ERR_NOSUCHNAME);
425
426	if (time(NULL) - bif->ports_age > bridge_get_data_maxage() &&
427	    bridge_update_memif(bif) <= 0)
428		return (SNMP_ERR_NOSUCHNAME);
429
430	switch (op) {
431		case SNMP_OP_GET:
432		    if (val->var.len - sub != 1)
433			return (SNMP_ERR_NOSUCHNAME);
434		    if ((bp = bridge_port_find(val->var.subs[sub],
435			bif)) == NULL)
436			    return (SNMP_ERR_NOSUCHNAME);
437		    goto get;
438
439		case SNMP_OP_GETNEXT:
440		    if (val->var.len - sub == 0) {
441			if ((bp = bridge_port_bif_first(bif)) == NULL)
442			    return (SNMP_ERR_NOSUCHNAME);
443		    } else {
444			if ((bp = bridge_port_find(val->var.subs[sub],
445			    bif)) == NULL ||
446			    (bp = bridge_port_bif_next(bp)) == NULL)
447				return (SNMP_ERR_NOSUCHNAME);
448		    }
449		    val->var.len = sub + 1;
450		    val->var.subs[sub] = bp->port_no;
451		    goto get;
452
453		case SNMP_OP_SET:
454		    if (val->var.len - sub != 1)
455			return (SNMP_ERR_NOSUCHNAME);
456		    if ((bp = bridge_port_find(val->var.subs[sub],
457			bif)) == NULL)
458			    return (SNMP_ERR_NOSUCHNAME);
459
460		    switch (val->var.subs[sub - 1]) {
461			case LEAF_dot1dStpPortPriority:
462			    if (val->v.integer < 0 || val->v.integer > 255)
463				return (SNMP_ERR_WRONG_VALUE);
464
465			    ctx->scratch->int1 = bp->priority;
466			    if (bridge_port_set_priority(bif->bif_name, bp,
467				val->v.integer) < 0)
468				return (SNMP_ERR_GENERR);
469			    return (SNMP_ERR_NOERROR);
470
471			case LEAF_dot1dStpPortEnable:
472			    if (val->v.integer != dot1dStpPortEnable_enabled &&
473				val->v.integer != dot1dStpPortEnable_disabled)
474				return (SNMP_ERR_WRONG_VALUE);
475
476			    ctx->scratch->int1 = bp->enable;
477			    if (bridge_port_set_stp_enable(bif->bif_name,
478				bp, val->v.integer) < 0)
479				return (SNMP_ERR_GENERR);
480			    return (SNMP_ERR_NOERROR);
481
482			case LEAF_dot1dStpPortPathCost:
483			    if (val->v.integer < SNMP_PORT_MIN_PATHCOST ||
484				val->v.integer > SNMP_PORT_MAX_PATHCOST)
485				return (SNMP_ERR_WRONG_VALUE);
486
487			    ctx->scratch->int1 = bp->path_cost;
488			    if (bridge_port_set_path_cost(bif->bif_name, bp,
489				val->v.integer) < 0)
490				return (SNMP_ERR_GENERR);
491			    return (SNMP_ERR_NOERROR);
492
493			case LEAF_dot1dStpPort:
494			case LEAF_dot1dStpPortState:
495			case LEAF_dot1dStpPortDesignatedRoot:
496			case LEAF_dot1dStpPortDesignatedCost:
497			case LEAF_dot1dStpPortDesignatedBridge:
498			case LEAF_dot1dStpPortDesignatedPort:
499			case LEAF_dot1dStpPortForwardTransitions:
500			    return (SNMP_ERR_NOT_WRITEABLE);
501		    }
502		    abort();
503
504		case SNMP_OP_ROLLBACK:
505		    if ((bp = bridge_port_find(val->var.subs[sub],
506			bif)) == NULL)
507			    return (SNMP_ERR_GENERR);
508		    switch (val->var.subs[sub - 1]) {
509			case LEAF_dot1dStpPortPriority:
510			    bridge_port_set_priority(bif->bif_name, bp,
511				ctx->scratch->int1);
512			    break;
513			case LEAF_dot1dStpPortEnable:
514			    bridge_port_set_stp_enable(bif->bif_name, bp,
515				ctx->scratch->int1);
516			    break;
517			case LEAF_dot1dStpPortPathCost:
518			    bridge_port_set_path_cost(bif->bif_name, bp,
519				ctx->scratch->int1);
520			    break;
521		    }
522		    return (SNMP_ERR_NOERROR);
523
524		case SNMP_OP_COMMIT:
525		    return (SNMP_ERR_NOERROR);
526	}
527	abort();
528
529get:
530	switch (val->var.subs[sub - 1]) {
531		case LEAF_dot1dStpPort:
532			val->v.integer = bp->port_no;
533			return (SNMP_ERR_NOERROR);
534
535		case LEAF_dot1dStpPortPriority:
536			val->v.integer = bp->priority;
537			return (SNMP_ERR_NOERROR);
538
539		case LEAF_dot1dStpPortState:
540			val->v.integer = bp->state;
541			return (SNMP_ERR_NOERROR);
542
543		case LEAF_dot1dStpPortEnable:
544			val->v.integer = bp->enable;
545			return (SNMP_ERR_NOERROR);
546
547		case LEAF_dot1dStpPortPathCost:
548			val->v.integer = bp->path_cost;
549			return (SNMP_ERR_NOERROR);
550
551		case LEAF_dot1dStpPortDesignatedRoot:
552			return (string_get(val, bp->design_root,
553			    SNMP_BRIDGE_ID_LEN));
554
555		case LEAF_dot1dStpPortDesignatedCost:
556			val->v.integer = bp->design_cost;
557			return (SNMP_ERR_NOERROR);
558
559		case LEAF_dot1dStpPortDesignatedBridge:
560			return (string_get(val, bp->design_bridge,
561			    SNMP_BRIDGE_ID_LEN));
562
563		case LEAF_dot1dStpPortDesignatedPort:
564			return (string_get(val, bp->design_port, 2));
565
566		case LEAF_dot1dStpPortForwardTransitions:
567			val->v.uint32 = bp->fwd_trans;
568			return (SNMP_ERR_NOERROR);
569	}
570
571	abort();
572}
573
574int
575op_dot1d_stp_ext_port(struct snmp_context *ctx, struct snmp_value *val,
576    uint sub, uint iidx __unused, enum snmp_op op)
577{
578	struct bridge_if *bif;
579	struct bridge_port *bp;
580
581	if ((bif = bridge_get_default()) == NULL)
582		return (SNMP_ERR_NOSUCHNAME);
583
584	if (time(NULL) - bif->ports_age > bridge_get_data_maxage() &&
585	    bridge_update_memif(bif) <= 0)
586		return (SNMP_ERR_NOSUCHNAME);
587
588	switch (op) {
589		case SNMP_OP_GET:
590		    if (val->var.len - sub != 1)
591			return (SNMP_ERR_NOSUCHNAME);
592		    if ((bp = bridge_port_find(val->var.subs[sub],
593			bif)) == NULL)
594			    return (SNMP_ERR_NOSUCHNAME);
595		    goto get;
596
597		case SNMP_OP_GETNEXT:
598		    if (val->var.len - sub == 0) {
599			if ((bp = bridge_port_bif_first(bif)) == NULL)
600			    return (SNMP_ERR_NOSUCHNAME);
601		    } else {
602			if ((bp = bridge_port_find(val->var.subs[sub],
603			    bif)) == NULL ||
604			    (bp = bridge_port_bif_next(bp)) == NULL)
605				return (SNMP_ERR_NOSUCHNAME);
606		    }
607		    val->var.len = sub + 1;
608		    val->var.subs[sub] = bp->port_no;
609		    goto get;
610
611		case SNMP_OP_SET:
612		    if (val->var.len - sub != 1)
613			return (SNMP_ERR_NOSUCHNAME);
614		    if ((bp = bridge_port_find(val->var.subs[sub],
615			bif)) == NULL)
616			    return (SNMP_ERR_NOSUCHNAME);
617
618		    switch (val->var.subs[sub - 1]) {
619			case LEAF_dot1dStpPortAdminEdgePort:
620			    if (val->v.integer != TruthValue_true &&
621				val->v.integer != TruthValue_false)
622				return (SNMP_ERR_WRONG_VALUE);
623
624			    ctx->scratch->int1 = bp->admin_edge;
625			    if (bridge_port_set_admin_edge(bif->bif_name, bp,
626				val->v.integer) < 0)
627				return (SNMP_ERR_GENERR);
628			    return (SNMP_ERR_NOERROR);
629
630			case LEAF_dot1dStpPortAdminPointToPoint:
631			    if (val->v.integer < 0 || val->v.integer >
632				StpPortAdminPointToPointType_auto)
633				return (SNMP_ERR_WRONG_VALUE);
634
635			    ctx->scratch->int1 = bp->admin_ptp;
636			    if (bridge_port_set_admin_ptp(bif->bif_name, bp,
637				val->v.integer) < 0)
638				return (SNMP_ERR_GENERR);
639			    return (SNMP_ERR_NOERROR);
640
641			case LEAF_dot1dStpPortAdminPathCost:
642			    if (val->v.integer < SNMP_PORT_MIN_PATHCOST ||
643				val->v.integer > SNMP_PORT_MAX_PATHCOST)
644				return (SNMP_ERR_WRONG_VALUE);
645
646			    ctx->scratch->int1 = bp->admin_path_cost;
647			    if (bridge_port_set_path_cost(bif->bif_name, bp,
648				val->v.integer) < 0)
649				return (SNMP_ERR_GENERR);
650			    return (SNMP_ERR_NOERROR);
651
652			case LEAF_dot1dStpPortProtocolMigration:
653			case LEAF_dot1dStpPortOperEdgePort:
654			case LEAF_dot1dStpPortOperPointToPoint:
655			    return (SNMP_ERR_NOT_WRITEABLE);
656		    }
657		    abort();
658
659		case SNMP_OP_ROLLBACK:
660		    if ((bp = bridge_port_find(val->var.subs[sub],
661			bif)) == NULL)
662			    return (SNMP_ERR_GENERR);
663
664		    switch (val->var.subs[sub - 1]) {
665			case LEAF_dot1dStpPortAdminEdgePort:
666			    bridge_port_set_admin_edge(bif->bif_name, bp,
667				ctx->scratch->int1);
668			    break;
669			case LEAF_dot1dStpPortAdminPointToPoint:
670			    bridge_port_set_admin_ptp(bif->bif_name, bp,
671				ctx->scratch->int1);
672			    break;
673			case LEAF_dot1dStpPortAdminPathCost:
674			    bridge_port_set_path_cost(bif->bif_name, bp,
675				ctx->scratch->int1);
676			    break;
677		    }
678		    return (SNMP_ERR_NOERROR);
679
680		case SNMP_OP_COMMIT:
681		    return (SNMP_ERR_NOERROR);
682	}
683	abort();
684
685get:
686	switch (val->var.subs[sub - 1]) {
687		case LEAF_dot1dStpPortProtocolMigration:
688			val->v.integer = bp->proto_migr;
689			return (SNMP_ERR_NOERROR);
690
691		case LEAF_dot1dStpPortAdminEdgePort:
692			val->v.integer = bp->admin_edge;
693			return (SNMP_ERR_NOERROR);
694
695		case LEAF_dot1dStpPortOperEdgePort:
696			val->v.integer = bp->oper_edge;
697			return (SNMP_ERR_NOERROR);
698
699		case LEAF_dot1dStpPortAdminPointToPoint:
700			val->v.integer = bp->admin_ptp;
701			return (SNMP_ERR_NOERROR);
702
703		case LEAF_dot1dStpPortOperPointToPoint:
704			val->v.integer = bp->oper_ptp;
705			return (SNMP_ERR_NOERROR);
706
707		case LEAF_dot1dStpPortAdminPathCost:
708			val->v.integer = bp->admin_path_cost;
709			return (SNMP_ERR_NOERROR);
710	}
711
712	abort();
713}
714
715int
716op_dot1d_tp_port(struct snmp_context *c __unused, struct snmp_value *val,
717    uint sub, uint iidx __unused, enum snmp_op op)
718{
719	struct bridge_if *bif;
720	struct bridge_port *bp;
721
722	if ((bif = bridge_get_default()) == NULL)
723		return (SNMP_ERR_NOSUCHNAME);
724
725	if (time(NULL) - bif->ports_age > bridge_get_data_maxage() &&
726	    bridge_update_memif(bif) <= 0)
727		return (SNMP_ERR_NOSUCHNAME);
728
729	switch (op) {
730		case SNMP_OP_GET:
731		    if (val->var.len - sub != 1)
732			return (SNMP_ERR_NOSUCHNAME);
733		    if ((bp = bridge_port_find(val->var.subs[sub],
734			bif)) == NULL)
735			    return (SNMP_ERR_NOSUCHNAME);
736		    goto get;
737
738		case SNMP_OP_GETNEXT:
739		    if (val->var.len - sub == 0) {
740			if ((bp = bridge_port_bif_first(bif)) == NULL)
741			    return (SNMP_ERR_NOSUCHNAME);
742		    } else {
743			if ((bp = bridge_port_find(val->var.subs[sub],
744			    bif)) == NULL ||
745			    (bp = bridge_port_bif_next(bp)) == NULL)
746				return (SNMP_ERR_NOSUCHNAME);
747		    }
748		    val->var.len = sub + 1;
749		    val->var.subs[sub] = bp->port_no;
750		    goto get;
751
752		case SNMP_OP_SET:
753		    return (SNMP_ERR_NOT_WRITEABLE);
754
755		case SNMP_OP_ROLLBACK:
756		case SNMP_OP_COMMIT:
757		    break;
758	}
759	abort();
760
761get:
762	switch (val->var.subs[sub - 1]) {
763		case LEAF_dot1dTpPort:
764			val->v.integer = bp->port_no;
765			return (SNMP_ERR_NOERROR);
766
767		case LEAF_dot1dTpPortMaxInfo:
768			val->v.integer = bp->max_info;
769			return (SNMP_ERR_NOERROR);
770
771		case LEAF_dot1dTpPortInFrames:
772			val->v.uint32 = bp->in_frames;
773			return (SNMP_ERR_NOERROR);
774
775		case LEAF_dot1dTpPortOutFrames:
776			val->v.uint32 = bp->out_frames;
777			return (SNMP_ERR_NOERROR);
778
779		case LEAF_dot1dTpPortInDiscards:
780			val->v.uint32 = bp->in_drops;
781			return (SNMP_ERR_NOERROR);
782	}
783
784	abort();
785}
786
787/*
788 * Private BEGEMOT-BRIDGE-MIB specifics.
789 */
790
791/*
792 * Construct a bridge port entry index.
793 */
794static int
795bridge_port_index_append(struct asn_oid *oid, uint sub,
796	const struct bridge_port *bp)
797{
798	uint i;
799	const char *b_name;
800
801	if ((b_name = bridge_if_find_name(bp->sysindex)) == NULL)
802		return (-1);
803
804	oid->len = sub + strlen(b_name) + 1 + 1;
805	oid->subs[sub] = strlen(b_name);
806
807	for (i = 1; i <= strlen(b_name); i++)
808		oid->subs[sub + i] = b_name[i - 1];
809
810	oid->subs[sub + i] = bp->port_no;
811
812	return (0);
813}
814
815/*
816 * Get the port entry from an entry's index.
817 */
818static struct bridge_port *
819bridge_port_index_get(const struct asn_oid *oid, uint sub, int8_t status)
820{
821	uint i;
822	int32_t port_no;
823	char bif_name[IFNAMSIZ];
824	struct bridge_if *bif;
825	struct bridge_port *bp;
826
827	if (oid->len - sub != oid->subs[sub] + 2 ||
828	    oid->subs[sub] >= IFNAMSIZ)
829		return (NULL);
830
831	for (i = 0; i < oid->subs[sub]; i++)
832		bif_name[i] = oid->subs[sub + i + 1];
833	bif_name[i] = '\0';
834
835	port_no = oid->subs[sub + i + 1];
836
837	if ((bif = bridge_if_find_ifname(bif_name)) == NULL)
838		return (NULL);
839
840	if ((bp = bridge_port_find(port_no, bif)) == NULL ||
841	    (status == 0 && bp->status != RowStatus_active))
842		return (NULL);
843
844	return (bp);
845}
846
847/*
848 * Get the next port entry from an entry's index.
849 */
850static struct bridge_port *
851bridge_port_index_getnext(const struct asn_oid *oid, uint sub, int8_t status)
852{
853	uint i;
854	int32_t port_no;
855	char bif_name[IFNAMSIZ];
856	struct bridge_if *bif;
857	struct bridge_port *bp;
858
859	if (oid->len - sub == 0)
860		bp = bridge_port_first();
861	else {
862		if (oid->len - sub != oid->subs[sub] + 2 ||
863		    oid->subs[sub] >= IFNAMSIZ)
864			return (NULL);
865
866		for (i = 0; i < oid->subs[sub]; i++)
867			bif_name[i] = oid->subs[sub + i + 1];
868		bif_name[i] = '\0';
869
870		port_no = oid->subs[sub + i + 1];
871
872		if ((bif = bridge_if_find_ifname(bif_name)) == NULL ||
873		    (bp = bridge_port_find(port_no, bif)) == NULL)
874			return (NULL);
875
876		bp = bridge_port_next(bp);
877	}
878
879	if (status == 1)
880		return (bp);
881
882	while (bp != NULL) {
883		if (bp->status == RowStatus_active)
884			break;
885		bp = bridge_port_next(bp);
886	}
887
888	return (bp);
889}
890
891/*
892 * Read the bridge name and port index from a ASN OID structure.
893 */
894static int
895bridge_port_index_decode(const struct asn_oid *oid, uint sub,
896	char *b_name, int32_t *idx)
897{
898	uint i;
899
900	if (oid->len - sub != oid->subs[sub] + 2 ||
901	    oid->subs[sub] >= IFNAMSIZ)
902		return (-1);
903
904	for (i = 0; i < oid->subs[sub]; i++)
905		b_name[i] = oid->subs[sub + i + 1];
906	b_name[i] = '\0';
907
908	*idx = oid->subs[sub + i + 1];
909	return (0);
910}
911
912static int
913bridge_port_set_status(struct snmp_context *ctx,
914	struct snmp_value *val, uint sub)
915{
916	int32_t if_idx;
917	char b_name[IFNAMSIZ];
918	struct bridge_if *bif;
919	struct bridge_port *bp;
920	struct mibif *mif;
921
922	if (bridge_port_index_decode(&val->var, sub, b_name, &if_idx) < 0)
923		return (SNMP_ERR_INCONS_VALUE);
924
925	if ((bif = bridge_if_find_ifname(b_name)) == NULL ||
926	    (mif = mib_find_if(if_idx)) == NULL)
927		return (SNMP_ERR_INCONS_VALUE);
928
929	bp = bridge_port_find(if_idx, bif);
930
931	switch (val->v.integer) {
932	    case RowStatus_active:
933		if (bp == NULL)
934		    return (SNMP_ERR_INCONS_VALUE);
935
936		if (bp->span_enable == 0)
937		    return (SNMP_ERR_INCONS_VALUE);
938
939		ctx->scratch->int1 = bp->status;
940		bp->status = RowStatus_active;
941		break;
942
943	    case RowStatus_notInService:
944		if (bp == NULL || bp->span_enable == 0 ||
945		    bp->status == RowStatus_active)
946			return (SNMP_ERR_INCONS_VALUE);
947
948		ctx->scratch->int1 = bp->status;
949		bp->status = RowStatus_notInService;
950
951	    case RowStatus_notReady:
952		/* FALLTHROUGH */
953	    case RowStatus_createAndGo:
954		return (SNMP_ERR_INCONS_VALUE);
955
956	    case RowStatus_createAndWait:
957		if (bp != NULL)
958		    return (SNMP_ERR_INCONS_VALUE);
959
960		if ((bp = bridge_new_port(mif, bif)) == NULL)
961			return (SNMP_ERR_GENERR);
962
963		ctx->scratch->int1 = RowStatus_destroy;
964		bp->status = RowStatus_notReady;
965		break;
966
967	    case RowStatus_destroy:
968		if (bp == NULL)
969		    return (SNMP_ERR_INCONS_VALUE);
970
971		ctx->scratch->int1 = bp->status;
972		bp->status = RowStatus_destroy;
973		break;
974	}
975
976	return (SNMP_ERR_NOERROR);
977}
978
979static int
980bridge_port_rollback_status(struct snmp_context *ctx,
981	struct snmp_value *val, uint sub)
982{
983	int32_t if_idx;
984	char b_name[IFNAMSIZ];
985	struct bridge_if *bif;
986	struct bridge_port *bp;
987
988	if (bridge_port_index_decode(&val->var, sub, b_name, &if_idx) < 0)
989		return (SNMP_ERR_GENERR);
990
991	if ((bif = bridge_if_find_ifname(b_name)) == NULL ||
992	    (bp = bridge_port_find(if_idx, bif)) == NULL)
993		return (SNMP_ERR_GENERR);
994
995	if (ctx->scratch->int1 == RowStatus_destroy)
996		bridge_port_remove(bp, bif);
997	else
998		bp->status = ctx->scratch->int1;
999
1000	return (SNMP_ERR_NOERROR);
1001}
1002
1003static int
1004bridge_port_commit_status(struct snmp_value *val, uint sub)
1005{
1006	int32_t if_idx;
1007	char b_name[IFNAMSIZ];
1008	struct bridge_if *bif;
1009	struct bridge_port *bp;
1010
1011	if (bridge_port_index_decode(&val->var, sub, b_name, &if_idx) < 0)
1012		return (SNMP_ERR_GENERR);
1013
1014	if ((bif = bridge_if_find_ifname(b_name)) == NULL ||
1015	    (bp = bridge_port_find(if_idx, bif)) == NULL)
1016		return (SNMP_ERR_GENERR);
1017
1018	switch (bp->status) {
1019		case RowStatus_active:
1020			if (bridge_port_addm(bp, b_name) < 0)
1021				return (SNMP_ERR_COMMIT_FAILED);
1022			break;
1023
1024		case RowStatus_destroy:
1025			if (bridge_port_delm(bp, b_name) < 0)
1026				return (SNMP_ERR_COMMIT_FAILED);
1027			bridge_port_remove(bp, bif);
1028			break;
1029	}
1030
1031	return (SNMP_ERR_NOERROR);
1032}
1033
1034static int
1035bridge_port_set_span_enable(struct snmp_context *ctx,
1036		struct snmp_value *val, uint sub)
1037{
1038	int32_t if_idx;
1039	char b_name[IFNAMSIZ];
1040	struct bridge_if *bif;
1041	struct bridge_port *bp;
1042	struct mibif *mif;
1043
1044	if (val->v.integer != begemotBridgeBaseSpanEnabled_enabled &&
1045	    val->v.integer != begemotBridgeBaseSpanEnabled_disabled)
1046		return (SNMP_ERR_BADVALUE);
1047
1048	if (bridge_port_index_decode(&val->var, sub, b_name, &if_idx) < 0)
1049		return (SNMP_ERR_INCONS_VALUE);
1050
1051	if ((bif = bridge_if_find_ifname(b_name)) == NULL)
1052		return (SNMP_ERR_INCONS_VALUE);
1053
1054	if ((bp = bridge_port_find(if_idx, bif)) == NULL) {
1055		if ((mif = mib_find_if(if_idx)) == NULL)
1056			return (SNMP_ERR_INCONS_VALUE);
1057
1058		if ((bp = bridge_new_port(mif, bif)) == NULL)
1059			return (SNMP_ERR_GENERR);
1060
1061		ctx->scratch->int1 = RowStatus_destroy;
1062	} else if (bp->status == RowStatus_active) {
1063		return (SNMP_ERR_INCONS_VALUE);
1064	} else {
1065		ctx->scratch->int1 = bp->status;
1066	}
1067
1068	bp->span_enable = val->v.integer;
1069	bp->status = RowStatus_notInService;
1070
1071	return (SNMP_ERR_NOERROR);
1072}
1073
1074int
1075op_begemot_base_port(struct snmp_context *ctx, struct snmp_value *val,
1076	uint sub, uint iidx __unused, enum snmp_op op)
1077{
1078	int8_t status, which;
1079	const char *bname;
1080	struct bridge_port *bp;
1081
1082	if (time(NULL) - ports_list_age > bridge_get_data_maxage())
1083		bridge_update_all_ports();
1084
1085	which = val->var.subs[sub - 1];
1086	status = 0;
1087
1088	switch (op) {
1089	    case SNMP_OP_GET:
1090		if (which == LEAF_begemotBridgeBaseSpanEnabled ||
1091		    which == LEAF_begemotBridgeBasePortStatus)
1092			status = 1;
1093		if ((bp = bridge_port_index_get(&val->var, sub,
1094		    status)) == NULL)
1095			return (SNMP_ERR_NOSUCHNAME);
1096		goto get;
1097
1098	    case SNMP_OP_GETNEXT:
1099		if (which == LEAF_begemotBridgeBaseSpanEnabled ||
1100		    which == LEAF_begemotBridgeBasePortStatus)
1101			status = 1;
1102		if ((bp = bridge_port_index_getnext(&val->var, sub,
1103		    status)) == NULL ||
1104		    bridge_port_index_append(&val->var, sub, bp) < 0)
1105			return (SNMP_ERR_NOSUCHNAME);
1106		goto get;
1107
1108	    case SNMP_OP_SET:
1109		switch (which) {
1110		    case LEAF_begemotBridgeBaseSpanEnabled:
1111			return (bridge_port_set_span_enable(ctx, val, sub));
1112
1113		    case LEAF_begemotBridgeBasePortStatus:
1114			return (bridge_port_set_status(ctx, val, sub));
1115
1116		    case LEAF_begemotBridgeBasePortPrivate:
1117			if ((bp = bridge_port_index_get(&val->var, sub,
1118			    status)) == NULL)
1119				return (SNMP_ERR_NOSUCHNAME);
1120			if ((bname = bridge_if_find_name(bp->sysindex)) == NULL)
1121				return (SNMP_ERR_GENERR);
1122			ctx->scratch->int1 = bp->priv_set;
1123			return (bridge_port_set_private(bname, bp,
1124			    val->v.integer));
1125
1126		    case LEAF_begemotBridgeBasePort:
1127		    case LEAF_begemotBridgeBasePortIfIndex:
1128		    case LEAF_begemotBridgeBasePortDelayExceededDiscards:
1129		    case LEAF_begemotBridgeBasePortMtuExceededDiscards:
1130			return (SNMP_ERR_NOT_WRITEABLE);
1131		}
1132		abort();
1133
1134	    case SNMP_OP_ROLLBACK:
1135		switch (which) {
1136		    case LEAF_begemotBridgeBaseSpanEnabled:
1137			/* FALLTHROUGH */
1138		    case LEAF_begemotBridgeBasePortStatus:
1139			return (bridge_port_rollback_status(ctx, val, sub));
1140		    case LEAF_begemotBridgeBasePortPrivate:
1141			if ((bp = bridge_port_index_get(&val->var, sub,
1142			    status)) == NULL)
1143				return (SNMP_ERR_GENERR);
1144			if ((bname = bridge_if_find_name(bp->sysindex)) == NULL)
1145				return (SNMP_ERR_GENERR);
1146			return (bridge_port_set_private(bname, bp,
1147			    ctx->scratch->int1));
1148		}
1149		return (SNMP_ERR_NOERROR);
1150
1151	    case SNMP_OP_COMMIT:
1152		if (which == LEAF_begemotBridgeBasePortStatus)
1153			return (bridge_port_commit_status(val, sub));
1154
1155		return (SNMP_ERR_NOERROR);
1156	}
1157	abort();
1158
1159get:
1160	switch (which) {
1161	    case LEAF_begemotBridgeBasePort:
1162		val->v.integer = bp->port_no;
1163		return (SNMP_ERR_NOERROR);
1164
1165	    case LEAF_begemotBridgeBasePortIfIndex:
1166		val->v.integer = bp->if_idx;
1167		return (SNMP_ERR_NOERROR);
1168
1169	    case LEAF_begemotBridgeBaseSpanEnabled:
1170		val->v.integer = bp->span_enable;
1171		return (SNMP_ERR_NOERROR);
1172
1173	    case LEAF_begemotBridgeBasePortDelayExceededDiscards:
1174		val->v.uint32 = bp->dly_ex_drops;
1175		return (SNMP_ERR_NOERROR);
1176
1177	    case LEAF_begemotBridgeBasePortMtuExceededDiscards:
1178		val->v.uint32 = bp->dly_mtu_drops;
1179		return (SNMP_ERR_NOERROR);
1180
1181	    case LEAF_begemotBridgeBasePortStatus:
1182		val->v.integer = bp->status;
1183		return (SNMP_ERR_NOERROR);
1184
1185	    case LEAF_begemotBridgeBasePortPrivate:
1186		val->v.integer = bp->priv_set;
1187		return (SNMP_ERR_NOERROR);
1188	}
1189
1190	abort();
1191}
1192
1193int
1194op_begemot_stp_port(struct snmp_context *ctx, struct snmp_value *val,
1195	uint sub, uint iidx __unused, enum snmp_op op)
1196{
1197	struct bridge_port *bp;
1198	const char *b_name;
1199
1200	if (time(NULL) - ports_list_age > bridge_get_data_maxage())
1201		bridge_update_all_ports();
1202
1203	switch (op) {
1204	    case SNMP_OP_GET:
1205		if ((bp = bridge_port_index_get(&val->var, sub, 0)) == NULL)
1206		    return (SNMP_ERR_NOSUCHNAME);
1207		goto get;
1208
1209	    case SNMP_OP_GETNEXT:
1210		if ((bp = bridge_port_index_getnext(&val->var, sub, 0)) ==
1211		    NULL || bridge_port_index_append(&val->var, sub, bp) < 0)
1212			return (SNMP_ERR_NOSUCHNAME);
1213		goto get;
1214
1215	    case SNMP_OP_SET:
1216		if ((bp = bridge_port_index_get(&val->var, sub, 0)) == NULL)
1217			return (SNMP_ERR_NOSUCHNAME);
1218		if ((b_name = bridge_if_find_name(bp->sysindex)) == NULL)
1219			return (SNMP_ERR_GENERR);
1220
1221		switch (val->var.subs[sub - 1]) {
1222		    case LEAF_begemotBridgeStpPortPriority:
1223			if (val->v.integer < 0 || val->v.integer > 255)
1224			    return (SNMP_ERR_WRONG_VALUE);
1225
1226			ctx->scratch->int1 = bp->priority;
1227			if (bridge_port_set_priority(b_name, bp,
1228			    val->v.integer) < 0)
1229			    return (SNMP_ERR_GENERR);
1230			return (SNMP_ERR_NOERROR);
1231
1232		    case LEAF_begemotBridgeStpPortEnable:
1233			if (val->v.integer !=
1234			    begemotBridgeStpPortEnable_enabled ||
1235			    val->v.integer !=
1236			    begemotBridgeStpPortEnable_disabled)
1237			    return (SNMP_ERR_WRONG_VALUE);
1238
1239			ctx->scratch->int1 = bp->enable;
1240			if (bridge_port_set_stp_enable(b_name, bp,
1241			    val->v.integer) < 0)
1242			    return (SNMP_ERR_GENERR);
1243			return (SNMP_ERR_NOERROR);
1244
1245		    case LEAF_begemotBridgeStpPortPathCost:
1246			if (val->v.integer < SNMP_PORT_MIN_PATHCOST ||
1247			    val->v.integer > SNMP_PORT_MAX_PATHCOST)
1248			    return (SNMP_ERR_WRONG_VALUE);
1249
1250			ctx->scratch->int1 = bp->path_cost;
1251			if (bridge_port_set_path_cost(b_name, bp,
1252			    val->v.integer) < 0)
1253			    return (SNMP_ERR_GENERR);
1254			return (SNMP_ERR_NOERROR);
1255
1256		    case LEAF_begemotBridgeStpPort:
1257		    case LEAF_begemotBridgeStpPortState:
1258		    case LEAF_begemotBridgeStpPortDesignatedRoot:
1259		    case LEAF_begemotBridgeStpPortDesignatedCost:
1260		    case LEAF_begemotBridgeStpPortDesignatedBridge:
1261		    case LEAF_begemotBridgeStpPortDesignatedPort:
1262		    case LEAF_begemotBridgeStpPortForwardTransitions:
1263			return (SNMP_ERR_NOT_WRITEABLE);
1264		}
1265		abort();
1266
1267	    case SNMP_OP_ROLLBACK:
1268		if ((bp = bridge_port_index_get(&val->var, sub, 0)) == NULL ||
1269		    (b_name = bridge_if_find_name(bp->sysindex)) == NULL)
1270			return (SNMP_ERR_GENERR);
1271
1272		switch (val->var.subs[sub - 1]) {
1273		    case LEAF_begemotBridgeStpPortPriority:
1274			bridge_port_set_priority(b_name, bp,
1275			    ctx->scratch->int1);
1276			break;
1277		    case LEAF_begemotBridgeStpPortEnable:
1278			bridge_port_set_stp_enable(b_name, bp,
1279			    ctx->scratch->int1);
1280			break;
1281		    case LEAF_begemotBridgeStpPortPathCost:
1282			bridge_port_set_path_cost(b_name, bp,
1283			    ctx->scratch->int1);
1284			break;
1285		}
1286		return (SNMP_ERR_NOERROR);
1287
1288	    case SNMP_OP_COMMIT:
1289		return (SNMP_ERR_NOERROR);
1290	}
1291	abort();
1292
1293get:
1294	switch (val->var.subs[sub - 1]) {
1295	    case LEAF_begemotBridgeStpPort:
1296		val->v.integer = bp->port_no;
1297		return (SNMP_ERR_NOERROR);
1298
1299	    case LEAF_begemotBridgeStpPortPriority:
1300		val->v.integer = bp->priority;
1301		return (SNMP_ERR_NOERROR);
1302
1303	    case LEAF_begemotBridgeStpPortState:
1304		val->v.integer = bp->state;
1305		return (SNMP_ERR_NOERROR);
1306
1307	    case LEAF_begemotBridgeStpPortEnable:
1308		val->v.integer = bp->enable;
1309		return (SNMP_ERR_NOERROR);
1310
1311	    case LEAF_begemotBridgeStpPortPathCost:
1312		val->v.integer = bp->path_cost;
1313		return (SNMP_ERR_NOERROR);
1314
1315	    case LEAF_begemotBridgeStpPortDesignatedRoot:
1316		return (string_get(val, bp->design_root, SNMP_BRIDGE_ID_LEN));
1317
1318	    case LEAF_begemotBridgeStpPortDesignatedCost:
1319		val->v.integer = bp->design_cost;
1320		return (SNMP_ERR_NOERROR);
1321
1322	    case LEAF_begemotBridgeStpPortDesignatedBridge:
1323		return (string_get(val, bp->design_bridge, SNMP_BRIDGE_ID_LEN));
1324
1325	    case LEAF_begemotBridgeStpPortDesignatedPort:
1326		return (string_get(val, bp->design_port, 2));
1327
1328	    case LEAF_begemotBridgeStpPortForwardTransitions:
1329		val->v.uint32 = bp->fwd_trans;
1330		return (SNMP_ERR_NOERROR);
1331	}
1332
1333	abort();
1334}
1335
1336int
1337op_begemot_stp_ext_port(struct snmp_context *ctx, struct snmp_value *val,
1338    uint sub, uint iidx __unused, enum snmp_op op)
1339{
1340	struct bridge_port *bp;
1341	const char *b_name;
1342
1343	if (time(NULL) - ports_list_age > bridge_get_data_maxage())
1344		bridge_update_all_ports();
1345
1346	switch (op) {
1347	    case SNMP_OP_GET:
1348		if ((bp = bridge_port_index_get(&val->var, sub, 0)) == NULL)
1349		    return (SNMP_ERR_NOSUCHNAME);
1350		goto get;
1351
1352	    case SNMP_OP_GETNEXT:
1353		if ((bp = bridge_port_index_getnext(&val->var, sub, 0)) ==
1354		    NULL || bridge_port_index_append(&val->var, sub, bp) < 0)
1355			return (SNMP_ERR_NOSUCHNAME);
1356		goto get;
1357
1358	    case SNMP_OP_SET:
1359		if ((bp = bridge_port_index_get(&val->var, sub, 0)) == NULL)
1360			return (SNMP_ERR_NOSUCHNAME);
1361		if ((b_name = bridge_if_find_name(bp->sysindex)) == NULL)
1362			return (SNMP_ERR_GENERR);
1363
1364		switch (val->var.subs[sub - 1]) {
1365		    case LEAF_begemotBridgeStpPortAdminEdgePort:
1366			if (val->v.integer != TruthValue_true &&
1367			    val->v.integer != TruthValue_false)
1368			    return (SNMP_ERR_WRONG_VALUE);
1369
1370			ctx->scratch->int1 = bp->admin_edge;
1371			if (bridge_port_set_admin_edge(b_name, bp,
1372			    val->v.integer) < 0)
1373			    return (SNMP_ERR_GENERR);
1374			return (SNMP_ERR_NOERROR);
1375
1376		    case LEAF_begemotBridgeStpPortAdminPointToPoint:
1377			if (val->v.integer < 0 || val->v.integer >
1378			    StpPortAdminPointToPointType_auto)
1379			    return (SNMP_ERR_WRONG_VALUE);
1380
1381			ctx->scratch->int1 = bp->admin_ptp;
1382			if (bridge_port_set_admin_ptp(b_name, bp,
1383			    val->v.integer) < 0)
1384			    return (SNMP_ERR_GENERR);
1385			return (SNMP_ERR_NOERROR);
1386
1387		    case LEAF_begemotBridgeStpPortAdminPathCost:
1388			if (val->v.integer < SNMP_PORT_MIN_PATHCOST ||
1389			    val->v.integer > SNMP_PORT_MAX_PATHCOST)
1390			    return (SNMP_ERR_WRONG_VALUE);
1391
1392			ctx->scratch->int1 = bp->admin_path_cost;
1393			if (bridge_port_set_path_cost(b_name, bp,
1394			    val->v.integer) < 0)
1395			    return (SNMP_ERR_GENERR);
1396			return (SNMP_ERR_NOERROR);
1397
1398		    case LEAF_begemotBridgeStpPortProtocolMigration:
1399		    case LEAF_begemotBridgeStpPortOperEdgePort:
1400		    case LEAF_begemotBridgeStpPortOperPointToPoint:
1401			return (SNMP_ERR_NOT_WRITEABLE);
1402		}
1403		abort();
1404
1405	    case SNMP_OP_ROLLBACK:
1406		if ((bp = bridge_port_index_get(&val->var, sub, 0)) == NULL ||
1407		    (b_name = bridge_if_find_name(bp->sysindex)) == NULL)
1408			return (SNMP_ERR_GENERR);
1409
1410		switch (val->var.subs[sub - 1]) {
1411		    case LEAF_begemotBridgeStpPortAdminEdgePort:
1412			bridge_port_set_admin_edge(b_name, bp,
1413			    ctx->scratch->int1);
1414			break;
1415		    case LEAF_begemotBridgeStpPortAdminPointToPoint:
1416			bridge_port_set_admin_ptp(b_name, bp,
1417			    ctx->scratch->int1);
1418			break;
1419		    case LEAF_begemotBridgeStpPortAdminPathCost:
1420			bridge_port_set_path_cost(b_name, bp,
1421			    ctx->scratch->int1);
1422			break;
1423		}
1424		return (SNMP_ERR_NOERROR);
1425
1426	    case SNMP_OP_COMMIT:
1427		return (SNMP_ERR_NOERROR);
1428	}
1429	abort();
1430
1431get:
1432	switch (val->var.subs[sub - 1]) {
1433		case LEAF_begemotBridgeStpPortProtocolMigration:
1434			val->v.integer = bp->proto_migr;
1435			return (SNMP_ERR_NOERROR);
1436
1437		case LEAF_begemotBridgeStpPortAdminEdgePort:
1438			val->v.integer = bp->admin_edge;
1439			return (SNMP_ERR_NOERROR);
1440
1441		case LEAF_begemotBridgeStpPortOperEdgePort:
1442			val->v.integer = bp->oper_edge;
1443			return (SNMP_ERR_NOERROR);
1444
1445		case LEAF_begemotBridgeStpPortAdminPointToPoint:
1446			val->v.integer = bp->admin_ptp;
1447			return (SNMP_ERR_NOERROR);
1448
1449		case LEAF_begemotBridgeStpPortOperPointToPoint:
1450			val->v.integer = bp->oper_ptp;
1451			return (SNMP_ERR_NOERROR);
1452
1453		case LEAF_begemotBridgeStpPortAdminPathCost:
1454			val->v.integer = bp->admin_path_cost;
1455			return (SNMP_ERR_NOERROR);
1456	}
1457
1458	abort();
1459}
1460
1461int
1462op_begemot_tp_port(struct snmp_context *c __unused, struct snmp_value *val,
1463	uint sub, uint iidx __unused, enum snmp_op op)
1464{
1465	struct bridge_port *bp;
1466
1467	if (time(NULL) - ports_list_age > bridge_get_data_maxage())
1468		bridge_update_all_ports();
1469
1470	switch (op) {
1471	    case SNMP_OP_GET:
1472		if ((bp = bridge_port_index_get(&val->var, sub, 0)) == NULL)
1473		    return (SNMP_ERR_NOSUCHNAME);
1474		goto get;
1475
1476	    case SNMP_OP_GETNEXT:
1477		if ((bp = bridge_port_index_getnext(&val->var, sub, 0)) ==
1478		    NULL || bridge_port_index_append(&val->var, sub, bp) < 0)
1479		    return (SNMP_ERR_NOSUCHNAME);
1480		goto get;
1481
1482	    case SNMP_OP_SET:
1483		return (SNMP_ERR_NOT_WRITEABLE);
1484
1485	    case SNMP_OP_ROLLBACK:
1486	    case SNMP_OP_COMMIT:
1487		break;
1488	}
1489	abort();
1490
1491get:
1492	switch (val->var.subs[sub - 1]) {
1493	    case LEAF_begemotBridgeTpPort:
1494		val->v.integer = bp->port_no;
1495		return (SNMP_ERR_NOERROR);
1496
1497	    case LEAF_begemotBridgeTpPortMaxInfo:
1498		val->v.integer = bp->max_info;
1499		return (SNMP_ERR_NOERROR);
1500
1501	    case LEAF_begemotBridgeTpPortInFrames:
1502		val->v.uint32 = bp->in_frames;
1503		return (SNMP_ERR_NOERROR);
1504
1505	    case LEAF_begemotBridgeTpPortOutFrames:
1506		val->v.uint32 = bp->out_frames;
1507		return (SNMP_ERR_NOERROR);
1508
1509	    case LEAF_begemotBridgeTpPortInDiscards:
1510		val->v.uint32 = bp->in_drops;
1511		return (SNMP_ERR_NOERROR);
1512	}
1513
1514	abort();
1515}
1516