xref: /freebsd/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_port.c (revision f856af0466c076beef4ea9b15d088e1119a945b8)
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 	struct bridge_port *bp;
1078 
1079 	if (time(NULL) - ports_list_age > bridge_get_data_maxage())
1080 		bridge_update_all_ports();
1081 
1082 	which = val->var.subs[sub - 1];
1083 	status = 0;
1084 
1085 	switch (op) {
1086 	    case SNMP_OP_GET:
1087 		if (which == LEAF_begemotBridgeBaseSpanEnabled ||
1088 		    which == LEAF_begemotBridgeBasePortStatus)
1089 			status = 1;
1090 		if ((bp = bridge_port_index_get(&val->var, sub,
1091 		    status)) == NULL)
1092 			return (SNMP_ERR_NOSUCHNAME);
1093 		goto get;
1094 
1095 	    case SNMP_OP_GETNEXT:
1096 		if (which == LEAF_begemotBridgeBaseSpanEnabled ||
1097 		    which == LEAF_begemotBridgeBasePortStatus)
1098 			status = 1;
1099 		if ((bp = bridge_port_index_getnext(&val->var, sub,
1100 		    status)) == NULL ||
1101 		    bridge_port_index_append(&val->var, sub, bp) < 0)
1102 			return (SNMP_ERR_NOSUCHNAME);
1103 		goto get;
1104 
1105 	    case SNMP_OP_SET:
1106 		switch (which) {
1107 		    case LEAF_begemotBridgeBaseSpanEnabled:
1108 			return (bridge_port_set_span_enable(ctx, val, sub));
1109 
1110 		    case LEAF_begemotBridgeBasePortStatus:
1111 			return (bridge_port_set_status(ctx, val, sub));
1112 
1113 		    case LEAF_begemotBridgeBasePort:
1114 		    case LEAF_begemotBridgeBasePortIfIndex:
1115 		    case LEAF_begemotBridgeBasePortDelayExceededDiscards:
1116 		    case LEAF_begemotBridgeBasePortMtuExceededDiscards:
1117 			return (SNMP_ERR_NOT_WRITEABLE);
1118 		}
1119 		abort();
1120 
1121 	    case SNMP_OP_ROLLBACK:
1122 		switch (which) {
1123 		    case LEAF_begemotBridgeBaseSpanEnabled:
1124 			/* FALLTHROUGH */
1125 		    case LEAF_begemotBridgeBasePortStatus:
1126 			return (bridge_port_rollback_status(ctx, val, sub));
1127 		}
1128 		return (SNMP_ERR_NOERROR);
1129 
1130 	    case SNMP_OP_COMMIT:
1131 		if (which == LEAF_begemotBridgeBasePortStatus)
1132 			return (bridge_port_commit_status(val, sub));
1133 
1134 		return (SNMP_ERR_NOERROR);
1135 	}
1136 	abort();
1137 
1138 get:
1139 	switch (which) {
1140 	    case LEAF_begemotBridgeBasePort:
1141 		val->v.integer = bp->port_no;
1142 		return (SNMP_ERR_NOERROR);
1143 
1144 	    case LEAF_begemotBridgeBasePortIfIndex:
1145 		val->v.integer = bp->if_idx;
1146 		return (SNMP_ERR_NOERROR);
1147 
1148 	    case LEAF_begemotBridgeBaseSpanEnabled:
1149 		val->v.integer = bp->span_enable;
1150 		return (SNMP_ERR_NOERROR);
1151 
1152 	    case LEAF_begemotBridgeBasePortDelayExceededDiscards:
1153 		val->v.uint32 = bp->dly_ex_drops;
1154 		return (SNMP_ERR_NOERROR);
1155 
1156 	    case LEAF_begemotBridgeBasePortMtuExceededDiscards:
1157 		val->v.uint32 = bp->dly_mtu_drops;
1158 		return (SNMP_ERR_NOERROR);
1159 
1160 	    case LEAF_begemotBridgeBasePortStatus:
1161 		val->v.integer = bp->status;
1162 		return (SNMP_ERR_NOERROR);
1163 	}
1164 
1165 	abort();
1166 }
1167 
1168 int
1169 op_begemot_stp_port(struct snmp_context *ctx, struct snmp_value *val,
1170 	uint sub, uint iidx __unused, enum snmp_op op)
1171 {
1172 	struct bridge_port *bp;
1173 	const char *b_name;
1174 
1175 	if (time(NULL) - ports_list_age > bridge_get_data_maxage())
1176 		bridge_update_all_ports();
1177 
1178 	switch (op) {
1179 	    case SNMP_OP_GET:
1180 		if ((bp = bridge_port_index_get(&val->var, sub, 0)) == NULL)
1181 		    return (SNMP_ERR_NOSUCHNAME);
1182 		goto get;
1183 
1184 	    case SNMP_OP_GETNEXT:
1185 		if ((bp = bridge_port_index_getnext(&val->var, sub, 0)) ==
1186 		    NULL || bridge_port_index_append(&val->var, sub, bp) < 0)
1187 			return (SNMP_ERR_NOSUCHNAME);
1188 		goto get;
1189 
1190 	    case SNMP_OP_SET:
1191 		if ((bp = bridge_port_index_get(&val->var, sub, 0)) == NULL)
1192 			return (SNMP_ERR_NOSUCHNAME);
1193 		if ((b_name = bridge_if_find_name(bp->sysindex)) == NULL)
1194 			return (SNMP_ERR_GENERR);
1195 
1196 		switch (val->var.subs[sub - 1]) {
1197 		    case LEAF_begemotBridgeStpPortPriority:
1198 			if (val->v.integer < 0 || val->v.integer > 255)
1199 			    return (SNMP_ERR_WRONG_VALUE);
1200 
1201 			ctx->scratch->int1 = bp->priority;
1202 			if (bridge_port_set_priority(b_name, bp,
1203 			    val->v.integer) < 0)
1204 			    return (SNMP_ERR_GENERR);
1205 			return (SNMP_ERR_NOERROR);
1206 
1207 		    case LEAF_begemotBridgeStpPortEnable:
1208 			if (val->v.integer !=
1209 			    begemotBridgeStpPortEnable_enabled ||
1210 			    val->v.integer !=
1211 			    begemotBridgeStpPortEnable_disabled)
1212 			    return (SNMP_ERR_WRONG_VALUE);
1213 
1214 			ctx->scratch->int1 = bp->enable;
1215 			if (bridge_port_set_stp_enable(b_name, bp,
1216 			    val->v.integer) < 0)
1217 			    return (SNMP_ERR_GENERR);
1218 			return (SNMP_ERR_NOERROR);
1219 
1220 		    case LEAF_begemotBridgeStpPortPathCost:
1221 			if (val->v.integer < SNMP_PORT_MIN_PATHCOST ||
1222 			    val->v.integer > SNMP_PORT_MAX_PATHCOST)
1223 			    return (SNMP_ERR_WRONG_VALUE);
1224 
1225 			ctx->scratch->int1 = bp->path_cost;
1226 			if (bridge_port_set_path_cost(b_name, bp,
1227 			    val->v.integer) < 0)
1228 			    return (SNMP_ERR_GENERR);
1229 			return (SNMP_ERR_NOERROR);
1230 
1231 		    case LEAF_begemotBridgeStpPort:
1232 		    case LEAF_begemotBridgeStpPortState:
1233 		    case LEAF_begemotBridgeStpPortDesignatedRoot:
1234 		    case LEAF_begemotBridgeStpPortDesignatedCost:
1235 		    case LEAF_begemotBridgeStpPortDesignatedBridge:
1236 		    case LEAF_begemotBridgeStpPortDesignatedPort:
1237 		    case LEAF_begemotBridgeStpPortForwardTransitions:
1238 			return (SNMP_ERR_NOT_WRITEABLE);
1239 		}
1240 		abort();
1241 
1242 	    case SNMP_OP_ROLLBACK:
1243 		if ((bp = bridge_port_index_get(&val->var, sub, 0)) == NULL ||
1244 		    (b_name = bridge_if_find_name(bp->sysindex)) == NULL)
1245 			return (SNMP_ERR_GENERR);
1246 
1247 		switch (val->var.subs[sub - 1]) {
1248 		    case LEAF_begemotBridgeStpPortPriority:
1249 			bridge_port_set_priority(b_name, bp,
1250 			    ctx->scratch->int1);
1251 			break;
1252 		    case LEAF_begemotBridgeStpPortEnable:
1253 			bridge_port_set_stp_enable(b_name, bp,
1254 			    ctx->scratch->int1);
1255 			break;
1256 		    case LEAF_begemotBridgeStpPortPathCost:
1257 			bridge_port_set_path_cost(b_name, bp,
1258 			    ctx->scratch->int1);
1259 			break;
1260 		}
1261 		return (SNMP_ERR_NOERROR);
1262 
1263 	    case SNMP_OP_COMMIT:
1264 		return (SNMP_ERR_NOERROR);
1265 	}
1266 	abort();
1267 
1268 get:
1269 	switch (val->var.subs[sub - 1]) {
1270 	    case LEAF_begemotBridgeStpPort:
1271 		val->v.integer = bp->port_no;
1272 		return (SNMP_ERR_NOERROR);
1273 
1274 	    case LEAF_begemotBridgeStpPortPriority:
1275 		val->v.integer = bp->priority;
1276 		return (SNMP_ERR_NOERROR);
1277 
1278 	    case LEAF_begemotBridgeStpPortState:
1279 		val->v.integer = bp->state;
1280 		return (SNMP_ERR_NOERROR);
1281 
1282 	    case LEAF_begemotBridgeStpPortEnable:
1283 		val->v.integer = bp->enable;
1284 		return (SNMP_ERR_NOERROR);
1285 
1286 	    case LEAF_begemotBridgeStpPortPathCost:
1287 		val->v.integer = bp->path_cost;
1288 		return (SNMP_ERR_NOERROR);
1289 
1290 	    case LEAF_begemotBridgeStpPortDesignatedRoot:
1291 		return (string_get(val, bp->design_root, SNMP_BRIDGE_ID_LEN));
1292 
1293 	    case LEAF_begemotBridgeStpPortDesignatedCost:
1294 		val->v.integer = bp->design_cost;
1295 		return (SNMP_ERR_NOERROR);
1296 
1297 	    case LEAF_begemotBridgeStpPortDesignatedBridge:
1298 		return (string_get(val, bp->design_bridge, SNMP_BRIDGE_ID_LEN));
1299 
1300 	    case LEAF_begemotBridgeStpPortDesignatedPort:
1301 		return (string_get(val, bp->design_port, 2));
1302 
1303 	    case LEAF_begemotBridgeStpPortForwardTransitions:
1304 		val->v.uint32 = bp->fwd_trans;
1305 		return (SNMP_ERR_NOERROR);
1306 	}
1307 
1308 	abort();
1309 }
1310 
1311 int
1312 op_begemot_stp_ext_port(struct snmp_context *ctx, struct snmp_value *val,
1313     uint sub, uint iidx __unused, enum snmp_op op)
1314 {
1315 	struct bridge_port *bp;
1316 	const char *b_name;
1317 
1318 	if (time(NULL) - ports_list_age > bridge_get_data_maxage())
1319 		bridge_update_all_ports();
1320 
1321 	switch (op) {
1322 	    case SNMP_OP_GET:
1323 		if ((bp = bridge_port_index_get(&val->var, sub, 0)) == NULL)
1324 		    return (SNMP_ERR_NOSUCHNAME);
1325 		goto get;
1326 
1327 	    case SNMP_OP_GETNEXT:
1328 		if ((bp = bridge_port_index_getnext(&val->var, sub, 0)) ==
1329 		    NULL || bridge_port_index_append(&val->var, sub, bp) < 0)
1330 			return (SNMP_ERR_NOSUCHNAME);
1331 		goto get;
1332 
1333 	    case SNMP_OP_SET:
1334 		if ((bp = bridge_port_index_get(&val->var, sub, 0)) == NULL)
1335 			return (SNMP_ERR_NOSUCHNAME);
1336 		if ((b_name = bridge_if_find_name(bp->sysindex)) == NULL)
1337 			return (SNMP_ERR_GENERR);
1338 
1339 		switch (val->var.subs[sub - 1]) {
1340 		    case LEAF_begemotBridgeStpPortAdminEdgePort:
1341 			if (val->v.integer != TruthValue_true &&
1342 			    val->v.integer != TruthValue_false)
1343 			    return (SNMP_ERR_WRONG_VALUE);
1344 
1345 			ctx->scratch->int1 = bp->admin_edge;
1346 			if (bridge_port_set_admin_edge(b_name, bp,
1347 			    val->v.integer) < 0)
1348 			    return (SNMP_ERR_GENERR);
1349 			return (SNMP_ERR_NOERROR);
1350 
1351 		    case LEAF_begemotBridgeStpPortAdminPointToPoint:
1352 			if (val->v.integer < 0 || val->v.integer >
1353 			    StpPortAdminPointToPointType_auto)
1354 			    return (SNMP_ERR_WRONG_VALUE);
1355 
1356 			ctx->scratch->int1 = bp->admin_ptp;
1357 			if (bridge_port_set_admin_ptp(b_name, bp,
1358 			    val->v.integer) < 0)
1359 			    return (SNMP_ERR_GENERR);
1360 			return (SNMP_ERR_NOERROR);
1361 
1362 		    case LEAF_begemotBridgeStpPortAdminPathCost:
1363 			if (val->v.integer < SNMP_PORT_MIN_PATHCOST ||
1364 			    val->v.integer > SNMP_PORT_MAX_PATHCOST)
1365 			    return (SNMP_ERR_WRONG_VALUE);
1366 
1367 			ctx->scratch->int1 = bp->admin_path_cost;
1368 			if (bridge_port_set_path_cost(b_name, bp,
1369 			    val->v.integer) < 0)
1370 			    return (SNMP_ERR_GENERR);
1371 			return (SNMP_ERR_NOERROR);
1372 
1373 		    case LEAF_begemotBridgeStpPortProtocolMigration:
1374 		    case LEAF_begemotBridgeStpPortOperEdgePort:
1375 		    case LEAF_begemotBridgeStpPortOperPointToPoint:
1376 			return (SNMP_ERR_NOT_WRITEABLE);
1377 		}
1378 		abort();
1379 
1380 	    case SNMP_OP_ROLLBACK:
1381 		if ((bp = bridge_port_index_get(&val->var, sub, 0)) == NULL ||
1382 		    (b_name = bridge_if_find_name(bp->sysindex)) == NULL)
1383 			return (SNMP_ERR_GENERR);
1384 
1385 		switch (val->var.subs[sub - 1]) {
1386 		    case LEAF_begemotBridgeStpPortAdminEdgePort:
1387 			bridge_port_set_admin_edge(b_name, bp,
1388 			    ctx->scratch->int1);
1389 			break;
1390 		    case LEAF_begemotBridgeStpPortAdminPointToPoint:
1391 			bridge_port_set_admin_ptp(b_name, bp,
1392 			    ctx->scratch->int1);
1393 			break;
1394 		    case LEAF_begemotBridgeStpPortAdminPathCost:
1395 			bridge_port_set_path_cost(b_name, bp,
1396 			    ctx->scratch->int1);
1397 			break;
1398 		}
1399 		return (SNMP_ERR_NOERROR);
1400 
1401 	    case SNMP_OP_COMMIT:
1402 		return (SNMP_ERR_NOERROR);
1403 	}
1404 	abort();
1405 
1406 get:
1407 	switch (val->var.subs[sub - 1]) {
1408 		case LEAF_begemotBridgeStpPortProtocolMigration:
1409 			val->v.integer = bp->proto_migr;
1410 			return (SNMP_ERR_NOERROR);
1411 
1412 		case LEAF_begemotBridgeStpPortAdminEdgePort:
1413 			val->v.integer = bp->admin_edge;
1414 			return (SNMP_ERR_NOERROR);
1415 
1416 		case LEAF_begemotBridgeStpPortOperEdgePort:
1417 			val->v.integer = bp->oper_edge;
1418 			return (SNMP_ERR_NOERROR);
1419 
1420 		case LEAF_begemotBridgeStpPortAdminPointToPoint:
1421 			val->v.integer = bp->admin_ptp;
1422 			return (SNMP_ERR_NOERROR);
1423 
1424 		case LEAF_begemotBridgeStpPortOperPointToPoint:
1425 			val->v.integer = bp->oper_ptp;
1426 			return (SNMP_ERR_NOERROR);
1427 
1428 		case LEAF_begemotBridgeStpPortAdminPathCost:
1429 			val->v.integer = bp->admin_path_cost;
1430 			return (SNMP_ERR_NOERROR);
1431 	}
1432 
1433 	abort();
1434 }
1435 
1436 int
1437 op_begemot_tp_port(struct snmp_context *c __unused, struct snmp_value *val,
1438 	uint sub, uint iidx __unused, enum snmp_op op)
1439 {
1440 	struct bridge_port *bp;
1441 
1442 	if (time(NULL) - ports_list_age > bridge_get_data_maxage())
1443 		bridge_update_all_ports();
1444 
1445 	switch (op) {
1446 	    case SNMP_OP_GET:
1447 		if ((bp = bridge_port_index_get(&val->var, sub, 0)) == NULL)
1448 		    return (SNMP_ERR_NOSUCHNAME);
1449 		goto get;
1450 
1451 	    case SNMP_OP_GETNEXT:
1452 		if ((bp = bridge_port_index_getnext(&val->var, sub, 0)) ==
1453 		    NULL || bridge_port_index_append(&val->var, sub, bp) < 0)
1454 		    return (SNMP_ERR_NOSUCHNAME);
1455 		goto get;
1456 
1457 	    case SNMP_OP_SET:
1458 		return (SNMP_ERR_NOT_WRITEABLE);
1459 
1460 	    case SNMP_OP_ROLLBACK:
1461 	    case SNMP_OP_COMMIT:
1462 		break;
1463 	}
1464 	abort();
1465 
1466 get:
1467 	switch (val->var.subs[sub - 1]) {
1468 	    case LEAF_begemotBridgeTpPort:
1469 		val->v.integer = bp->port_no;
1470 		return (SNMP_ERR_NOERROR);
1471 
1472 	    case LEAF_begemotBridgeTpPortMaxInfo:
1473 		val->v.integer = bp->max_info;
1474 		return (SNMP_ERR_NOERROR);
1475 
1476 	    case LEAF_begemotBridgeTpPortInFrames:
1477 		val->v.uint32 = bp->in_frames;
1478 		return (SNMP_ERR_NOERROR);
1479 
1480 	    case LEAF_begemotBridgeTpPortOutFrames:
1481 		val->v.uint32 = bp->out_frames;
1482 		return (SNMP_ERR_NOERROR);
1483 
1484 	    case LEAF_begemotBridgeTpPortInDiscards:
1485 		val->v.uint32 = bp->in_drops;
1486 		return (SNMP_ERR_NOERROR);
1487 	}
1488 
1489 	abort();
1490 }
1491