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
55static const struct asn_oid oid_newRoot = OIDX_newRoot;
56static const struct asn_oid oid_TopologyChange = OIDX_topologyChange;
57static const struct asn_oid oid_begemotBrigeName = \
58			OIDX_begemotBridgeBaseName;
59static const struct asn_oid oid_begemotNewRoot = OIDX_begemotBridgeNewRoot;
60static const struct asn_oid oid_begemotTopologyChange = \
61			OIDX_begemotBridgeTopologyChange;
62
63TAILQ_HEAD(bridge_ifs, bridge_if);
64
65/*
66 * Free the bridge interface list.
67 */
68static void
69bridge_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 */
83static void
84bridge_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. */
103static struct bridge_ifs bridge_ifs = TAILQ_HEAD_INITIALIZER(bridge_ifs);
104static time_t bridge_list_age;
105
106/*
107 * Free the global list.
108 */
109void
110bridge_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 */
118struct bridge_if *
119bridge_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 */
133struct bridge_if *
134bridge_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 */
148const char *
149bridge_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 */
169int
170bridge_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 */
199struct bridge_if *
200bridge_first_bif(void)
201{
202	return (TAILQ_FIRST(&bridge_ifs));
203}
204
205/*
206 * Fetch the next bridge interface from the list.
207 */
208struct bridge_if *
209bridge_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 */
218static struct bridge_if *
219bridge_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 */
246void
247bridge_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 */
260static struct snmp_value*
261bridge_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 */
287static void
288bridge_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 */
306static void
307bridge_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
322static int
323bridge_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
338static int
339bridge_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 */
352static int
353bridge_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 */
383static int
384bridge_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		    ETHER_ADDR_LEN);
404
405	if (ifp->mib.ifmd_flags & IFF_RUNNING)
406		bif->if_status = RowStatus_active;
407	else
408		bif->if_status = RowStatus_notInService;
409
410	switch (bridge_getinfo_bif(bif)) {
411		case 2:
412			bridge_new_root(bif);
413			break;
414		case 1:
415			bridge_top_change(bif);
416			break;
417		case -1:
418			bridge_remove_bif(bif);
419			return (-1);
420		default:
421			break;
422	}
423
424	/*
425	 * The number of ports is accessible via SNMP -
426	 * update the ports each time the bridge interface data
427	 * is refreshed too.
428	 */
429	bif->num_ports = bridge_update_memif(bif);
430	bif->entry_age = time(NULL);
431
432	return (1);
433}
434
435/*
436 * Update all bridge interfaces' ports only -
437 * make sure each bridge interface exists first.
438 */
439void
440bridge_update_all_ports(void)
441{
442	struct mibif *ifp;
443	struct bridge_if *bif, *t_bif;
444
445	for (bif = bridge_first_bif(); bif != NULL; bif = t_bif) {
446		t_bif = bridge_next_bif(bif);
447
448		for (ifp = mib_first_if(); ifp != NULL;
449		    ifp = mib_next_if(ifp))
450			if (strcmp(ifp->name, bif->bif_name) == 0)
451				break;
452
453		if (ifp != NULL)
454			bif->num_ports = bridge_update_memif(bif);
455		else  /* Ops, we do not exist anymore. */
456			bridge_remove_bif(bif);
457	}
458
459	bridge_ports_update_listage();
460}
461
462/*
463 * Update all addresses only.
464 */
465void
466bridge_update_all_addrs(void)
467{
468	struct mibif *ifp;
469	struct bridge_if *bif, *t_bif;
470
471	for (bif = bridge_first_bif(); bif != NULL; bif = t_bif) {
472		t_bif = bridge_next_bif(bif);
473
474		for (ifp = mib_first_if(); ifp != NULL;
475		    ifp = mib_next_if(ifp))
476			if (strcmp(ifp->name, bif->bif_name) == 0)
477				break;
478
479		if (ifp != NULL)
480			bif->num_addrs = bridge_update_addrs(bif);
481		else  /* Ops, we don't exist anymore. */
482			bridge_remove_bif(bif);
483	}
484
485	bridge_addrs_update_listage();
486}
487
488/*
489 * Update only the bridge interfaces' data - skip addresses.
490 */
491void
492bridge_update_all_ifs(void)
493{
494	struct bridge_if *bif, *t_bif;
495
496	for (bif = bridge_first_bif(); bif != NULL; bif = t_bif) {
497		t_bif = bridge_next_bif(bif);
498		bridge_update_bif(bif);
499	}
500
501	bridge_ports_update_listage();
502	bridge_list_age = time(NULL);
503}
504
505/*
506 * Update all info we have for all bridges.
507 */
508void
509bridge_update_all(void *arg __unused)
510{
511	struct bridge_if *bif, *t_bif;
512
513	for (bif = bridge_first_bif(); bif != NULL; bif = t_bif) {
514		t_bif = bridge_next_bif(bif);
515		if (bridge_update_bif(bif) <= 0)
516			continue;
517
518		/* Update our learnt addresses. */
519		bif->num_addrs = bridge_update_addrs(bif);
520	}
521
522	bridge_list_age = time(NULL);
523	bridge_ports_update_listage();
524	bridge_addrs_update_listage();
525}
526
527/*
528 * Callback for polling our last topology change time -
529 * check whether we are root or whether a TC was detected once every
530 * 30 seconds, so that we can send the newRoot and TopologyChange traps
531 * on time. The rest of the data is polled only once every 5 min.
532 */
533void
534bridge_update_tc_time(void *arg __unused)
535{
536	struct bridge_if *bif;
537	struct mibif *ifp;
538
539	TAILQ_FOREACH(bif, &bridge_ifs, b_if) {
540		/* Walk through the mibII interface list. */
541		for (ifp = mib_first_if(); ifp != NULL; ifp = mib_next_if(ifp))
542			if (strcmp(ifp->name, bif->bif_name) == 0)
543				break;
544
545		if (ifp == NULL) {
546			bridge_remove_bif(bif);
547			continue;
548		}
549
550		switch (bridge_get_op_param(bif)) {
551			case 2:
552				bridge_new_root(bif);
553				break;
554			case 1:
555				bridge_top_change(bif);
556				break;
557		}
558	}
559}
560
561/*
562 * Callback for handling new bridge interface creation.
563 */
564int
565bridge_attach_newif(struct mibif *ifp)
566{
567	u_char mac[ETHER_ADDR_LEN];
568	struct bridge_if *bif;
569
570	if (ifp->mib.ifmd_data.ifi_type != IFT_BRIDGE)
571		return (0);
572
573	/* Make sure it does not exist in our list. */
574	TAILQ_FOREACH(bif, &bridge_ifs, b_if)
575		if(strcmp(bif->bif_name, ifp->name) == 0) {
576			syslog(LOG_ERR, "bridge interface %s already "
577			    "in list", bif->bif_name);
578			return (-1);
579		}
580
581	if (ifp->physaddr == NULL) {
582		if (bridge_get_basemac(ifp->name, mac, sizeof(mac)) == NULL) {
583			syslog(LOG_ERR, "bridge attach new %s failed - "
584			    "no bridge mac address", ifp->name);
585			return (-1);
586		}
587	} else
588		bcopy(ifp->physaddr, &mac, sizeof(mac));
589
590	if ((bif = bridge_new_bif(ifp->name, ifp->sysindex, mac)) == NULL)
591		return (-1);
592
593	if (ifp->mib.ifmd_flags & IFF_RUNNING)
594		bif->if_status = RowStatus_active;
595	else
596		bif->if_status = RowStatus_notInService;
597
598	/* Skip sending notifications if the interface was just created. */
599	if (bridge_getinfo_bif(bif) < 0 ||
600	    (bif->num_ports = bridge_getinfo_bif_ports(bif)) < 0 ||
601	    (bif->num_addrs = bridge_getinfo_bif_addrs(bif)) < 0) {
602		bridge_remove_bif(bif);
603		return (-1);
604	}
605
606	/* Check whether we are the default bridge interface. */
607	if (strcmp(ifp->name, bridge_get_default_name()) == 0)
608		bridge_set_default(bif);
609
610	return (0);
611}
612
613void
614bridge_ifs_dump(void)
615{
616	struct bridge_if *bif;
617
618	for (bif = bridge_first_bif(); bif != NULL;
619		bif = bridge_next_bif(bif)) {
620		syslog(LOG_ERR, "Bridge %s, index - %d", bif->bif_name,
621		    bif->sysindex);
622		bridge_ports_dump(bif);
623		bridge_addrs_dump(bif);
624	}
625}
626
627/*
628 * RFC4188 specifics.
629 */
630int
631op_dot1d_base(struct snmp_context *ctx __unused, struct snmp_value *value,
632	uint sub, uint iidx __unused, enum snmp_op op)
633{
634	struct bridge_if *bif;
635
636	if ((bif = bridge_get_default()) == NULL)
637		return (SNMP_ERR_NOSUCHNAME);
638
639	if (time(NULL) - bif->entry_age > bridge_get_data_maxage() &&
640	    bridge_update_bif(bif) <= 0) /* It was just deleted. */
641		return (SNMP_ERR_NOSUCHNAME);
642
643	switch (op) {
644	    case SNMP_OP_GET:
645		switch (value->var.subs[sub - 1]) {
646		    case LEAF_dot1dBaseBridgeAddress:
647			return (string_get(value, bif->br_addr.octet,
648			    ETHER_ADDR_LEN));
649		    case LEAF_dot1dBaseNumPorts:
650			value->v.integer = bif->num_ports;
651			return (SNMP_ERR_NOERROR);
652		    case LEAF_dot1dBaseType:
653			value->v.integer = bif->br_type;
654			return (SNMP_ERR_NOERROR);
655		}
656		abort();
657
658		case SNMP_OP_SET:
659		    return (SNMP_ERR_NOT_WRITEABLE);
660
661		case SNMP_OP_GETNEXT:
662		case SNMP_OP_ROLLBACK:
663		case SNMP_OP_COMMIT:
664		   break;
665	}
666
667	abort();
668}
669
670int
671op_dot1d_stp(struct snmp_context *ctx, struct snmp_value *val, uint sub,
672    uint iidx __unused, enum snmp_op op)
673{
674	struct bridge_if *bif;
675
676	if ((bif = bridge_get_default()) == NULL)
677		return (SNMP_ERR_NOSUCHNAME);
678
679	if (time(NULL) - bif->entry_age > bridge_get_data_maxage() &&
680	    bridge_update_bif(bif) <= 0) /* It was just deleted. */
681		return (SNMP_ERR_NOSUCHNAME);
682
683	switch (op) {
684	    case SNMP_OP_GET:
685		switch (val->var.subs[sub - 1]) {
686		    case LEAF_dot1dStpProtocolSpecification:
687			val->v.integer = bif->prot_spec;
688			return (SNMP_ERR_NOERROR);
689
690		    case LEAF_dot1dStpPriority:
691			val->v.integer = bif->priority;
692			return (SNMP_ERR_NOERROR);
693
694		    case LEAF_dot1dStpTimeSinceTopologyChange:
695			if (bridge_get_time_since_tc(bif,
696			    &(val->v.uint32)) < 0)
697				return (SNMP_ERR_GENERR);
698			return (SNMP_ERR_NOERROR);
699
700		    case LEAF_dot1dStpTopChanges:
701			val->v.uint32 = bif->top_changes;
702			return (SNMP_ERR_NOERROR);
703
704		    case LEAF_dot1dStpDesignatedRoot:
705			return (string_get(val, bif->design_root,
706			    SNMP_BRIDGE_ID_LEN));
707
708		    case LEAF_dot1dStpRootCost:
709			val->v.integer = bif->root_cost;
710			return (SNMP_ERR_NOERROR);
711
712		    case LEAF_dot1dStpRootPort:
713			val->v.integer = bif->root_port;
714			return (SNMP_ERR_NOERROR);
715
716		    case LEAF_dot1dStpMaxAge:
717			val->v.integer = bif->max_age;
718			return (SNMP_ERR_NOERROR);
719
720		    case LEAF_dot1dStpHelloTime:
721			val->v.integer = bif->hello_time;
722			return (SNMP_ERR_NOERROR);
723
724		    case LEAF_dot1dStpHoldTime:
725			val->v.integer = bif->hold_time;
726			return (SNMP_ERR_NOERROR);
727
728		    case LEAF_dot1dStpForwardDelay:
729			val->v.integer = bif->fwd_delay;
730			return (SNMP_ERR_NOERROR);
731
732		    case LEAF_dot1dStpBridgeMaxAge:
733			val->v.integer = bif->bridge_max_age;
734			return (SNMP_ERR_NOERROR);
735
736		    case LEAF_dot1dStpBridgeHelloTime:
737			val->v.integer = bif->bridge_hello_time;
738			return (SNMP_ERR_NOERROR);
739
740		    case LEAF_dot1dStpBridgeForwardDelay:
741			val->v.integer = bif->bridge_fwd_delay;
742			return (SNMP_ERR_NOERROR);
743
744		    case LEAF_dot1dStpVersion:
745			val->v.integer = bif->stp_version;
746			return (SNMP_ERR_NOERROR);
747
748		    case LEAF_dot1dStpTxHoldCount:
749			val->v.integer = bif->tx_hold_count;
750			return (SNMP_ERR_NOERROR);
751		}
752		abort();
753
754	    case SNMP_OP_GETNEXT:
755		abort();
756
757	    case SNMP_OP_SET:
758		switch (val->var.subs[sub - 1]) {
759		    case LEAF_dot1dStpPriority:
760			if (val->v.integer > SNMP_BRIDGE_MAX_PRIORITY ||
761			    val->v.integer % 4096 != 0)
762			    return (SNMP_ERR_WRONG_VALUE);
763
764			ctx->scratch->int1 = bif->priority;
765			if (bridge_set_priority(bif, val->v.integer) < 0)
766			    return (SNMP_ERR_GENERR);
767			return (SNMP_ERR_NOERROR);
768
769		    case LEAF_dot1dStpBridgeMaxAge:
770			if (val->v.integer < SNMP_BRIDGE_MIN_MAGE ||
771			    val->v.integer > SNMP_BRIDGE_MAX_MAGE)
772			    return (SNMP_ERR_WRONG_VALUE);
773
774			ctx->scratch->int1 = bif->bridge_max_age;
775			if (bridge_set_maxage(bif, val->v.integer) < 0)
776			    return (SNMP_ERR_GENERR);
777			return (SNMP_ERR_NOERROR);
778
779		    case LEAF_dot1dStpBridgeHelloTime:
780			if (val->v.integer < SNMP_BRIDGE_MIN_HTIME ||
781			    val->v.integer > SNMP_BRIDGE_MAX_HTIME)
782			    return (SNMP_ERR_WRONG_VALUE);
783
784			ctx->scratch->int1 = bif->bridge_hello_time;
785			if (bridge_set_hello_time(bif, val->v.integer) < 0)
786			    return (SNMP_ERR_GENERR);
787			return (SNMP_ERR_NOERROR);
788
789		    case LEAF_dot1dStpBridgeForwardDelay:
790			if (val->v.integer < SNMP_BRIDGE_MIN_FDELAY ||
791			    val->v.integer > SNMP_BRIDGE_MAX_FDELAY)
792			    return (SNMP_ERR_WRONG_VALUE);
793
794			ctx->scratch->int1 = bif->bridge_fwd_delay;
795			if (bridge_set_forward_delay(bif, val->v.integer) < 0)
796			    return (SNMP_ERR_GENERR);
797			return (SNMP_ERR_NOERROR);
798
799		    case LEAF_dot1dStpVersion:
800			if (val->v.integer != dot1dStpVersion_stpCompatible &&
801			    val->v.integer != dot1dStpVersion_rstp)
802			    return (SNMP_ERR_WRONG_VALUE);
803
804			ctx->scratch->int1 = bif->stp_version;
805			if (bridge_set_stp_version(bif, val->v.integer) < 0)
806			    return (SNMP_ERR_GENERR);
807			return (SNMP_ERR_NOERROR);
808
809		    case LEAF_dot1dStpTxHoldCount:
810			if (val->v.integer < SNMP_BRIDGE_MIN_TXHC ||
811			    val->v.integer > SNMP_BRIDGE_MAX_TXHC)
812			    return (SNMP_ERR_WRONG_VALUE);
813
814			ctx->scratch->int1 = bif->tx_hold_count;
815			if (bridge_set_tx_hold_count(bif, val->v.integer) < 0)
816			    return (SNMP_ERR_GENERR);
817			return (SNMP_ERR_NOERROR);
818
819		    case LEAF_dot1dStpProtocolSpecification:
820		    case LEAF_dot1dStpTimeSinceTopologyChange:
821		    case LEAF_dot1dStpTopChanges:
822		    case LEAF_dot1dStpDesignatedRoot:
823		    case LEAF_dot1dStpRootCost:
824		    case LEAF_dot1dStpRootPort:
825		    case LEAF_dot1dStpMaxAge:
826		    case LEAF_dot1dStpHelloTime:
827		    case LEAF_dot1dStpHoldTime:
828		    case LEAF_dot1dStpForwardDelay:
829			return (SNMP_ERR_NOT_WRITEABLE);
830		}
831		abort();
832
833	    case SNMP_OP_ROLLBACK:
834		switch (val->var.subs[sub - 1]) {
835		    case LEAF_dot1dStpPriority:
836			bridge_set_priority(bif, ctx->scratch->int1);
837			break;
838		    case LEAF_dot1dStpBridgeMaxAge:
839			bridge_set_maxage(bif, ctx->scratch->int1);
840			break;
841		    case LEAF_dot1dStpBridgeHelloTime:
842			bridge_set_hello_time(bif, ctx->scratch->int1);
843			break;
844		    case LEAF_dot1dStpBridgeForwardDelay:
845			bridge_set_forward_delay(bif, ctx->scratch->int1);
846			break;
847		    case LEAF_dot1dStpVersion:
848			bridge_set_stp_version(bif, ctx->scratch->int1);
849			break;
850		    case LEAF_dot1dStpTxHoldCount:
851			bridge_set_tx_hold_count(bif, ctx->scratch->int1);
852			break;
853		}
854		return (SNMP_ERR_NOERROR);
855
856	    case SNMP_OP_COMMIT:
857		return (SNMP_ERR_NOERROR);
858	}
859
860	abort();
861}
862
863int
864op_dot1d_tp(struct snmp_context *ctx, struct snmp_value *value,
865	uint sub, uint iidx __unused, enum snmp_op op)
866{
867	struct bridge_if *bif;
868
869	if ((bif = bridge_get_default()) == NULL)
870		return (SNMP_ERR_NOSUCHNAME);
871
872	if (time(NULL) - bif->entry_age > bridge_get_data_maxage() &&
873	    bridge_update_bif(bif) <= 0) /* It was just deleted. */
874		return (SNMP_ERR_NOSUCHNAME);
875
876	switch (op) {
877	    case SNMP_OP_GET:
878		switch (value->var.subs[sub - 1]) {
879		    case LEAF_dot1dTpLearnedEntryDiscards:
880			value->v.uint32 = bif->lrnt_drops;
881			return (SNMP_ERR_NOERROR);
882		    case LEAF_dot1dTpAgingTime:
883			value->v.integer = bif->age_time;
884			return (SNMP_ERR_NOERROR);
885		}
886		abort();
887
888	    case SNMP_OP_GETNEXT:
889		abort();
890
891	    case SNMP_OP_SET:
892		switch (value->var.subs[sub - 1]) {
893		    case LEAF_dot1dTpLearnedEntryDiscards:
894			return (SNMP_ERR_NOT_WRITEABLE);
895
896		    case LEAF_dot1dTpAgingTime:
897			if (value->v.integer < SNMP_BRIDGE_MIN_AGE_TIME ||
898			    value->v.integer > SNMP_BRIDGE_MAX_AGE_TIME)
899			    return (SNMP_ERR_WRONG_VALUE);
900
901			ctx->scratch->int1 = bif->age_time;
902			if (bridge_set_aging_time(bif, value->v.integer) < 0)
903			    return (SNMP_ERR_GENERR);
904			return (SNMP_ERR_NOERROR);
905		}
906		abort();
907
908	    case SNMP_OP_ROLLBACK:
909		if (value->var.subs[sub - 1] == LEAF_dot1dTpAgingTime)
910		    bridge_set_aging_time(bif, ctx->scratch->int1);
911		return (SNMP_ERR_NOERROR);
912
913	    case SNMP_OP_COMMIT:
914		return (SNMP_ERR_NOERROR);
915	}
916
917	abort();
918}
919
920/*
921 * Private BEGEMOT-BRIDGE-MIB specifics.
922 */
923
924/*
925 * Get the bridge name from an OID index.
926 */
927static char *
928bridge_name_index_get(const struct asn_oid *oid, uint sub, char *b_name)
929{
930	uint i;
931
932	if (oid->len - sub != oid->subs[sub] + 1 || oid->subs[sub] >= IFNAMSIZ)
933		return (NULL);
934
935	for (i = 0; i < oid->subs[sub]; i++)
936		b_name[i] = oid->subs[sub + i + 1];
937	b_name[i] = '\0';
938
939	return (b_name);
940}
941
942static void
943bridge_if_index_append(struct asn_oid *oid, uint sub,
944	const struct bridge_if *bif)
945{
946	uint i;
947
948	oid->len = sub + strlen(bif->bif_name) + 1;
949	oid->subs[sub] = strlen(bif->bif_name);
950
951	for (i = 1; i <= strlen(bif->bif_name); i++)
952		oid->subs[sub + i] = bif->bif_name[i - 1];
953}
954
955static struct bridge_if *
956bridge_if_index_get(const struct asn_oid *oid, uint sub)
957{
958	uint i;
959	char bif_name[IFNAMSIZ];
960
961	if (oid->len - sub != oid->subs[sub] + 1 || oid->subs[sub] >= IFNAMSIZ)
962		return (NULL);
963
964	for (i = 0; i < oid->subs[sub]; i++)
965		bif_name[i] = oid->subs[sub + i + 1];
966	bif_name[i] = '\0';
967
968	return (bridge_if_find_ifname(bif_name));
969}
970
971static struct bridge_if *
972bridge_if_index_getnext(const struct asn_oid *oid, uint sub)
973{
974	uint i;
975	char bif_name[IFNAMSIZ];
976	struct bridge_if *bif;
977
978	if (oid->len - sub == 0)
979		return (bridge_first_bif());
980
981	if (oid->len - sub != oid->subs[sub] + 1 || oid->subs[sub] >= IFNAMSIZ)
982		return (NULL);
983
984	for (i = 0; i < oid->subs[sub]; i++)
985		bif_name[i] = oid->subs[sub + i + 1];
986	bif_name[i] = '\0';
987
988	if ((bif = bridge_if_find_ifname(bif_name)) == NULL)
989		return (NULL);
990
991	return (bridge_next_bif(bif));
992}
993
994static int
995bridge_set_if_status(struct snmp_context *ctx,
996	struct snmp_value *val, uint sub)
997{
998	struct bridge_if *bif;
999	char bif_name[IFNAMSIZ];
1000
1001	bif = bridge_if_index_get(&val->var, sub);
1002
1003	switch (val->v.integer) {
1004	    case RowStatus_active:
1005		if (bif == NULL)
1006		    return (SNMP_ERR_INCONS_VALUE);
1007
1008		ctx->scratch->int1 = bif->if_status;
1009
1010		switch (bif->if_status) {
1011		    case RowStatus_active:
1012			return (SNMP_ERR_NOERROR);
1013		    case RowStatus_notInService:
1014			if (bridge_set_if_up(bif->bif_name, 1) < 0)
1015			    return (SNMP_ERR_GENERR);
1016			return (SNMP_ERR_NOERROR);
1017		    default:
1018			break;
1019		}
1020		return (SNMP_ERR_INCONS_VALUE);
1021
1022	    case RowStatus_notInService:
1023		if (bif == NULL)
1024		    return (SNMP_ERR_INCONS_VALUE);
1025
1026		ctx->scratch->int1 = bif->if_status;
1027
1028		switch (bif->if_status) {
1029		    case RowStatus_active:
1030			if (bridge_set_if_up(bif->bif_name, 1) < 0)
1031			    return (SNMP_ERR_GENERR);
1032			return (SNMP_ERR_NOERROR);
1033		    case RowStatus_notInService:
1034			return (SNMP_ERR_NOERROR);
1035		    default:
1036			break;
1037		}
1038		return (SNMP_ERR_INCONS_VALUE);
1039
1040	    case RowStatus_notReady:
1041		return (SNMP_ERR_INCONS_VALUE);
1042
1043	    case RowStatus_createAndGo:
1044		if (bif != NULL)
1045		    return (SNMP_ERR_INCONS_VALUE);
1046
1047		ctx->scratch->int1 = RowStatus_destroy;
1048
1049		if (bridge_name_index_get(&val->var, sub, bif_name) == NULL)
1050		    return (SNMP_ERR_BADVALUE);
1051		if (bridge_if_create(bif_name, 1) < 0)
1052		    return (SNMP_ERR_GENERR);
1053		return (SNMP_ERR_NOERROR);
1054
1055	    case RowStatus_createAndWait:
1056		if (bif != NULL)
1057		    return (SNMP_ERR_INCONS_VALUE);
1058
1059		if (bridge_name_index_get(&val->var, sub, bif_name) == NULL)
1060		    return (SNMP_ERR_BADVALUE);
1061
1062		ctx->scratch->int1 = RowStatus_destroy;
1063
1064		if (bridge_if_create(bif_name, 0) < 0)
1065		    return (SNMP_ERR_GENERR);
1066		return (SNMP_ERR_NOERROR);
1067
1068	    case RowStatus_destroy:
1069		if (bif == NULL)
1070		    return (SNMP_ERR_NOSUCHNAME);
1071
1072		ctx->scratch->int1 = bif->if_status;
1073		bif->if_status = RowStatus_destroy;
1074	}
1075
1076	return (SNMP_ERR_NOERROR);
1077}
1078
1079static int
1080bridge_rollback_if_status(struct snmp_context *ctx,
1081	struct snmp_value *val, uint sub)
1082{
1083	struct bridge_if *bif;
1084
1085	if ((bif = bridge_if_index_get(&val->var, sub)) == NULL)
1086		return (SNMP_ERR_GENERR);
1087
1088	switch (ctx->scratch->int1) {
1089		case RowStatus_destroy:
1090			bridge_if_destroy(bif);
1091			return (SNMP_ERR_NOERROR);
1092
1093		case RowStatus_notInService:
1094			if (bif->if_status != ctx->scratch->int1)
1095				bridge_set_if_up(bif->bif_name, 0);
1096			bif->if_status = RowStatus_notInService;
1097			return (SNMP_ERR_NOERROR);
1098
1099		case RowStatus_active:
1100			if (bif->if_status != ctx->scratch->int1)
1101				bridge_set_if_up(bif->bif_name, 1);
1102			bif->if_status = RowStatus_active;
1103			return (SNMP_ERR_NOERROR);
1104	}
1105
1106	abort();
1107}
1108
1109static int
1110bridge_commit_if_status(struct snmp_value *val, uint sub)
1111{
1112	struct bridge_if *bif;
1113
1114	if ((bif = bridge_if_index_get(&val->var, sub)) == NULL)
1115		return (SNMP_ERR_GENERR);
1116
1117	if (bif->if_status == RowStatus_destroy &&
1118	    bridge_if_destroy(bif) < 0)
1119		return (SNMP_ERR_COMMIT_FAILED);
1120
1121	return (SNMP_ERR_NOERROR);
1122}
1123
1124int
1125op_begemot_base_bridge(struct snmp_context *ctx, struct snmp_value *val,
1126	uint sub, uint iidx __unused, enum snmp_op op)
1127{
1128	struct bridge_if *bif;
1129
1130	if (time(NULL) - bridge_list_age > bridge_get_data_maxage())
1131		bridge_update_all_ifs();
1132
1133	switch (op) {
1134	    case SNMP_OP_GET:
1135		if ((bif = bridge_if_index_get(&val->var, sub)) == NULL)
1136		    return (SNMP_ERR_NOSUCHNAME);
1137		goto get;
1138
1139	    case SNMP_OP_GETNEXT:
1140		if ((bif = bridge_if_index_getnext(&val->var, sub)) == NULL)
1141		    return (SNMP_ERR_NOSUCHNAME);
1142		bridge_if_index_append(&val->var, sub, bif);
1143		goto get;
1144
1145	    case SNMP_OP_SET:
1146		switch (val->var.subs[sub - 1]) {
1147		    case LEAF_begemotBridgeBaseStatus:
1148			return (bridge_set_if_status(ctx, val, sub));
1149		    case LEAF_begemotBridgeBaseName:
1150		    case LEAF_begemotBridgeBaseAddress:
1151		    case LEAF_begemotBridgeBaseNumPorts:
1152		    case LEAF_begemotBridgeBaseType:
1153			return (SNMP_ERR_NOT_WRITEABLE);
1154		}
1155		abort();
1156
1157	    case SNMP_OP_ROLLBACK:
1158		return (bridge_rollback_if_status(ctx, val, sub));
1159
1160	    case SNMP_OP_COMMIT:
1161		return (bridge_commit_if_status(val, sub));
1162	}
1163	abort();
1164
1165get:
1166	switch (val->var.subs[sub - 1]) {
1167	    case LEAF_begemotBridgeBaseName:
1168		return (string_get(val, bif->bif_name, -1));
1169
1170	    case LEAF_begemotBridgeBaseAddress:
1171		return (string_get(val, bif->br_addr.octet, ETHER_ADDR_LEN));
1172
1173	    case LEAF_begemotBridgeBaseNumPorts:
1174		val->v.integer = bif->num_ports;
1175		return (SNMP_ERR_NOERROR);
1176
1177	    case LEAF_begemotBridgeBaseType:
1178		val->v.integer = bif->br_type;
1179		return (SNMP_ERR_NOERROR);
1180
1181	    case LEAF_begemotBridgeBaseStatus:
1182		val->v.integer = bif->if_status;
1183		return (SNMP_ERR_NOERROR);
1184	}
1185
1186	abort();
1187}
1188
1189int
1190op_begemot_stp(struct snmp_context *ctx, struct snmp_value *val,
1191	uint sub, uint iidx __unused, enum snmp_op op)
1192{
1193	struct bridge_if *bif;
1194
1195	if (time(NULL) - bridge_list_age > bridge_get_data_maxage())
1196		bridge_update_all_ifs();
1197
1198	switch (op) {
1199	    case SNMP_OP_GET:
1200		if ((bif = bridge_if_index_get(&val->var, sub)) == NULL)
1201		    return (SNMP_ERR_NOSUCHNAME);
1202		goto get;
1203
1204	    case SNMP_OP_GETNEXT:
1205		if ((bif = bridge_if_index_getnext(&val->var, sub)) == NULL)
1206		    return (SNMP_ERR_NOSUCHNAME);
1207		bridge_if_index_append(&val->var, sub, bif);
1208		goto get;
1209
1210	    case SNMP_OP_SET:
1211		if ((bif = bridge_if_index_get(&val->var, sub)) == NULL)
1212		    return (SNMP_ERR_NOSUCHNAME);
1213
1214		switch (val->var.subs[sub - 1]) {
1215		    case LEAF_begemotBridgeStpPriority:
1216			if (val->v.integer > SNMP_BRIDGE_MAX_PRIORITY ||
1217			    val->v.integer % 4096 != 0)
1218			    return (SNMP_ERR_WRONG_VALUE);
1219
1220			ctx->scratch->int1 = bif->priority;
1221			if (bridge_set_priority(bif, val->v.integer) < 0)
1222			    return (SNMP_ERR_GENERR);
1223			return (SNMP_ERR_NOERROR);
1224
1225		    case LEAF_begemotBridgeStpBridgeMaxAge:
1226			if (val->v.integer < SNMP_BRIDGE_MIN_MAGE ||
1227			    val->v.integer > SNMP_BRIDGE_MAX_MAGE)
1228			    return (SNMP_ERR_WRONG_VALUE);
1229
1230			ctx->scratch->int1 = bif->bridge_max_age;
1231			if (bridge_set_maxage(bif, val->v.integer) < 0)
1232			    return (SNMP_ERR_GENERR);
1233			return (SNMP_ERR_NOERROR);
1234
1235		    case LEAF_begemotBridgeStpBridgeHelloTime:
1236			if (val->v.integer < SNMP_BRIDGE_MIN_HTIME ||
1237			    val->v.integer > SNMP_BRIDGE_MAX_HTIME)
1238			    return (SNMP_ERR_WRONG_VALUE);
1239
1240			ctx->scratch->int1 = bif->bridge_hello_time;
1241			if (bridge_set_hello_time(bif, val->v.integer) < 0)
1242			    return (SNMP_ERR_GENERR);
1243			return (SNMP_ERR_NOERROR);
1244
1245		    case LEAF_begemotBridgeStpBridgeForwardDelay:
1246			if (val->v.integer < SNMP_BRIDGE_MIN_FDELAY ||
1247			    val->v.integer > SNMP_BRIDGE_MAX_FDELAY)
1248			    return (SNMP_ERR_WRONG_VALUE);
1249
1250			ctx->scratch->int1 = bif->bridge_fwd_delay;
1251			if (bridge_set_forward_delay(bif, val->v.integer) < 0)
1252			    return (SNMP_ERR_GENERR);
1253			return (SNMP_ERR_NOERROR);
1254
1255		    case LEAF_begemotBridgeStpVersion:
1256			if (val->v.integer !=
1257			    begemotBridgeStpVersion_stpCompatible &&
1258			    val->v.integer != begemotBridgeStpVersion_rstp)
1259			    return (SNMP_ERR_WRONG_VALUE);
1260
1261			ctx->scratch->int1 = bif->stp_version;
1262			if (bridge_set_stp_version(bif, val->v.integer) < 0)
1263			    return (SNMP_ERR_GENERR);
1264			return (SNMP_ERR_NOERROR);
1265
1266		    case LEAF_begemotBridgeStpTxHoldCount:
1267			if (val->v.integer < SNMP_BRIDGE_MIN_TXHC ||
1268			    val->v.integer > SNMP_BRIDGE_MAX_TXHC)
1269			    return (SNMP_ERR_WRONG_VALUE);
1270
1271			ctx->scratch->int1 = bif->tx_hold_count;
1272			if (bridge_set_tx_hold_count(bif, val->v.integer) < 0)
1273			    return (SNMP_ERR_GENERR);
1274			return (SNMP_ERR_NOERROR);
1275
1276		    case LEAF_begemotBridgeStpProtocolSpecification:
1277		    case LEAF_begemotBridgeStpTimeSinceTopologyChange:
1278		    case LEAF_begemotBridgeStpTopChanges:
1279		    case LEAF_begemotBridgeStpDesignatedRoot:
1280		    case LEAF_begemotBridgeStpRootCost:
1281		    case LEAF_begemotBridgeStpRootPort:
1282		    case LEAF_begemotBridgeStpMaxAge:
1283		    case LEAF_begemotBridgeStpHelloTime:
1284		    case LEAF_begemotBridgeStpHoldTime:
1285		    case LEAF_begemotBridgeStpForwardDelay:
1286			return (SNMP_ERR_NOT_WRITEABLE);
1287		}
1288		abort();
1289
1290	    case SNMP_OP_ROLLBACK:
1291		if ((bif = bridge_if_index_get(&val->var, sub)) == NULL)
1292		    return (SNMP_ERR_NOSUCHNAME);
1293
1294		switch (val->var.subs[sub - 1]) {
1295		    case LEAF_begemotBridgeStpPriority:
1296			bridge_set_priority(bif, ctx->scratch->int1);
1297			break;
1298
1299		    case LEAF_begemotBridgeStpBridgeMaxAge:
1300			bridge_set_maxage(bif, ctx->scratch->int1);
1301			break;
1302
1303		    case LEAF_begemotBridgeStpBridgeHelloTime:
1304			bridge_set_hello_time(bif, ctx->scratch->int1);
1305			break;
1306
1307		    case LEAF_begemotBridgeStpBridgeForwardDelay:
1308			bridge_set_forward_delay(bif, ctx->scratch->int1);
1309			break;
1310
1311		    case LEAF_begemotBridgeStpVersion:
1312			bridge_set_stp_version(bif, ctx->scratch->int1);
1313			break;
1314
1315		    case LEAF_begemotBridgeStpTxHoldCount:
1316			bridge_set_tx_hold_count(bif, ctx->scratch->int1);
1317			break;
1318		}
1319		return (SNMP_ERR_NOERROR);
1320
1321	    case SNMP_OP_COMMIT:
1322		return (SNMP_ERR_NOERROR);
1323	}
1324	abort();
1325
1326get:
1327	switch (val->var.subs[sub - 1]) {
1328	    case LEAF_begemotBridgeStpProtocolSpecification:
1329		val->v.integer = bif->prot_spec;
1330		return (SNMP_ERR_NOERROR);
1331
1332	    case LEAF_begemotBridgeStpPriority:
1333		val->v.integer = bif->priority;
1334		return (SNMP_ERR_NOERROR);
1335
1336	    case LEAF_begemotBridgeStpTimeSinceTopologyChange:
1337		if (bridge_get_time_since_tc(bif, &(val->v.uint32)) < 0)
1338		    return (SNMP_ERR_GENERR);
1339		return (SNMP_ERR_NOERROR);
1340
1341	    case LEAF_begemotBridgeStpTopChanges:
1342		val->v.uint32 = bif->top_changes;
1343		return (SNMP_ERR_NOERROR);
1344
1345	    case LEAF_begemotBridgeStpDesignatedRoot:
1346		return (string_get(val, bif->design_root, SNMP_BRIDGE_ID_LEN));
1347
1348	    case LEAF_begemotBridgeStpRootCost:
1349		val->v.integer = bif->root_cost;
1350		return (SNMP_ERR_NOERROR);
1351
1352	    case LEAF_begemotBridgeStpRootPort:
1353		val->v.integer = bif->root_port;
1354		return (SNMP_ERR_NOERROR);
1355
1356	    case LEAF_begemotBridgeStpMaxAge:
1357		val->v.integer = bif->max_age;
1358		return (SNMP_ERR_NOERROR);
1359
1360	    case LEAF_begemotBridgeStpHelloTime:
1361		val->v.integer = bif->hello_time;
1362		return (SNMP_ERR_NOERROR);
1363
1364	    case LEAF_begemotBridgeStpHoldTime:
1365		val->v.integer = bif->hold_time;
1366		return (SNMP_ERR_NOERROR);
1367
1368	    case LEAF_begemotBridgeStpForwardDelay:
1369		val->v.integer = bif->fwd_delay;
1370		return (SNMP_ERR_NOERROR);
1371
1372	    case LEAF_begemotBridgeStpBridgeMaxAge:
1373		val->v.integer = bif->bridge_max_age;
1374		return (SNMP_ERR_NOERROR);
1375
1376	    case LEAF_begemotBridgeStpBridgeHelloTime:
1377		val->v.integer = bif->bridge_hello_time;
1378		return (SNMP_ERR_NOERROR);
1379
1380	    case LEAF_begemotBridgeStpBridgeForwardDelay:
1381		val->v.integer = bif->bridge_fwd_delay;
1382		return (SNMP_ERR_NOERROR);
1383
1384	    case LEAF_begemotBridgeStpVersion:
1385		val->v.integer = bif->stp_version;
1386		return (SNMP_ERR_NOERROR);
1387
1388	    case LEAF_begemotBridgeStpTxHoldCount:
1389		val->v.integer = bif->tx_hold_count;
1390		return (SNMP_ERR_NOERROR);
1391	}
1392
1393	abort();
1394}
1395
1396int
1397op_begemot_tp(struct snmp_context *ctx, struct snmp_value *val,
1398	uint sub, uint iidx __unused, enum snmp_op op)
1399{
1400	struct bridge_if *bif;
1401
1402	if (time(NULL) - bridge_list_age > bridge_get_data_maxage())
1403		bridge_update_all_ifs();
1404
1405	switch (op) {
1406	    case SNMP_OP_GET:
1407		if ((bif = bridge_if_index_get(&val->var, sub)) == NULL)
1408		    return (SNMP_ERR_NOSUCHNAME);
1409		goto get;
1410
1411	    case SNMP_OP_GETNEXT:
1412		if ((bif = bridge_if_index_getnext(&val->var, sub)) == NULL)
1413		    return (SNMP_ERR_NOSUCHNAME);
1414		bridge_if_index_append(&val->var, sub, bif);
1415		goto get;
1416
1417	    case SNMP_OP_SET:
1418		if ((bif = bridge_if_index_get(&val->var, sub)) == NULL)
1419		    return (SNMP_ERR_NOSUCHNAME);
1420
1421		switch (val->var.subs[sub - 1]) {
1422		    case LEAF_begemotBridgeTpAgingTime:
1423			if (val->v.integer < SNMP_BRIDGE_MIN_AGE_TIME ||
1424			    val->v.integer > SNMP_BRIDGE_MAX_AGE_TIME)
1425			    return (SNMP_ERR_WRONG_VALUE);
1426
1427			ctx->scratch->int1 = bif->age_time;
1428			if (bridge_set_aging_time(bif, val->v.integer) < 0)
1429			    return (SNMP_ERR_GENERR);
1430			return (SNMP_ERR_NOERROR);
1431
1432		    case LEAF_begemotBridgeTpMaxAddresses:
1433			ctx->scratch->int1 = bif->max_addrs;
1434			if (bridge_set_max_cache(bif, val->v.integer) < 0)
1435			    return (SNMP_ERR_GENERR);
1436			return (SNMP_ERR_NOERROR);
1437
1438		    case LEAF_begemotBridgeTpLearnedEntryDiscards:
1439			return (SNMP_ERR_NOT_WRITEABLE);
1440		}
1441		abort();
1442
1443	    case SNMP_OP_ROLLBACK:
1444		if ((bif = bridge_if_index_get(&val->var, sub)) == NULL)
1445		    return (SNMP_ERR_GENERR);
1446
1447		switch (val->var.subs[sub - 1]) {
1448		    case LEAF_begemotBridgeTpAgingTime:
1449			bridge_set_aging_time(bif, ctx->scratch->int1);
1450			break;
1451
1452		    case LEAF_begemotBridgeTpMaxAddresses:
1453			bridge_set_max_cache(bif, ctx->scratch->int1);
1454			break;
1455		}
1456		return (SNMP_ERR_NOERROR);
1457
1458	    case SNMP_OP_COMMIT:
1459		return (SNMP_ERR_NOERROR);
1460	}
1461	abort();
1462
1463get:
1464	switch (val->var.subs[sub - 1]) {
1465	    case LEAF_begemotBridgeTpLearnedEntryDiscards:
1466		val->v.uint32 = bif->lrnt_drops;
1467		return (SNMP_ERR_NOERROR);
1468
1469	    case LEAF_begemotBridgeTpAgingTime:
1470		val->v.integer = bif->age_time;
1471		return (SNMP_ERR_NOERROR);
1472
1473	    case LEAF_begemotBridgeTpMaxAddresses:
1474		val->v.integer = bif->max_addrs;
1475		return (SNMP_ERR_NOERROR);
1476	}
1477
1478	abort();
1479}
1480