xref: /freebsd/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_if.c (revision f02a1e579450e1ffe500f7f27594f58a9f045daa)
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 interface objects.
28  *
29  * $FreeBSD$
30  */
31 
32 #include <sys/queue.h>
33 #include <sys/socket.h>
34 #include <sys/time.h>
35 #include <sys/types.h>
36 
37 #include <net/ethernet.h>
38 #include <net/if.h>
39 #include <net/if_mib.h>
40 #include <net/if_types.h>
41 
42 #include <errno.h>
43 #include <stdarg.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <syslog.h>
47 
48 #include <bsnmp/snmpmod.h>
49 #include <bsnmp/snmp_mibII.h>
50 
51 #include "bridge_tree.h"
52 #include "bridge_snmp.h"
53 #include "bridge_oid.h"
54 
55 static const struct asn_oid oid_newRoot = OIDX_newRoot;
56 static const struct asn_oid oid_TopologyChange = OIDX_topologyChange;
57 static const struct asn_oid oid_begemotBrigeName = \
58 			OIDX_begemotBridgeBaseName;
59 static const struct asn_oid oid_begemotNewRoot = OIDX_begemotBridgeNewRoot;
60 static const struct asn_oid oid_begemotTopologyChange = \
61 			OIDX_begemotBridgeTopologyChange;
62 
63 TAILQ_HEAD(bridge_ifs, bridge_if);
64 
65 /*
66  * Free the bridge interface list.
67  */
68 static void
69 bridge_ifs_free(struct bridge_ifs *headp)
70 {
71 	struct bridge_if *b;
72 
73 	while ((b = TAILQ_FIRST(headp)) != NULL) {
74 		TAILQ_REMOVE(headp, b, b_if);
75 		free(b);
76 	}
77 }
78 
79 /*
80  * Insert an entry in the bridge interface TAILQ. Keep the
81  * TAILQ sorted by the bridge's interface name.
82  */
83 static void
84 bridge_ifs_insert(struct bridge_ifs *headp,
85 	struct bridge_if *b)
86 {
87 	struct bridge_if *temp;
88 
89 	if ((temp = TAILQ_FIRST(headp)) == NULL ||
90 	    strcmp(b->bif_name, temp->bif_name) < 0) {
91 		TAILQ_INSERT_HEAD(headp, b, b_if);
92 		return;
93 	}
94 
95 	TAILQ_FOREACH(temp, headp, b_if)
96 		if(strcmp(b->bif_name, temp->bif_name) < 0)
97 			TAILQ_INSERT_BEFORE(temp, b, b_if);
98 
99 	TAILQ_INSERT_TAIL(headp, b, b_if);
100 }
101 
102 /* The global bridge interface list. */
103 static struct bridge_ifs bridge_ifs = TAILQ_HEAD_INITIALIZER(bridge_ifs);
104 static time_t bridge_list_age;
105 
106 /*
107  * Free the global list.
108  */
109 void
110 bridge_ifs_fini(void)
111 {
112 	bridge_ifs_free(&bridge_ifs);
113 }
114 
115 /*
116  * Find a bridge interface entry by the bridge interface system index.
117  */
118 struct bridge_if *
119 bridge_if_find_ifs(uint32_t sysindex)
120 {
121 	struct bridge_if *b;
122 
123 	TAILQ_FOREACH(b, &bridge_ifs, b_if)
124 		if (b->sysindex == sysindex)
125 			return (b);
126 
127 	return (NULL);
128 }
129 
130 /*
131  * Find a bridge interface entry by the bridge interface name.
132  */
133 struct bridge_if *
134 bridge_if_find_ifname(const char *b_name)
135 {
136 	struct bridge_if *b;
137 
138 	TAILQ_FOREACH(b, &bridge_ifs, b_if)
139 		if (strcmp(b_name, b->bif_name) == 0)
140 			return (b);
141 
142 	return (NULL);
143 }
144 
145 /*
146  * Find a bridge name by the bridge interface system index.
147  */
148 const char *
149 bridge_if_find_name(uint32_t sysindex)
150 {
151 	struct bridge_if *b;
152 
153 	TAILQ_FOREACH(b, &bridge_ifs, b_if)
154 		if (b->sysindex == sysindex)
155 			return (b->bif_name);
156 
157 	return (NULL);
158 }
159 
160 /*
161  * Given two bridge interfaces' system indexes, find their
162  * corresponding names and return the result of the name
163  * comparison. Returns:
164  * error : -2
165  * i1 < i2 : -1
166  * i1 > i2 : +1
167  * i1 = i2 : 0
168  */
169 int
170 bridge_compare_sysidx(uint32_t i1, uint32_t i2)
171 {
172 	int c;
173 	const char *b1, *b2;
174 
175 	if (i1 == i2)
176 		return (0);
177 
178 	if ((b1 = bridge_if_find_name(i1)) == NULL) {
179 		syslog(LOG_ERR, "Bridge interface %d does not exist", i1);
180 		return (-2);
181 	}
182 
183 	if ((b2 = bridge_if_find_name(i2)) == NULL) {
184 		syslog(LOG_ERR, "Bridge interface %d does not exist", i2);
185 		return (-2);
186 	}
187 
188 	if ((c = strcmp(b1, b2)) < 0)
189 		return (-1);
190 	else if (c > 0)
191 		return (1);
192 
193 	return (0);
194 }
195 
196 /*
197  * Fetch the first bridge interface from the list.
198  */
199 struct bridge_if *
200 bridge_first_bif(void)
201 {
202 	return (TAILQ_FIRST(&bridge_ifs));
203 }
204 
205 /*
206  * Fetch the next bridge interface from the list.
207  */
208 struct bridge_if *
209 bridge_next_bif(struct bridge_if *b_pr)
210 {
211 	return (TAILQ_NEXT(b_pr, b_if));
212 }
213 
214 /*
215  * Create a new entry for a bridge interface and insert
216  * it in the list.
217  */
218 static struct bridge_if *
219 bridge_new_bif(const char *bif_n, uint32_t sysindex, const u_char *physaddr)
220 {
221 	struct bridge_if *bif;
222 
223 	if ((bif = (struct bridge_if *) malloc(sizeof(*bif)))== NULL) {
224 		syslog(LOG_ERR, "bridge new interface failed: %s",
225 		    strerror(errno));
226 		return (NULL);
227 	}
228 
229 	bzero(bif, sizeof(struct bridge_if));
230 	strlcpy(bif->bif_name, bif_n, IFNAMSIZ);
231 	bcopy(physaddr, bif->br_addr.octet, ETHER_ADDR_LEN);
232 	bif->sysindex = sysindex;
233 	bif->br_type = BaseType_transparent_only;
234 	/* 1 - all bridges default hold time * 100 - centi-seconds */
235 	bif->hold_time = 1 * 100;
236 	bif->prot_spec = dot1dStpProtocolSpecification_ieee8021d;
237 	bridge_ifs_insert(&bridge_ifs, bif);
238 
239 	return (bif);
240 }
241 
242 /*
243  * Remove a bridge interface from the list, freeing all it's ports
244  * and address entries.
245  */
246 void
247 bridge_remove_bif(struct bridge_if *bif)
248 {
249 	bridge_members_free(bif);
250 	bridge_addrs_free(bif);
251 	TAILQ_REMOVE(&bridge_ifs, bif, b_if);
252 	free(bif);
253 }
254 
255 
256 /*
257  * Prepare the variable (bridge interface name) for the private
258  * begemot notifications.
259  */
260 static struct snmp_value*
261 bridge_basename_var(struct bridge_if *bif, struct snmp_value* b_val)
262 {
263 	uint i;
264 
265 	b_val->var = oid_begemotBrigeName;
266 	b_val->var.subs[b_val->var.len++] = strlen(bif->bif_name);
267 
268 	if ((b_val->v.octetstring.octets = (u_char *)
269 	    malloc(strlen(bif->bif_name))) == NULL)
270 		return (NULL);
271 
272 	for (i = 0; i < strlen(bif->bif_name); i++)
273 		b_val->var.subs[b_val->var.len++] = bif->bif_name[i];
274 
275 	b_val->v.octetstring.len = strlen(bif->bif_name);
276 	bcopy(bif->bif_name, b_val->v.octetstring.octets,
277 	    strlen(bif->bif_name));
278 	b_val->syntax = SNMP_SYNTAX_OCTETSTRING;
279 
280 	return (b_val);
281 }
282 
283 /*
284  * Compare the values of the old and the new root port and
285  * send a new root notification, if they are not matching.
286  */
287 static void
288 bridge_new_root(struct bridge_if *bif)
289 {
290 	struct snmp_value bif_idx;
291 
292 	if (bridge_get_default() == bif)
293 		snmp_send_trap(&oid_newRoot, (struct snmp_value *) NULL);
294 
295 	if (bridge_basename_var(bif, &bif_idx) == NULL)
296 		return;
297 
298 	snmp_send_trap(&oid_begemotTopologyChange,
299 	    &bif_idx, (struct snmp_value *) NULL);
300 }
301 
302 /*
303  * Compare the new and old topology change times and send a
304  * topology change notification if necessary.
305  */
306 static void
307 bridge_top_change(struct bridge_if *bif)
308 {
309 	struct snmp_value bif_idx;
310 
311 	if (bridge_get_default() == bif)
312 		snmp_send_trap(&oid_TopologyChange,
313 		    (struct snmp_value *) NULL);
314 
315 	if (bridge_basename_var(bif, &bif_idx) == NULL)
316 		return;
317 
318 	snmp_send_trap(&oid_begemotNewRoot,
319 	    &bif_idx, (struct snmp_value *) NULL);
320 }
321 
322 static int
323 bridge_if_create(const char* b_name, int8_t up)
324 {
325 	if (bridge_create(b_name) < 0)
326 		return (-1);
327 
328 	if (up == 1 && (bridge_set_if_up(b_name, 1) < 0))
329 		return (-1);
330 
331 	/*
332 	 * Do not create a new bridge entry here -
333 	 * wait until the mibII module notifies us.
334 	 */
335 	return (0);
336 }
337 
338 static int
339 bridge_if_destroy(struct bridge_if *bif)
340 {
341 	if (bridge_destroy(bif->bif_name) < 0)
342 		return (-1);
343 
344 	bridge_remove_bif(bif);
345 
346 	return (0);
347 }
348 
349 /*
350  * Calculate the timeticks since the last topology change.
351  */
352 static int
353 bridge_get_time_since_tc(struct bridge_if *bif, uint32_t *ticks)
354 {
355 	struct timeval ct;
356 
357 	if (gettimeofday(&ct, NULL) < 0) {
358 		syslog(LOG_ERR, "bridge get time since last TC:"
359 		    "getttimeofday failed: %s", strerror(errno));
360 		return (-1);
361 	}
362 
363 	if (ct.tv_usec - bif->last_tc_time.tv_usec < 0) {
364 		ct.tv_sec -= 1;
365 		ct.tv_usec += 1000000;
366 	}
367 
368 	ct.tv_sec -= bif->last_tc_time.tv_sec;
369 	ct.tv_usec -= bif->last_tc_time.tv_usec;
370 
371 	*ticks = ct.tv_sec * 100 + ct.tv_usec/10000;
372 
373 	return (0);
374 }
375 
376 /*
377  * Update the info we have for a single bridge interface.
378  * Return:
379  * 1, if successful
380  * 0, if the interface was deleted
381  * -1, error occured while fetching the info from the kernel.
382  */
383 static int
384 bridge_update_bif(struct bridge_if *bif)
385 {
386 	struct mibif *ifp;
387 
388 	/* Walk through the mibII interface list. */
389 	for (ifp = mib_first_if(); ifp != NULL; ifp = mib_next_if(ifp))
390 		if (strcmp(ifp->name, bif->bif_name) == 0)
391 			break;
392 
393 	if (ifp == NULL) {
394 		/* Ops, we do not exist anymore. */
395 		bridge_remove_bif(bif);
396 		return (0);
397 	}
398 
399 	if (ifp->physaddr != NULL )
400 		bcopy(ifp->physaddr, bif->br_addr.octet, ETHER_ADDR_LEN);
401 	else
402 		bridge_get_basemac(bif->bif_name, bif->br_addr.octet);
403 
404 	if (ifp->mib.ifmd_flags & IFF_RUNNING)
405 		bif->if_status = RowStatus_active;
406 	else
407 		bif->if_status = RowStatus_notInService;
408 
409 	switch (bridge_getinfo_bif(bif)) {
410 		case 2:
411 			bridge_new_root(bif);
412 			break;
413 		case 1:
414 			bridge_top_change(bif);
415 			break;
416 		case -1:
417 			bridge_remove_bif(bif);
418 			return (-1);
419 		default:
420 			break;
421 	}
422 
423 	/*
424 	 * The number of ports is accessible via SNMP -
425 	 * update the ports each time the bridge interface data
426 	 * is refreshed too.
427 	 */
428 	bif->num_ports = bridge_update_memif(bif);
429 	bif->entry_age = time(NULL);
430 
431 	return (1);
432 }
433 
434 /*
435  * Update all bridge interfaces' ports only -
436  * make sure each bridge interface exists first.
437  */
438 void
439 bridge_update_all_ports(void)
440 {
441 	struct mibif *ifp;
442 	struct bridge_if *bif, *t_bif;
443 
444 	for (bif = bridge_first_bif(); bif != NULL; bif = t_bif) {
445 		t_bif = bridge_next_bif(bif);
446 
447 		for (ifp = mib_first_if(); ifp != NULL;
448 		    ifp = mib_next_if(ifp))
449 			if (strcmp(ifp->name, bif->bif_name) == 0)
450 				break;
451 
452 		if (ifp != NULL)
453 			bif->num_ports = bridge_update_memif(bif);
454 		else  /* Ops, we do not exist anymore. */
455 			bridge_remove_bif(bif);
456 	}
457 
458 	bridge_ports_update_listage();
459 }
460 
461 /*
462  * Update all addresses only.
463  */
464 void
465 bridge_update_all_addrs(void)
466 {
467 	struct mibif *ifp;
468 	struct bridge_if *bif, *t_bif;
469 
470 	for (bif = bridge_first_bif(); bif != NULL; bif = t_bif) {
471 		t_bif = bridge_next_bif(bif);
472 
473 		for (ifp = mib_first_if(); ifp != NULL;
474 		    ifp = mib_next_if(ifp))
475 			if (strcmp(ifp->name, bif->bif_name) == 0)
476 				break;
477 
478 		if (ifp != NULL)
479 			bif->num_addrs = bridge_update_addrs(bif);
480 		else  /* Ops, we don't exist anymore. */
481 			bridge_remove_bif(bif);
482 	}
483 
484 	bridge_addrs_update_listage();
485 }
486 
487 /*
488  * Update only the bridge interfaces' data - skip addresses.
489  */
490 void
491 bridge_update_all_ifs(void)
492 {
493 	struct bridge_if *bif, *t_bif;
494 
495 	for (bif = bridge_first_bif(); bif != NULL; bif = t_bif) {
496 		t_bif = bridge_next_bif(bif);
497 		bridge_update_bif(bif);
498 	}
499 
500 	bridge_ports_update_listage();
501 	bridge_list_age = time(NULL);
502 }
503 
504 /*
505  * Update all info we have for all bridges.
506  */
507 void
508 bridge_update_all(void *arg __unused)
509 {
510 	struct bridge_if *bif, *t_bif;
511 
512 	for (bif = bridge_first_bif(); bif != NULL; bif = t_bif) {
513 		t_bif = bridge_next_bif(bif);
514 		if (bridge_update_bif(bif) <= 0)
515 			continue;
516 
517 		/* Update our learnt addresses. */
518 		bif->num_addrs = bridge_update_addrs(bif);
519 	}
520 
521 	bridge_list_age = time(NULL);
522 	bridge_ports_update_listage();
523 	bridge_addrs_update_listage();
524 }
525 
526 /*
527  * Callback for polling our last topology change time -
528  * check whether we are root or whether a TC was detected once every
529  * 30 seconds, so that we can send the newRoot and TopologyChange traps
530  * on time. The rest of the data is polled only once every 5 min.
531  */
532 void
533 bridge_update_tc_time(void *arg __unused)
534 {
535 	struct bridge_if *bif;
536 	struct mibif *ifp;
537 
538 	TAILQ_FOREACH(bif, &bridge_ifs, b_if) {
539 		/* Walk through the mibII interface list. */
540 		for (ifp = mib_first_if(); ifp != NULL; ifp = mib_next_if(ifp))
541 			if (strcmp(ifp->name, bif->bif_name) == 0)
542 				break;
543 
544 		if (ifp == NULL) {
545 			bridge_remove_bif(bif);
546 			continue;
547 		}
548 
549 		switch (bridge_get_op_param(bif)) {
550 			case 2:
551 				bridge_new_root(bif);
552 				break;
553 			case 1:
554 				bridge_top_change(bif);
555 				break;
556 		}
557 	}
558 }
559 
560 /*
561  * Callback for handling new bridge interface creation.
562  */
563 int
564 bridge_attach_newif(struct mibif *ifp)
565 {
566 	u_char *p_mac, mac[ETHER_ADDR_LEN];
567 	struct bridge_if *bif;
568 
569 	if (ifp->mib.ifmd_data.ifi_type != IFT_BRIDGE)
570 		return (0);
571 
572 	/* Make sure it does not exist in our list. */
573 	TAILQ_FOREACH(bif, &bridge_ifs, b_if)
574 		if(strcmp(bif->bif_name, ifp->name) == 0) {
575 			syslog(LOG_ERR, "bridge interface %s already "
576 			    "in list", bif->bif_name);
577 			return (-1);
578 		}
579 
580 	if ((p_mac = ifp->physaddr) == NULL &&
581 	    (p_mac = bridge_get_basemac(ifp->name, mac)) == NULL) {
582 		syslog(LOG_ERR, "bridge attach new %s failed - "
583 		    "no bridge mac address", ifp->name);
584 		return (-1);
585 	}
586 
587 	if ((bif = bridge_new_bif(ifp->name, ifp->sysindex, p_mac)) == NULL)
588 		return (-1);
589 
590 	if (ifp->mib.ifmd_flags & IFF_RUNNING)
591 		bif->if_status = RowStatus_active;
592 	else
593 		bif->if_status = RowStatus_notInService;
594 
595 	/* Skip sending notifications if the interface was just created. */
596 	if (bridge_getinfo_bif(bif) < 0 ||
597 	    (bif->num_ports = bridge_getinfo_bif_ports(bif)) < 0 ||
598 	    (bif->num_addrs = bridge_getinfo_bif_addrs(bif)) < 0) {
599 		bridge_remove_bif(bif);
600 		return (-1);
601 	}
602 
603 	/* Check whether we are the default bridge interface. */
604 	if (strcmp(ifp->name, bridge_get_default_name()) == 0)
605 		bridge_set_default(bif);
606 
607 	return (0);
608 }
609 
610 void
611 bridge_ifs_dump(void)
612 {
613 	struct bridge_if *bif;
614 
615 	for (bif = bridge_first_bif(); bif != NULL;
616 		bif = bridge_next_bif(bif)) {
617 		syslog(LOG_ERR, "Bridge %s, index - %d", bif->bif_name,
618 		    bif->sysindex);
619 		bridge_ports_dump(bif);
620 		bridge_addrs_dump(bif);
621 	}
622 }
623 
624 /*
625  * RFC4188 specifics.
626  */
627 int
628 op_dot1d_base(struct snmp_context *ctx __unused, struct snmp_value *value,
629 	uint sub, uint iidx __unused, enum snmp_op op)
630 {
631 	struct bridge_if *bif;
632 
633 	if ((bif = bridge_get_default()) == NULL)
634 		return (SNMP_ERR_NOSUCHNAME);
635 
636 	if (time(NULL) - bif->entry_age > bridge_get_data_maxage() &&
637 	    bridge_update_bif(bif) <= 0) /* It was just deleted. */
638 		return (SNMP_ERR_NOSUCHNAME);
639 
640 	switch (op) {
641 	    case SNMP_OP_GET:
642 		switch (value->var.subs[sub - 1]) {
643 		    case LEAF_dot1dBaseBridgeAddress:
644 			return (string_get(value, bif->br_addr.octet,
645 			    ETHER_ADDR_LEN));
646 		    case LEAF_dot1dBaseNumPorts:
647 			value->v.integer = bif->num_ports;
648 			return (SNMP_ERR_NOERROR);
649 		    case LEAF_dot1dBaseType:
650 			value->v.integer = bif->br_type;
651 			return (SNMP_ERR_NOERROR);
652 		}
653 		abort();
654 
655 		case SNMP_OP_SET:
656 		    return (SNMP_ERR_NOT_WRITEABLE);
657 
658 		case SNMP_OP_GETNEXT:
659 		case SNMP_OP_ROLLBACK:
660 		case SNMP_OP_COMMIT:
661 		   break;
662 	}
663 
664 	abort();
665 }
666 
667 int
668 op_dot1d_stp(struct snmp_context *ctx, struct snmp_value *val, uint sub,
669     uint iidx __unused, enum snmp_op op)
670 {
671 	int ret;
672 	struct bridge_if *bif;
673 
674 	if ((bif = bridge_get_default()) == NULL)
675 		return (SNMP_ERR_NOSUCHNAME);
676 
677 	if (time(NULL) - bif->entry_age > bridge_get_data_maxage() &&
678 	    bridge_update_bif(bif) <= 0) /* It was just deleted. */
679 		return (SNMP_ERR_NOSUCHNAME);
680 
681 	switch (op) {
682 	    case SNMP_OP_GET:
683 		switch (val->var.subs[sub - 1]) {
684 		    case LEAF_dot1dStpProtocolSpecification:
685 			val->v.integer = bif->prot_spec;
686 			return (SNMP_ERR_NOERROR);
687 
688 		    case LEAF_dot1dStpPriority:
689 			val->v.integer = bif->priority;
690 			return (SNMP_ERR_NOERROR);
691 
692 		    case LEAF_dot1dStpTimeSinceTopologyChange:
693 			if (bridge_get_time_since_tc(bif,
694 			    &(val->v.uint32)) < 0)
695 				return (SNMP_ERR_GENERR);
696 			return (SNMP_ERR_NOERROR);
697 
698 		    case LEAF_dot1dStpTopChanges:
699 			val->v.uint32 = bif->top_changes;
700 			return (SNMP_ERR_NOERROR);
701 
702 		    case LEAF_dot1dStpDesignatedRoot:
703 			return (string_get(val, bif->design_root,
704 			    SNMP_BRIDGE_ID_LEN));
705 
706 		    case LEAF_dot1dStpRootCost:
707 			val->v.integer = bif->root_cost;
708 			return (SNMP_ERR_NOERROR);
709 
710 		    case LEAF_dot1dStpRootPort:
711 			val->v.integer = bif->root_port;
712 			return (SNMP_ERR_NOERROR);
713 
714 		    case LEAF_dot1dStpMaxAge:
715 			val->v.integer = bif->max_age;
716 			return (SNMP_ERR_NOERROR);
717 
718 		    case LEAF_dot1dStpHelloTime:
719 			val->v.integer = bif->hello_time;
720 			return (SNMP_ERR_NOERROR);
721 
722 		    case LEAF_dot1dStpHoldTime:
723 			val->v.integer = bif->hold_time;
724 			return (SNMP_ERR_NOERROR);
725 
726 		    case LEAF_dot1dStpForwardDelay:
727 			val->v.integer = bif->fwd_delay;
728 			return (SNMP_ERR_NOERROR);
729 
730 		    case LEAF_dot1dStpBridgeMaxAge:
731 			val->v.integer = bif->bridge_max_age;
732 			return (SNMP_ERR_NOERROR);
733 
734 		    case LEAF_dot1dStpBridgeHelloTime:
735 			val->v.integer = bif->bridge_hello_time;
736 			return (SNMP_ERR_NOERROR);
737 
738 		    case LEAF_dot1dStpBridgeForwardDelay:
739 			val->v.integer = bif->bridge_fwd_delay;
740 			return (SNMP_ERR_NOERROR);
741 
742 		    case LEAF_dot1dStpVersion:
743 			val->v.integer = bif->stp_version;
744 			return (SNMP_ERR_NOERROR);
745 
746 		    case LEAF_dot1dStpTxHoldCount:
747 			val->v.integer = bif->tx_hold_count;
748 			return (SNMP_ERR_NOERROR);
749 		}
750 		abort();
751 
752 	    case SNMP_OP_GETNEXT:
753 		abort();
754 
755 	    case SNMP_OP_SET:
756 		switch (val->var.subs[sub - 1]) {
757 		    case LEAF_dot1dStpPriority:
758 			if (val->v.integer > SNMP_BRIDGE_MAX_PRIORITY ||
759 			    val->v.integer % 4096 != 0)
760 			    return (SNMP_ERR_WRONG_VALUE);
761 
762 			ctx->scratch->int1 = bif->priority;
763 			if (bridge_set_priority(bif, val->v.integer) < 0)
764 			    return (SNMP_ERR_GENERR);
765 			return (SNMP_ERR_NOERROR);
766 
767 		    case LEAF_dot1dStpBridgeMaxAge:
768 			if (val->v.integer < SNMP_BRIDGE_MIN_MAGE ||
769 			    val->v.integer > SNMP_BRIDGE_MAX_MAGE)
770 			    return (SNMP_ERR_WRONG_VALUE);
771 
772 			ctx->scratch->int1 = bif->bridge_max_age;
773 			if (bridge_set_maxage(bif, val->v.integer) < 0)
774 			    return (SNMP_ERR_GENERR);
775 			return (SNMP_ERR_NOERROR);
776 
777 		    case LEAF_dot1dStpBridgeHelloTime:
778 			if (val->v.integer < SNMP_BRIDGE_MIN_HTIME ||
779 			    val->v.integer > SNMP_BRIDGE_MAX_HTIME)
780 			    return (SNMP_ERR_WRONG_VALUE);
781 
782 			ctx->scratch->int1 = bif->bridge_hello_time;
783 			if (bridge_set_hello_time(bif, val->v.integer) < 0)
784 			    return (SNMP_ERR_GENERR);
785 			return (SNMP_ERR_NOERROR);
786 
787 		    case LEAF_dot1dStpBridgeForwardDelay:
788 			if (val->v.integer < SNMP_BRIDGE_MIN_FDELAY ||
789 			    val->v.integer > SNMP_BRIDGE_MAX_FDELAY)
790 			    return (SNMP_ERR_WRONG_VALUE);
791 
792 			ctx->scratch->int1 = bif->bridge_fwd_delay;
793 			if (bridge_set_forward_delay(bif, val->v.integer) < 0)
794 			    return (SNMP_ERR_GENERR);
795 			return (SNMP_ERR_NOERROR);
796 
797 		    case LEAF_dot1dStpVersion:
798 			if (val->v.integer != dot1dStpVersion_stpCompatible &&
799 			    val->v.integer != dot1dStpVersion_rstp)
800 			    return (SNMP_ERR_WRONG_VALUE);
801 
802 			ctx->scratch->int1 = bif->stp_version;
803 			if (bridge_set_stp_version(bif, val->v.integer) < 0)
804 			    return (SNMP_ERR_GENERR);
805 			return (SNMP_ERR_NOERROR);
806 
807 		    case LEAF_dot1dStpTxHoldCount:
808 			if (val->v.integer < SNMP_BRIDGE_MIN_TXHC ||
809 			    val->v.integer > SNMP_BRIDGE_MAX_TXHC)
810 			    return (SNMP_ERR_WRONG_VALUE);
811 
812 			ctx->scratch->int1 = bif->tx_hold_count;
813 			if (bridge_set_tx_hold_count(bif, val->v.integer) < 0)
814 			    return (SNMP_ERR_GENERR);
815 			return (SNMP_ERR_NOERROR);
816 
817 		    case LEAF_dot1dStpProtocolSpecification:
818 		    case LEAF_dot1dStpTimeSinceTopologyChange:
819 		    case LEAF_dot1dStpTopChanges:
820 		    case LEAF_dot1dStpDesignatedRoot:
821 		    case LEAF_dot1dStpRootCost:
822 		    case LEAF_dot1dStpRootPort:
823 		    case LEAF_dot1dStpMaxAge:
824 		    case LEAF_dot1dStpHelloTime:
825 		    case LEAF_dot1dStpHoldTime:
826 		    case LEAF_dot1dStpForwardDelay:
827 			return (SNMP_ERR_NOT_WRITEABLE);
828 		}
829 		abort();
830 
831 	    case SNMP_OP_ROLLBACK:
832 		switch (val->var.subs[sub - 1]) {
833 		    case LEAF_dot1dStpPriority:
834 			bridge_set_priority(bif, ctx->scratch->int1);
835 			break;
836 		    case LEAF_dot1dStpBridgeMaxAge:
837 			bridge_set_maxage(bif, ctx->scratch->int1);
838 			break;
839 		    case LEAF_dot1dStpBridgeHelloTime:
840 			bridge_set_hello_time(bif, ctx->scratch->int1);
841 			break;
842 		    case LEAF_dot1dStpBridgeForwardDelay:
843 			bridge_set_forward_delay(bif, ctx->scratch->int1);
844 			break;
845 		    case LEAF_dot1dStpVersion:
846 			bridge_set_stp_version(bif, ctx->scratch->int1);
847 			break;
848 		    case LEAF_dot1dStpTxHoldCount:
849 			bridge_set_tx_hold_count(bif, ctx->scratch->int1);
850 			break;
851 		}
852 		return (SNMP_ERR_NOERROR);
853 
854 	    case SNMP_OP_COMMIT:
855 		return (SNMP_ERR_NOERROR);
856 	}
857 
858 	abort();
859 }
860 
861 int
862 op_dot1d_tp(struct snmp_context *ctx, struct snmp_value *value,
863 	uint sub, uint iidx __unused, enum snmp_op op)
864 {
865 	struct bridge_if *bif;
866 
867 	if ((bif = bridge_get_default()) == NULL)
868 		return (SNMP_ERR_NOSUCHNAME);
869 
870 	if (time(NULL) - bif->entry_age > bridge_get_data_maxage() &&
871 	    bridge_update_bif(bif) <= 0) /* It was just deleted. */
872 		return (SNMP_ERR_NOSUCHNAME);
873 
874 	switch (op) {
875 	    case SNMP_OP_GET:
876 		switch (value->var.subs[sub - 1]) {
877 		    case LEAF_dot1dTpLearnedEntryDiscards:
878 			value->v.uint32 = bif->lrnt_drops;
879 			return (SNMP_ERR_NOERROR);
880 		    case LEAF_dot1dTpAgingTime:
881 			value->v.integer = bif->age_time;
882 			return (SNMP_ERR_NOERROR);
883 		}
884 		abort();
885 
886 	    case SNMP_OP_GETNEXT:
887 		abort();
888 
889 	    case SNMP_OP_SET:
890 		switch (value->var.subs[sub - 1]) {
891 		    case LEAF_dot1dTpLearnedEntryDiscards:
892 			return (SNMP_ERR_NOT_WRITEABLE);
893 
894 		    case LEAF_dot1dTpAgingTime:
895 			if (value->v.integer < SNMP_BRIDGE_MIN_AGE_TIME ||
896 			    value->v.integer > SNMP_BRIDGE_MAX_AGE_TIME)
897 			    return (SNMP_ERR_WRONG_VALUE);
898 
899 			ctx->scratch->int1 = bif->age_time;
900 			if (bridge_set_aging_time(bif, value->v.integer) < 0)
901 			    return (SNMP_ERR_GENERR);
902 			return (SNMP_ERR_NOERROR);
903 		}
904 		abort();
905 
906 	    case SNMP_OP_ROLLBACK:
907 		if (value->var.subs[sub - 1] == LEAF_dot1dTpAgingTime)
908 		    bridge_set_aging_time(bif, ctx->scratch->int1);
909 		return (SNMP_ERR_NOERROR);
910 
911 	    case SNMP_OP_COMMIT:
912 		return (SNMP_ERR_NOERROR);
913 	}
914 
915 	abort();
916 }
917 
918 /*
919  * Private BEGEMOT-BRIDGE-MIB specifics.
920  */
921 
922 /*
923  * Get the bridge name from an OID index.
924  */
925 static char *
926 bridge_name_index_get(const struct asn_oid *oid, uint sub, char *b_name)
927 {
928 	uint i;
929 
930 	if (oid->len - sub != oid->subs[sub] + 1 || oid->subs[sub] >= IFNAMSIZ)
931 		return (NULL);
932 
933 	for (i = 0; i < oid->subs[sub]; i++)
934 		b_name[i] = oid->subs[sub + i + 1];
935 	b_name[i] = '\0';
936 
937 	return (b_name);
938 }
939 
940 static void
941 bridge_if_index_append(struct asn_oid *oid, uint sub,
942 	const struct bridge_if *bif)
943 {
944 	uint i;
945 
946 	oid->len = sub + strlen(bif->bif_name) + 1;
947 	oid->subs[sub] = strlen(bif->bif_name);
948 
949 	for (i = 1; i <= strlen(bif->bif_name); i++)
950 		oid->subs[sub + i] = bif->bif_name[i - 1];
951 }
952 
953 static struct bridge_if *
954 bridge_if_index_get(const struct asn_oid *oid, uint sub)
955 {
956 	uint i;
957 	char bif_name[IFNAMSIZ];
958 
959 	if (oid->len - sub != oid->subs[sub] + 1 || oid->subs[sub] >= IFNAMSIZ)
960 		return (NULL);
961 
962 	for (i = 0; i < oid->subs[sub]; i++)
963 		bif_name[i] = oid->subs[sub + i + 1];
964 	bif_name[i] = '\0';
965 
966 	return (bridge_if_find_ifname(bif_name));
967 }
968 
969 static struct bridge_if *
970 bridge_if_index_getnext(const struct asn_oid *oid, uint sub)
971 {
972 	uint i;
973 	char bif_name[IFNAMSIZ];
974 	struct bridge_if *bif;
975 
976 	if (oid->len - sub == 0)
977 		return (bridge_first_bif());
978 
979 	if (oid->len - sub != oid->subs[sub] + 1 || oid->subs[sub] >= IFNAMSIZ)
980 		return (NULL);
981 
982 	for (i = 0; i < oid->subs[sub]; i++)
983 		bif_name[i] = oid->subs[sub + i + 1];
984 	bif_name[i] = '\0';
985 
986 	if ((bif = bridge_if_find_ifname(bif_name)) == NULL)
987 		return (NULL);
988 
989 	return (bridge_next_bif(bif));
990 }
991 
992 static int
993 bridge_set_if_status(struct snmp_context *ctx,
994 	struct snmp_value *val, uint sub)
995 {
996 	struct bridge_if *bif;
997 	char bif_name[IFNAMSIZ];
998 
999 	bif = bridge_if_index_get(&val->var, sub);
1000 
1001 	switch (val->v.integer) {
1002 	    case RowStatus_active:
1003 		if (bif == NULL)
1004 		    return (SNMP_ERR_INCONS_VALUE);
1005 
1006 		ctx->scratch->int1 = bif->if_status;
1007 
1008 		switch (bif->if_status) {
1009 		    case RowStatus_active:
1010 			return (SNMP_ERR_NOERROR);
1011 		    case RowStatus_notInService:
1012 			if (bridge_set_if_up(bif->bif_name, 1) < 0)
1013 			    return (SNMP_ERR_GENERR);
1014 			return (SNMP_ERR_NOERROR);
1015 		    default:
1016 			break;
1017 		}
1018 		return (SNMP_ERR_INCONS_VALUE);
1019 
1020 	    case RowStatus_notInService:
1021 		if (bif == NULL)
1022 		    return (SNMP_ERR_INCONS_VALUE);
1023 
1024 		ctx->scratch->int1 = bif->if_status;
1025 
1026 		switch (bif->if_status) {
1027 		    case RowStatus_active:
1028 			if (bridge_set_if_up(bif->bif_name, 1) < 0)
1029 			    return (SNMP_ERR_GENERR);
1030 			return (SNMP_ERR_NOERROR);
1031 		    case RowStatus_notInService:
1032 			return (SNMP_ERR_NOERROR);
1033 		    default:
1034 			break;
1035 		}
1036 		return (SNMP_ERR_INCONS_VALUE);
1037 
1038 	    case RowStatus_notReady:
1039 		return (SNMP_ERR_INCONS_VALUE);
1040 
1041 	    case RowStatus_createAndGo:
1042 		if (bif != NULL)
1043 		    return (SNMP_ERR_INCONS_VALUE);
1044 
1045 		ctx->scratch->int1 = RowStatus_destroy;
1046 
1047 		if (bridge_name_index_get(&val->var, sub, bif_name) == NULL)
1048 		    return (SNMP_ERR_BADVALUE);
1049 		if (bridge_if_create(bif_name, 1) < 0)
1050 		    return (SNMP_ERR_GENERR);
1051 		return (SNMP_ERR_NOERROR);
1052 
1053 	    case RowStatus_createAndWait:
1054 		if (bif != NULL)
1055 		    return (SNMP_ERR_INCONS_VALUE);
1056 
1057 		if (bridge_name_index_get(&val->var, sub, bif_name) == NULL)
1058 		    return (SNMP_ERR_BADVALUE);
1059 
1060 		ctx->scratch->int1 = RowStatus_destroy;
1061 
1062 		if (bridge_if_create(bif_name, 0) < 0)
1063 		    return (SNMP_ERR_GENERR);
1064 		return (SNMP_ERR_NOERROR);
1065 
1066 	    case RowStatus_destroy:
1067 		if (bif == NULL)
1068 		    return (SNMP_ERR_NOSUCHNAME);
1069 
1070 		ctx->scratch->int1 = bif->if_status;
1071 		bif->if_status = RowStatus_destroy;
1072 	}
1073 
1074 	return (SNMP_ERR_NOERROR);
1075 }
1076 
1077 static int
1078 bridge_rollback_if_status(struct snmp_context *ctx,
1079 	struct snmp_value *val, uint sub)
1080 {
1081 	struct bridge_if *bif;
1082 
1083 	if ((bif = bridge_if_index_get(&val->var, sub)) == NULL)
1084 		return (SNMP_ERR_GENERR);
1085 
1086 	switch (ctx->scratch->int1) {
1087 		case RowStatus_destroy:
1088 			bridge_if_destroy(bif);
1089 			return (SNMP_ERR_NOERROR);
1090 
1091 		case RowStatus_notInService:
1092 			if (bif->if_status != ctx->scratch->int1)
1093 				bridge_set_if_up(bif->bif_name, 0);
1094 			bif->if_status = RowStatus_notInService;
1095 			return (SNMP_ERR_NOERROR);
1096 
1097 		case RowStatus_active:
1098 			if (bif->if_status != ctx->scratch->int1)
1099 				bridge_set_if_up(bif->bif_name, 1);
1100 			bif->if_status = RowStatus_active;
1101 			return (SNMP_ERR_NOERROR);
1102 	}
1103 
1104 	abort();
1105 }
1106 
1107 static int
1108 bridge_commit_if_status(struct snmp_value *val, uint sub)
1109 {
1110 	struct bridge_if *bif;
1111 
1112 	if ((bif = bridge_if_index_get(&val->var, sub)) == NULL)
1113 		return (SNMP_ERR_GENERR);
1114 
1115 	if (bif->if_status == RowStatus_destroy &&
1116 	    bridge_if_destroy(bif) < 0)
1117 		return (SNMP_ERR_COMMIT_FAILED);
1118 
1119 	return (SNMP_ERR_NOERROR);
1120 }
1121 
1122 int
1123 op_begemot_base_bridge(struct snmp_context *ctx, struct snmp_value *val,
1124 	uint sub, uint iidx __unused, enum snmp_op op)
1125 {
1126 	struct bridge_if *bif;
1127 
1128 	if (time(NULL) - bridge_list_age > bridge_get_data_maxage())
1129 		bridge_update_all_ifs();
1130 
1131 	switch (op) {
1132 	    case SNMP_OP_GET:
1133 		if ((bif = bridge_if_index_get(&val->var, sub)) == NULL)
1134 		    return (SNMP_ERR_NOSUCHNAME);
1135 		goto get;
1136 
1137 	    case SNMP_OP_GETNEXT:
1138 		if ((bif = bridge_if_index_getnext(&val->var, sub)) == NULL)
1139 		    return (SNMP_ERR_NOSUCHNAME);
1140 		bridge_if_index_append(&val->var, sub, bif);
1141 		goto get;
1142 
1143 	    case SNMP_OP_SET:
1144 		switch (val->var.subs[sub - 1]) {
1145 		    case LEAF_begemotBridgeBaseStatus:
1146 			return (bridge_set_if_status(ctx, val, sub));
1147 		    case LEAF_begemotBridgeBaseName:
1148 		    case LEAF_begemotBridgeBaseAddress:
1149 		    case LEAF_begemotBridgeBaseNumPorts:
1150 		    case LEAF_begemotBridgeBaseType:
1151 			return (SNMP_ERR_NOT_WRITEABLE);
1152 		}
1153 		abort();
1154 
1155 	    case SNMP_OP_ROLLBACK:
1156 		return (bridge_rollback_if_status(ctx, val, sub));
1157 
1158 	    case SNMP_OP_COMMIT:
1159 		return (bridge_commit_if_status(val, sub));
1160 	}
1161 	abort();
1162 
1163 get:
1164 	switch (val->var.subs[sub - 1]) {
1165 	    case LEAF_begemotBridgeBaseName:
1166 		return (string_get(val, bif->bif_name, -1));
1167 
1168 	    case LEAF_begemotBridgeBaseAddress:
1169 		return (string_get(val, bif->br_addr.octet, ETHER_ADDR_LEN));
1170 
1171 	    case LEAF_begemotBridgeBaseNumPorts:
1172 		val->v.integer = bif->num_ports;
1173 		return (SNMP_ERR_NOERROR);
1174 
1175 	    case LEAF_begemotBridgeBaseType:
1176 		val->v.integer = bif->br_type;
1177 		return (SNMP_ERR_NOERROR);
1178 
1179 	    case LEAF_begemotBridgeBaseStatus:
1180 		val->v.integer = bif->if_status;
1181 		return (SNMP_ERR_NOERROR);
1182 	}
1183 
1184 	abort();
1185 }
1186 
1187 int
1188 op_begemot_stp(struct snmp_context *ctx, struct snmp_value *val,
1189 	uint sub, uint iidx __unused, enum snmp_op op)
1190 {
1191 	struct bridge_if *bif;
1192 
1193 	if (time(NULL) - bridge_list_age > bridge_get_data_maxage())
1194 		bridge_update_all_ifs();
1195 
1196 	switch (op) {
1197 	    case SNMP_OP_GET:
1198 		if ((bif = bridge_if_index_get(&val->var, sub)) == NULL)
1199 		    return (SNMP_ERR_NOSUCHNAME);
1200 		goto get;
1201 
1202 	    case SNMP_OP_GETNEXT:
1203 		if ((bif = bridge_if_index_getnext(&val->var, sub)) == NULL)
1204 		    return (SNMP_ERR_NOSUCHNAME);
1205 		bridge_if_index_append(&val->var, sub, bif);
1206 		goto get;
1207 
1208 	    case SNMP_OP_SET:
1209 		if ((bif = bridge_if_index_get(&val->var, sub)) == NULL)
1210 		    return (SNMP_ERR_NOSUCHNAME);
1211 
1212 		switch (val->var.subs[sub - 1]) {
1213 		    case LEAF_begemotBridgeStpPriority:
1214 			if (val->v.integer > SNMP_BRIDGE_MAX_PRIORITY ||
1215 			    val->v.integer % 4096 != 0)
1216 			    return (SNMP_ERR_WRONG_VALUE);
1217 
1218 			ctx->scratch->int1 = bif->priority;
1219 			if (bridge_set_priority(bif, val->v.integer) < 0)
1220 			    return (SNMP_ERR_GENERR);
1221 			return (SNMP_ERR_NOERROR);
1222 
1223 		    case LEAF_begemotBridgeStpBridgeMaxAge:
1224 			if (val->v.integer < SNMP_BRIDGE_MIN_MAGE ||
1225 			    val->v.integer > SNMP_BRIDGE_MAX_MAGE)
1226 			    return (SNMP_ERR_WRONG_VALUE);
1227 
1228 			ctx->scratch->int1 = bif->bridge_max_age;
1229 			if (bridge_set_maxage(bif, val->v.integer) < 0)
1230 			    return (SNMP_ERR_GENERR);
1231 			return (SNMP_ERR_NOERROR);
1232 
1233 		    case LEAF_begemotBridgeStpBridgeHelloTime:
1234 			if (val->v.integer < SNMP_BRIDGE_MIN_HTIME ||
1235 			    val->v.integer > SNMP_BRIDGE_MAX_HTIME)
1236 			    return (SNMP_ERR_WRONG_VALUE);
1237 
1238 			ctx->scratch->int1 = bif->bridge_hello_time;
1239 			if (bridge_set_hello_time(bif, val->v.integer) < 0)
1240 			    return (SNMP_ERR_GENERR);
1241 			return (SNMP_ERR_NOERROR);
1242 
1243 		    case LEAF_begemotBridgeStpBridgeForwardDelay:
1244 			if (val->v.integer < SNMP_BRIDGE_MIN_FDELAY ||
1245 			    val->v.integer > SNMP_BRIDGE_MAX_FDELAY)
1246 			    return (SNMP_ERR_WRONG_VALUE);
1247 
1248 			ctx->scratch->int1 = bif->bridge_fwd_delay;
1249 			if (bridge_set_forward_delay(bif, val->v.integer) < 0)
1250 			    return (SNMP_ERR_GENERR);
1251 			return (SNMP_ERR_NOERROR);
1252 
1253 		    case LEAF_begemotBridgeStpVersion:
1254 			if (val->v.integer !=
1255 			    begemotBridgeStpVersion_stpCompatible &&
1256 			    val->v.integer != begemotBridgeStpVersion_rstp)
1257 			    return (SNMP_ERR_WRONG_VALUE);
1258 
1259 			ctx->scratch->int1 = bif->stp_version;
1260 			if (bridge_set_stp_version(bif, val->v.integer) < 0)
1261 			    return (SNMP_ERR_GENERR);
1262 			return (SNMP_ERR_NOERROR);
1263 
1264 		    case LEAF_begemotBridgeStpTxHoldCount:
1265 			if (val->v.integer < SNMP_BRIDGE_MIN_TXHC ||
1266 			    val->v.integer > SNMP_BRIDGE_MAX_TXHC)
1267 			    return (SNMP_ERR_WRONG_VALUE);
1268 
1269 			ctx->scratch->int1 = bif->tx_hold_count;
1270 			if (bridge_set_tx_hold_count(bif, val->v.integer) < 0)
1271 			    return (SNMP_ERR_GENERR);
1272 			return (SNMP_ERR_NOERROR);
1273 
1274 		    case LEAF_begemotBridgeStpProtocolSpecification:
1275 		    case LEAF_begemotBridgeStpTimeSinceTopologyChange:
1276 		    case LEAF_begemotBridgeStpTopChanges:
1277 		    case LEAF_begemotBridgeStpDesignatedRoot:
1278 		    case LEAF_begemotBridgeStpRootCost:
1279 		    case LEAF_begemotBridgeStpRootPort:
1280 		    case LEAF_begemotBridgeStpMaxAge:
1281 		    case LEAF_begemotBridgeStpHelloTime:
1282 		    case LEAF_begemotBridgeStpHoldTime:
1283 		    case LEAF_begemotBridgeStpForwardDelay:
1284 			return (SNMP_ERR_NOT_WRITEABLE);
1285 		}
1286 		abort();
1287 
1288 	    case SNMP_OP_ROLLBACK:
1289 		if ((bif = bridge_if_index_get(&val->var, sub)) == NULL)
1290 		    return (SNMP_ERR_NOSUCHNAME);
1291 
1292 		switch (val->var.subs[sub - 1]) {
1293 		    case LEAF_begemotBridgeStpPriority:
1294 			bridge_set_priority(bif, ctx->scratch->int1);
1295 			break;
1296 
1297 		    case LEAF_begemotBridgeStpBridgeMaxAge:
1298 			bridge_set_maxage(bif, ctx->scratch->int1);
1299 			break;
1300 
1301 		    case LEAF_begemotBridgeStpBridgeHelloTime:
1302 			bridge_set_hello_time(bif, ctx->scratch->int1);
1303 			break;
1304 
1305 		    case LEAF_begemotBridgeStpBridgeForwardDelay:
1306 			bridge_set_forward_delay(bif, ctx->scratch->int1);
1307 			break;
1308 
1309 		    case LEAF_begemotBridgeStpVersion:
1310 			bridge_set_stp_version(bif, ctx->scratch->int1);
1311 			break;
1312 
1313 		    case LEAF_begemotBridgeStpTxHoldCount:
1314 			bridge_set_tx_hold_count(bif, ctx->scratch->int1);
1315 			break;
1316 		}
1317 		return (SNMP_ERR_NOERROR);
1318 
1319 	    case SNMP_OP_COMMIT:
1320 		return (SNMP_ERR_NOERROR);
1321 	}
1322 	abort();
1323 
1324 get:
1325 	switch (val->var.subs[sub - 1]) {
1326 	    case LEAF_begemotBridgeStpProtocolSpecification:
1327 		val->v.integer = bif->prot_spec;
1328 		return (SNMP_ERR_NOERROR);
1329 
1330 	    case LEAF_begemotBridgeStpPriority:
1331 		val->v.integer = bif->priority;
1332 		return (SNMP_ERR_NOERROR);
1333 
1334 	    case LEAF_begemotBridgeStpTimeSinceTopologyChange:
1335 		if (bridge_get_time_since_tc(bif, &(val->v.uint32)) < 0)
1336 		    return (SNMP_ERR_GENERR);
1337 		return (SNMP_ERR_NOERROR);
1338 
1339 	    case LEAF_begemotBridgeStpTopChanges:
1340 		val->v.uint32 = bif->top_changes;
1341 		return (SNMP_ERR_NOERROR);
1342 
1343 	    case LEAF_begemotBridgeStpDesignatedRoot:
1344 		return (string_get(val, bif->design_root, SNMP_BRIDGE_ID_LEN));
1345 
1346 	    case LEAF_begemotBridgeStpRootCost:
1347 		val->v.integer = bif->root_cost;
1348 		return (SNMP_ERR_NOERROR);
1349 
1350 	    case LEAF_begemotBridgeStpRootPort:
1351 		val->v.integer = bif->root_port;
1352 		return (SNMP_ERR_NOERROR);
1353 
1354 	    case LEAF_begemotBridgeStpMaxAge:
1355 		val->v.integer = bif->max_age;
1356 		return (SNMP_ERR_NOERROR);
1357 
1358 	    case LEAF_begemotBridgeStpHelloTime:
1359 		val->v.integer = bif->hello_time;
1360 		return (SNMP_ERR_NOERROR);
1361 
1362 	    case LEAF_begemotBridgeStpHoldTime:
1363 		val->v.integer = bif->hold_time;
1364 		return (SNMP_ERR_NOERROR);
1365 
1366 	    case LEAF_begemotBridgeStpForwardDelay:
1367 		val->v.integer = bif->fwd_delay;
1368 		return (SNMP_ERR_NOERROR);
1369 
1370 	    case LEAF_begemotBridgeStpBridgeMaxAge:
1371 		val->v.integer = bif->bridge_max_age;
1372 		return (SNMP_ERR_NOERROR);
1373 
1374 	    case LEAF_begemotBridgeStpBridgeHelloTime:
1375 		val->v.integer = bif->bridge_hello_time;
1376 		return (SNMP_ERR_NOERROR);
1377 
1378 	    case LEAF_begemotBridgeStpBridgeForwardDelay:
1379 		val->v.integer = bif->bridge_fwd_delay;
1380 		return (SNMP_ERR_NOERROR);
1381 
1382 	    case LEAF_begemotBridgeStpVersion:
1383 		val->v.integer = bif->stp_version;
1384 		return (SNMP_ERR_NOERROR);
1385 
1386 	    case LEAF_begemotBridgeStpTxHoldCount:
1387 		val->v.integer = bif->tx_hold_count;
1388 		return (SNMP_ERR_NOERROR);
1389 	}
1390 
1391 	abort();
1392 }
1393 
1394 int
1395 op_begemot_tp(struct snmp_context *ctx, struct snmp_value *val,
1396 	uint sub, uint iidx __unused, enum snmp_op op)
1397 {
1398 	struct bridge_if *bif;
1399 
1400 	if (time(NULL) - bridge_list_age > bridge_get_data_maxage())
1401 		bridge_update_all_ifs();
1402 
1403 	switch (op) {
1404 	    case SNMP_OP_GET:
1405 		if ((bif = bridge_if_index_get(&val->var, sub)) == NULL)
1406 		    return (SNMP_ERR_NOSUCHNAME);
1407 		goto get;
1408 
1409 	    case SNMP_OP_GETNEXT:
1410 		if ((bif = bridge_if_index_getnext(&val->var, sub)) == NULL)
1411 		    return (SNMP_ERR_NOSUCHNAME);
1412 		bridge_if_index_append(&val->var, sub, bif);
1413 		goto get;
1414 
1415 	    case SNMP_OP_SET:
1416 		if ((bif = bridge_if_index_get(&val->var, sub)) == NULL)
1417 		    return (SNMP_ERR_NOSUCHNAME);
1418 
1419 		switch (val->var.subs[sub - 1]) {
1420 		    case LEAF_begemotBridgeTpAgingTime:
1421 			if (val->v.integer < SNMP_BRIDGE_MIN_AGE_TIME ||
1422 			    val->v.integer > SNMP_BRIDGE_MAX_AGE_TIME)
1423 			    return (SNMP_ERR_WRONG_VALUE);
1424 
1425 			ctx->scratch->int1 = bif->age_time;
1426 			if (bridge_set_aging_time(bif, val->v.integer) < 0)
1427 			    return (SNMP_ERR_GENERR);
1428 			return (SNMP_ERR_NOERROR);
1429 
1430 		    case LEAF_begemotBridgeTpMaxAddresses:
1431 			ctx->scratch->int1 = bif->max_addrs;
1432 			if (bridge_set_max_cache(bif, val->v.integer) < 0)
1433 			    return (SNMP_ERR_GENERR);
1434 			return (SNMP_ERR_NOERROR);
1435 
1436 		    case LEAF_begemotBridgeTpLearnedEntryDiscards:
1437 			return (SNMP_ERR_NOT_WRITEABLE);
1438 		}
1439 		abort();
1440 
1441 	    case SNMP_OP_ROLLBACK:
1442 		if ((bif = bridge_if_index_get(&val->var, sub)) == NULL)
1443 		    return (SNMP_ERR_GENERR);
1444 
1445 		switch (val->var.subs[sub - 1]) {
1446 		    case LEAF_begemotBridgeTpAgingTime:
1447 			bridge_set_aging_time(bif, ctx->scratch->int1);
1448 			break;
1449 
1450 		    case LEAF_begemotBridgeTpMaxAddresses:
1451 			bridge_set_max_cache(bif, ctx->scratch->int1);
1452 			break;
1453 		}
1454 		return (SNMP_ERR_NOERROR);
1455 
1456 	    case SNMP_OP_COMMIT:
1457 		return (SNMP_ERR_NOERROR);
1458 	}
1459 	abort();
1460 
1461 get:
1462 	switch (val->var.subs[sub - 1]) {
1463 	    case LEAF_begemotBridgeTpLearnedEntryDiscards:
1464 		val->v.uint32 = bif->lrnt_drops;
1465 		return (SNMP_ERR_NOERROR);
1466 
1467 	    case LEAF_begemotBridgeTpAgingTime:
1468 		val->v.integer = bif->age_time;
1469 		return (SNMP_ERR_NOERROR);
1470 
1471 	    case LEAF_begemotBridgeTpMaxAddresses:
1472 		val->v.integer = bif->max_addrs;
1473 		return (SNMP_ERR_NOERROR);
1474 	}
1475 
1476 	abort();
1477 }
1478