diff options
Diffstat (limited to 'usr.sbin/bsnmpd/modules/snmp_bridge/bridge_addrs.c')
| -rw-r--r-- | usr.sbin/bsnmpd/modules/snmp_bridge/bridge_addrs.c | 590 | 
1 files changed, 590 insertions, 0 deletions
| diff --git a/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_addrs.c b/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_addrs.c new file mode 100644 index 000000000000..47475b693685 --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_addrs.c @@ -0,0 +1,590 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2006 Shteryana Shopova <syrinx@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + *    notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + *    notice, this list of conditions and the following disclaimer in the + *    documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Bridge MIB implementation for SNMPd. + * Bridge addresses. + */ + +#include <sys/queue.h> +#include <sys/socket.h> +#include <sys/types.h> + +#include <net/ethernet.h> +#include <net/if.h> +#include <net/if_mib.h> + +#include <assert.h> +#include <errno.h> +#include <stdarg.h> +#include <string.h> +#include <stdlib.h> +#include <syslog.h> + +#include <bsnmp/snmpmod.h> +#include <bsnmp/snmp_mibII.h> + +#define	SNMPTREE_TYPES +#include "bridge_tree.h" +#include "bridge_snmp.h" + +TAILQ_HEAD(tp_entries, tp_entry); + +/* + * Free the bridge address list. + */ +static void +bridge_tpe_free(struct tp_entries *headp) +{ +	struct tp_entry *t; + +	while ((t = TAILQ_FIRST(headp)) != NULL) { +		TAILQ_REMOVE(headp, t, tp_e); +		free(t); +	} +} + +/* + * Free the bridge address entries from the address list, + * for the specified bridge interface only. + */ +static void +bridge_tpe_bif_free(struct tp_entries *headp, +	struct bridge_if *bif) +{ +	struct tp_entry *tp; + +	while (bif->f_tpa != NULL && bif->sysindex == bif->f_tpa->sysindex) { +		tp = TAILQ_NEXT(bif->f_tpa, tp_e); +		TAILQ_REMOVE(headp, bif->f_tpa, tp_e); +		free(bif->f_tpa); +		bif->f_tpa = tp; +	} +} + +/* + * Compare two mac addresses. + * m1 < m2 : -1 + * m1 > m2 : +1 + * m1 = m2 :  0 + */ +static int +bridge_compare_macs(const uint8_t *m1, const uint8_t *m2) +{ +	int i; + +	for (i = 0; i < ETHER_ADDR_LEN; i++) { +		if (m1[i] < m2[i]) +			return (-1); +		if (m1[i] > m2[i]) +			return (1); +	} + +	return (0); +} + +/* + * Insert an address entry in the bridge address TAILQ starting to search + * for its place from the position of the first bridge address for the bridge + * interface. Update the first bridge address if necessary. + */ +static void +bridge_addrs_insert_at(struct tp_entries *headp, +	struct tp_entry *ta, struct tp_entry **f_tpa) +{ +	struct tp_entry *t1; + +	assert(f_tpa != NULL); + +	for (t1 = *f_tpa; +	    t1 != NULL && ta->sysindex == t1->sysindex; +	    t1 = TAILQ_NEXT(t1, tp_e)) { +		if (bridge_compare_macs(ta->tp_addr, t1->tp_addr) < 0) { +			TAILQ_INSERT_BEFORE(t1, ta, tp_e); +			if (*f_tpa == t1) +				(*f_tpa) = ta; +			return; +		} +	} + +	if (t1 == NULL) +		TAILQ_INSERT_TAIL(headp, ta, tp_e); +	else +		TAILQ_INSERT_BEFORE(t1, ta, tp_e); +} + +/* + * Find an address entry's position in the address list + * according to bridge interface name. + */ +static struct tp_entry * +bridge_addrs_find_pos(struct tp_entries *headp, uint32_t b_idx) +{ +	uint32_t t_idx; +	struct tp_entry *t1; + +	if ((t1 = TAILQ_FIRST(headp)) == NULL || +	    bridge_compare_sysidx(b_idx, t1->sysindex) < 0) +		return (NULL); + +	t_idx = t1->sysindex; + +	for (t1 = TAILQ_NEXT(t1, tp_e); t1 != NULL; t1 = TAILQ_NEXT(t1, tp_e)) { + +		if (t1->sysindex != t_idx) { +			if (bridge_compare_sysidx(b_idx, t1->sysindex) < 0) +				return (TAILQ_PREV(t1, tp_entries, tp_e)); +			else +				t_idx = t1->sysindex; +		} +	} + +	if (t1 == NULL) +		t1 = TAILQ_LAST(headp, tp_entries); + +	return (t1); +} + +/* + * Insert a bridge address in the bridge addresses list. + */ +static void +bridge_addrs_bif_insert(struct tp_entries *headp, struct tp_entry *te, +    struct tp_entry **f_tpa) +{ +	struct tp_entry *temp; + +	if (*f_tpa != NULL) +		bridge_addrs_insert_at(headp, te, f_tpa); +	else { +		temp = bridge_addrs_find_pos(headp, te->sysindex); + +		if (temp == NULL) +			TAILQ_INSERT_HEAD(headp, te, tp_e); +		else +			TAILQ_INSERT_AFTER(headp, temp, te, tp_e); +		*f_tpa = te; +	} +} + +static struct tp_entries tp_entries = TAILQ_HEAD_INITIALIZER(tp_entries); +static time_t address_list_age; + +void +bridge_addrs_update_listage(void) +{ +	address_list_age = time(NULL); +} + +void +bridge_addrs_fini(void) +{ +	bridge_tpe_free(&tp_entries); +} + +void +bridge_addrs_free(struct bridge_if *bif) +{ +	bridge_tpe_bif_free(&tp_entries, bif); +} + +/* + * Find the first address in the list. + */ +static struct tp_entry * +bridge_addrs_first(void) +{ +	return (TAILQ_FIRST(&tp_entries)); +} + +/* + * Find the next address in the list. + */ +static struct tp_entry * +bridge_addrs_next(struct tp_entry *te) +{ +	return (TAILQ_NEXT(te, tp_e)); +} + +/* + * Find the first address, learnt by the specified bridge interface. + */ +struct tp_entry * +bridge_addrs_bif_first(struct bridge_if *bif) +{ +	return (bif->f_tpa); +} + +/* + * Find the next address, learnt by the specified bridge interface. + */ +struct tp_entry * +bridge_addrs_bif_next(struct tp_entry *te) +{ +	struct tp_entry *te_next; + +	if ((te_next = TAILQ_NEXT(te, tp_e)) == NULL || +	    te_next->sysindex != te->sysindex) +		return (NULL); + +	return (te_next); +} + +/* + * Remove a bridge address from the list. + */ +void +bridge_addrs_remove(struct tp_entry *te, struct bridge_if *bif) +{ +	if (bif->f_tpa == te) +		bif->f_tpa = bridge_addrs_bif_next(te); + +	TAILQ_REMOVE(&tp_entries, te, tp_e); +	free(te); +} + +/* + * Allocate memory for a new bridge address and insert it in the list. + */ +struct tp_entry * +bridge_new_addrs(uint8_t *mac, struct bridge_if *bif) +{ +	struct tp_entry *te; + +	if ((te = (struct tp_entry *) malloc(sizeof(*te))) == NULL) { +		syslog(LOG_ERR, "bridge new address: failed: %s", +		    strerror(errno)); +		return (NULL); +	} + +	bzero(te, sizeof(*te)); + +	te->sysindex = bif->sysindex; +	bcopy(mac, te->tp_addr, ETHER_ADDR_LEN); +	bridge_addrs_bif_insert(&tp_entries, te, &(bif->f_tpa)); + +	return (te); +} + +/* + * Given a mac address, learnt on a bridge, + * find the corrsponding TP entry for it. + */ +struct tp_entry * +bridge_addrs_find(uint8_t *mac, struct bridge_if *bif) +{ +	struct tp_entry *te; + +	for (te = bif->f_tpa; te != NULL; te = TAILQ_NEXT(te, tp_e)) { +		if (te->sysindex != bif->sysindex) { +			te = NULL; +			break; +		} + +		if (bridge_compare_macs(te->tp_addr, mac) == 0) +			break; +	} + +	return (te); +} + +void +bridge_addrs_dump(struct bridge_if *bif) +{ +	struct tp_entry *te; + +	syslog(LOG_ERR, "Addresses count - %d", bif->num_addrs); +	for (te = bridge_addrs_bif_first(bif); te != NULL; +	    te = bridge_addrs_bif_next(te)) { +		syslog(LOG_ERR, "address %x:%x:%x:%x:%x:%x on port %d.%d", +		    te->tp_addr[0], te->tp_addr[1], te->tp_addr[2], +		    te->tp_addr[3], te->tp_addr[4], te->tp_addr[5], +		    te->sysindex, te->port_no); +	} +} + +/* + * RFC4188 specifics. + */ + +/* + * Construct the SNMP index from the address DST Mac. + */ +static void +bridge_addrs_index_append(struct asn_oid *oid, uint sub, +	const struct tp_entry *te) +{ +	int i; + +	oid->len = sub + ETHER_ADDR_LEN + 1; +	oid->subs[sub] = ETHER_ADDR_LEN; + +	for (i = 1; i <= ETHER_ADDR_LEN; i++) +		oid->subs[sub + i] = te->tp_addr[i - 1]; +} + +/* + * Find the address entry for the SNMP index from the default bridge only. + */ +static struct tp_entry * +bridge_addrs_get(const struct asn_oid *oid, uint sub, +	struct bridge_if *bif) +{ +	int i; +	uint8_t tp_addr[ETHER_ADDR_LEN]; + +	if (oid->len - sub != ETHER_ADDR_LEN + 1 || +	    oid->subs[sub] != ETHER_ADDR_LEN) +		return (NULL); + +	for (i = 0; i < ETHER_ADDR_LEN; i++) +		tp_addr[i] = oid->subs[sub + i + 1]; + +	return (bridge_addrs_find(tp_addr, bif)); +} + +/* + * Find the next address entry for the SNMP index + * from the default bridge only. + */ +static struct tp_entry * +bridge_addrs_getnext(const struct asn_oid *oid, uint sub, +	struct bridge_if *bif) +{ +	int i; +	uint8_t tp_addr[ETHER_ADDR_LEN]; +	static struct tp_entry *te; + +	if (oid->len - sub == 0) +		return (bridge_addrs_bif_first(bif)); + +	if (oid->len - sub != ETHER_ADDR_LEN + 1 || +	    oid->subs[sub] != ETHER_ADDR_LEN) +		return (NULL); + +	for (i = 0; i < ETHER_ADDR_LEN; i++) +		tp_addr[i] = oid->subs[sub + i + 1]; + +	if ((te = bridge_addrs_find(tp_addr, bif)) == NULL) +		return (NULL); + +	return (bridge_addrs_bif_next(te)); +} + +int +op_dot1d_tp_fdb(struct snmp_context *c __unused, struct snmp_value *val, +	uint sub, uint iidx __unused, enum snmp_op op) +{ +	struct bridge_if *bif; +	struct tp_entry *te; + +	if ((bif = bridge_get_default()) == NULL) +		return (SNMP_ERR_NOSUCHNAME); + +	if (time(NULL) - bif->addrs_age > bridge_get_data_maxage() && +	    bridge_update_addrs(bif) <= 0) +		return (SNMP_ERR_NOSUCHNAME); + +	switch (op) { +	    case SNMP_OP_GET: +		if ((te = bridge_addrs_get(&val->var, sub, bif)) == NULL) +			return (SNMP_ERR_NOSUCHNAME); +		goto get; + +	    case SNMP_OP_GETNEXT: +		if ((te = bridge_addrs_getnext(&val->var, sub, bif)) == NULL) +			return (SNMP_ERR_NOSUCHNAME); +		bridge_addrs_index_append(&val->var, sub, te); +		goto get; + +	    case SNMP_OP_SET: +		return (SNMP_ERR_NOT_WRITEABLE); + +	    case SNMP_OP_ROLLBACK: +	    case SNMP_OP_COMMIT: +		break; +	} +	abort(); + +get: +	switch (val->var.subs[sub - 1]) { +		case LEAF_dot1dTpFdbAddress: +			return (string_get(val, te->tp_addr, ETHER_ADDR_LEN)); +		case LEAF_dot1dTpFdbPort : +			val->v.integer = te->port_no; +			return (SNMP_ERR_NOERROR); +		case LEAF_dot1dTpFdbStatus: +			val->v.integer = te->status; +			return (SNMP_ERR_NOERROR); +	} + +	abort(); +} + +/* + * Private BEGEMOT-BRIDGE-MIB specifics. + */ + +/* + * Construct the SNMP index from the bridge interface name + * and the address DST Mac. + */ +static int +bridge_addrs_begemot_index_append(struct asn_oid *oid, uint sub, +	const struct tp_entry *te) +{ +	uint i, n_len; +	const char *b_name; + +	if ((b_name = bridge_if_find_name(te->sysindex)) == NULL) +		return (-1); + +	n_len = strlen(b_name); +	oid->len = sub++; +	oid->subs[oid->len++] = n_len; + +	for (i = 1; i <= n_len; i++) +		oid->subs[oid->len++] = b_name[i - 1]; + +	oid->subs[oid->len++] = ETHER_ADDR_LEN; +	for (i = 1 ; i <= ETHER_ADDR_LEN; i++) +		oid->subs[oid->len++] = te->tp_addr[i - 1]; + +	return (0); +} + +/* + * Find a bridge address entry by the bridge interface name + * and the address DST Mac. + */ +static struct tp_entry * +bridge_addrs_begemot_get(const struct asn_oid *oid, uint sub) +{ +	uint i, n_len; +	uint8_t tp_addr[ETHER_ADDR_LEN]; +	char bif_name[IFNAMSIZ]; +	struct bridge_if *bif; + +	n_len = oid->subs[sub]; +	if (oid->len - sub != n_len + ETHER_ADDR_LEN + 3 || +	    n_len >= IFNAMSIZ || oid->subs[sub + n_len + 1] != ETHER_ADDR_LEN) +		return (NULL); + +	for (i = 0; i < n_len; i++) +		bif_name[i] = oid->subs[n_len + i + 1]; +	bif_name[i] = '\0'; + +	for (i = 1; i <= ETHER_ADDR_LEN; i++) +		tp_addr[i - 1] = oid->subs[n_len + i + 1]; + +	if ((bif = bridge_if_find_ifname(bif_name)) == NULL) +		return (NULL); + +	return (bridge_addrs_find(tp_addr, bif)); +} + +/* + * Find the next bridge address entry by the bridge interface name + * and the address DST Mac. + */ +static struct tp_entry * +bridge_addrs_begemot_getnext(const struct asn_oid *oid, uint sub) +{ +	uint i, n_len; +	uint8_t tp_addr[ETHER_ADDR_LEN]; +	char bif_name[IFNAMSIZ]; +	struct bridge_if *bif; +	struct tp_entry *tp; + +	if (oid->len - sub == 0) +		return (bridge_addrs_first()); + +	n_len = oid->subs[sub]; +	if (oid->len - sub != n_len + ETHER_ADDR_LEN + 2 || +	    n_len >= IFNAMSIZ || oid->subs[sub + n_len + 1] != ETHER_ADDR_LEN) +		return (NULL); + +	for (i = 1; i <= n_len; i++) +		bif_name[i - 1] = oid->subs[sub + i]; + +	bif_name[i - 1] = '\0'; + +	for (i = 1; i <= ETHER_ADDR_LEN; i++) +		tp_addr[i - 1] = oid->subs[sub + n_len + i + 1]; + +	if ((bif = bridge_if_find_ifname(bif_name)) == NULL || +	    (tp = bridge_addrs_find(tp_addr, bif)) == NULL) +		return (NULL); + +	return (bridge_addrs_next(tp)); +} + +int +op_begemot_tp_fdb(struct snmp_context *c __unused, struct snmp_value *val, +	uint sub, uint iidx __unused, enum snmp_op op) +{ +	struct tp_entry *te; + +	if (time(NULL) - address_list_age > bridge_get_data_maxage()) +		bridge_update_all_addrs(); + +	switch (op) { +	    case SNMP_OP_GET: +		if ((te = bridge_addrs_begemot_get(&val->var, sub)) == NULL) +		    return (SNMP_ERR_NOSUCHNAME); +		goto get; + +	    case SNMP_OP_GETNEXT: +		if ((te = bridge_addrs_begemot_getnext(&val->var, +		    sub)) == NULL || +		    bridge_addrs_begemot_index_append(&val->var, +		    sub, te) < 0) +			return (SNMP_ERR_NOSUCHNAME); +		goto get; + +	    case SNMP_OP_SET: +		return (SNMP_ERR_NOT_WRITEABLE); + +	    case SNMP_OP_ROLLBACK: +	    case SNMP_OP_COMMIT: +		break; +	} +	abort(); + +get: +	switch (val->var.subs[sub - 1]) { +	    case LEAF_begemotBridgeTpFdbAddress: +		return (string_get(val, te->tp_addr, ETHER_ADDR_LEN)); +	    case LEAF_begemotBridgeTpFdbPort: +		val->v.integer = te->port_no; +		return (SNMP_ERR_NOERROR); +	    case LEAF_begemotBridgeTpFdbStatus: +		val->v.integer = te->status; +		return (SNMP_ERR_NOERROR); +	} + +	abort(); +} | 
