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 #include "bridge_tree.h"
53 #include "bridge_snmp.h"
54 
55 TAILQ_HEAD(bridge_ports, bridge_port);
56 
57 /*
58  * Free the bridge base ports list.
59  */
60 static void
61 bridge_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  */
75 static void
76 bridge_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  */
94 static void
95 bridge_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  */
129 static struct bridge_port *
130 bridge_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  */
159 static void
160 bridge_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. */
179 static struct bridge_ports bridge_ports = TAILQ_HEAD_INITIALIZER(bridge_ports);
180 static time_t ports_list_age;
181 
182 void
183 bridge_ports_update_listage(void)
184 {
185 	ports_list_age = time(NULL);
186 }
187 
188 void
189 bridge_ports_fini(void)
190 {
191 	bridge_ports_free(&bridge_ports);
192 }
193 
194 void
195 bridge_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  */
203 static struct bridge_port *
204 bridge_port_first(void)
205 {
206 	return (TAILQ_FIRST(&bridge_ports));
207 }
208 
209 /*
210  * Find the next port in the ports list.
211  */
212 static struct bridge_port *
213 bridge_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  */
221 struct bridge_port *
222 bridge_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  */
230 struct bridge_port *
231 bridge_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  */
245 void
246 bridge_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  */
260 struct bridge_port *
261 bridge_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  */
298 void
299 bridge_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  */
311 struct bridge_port *
312 bridge_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 
329 void
330 bridge_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  */
344 int
345 op_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 
390 get:
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 
416 int
417 op_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 
529 get:
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 
574 int
575 op_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 
685 get:
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 
715 int
716 op_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 
761 get:
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  */
794 static int
795 bridge_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  */
818 static struct bridge_port *
819 bridge_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  */
850 static struct bridge_port *
851 bridge_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  */
894 static int
895 bridge_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 
912 static int
913 bridge_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 
979 static int
980 bridge_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 
1003 static int
1004 bridge_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 
1034 static int
1035 bridge_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 
1074 int
1075 op_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 
1159 get:
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 
1193 int
1194 op_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 
1293 get:
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 
1336 int
1337 op_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 
1431 get:
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 
1461 int
1462 op_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 
1491 get:
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