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