xref: /freebsd/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_if.c (revision 5b6598a8c2e22586228ddd5986de34745525da9c)
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 	int ret;
632 	struct bridge_if *bif;
633 
634 	if ((bif = bridge_get_default()) == NULL)
635 		return (SNMP_ERR_NOSUCHNAME);
636 
637 	if (time(NULL) - bif->entry_age > bridge_get_data_maxage() &&
638 	    bridge_update_bif(bif) <= 0) /* It was just deleted. */
639 		return (SNMP_ERR_NOSUCHNAME);
640 
641 	ret = SNMP_ERR_NOERROR;
642 
643 	switch (op) {
644 	    case SNMP_OP_GET:
645 		switch (value->var.subs[sub - 1]) {
646 		    case LEAF_dot1dBaseBridgeAddress:
647 			ret = string_get(value, bif->br_addr.octet,
648 			    ETHER_ADDR_LEN);
649 			break;
650 		    case LEAF_dot1dBaseNumPorts:
651 			value->v.integer = bif->num_ports;
652 			break;
653 		    case LEAF_dot1dBaseType:
654 			value->v.integer = bif->br_type;
655 			break;
656 		    abort();
657 		}
658 		break;
659 
660 		case SNMP_OP_SET:
661 		    ret = SNMP_ERR_NOT_WRITEABLE;
662 		    break;
663 
664 		case SNMP_OP_GETNEXT:
665 		case SNMP_OP_ROLLBACK:
666 		case SNMP_OP_COMMIT:
667 		   abort();
668 	}
669 
670 	return (ret);
671 }
672 
673 int
674 op_dot1d_stp(struct snmp_context *ctx, struct snmp_value *value,
675 	 uint sub, uint iidx __unused, enum snmp_op op)
676 {
677 	int ret;
678 	struct bridge_if *bif;
679 
680 	if ((bif = bridge_get_default()) == NULL)
681 		return (SNMP_ERR_NOSUCHNAME);
682 
683 	if (time(NULL) - bif->entry_age > bridge_get_data_maxage() &&
684 	    bridge_update_bif(bif) <= 0) /* It was just deleted. */
685 		return (SNMP_ERR_NOSUCHNAME);
686 
687 	switch (op) {
688 	    case SNMP_OP_GET:
689 		switch (value->var.subs[sub - 1]) {
690 		    case LEAF_dot1dStpProtocolSpecification:
691 			value->v.integer = bif->prot_spec;
692 			break;
693 
694 		    case LEAF_dot1dStpPriority:
695 			value->v.integer = bif->priority;
696 			break;
697 
698 		    case LEAF_dot1dStpTimeSinceTopologyChange:
699 			if (bridge_get_time_since_tc(bif,
700 			    &(value->v.uint32)) < 0)
701 				return (SNMP_ERR_GENERR);
702 			break;
703 
704 		    case LEAF_dot1dStpTopChanges:
705 			value->v.uint32 = bif->top_changes;
706 			break;
707 
708 		    case LEAF_dot1dStpDesignatedRoot:
709 			return (string_get(value, bif->design_root,
710 			    SNMP_BRIDGE_ID_LEN));
711 
712 		    case LEAF_dot1dStpRootCost:
713 			value->v.integer = bif->root_cost;
714 			break;
715 
716 		    case LEAF_dot1dStpRootPort:
717 			value->v.integer = bif->root_port;
718 			break;
719 
720 		    case LEAF_dot1dStpMaxAge:
721 			value->v.integer = bif->max_age;
722 			break;
723 
724 		    case LEAF_dot1dStpHelloTime:
725 			value->v.integer = bif->hello_time;
726 			break;
727 
728 		    case LEAF_dot1dStpHoldTime:
729 			value->v.integer = bif->hold_time;
730 			break;
731 
732 		    case LEAF_dot1dStpForwardDelay:
733 			value->v.integer = bif->fwd_delay;
734 			break;
735 
736 		    case LEAF_dot1dStpBridgeMaxAge:
737 			value->v.integer = bif->bridge_max_age;
738 			break;
739 
740 		    case LEAF_dot1dStpBridgeHelloTime:
741 			value->v.integer = bif->bridge_hello_time;
742 			break;
743 
744 		    case LEAF_dot1dStpBridgeForwardDelay:
745 			value->v.integer = bif->bridge_fwd_delay;
746 			break;
747 		    case LEAF_dot1dStpVersion:
748 			value->v.integer = bif->stp_version;
749 			break;
750 		    case LEAF_dot1dStpTxHoldCount:
751 			value->v.integer = bif->tx_hold_count;
752 		}
753 
754 		return (SNMP_ERR_NOERROR);
755 
756 	    case SNMP_OP_GETNEXT:
757 		abort();
758 
759 	    case SNMP_OP_SET:
760 		switch (value->var.subs[sub - 1]) {
761 		    case LEAF_dot1dStpPriority:
762 			ctx->scratch->int1 = bif->priority;
763 			ret = bridge_set_priority(bif, value->v.integer);
764 			break;
765 
766 		    case LEAF_dot1dStpBridgeMaxAge:
767 			ctx->scratch->int1 = bif->bridge_max_age;
768 			ret = bridge_set_maxage(bif, value->v.integer);
769 			break;
770 
771 		    case LEAF_dot1dStpBridgeHelloTime:
772 			ctx->scratch->int1 = bif->bridge_hello_time;
773 			ret = bridge_set_hello_time(bif, value->v.integer);
774 			break;
775 
776 		    case LEAF_dot1dStpBridgeForwardDelay:
777 			ctx->scratch->int1 = bif->bridge_fwd_delay;
778 			ret = bridge_set_forward_delay(bif, value->v.integer);
779 			break;
780 
781 		    case LEAF_dot1dStpVersion:
782 			ctx->scratch->int1 = bif->stp_version;
783 			ret = bridge_set_stp_version(bif, value->v.integer);
784 			break;
785 
786 		    case LEAF_dot1dStpTxHoldCount:
787 			ctx->scratch->int1 = bif->tx_hold_count;
788 			ret = bridge_set_tx_hold_count(bif, value->v.integer);
789 			break;
790 
791 		    case LEAF_dot1dStpProtocolSpecification:
792 		    case LEAF_dot1dStpTimeSinceTopologyChange:
793 		    case LEAF_dot1dStpTopChanges:
794 		    case LEAF_dot1dStpDesignatedRoot:
795 		    case LEAF_dot1dStpRootCost:
796 		    case LEAF_dot1dStpRootPort:
797 		    case LEAF_dot1dStpMaxAge:
798 		    case LEAF_dot1dStpHelloTime:
799 		    case LEAF_dot1dStpHoldTime:
800 		    case LEAF_dot1dStpForwardDelay:
801 			return (SNMP_ERR_NOT_WRITEABLE);
802 		    default:
803 			return (SNMP_ERR_NOSUCHNAME);
804 		}
805 
806 		if (ret == -2)
807 			return (SNMP_ERR_WRONG_VALUE);
808 		else if (ret < 0)
809 			return (SNMP_ERR_GENERR);
810 		return (SNMP_ERR_NOERROR);
811 
812 	    case SNMP_OP_ROLLBACK:
813 		switch (value->var.subs[sub - 1]) {
814 		    case LEAF_dot1dStpPriority:
815 			bridge_set_priority(bif, ctx->scratch->int1);
816 			break;
817 		    case LEAF_dot1dStpBridgeMaxAge:
818 			bridge_set_maxage(bif, ctx->scratch->int1);
819 			break;
820 		    case LEAF_dot1dStpBridgeHelloTime:
821 			bridge_set_hello_time(bif, ctx->scratch->int1);
822 			break;
823 		    case LEAF_dot1dStpBridgeForwardDelay:
824 			bridge_set_forward_delay(bif, ctx->scratch->int1);
825 			break;
826 		    case LEAF_dot1dStpVersion:
827 			bridge_set_stp_version(bif, ctx->scratch->int1);
828 			break;
829 		    case LEAF_dot1dStpTxHoldCount:
830 			bridge_set_tx_hold_count(bif, ctx->scratch->int1);
831 			break;
832 		}
833 		return (SNMP_ERR_NOERROR);
834 
835 	    case SNMP_OP_COMMIT:
836 		return (SNMP_ERR_NOERROR);
837 	}
838 
839 	return (SNMP_ERR_NOERROR);
840 }
841 
842 int
843 op_dot1d_tp(struct snmp_context *ctx, struct snmp_value *value,
844 	uint sub, uint iidx __unused, enum snmp_op op)
845 {
846 	struct bridge_if *bif;
847 
848 	if ((bif = bridge_get_default()) == NULL)
849 		return (SNMP_ERR_NOSUCHNAME);
850 
851 	if (time(NULL) - bif->entry_age > bridge_get_data_maxage() &&
852 	    bridge_update_bif(bif) <= 0) /* It was just deleted. */
853 		return (SNMP_ERR_NOSUCHNAME);
854 
855 	switch (op) {
856 	    case SNMP_OP_GET:
857 		switch (value->var.subs[sub - 1]) {
858 		    case LEAF_dot1dTpLearnedEntryDiscards:
859 			value->v.uint32 = bif->lrnt_drops;
860 			break;
861 		    case LEAF_dot1dTpAgingTime:
862 			value->v.integer = bif->age_time;
863 			break;
864 		}
865 		return (SNMP_ERR_NOERROR);
866 
867 	    case SNMP_OP_GETNEXT:
868 		abort();
869 
870 	    case SNMP_OP_SET:
871 		if (value->var.subs[sub - 1] == LEAF_dot1dTpAgingTime) {
872 		    ctx->scratch->int1 = bif->age_time;
873 		    if (bridge_set_aging_time(bif, value->v.integer) < 0)
874 			return (SNMP_ERR_GENERR);
875 		}
876 		return (SNMP_ERR_NOERROR);
877 
878 	    case SNMP_OP_ROLLBACK:
879 		if (value->var.subs[sub - 1] == LEAF_dot1dTpAgingTime)
880 		    bridge_set_aging_time(bif, ctx->scratch->int1);
881 		return (SNMP_ERR_NOERROR);
882 
883 	    case SNMP_OP_COMMIT:
884 		return (SNMP_ERR_NOERROR);
885 	}
886 
887 	return (SNMP_ERR_NOERROR);
888 }
889 
890 /*
891  * Private BEGEMOT-BRIDGE-MIB specifics.
892  */
893 
894 /*
895  * Get the bridge name from an OID index.
896  */
897 static char *
898 bridge_name_index_get(const struct asn_oid *oid, uint sub, char *b_name)
899 {
900 	uint i;
901 
902 	if (oid->len - sub != oid->subs[sub] + 1 || oid->subs[sub] >= IFNAMSIZ)
903 		return (NULL);
904 
905 	for (i = 0; i < oid->subs[sub]; i++)
906 		b_name[i] = oid->subs[sub + i + 1];
907 	b_name[i] = '\0';
908 
909 	return (b_name);
910 }
911 
912 static void
913 bridge_if_index_append(struct asn_oid *oid, uint sub,
914 	const struct bridge_if *bif)
915 {
916 	uint i;
917 
918 	oid->len = sub + strlen(bif->bif_name) + 1;
919 	oid->subs[sub] = strlen(bif->bif_name);
920 
921 	for (i = 1; i <= strlen(bif->bif_name); i++)
922 		oid->subs[sub + i] = bif->bif_name[i - 1];
923 }
924 
925 static struct bridge_if *
926 bridge_if_index_get(const struct asn_oid *oid, uint sub)
927 {
928 	uint i;
929 	char bif_name[IFNAMSIZ];
930 
931 	if (oid->len - sub != oid->subs[sub] + 1 || oid->subs[sub] >= IFNAMSIZ)
932 		return (NULL);
933 
934 	for (i = 0; i < oid->subs[sub]; i++)
935 		bif_name[i] = oid->subs[sub + i + 1];
936 	bif_name[i] = '\0';
937 
938 	return (bridge_if_find_ifname(bif_name));
939 }
940 
941 static struct bridge_if *
942 bridge_if_index_getnext(const struct asn_oid *oid, uint sub)
943 {
944 	uint i;
945 	char bif_name[IFNAMSIZ];
946 	struct bridge_if *bif;
947 
948 	if (oid->len - sub == 0)
949 		return (bridge_first_bif());
950 
951 	if (oid->len - sub != oid->subs[sub] + 1 || oid->subs[sub] >= IFNAMSIZ)
952 		return (NULL);
953 
954 	for (i = 0; i < oid->subs[sub]; i++)
955 		bif_name[i] = oid->subs[sub + i + 1];
956 	bif_name[i] = '\0';
957 
958 	if ((bif = bridge_if_find_ifname(bif_name)) == NULL)
959 		return (NULL);
960 
961 	return (bridge_next_bif(bif));
962 }
963 
964 static int
965 bridge_set_if_status(struct snmp_context *ctx,
966 	struct snmp_value *val, uint sub)
967 {
968 	struct bridge_if *bif;
969 	char bif_name[IFNAMSIZ];
970 
971 	bif = bridge_if_index_get(&val->var, sub);
972 
973 	switch (val->v.integer) {
974 	    case RowStatus_active:
975 		if (bif == NULL)
976 		    return (SNMP_ERR_INCONS_VALUE);
977 
978 		ctx->scratch->int1 = bif->if_status;
979 
980 		switch (bif->if_status) {
981 		    case RowStatus_active:
982 			return (SNMP_ERR_NOERROR);
983 		    case RowStatus_notInService:
984 			if (bridge_set_if_up(bif->bif_name, 1) < 0)
985 			    return (SNMP_ERR_GENERR);
986 			return (SNMP_ERR_NOERROR);
987 		    default:
988 			break;
989 		}
990 		return (SNMP_ERR_INCONS_VALUE);
991 
992 	    case RowStatus_notInService:
993 		if (bif == NULL)
994 		    return (SNMP_ERR_INCONS_VALUE);
995 
996 		ctx->scratch->int1 = bif->if_status;
997 
998 		switch (bif->if_status) {
999 		    case RowStatus_active:
1000 			if (bridge_set_if_up(bif->bif_name, 1) < 0)
1001 			    return (SNMP_ERR_GENERR);
1002 			return (SNMP_ERR_NOERROR);
1003 		    case RowStatus_notInService:
1004 			return (SNMP_ERR_NOERROR);
1005 		    default:
1006 			break;
1007 		}
1008 		return (SNMP_ERR_INCONS_VALUE);
1009 
1010 	    case RowStatus_notReady:
1011 		return (SNMP_ERR_INCONS_VALUE);
1012 
1013 	    case RowStatus_createAndGo:
1014 		if (bif != NULL)
1015 		    return (SNMP_ERR_INCONS_VALUE);
1016 
1017 		ctx->scratch->int1 = RowStatus_destroy;
1018 
1019 		if (bridge_name_index_get(&val->var, sub, bif_name) == NULL)
1020 		    return (SNMP_ERR_BADVALUE);
1021 		if (bridge_if_create(bif_name, 1) < 0)
1022 		    return (SNMP_ERR_GENERR);
1023 		return (SNMP_ERR_NOERROR);
1024 
1025 	    case RowStatus_createAndWait:
1026 		if (bif != NULL)
1027 		    return (SNMP_ERR_INCONS_VALUE);
1028 
1029 		if (bridge_name_index_get(&val->var, sub, bif_name) == NULL)
1030 		    return (SNMP_ERR_BADVALUE);
1031 
1032 		ctx->scratch->int1 = RowStatus_destroy;
1033 
1034 		if (bridge_if_create(bif_name, 0) < 0)
1035 		    return (SNMP_ERR_GENERR);
1036 		return (SNMP_ERR_NOERROR);
1037 
1038 	    case RowStatus_destroy:
1039 		if (bif == NULL)
1040 		    return (SNMP_ERR_NOSUCHNAME);
1041 
1042 		ctx->scratch->int1 = bif->if_status;
1043 		bif->if_status = RowStatus_destroy;
1044 	}
1045 
1046 	return (SNMP_ERR_NOERROR);
1047 }
1048 
1049 static int
1050 bridge_rollback_if_status(struct snmp_context *ctx,
1051 	struct snmp_value *val, uint sub)
1052 {
1053 	struct bridge_if *bif;
1054 
1055 	if ((bif = bridge_if_index_get(&val->var, sub)) == NULL)
1056 		return (SNMP_ERR_GENERR);
1057 
1058 	switch (ctx->scratch->int1) {
1059 		case RowStatus_destroy:
1060 			bridge_if_destroy(bif);
1061 			return (SNMP_ERR_NOERROR);
1062 
1063 		case RowStatus_notInService:
1064 			if (bif->if_status != ctx->scratch->int1)
1065 				bridge_set_if_up(bif->bif_name, 0);
1066 			bif->if_status = RowStatus_notInService;
1067 			return (SNMP_ERR_NOERROR);
1068 
1069 		case RowStatus_active:
1070 			if (bif->if_status != ctx->scratch->int1)
1071 				bridge_set_if_up(bif->bif_name, 1);
1072 			bif->if_status = RowStatus_active;
1073 			return (SNMP_ERR_NOERROR);
1074 	}
1075 
1076 	abort();
1077 }
1078 
1079 static int
1080 bridge_commit_if_status(struct snmp_value *val, uint sub)
1081 {
1082 	struct bridge_if *bif;
1083 
1084 	if ((bif = bridge_if_index_get(&val->var, sub)) == NULL)
1085 		return (SNMP_ERR_GENERR);
1086 
1087 	if (bif->if_status == RowStatus_destroy &&
1088 	    bridge_if_destroy(bif) < 0)
1089 		return (SNMP_ERR_COMMIT_FAILED);
1090 
1091 	return (SNMP_ERR_NOERROR);
1092 }
1093 
1094 int
1095 op_begemot_base_bridge(struct snmp_context *ctx, struct snmp_value *val,
1096 	uint sub, uint iidx __unused, enum snmp_op op)
1097 {
1098 	int ret;
1099 	struct bridge_if *bif;
1100 
1101 	if (time(NULL) - bridge_list_age > bridge_get_data_maxage())
1102 		bridge_update_all_ifs();
1103 
1104 	switch (op) {
1105 	    case SNMP_OP_GET:
1106 		if ((bif = bridge_if_index_get(&val->var, sub)) == NULL)
1107 		    return (SNMP_ERR_NOSUCHNAME);
1108 		break;
1109 
1110 	    case SNMP_OP_GETNEXT:
1111 		if ((bif = bridge_if_index_getnext(&val->var, sub)) == NULL)
1112 		    return (SNMP_ERR_NOSUCHNAME);
1113 		bridge_if_index_append(&val->var, sub, bif);
1114 		break;
1115 
1116 	    case SNMP_OP_SET:
1117 		switch (val->var.subs[sub - 1]) {
1118 		    case LEAF_begemotBridgeBaseStatus:
1119 			return (bridge_set_if_status(ctx, val, sub));
1120 		    case LEAF_begemotBridgeBaseName:
1121 		    case LEAF_begemotBridgeBaseAddress:
1122 		    case LEAF_begemotBridgeBaseNumPorts:
1123 		    case LEAF_begemotBridgeBaseType:
1124 			return (SNMP_ERR_NOT_WRITEABLE);
1125 		}
1126 		abort();
1127 
1128 	    case SNMP_OP_ROLLBACK:
1129 		return (bridge_rollback_if_status(ctx, val, sub));
1130 
1131 	    case SNMP_OP_COMMIT:
1132 		return (bridge_commit_if_status(val, sub));
1133 
1134 	    default:
1135 		abort();
1136 	}
1137 
1138 	ret = SNMP_ERR_NOERROR;
1139 	switch (val->var.subs[sub - 1]) {
1140 	    case LEAF_begemotBridgeBaseName:
1141 		ret = string_get(val, bif->bif_name, -1);
1142 		break;
1143 
1144 	    case LEAF_begemotBridgeBaseAddress:
1145 		ret = string_get(val, bif->br_addr.octet, ETHER_ADDR_LEN);
1146 		break;
1147 
1148 	    case LEAF_begemotBridgeBaseNumPorts:
1149 		val->v.integer = bif->num_ports;
1150 		break;
1151 
1152 	    case LEAF_begemotBridgeBaseType:
1153 		val->v.integer = bif->br_type;
1154 		break;
1155 
1156 	    case LEAF_begemotBridgeBaseStatus:
1157 		val->v.integer = bif->if_status;
1158 		break;
1159 	}
1160 
1161 	return (ret);
1162 }
1163 
1164 int
1165 op_begemot_stp(struct snmp_context *ctx, struct snmp_value *val,
1166 	uint sub, uint iidx __unused, enum snmp_op op)
1167 {
1168 	int ret;
1169 	struct bridge_if *bif;
1170 
1171 	if (time(NULL) - bridge_list_age > bridge_get_data_maxage())
1172 		bridge_update_all_ifs();
1173 
1174 	switch (op) {
1175 	    case SNMP_OP_GET:
1176 		if ((bif = bridge_if_index_get(&val->var, sub)) == NULL)
1177 		    return (SNMP_ERR_NOSUCHNAME);
1178 		break;
1179 
1180 	    case SNMP_OP_GETNEXT:
1181 		if ((bif = bridge_if_index_getnext(&val->var, sub)) == NULL)
1182 		    return (SNMP_ERR_NOSUCHNAME);
1183 		bridge_if_index_append(&val->var, sub, bif);
1184 		break;
1185 
1186 	    case SNMP_OP_SET:
1187 		if ((bif = bridge_if_index_get(&val->var, sub)) == NULL)
1188 		    return (SNMP_ERR_NOSUCHNAME);
1189 
1190 		switch (val->var.subs[sub - 1]) {
1191 		    case LEAF_begemotBridgeStpPriority:
1192 			ctx->scratch->int1 = bif->priority;
1193 			ret = bridge_set_priority(bif, val->v.integer);
1194 			break;
1195 
1196 		    case LEAF_begemotBridgeStpBridgeMaxAge:
1197 			ctx->scratch->int1 = bif->bridge_max_age;
1198 			ret = bridge_set_maxage(bif, val->v.integer);
1199 			break;
1200 
1201 		    case LEAF_begemotBridgeStpBridgeHelloTime:
1202 			ctx->scratch->int1 = bif->bridge_hello_time;
1203 			ret = bridge_set_hello_time(bif, val->v.integer);
1204 			break;
1205 
1206 		    case LEAF_begemotBridgeStpBridgeForwardDelay:
1207 			ctx->scratch->int1 = bif->bridge_fwd_delay;
1208 			ret = bridge_set_forward_delay(bif, val->v.integer);
1209 			break;
1210 
1211 		    case LEAF_begemotBridgeStpVersion:
1212 			ctx->scratch->int1 = bif->stp_version;
1213 			ret = bridge_set_stp_version(bif, val->v.integer);
1214 			break;
1215 
1216 		    case LEAF_begemotBridgeStpTxHoldCount:
1217 			ctx->scratch->int1 = bif->tx_hold_count;
1218 			ret = bridge_set_tx_hold_count(bif, val->v.integer);
1219 			break;
1220 
1221 		    case LEAF_begemotBridgeStpProtocolSpecification:
1222 		    case LEAF_begemotBridgeStpTimeSinceTopologyChange:
1223 		    case LEAF_begemotBridgeStpTopChanges:
1224 		    case LEAF_begemotBridgeStpDesignatedRoot:
1225 		    case LEAF_begemotBridgeStpRootCost:
1226 		    case LEAF_begemotBridgeStpRootPort:
1227 		    case LEAF_begemotBridgeStpMaxAge:
1228 		    case LEAF_begemotBridgeStpHelloTime:
1229 		    case LEAF_begemotBridgeStpHoldTime:
1230 		    case LEAF_begemotBridgeStpForwardDelay:
1231 			return (SNMP_ERR_NOT_WRITEABLE);
1232 
1233 		    default:
1234 			return (SNMP_ERR_NOSUCHNAME);
1235 		}
1236 
1237 		if (ret == 0)
1238 		    return (SNMP_ERR_NOERROR);
1239 		else if (ret == -2)
1240 		    return (SNMP_ERR_WRONG_VALUE);
1241 		return (SNMP_ERR_GENERR);
1242 
1243 	    case SNMP_OP_ROLLBACK:
1244 		if ((bif = bridge_if_index_get(&val->var, sub)) == NULL)
1245 		    return (SNMP_ERR_NOSUCHNAME);
1246 
1247 		switch (val->var.subs[sub - 1]) {
1248 		    case LEAF_begemotBridgeStpPriority:
1249 			bridge_set_priority(bif, ctx->scratch->int1);
1250 			break;
1251 
1252 		    case LEAF_begemotBridgeStpBridgeMaxAge:
1253 			bridge_set_maxage(bif, ctx->scratch->int1);
1254 			break;
1255 
1256 		    case LEAF_begemotBridgeStpBridgeHelloTime:
1257 			bridge_set_hello_time(bif, ctx->scratch->int1);
1258 			break;
1259 
1260 		    case LEAF_begemotBridgeStpBridgeForwardDelay:
1261 			bridge_set_forward_delay(bif, ctx->scratch->int1);
1262 			break;
1263 
1264 		    case LEAF_begemotBridgeStpVersion:
1265 			bridge_set_stp_version(bif, ctx->scratch->int1);
1266 			break;
1267 
1268 		    case LEAF_begemotBridgeStpTxHoldCount:
1269 			bridge_set_tx_hold_count(bif, ctx->scratch->int1);
1270 			break;
1271 		}
1272 		return (SNMP_ERR_NOERROR);
1273 
1274 	    case SNMP_OP_COMMIT:
1275 		return (SNMP_ERR_NOERROR);
1276 
1277 	    default:
1278 		abort();
1279 	}
1280 
1281 	ret = SNMP_ERR_NOERROR;
1282 
1283 	switch (val->var.subs[sub - 1]) {
1284 	    case LEAF_begemotBridgeStpProtocolSpecification:
1285 		val->v.integer = bif->prot_spec;
1286 		break;
1287 
1288 	    case LEAF_begemotBridgeStpPriority:
1289 		val->v.integer = bif->priority;
1290 		break;
1291 
1292 	    case LEAF_begemotBridgeStpTimeSinceTopologyChange:
1293 		if (bridge_get_time_since_tc(bif, &(val->v.uint32)) < 0)
1294 		    return (SNMP_ERR_GENERR);
1295 		break;
1296 
1297 	    case LEAF_begemotBridgeStpTopChanges:
1298 		val->v.uint32 = bif->top_changes;
1299 		break;
1300 
1301 	    case LEAF_begemotBridgeStpDesignatedRoot:
1302 		ret = string_get(val, bif->design_root, SNMP_BRIDGE_ID_LEN);
1303 		break;
1304 
1305 	    case LEAF_begemotBridgeStpRootCost:
1306 		val->v.integer = bif->root_cost;
1307 		break;
1308 
1309 	    case LEAF_begemotBridgeStpRootPort:
1310 		val->v.integer = bif->root_port;
1311 		break;
1312 
1313 	    case LEAF_begemotBridgeStpMaxAge:
1314 		val->v.integer = bif->max_age;
1315 		break;
1316 
1317 	    case LEAF_begemotBridgeStpHelloTime:
1318 		val->v.integer = bif->hello_time;
1319 		break;
1320 
1321 	    case LEAF_begemotBridgeStpHoldTime:
1322 		val->v.integer = bif->hold_time;
1323 		break;
1324 
1325 	    case LEAF_begemotBridgeStpForwardDelay:
1326 		val->v.integer = bif->fwd_delay;
1327 		break;
1328 
1329 	    case LEAF_begemotBridgeStpBridgeMaxAge:
1330 		val->v.integer = bif->bridge_max_age;
1331 		break;
1332 
1333 	    case LEAF_begemotBridgeStpBridgeHelloTime:
1334 		val->v.integer = bif->bridge_hello_time;
1335 		break;
1336 
1337 	    case LEAF_begemotBridgeStpBridgeForwardDelay:
1338 		val->v.integer = bif->bridge_fwd_delay;
1339 		break;
1340 
1341 	    case LEAF_begemotBridgeStpVersion:
1342 		val->v.integer = bif->stp_version;
1343 		break;
1344 
1345 	    case LEAF_begemotBridgeStpTxHoldCount:
1346 		val->v.integer = bif->tx_hold_count;
1347 		break;
1348 	}
1349 
1350 	return (ret);
1351 }
1352 
1353 int
1354 op_begemot_tp(struct snmp_context *ctx, struct snmp_value *val,
1355 	uint sub, uint iidx __unused, enum snmp_op op)
1356 {
1357 	struct bridge_if *bif;
1358 
1359 	if (time(NULL) - bridge_list_age > bridge_get_data_maxage())
1360 		bridge_update_all_ifs();
1361 
1362 	switch (op) {
1363 	    case SNMP_OP_GET:
1364 		if ((bif = bridge_if_index_get(&val->var, sub)) == NULL)
1365 		    return (SNMP_ERR_NOSUCHNAME);
1366 		break;
1367 
1368 	    case SNMP_OP_GETNEXT:
1369 		if ((bif = bridge_if_index_getnext(&val->var, sub)) == NULL)
1370 		    return (SNMP_ERR_NOSUCHNAME);
1371 		bridge_if_index_append(&val->var, sub, bif);
1372 		break;
1373 
1374 	    case SNMP_OP_SET:
1375 		if ((bif = bridge_if_index_get(&val->var, sub)) == NULL)
1376 		    return (SNMP_ERR_NOSUCHNAME);
1377 
1378 		switch (val->var.subs[sub - 1]) {
1379 		    case LEAF_begemotBridgeTpAgingTime:
1380 			ctx->scratch->int1 = bif->age_time;
1381 			if (bridge_set_aging_time(bif, val->v.integer) < 0)
1382 			    return (SNMP_ERR_GENERR);
1383 			return (SNMP_ERR_NOERROR);
1384 
1385 		    case LEAF_begemotBridgeTpMaxAddresses:
1386 			ctx->scratch->int1 = bif->max_addrs;
1387 			if (bridge_set_max_cache(bif, val->v.integer) < 0)
1388 			    return (SNMP_ERR_GENERR);
1389 			return (SNMP_ERR_NOERROR);
1390 
1391 		    case LEAF_begemotBridgeTpLearnedEntryDiscards:
1392 			return (SNMP_ERR_NOT_WRITEABLE);
1393 		}
1394 		abort();
1395 
1396 	    case SNMP_OP_ROLLBACK:
1397 		if ((bif = bridge_if_index_get(&val->var, sub)) == NULL)
1398 		    return (SNMP_ERR_GENERR);
1399 
1400 		switch (val->var.subs[sub - 1]) {
1401 		    case LEAF_begemotBridgeTpAgingTime:
1402 			bridge_set_aging_time(bif, ctx->scratch->int1);
1403 			break;
1404 
1405 		    case LEAF_begemotBridgeTpMaxAddresses:
1406 			bridge_set_max_cache(bif, ctx->scratch->int1);
1407 			break;
1408 		}
1409 		return (SNMP_ERR_NOERROR);
1410 
1411 	    case SNMP_OP_COMMIT:
1412 		return (SNMP_ERR_NOERROR);
1413 
1414 	    default:
1415 		abort();
1416 	}
1417 
1418 	switch (val->var.subs[sub - 1]) {
1419 	    case LEAF_begemotBridgeTpLearnedEntryDiscards:
1420 		val->v.uint32 = bif->lrnt_drops;
1421 		break;
1422 
1423 	    case LEAF_begemotBridgeTpAgingTime:
1424 		val->v.integer = bif->age_time;
1425 		break;
1426 
1427 	    case LEAF_begemotBridgeTpMaxAddresses:
1428 		val->v.integer = bif->max_addrs;
1429 		break;
1430 	}
1431 
1432 	return (SNMP_ERR_NOERROR);
1433 }
1434