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