Path: blob/main/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_addrs.c
107769 views
/*-1* SPDX-License-Identifier: BSD-2-Clause2*3* Copyright (c) 2006 Shteryana Shopova <[email protected]>4* All rights reserved.5*6* Redistribution and use in source and binary forms, with or without7* modification, are permitted provided that the following conditions8* are met:9* 1. Redistributions of source code must retain the above copyright10* notice, this list of conditions and the following disclaimer.11* 2. Redistributions in binary form must reproduce the above copyright12* notice, this list of conditions and the following disclaimer in the13* documentation and/or other materials provided with the distribution.14*15* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND16* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE17* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE18* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE19* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL20* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS21* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)22* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT23* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY24* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF25* SUCH DAMAGE.26*27* Bridge MIB implementation for SNMPd.28* Bridge addresses.29*/3031#include <sys/queue.h>32#include <sys/socket.h>33#include <sys/types.h>3435#include <net/ethernet.h>36#include <net/if.h>37#include <net/if_mib.h>3839#include <assert.h>40#include <errno.h>41#include <stdarg.h>42#include <string.h>43#include <stdlib.h>44#include <syslog.h>4546#include <bsnmp/snmpmod.h>47#include <bsnmp/snmp_mibII.h>4849#define SNMPTREE_TYPES50#include "bridge_tree.h"51#include "bridge_snmp.h"5253TAILQ_HEAD(tp_entries, tp_entry);5455/*56* Free the bridge address list.57*/58static void59bridge_tpe_free(struct tp_entries *headp)60{61struct tp_entry *t;6263while ((t = TAILQ_FIRST(headp)) != NULL) {64TAILQ_REMOVE(headp, t, tp_e);65free(t);66}67}6869/*70* Free the bridge address entries from the address list,71* for the specified bridge interface only.72*/73static void74bridge_tpe_bif_free(struct tp_entries *headp,75struct bridge_if *bif)76{77struct tp_entry *tp;7879while (bif->f_tpa != NULL && bif->sysindex == bif->f_tpa->sysindex) {80tp = TAILQ_NEXT(bif->f_tpa, tp_e);81TAILQ_REMOVE(headp, bif->f_tpa, tp_e);82free(bif->f_tpa);83bif->f_tpa = tp;84}85}8687/*88* Compare two mac addresses.89* m1 < m2 : -190* m1 > m2 : +191* m1 = m2 : 092*/93static int94bridge_compare_macs(const uint8_t *m1, const uint8_t *m2)95{96int i;9798for (i = 0; i < ETHER_ADDR_LEN; i++) {99if (m1[i] < m2[i])100return (-1);101if (m1[i] > m2[i])102return (1);103}104105return (0);106}107108/*109* Insert an address entry in the bridge address TAILQ starting to search110* for its place from the position of the first bridge address for the bridge111* interface. Update the first bridge address if necessary.112*/113static void114bridge_addrs_insert_at(struct tp_entries *headp,115struct tp_entry *ta, struct tp_entry **f_tpa)116{117struct tp_entry *t1;118119assert(f_tpa != NULL);120121for (t1 = *f_tpa;122t1 != NULL && ta->sysindex == t1->sysindex;123t1 = TAILQ_NEXT(t1, tp_e)) {124if (bridge_compare_macs(ta->tp_addr, t1->tp_addr) < 0) {125TAILQ_INSERT_BEFORE(t1, ta, tp_e);126if (*f_tpa == t1)127(*f_tpa) = ta;128return;129}130}131132if (t1 == NULL)133TAILQ_INSERT_TAIL(headp, ta, tp_e);134else135TAILQ_INSERT_BEFORE(t1, ta, tp_e);136}137138/*139* Find an address entry's position in the address list140* according to bridge interface name.141*/142static struct tp_entry *143bridge_addrs_find_pos(struct tp_entries *headp, uint32_t b_idx)144{145uint32_t t_idx;146struct tp_entry *t1;147148if ((t1 = TAILQ_FIRST(headp)) == NULL ||149bridge_compare_sysidx(b_idx, t1->sysindex) < 0)150return (NULL);151152t_idx = t1->sysindex;153154for (t1 = TAILQ_NEXT(t1, tp_e); t1 != NULL; t1 = TAILQ_NEXT(t1, tp_e)) {155156if (t1->sysindex != t_idx) {157if (bridge_compare_sysidx(b_idx, t1->sysindex) < 0)158return (TAILQ_PREV(t1, tp_entries, tp_e));159else160t_idx = t1->sysindex;161}162}163164if (t1 == NULL)165t1 = TAILQ_LAST(headp, tp_entries);166167return (t1);168}169170/*171* Insert a bridge address in the bridge addresses list.172*/173static void174bridge_addrs_bif_insert(struct tp_entries *headp, struct tp_entry *te,175struct tp_entry **f_tpa)176{177struct tp_entry *temp;178179if (*f_tpa != NULL)180bridge_addrs_insert_at(headp, te, f_tpa);181else {182temp = bridge_addrs_find_pos(headp, te->sysindex);183184if (temp == NULL)185TAILQ_INSERT_HEAD(headp, te, tp_e);186else187TAILQ_INSERT_AFTER(headp, temp, te, tp_e);188*f_tpa = te;189}190}191192static struct tp_entries tp_entries = TAILQ_HEAD_INITIALIZER(tp_entries);193static time_t address_list_age;194195void196bridge_addrs_update_listage(void)197{198address_list_age = time(NULL);199}200201void202bridge_addrs_fini(void)203{204bridge_tpe_free(&tp_entries);205}206207void208bridge_addrs_free(struct bridge_if *bif)209{210bridge_tpe_bif_free(&tp_entries, bif);211}212213/*214* Find the first address in the list.215*/216static struct tp_entry *217bridge_addrs_first(void)218{219return (TAILQ_FIRST(&tp_entries));220}221222/*223* Find the next address in the list.224*/225static struct tp_entry *226bridge_addrs_next(struct tp_entry *te)227{228return (TAILQ_NEXT(te, tp_e));229}230231/*232* Find the first address, learnt by the specified bridge interface.233*/234struct tp_entry *235bridge_addrs_bif_first(struct bridge_if *bif)236{237return (bif->f_tpa);238}239240/*241* Find the next address, learnt by the specified bridge interface.242*/243struct tp_entry *244bridge_addrs_bif_next(struct tp_entry *te)245{246struct tp_entry *te_next;247248if ((te_next = TAILQ_NEXT(te, tp_e)) == NULL ||249te_next->sysindex != te->sysindex)250return (NULL);251252return (te_next);253}254255/*256* Remove a bridge address from the list.257*/258void259bridge_addrs_remove(struct tp_entry *te, struct bridge_if *bif)260{261if (bif->f_tpa == te)262bif->f_tpa = bridge_addrs_bif_next(te);263264TAILQ_REMOVE(&tp_entries, te, tp_e);265free(te);266}267268/*269* Allocate memory for a new bridge address and insert it in the list.270*/271struct tp_entry *272bridge_new_addrs(uint8_t *mac, struct bridge_if *bif)273{274struct tp_entry *te;275276if ((te = (struct tp_entry *) malloc(sizeof(*te))) == NULL) {277syslog(LOG_ERR, "bridge new address: failed: %s",278strerror(errno));279return (NULL);280}281282bzero(te, sizeof(*te));283284te->sysindex = bif->sysindex;285bcopy(mac, te->tp_addr, ETHER_ADDR_LEN);286bridge_addrs_bif_insert(&tp_entries, te, &(bif->f_tpa));287288return (te);289}290291/*292* Given a mac address, learnt on a bridge,293* find the corrsponding TP entry for it.294*/295struct tp_entry *296bridge_addrs_find(uint8_t *mac, struct bridge_if *bif)297{298struct tp_entry *te;299300for (te = bif->f_tpa; te != NULL; te = TAILQ_NEXT(te, tp_e)) {301if (te->sysindex != bif->sysindex) {302te = NULL;303break;304}305306if (bridge_compare_macs(te->tp_addr, mac) == 0)307break;308}309310return (te);311}312313void314bridge_addrs_dump(struct bridge_if *bif)315{316struct tp_entry *te;317318syslog(LOG_ERR, "Addresses count - %d", bif->num_addrs);319for (te = bridge_addrs_bif_first(bif); te != NULL;320te = bridge_addrs_bif_next(te)) {321syslog(LOG_ERR, "address %x:%x:%x:%x:%x:%x on port %d.%d",322te->tp_addr[0], te->tp_addr[1], te->tp_addr[2],323te->tp_addr[3], te->tp_addr[4], te->tp_addr[5],324te->sysindex, te->port_no);325}326}327328/*329* RFC4188 specifics.330*/331332/*333* Construct the SNMP index from the address DST Mac.334*/335static void336bridge_addrs_index_append(struct asn_oid *oid, uint sub,337const struct tp_entry *te)338{339int i;340341oid->len = sub + ETHER_ADDR_LEN + 1;342oid->subs[sub] = ETHER_ADDR_LEN;343344for (i = 1; i <= ETHER_ADDR_LEN; i++)345oid->subs[sub + i] = te->tp_addr[i - 1];346}347348/*349* Find the address entry for the SNMP index from the default bridge only.350*/351static struct tp_entry *352bridge_addrs_get(const struct asn_oid *oid, uint sub,353struct bridge_if *bif)354{355int i;356uint8_t tp_addr[ETHER_ADDR_LEN];357358if (oid->len - sub != ETHER_ADDR_LEN + 1 ||359oid->subs[sub] != ETHER_ADDR_LEN)360return (NULL);361362for (i = 0; i < ETHER_ADDR_LEN; i++)363tp_addr[i] = oid->subs[sub + i + 1];364365return (bridge_addrs_find(tp_addr, bif));366}367368/*369* Find the next address entry for the SNMP index370* from the default bridge only.371*/372static struct tp_entry *373bridge_addrs_getnext(const struct asn_oid *oid, uint sub,374struct bridge_if *bif)375{376int i;377uint8_t tp_addr[ETHER_ADDR_LEN];378static struct tp_entry *te;379380if (oid->len - sub == 0)381return (bridge_addrs_bif_first(bif));382383if (oid->len - sub != ETHER_ADDR_LEN + 1 ||384oid->subs[sub] != ETHER_ADDR_LEN)385return (NULL);386387for (i = 0; i < ETHER_ADDR_LEN; i++)388tp_addr[i] = oid->subs[sub + i + 1];389390if ((te = bridge_addrs_find(tp_addr, bif)) == NULL)391return (NULL);392393return (bridge_addrs_bif_next(te));394}395396int397op_dot1d_tp_fdb(struct snmp_context *c __unused, struct snmp_value *val,398uint sub, uint iidx __unused, enum snmp_op op)399{400struct bridge_if *bif;401struct tp_entry *te;402403if ((bif = bridge_get_default()) == NULL)404return (SNMP_ERR_NOSUCHNAME);405406if (time(NULL) - bif->addrs_age > bridge_get_data_maxage() &&407bridge_update_addrs(bif) <= 0)408return (SNMP_ERR_NOSUCHNAME);409410switch (op) {411case SNMP_OP_GET:412if ((te = bridge_addrs_get(&val->var, sub, bif)) == NULL)413return (SNMP_ERR_NOSUCHNAME);414goto get;415416case SNMP_OP_GETNEXT:417if ((te = bridge_addrs_getnext(&val->var, sub, bif)) == NULL)418return (SNMP_ERR_NOSUCHNAME);419bridge_addrs_index_append(&val->var, sub, te);420goto get;421422case SNMP_OP_SET:423return (SNMP_ERR_NOT_WRITEABLE);424425case SNMP_OP_ROLLBACK:426case SNMP_OP_COMMIT:427break;428}429abort();430431get:432switch (val->var.subs[sub - 1]) {433case LEAF_dot1dTpFdbAddress:434return (string_get(val, te->tp_addr, ETHER_ADDR_LEN));435case LEAF_dot1dTpFdbPort :436val->v.integer = te->port_no;437return (SNMP_ERR_NOERROR);438case LEAF_dot1dTpFdbStatus:439val->v.integer = te->status;440return (SNMP_ERR_NOERROR);441}442443abort();444}445446/*447* Private BEGEMOT-BRIDGE-MIB specifics.448*/449450/*451* Construct the SNMP index from the bridge interface name452* and the address DST Mac.453*/454static int455bridge_addrs_begemot_index_append(struct asn_oid *oid, uint sub,456const struct tp_entry *te)457{458uint i, n_len;459const char *b_name;460461if ((b_name = bridge_if_find_name(te->sysindex)) == NULL)462return (-1);463464n_len = strlen(b_name);465oid->len = sub++;466oid->subs[oid->len++] = n_len;467468for (i = 1; i <= n_len; i++)469oid->subs[oid->len++] = b_name[i - 1];470471oid->subs[oid->len++] = ETHER_ADDR_LEN;472for (i = 1 ; i <= ETHER_ADDR_LEN; i++)473oid->subs[oid->len++] = te->tp_addr[i - 1];474475return (0);476}477478/*479* Find a bridge address entry by the bridge interface name480* and the address DST Mac.481*/482static struct tp_entry *483bridge_addrs_begemot_get(const struct asn_oid *oid, uint sub)484{485uint i, n_len;486uint8_t tp_addr[ETHER_ADDR_LEN];487char bif_name[IFNAMSIZ];488struct bridge_if *bif;489490n_len = oid->subs[sub];491if (oid->len - sub != n_len + ETHER_ADDR_LEN + 3 ||492n_len >= IFNAMSIZ || oid->subs[sub + n_len + 1] != ETHER_ADDR_LEN)493return (NULL);494495for (i = 0; i < n_len; i++)496bif_name[i] = oid->subs[n_len + i + 1];497bif_name[i] = '\0';498499for (i = 1; i <= ETHER_ADDR_LEN; i++)500tp_addr[i - 1] = oid->subs[n_len + i + 1];501502if ((bif = bridge_if_find_ifname(bif_name)) == NULL)503return (NULL);504505return (bridge_addrs_find(tp_addr, bif));506}507508/*509* Find the next bridge address entry by the bridge interface name510* and the address DST Mac.511*/512static struct tp_entry *513bridge_addrs_begemot_getnext(const struct asn_oid *oid, uint sub)514{515uint i, n_len;516uint8_t tp_addr[ETHER_ADDR_LEN];517char bif_name[IFNAMSIZ];518struct bridge_if *bif;519struct tp_entry *tp;520521if (oid->len - sub == 0)522return (bridge_addrs_first());523524n_len = oid->subs[sub];525if (oid->len - sub != n_len + ETHER_ADDR_LEN + 2 ||526n_len >= IFNAMSIZ || oid->subs[sub + n_len + 1] != ETHER_ADDR_LEN)527return (NULL);528529for (i = 1; i <= n_len; i++)530bif_name[i - 1] = oid->subs[sub + i];531532bif_name[i - 1] = '\0';533534for (i = 1; i <= ETHER_ADDR_LEN; i++)535tp_addr[i - 1] = oid->subs[sub + n_len + i + 1];536537if ((bif = bridge_if_find_ifname(bif_name)) == NULL ||538(tp = bridge_addrs_find(tp_addr, bif)) == NULL)539return (NULL);540541return (bridge_addrs_next(tp));542}543544int545op_begemot_tp_fdb(struct snmp_context *c __unused, struct snmp_value *val,546uint sub, uint iidx __unused, enum snmp_op op)547{548struct tp_entry *te;549550if (time(NULL) - address_list_age > bridge_get_data_maxage())551bridge_update_all_addrs();552553switch (op) {554case SNMP_OP_GET:555if ((te = bridge_addrs_begemot_get(&val->var, sub)) == NULL)556return (SNMP_ERR_NOSUCHNAME);557goto get;558559case SNMP_OP_GETNEXT:560if ((te = bridge_addrs_begemot_getnext(&val->var,561sub)) == NULL ||562bridge_addrs_begemot_index_append(&val->var,563sub, te) < 0)564return (SNMP_ERR_NOSUCHNAME);565goto get;566567case SNMP_OP_SET:568return (SNMP_ERR_NOT_WRITEABLE);569570case SNMP_OP_ROLLBACK:571case SNMP_OP_COMMIT:572break;573}574abort();575576get:577switch (val->var.subs[sub - 1]) {578case LEAF_begemotBridgeTpFdbAddress:579return (string_get(val, te->tp_addr, ETHER_ADDR_LEN));580case LEAF_begemotBridgeTpFdbPort:581val->v.integer = te->port_no;582return (SNMP_ERR_NOERROR);583case LEAF_begemotBridgeTpFdbStatus:584val->v.integer = te->status;585return (SNMP_ERR_NOERROR);586}587588abort();589}590591592