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