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