xref: /freebsd/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_addrs.c (revision b3e7694832e81d7a904a10f525f8797b753bf0d3)
17850265eSShteryana Shopova /*-
2*4d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
31de7b4b8SPedro F. Giffuni  *
47850265eSShteryana Shopova  * Copyright (c) 2006 Shteryana Shopova <syrinx@FreeBSD.org>
57850265eSShteryana Shopova  * All rights reserved.
67850265eSShteryana Shopova  *
77850265eSShteryana Shopova  * Redistribution and use in source and binary forms, with or without
87850265eSShteryana Shopova  * modification, are permitted provided that the following conditions
97850265eSShteryana Shopova  * are met:
107850265eSShteryana Shopova  * 1. Redistributions of source code must retain the above copyright
117850265eSShteryana Shopova  *    notice, this list of conditions and the following disclaimer.
127850265eSShteryana Shopova  * 2. Redistributions in binary form must reproduce the above copyright
137850265eSShteryana Shopova  *    notice, this list of conditions and the following disclaimer in the
147850265eSShteryana Shopova  *    documentation and/or other materials provided with the distribution.
157850265eSShteryana Shopova  *
167850265eSShteryana Shopova  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
177850265eSShteryana Shopova  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
187850265eSShteryana Shopova  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
197850265eSShteryana Shopova  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
207850265eSShteryana Shopova  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
217850265eSShteryana Shopova  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
227850265eSShteryana Shopova  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
237850265eSShteryana Shopova  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
247850265eSShteryana Shopova  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
257850265eSShteryana Shopova  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
267850265eSShteryana Shopova  * SUCH DAMAGE.
277850265eSShteryana Shopova  *
287850265eSShteryana Shopova  * Bridge MIB implementation for SNMPd.
297850265eSShteryana Shopova  * Bridge addresses.
307850265eSShteryana Shopova  */
317850265eSShteryana Shopova 
327850265eSShteryana Shopova #include <sys/queue.h>
337850265eSShteryana Shopova #include <sys/socket.h>
347850265eSShteryana Shopova #include <sys/types.h>
357850265eSShteryana Shopova 
367850265eSShteryana Shopova #include <net/ethernet.h>
377850265eSShteryana Shopova #include <net/if.h>
387850265eSShteryana Shopova #include <net/if_mib.h>
397850265eSShteryana Shopova 
407850265eSShteryana Shopova #include <assert.h>
417850265eSShteryana Shopova #include <errno.h>
427850265eSShteryana Shopova #include <stdarg.h>
437850265eSShteryana Shopova #include <string.h>
447850265eSShteryana Shopova #include <stdlib.h>
457850265eSShteryana Shopova #include <syslog.h>
467850265eSShteryana Shopova 
477850265eSShteryana Shopova #include <bsnmp/snmpmod.h>
487850265eSShteryana Shopova #include <bsnmp/snmp_mibII.h>
497850265eSShteryana Shopova 
508e9b3e70SHartmut Brandt #define	SNMPTREE_TYPES
517850265eSShteryana Shopova #include "bridge_tree.h"
527850265eSShteryana Shopova #include "bridge_snmp.h"
537850265eSShteryana Shopova 
547850265eSShteryana Shopova TAILQ_HEAD(tp_entries, tp_entry);
557850265eSShteryana Shopova 
567850265eSShteryana Shopova /*
577850265eSShteryana Shopova  * Free the bridge address list.
587850265eSShteryana Shopova  */
597850265eSShteryana Shopova static void
bridge_tpe_free(struct tp_entries * headp)607850265eSShteryana Shopova bridge_tpe_free(struct tp_entries *headp)
617850265eSShteryana Shopova {
627850265eSShteryana Shopova 	struct tp_entry *t;
637850265eSShteryana Shopova 
647850265eSShteryana Shopova 	while ((t = TAILQ_FIRST(headp)) != NULL) {
657850265eSShteryana Shopova 		TAILQ_REMOVE(headp, t, tp_e);
667850265eSShteryana Shopova 		free(t);
677850265eSShteryana Shopova 	}
687850265eSShteryana Shopova }
697850265eSShteryana Shopova 
707850265eSShteryana Shopova /*
717850265eSShteryana Shopova  * Free the bridge address entries from the address list,
727850265eSShteryana Shopova  * for the specified bridge interface only.
737850265eSShteryana Shopova  */
747850265eSShteryana Shopova static void
bridge_tpe_bif_free(struct tp_entries * headp,struct bridge_if * bif)757850265eSShteryana Shopova bridge_tpe_bif_free(struct tp_entries *headp,
767850265eSShteryana Shopova 	struct bridge_if *bif)
777850265eSShteryana Shopova {
787850265eSShteryana Shopova 	struct tp_entry *tp;
797850265eSShteryana Shopova 
807850265eSShteryana Shopova 	while (bif->f_tpa != NULL && bif->sysindex == bif->f_tpa->sysindex) {
817850265eSShteryana Shopova 		tp = TAILQ_NEXT(bif->f_tpa, tp_e);
827850265eSShteryana Shopova 		TAILQ_REMOVE(headp, bif->f_tpa, tp_e);
837850265eSShteryana Shopova 		free(bif->f_tpa);
847850265eSShteryana Shopova 		bif->f_tpa = tp;
857850265eSShteryana Shopova 	}
867850265eSShteryana Shopova }
877850265eSShteryana Shopova 
887850265eSShteryana Shopova /*
897850265eSShteryana Shopova  * Compare two mac addresses.
907850265eSShteryana Shopova  * m1 < m2 : -1
917850265eSShteryana Shopova  * m1 > m2 : +1
927850265eSShteryana Shopova  * m1 = m2 :  0
937850265eSShteryana Shopova  */
947850265eSShteryana Shopova static int
bridge_compare_macs(const uint8_t * m1,const uint8_t * m2)957850265eSShteryana Shopova bridge_compare_macs(const uint8_t *m1, const uint8_t *m2)
967850265eSShteryana Shopova {
977850265eSShteryana Shopova 	int i;
987850265eSShteryana Shopova 
997850265eSShteryana Shopova 	for (i = 0; i < ETHER_ADDR_LEN; i++) {
1007850265eSShteryana Shopova 		if (m1[i] < m2[i])
1017850265eSShteryana Shopova 			return (-1);
1027850265eSShteryana Shopova 		if (m1[i] > m2[i])
1037850265eSShteryana Shopova 			return (1);
1047850265eSShteryana Shopova 	}
1057850265eSShteryana Shopova 
1067850265eSShteryana Shopova 	return (0);
1077850265eSShteryana Shopova }
1087850265eSShteryana Shopova 
1097850265eSShteryana Shopova /*
1107850265eSShteryana Shopova  * Insert an address entry in the bridge address TAILQ starting to search
1117850265eSShteryana Shopova  * for its place from the position of the first bridge address for the bridge
1123df5ecacSUlrich Spörlein  * interface. Update the first bridge address if necessary.
1137850265eSShteryana Shopova  */
1147850265eSShteryana Shopova static void
bridge_addrs_insert_at(struct tp_entries * headp,struct tp_entry * ta,struct tp_entry ** f_tpa)1157850265eSShteryana Shopova bridge_addrs_insert_at(struct tp_entries *headp,
1167850265eSShteryana Shopova 	struct tp_entry *ta, struct tp_entry **f_tpa)
1177850265eSShteryana Shopova {
1187850265eSShteryana Shopova 	struct tp_entry *t1;
1197850265eSShteryana Shopova 
1207850265eSShteryana Shopova 	assert(f_tpa != NULL);
1217850265eSShteryana Shopova 
1227850265eSShteryana Shopova 	for (t1 = *f_tpa;
1237850265eSShteryana Shopova 	    t1 != NULL && ta->sysindex == t1->sysindex;
1247850265eSShteryana Shopova 	    t1 = TAILQ_NEXT(t1, tp_e)) {
1257850265eSShteryana Shopova 		if (bridge_compare_macs(ta->tp_addr, t1->tp_addr) < 0) {
1267850265eSShteryana Shopova 			TAILQ_INSERT_BEFORE(t1, ta, tp_e);
1277850265eSShteryana Shopova 			if (*f_tpa == t1)
1287850265eSShteryana Shopova 				(*f_tpa) = ta;
1297850265eSShteryana Shopova 			return;
1307850265eSShteryana Shopova 		}
1317850265eSShteryana Shopova 	}
1327850265eSShteryana Shopova 
1337850265eSShteryana Shopova 	if (t1 == NULL)
1347850265eSShteryana Shopova 		TAILQ_INSERT_TAIL(headp, ta, tp_e);
1357850265eSShteryana Shopova 	else
1367850265eSShteryana Shopova 		TAILQ_INSERT_BEFORE(t1, ta, tp_e);
1377850265eSShteryana Shopova }
1387850265eSShteryana Shopova 
1397850265eSShteryana Shopova /*
1403df5ecacSUlrich Spörlein  * Find an address entry's position in the address list
1417850265eSShteryana Shopova  * according to bridge interface name.
1427850265eSShteryana Shopova  */
1437850265eSShteryana Shopova static struct tp_entry *
bridge_addrs_find_pos(struct tp_entries * headp,uint32_t b_idx)1447850265eSShteryana Shopova bridge_addrs_find_pos(struct tp_entries *headp, uint32_t b_idx)
1457850265eSShteryana Shopova {
1467850265eSShteryana Shopova 	uint32_t t_idx;
1477850265eSShteryana Shopova 	struct tp_entry *t1;
1487850265eSShteryana Shopova 
1497850265eSShteryana Shopova 	if ((t1 = TAILQ_FIRST(headp)) == NULL ||
1507850265eSShteryana Shopova 	    bridge_compare_sysidx(b_idx, t1->sysindex) < 0)
1517850265eSShteryana Shopova 		return (NULL);
1527850265eSShteryana Shopova 
1537850265eSShteryana Shopova 	t_idx = t1->sysindex;
1547850265eSShteryana Shopova 
1557850265eSShteryana Shopova 	for (t1 = TAILQ_NEXT(t1, tp_e); t1 != NULL; t1 = TAILQ_NEXT(t1, tp_e)) {
1567850265eSShteryana Shopova 
1577850265eSShteryana Shopova 		if (t1->sysindex != t_idx) {
1587850265eSShteryana Shopova 			if (bridge_compare_sysidx(b_idx, t1->sysindex) < 0)
1597850265eSShteryana Shopova 				return (TAILQ_PREV(t1, tp_entries, tp_e));
1607850265eSShteryana Shopova 			else
1617850265eSShteryana Shopova 				t_idx = t1->sysindex;
1627850265eSShteryana Shopova 		}
1637850265eSShteryana Shopova 	}
1647850265eSShteryana Shopova 
1657850265eSShteryana Shopova 	if (t1 == NULL)
1667850265eSShteryana Shopova 		t1 = TAILQ_LAST(headp, tp_entries);
1677850265eSShteryana Shopova 
1687850265eSShteryana Shopova 	return (t1);
1697850265eSShteryana Shopova }
1707850265eSShteryana Shopova 
1717850265eSShteryana Shopova /*
1727850265eSShteryana Shopova  * Insert a bridge address in the bridge addresses list.
1737850265eSShteryana Shopova  */
1747850265eSShteryana Shopova static void
bridge_addrs_bif_insert(struct tp_entries * headp,struct tp_entry * te,struct tp_entry ** f_tpa)1757850265eSShteryana Shopova bridge_addrs_bif_insert(struct tp_entries *headp, struct tp_entry *te,
1767850265eSShteryana Shopova     struct tp_entry **f_tpa)
1777850265eSShteryana Shopova {
1787850265eSShteryana Shopova 	struct tp_entry *temp;
1797850265eSShteryana Shopova 
1807850265eSShteryana Shopova 	if (*f_tpa != NULL)
1817850265eSShteryana Shopova 		bridge_addrs_insert_at(headp, te, f_tpa);
1827850265eSShteryana Shopova 	else {
1837850265eSShteryana Shopova 		temp = bridge_addrs_find_pos(headp, te->sysindex);
1847850265eSShteryana Shopova 
1857850265eSShteryana Shopova 		if (temp == NULL)
1867850265eSShteryana Shopova 			TAILQ_INSERT_HEAD(headp, te, tp_e);
1877850265eSShteryana Shopova 		else
1887850265eSShteryana Shopova 			TAILQ_INSERT_AFTER(headp, temp, te, tp_e);
1897850265eSShteryana Shopova 		*f_tpa = te;
1907850265eSShteryana Shopova 	}
1917850265eSShteryana Shopova }
1927850265eSShteryana Shopova 
1937850265eSShteryana Shopova static struct tp_entries tp_entries = TAILQ_HEAD_INITIALIZER(tp_entries);
1947850265eSShteryana Shopova static time_t address_list_age;
1957850265eSShteryana Shopova 
1967850265eSShteryana Shopova void
bridge_addrs_update_listage(void)1977850265eSShteryana Shopova bridge_addrs_update_listage(void)
1987850265eSShteryana Shopova {
1997850265eSShteryana Shopova 	address_list_age = time(NULL);
2007850265eSShteryana Shopova }
2017850265eSShteryana Shopova 
2027850265eSShteryana Shopova void
bridge_addrs_fini(void)2037850265eSShteryana Shopova bridge_addrs_fini(void)
2047850265eSShteryana Shopova {
2057850265eSShteryana Shopova 	bridge_tpe_free(&tp_entries);
2067850265eSShteryana Shopova }
2077850265eSShteryana Shopova 
2087850265eSShteryana Shopova void
bridge_addrs_free(struct bridge_if * bif)2097850265eSShteryana Shopova bridge_addrs_free(struct bridge_if *bif)
2107850265eSShteryana Shopova {
2117850265eSShteryana Shopova 	bridge_tpe_bif_free(&tp_entries, bif);
2127850265eSShteryana Shopova }
2137850265eSShteryana Shopova 
2147850265eSShteryana Shopova /*
2157850265eSShteryana Shopova  * Find the first address in the list.
2167850265eSShteryana Shopova  */
2177850265eSShteryana Shopova static struct tp_entry *
bridge_addrs_first(void)2187850265eSShteryana Shopova bridge_addrs_first(void)
2197850265eSShteryana Shopova {
2207850265eSShteryana Shopova 	return (TAILQ_FIRST(&tp_entries));
2217850265eSShteryana Shopova }
2227850265eSShteryana Shopova 
2237850265eSShteryana Shopova /*
2247850265eSShteryana Shopova  * Find the next address in the list.
2257850265eSShteryana Shopova  */
2267850265eSShteryana Shopova static struct tp_entry *
bridge_addrs_next(struct tp_entry * te)2277850265eSShteryana Shopova bridge_addrs_next(struct tp_entry *te)
2287850265eSShteryana Shopova {
2297850265eSShteryana Shopova 	return (TAILQ_NEXT(te, tp_e));
2307850265eSShteryana Shopova }
2317850265eSShteryana Shopova 
2327850265eSShteryana Shopova /*
2337850265eSShteryana Shopova  * Find the first address, learnt by the specified bridge interface.
2347850265eSShteryana Shopova  */
2357850265eSShteryana Shopova struct tp_entry *
bridge_addrs_bif_first(struct bridge_if * bif)2367850265eSShteryana Shopova bridge_addrs_bif_first(struct bridge_if *bif)
2377850265eSShteryana Shopova {
2387850265eSShteryana Shopova 	return (bif->f_tpa);
2397850265eSShteryana Shopova }
2407850265eSShteryana Shopova 
2417850265eSShteryana Shopova /*
2427850265eSShteryana Shopova  * Find the next address, learnt by the specified bridge interface.
2437850265eSShteryana Shopova  */
2447850265eSShteryana Shopova struct tp_entry *
bridge_addrs_bif_next(struct tp_entry * te)2457850265eSShteryana Shopova bridge_addrs_bif_next(struct tp_entry *te)
2467850265eSShteryana Shopova {
2477850265eSShteryana Shopova 	struct tp_entry *te_next;
2487850265eSShteryana Shopova 
2497850265eSShteryana Shopova 	if ((te_next = TAILQ_NEXT(te, tp_e)) == NULL ||
2507850265eSShteryana Shopova 	    te_next->sysindex != te->sysindex)
2517850265eSShteryana Shopova 		return (NULL);
2527850265eSShteryana Shopova 
2537850265eSShteryana Shopova 	return (te_next);
2547850265eSShteryana Shopova }
2557850265eSShteryana Shopova 
2567850265eSShteryana Shopova /*
2577850265eSShteryana Shopova  * Remove a bridge address from the list.
2587850265eSShteryana Shopova  */
2597850265eSShteryana Shopova void
bridge_addrs_remove(struct tp_entry * te,struct bridge_if * bif)2607850265eSShteryana Shopova bridge_addrs_remove(struct tp_entry *te, struct bridge_if *bif)
2617850265eSShteryana Shopova {
2627850265eSShteryana Shopova 	if (bif->f_tpa == te)
2637850265eSShteryana Shopova 		bif->f_tpa = bridge_addrs_bif_next(te);
2647850265eSShteryana Shopova 
2657850265eSShteryana Shopova 	TAILQ_REMOVE(&tp_entries, te, tp_e);
2667850265eSShteryana Shopova 	free(te);
2677850265eSShteryana Shopova }
2687850265eSShteryana Shopova 
2697850265eSShteryana Shopova /*
2707850265eSShteryana Shopova  * Allocate memory for a new bridge address and insert it in the list.
2717850265eSShteryana Shopova  */
2727850265eSShteryana Shopova struct tp_entry *
bridge_new_addrs(uint8_t * mac,struct bridge_if * bif)2737850265eSShteryana Shopova bridge_new_addrs(uint8_t *mac, struct bridge_if *bif)
2747850265eSShteryana Shopova {
2757850265eSShteryana Shopova 	struct tp_entry *te;
2767850265eSShteryana Shopova 
2777850265eSShteryana Shopova 	if ((te = (struct tp_entry *) malloc(sizeof(*te))) == NULL) {
2787850265eSShteryana Shopova 		syslog(LOG_ERR, "bridge new address: failed: %s",
2797850265eSShteryana Shopova 		    strerror(errno));
2807850265eSShteryana Shopova 		return (NULL);
2817850265eSShteryana Shopova 	}
2827850265eSShteryana Shopova 
2837850265eSShteryana Shopova 	bzero(te, sizeof(*te));
2847850265eSShteryana Shopova 
2857850265eSShteryana Shopova 	te->sysindex = bif->sysindex;
2867850265eSShteryana Shopova 	bcopy(mac, te->tp_addr, ETHER_ADDR_LEN);
2877850265eSShteryana Shopova 	bridge_addrs_bif_insert(&tp_entries, te, &(bif->f_tpa));
2887850265eSShteryana Shopova 
2897850265eSShteryana Shopova 	return (te);
2907850265eSShteryana Shopova }
2917850265eSShteryana Shopova 
2927850265eSShteryana Shopova /*
2937850265eSShteryana Shopova  * Given a mac address, learnt on a bridge,
2947850265eSShteryana Shopova  * find the corrsponding TP entry for it.
2957850265eSShteryana Shopova  */
2967850265eSShteryana Shopova struct tp_entry *
bridge_addrs_find(uint8_t * mac,struct bridge_if * bif)2977850265eSShteryana Shopova bridge_addrs_find(uint8_t *mac, struct bridge_if *bif)
2987850265eSShteryana Shopova {
2997850265eSShteryana Shopova 	struct tp_entry *te;
3007850265eSShteryana Shopova 
3017850265eSShteryana Shopova 	for (te = bif->f_tpa; te != NULL; te = TAILQ_NEXT(te, tp_e)) {
3027850265eSShteryana Shopova 		if (te->sysindex != bif->sysindex) {
3037850265eSShteryana Shopova 			te = NULL;
3047850265eSShteryana Shopova 			break;
3057850265eSShteryana Shopova 		}
3067850265eSShteryana Shopova 
3077850265eSShteryana Shopova 		if (bridge_compare_macs(te->tp_addr, mac) == 0)
3087850265eSShteryana Shopova 			break;
3097850265eSShteryana Shopova 	}
3107850265eSShteryana Shopova 
3117850265eSShteryana Shopova 	return (te);
3127850265eSShteryana Shopova }
3137850265eSShteryana Shopova 
3147850265eSShteryana Shopova void
bridge_addrs_dump(struct bridge_if * bif)3157850265eSShteryana Shopova bridge_addrs_dump(struct bridge_if *bif)
3167850265eSShteryana Shopova {
3177850265eSShteryana Shopova 	struct tp_entry *te;
3187850265eSShteryana Shopova 
3197850265eSShteryana Shopova 	syslog(LOG_ERR, "Addresses count - %d", bif->num_addrs);
3207850265eSShteryana Shopova 	for (te = bridge_addrs_bif_first(bif); te != NULL;
3217850265eSShteryana Shopova 	    te = bridge_addrs_bif_next(te)) {
3227850265eSShteryana Shopova 		syslog(LOG_ERR, "address %x:%x:%x:%x:%x:%x on port %d.%d",
3237850265eSShteryana Shopova 		    te->tp_addr[0], te->tp_addr[1], te->tp_addr[2],
3247850265eSShteryana Shopova 		    te->tp_addr[3], te->tp_addr[4], te->tp_addr[5],
3257850265eSShteryana Shopova 		    te->sysindex, te->port_no);
3267850265eSShteryana Shopova 	}
3277850265eSShteryana Shopova }
3287850265eSShteryana Shopova 
3297850265eSShteryana Shopova /*
3307850265eSShteryana Shopova  * RFC4188 specifics.
3317850265eSShteryana Shopova  */
3327850265eSShteryana Shopova 
3337850265eSShteryana Shopova /*
3347850265eSShteryana Shopova  * Construct the SNMP index from the address DST Mac.
3357850265eSShteryana Shopova  */
3367850265eSShteryana Shopova static void
bridge_addrs_index_append(struct asn_oid * oid,uint sub,const struct tp_entry * te)3377850265eSShteryana Shopova bridge_addrs_index_append(struct asn_oid *oid, uint sub,
3387850265eSShteryana Shopova 	const struct tp_entry *te)
3397850265eSShteryana Shopova {
3407850265eSShteryana Shopova 	int i;
3417850265eSShteryana Shopova 
3427850265eSShteryana Shopova 	oid->len = sub + ETHER_ADDR_LEN + 1;
3437850265eSShteryana Shopova 	oid->subs[sub] = ETHER_ADDR_LEN;
3447850265eSShteryana Shopova 
3457850265eSShteryana Shopova 	for (i = 1; i <= ETHER_ADDR_LEN; i++)
3467850265eSShteryana Shopova 		oid->subs[sub + i] = te->tp_addr[i - 1];
3477850265eSShteryana Shopova }
3487850265eSShteryana Shopova 
3497850265eSShteryana Shopova /*
3507850265eSShteryana Shopova  * Find the address entry for the SNMP index from the default bridge only.
3517850265eSShteryana Shopova  */
3527850265eSShteryana Shopova static struct tp_entry *
bridge_addrs_get(const struct asn_oid * oid,uint sub,struct bridge_if * bif)3537850265eSShteryana Shopova bridge_addrs_get(const struct asn_oid *oid, uint sub,
3547850265eSShteryana Shopova 	struct bridge_if *bif)
3557850265eSShteryana Shopova {
3567850265eSShteryana Shopova 	int i;
3577850265eSShteryana Shopova 	uint8_t tp_addr[ETHER_ADDR_LEN];
3587850265eSShteryana Shopova 
3597850265eSShteryana Shopova 	if (oid->len - sub != ETHER_ADDR_LEN + 1 ||
3607850265eSShteryana Shopova 	    oid->subs[sub] != ETHER_ADDR_LEN)
3617850265eSShteryana Shopova 		return (NULL);
3627850265eSShteryana Shopova 
3637850265eSShteryana Shopova 	for (i = 0; i < ETHER_ADDR_LEN; i++)
3647850265eSShteryana Shopova 		tp_addr[i] = oid->subs[sub + i + 1];
3657850265eSShteryana Shopova 
3667850265eSShteryana Shopova 	return (bridge_addrs_find(tp_addr, bif));
3677850265eSShteryana Shopova }
3687850265eSShteryana Shopova 
3697850265eSShteryana Shopova /*
3707850265eSShteryana Shopova  * Find the next address entry for the SNMP index
3717850265eSShteryana Shopova  * from the default bridge only.
3727850265eSShteryana Shopova  */
3737850265eSShteryana Shopova static struct tp_entry *
bridge_addrs_getnext(const struct asn_oid * oid,uint sub,struct bridge_if * bif)3747850265eSShteryana Shopova bridge_addrs_getnext(const struct asn_oid *oid, uint sub,
3757850265eSShteryana Shopova 	struct bridge_if *bif)
3767850265eSShteryana Shopova {
3777850265eSShteryana Shopova 	int i;
3787850265eSShteryana Shopova 	uint8_t tp_addr[ETHER_ADDR_LEN];
3797850265eSShteryana Shopova 	static struct tp_entry *te;
3807850265eSShteryana Shopova 
3817850265eSShteryana Shopova 	if (oid->len - sub == 0)
3827850265eSShteryana Shopova 		return (bridge_addrs_bif_first(bif));
3837850265eSShteryana Shopova 
3847850265eSShteryana Shopova 	if (oid->len - sub != ETHER_ADDR_LEN + 1 ||
3857850265eSShteryana Shopova 	    oid->subs[sub] != ETHER_ADDR_LEN)
3867850265eSShteryana Shopova 		return (NULL);
3877850265eSShteryana Shopova 
3887850265eSShteryana Shopova 	for (i = 0; i < ETHER_ADDR_LEN; i++)
3897850265eSShteryana Shopova 		tp_addr[i] = oid->subs[sub + i + 1];
3907850265eSShteryana Shopova 
3917850265eSShteryana Shopova 	if ((te = bridge_addrs_find(tp_addr, bif)) == NULL)
3927850265eSShteryana Shopova 		return (NULL);
3937850265eSShteryana Shopova 
3947850265eSShteryana Shopova 	return (bridge_addrs_bif_next(te));
3957850265eSShteryana Shopova }
3967850265eSShteryana Shopova 
3977850265eSShteryana Shopova int
op_dot1d_tp_fdb(struct snmp_context * c __unused,struct snmp_value * val,uint sub,uint iidx __unused,enum snmp_op op)3987850265eSShteryana Shopova op_dot1d_tp_fdb(struct snmp_context *c __unused, struct snmp_value *val,
3997850265eSShteryana Shopova 	uint sub, uint iidx __unused, enum snmp_op op)
4007850265eSShteryana Shopova {
4017850265eSShteryana Shopova 	struct bridge_if *bif;
4027850265eSShteryana Shopova 	struct tp_entry *te;
4037850265eSShteryana Shopova 
4047850265eSShteryana Shopova 	if ((bif = bridge_get_default()) == NULL)
4057850265eSShteryana Shopova 		return (SNMP_ERR_NOSUCHNAME);
4067850265eSShteryana Shopova 
4077850265eSShteryana Shopova 	if (time(NULL) - bif->addrs_age > bridge_get_data_maxage() &&
4087850265eSShteryana Shopova 	    bridge_update_addrs(bif) <= 0)
4097850265eSShteryana Shopova 		return (SNMP_ERR_NOSUCHNAME);
4107850265eSShteryana Shopova 
4117850265eSShteryana Shopova 	switch (op) {
4127850265eSShteryana Shopova 	    case SNMP_OP_GET:
4137850265eSShteryana Shopova 		if ((te = bridge_addrs_get(&val->var, sub, bif)) == NULL)
4147850265eSShteryana Shopova 			return (SNMP_ERR_NOSUCHNAME);
415f02a1e57SShteryana Shopova 		goto get;
4167850265eSShteryana Shopova 
4177850265eSShteryana Shopova 	    case SNMP_OP_GETNEXT:
4187850265eSShteryana Shopova 		if ((te = bridge_addrs_getnext(&val->var, sub, bif)) == NULL)
4197850265eSShteryana Shopova 			return (SNMP_ERR_NOSUCHNAME);
4207850265eSShteryana Shopova 		bridge_addrs_index_append(&val->var, sub, te);
421f02a1e57SShteryana Shopova 		goto get;
4227850265eSShteryana Shopova 
4237850265eSShteryana Shopova 	    case SNMP_OP_SET:
4247850265eSShteryana Shopova 		return (SNMP_ERR_NOT_WRITEABLE);
4257850265eSShteryana Shopova 
4267850265eSShteryana Shopova 	    case SNMP_OP_ROLLBACK:
4277850265eSShteryana Shopova 	    case SNMP_OP_COMMIT:
428f02a1e57SShteryana Shopova 		break;
4297850265eSShteryana Shopova 	}
430f02a1e57SShteryana Shopova 	abort();
4317850265eSShteryana Shopova 
432f02a1e57SShteryana Shopova get:
4337850265eSShteryana Shopova 	switch (val->var.subs[sub - 1]) {
4347850265eSShteryana Shopova 		case LEAF_dot1dTpFdbAddress:
435f02a1e57SShteryana Shopova 			return (string_get(val, te->tp_addr, ETHER_ADDR_LEN));
4367850265eSShteryana Shopova 		case LEAF_dot1dTpFdbPort :
4377850265eSShteryana Shopova 			val->v.integer = te->port_no;
438f02a1e57SShteryana Shopova 			return (SNMP_ERR_NOERROR);
4397850265eSShteryana Shopova 		case LEAF_dot1dTpFdbStatus:
4407850265eSShteryana Shopova 			val->v.integer = te->status;
441f02a1e57SShteryana Shopova 			return (SNMP_ERR_NOERROR);
4427850265eSShteryana Shopova 	}
4437850265eSShteryana Shopova 
444f02a1e57SShteryana Shopova 	abort();
4457850265eSShteryana Shopova }
4467850265eSShteryana Shopova 
4477850265eSShteryana Shopova /*
4487850265eSShteryana Shopova  * Private BEGEMOT-BRIDGE-MIB specifics.
4497850265eSShteryana Shopova  */
4507850265eSShteryana Shopova 
4517850265eSShteryana Shopova /*
4527850265eSShteryana Shopova  * Construct the SNMP index from the bridge interface name
4537850265eSShteryana Shopova  * and the address DST Mac.
4547850265eSShteryana Shopova  */
4557850265eSShteryana Shopova static int
bridge_addrs_begemot_index_append(struct asn_oid * oid,uint sub,const struct tp_entry * te)4567850265eSShteryana Shopova bridge_addrs_begemot_index_append(struct asn_oid *oid, uint sub,
4577850265eSShteryana Shopova 	const struct tp_entry *te)
4587850265eSShteryana Shopova {
4597850265eSShteryana Shopova 	uint i, n_len;
4607850265eSShteryana Shopova 	const char *b_name;
4617850265eSShteryana Shopova 
4627850265eSShteryana Shopova 	if ((b_name = bridge_if_find_name(te->sysindex)) == NULL)
4637850265eSShteryana Shopova 		return (-1);
4647850265eSShteryana Shopova 
4657850265eSShteryana Shopova 	n_len = strlen(b_name);
4667850265eSShteryana Shopova 	oid->len = sub++;
4677850265eSShteryana Shopova 	oid->subs[oid->len++] = n_len;
4687850265eSShteryana Shopova 
4697850265eSShteryana Shopova 	for (i = 1; i <= n_len; i++)
4707850265eSShteryana Shopova 		oid->subs[oid->len++] = b_name[i - 1];
4717850265eSShteryana Shopova 
4727850265eSShteryana Shopova 	oid->subs[oid->len++] = ETHER_ADDR_LEN;
4737850265eSShteryana Shopova 	for (i = 1 ; i <= ETHER_ADDR_LEN; i++)
4747850265eSShteryana Shopova 		oid->subs[oid->len++] = te->tp_addr[i - 1];
4757850265eSShteryana Shopova 
4767850265eSShteryana Shopova 	return (0);
4777850265eSShteryana Shopova }
4787850265eSShteryana Shopova 
4797850265eSShteryana Shopova /*
4807850265eSShteryana Shopova  * Find a bridge address entry by the bridge interface name
4817850265eSShteryana Shopova  * and the address DST Mac.
4827850265eSShteryana Shopova  */
4837850265eSShteryana Shopova static struct tp_entry *
bridge_addrs_begemot_get(const struct asn_oid * oid,uint sub)4847850265eSShteryana Shopova bridge_addrs_begemot_get(const struct asn_oid *oid, uint sub)
4857850265eSShteryana Shopova {
4867850265eSShteryana Shopova 	uint i, n_len;
4877850265eSShteryana Shopova 	uint8_t tp_addr[ETHER_ADDR_LEN];
4887850265eSShteryana Shopova 	char bif_name[IFNAMSIZ];
4897850265eSShteryana Shopova 	struct bridge_if *bif;
4907850265eSShteryana Shopova 
4917850265eSShteryana Shopova 	n_len = oid->subs[sub];
4927850265eSShteryana Shopova 	if (oid->len - sub != n_len + ETHER_ADDR_LEN + 3 ||
4937850265eSShteryana Shopova 	    n_len >= IFNAMSIZ || oid->subs[sub + n_len + 1] != ETHER_ADDR_LEN)
4947850265eSShteryana Shopova 		return (NULL);
4957850265eSShteryana Shopova 
4967850265eSShteryana Shopova 	for (i = 0; i < n_len; i++)
4977850265eSShteryana Shopova 		bif_name[i] = oid->subs[n_len + i + 1];
4987850265eSShteryana Shopova 	bif_name[i] = '\0';
4997850265eSShteryana Shopova 
5007850265eSShteryana Shopova 	for (i = 1; i <= ETHER_ADDR_LEN; i++)
5017850265eSShteryana Shopova 		tp_addr[i - 1] = oid->subs[n_len + i + 1];
5027850265eSShteryana Shopova 
5037850265eSShteryana Shopova 	if ((bif = bridge_if_find_ifname(bif_name)) == NULL)
5047850265eSShteryana Shopova 		return (NULL);
5057850265eSShteryana Shopova 
5067850265eSShteryana Shopova 	return (bridge_addrs_find(tp_addr, bif));
5077850265eSShteryana Shopova }
5087850265eSShteryana Shopova 
5097850265eSShteryana Shopova /*
5107850265eSShteryana Shopova  * Find the next bridge address entry by the bridge interface name
5117850265eSShteryana Shopova  * and the address DST Mac.
5127850265eSShteryana Shopova  */
5137850265eSShteryana Shopova static struct tp_entry *
bridge_addrs_begemot_getnext(const struct asn_oid * oid,uint sub)5147850265eSShteryana Shopova bridge_addrs_begemot_getnext(const struct asn_oid *oid, uint sub)
5157850265eSShteryana Shopova {
5167850265eSShteryana Shopova 	uint i, n_len;
5177850265eSShteryana Shopova 	uint8_t tp_addr[ETHER_ADDR_LEN];
5187850265eSShteryana Shopova 	char bif_name[IFNAMSIZ];
5197850265eSShteryana Shopova 	struct bridge_if *bif;
5207850265eSShteryana Shopova 	struct tp_entry *tp;
5217850265eSShteryana Shopova 
5227850265eSShteryana Shopova 	if (oid->len - sub == 0)
5237850265eSShteryana Shopova 		return (bridge_addrs_first());
5247850265eSShteryana Shopova 
5257850265eSShteryana Shopova 	n_len = oid->subs[sub];
5267850265eSShteryana Shopova 	if (oid->len - sub != n_len + ETHER_ADDR_LEN + 2 ||
5277850265eSShteryana Shopova 	    n_len >= IFNAMSIZ || oid->subs[sub + n_len + 1] != ETHER_ADDR_LEN)
5287850265eSShteryana Shopova 		return (NULL);
5297850265eSShteryana Shopova 
5307850265eSShteryana Shopova 	for (i = 1; i <= n_len; i++)
5317850265eSShteryana Shopova 		bif_name[i - 1] = oid->subs[sub + i];
5327850265eSShteryana Shopova 
5337850265eSShteryana Shopova 	bif_name[i - 1] = '\0';
5347850265eSShteryana Shopova 
5357850265eSShteryana Shopova 	for (i = 1; i <= ETHER_ADDR_LEN; i++)
5367850265eSShteryana Shopova 		tp_addr[i - 1] = oid->subs[sub + n_len + i + 1];
5377850265eSShteryana Shopova 
5387850265eSShteryana Shopova 	if ((bif = bridge_if_find_ifname(bif_name)) == NULL ||
5397850265eSShteryana Shopova 	    (tp = bridge_addrs_find(tp_addr, bif)) == NULL)
5407850265eSShteryana Shopova 		return (NULL);
5417850265eSShteryana Shopova 
5427850265eSShteryana Shopova 	return (bridge_addrs_next(tp));
5437850265eSShteryana Shopova }
5447850265eSShteryana Shopova 
5457850265eSShteryana Shopova int
op_begemot_tp_fdb(struct snmp_context * c __unused,struct snmp_value * val,uint sub,uint iidx __unused,enum snmp_op op)5467850265eSShteryana Shopova op_begemot_tp_fdb(struct snmp_context *c __unused, struct snmp_value *val,
5477850265eSShteryana Shopova 	uint sub, uint iidx __unused, enum snmp_op op)
5487850265eSShteryana Shopova {
5495b6598a8SShteryana Shopova 	struct tp_entry *te;
5507850265eSShteryana Shopova 
5517850265eSShteryana Shopova 	if (time(NULL) - address_list_age > bridge_get_data_maxage())
5527850265eSShteryana Shopova 		bridge_update_all_addrs();
5537850265eSShteryana Shopova 
5547850265eSShteryana Shopova 	switch (op) {
5557850265eSShteryana Shopova 	    case SNMP_OP_GET:
5567850265eSShteryana Shopova 		if ((te = bridge_addrs_begemot_get(&val->var, sub)) == NULL)
5577850265eSShteryana Shopova 		    return (SNMP_ERR_NOSUCHNAME);
558f02a1e57SShteryana Shopova 		goto get;
5597850265eSShteryana Shopova 
5607850265eSShteryana Shopova 	    case SNMP_OP_GETNEXT:
5617850265eSShteryana Shopova 		if ((te = bridge_addrs_begemot_getnext(&val->var,
5627850265eSShteryana Shopova 		    sub)) == NULL ||
5637850265eSShteryana Shopova 		    bridge_addrs_begemot_index_append(&val->var,
5647850265eSShteryana Shopova 		    sub, te) < 0)
5657850265eSShteryana Shopova 			return (SNMP_ERR_NOSUCHNAME);
566f02a1e57SShteryana Shopova 		goto get;
5677850265eSShteryana Shopova 
5687850265eSShteryana Shopova 	    case SNMP_OP_SET:
5697850265eSShteryana Shopova 		return (SNMP_ERR_NOT_WRITEABLE);
5707850265eSShteryana Shopova 
5717850265eSShteryana Shopova 	    case SNMP_OP_ROLLBACK:
5727850265eSShteryana Shopova 	    case SNMP_OP_COMMIT:
573f02a1e57SShteryana Shopova 		break;
5747850265eSShteryana Shopova 	}
575f02a1e57SShteryana Shopova 	abort();
5767850265eSShteryana Shopova 
577f02a1e57SShteryana Shopova get:
5787850265eSShteryana Shopova 	switch (val->var.subs[sub - 1]) {
5797850265eSShteryana Shopova 	    case LEAF_begemotBridgeTpFdbAddress:
580f02a1e57SShteryana Shopova 		return (string_get(val, te->tp_addr, ETHER_ADDR_LEN));
5817850265eSShteryana Shopova 	    case LEAF_begemotBridgeTpFdbPort:
5827850265eSShteryana Shopova 		val->v.integer = te->port_no;
583f02a1e57SShteryana Shopova 		return (SNMP_ERR_NOERROR);
5847850265eSShteryana Shopova 	    case LEAF_begemotBridgeTpFdbStatus:
5857850265eSShteryana Shopova 		val->v.integer = te->status;
586f02a1e57SShteryana Shopova 		return (SNMP_ERR_NOERROR);
5877850265eSShteryana Shopova 	}
5887850265eSShteryana Shopova 
589f02a1e57SShteryana Shopova 	abort();
5907850265eSShteryana Shopova }
591