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 ports.
30 */
31
32#include <sys/queue.h>
33#include <sys/socket.h>
34#include <sys/types.h>
35
36#include <net/ethernet.h>
37#include <net/if.h>
38#include <net/if_mib.h>
39
40#include <assert.h>
41#include <errno.h>
42#include <stdarg.h>
43#include <string.h>
44#include <stdlib.h>
45#include <syslog.h>
46
47#include <bsnmp/snmpmod.h>
48#include <bsnmp/snmp_mibII.h>
49
50#define	SNMPTREE_TYPES
51#include "bridge_tree.h"
52#include "bridge_snmp.h"
53
54TAILQ_HEAD(bridge_ports, bridge_port);
55
56/*
57 * Free the bridge base ports list.
58 */
59static void
60bridge_ports_free(struct bridge_ports *headp)
61{
62	struct bridge_port *bp;
63
64	while ((bp = TAILQ_FIRST(headp)) != NULL) {
65		TAILQ_REMOVE(headp, bp, b_p);
66		free(bp);
67	}
68}
69
70/*
71 * Free the bridge base ports from the base ports list,
72 * members of a specified bridge interface only.
73 */
74static void
75bridge_port_memif_free(struct bridge_ports *headp,
76	struct bridge_if *bif)
77{
78	struct bridge_port *bp;
79
80	while (bif->f_bp != NULL && bif->sysindex == bif->f_bp->sysindex) {
81		bp = TAILQ_NEXT(bif->f_bp, b_p);
82		TAILQ_REMOVE(headp, bif->f_bp, b_p);
83		free(bif->f_bp);
84		bif->f_bp = bp;
85	}
86}
87
88/*
89 * Insert a port entry in the base port TAILQ starting to search
90 * for its place from the position of the first bridge port for the bridge
91 * interface. Update the first bridge port if necessary.
92 */
93static void
94bridge_port_insert_at(struct bridge_ports *headp,
95	struct bridge_port *bp, struct bridge_port **f_bp)
96{
97	struct bridge_port *t1;
98
99	assert(f_bp != NULL);
100
101	for (t1 = *f_bp;
102	    t1 != NULL && bp->sysindex == t1->sysindex;
103	    t1 = TAILQ_NEXT(t1, b_p)) {
104		if (bp->if_idx < t1->if_idx) {
105			TAILQ_INSERT_BEFORE(t1, bp, b_p);
106			if (*f_bp == t1)
107				*f_bp = bp;
108			return;
109		}
110	}
111
112	/*
113	 * Handle the case when our first port was actually the
114	 * last element of the TAILQ.
115	 */
116	if (t1 == NULL)
117		TAILQ_INSERT_TAIL(headp, bp, b_p);
118	else
119		TAILQ_INSERT_BEFORE(t1, bp, b_p);
120}
121
122/*
123 * Find a port entry's position in the ports list according
124 * to it's parent bridge interface name. Returns a NULL if
125 * we should be at the TAILQ head, otherwise the entry after
126 * which we should be inserted.
127 */
128static struct bridge_port *
129bridge_port_find_pos(struct bridge_ports *headp, uint32_t b_idx)
130{
131	uint32_t t_idx;
132	struct bridge_port *t1;
133
134	if ((t1 = TAILQ_FIRST(headp)) == NULL ||
135	    bridge_compare_sysidx(b_idx, t1->sysindex) < 0)
136		return (NULL);
137
138	t_idx = t1->sysindex;
139
140	for (t1 = TAILQ_NEXT(t1, b_p); t1 != NULL; t1 = TAILQ_NEXT(t1, b_p)) {
141		if (t1->sysindex != t_idx) {
142			if (bridge_compare_sysidx(b_idx, t1->sysindex) < 0)
143				return (TAILQ_PREV(t1, bridge_ports, b_p));
144			else
145				t_idx = t1->sysindex;
146		}
147	}
148
149	if (t1 == NULL)
150		t1 = TAILQ_LAST(headp, bridge_ports);
151
152	return (t1);
153}
154
155/*
156 * Insert a bridge member interface in the ports TAILQ.
157 */
158static void
159bridge_port_memif_insert(struct bridge_ports *headp,
160	struct bridge_port *bp, struct bridge_port **f_bp)
161{
162	struct bridge_port *temp;
163
164	if (*f_bp != NULL)
165		bridge_port_insert_at(headp, bp, f_bp);
166	else {
167		temp = bridge_port_find_pos(headp, bp->sysindex);
168
169		if (temp == NULL)
170			TAILQ_INSERT_HEAD(headp, bp, b_p);
171		else
172			TAILQ_INSERT_AFTER(headp, temp, bp, b_p);
173		*f_bp = bp;
174	}
175}
176
177/* The global ports list. */
178static struct bridge_ports bridge_ports = TAILQ_HEAD_INITIALIZER(bridge_ports);
179static time_t ports_list_age;
180
181void
182bridge_ports_update_listage(void)
183{
184	ports_list_age = time(NULL);
185}
186
187void
188bridge_ports_fini(void)
189{
190	bridge_ports_free(&bridge_ports);
191}
192
193void
194bridge_members_free(struct bridge_if *bif)
195{
196	bridge_port_memif_free(&bridge_ports, bif);
197}
198
199/*
200 * Find the first port in the ports list.
201 */
202static struct bridge_port *
203bridge_port_first(void)
204{
205	return (TAILQ_FIRST(&bridge_ports));
206}
207
208/*
209 * Find the next port in the ports list.
210 */
211static struct bridge_port *
212bridge_port_next(struct bridge_port *bp)
213{
214	return (TAILQ_NEXT(bp, b_p));
215}
216
217/*
218 * Find the first member of the specified bridge interface.
219 */
220struct bridge_port *
221bridge_port_bif_first(struct bridge_if *bif)
222{
223	return (bif->f_bp);
224}
225
226/*
227 * Find the next member of the specified bridge interface.
228 */
229struct bridge_port *
230bridge_port_bif_next(struct bridge_port *bp)
231{
232	struct bridge_port *bp_next;
233
234	if ((bp_next = TAILQ_NEXT(bp, b_p)) == NULL ||
235	    bp_next->sysindex != bp->sysindex)
236		return (NULL);
237
238	return (bp_next);
239}
240
241/*
242 * Remove a bridge port from the ports list.
243 */
244void
245bridge_port_remove(struct bridge_port *bp, struct bridge_if *bif)
246{
247	if (bif->f_bp == bp)
248		bif->f_bp = bridge_port_bif_next(bp);
249
250	TAILQ_REMOVE(&bridge_ports, bp, b_p);
251	free(bp);
252}
253
254/*
255 * Allocate memory for a new bridge port and insert it
256 * in the base ports list. Return a pointer to the port's
257 * structure in case we want to do anything else with it.
258 */
259struct bridge_port *
260bridge_new_port(struct mibif *mif, struct bridge_if *bif)
261{
262	struct bridge_port *bp;
263
264	if ((bp = (struct bridge_port *) malloc(sizeof(*bp))) == NULL) {
265		syslog(LOG_ERR, "bridge new member: failed: %s",
266			strerror(errno));
267		return (NULL);
268	}
269
270	bzero(bp, sizeof(*bp));
271
272	bp->sysindex = bif->sysindex;
273	bp->if_idx = mif->index;
274	bp->port_no = mif->sysindex;
275	strlcpy(bp->p_name, mif->name, IFNAMSIZ);
276	bp->circuit = oid_zeroDotZero;
277
278	/*
279	 * Initialize all rstpMib specific values to false/default.
280	 * These will be set to their true values later if the bridge
281	 * supports RSTP.
282	 */
283	bp->proto_migr = TruthValue_false;
284	bp->admin_edge = TruthValue_false;
285	bp->oper_edge = TruthValue_false;
286	bp->oper_ptp = TruthValue_false;
287	bp->admin_ptp = StpPortAdminPointToPointType_auto;
288
289	bridge_port_memif_insert(&bridge_ports, bp, &(bif->f_bp));
290
291	return (bp);
292}
293
294/*
295 * Update our info from the corresponding mibII interface info.
296 */
297void
298bridge_port_getinfo_mibif(struct mibif *m_if, struct bridge_port *bp)
299{
300	bp->max_info = m_if->mib.ifmd_data.ifi_mtu;
301	bp->in_frames = m_if->mib.ifmd_data.ifi_ipackets;
302	bp->out_frames = m_if->mib.ifmd_data.ifi_opackets;
303	bp->in_drops = m_if->mib.ifmd_data.ifi_iqdrops;
304}
305
306/*
307 * Find a port, whose SNMP's mibII ifIndex matches one of the ports,
308 * members of the specified bridge interface.
309 */
310struct bridge_port *
311bridge_port_find(int32_t if_idx, struct bridge_if *bif)
312{
313	struct bridge_port *bp;
314
315	for (bp = bif->f_bp; bp != NULL; bp = TAILQ_NEXT(bp, b_p)) {
316		if (bp->sysindex != bif->sysindex) {
317			bp = NULL;
318			break;
319		}
320
321		if (bp->if_idx == if_idx)
322			break;
323	}
324
325	return (bp);
326}
327
328void
329bridge_ports_dump(struct bridge_if *bif)
330{
331	struct bridge_port *bp;
332
333	for (bp = bridge_port_bif_first(bif); bp != NULL;
334	    bp = bridge_port_bif_next(bp)) {
335		syslog(LOG_ERR, "memif - %s, index - %d",
336		bp->p_name, bp->port_no);
337	}
338}
339
340/*
341 * RFC4188 specifics.
342 */
343int
344op_dot1d_base_port(struct snmp_context *c __unused, struct snmp_value *val,
345	uint sub, uint iidx __unused, enum snmp_op op)
346{
347	struct bridge_if *bif;
348	struct bridge_port *bp;
349
350	if ((bif = bridge_get_default()) == NULL)
351		return (SNMP_ERR_NOSUCHNAME);
352
353	if (time(NULL) - bif->ports_age > bridge_get_data_maxage() &&
354	    bridge_update_memif(bif) <= 0)
355		return (SNMP_ERR_NOSUCHNAME);
356
357	switch (op) {
358		case SNMP_OP_GET:
359		    if (val->var.len - sub != 1)
360			return (SNMP_ERR_NOSUCHNAME);
361		    if ((bp = bridge_port_find(val->var.subs[sub],
362			bif)) == NULL)
363			    return (SNMP_ERR_NOSUCHNAME);
364		    goto get;
365
366		case SNMP_OP_GETNEXT:
367		    if (val->var.len - sub == 0) {
368			if ((bp = bridge_port_bif_first(bif)) == NULL)
369			    return (SNMP_ERR_NOSUCHNAME);
370		    } else {
371			if ((bp = bridge_port_find(val->var.subs[sub],
372			    bif)) == NULL ||
373			    (bp = bridge_port_bif_next(bp)) == NULL)
374				return (SNMP_ERR_NOSUCHNAME);
375		    }
376		    val->var.len = sub + 1;
377		    val->var.subs[sub] = bp->port_no;
378		    goto get;
379
380		case SNMP_OP_SET:
381		    return (SNMP_ERR_NOT_WRITEABLE);
382
383		case SNMP_OP_ROLLBACK:
384		case SNMP_OP_COMMIT:
385		    break;
386	}
387	abort();
388
389get:
390	switch (val->var.subs[sub - 1]) {
391	    case LEAF_dot1dBasePort:
392		val->v.integer = bp->port_no;
393		return (SNMP_ERR_NOERROR);
394
395	    case LEAF_dot1dBasePortIfIndex:
396		val->v.integer = bp->if_idx;
397		return (SNMP_ERR_NOERROR);
398
399	    case LEAF_dot1dBasePortCircuit:
400		val->v.oid = bp->circuit;
401		return (SNMP_ERR_NOERROR);
402
403	    case LEAF_dot1dBasePortDelayExceededDiscards:
404		val->v.uint32 = bp->dly_ex_drops;
405		return (SNMP_ERR_NOERROR);
406
407	    case LEAF_dot1dBasePortMtuExceededDiscards:
408		val->v.uint32 = bp->dly_mtu_drops;
409		return (SNMP_ERR_NOERROR);
410	}
411
412	abort();
413}
414
415int
416op_dot1d_stp_port(struct snmp_context *ctx, struct snmp_value *val,
417	 uint sub, uint iidx __unused, enum snmp_op op)
418{
419	struct bridge_if *bif;
420	struct bridge_port *bp;
421
422	if ((bif = bridge_get_default()) == NULL)
423		return (SNMP_ERR_NOSUCHNAME);
424
425	if (time(NULL) - bif->ports_age > bridge_get_data_maxage() &&
426	    bridge_update_memif(bif) <= 0)
427		return (SNMP_ERR_NOSUCHNAME);
428
429	switch (op) {
430		case SNMP_OP_GET:
431		    if (val->var.len - sub != 1)
432			return (SNMP_ERR_NOSUCHNAME);
433		    if ((bp = bridge_port_find(val->var.subs[sub],
434			bif)) == NULL)
435			    return (SNMP_ERR_NOSUCHNAME);
436		    goto get;
437
438		case SNMP_OP_GETNEXT:
439		    if (val->var.len - sub == 0) {
440			if ((bp = bridge_port_bif_first(bif)) == NULL)
441			    return (SNMP_ERR_NOSUCHNAME);
442		    } else {
443			if ((bp = bridge_port_find(val->var.subs[sub],
444			    bif)) == NULL ||
445			    (bp = bridge_port_bif_next(bp)) == NULL)
446				return (SNMP_ERR_NOSUCHNAME);
447		    }
448		    val->var.len = sub + 1;
449		    val->var.subs[sub] = bp->port_no;
450		    goto get;
451
452		case SNMP_OP_SET:
453		    if (val->var.len - sub != 1)
454			return (SNMP_ERR_NOSUCHNAME);
455		    if ((bp = bridge_port_find(val->var.subs[sub],
456			bif)) == NULL)
457			    return (SNMP_ERR_NOSUCHNAME);
458
459		    switch (val->var.subs[sub - 1]) {
460			case LEAF_dot1dStpPortPriority:
461			    if (val->v.integer < 0 || val->v.integer > 255)
462				return (SNMP_ERR_WRONG_VALUE);
463
464			    ctx->scratch->int1 = bp->priority;
465			    if (bridge_port_set_priority(bif->bif_name, bp,
466				val->v.integer) < 0)
467				return (SNMP_ERR_GENERR);
468			    return (SNMP_ERR_NOERROR);
469
470			case LEAF_dot1dStpPortEnable:
471			    if (val->v.integer != dot1dStpPortEnable_enabled &&
472				val->v.integer != dot1dStpPortEnable_disabled)
473				return (SNMP_ERR_WRONG_VALUE);
474
475			    ctx->scratch->int1 = bp->enable;
476			    if (bridge_port_set_stp_enable(bif->bif_name,
477				bp, val->v.integer) < 0)
478				return (SNMP_ERR_GENERR);
479			    return (SNMP_ERR_NOERROR);
480
481			case LEAF_dot1dStpPortPathCost:
482			    if (val->v.integer < SNMP_PORT_MIN_PATHCOST ||
483				val->v.integer > SNMP_PORT_MAX_PATHCOST)
484				return (SNMP_ERR_WRONG_VALUE);
485
486			    ctx->scratch->int1 = bp->path_cost;
487			    if (bridge_port_set_path_cost(bif->bif_name, bp,
488				val->v.integer) < 0)
489				return (SNMP_ERR_GENERR);
490			    return (SNMP_ERR_NOERROR);
491
492			case LEAF_dot1dStpPort:
493			case LEAF_dot1dStpPortState:
494			case LEAF_dot1dStpPortDesignatedRoot:
495			case LEAF_dot1dStpPortDesignatedCost:
496			case LEAF_dot1dStpPortDesignatedBridge:
497			case LEAF_dot1dStpPortDesignatedPort:
498			case LEAF_dot1dStpPortForwardTransitions:
499			    return (SNMP_ERR_NOT_WRITEABLE);
500		    }
501		    abort();
502
503		case SNMP_OP_ROLLBACK:
504		    if ((bp = bridge_port_find(val->var.subs[sub],
505			bif)) == NULL)
506			    return (SNMP_ERR_GENERR);
507		    switch (val->var.subs[sub - 1]) {
508			case LEAF_dot1dStpPortPriority:
509			    bridge_port_set_priority(bif->bif_name, bp,
510				ctx->scratch->int1);
511			    break;
512			case LEAF_dot1dStpPortEnable:
513			    bridge_port_set_stp_enable(bif->bif_name, bp,
514				ctx->scratch->int1);
515			    break;
516			case LEAF_dot1dStpPortPathCost:
517			    bridge_port_set_path_cost(bif->bif_name, bp,
518				ctx->scratch->int1);
519			    break;
520		    }
521		    return (SNMP_ERR_NOERROR);
522
523		case SNMP_OP_COMMIT:
524		    return (SNMP_ERR_NOERROR);
525	}
526	abort();
527
528get:
529	switch (val->var.subs[sub - 1]) {
530		case LEAF_dot1dStpPort:
531			val->v.integer = bp->port_no;
532			return (SNMP_ERR_NOERROR);
533
534		case LEAF_dot1dStpPortPriority:
535			val->v.integer = bp->priority;
536			return (SNMP_ERR_NOERROR);
537
538		case LEAF_dot1dStpPortState:
539			val->v.integer = bp->state;
540			return (SNMP_ERR_NOERROR);
541
542		case LEAF_dot1dStpPortEnable:
543			val->v.integer = bp->enable;
544			return (SNMP_ERR_NOERROR);
545
546		case LEAF_dot1dStpPortPathCost:
547			val->v.integer = bp->path_cost;
548			return (SNMP_ERR_NOERROR);
549
550		case LEAF_dot1dStpPortDesignatedRoot:
551			return (string_get(val, bp->design_root,
552			    SNMP_BRIDGE_ID_LEN));
553
554		case LEAF_dot1dStpPortDesignatedCost:
555			val->v.integer = bp->design_cost;
556			return (SNMP_ERR_NOERROR);
557
558		case LEAF_dot1dStpPortDesignatedBridge:
559			return (string_get(val, bp->design_bridge,
560			    SNMP_BRIDGE_ID_LEN));
561
562		case LEAF_dot1dStpPortDesignatedPort:
563			return (string_get(val, bp->design_port, 2));
564
565		case LEAF_dot1dStpPortForwardTransitions:
566			val->v.uint32 = bp->fwd_trans;
567			return (SNMP_ERR_NOERROR);
568	}
569
570	abort();
571}
572
573int
574op_dot1d_stp_ext_port(struct snmp_context *ctx, struct snmp_value *val,
575    uint sub, uint iidx __unused, enum snmp_op op)
576{
577	struct bridge_if *bif;
578	struct bridge_port *bp;
579
580	if ((bif = bridge_get_default()) == NULL)
581		return (SNMP_ERR_NOSUCHNAME);
582
583	if (time(NULL) - bif->ports_age > bridge_get_data_maxage() &&
584	    bridge_update_memif(bif) <= 0)
585		return (SNMP_ERR_NOSUCHNAME);
586
587	switch (op) {
588		case SNMP_OP_GET:
589		    if (val->var.len - sub != 1)
590			return (SNMP_ERR_NOSUCHNAME);
591		    if ((bp = bridge_port_find(val->var.subs[sub],
592			bif)) == NULL)
593			    return (SNMP_ERR_NOSUCHNAME);
594		    goto get;
595
596		case SNMP_OP_GETNEXT:
597		    if (val->var.len - sub == 0) {
598			if ((bp = bridge_port_bif_first(bif)) == NULL)
599			    return (SNMP_ERR_NOSUCHNAME);
600		    } else {
601			if ((bp = bridge_port_find(val->var.subs[sub],
602			    bif)) == NULL ||
603			    (bp = bridge_port_bif_next(bp)) == NULL)
604				return (SNMP_ERR_NOSUCHNAME);
605		    }
606		    val->var.len = sub + 1;
607		    val->var.subs[sub] = bp->port_no;
608		    goto get;
609
610		case SNMP_OP_SET:
611		    if (val->var.len - sub != 1)
612			return (SNMP_ERR_NOSUCHNAME);
613		    if ((bp = bridge_port_find(val->var.subs[sub],
614			bif)) == NULL)
615			    return (SNMP_ERR_NOSUCHNAME);
616
617		    switch (val->var.subs[sub - 1]) {
618			case LEAF_dot1dStpPortAdminEdgePort:
619			    if (val->v.integer != TruthValue_true &&
620				val->v.integer != TruthValue_false)
621				return (SNMP_ERR_WRONG_VALUE);
622
623			    ctx->scratch->int1 = bp->admin_edge;
624			    if (bridge_port_set_admin_edge(bif->bif_name, bp,
625				val->v.integer) < 0)
626				return (SNMP_ERR_GENERR);
627			    return (SNMP_ERR_NOERROR);
628
629			case LEAF_dot1dStpPortAdminPointToPoint:
630			    if (val->v.integer < 0 || val->v.integer >
631				StpPortAdminPointToPointType_auto)
632				return (SNMP_ERR_WRONG_VALUE);
633
634			    ctx->scratch->int1 = bp->admin_ptp;
635			    if (bridge_port_set_admin_ptp(bif->bif_name, bp,
636				val->v.integer) < 0)
637				return (SNMP_ERR_GENERR);
638			    return (SNMP_ERR_NOERROR);
639
640			case LEAF_dot1dStpPortAdminPathCost:
641			    if (val->v.integer < SNMP_PORT_MIN_PATHCOST ||
642				val->v.integer > SNMP_PORT_MAX_PATHCOST)
643				return (SNMP_ERR_WRONG_VALUE);
644
645			    ctx->scratch->int1 = bp->admin_path_cost;
646			    if (bridge_port_set_path_cost(bif->bif_name, bp,
647				val->v.integer) < 0)
648				return (SNMP_ERR_GENERR);
649			    return (SNMP_ERR_NOERROR);
650
651			case LEAF_dot1dStpPortProtocolMigration:
652			case LEAF_dot1dStpPortOperEdgePort:
653			case LEAF_dot1dStpPortOperPointToPoint:
654			    return (SNMP_ERR_NOT_WRITEABLE);
655		    }
656		    abort();
657
658		case SNMP_OP_ROLLBACK:
659		    if ((bp = bridge_port_find(val->var.subs[sub],
660			bif)) == NULL)
661			    return (SNMP_ERR_GENERR);
662
663		    switch (val->var.subs[sub - 1]) {
664			case LEAF_dot1dStpPortAdminEdgePort:
665			    bridge_port_set_admin_edge(bif->bif_name, bp,
666				ctx->scratch->int1);
667			    break;
668			case LEAF_dot1dStpPortAdminPointToPoint:
669			    bridge_port_set_admin_ptp(bif->bif_name, bp,
670				ctx->scratch->int1);
671			    break;
672			case LEAF_dot1dStpPortAdminPathCost:
673			    bridge_port_set_path_cost(bif->bif_name, bp,
674				ctx->scratch->int1);
675			    break;
676		    }
677		    return (SNMP_ERR_NOERROR);
678
679		case SNMP_OP_COMMIT:
680		    return (SNMP_ERR_NOERROR);
681	}
682	abort();
683
684get:
685	switch (val->var.subs[sub - 1]) {
686		case LEAF_dot1dStpPortProtocolMigration:
687			val->v.integer = bp->proto_migr;
688			return (SNMP_ERR_NOERROR);
689
690		case LEAF_dot1dStpPortAdminEdgePort:
691			val->v.integer = bp->admin_edge;
692			return (SNMP_ERR_NOERROR);
693
694		case LEAF_dot1dStpPortOperEdgePort:
695			val->v.integer = bp->oper_edge;
696			return (SNMP_ERR_NOERROR);
697
698		case LEAF_dot1dStpPortAdminPointToPoint:
699			val->v.integer = bp->admin_ptp;
700			return (SNMP_ERR_NOERROR);
701
702		case LEAF_dot1dStpPortOperPointToPoint:
703			val->v.integer = bp->oper_ptp;
704			return (SNMP_ERR_NOERROR);
705
706		case LEAF_dot1dStpPortAdminPathCost:
707			val->v.integer = bp->admin_path_cost;
708			return (SNMP_ERR_NOERROR);
709	}
710
711	abort();
712}
713
714int
715op_dot1d_tp_port(struct snmp_context *c __unused, struct snmp_value *val,
716    uint sub, uint iidx __unused, enum snmp_op op)
717{
718	struct bridge_if *bif;
719	struct bridge_port *bp;
720
721	if ((bif = bridge_get_default()) == NULL)
722		return (SNMP_ERR_NOSUCHNAME);
723
724	if (time(NULL) - bif->ports_age > bridge_get_data_maxage() &&
725	    bridge_update_memif(bif) <= 0)
726		return (SNMP_ERR_NOSUCHNAME);
727
728	switch (op) {
729		case SNMP_OP_GET:
730		    if (val->var.len - sub != 1)
731			return (SNMP_ERR_NOSUCHNAME);
732		    if ((bp = bridge_port_find(val->var.subs[sub],
733			bif)) == NULL)
734			    return (SNMP_ERR_NOSUCHNAME);
735		    goto get;
736
737		case SNMP_OP_GETNEXT:
738		    if (val->var.len - sub == 0) {
739			if ((bp = bridge_port_bif_first(bif)) == NULL)
740			    return (SNMP_ERR_NOSUCHNAME);
741		    } else {
742			if ((bp = bridge_port_find(val->var.subs[sub],
743			    bif)) == NULL ||
744			    (bp = bridge_port_bif_next(bp)) == NULL)
745				return (SNMP_ERR_NOSUCHNAME);
746		    }
747		    val->var.len = sub + 1;
748		    val->var.subs[sub] = bp->port_no;
749		    goto get;
750
751		case SNMP_OP_SET:
752		    return (SNMP_ERR_NOT_WRITEABLE);
753
754		case SNMP_OP_ROLLBACK:
755		case SNMP_OP_COMMIT:
756		    break;
757	}
758	abort();
759
760get:
761	switch (val->var.subs[sub - 1]) {
762		case LEAF_dot1dTpPort:
763			val->v.integer = bp->port_no;
764			return (SNMP_ERR_NOERROR);
765
766		case LEAF_dot1dTpPortMaxInfo:
767			val->v.integer = bp->max_info;
768			return (SNMP_ERR_NOERROR);
769
770		case LEAF_dot1dTpPortInFrames:
771			val->v.uint32 = bp->in_frames;
772			return (SNMP_ERR_NOERROR);
773
774		case LEAF_dot1dTpPortOutFrames:
775			val->v.uint32 = bp->out_frames;
776			return (SNMP_ERR_NOERROR);
777
778		case LEAF_dot1dTpPortInDiscards:
779			val->v.uint32 = bp->in_drops;
780			return (SNMP_ERR_NOERROR);
781	}
782
783	abort();
784}
785
786/*
787 * Private BEGEMOT-BRIDGE-MIB specifics.
788 */
789
790/*
791 * Construct a bridge port entry index.
792 */
793static int
794bridge_port_index_append(struct asn_oid *oid, uint sub,
795	const struct bridge_port *bp)
796{
797	uint i;
798	const char *b_name;
799
800	if ((b_name = bridge_if_find_name(bp->sysindex)) == NULL)
801		return (-1);
802
803	oid->len = sub + strlen(b_name) + 1 + 1;
804	oid->subs[sub] = strlen(b_name);
805
806	for (i = 1; i <= strlen(b_name); i++)
807		oid->subs[sub + i] = b_name[i - 1];
808
809	oid->subs[sub + i] = bp->port_no;
810
811	return (0);
812}
813
814/*
815 * Get the port entry from an entry's index.
816 */
817static struct bridge_port *
818bridge_port_index_get(const struct asn_oid *oid, uint sub, int8_t status)
819{
820	uint i;
821	int32_t port_no;
822	char bif_name[IFNAMSIZ];
823	struct bridge_if *bif;
824	struct bridge_port *bp;
825
826	if (oid->len - sub != oid->subs[sub] + 2 ||
827	    oid->subs[sub] >= IFNAMSIZ)
828		return (NULL);
829
830	for (i = 0; i < oid->subs[sub]; i++)
831		bif_name[i] = oid->subs[sub + i + 1];
832	bif_name[i] = '\0';
833
834	port_no = oid->subs[sub + i + 1];
835
836	if ((bif = bridge_if_find_ifname(bif_name)) == NULL)
837		return (NULL);
838
839	if ((bp = bridge_port_find(port_no, bif)) == NULL ||
840	    (status == 0 && bp->status != RowStatus_active))
841		return (NULL);
842
843	return (bp);
844}
845
846/*
847 * Get the next port entry from an entry's index.
848 */
849static struct bridge_port *
850bridge_port_index_getnext(const struct asn_oid *oid, uint sub, int8_t status)
851{
852	uint i;
853	int32_t port_no;
854	char bif_name[IFNAMSIZ];
855	struct bridge_if *bif;
856	struct bridge_port *bp;
857
858	if (oid->len - sub == 0)
859		bp = bridge_port_first();
860	else {
861		if (oid->len - sub != oid->subs[sub] + 2 ||
862		    oid->subs[sub] >= IFNAMSIZ)
863			return (NULL);
864
865		for (i = 0; i < oid->subs[sub]; i++)
866			bif_name[i] = oid->subs[sub + i + 1];
867		bif_name[i] = '\0';
868
869		port_no = oid->subs[sub + i + 1];
870
871		if ((bif = bridge_if_find_ifname(bif_name)) == NULL ||
872		    (bp = bridge_port_find(port_no, bif)) == NULL)
873			return (NULL);
874
875		bp = bridge_port_next(bp);
876	}
877
878	if (status == 1)
879		return (bp);
880
881	while (bp != NULL) {
882		if (bp->status == RowStatus_active)
883			break;
884		bp = bridge_port_next(bp);
885	}
886
887	return (bp);
888}
889
890/*
891 * Read the bridge name and port index from a ASN OID structure.
892 */
893static int
894bridge_port_index_decode(const struct asn_oid *oid, uint sub,
895	char *b_name, int32_t *idx)
896{
897	uint i;
898
899	if (oid->len - sub != oid->subs[sub] + 2 ||
900	    oid->subs[sub] >= IFNAMSIZ)
901		return (-1);
902
903	for (i = 0; i < oid->subs[sub]; i++)
904		b_name[i] = oid->subs[sub + i + 1];
905	b_name[i] = '\0';
906
907	*idx = oid->subs[sub + i + 1];
908	return (0);
909}
910
911static int
912bridge_port_set_status(struct snmp_context *ctx,
913	struct snmp_value *val, uint sub)
914{
915	int32_t if_idx;
916	char b_name[IFNAMSIZ];
917	struct bridge_if *bif;
918	struct bridge_port *bp;
919	struct mibif *mif;
920
921	if (bridge_port_index_decode(&val->var, sub, b_name, &if_idx) < 0)
922		return (SNMP_ERR_INCONS_VALUE);
923
924	if ((bif = bridge_if_find_ifname(b_name)) == NULL ||
925	    (mif = mib_find_if(if_idx)) == NULL)
926		return (SNMP_ERR_INCONS_VALUE);
927
928	bp = bridge_port_find(if_idx, bif);
929
930	switch (val->v.integer) {
931	    case RowStatus_active:
932		if (bp == NULL)
933		    return (SNMP_ERR_INCONS_VALUE);
934
935		if (bp->span_enable == 0)
936		    return (SNMP_ERR_INCONS_VALUE);
937
938		ctx->scratch->int1 = bp->status;
939		bp->status = RowStatus_active;
940		break;
941
942	    case RowStatus_notInService:
943		if (bp == NULL || bp->span_enable == 0 ||
944		    bp->status == RowStatus_active)
945			return (SNMP_ERR_INCONS_VALUE);
946
947		ctx->scratch->int1 = bp->status;
948		bp->status = RowStatus_notInService;
949
950	    case RowStatus_notReady:
951		/* FALLTHROUGH */
952	    case RowStatus_createAndGo:
953		return (SNMP_ERR_INCONS_VALUE);
954
955	    case RowStatus_createAndWait:
956		if (bp != NULL)
957		    return (SNMP_ERR_INCONS_VALUE);
958
959		if ((bp = bridge_new_port(mif, bif)) == NULL)
960			return (SNMP_ERR_GENERR);
961
962		ctx->scratch->int1 = RowStatus_destroy;
963		bp->status = RowStatus_notReady;
964		break;
965
966	    case RowStatus_destroy:
967		if (bp == NULL)
968		    return (SNMP_ERR_INCONS_VALUE);
969
970		ctx->scratch->int1 = bp->status;
971		bp->status = RowStatus_destroy;
972		break;
973	}
974
975	return (SNMP_ERR_NOERROR);
976}
977
978static int
979bridge_port_rollback_status(struct snmp_context *ctx,
980	struct snmp_value *val, uint sub)
981{
982	int32_t if_idx;
983	char b_name[IFNAMSIZ];
984	struct bridge_if *bif;
985	struct bridge_port *bp;
986
987	if (bridge_port_index_decode(&val->var, sub, b_name, &if_idx) < 0)
988		return (SNMP_ERR_GENERR);
989
990	if ((bif = bridge_if_find_ifname(b_name)) == NULL ||
991	    (bp = bridge_port_find(if_idx, bif)) == NULL)
992		return (SNMP_ERR_GENERR);
993
994	if (ctx->scratch->int1 == RowStatus_destroy)
995		bridge_port_remove(bp, bif);
996	else
997		bp->status = ctx->scratch->int1;
998
999	return (SNMP_ERR_NOERROR);
1000}
1001
1002static int
1003bridge_port_commit_status(struct snmp_value *val, uint sub)
1004{
1005	int32_t if_idx;
1006	char b_name[IFNAMSIZ];
1007	struct bridge_if *bif;
1008	struct bridge_port *bp;
1009
1010	if (bridge_port_index_decode(&val->var, sub, b_name, &if_idx) < 0)
1011		return (SNMP_ERR_GENERR);
1012
1013	if ((bif = bridge_if_find_ifname(b_name)) == NULL ||
1014	    (bp = bridge_port_find(if_idx, bif)) == NULL)
1015		return (SNMP_ERR_GENERR);
1016
1017	switch (bp->status) {
1018		case RowStatus_active:
1019			if (bridge_port_addm(bp, b_name) < 0)
1020				return (SNMP_ERR_COMMIT_FAILED);
1021			break;
1022
1023		case RowStatus_destroy:
1024			if (bridge_port_delm(bp, b_name) < 0)
1025				return (SNMP_ERR_COMMIT_FAILED);
1026			bridge_port_remove(bp, bif);
1027			break;
1028	}
1029
1030	return (SNMP_ERR_NOERROR);
1031}
1032
1033static int
1034bridge_port_set_span_enable(struct snmp_context *ctx,
1035		struct snmp_value *val, uint sub)
1036{
1037	int32_t if_idx;
1038	char b_name[IFNAMSIZ];
1039	struct bridge_if *bif;
1040	struct bridge_port *bp;
1041	struct mibif *mif;
1042
1043	if (val->v.integer != begemotBridgeBaseSpanEnabled_enabled &&
1044	    val->v.integer != begemotBridgeBaseSpanEnabled_disabled)
1045		return (SNMP_ERR_BADVALUE);
1046
1047	if (bridge_port_index_decode(&val->var, sub, b_name, &if_idx) < 0)
1048		return (SNMP_ERR_INCONS_VALUE);
1049
1050	if ((bif = bridge_if_find_ifname(b_name)) == NULL)
1051		return (SNMP_ERR_INCONS_VALUE);
1052
1053	if ((bp = bridge_port_find(if_idx, bif)) == NULL) {
1054		if ((mif = mib_find_if(if_idx)) == NULL)
1055			return (SNMP_ERR_INCONS_VALUE);
1056
1057		if ((bp = bridge_new_port(mif, bif)) == NULL)
1058			return (SNMP_ERR_GENERR);
1059
1060		ctx->scratch->int1 = RowStatus_destroy;
1061	} else if (bp->status == RowStatus_active) {
1062		return (SNMP_ERR_INCONS_VALUE);
1063	} else {
1064		ctx->scratch->int1 = bp->status;
1065	}
1066
1067	bp->span_enable = val->v.integer;
1068	bp->status = RowStatus_notInService;
1069
1070	return (SNMP_ERR_NOERROR);
1071}
1072
1073int
1074op_begemot_base_port(struct snmp_context *ctx, struct snmp_value *val,
1075	uint sub, uint iidx __unused, enum snmp_op op)
1076{
1077	int8_t status, which;
1078	const char *bname;
1079	struct bridge_port *bp;
1080
1081	if (time(NULL) - ports_list_age > bridge_get_data_maxage())
1082		bridge_update_all_ports();
1083
1084	which = val->var.subs[sub - 1];
1085	status = 0;
1086
1087	switch (op) {
1088	    case SNMP_OP_GET:
1089		if (which == LEAF_begemotBridgeBaseSpanEnabled ||
1090		    which == LEAF_begemotBridgeBasePortStatus)
1091			status = 1;
1092		if ((bp = bridge_port_index_get(&val->var, sub,
1093		    status)) == NULL)
1094			return (SNMP_ERR_NOSUCHNAME);
1095		goto get;
1096
1097	    case SNMP_OP_GETNEXT:
1098		if (which == LEAF_begemotBridgeBaseSpanEnabled ||
1099		    which == LEAF_begemotBridgeBasePortStatus)
1100			status = 1;
1101		if ((bp = bridge_port_index_getnext(&val->var, sub,
1102		    status)) == NULL ||
1103		    bridge_port_index_append(&val->var, sub, bp) < 0)
1104			return (SNMP_ERR_NOSUCHNAME);
1105		goto get;
1106
1107	    case SNMP_OP_SET:
1108		switch (which) {
1109		    case LEAF_begemotBridgeBaseSpanEnabled:
1110			return (bridge_port_set_span_enable(ctx, val, sub));
1111
1112		    case LEAF_begemotBridgeBasePortStatus:
1113			return (bridge_port_set_status(ctx, val, sub));
1114
1115		    case LEAF_begemotBridgeBasePortPrivate:
1116			if ((bp = bridge_port_index_get(&val->var, sub,
1117			    status)) == NULL)
1118				return (SNMP_ERR_NOSUCHNAME);
1119			if ((bname = bridge_if_find_name(bp->sysindex)) == NULL)
1120				return (SNMP_ERR_GENERR);
1121			ctx->scratch->int1 = bp->priv_set;
1122			return (bridge_port_set_private(bname, bp,
1123			    val->v.integer));
1124
1125		    case LEAF_begemotBridgeBasePort:
1126		    case LEAF_begemotBridgeBasePortIfIndex:
1127		    case LEAF_begemotBridgeBasePortDelayExceededDiscards:
1128		    case LEAF_begemotBridgeBasePortMtuExceededDiscards:
1129			return (SNMP_ERR_NOT_WRITEABLE);
1130		}
1131		abort();
1132
1133	    case SNMP_OP_ROLLBACK:
1134		switch (which) {
1135		    case LEAF_begemotBridgeBaseSpanEnabled:
1136			/* FALLTHROUGH */
1137		    case LEAF_begemotBridgeBasePortStatus:
1138			return (bridge_port_rollback_status(ctx, val, sub));
1139		    case LEAF_begemotBridgeBasePortPrivate:
1140			if ((bp = bridge_port_index_get(&val->var, sub,
1141			    status)) == NULL)
1142				return (SNMP_ERR_GENERR);
1143			if ((bname = bridge_if_find_name(bp->sysindex)) == NULL)
1144				return (SNMP_ERR_GENERR);
1145			return (bridge_port_set_private(bname, bp,
1146			    ctx->scratch->int1));
1147		}
1148		return (SNMP_ERR_NOERROR);
1149
1150	    case SNMP_OP_COMMIT:
1151		if (which == LEAF_begemotBridgeBasePortStatus)
1152			return (bridge_port_commit_status(val, sub));
1153
1154		return (SNMP_ERR_NOERROR);
1155	}
1156	abort();
1157
1158get:
1159	switch (which) {
1160	    case LEAF_begemotBridgeBasePort:
1161		val->v.integer = bp->port_no;
1162		return (SNMP_ERR_NOERROR);
1163
1164	    case LEAF_begemotBridgeBasePortIfIndex:
1165		val->v.integer = bp->if_idx;
1166		return (SNMP_ERR_NOERROR);
1167
1168	    case LEAF_begemotBridgeBaseSpanEnabled:
1169		val->v.integer = bp->span_enable;
1170		return (SNMP_ERR_NOERROR);
1171
1172	    case LEAF_begemotBridgeBasePortDelayExceededDiscards:
1173		val->v.uint32 = bp->dly_ex_drops;
1174		return (SNMP_ERR_NOERROR);
1175
1176	    case LEAF_begemotBridgeBasePortMtuExceededDiscards:
1177		val->v.uint32 = bp->dly_mtu_drops;
1178		return (SNMP_ERR_NOERROR);
1179
1180	    case LEAF_begemotBridgeBasePortStatus:
1181		val->v.integer = bp->status;
1182		return (SNMP_ERR_NOERROR);
1183
1184	    case LEAF_begemotBridgeBasePortPrivate:
1185		val->v.integer = bp->priv_set;
1186		return (SNMP_ERR_NOERROR);
1187	}
1188
1189	abort();
1190}
1191
1192int
1193op_begemot_stp_port(struct snmp_context *ctx, struct snmp_value *val,
1194	uint sub, uint iidx __unused, enum snmp_op op)
1195{
1196	struct bridge_port *bp;
1197	const char *b_name;
1198
1199	if (time(NULL) - ports_list_age > bridge_get_data_maxage())
1200		bridge_update_all_ports();
1201
1202	switch (op) {
1203	    case SNMP_OP_GET:
1204		if ((bp = bridge_port_index_get(&val->var, sub, 0)) == NULL)
1205		    return (SNMP_ERR_NOSUCHNAME);
1206		goto get;
1207
1208	    case SNMP_OP_GETNEXT:
1209		if ((bp = bridge_port_index_getnext(&val->var, sub, 0)) ==
1210		    NULL || bridge_port_index_append(&val->var, sub, bp) < 0)
1211			return (SNMP_ERR_NOSUCHNAME);
1212		goto get;
1213
1214	    case SNMP_OP_SET:
1215		if ((bp = bridge_port_index_get(&val->var, sub, 0)) == NULL)
1216			return (SNMP_ERR_NOSUCHNAME);
1217		if ((b_name = bridge_if_find_name(bp->sysindex)) == NULL)
1218			return (SNMP_ERR_GENERR);
1219
1220		switch (val->var.subs[sub - 1]) {
1221		    case LEAF_begemotBridgeStpPortPriority:
1222			if (val->v.integer < 0 || val->v.integer > 255)
1223			    return (SNMP_ERR_WRONG_VALUE);
1224
1225			ctx->scratch->int1 = bp->priority;
1226			if (bridge_port_set_priority(b_name, bp,
1227			    val->v.integer) < 0)
1228			    return (SNMP_ERR_GENERR);
1229			return (SNMP_ERR_NOERROR);
1230
1231		    case LEAF_begemotBridgeStpPortEnable:
1232			if (val->v.integer !=
1233			    (int32_t)begemotBridgeStpPortEnable_enabled ||
1234			    val->v.integer !=
1235			    (int32_t)begemotBridgeStpPortEnable_disabled)
1236			    return (SNMP_ERR_WRONG_VALUE);
1237
1238			ctx->scratch->int1 = bp->enable;
1239			if (bridge_port_set_stp_enable(b_name, bp,
1240			    val->v.integer) < 0)
1241			    return (SNMP_ERR_GENERR);
1242			return (SNMP_ERR_NOERROR);
1243
1244		    case LEAF_begemotBridgeStpPortPathCost:
1245			if (val->v.integer < SNMP_PORT_MIN_PATHCOST ||
1246			    val->v.integer > SNMP_PORT_MAX_PATHCOST)
1247			    return (SNMP_ERR_WRONG_VALUE);
1248
1249			ctx->scratch->int1 = bp->path_cost;
1250			if (bridge_port_set_path_cost(b_name, bp,
1251			    val->v.integer) < 0)
1252			    return (SNMP_ERR_GENERR);
1253			return (SNMP_ERR_NOERROR);
1254
1255		    case LEAF_begemotBridgeStpPort:
1256		    case LEAF_begemotBridgeStpPortState:
1257		    case LEAF_begemotBridgeStpPortDesignatedRoot:
1258		    case LEAF_begemotBridgeStpPortDesignatedCost:
1259		    case LEAF_begemotBridgeStpPortDesignatedBridge:
1260		    case LEAF_begemotBridgeStpPortDesignatedPort:
1261		    case LEAF_begemotBridgeStpPortForwardTransitions:
1262			return (SNMP_ERR_NOT_WRITEABLE);
1263		}
1264		abort();
1265
1266	    case SNMP_OP_ROLLBACK:
1267		if ((bp = bridge_port_index_get(&val->var, sub, 0)) == NULL ||
1268		    (b_name = bridge_if_find_name(bp->sysindex)) == NULL)
1269			return (SNMP_ERR_GENERR);
1270
1271		switch (val->var.subs[sub - 1]) {
1272		    case LEAF_begemotBridgeStpPortPriority:
1273			bridge_port_set_priority(b_name, bp,
1274			    ctx->scratch->int1);
1275			break;
1276		    case LEAF_begemotBridgeStpPortEnable:
1277			bridge_port_set_stp_enable(b_name, bp,
1278			    ctx->scratch->int1);
1279			break;
1280		    case LEAF_begemotBridgeStpPortPathCost:
1281			bridge_port_set_path_cost(b_name, bp,
1282			    ctx->scratch->int1);
1283			break;
1284		}
1285		return (SNMP_ERR_NOERROR);
1286
1287	    case SNMP_OP_COMMIT:
1288		return (SNMP_ERR_NOERROR);
1289	}
1290	abort();
1291
1292get:
1293	switch (val->var.subs[sub - 1]) {
1294	    case LEAF_begemotBridgeStpPort:
1295		val->v.integer = bp->port_no;
1296		return (SNMP_ERR_NOERROR);
1297
1298	    case LEAF_begemotBridgeStpPortPriority:
1299		val->v.integer = bp->priority;
1300		return (SNMP_ERR_NOERROR);
1301
1302	    case LEAF_begemotBridgeStpPortState:
1303		val->v.integer = bp->state;
1304		return (SNMP_ERR_NOERROR);
1305
1306	    case LEAF_begemotBridgeStpPortEnable:
1307		val->v.integer = bp->enable;
1308		return (SNMP_ERR_NOERROR);
1309
1310	    case LEAF_begemotBridgeStpPortPathCost:
1311		val->v.integer = bp->path_cost;
1312		return (SNMP_ERR_NOERROR);
1313
1314	    case LEAF_begemotBridgeStpPortDesignatedRoot:
1315		return (string_get(val, bp->design_root, SNMP_BRIDGE_ID_LEN));
1316
1317	    case LEAF_begemotBridgeStpPortDesignatedCost:
1318		val->v.integer = bp->design_cost;
1319		return (SNMP_ERR_NOERROR);
1320
1321	    case LEAF_begemotBridgeStpPortDesignatedBridge:
1322		return (string_get(val, bp->design_bridge, SNMP_BRIDGE_ID_LEN));
1323
1324	    case LEAF_begemotBridgeStpPortDesignatedPort:
1325		return (string_get(val, bp->design_port, 2));
1326
1327	    case LEAF_begemotBridgeStpPortForwardTransitions:
1328		val->v.uint32 = bp->fwd_trans;
1329		return (SNMP_ERR_NOERROR);
1330	}
1331
1332	abort();
1333}
1334
1335int
1336op_begemot_stp_ext_port(struct snmp_context *ctx, struct snmp_value *val,
1337    uint sub, uint iidx __unused, enum snmp_op op)
1338{
1339	struct bridge_port *bp;
1340	const char *b_name;
1341
1342	if (time(NULL) - ports_list_age > bridge_get_data_maxage())
1343		bridge_update_all_ports();
1344
1345	switch (op) {
1346	    case SNMP_OP_GET:
1347		if ((bp = bridge_port_index_get(&val->var, sub, 0)) == NULL)
1348		    return (SNMP_ERR_NOSUCHNAME);
1349		goto get;
1350
1351	    case SNMP_OP_GETNEXT:
1352		if ((bp = bridge_port_index_getnext(&val->var, sub, 0)) ==
1353		    NULL || bridge_port_index_append(&val->var, sub, bp) < 0)
1354			return (SNMP_ERR_NOSUCHNAME);
1355		goto get;
1356
1357	    case SNMP_OP_SET:
1358		if ((bp = bridge_port_index_get(&val->var, sub, 0)) == NULL)
1359			return (SNMP_ERR_NOSUCHNAME);
1360		if ((b_name = bridge_if_find_name(bp->sysindex)) == NULL)
1361			return (SNMP_ERR_GENERR);
1362
1363		switch (val->var.subs[sub - 1]) {
1364		    case LEAF_begemotBridgeStpPortAdminEdgePort:
1365			if (val->v.integer != TruthValue_true &&
1366			    val->v.integer != TruthValue_false)
1367			    return (SNMP_ERR_WRONG_VALUE);
1368
1369			ctx->scratch->int1 = bp->admin_edge;
1370			if (bridge_port_set_admin_edge(b_name, bp,
1371			    val->v.integer) < 0)
1372			    return (SNMP_ERR_GENERR);
1373			return (SNMP_ERR_NOERROR);
1374
1375		    case LEAF_begemotBridgeStpPortAdminPointToPoint:
1376			if (val->v.integer < 0 || val->v.integer >
1377			    StpPortAdminPointToPointType_auto)
1378			    return (SNMP_ERR_WRONG_VALUE);
1379
1380			ctx->scratch->int1 = bp->admin_ptp;
1381			if (bridge_port_set_admin_ptp(b_name, bp,
1382			    val->v.integer) < 0)
1383			    return (SNMP_ERR_GENERR);
1384			return (SNMP_ERR_NOERROR);
1385
1386		    case LEAF_begemotBridgeStpPortAdminPathCost:
1387			if (val->v.integer < SNMP_PORT_MIN_PATHCOST ||
1388			    val->v.integer > SNMP_PORT_MAX_PATHCOST)
1389			    return (SNMP_ERR_WRONG_VALUE);
1390
1391			ctx->scratch->int1 = bp->admin_path_cost;
1392			if (bridge_port_set_path_cost(b_name, bp,
1393			    val->v.integer) < 0)
1394			    return (SNMP_ERR_GENERR);
1395			return (SNMP_ERR_NOERROR);
1396
1397		    case LEAF_begemotBridgeStpPortProtocolMigration:
1398		    case LEAF_begemotBridgeStpPortOperEdgePort:
1399		    case LEAF_begemotBridgeStpPortOperPointToPoint:
1400			return (SNMP_ERR_NOT_WRITEABLE);
1401		}
1402		abort();
1403
1404	    case SNMP_OP_ROLLBACK:
1405		if ((bp = bridge_port_index_get(&val->var, sub, 0)) == NULL ||
1406		    (b_name = bridge_if_find_name(bp->sysindex)) == NULL)
1407			return (SNMP_ERR_GENERR);
1408
1409		switch (val->var.subs[sub - 1]) {
1410		    case LEAF_begemotBridgeStpPortAdminEdgePort:
1411			bridge_port_set_admin_edge(b_name, bp,
1412			    ctx->scratch->int1);
1413			break;
1414		    case LEAF_begemotBridgeStpPortAdminPointToPoint:
1415			bridge_port_set_admin_ptp(b_name, bp,
1416			    ctx->scratch->int1);
1417			break;
1418		    case LEAF_begemotBridgeStpPortAdminPathCost:
1419			bridge_port_set_path_cost(b_name, bp,
1420			    ctx->scratch->int1);
1421			break;
1422		}
1423		return (SNMP_ERR_NOERROR);
1424
1425	    case SNMP_OP_COMMIT:
1426		return (SNMP_ERR_NOERROR);
1427	}
1428	abort();
1429
1430get:
1431	switch (val->var.subs[sub - 1]) {
1432		case LEAF_begemotBridgeStpPortProtocolMigration:
1433			val->v.integer = bp->proto_migr;
1434			return (SNMP_ERR_NOERROR);
1435
1436		case LEAF_begemotBridgeStpPortAdminEdgePort:
1437			val->v.integer = bp->admin_edge;
1438			return (SNMP_ERR_NOERROR);
1439
1440		case LEAF_begemotBridgeStpPortOperEdgePort:
1441			val->v.integer = bp->oper_edge;
1442			return (SNMP_ERR_NOERROR);
1443
1444		case LEAF_begemotBridgeStpPortAdminPointToPoint:
1445			val->v.integer = bp->admin_ptp;
1446			return (SNMP_ERR_NOERROR);
1447
1448		case LEAF_begemotBridgeStpPortOperPointToPoint:
1449			val->v.integer = bp->oper_ptp;
1450			return (SNMP_ERR_NOERROR);
1451
1452		case LEAF_begemotBridgeStpPortAdminPathCost:
1453			val->v.integer = bp->admin_path_cost;
1454			return (SNMP_ERR_NOERROR);
1455	}
1456
1457	abort();
1458}
1459
1460int
1461op_begemot_tp_port(struct snmp_context *c __unused, struct snmp_value *val,
1462	uint sub, uint iidx __unused, enum snmp_op op)
1463{
1464	struct bridge_port *bp;
1465
1466	if (time(NULL) - ports_list_age > bridge_get_data_maxage())
1467		bridge_update_all_ports();
1468
1469	switch (op) {
1470	    case SNMP_OP_GET:
1471		if ((bp = bridge_port_index_get(&val->var, sub, 0)) == NULL)
1472		    return (SNMP_ERR_NOSUCHNAME);
1473		goto get;
1474
1475	    case SNMP_OP_GETNEXT:
1476		if ((bp = bridge_port_index_getnext(&val->var, sub, 0)) ==
1477		    NULL || bridge_port_index_append(&val->var, sub, bp) < 0)
1478		    return (SNMP_ERR_NOSUCHNAME);
1479		goto get;
1480
1481	    case SNMP_OP_SET:
1482		return (SNMP_ERR_NOT_WRITEABLE);
1483
1484	    case SNMP_OP_ROLLBACK:
1485	    case SNMP_OP_COMMIT:
1486		break;
1487	}
1488	abort();
1489
1490get:
1491	switch (val->var.subs[sub - 1]) {
1492	    case LEAF_begemotBridgeTpPort:
1493		val->v.integer = bp->port_no;
1494		return (SNMP_ERR_NOERROR);
1495
1496	    case LEAF_begemotBridgeTpPortMaxInfo:
1497		val->v.integer = bp->max_info;
1498		return (SNMP_ERR_NOERROR);
1499
1500	    case LEAF_begemotBridgeTpPortInFrames:
1501		val->v.uint32 = bp->in_frames;
1502		return (SNMP_ERR_NOERROR);
1503
1504	    case LEAF_begemotBridgeTpPortOutFrames:
1505		val->v.uint32 = bp->out_frames;
1506		return (SNMP_ERR_NOERROR);
1507
1508	    case LEAF_begemotBridgeTpPortInDiscards:
1509		val->v.uint32 = bp->in_drops;
1510		return (SNMP_ERR_NOERROR);
1511	}
1512
1513	abort();
1514}
1515