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