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