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
56static const struct asn_oid oid_newRoot = OIDX_newRoot;
57static const struct asn_oid oid_TopologyChange = OIDX_topologyChange;
58static const struct asn_oid oid_begemotBrigeName = \
59			OIDX_begemotBridgeBaseName;
60static const struct asn_oid oid_begemotNewRoot = OIDX_begemotBridgeNewRoot;
61static const struct asn_oid oid_begemotTopologyChange = \
62			OIDX_begemotBridgeTopologyChange;
63
64TAILQ_HEAD(bridge_ifs, bridge_if);
65
66/*
67 * Free the bridge interface list.
68 */
69static void
70bridge_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 */
84static void
85bridge_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. */
104static struct bridge_ifs bridge_ifs = TAILQ_HEAD_INITIALIZER(bridge_ifs);
105static time_t bridge_list_age;
106
107/*
108 * Free the global list.
109 */
110void
111bridge_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 */
119struct bridge_if *
120bridge_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 */
134struct bridge_if *
135bridge_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 */
149const char *
150bridge_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 */
170int
171bridge_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 */
200struct bridge_if *
201bridge_first_bif(void)
202{
203	return (TAILQ_FIRST(&bridge_ifs));
204}
205
206/*
207 * Fetch the next bridge interface from the list.
208 */
209struct bridge_if *
210bridge_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 */
219static struct bridge_if *
220bridge_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 */
247void
248bridge_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 */
261static struct snmp_value*
262bridge_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 */
288static void
289bridge_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 */
307static void
308bridge_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
323static int
324bridge_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
339static int
340bridge_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 */
353static int
354bridge_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 */
384static int
385bridge_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 */
440void
441bridge_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 */
466void
467bridge_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 */
492void
493bridge_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 */
509void
510bridge_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 */
534void
535bridge_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 */
565int
566bridge_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
614void
615bridge_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 */
631int
632op_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
671int
672op_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
864int
865op_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 */
928static char *
929bridge_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
943static void
944bridge_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
956static struct bridge_if *
957bridge_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
972static struct bridge_if *
973bridge_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
995static int
996bridge_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
1080static int
1081bridge_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
1110static int
1111bridge_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
1125int
1126op_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
1166get:
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
1190int
1191op_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
1327get:
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
1397int
1398op_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
1464get:
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