diff options
Diffstat (limited to 'usr.sbin/bsnmpd/modules/snmp_bridge/bridge_if.c')
-rw-r--r-- | usr.sbin/bsnmpd/modules/snmp_bridge/bridge_if.c | 1369 |
1 files changed, 1369 insertions, 0 deletions
diff --git a/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_if.c b/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_if.c new file mode 100644 index 0000000..d188ab5 --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_if.c @@ -0,0 +1,1369 @@ +/*- + * 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 interface objects. + * + * $FreeBSD$ + */ + +#include <sys/queue.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <sys/types.h> + +#include <net/ethernet.h> +#include <net/if.h> +#include <net/if_mib.h> +#include <net/if_types.h> + +#include <errno.h> +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> + +#include <bsnmp/snmpmod.h> +#include <bsnmp/snmp_mibII.h> + +#include "bridge_tree.h" +#include "bridge_snmp.h" +#include "bridge_oid.h" + +static const struct asn_oid oid_newRoot = OIDX_newRoot; +static const struct asn_oid oid_TopologyChange = OIDX_topologyChange; +static const struct asn_oid oid_begemotBrigeName = \ + OIDX_begemotBridgeBaseName; +static const struct asn_oid oid_begemotNewRoot = OIDX_begemotBridgeNewRoot; +static const struct asn_oid oid_begemotTopologyChange = \ + OIDX_begemotBridgeTopologyChange; + +TAILQ_HEAD(bridge_ifs, bridge_if); + +/* + * Free the bridge interface list. + */ +static void +bridge_ifs_free(struct bridge_ifs *headp) +{ + struct bridge_if *b; + + while ((b = TAILQ_FIRST(headp)) != NULL) { + TAILQ_REMOVE(headp, b, b_if); + free(b); + } +} + +/* + * Insert an entry in the bridge interface TAILQ. Keep the + * TAILQ sorted by the bridge's interface name. + */ +static void +bridge_ifs_insert(struct bridge_ifs *headp, + struct bridge_if *b) +{ + struct bridge_if *temp; + + if ((temp = TAILQ_FIRST(headp)) == NULL || + strcmp(b->bif_name, temp->bif_name) < 0) { + TAILQ_INSERT_HEAD(headp, b, b_if); + return; + } + + TAILQ_FOREACH(temp, headp, b_if) + if(strcmp(b->bif_name, temp->bif_name) < 0) + TAILQ_INSERT_BEFORE(temp, b, b_if); + + TAILQ_INSERT_TAIL(headp, b, b_if); +} + +/* The global bridge interface list. */ +static struct bridge_ifs bridge_ifs = TAILQ_HEAD_INITIALIZER(bridge_ifs); +static time_t bridge_list_age; + +/* + * Free the global list. + */ +void +bridge_ifs_fini(void) +{ + bridge_ifs_free(&bridge_ifs); +} + +/* + * Find a bridge interface entry by the bridge interface system index. + */ +struct bridge_if * +bridge_if_find_ifs(uint32_t sysindex) +{ + struct bridge_if *b; + + TAILQ_FOREACH(b, &bridge_ifs, b_if) + if (b->sysindex == sysindex) + return (b); + + return (NULL); +} + +/* + * Find a bridge interface entry by the bridge interface name. + */ +struct bridge_if * +bridge_if_find_ifname(const char *b_name) +{ + struct bridge_if *b; + + TAILQ_FOREACH(b, &bridge_ifs, b_if) + if (strcmp(b_name, b->bif_name) == 0) + return (b); + + return (NULL); +} + +/* + * Find a bridge name by the bridge interface system index. + */ +const char * +bridge_if_find_name(uint32_t sysindex) +{ + struct bridge_if *b; + + TAILQ_FOREACH(b, &bridge_ifs, b_if) + if (b->sysindex == sysindex) + return (b->bif_name); + + return (NULL); +} + +/* + * Given two bridge interfaces' system indexes, find their + * corresponding names and return the result of the name + * comparison. Returns: + * error : -2 + * i1 < i2 : -1 + * i1 > i2 : +1 + * i1 = i2 : 0 + */ +int +bridge_compare_sysidx(uint32_t i1, uint32_t i2) +{ + int c; + const char *b1, *b2; + + if (i1 == i2) + return (0); + + if ((b1 = bridge_if_find_name(i1)) == NULL) { + syslog(LOG_ERR, "Bridge interface %d does not exist", i1); + return (-2); + } + + if ((b2 = bridge_if_find_name(i2)) == NULL) { + syslog(LOG_ERR, "Bridge interface %d does not exist", i2); + return (-2); + } + + if ((c = strcmp(b1, b2)) < 0) + return (-1); + else if (c > 0) + return (1); + + return (0); +} + +/* + * Fetch the first bridge interface from the list. + */ +struct bridge_if * +bridge_first_bif(void) +{ + return (TAILQ_FIRST(&bridge_ifs)); +} + +/* + * Fetch the next bridge interface from the list. + */ +struct bridge_if * +bridge_next_bif(struct bridge_if *b_pr) +{ + return (TAILQ_NEXT(b_pr, b_if)); +} + +/* + * Create a new entry for a bridge interface and insert + * it in the list. + */ +static struct bridge_if * +bridge_new_bif(const char *bif_n, uint32_t sysindex, const u_char *physaddr) +{ + struct bridge_if *bif; + + if ((bif = (struct bridge_if *) malloc(sizeof(*bif)))== NULL) { + syslog(LOG_ERR, "bridge new interface failed: %s", + strerror(errno)); + return (NULL); + } + + bzero(bif, sizeof(struct bridge_if)); + strlcpy(bif->bif_name, bif_n, IFNAMSIZ); + bcopy(physaddr, bif->br_addr.octet, ETHER_ADDR_LEN); + bif->sysindex = sysindex; + bif->br_type = BaseType_transparent_only; + /* 1 - all bridges default hold time * 100 - centi-seconds */ + bif->hold_time = 1 * 100; + bif->prot_spec = dot1dStpProtocolSpecification_ieee8021d; + bridge_ifs_insert(&bridge_ifs, bif); + + return (bif); +} + +/* + * Remove a bridge interface from the list, freeing all it's ports + * and address entries. + */ +void +bridge_remove_bif(struct bridge_if *bif) +{ + bridge_members_free(bif); + bridge_addrs_free(bif); + TAILQ_REMOVE(&bridge_ifs, bif, b_if); + free(bif); +} + + +/* + * Prepare the variable (bridge interface name) for the private + * begemot notifications. + */ +static struct snmp_value* +bridge_basename_var(struct bridge_if *bif, struct snmp_value* b_val) +{ + uint i; + + b_val->var = oid_begemotBrigeName; + b_val->var.subs[b_val->var.len++] = strlen(bif->bif_name); + + if ((b_val->v.octetstring.octets = (u_char *) + malloc(strlen(bif->bif_name))) == NULL) + return (NULL); + + for (i = 0; i < strlen(bif->bif_name); i++) + b_val->var.subs[b_val->var.len++] = bif->bif_name[i]; + + b_val->v.octetstring.len = strlen(bif->bif_name); + bcopy(bif->bif_name, b_val->v.octetstring.octets, + strlen(bif->bif_name)); + b_val->syntax = SNMP_SYNTAX_OCTETSTRING; + + return (b_val); +} + +/* + * Compare the values of the old and the new root port and + * send a new root notification, if they are not matching. + */ +static void +bridge_new_root(struct bridge_if *bif) +{ + struct snmp_value bif_idx; + + if (bridge_get_default() == bif) + snmp_send_trap(&oid_newRoot, (struct snmp_value *) NULL); + + if (bridge_basename_var(bif, &bif_idx) == NULL) + return; + + snmp_send_trap(&oid_begemotTopologyChange, + &bif_idx, (struct snmp_value *) NULL); +} + +/* + * Compare the new and old topology change times and send a + * topology change notification if necessary. + */ +static void +bridge_top_change(struct bridge_if *bif) +{ + struct snmp_value bif_idx; + + if (bridge_get_default() == bif) + snmp_send_trap(&oid_TopologyChange, + (struct snmp_value *) NULL); + + if (bridge_basename_var(bif, &bif_idx) == NULL) + return; + + snmp_send_trap(&oid_begemotNewRoot, + &bif_idx, (struct snmp_value *) NULL); +} + +static int +bridge_if_create(const char* b_name, int8_t up) +{ + if (bridge_create(b_name) < 0) + return (-1); + + if (up == 1 && (bridge_set_if_up(b_name, 1) < 0)) + return (-1); + + /* + * Do not create a new bridge entry here - + * wait until the mibII module notifies us. + */ + return (0); +} + +static int +bridge_if_destroy(struct bridge_if *bif) +{ + if (bridge_destroy(bif->bif_name) < 0) + return (-1); + + bridge_remove_bif(bif); + + return (0); +} + +/* + * Calculate the timeticks since the last topology change. + */ +static int +bridge_get_time_since_tc(struct bridge_if *bif, uint32_t *ticks) +{ + struct timeval ct; + + if (gettimeofday(&ct, NULL) < 0) { + syslog(LOG_ERR, "bridge get time since last TC:" + "getttimeofday failed: %s", strerror(errno)); + return (-1); + } + + if (ct.tv_usec - bif->last_tc_time.tv_usec < 0) { + ct.tv_sec -= 1; + ct.tv_usec += 1000000; + } + + ct.tv_sec -= bif->last_tc_time.tv_sec; + ct.tv_usec -= bif->last_tc_time.tv_usec; + + *ticks = ct.tv_sec * 100 + ct.tv_usec/10000; + + return (0); +} + +/* + * Update the info we have for a single bridge interface. + * Return: + * 1, if successful + * 0, if the interface was deleted + * -1, error occured while fetching the info from the kernel. + */ +static int +bridge_update_bif(struct bridge_if *bif) +{ + struct mibif *ifp; + + /* Walk through the mibII interface list. */ + for (ifp = mib_first_if(); ifp != NULL; ifp = mib_next_if(ifp)) + if (strcmp(ifp->name, bif->bif_name) == 0) + break; + + if (ifp == NULL) { + /* Ops, we do not exist anymore. */ + bridge_remove_bif(bif); + return (0); + } + + if (ifp->physaddr != NULL ) + bcopy(ifp->physaddr, bif->br_addr.octet, ETHER_ADDR_LEN); + else + bridge_get_basemac(bif->bif_name, bif->br_addr.octet); + + if (ifp->mib.ifmd_flags & IFF_RUNNING) + bif->if_status = RowStatus_active; + else + bif->if_status = RowStatus_notInService; + + switch (bridge_getinfo_bif(bif)) { + case 2: + bridge_new_root(bif); + break; + case 1: + bridge_top_change(bif); + break; + case -1: + bridge_remove_bif(bif); + return (-1); + default: + break; + } + + /* + * The number of ports is accessible via SNMP - + * update the ports each time the bridge interface data + * is refreshed too. + */ + bif->num_ports = bridge_update_memif(bif); + bif->entry_age = time(NULL); + + return (1); +} + +/* + * Update all bridge interfaces' ports only - + * make sure each bridge interface exists first. + */ +void +bridge_update_all_ports(void) +{ + struct mibif *ifp; + struct bridge_if *bif, *t_bif; + + for (bif = bridge_first_bif(); bif != NULL; bif = t_bif) { + t_bif = bridge_next_bif(bif); + + for (ifp = mib_first_if(); ifp != NULL; + ifp = mib_next_if(ifp)) + if (strcmp(ifp->name, bif->bif_name) == 0) + break; + + if (ifp != NULL) + bif->num_ports = bridge_update_memif(bif); + else /* Ops, we do not exist anymore. */ + bridge_remove_bif(bif); + } + + bridge_ports_update_listage(); +} + +/* + * Update all addresses only. + */ +void +bridge_update_all_addrs(void) +{ + struct mibif *ifp; + struct bridge_if *bif, *t_bif; + + for (bif = bridge_first_bif(); bif != NULL; bif = t_bif) { + t_bif = bridge_next_bif(bif); + + for (ifp = mib_first_if(); ifp != NULL; + ifp = mib_next_if(ifp)) + if (strcmp(ifp->name, bif->bif_name) == 0) + break; + + if (ifp != NULL) + bif->num_addrs = bridge_update_addrs(bif); + else /* Ops, we don't exist anymore. */ + bridge_remove_bif(bif); + } + + bridge_addrs_update_listage(); +} + +/* + * Update only the bridge interfaces' data - skip addresses. + */ +void +bridge_update_all_ifs(void) +{ + struct bridge_if *bif, *t_bif; + + for (bif = bridge_first_bif(); bif != NULL; bif = t_bif) { + t_bif = bridge_next_bif(bif); + bridge_update_bif(bif); + } + + bridge_ports_update_listage(); + bridge_list_age = time(NULL); +} + +/* + * Update all info we have for all bridges. + */ +void +bridge_update_all(void *arg __unused) +{ + struct bridge_if *bif, *t_bif; + + for (bif = bridge_first_bif(); bif != NULL; bif = t_bif) { + t_bif = bridge_next_bif(bif); + if (bridge_update_bif(bif) <= 0) + continue; + + /* Update our learnt addresses. */ + bif->num_addrs = bridge_update_addrs(bif); + } + + bridge_list_age = time(NULL); + bridge_ports_update_listage(); + bridge_addrs_update_listage(); +} + +/* + * Callback for polling our last topology change time - + * check whether we are root or whether a TC was detected once every + * 30 seconds, so that we can send the newRoot and TopologyChange traps + * on time. The rest of the data is polled only once every 5 min. + */ +void +bridge_update_tc_time(void *arg __unused) +{ + struct bridge_if *bif; + struct mibif *ifp; + + TAILQ_FOREACH(bif, &bridge_ifs, b_if) { + /* Walk through the mibII interface list. */ + for (ifp = mib_first_if(); ifp != NULL; ifp = mib_next_if(ifp)) + if (strcmp(ifp->name, bif->bif_name) == 0) + break; + + if (ifp == NULL) { + bridge_remove_bif(bif); + continue; + } + + switch (bridge_get_op_param(bif)) { + case 2: + bridge_new_root(bif); + break; + case 1: + bridge_top_change(bif); + break; + } + } +} + +/* + * Callback for handling new bridge interface creation. + */ +int +bridge_attach_newif(struct mibif *ifp) +{ + u_char *p_mac, mac[ETHER_ADDR_LEN]; + struct bridge_if *bif; + + if (ifp->mib.ifmd_data.ifi_type != IFT_BRIDGE) + return (0); + + /* Make sure it does not exist in our list. */ + TAILQ_FOREACH(bif, &bridge_ifs, b_if) + if(strcmp(bif->bif_name, ifp->name) == 0) { + syslog(LOG_ERR, "bridge interface %s already " + "in list", bif->bif_name); + return (-1); + } + + if ((p_mac = ifp->physaddr) == NULL && + (p_mac = bridge_get_basemac(ifp->name, mac)) == NULL) { + syslog(LOG_ERR, "bridge attach new %s failed - " + "no bridge mac address", ifp->name); + return (-1); + } + + if ((bif = bridge_new_bif(ifp->name, ifp->sysindex, p_mac)) == NULL) + return (-1); + + if (ifp->mib.ifmd_flags & IFF_RUNNING) + bif->if_status = RowStatus_active; + else + bif->if_status = RowStatus_notInService; + + /* Skip sending notifications if the interface was just created. */ + if (bridge_getinfo_bif(bif) < 0 || + (bif->num_ports = bridge_getinfo_bif_ports(bif)) < 0 || + (bif->num_addrs = bridge_getinfo_bif_addrs(bif)) < 0) { + bridge_remove_bif(bif); + return (-1); + } + + /* Check whether we are the default bridge interface. */ + if (strcmp(ifp->name, bridge_get_default_name()) == 0) + bridge_set_default(bif); + + return (0); +} + +void +bridge_ifs_dump(void) +{ + struct bridge_if *bif; + + for (bif = bridge_first_bif(); bif != NULL; + bif = bridge_next_bif(bif)) { + syslog(LOG_ERR, "Bridge %s, index - %d", bif->bif_name, + bif->sysindex); + bridge_ports_dump(bif); + bridge_addrs_dump(bif); + } +} + +/* + * RFC4188 specifics. + */ +int +op_dot1d_base(struct snmp_context *ctx __unused, struct snmp_value *value, + uint sub, uint iidx __unused, enum snmp_op op) +{ + int ret; + struct bridge_if *bif; + + if ((bif = bridge_get_default()) == NULL) + return (SNMP_ERR_NOSUCHNAME); + + if (time(NULL) - bif->entry_age > bridge_get_data_maxage() && + bridge_update_bif(bif) <= 0) /* It was just deleted. */ + return (SNMP_ERR_NOSUCHNAME); + + ret = SNMP_ERR_NOERROR; + + switch (op) { + case SNMP_OP_GET: + switch (value->var.subs[sub - 1]) { + case LEAF_dot1dBaseBridgeAddress: + ret = string_get(value, bif->br_addr.octet, + ETHER_ADDR_LEN); + break; + case LEAF_dot1dBaseNumPorts: + value->v.integer = bif->num_ports; + break; + case LEAF_dot1dBaseType: + value->v.integer = bif->br_type; + break; + abort(); + } + break; + + case SNMP_OP_SET: + ret = SNMP_ERR_NOT_WRITEABLE; + break; + + case SNMP_OP_GETNEXT: + case SNMP_OP_ROLLBACK: + case SNMP_OP_COMMIT: + abort(); + } + + return (ret); +} + +int +op_dot1d_stp(struct snmp_context *ctx, struct snmp_value *value, + uint sub, uint iidx __unused, enum snmp_op op) +{ + struct bridge_if *bif; + + if ((bif = bridge_get_default()) == NULL) + return (SNMP_ERR_NOSUCHNAME); + + if (time(NULL) - bif->entry_age > bridge_get_data_maxage() && + bridge_update_bif(bif) <= 0) /* It was just deleted. */ + return (SNMP_ERR_NOSUCHNAME); + + switch (op) { + case SNMP_OP_GET: + switch (value->var.subs[sub - 1]) { + case LEAF_dot1dStpProtocolSpecification: + value->v.integer = bif->prot_spec; + break; + + case LEAF_dot1dStpPriority: + value->v.integer = bif->priority; + break; + + case LEAF_dot1dStpTimeSinceTopologyChange: + if (bridge_get_time_since_tc(bif, + &(value->v.uint32)) < 0) + return (SNMP_ERR_GENERR); + break; + + case LEAF_dot1dStpTopChanges: + value->v.uint32 = bif->top_changes; + break; + + case LEAF_dot1dStpDesignatedRoot: + return (string_get(value, bif->design_root, + SNMP_BRIDGE_ID_LEN)); + + case LEAF_dot1dStpRootCost: + value->v.integer = bif->root_cost; + break; + + case LEAF_dot1dStpRootPort: + value->v.integer = bif->root_port; + break; + + case LEAF_dot1dStpMaxAge: + value->v.integer = bif->max_age; + break; + + case LEAF_dot1dStpHelloTime: + value->v.integer = bif->hello_time; + break; + + case LEAF_dot1dStpHoldTime: + value->v.integer = bif->hold_time; + break; + + case LEAF_dot1dStpForwardDelay: + value->v.integer = bif->fwd_delay; + break; + + case LEAF_dot1dStpBridgeMaxAge: + value->v.integer = bif->bridge_max_age; + break; + + case LEAF_dot1dStpBridgeHelloTime: + value->v.integer = bif->bridge_hello_time; + break; + + case LEAF_dot1dStpBridgeForwardDelay: + value->v.integer = bif->bridge_fwd_delay; + break; + } + + return (SNMP_ERR_NOERROR); + + case SNMP_OP_GETNEXT: + abort(); + + case SNMP_OP_SET: + switch (value->var.subs[sub - 1]) { + case LEAF_dot1dStpPriority: + ctx->scratch->int1 = bif->priority; + if (bridge_set_priority(bif, value->v.integer) < 0) + return (SNMP_ERR_GENERR); + break; + + case LEAF_dot1dStpBridgeMaxAge: + ctx->scratch->int1 = bif->bridge_max_age; + if (bridge_set_maxage(bif, value->v.integer) < 0) + return (SNMP_ERR_GENERR); + break; + + case LEAF_dot1dStpBridgeHelloTime: + ctx->scratch->int1 = bif->bridge_hello_time; + if (bridge_set_hello_time(bif, value->v.integer) < 0) + return (SNMP_ERR_GENERR); + break; + + case LEAF_dot1dStpBridgeForwardDelay: + ctx->scratch->int1 = bif->bridge_fwd_delay; + if (bridge_set_forward_delay(bif, value->v.integer) < 0) + return (SNMP_ERR_GENERR); + break; + + case LEAF_dot1dStpProtocolSpecification: + case LEAF_dot1dStpTimeSinceTopologyChange: + case LEAF_dot1dStpTopChanges: + case LEAF_dot1dStpDesignatedRoot: + case LEAF_dot1dStpRootCost: + case LEAF_dot1dStpRootPort: + case LEAF_dot1dStpMaxAge: + case LEAF_dot1dStpHelloTime: + case LEAF_dot1dStpHoldTime: + case LEAF_dot1dStpForwardDelay: + return (SNMP_ERR_NOT_WRITEABLE); + } + + return (SNMP_ERR_NOERROR); + + case SNMP_OP_ROLLBACK: + switch (value->var.subs[sub - 1]) { + case LEAF_dot1dStpPriority: + (void) bridge_set_priority(bif, ctx->scratch->int1); + break; + case LEAF_dot1dStpBridgeMaxAge: + (void) bridge_set_maxage(bif, ctx->scratch->int1); + break; + case LEAF_dot1dStpBridgeHelloTime: + (void) bridge_set_hello_time(bif, ctx->scratch->int1); + break; + case LEAF_dot1dStpBridgeForwardDelay: + (void) bridge_set_forward_delay(bif, + ctx->scratch->int1); + break; + } + return (SNMP_ERR_NOERROR); + + case SNMP_OP_COMMIT: + return (SNMP_ERR_NOERROR); + } + + return (SNMP_ERR_NOERROR); +} + +int +op_dot1d_tp(struct snmp_context *ctx, struct snmp_value *value, + uint sub, uint iidx __unused, enum snmp_op op) +{ + struct bridge_if *bif; + + if ((bif = bridge_get_default()) == NULL) + return (SNMP_ERR_NOSUCHNAME); + + if (time(NULL) - bif->entry_age > bridge_get_data_maxage() && + bridge_update_bif(bif) <= 0) /* It was just deleted. */ + return (SNMP_ERR_NOSUCHNAME); + + switch (op) { + case SNMP_OP_GET: + switch (value->var.subs[sub - 1]) { + case LEAF_dot1dTpLearnedEntryDiscards: + value->v.uint32 = bif->lrnt_drops; + break; + case LEAF_dot1dTpAgingTime: + value->v.integer = bif->age_time; + break; + } + return (SNMP_ERR_NOERROR); + + case SNMP_OP_GETNEXT: + abort(); + + case SNMP_OP_SET: + if (value->var.subs[sub - 1] == LEAF_dot1dTpAgingTime) { + ctx->scratch->int1 = bif->age_time; + if (bridge_set_aging_time(bif, value->v.integer) < 0) + return (SNMP_ERR_GENERR); + } + return (SNMP_ERR_NOERROR); + + case SNMP_OP_ROLLBACK: + if (value->var.subs[sub - 1] == LEAF_dot1dTpAgingTime) + (void) bridge_set_aging_time(bif, ctx->scratch->int1); + return (SNMP_ERR_NOERROR); + + case SNMP_OP_COMMIT: + return (SNMP_ERR_NOERROR); + } + + return (SNMP_ERR_NOERROR); +} + +/* + * Private BEGEMOT-BRIDGE-MIB specifics. + */ + +/* + * Get the bridge name from an OID index. + */ +static char * +bridge_name_index_get(const struct asn_oid *oid, uint sub, char *b_name) +{ + uint i; + + if (oid->len - sub != oid->subs[sub] + 1 || oid->subs[sub] >= IFNAMSIZ) + return (NULL); + + for (i = 0; i < oid->subs[sub]; i++) + b_name[i] = oid->subs[sub + i + 1]; + b_name[i] = '\0'; + + return (b_name); +} + +static void +bridge_if_index_append(struct asn_oid *oid, uint sub, + const struct bridge_if *bif) +{ + uint i; + + oid->len = sub + strlen(bif->bif_name) + 1; + oid->subs[sub] = strlen(bif->bif_name); + + for (i = 1; i <= strlen(bif->bif_name); i++) + oid->subs[sub + i] = bif->bif_name[i - 1]; +} + +static struct bridge_if * +bridge_if_index_get(const struct asn_oid *oid, uint sub) +{ + uint i; + char bif_name[IFNAMSIZ]; + + if (oid->len - sub != oid->subs[sub] + 1 || oid->subs[sub] >= IFNAMSIZ) + return (NULL); + + for (i = 0; i < oid->subs[sub]; i++) + bif_name[i] = oid->subs[sub + i + 1]; + bif_name[i] = '\0'; + + return (bridge_if_find_ifname(bif_name)); +} + +static struct bridge_if * +bridge_if_index_getnext(const struct asn_oid *oid, uint sub) +{ + uint i; + char bif_name[IFNAMSIZ]; + struct bridge_if *bif; + + if (oid->len - sub == 0) + return (bridge_first_bif()); + + if (oid->len - sub != oid->subs[sub] + 1 || oid->subs[sub] >= IFNAMSIZ) + return (NULL); + + for (i = 0; i < oid->subs[sub]; i++) + bif_name[i] = oid->subs[sub + i + 1]; + bif_name[i] = '\0'; + + if ((bif = bridge_if_find_ifname(bif_name)) == NULL) + return (NULL); + + return (bridge_next_bif(bif)); +} + +static int +bridge_set_if_status(struct snmp_context *ctx, + struct snmp_value *val, uint sub) +{ + struct bridge_if *bif; + char bif_name[IFNAMSIZ]; + + bif = bridge_if_index_get(&val->var, sub); + + switch (val->v.integer) { + case RowStatus_active: + if (bif == NULL) + return (SNMP_ERR_INCONS_VALUE); + + ctx->scratch->int1 = bif->if_status; + + switch (bif->if_status) { + case RowStatus_active: + return (SNMP_ERR_NOERROR); + case RowStatus_notInService: + if (bridge_set_if_up(bif->bif_name, 1) < 0) + return (SNMP_ERR_GENERR); + return (SNMP_ERR_NOERROR); + default: + break; + } + return (SNMP_ERR_INCONS_VALUE); + + case RowStatus_notInService: + if (bif == NULL) + return (SNMP_ERR_INCONS_VALUE); + + ctx->scratch->int1 = bif->if_status; + + switch (bif->if_status) { + case RowStatus_active: + if (bridge_set_if_up(bif->bif_name, 1) < 0) + return (SNMP_ERR_GENERR); + return (SNMP_ERR_NOERROR); + case RowStatus_notInService: + return (SNMP_ERR_NOERROR); + default: + break; + } + return (SNMP_ERR_INCONS_VALUE); + + case RowStatus_notReady: + return (SNMP_ERR_INCONS_VALUE); + + case RowStatus_createAndGo: + if (bif != NULL) + return (SNMP_ERR_INCONS_VALUE); + + ctx->scratch->int1 = RowStatus_destroy; + + if (bridge_name_index_get(&val->var, sub, bif_name) == NULL) + return (SNMP_ERR_BADVALUE); + if (bridge_if_create(bif_name, 1) < 0) + return (SNMP_ERR_GENERR); + return (SNMP_ERR_NOERROR); + + case RowStatus_createAndWait: + if (bif != NULL) + return (SNMP_ERR_INCONS_VALUE); + + if (bridge_name_index_get(&val->var, sub, bif_name) == NULL) + return (SNMP_ERR_BADVALUE); + + ctx->scratch->int1 = RowStatus_destroy; + + if (bridge_if_create(bif_name, 0) < 0) + return (SNMP_ERR_GENERR); + return (SNMP_ERR_NOERROR); + + case RowStatus_destroy: + if (bif == NULL) + return (SNMP_ERR_NOSUCHNAME); + + ctx->scratch->int1 = bif->if_status; + bif->if_status = RowStatus_destroy; + } + + return (SNMP_ERR_NOERROR); +} + +static int +bridge_rollback_if_status(struct snmp_context *ctx, + struct snmp_value *val, uint sub) +{ + struct bridge_if *bif; + + if ((bif = bridge_if_index_get(&val->var, sub)) == NULL) + return (SNMP_ERR_GENERR); + + switch (ctx->scratch->int1) { + case RowStatus_destroy: + bridge_if_destroy(bif); + return (SNMP_ERR_NOERROR); + + case RowStatus_notInService: + if (bif->if_status != ctx->scratch->int1) + bridge_set_if_up(bif->bif_name, 0); + bif->if_status = RowStatus_notInService; + return (SNMP_ERR_NOERROR); + + case RowStatus_active: + if (bif->if_status != ctx->scratch->int1) + bridge_set_if_up(bif->bif_name, 1); + bif->if_status = RowStatus_active; + return (SNMP_ERR_NOERROR); + } + + abort(); +} + +static int +bridge_commit_if_status(struct snmp_value *val, uint sub) +{ + struct bridge_if *bif; + + if ((bif = bridge_if_index_get(&val->var, sub)) == NULL) + return (SNMP_ERR_GENERR); + + if (bif->if_status == RowStatus_destroy && + bridge_if_destroy(bif) < 0) + return (SNMP_ERR_COMMIT_FAILED); + + return (SNMP_ERR_NOERROR); +} + +int +op_begemot_base_bridge(struct snmp_context *ctx, struct snmp_value *val, + uint sub, uint iidx __unused, enum snmp_op op) +{ + int ret; + struct bridge_if *bif = NULL; + + if (time(NULL) - bridge_list_age > bridge_get_data_maxage()) + bridge_update_all_ifs(); + + switch (op) { + case SNMP_OP_GET: + if ((bif = bridge_if_index_get(&val->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + break; + + case SNMP_OP_GETNEXT: + if ((bif = bridge_if_index_getnext(&val->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + bridge_if_index_append(&val->var, sub, bif); + break; + + case SNMP_OP_SET: + switch (val->var.subs[sub - 1]) { + case LEAF_begemotBridgeBaseStatus: + return (bridge_set_if_status(ctx, val, sub)); + case LEAF_begemotBridgeBaseName: + case LEAF_begemotBridgeBaseAddress: + case LEAF_begemotBridgeBaseNumPorts: + case LEAF_begemotBridgeBaseType: + return (SNMP_ERR_NOT_WRITEABLE); + } + abort(); + + case SNMP_OP_ROLLBACK: + return (bridge_rollback_if_status(ctx, val, sub)); + case SNMP_OP_COMMIT: + return (bridge_commit_if_status(val, sub)); + } + + ret = SNMP_ERR_NOERROR; + switch (val->var.subs[sub - 1]) { + case LEAF_begemotBridgeBaseName: + ret = string_get(val, bif->bif_name, -1); + break; + + case LEAF_begemotBridgeBaseAddress: + ret = string_get(val, bif->br_addr.octet, ETHER_ADDR_LEN); + break; + + case LEAF_begemotBridgeBaseNumPorts: + val->v.integer = bif->num_ports; + break; + + case LEAF_begemotBridgeBaseType: + val->v.integer = bif->br_type; + break; + + case LEAF_begemotBridgeBaseStatus: + val->v.integer = bif->if_status; + break; + } + + return (ret); +} + +int +op_begemot_stp(struct snmp_context *ctx, struct snmp_value *val, + uint sub, uint iidx __unused, enum snmp_op op) +{ + int ret = SNMP_ERR_NOERROR; /* Make the compiler happy. */ + struct bridge_if *bif = NULL; + + if (time(NULL) - bridge_list_age > bridge_get_data_maxage()) + bridge_update_all_ifs(); + + switch (op) { + case SNMP_OP_GET: + if ((bif = bridge_if_index_get(&val->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + break; + + case SNMP_OP_GETNEXT: + if ((bif = bridge_if_index_getnext(&val->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + bridge_if_index_append(&val->var, sub, bif); + break; + + case SNMP_OP_SET: + if ((bif = bridge_if_index_get(&val->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + + switch (val->var.subs[sub - 1]) { + case LEAF_begemotBridgeStpPriority: + ctx->scratch->int1 = bif->priority; + ret = bridge_set_priority(bif, val->v.integer); + break; + + case LEAF_begemotBridgeStpBridgeMaxAge: + ctx->scratch->int1 = bif->bridge_max_age; + ret = bridge_set_maxage(bif, val->v.integer); + break; + + case LEAF_begemotBridgeStpBridgeHelloTime: + ctx->scratch->int1 = bif->bridge_hello_time; + ret = bridge_set_hello_time(bif, val->v.integer); + break; + + case LEAF_begemotBridgeStpBridgeForwardDelay: + ctx->scratch->int1 = bif->bridge_fwd_delay; + ret = bridge_set_forward_delay(bif, val->v.integer); + break; + + case LEAF_begemotBridgeStpProtocolSpecification: + case LEAF_begemotBridgeStpTimeSinceTopologyChange: + case LEAF_begemotBridgeStpTopChanges: + case LEAF_begemotBridgeStpDesignatedRoot: + case LEAF_begemotBridgeStpRootCost: + case LEAF_begemotBridgeStpRootPort: + case LEAF_begemotBridgeStpMaxAge: + case LEAF_begemotBridgeStpHelloTime: + case LEAF_begemotBridgeStpHoldTime: + case LEAF_begemotBridgeStpForwardDelay: + return (SNMP_ERR_NOT_WRITEABLE); + } + + if (ret == 0) + return (SNMP_ERR_NOERROR); + else if (ret == -2) + return (SNMP_ERR_WRONG_VALUE); + return (SNMP_ERR_GENERR); + + case SNMP_OP_ROLLBACK: + if ((bif = bridge_if_index_get(&val->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + + switch (val->var.subs[sub - 1]) { + case LEAF_begemotBridgeStpPriority: + bridge_set_priority(bif, ctx->scratch->int1); + break; + + case LEAF_begemotBridgeStpBridgeMaxAge: + bridge_set_maxage(bif, ctx->scratch->int1); + break; + + case LEAF_begemotBridgeStpBridgeHelloTime: + bridge_set_hello_time(bif, ctx->scratch->int1); + break; + + case LEAF_begemotBridgeStpBridgeForwardDelay: + bridge_set_forward_delay(bif, ctx->scratch->int1); + break; + } + return (SNMP_ERR_NOERROR); + + case SNMP_OP_COMMIT: + return (SNMP_ERR_NOERROR); + } + + switch (val->var.subs[sub - 1]) { + case LEAF_begemotBridgeStpProtocolSpecification: + val->v.integer = bif->prot_spec; + break; + + case LEAF_begemotBridgeStpPriority: + val->v.integer = bif->priority; + break; + + case LEAF_begemotBridgeStpTimeSinceTopologyChange: + if (bridge_get_time_since_tc(bif, &(val->v.uint32)) < 0) + return (SNMP_ERR_GENERR); + break; + + case LEAF_begemotBridgeStpTopChanges: + val->v.uint32 = bif->top_changes; + break; + + case LEAF_begemotBridgeStpDesignatedRoot: + ret = string_get(val, bif->design_root, SNMP_BRIDGE_ID_LEN); + break; + + case LEAF_begemotBridgeStpRootCost: + val->v.integer = bif->root_cost; + break; + + case LEAF_begemotBridgeStpRootPort: + val->v.integer = bif->root_port; + break; + + case LEAF_begemotBridgeStpMaxAge: + val->v.integer = bif->max_age; + break; + + case LEAF_begemotBridgeStpHelloTime: + val->v.integer = bif->hello_time; + break; + + case LEAF_begemotBridgeStpHoldTime: + val->v.integer = bif->hold_time; + break; + + case LEAF_begemotBridgeStpForwardDelay: + val->v.integer = bif->fwd_delay; + break; + + case LEAF_begemotBridgeStpBridgeMaxAge: + val->v.integer = bif->bridge_max_age; + break; + + case LEAF_begemotBridgeStpBridgeHelloTime: + val->v.integer = bif->bridge_hello_time; + break; + + case LEAF_begemotBridgeStpBridgeForwardDelay: + val->v.integer = bif->bridge_fwd_delay; + break; + } + + return (ret); +} + +int +op_begemot_tp(struct snmp_context *ctx, struct snmp_value *val, + uint sub, uint iidx __unused, enum snmp_op op) +{ + struct bridge_if *bif = NULL; + + if (time(NULL) - bridge_list_age > bridge_get_data_maxage()) + bridge_update_all_ifs(); + + switch (op) { + case SNMP_OP_GET: + if ((bif = bridge_if_index_get(&val->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + break; + + case SNMP_OP_GETNEXT: + if ((bif = bridge_if_index_getnext(&val->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + bridge_if_index_append(&val->var, sub, bif); + break; + + case SNMP_OP_SET: + if ((bif = bridge_if_index_get(&val->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + + switch (val->var.subs[sub - 1]) { + case LEAF_begemotBridgeTpAgingTime: + ctx->scratch->int1 = bif->age_time; + if (bridge_set_aging_time(bif, val->v.integer) < 0) + return (SNMP_ERR_GENERR); + return (SNMP_ERR_NOERROR); + + case LEAF_begemotBridgeTpMaxAddresses: + ctx->scratch->int1 = bif->max_addrs; + if (bridge_set_max_cache(bif, val->v.integer) < 0) + return (SNMP_ERR_GENERR); + return (SNMP_ERR_NOERROR); + + case LEAF_begemotBridgeTpLearnedEntryDiscards: + return (SNMP_ERR_NOT_WRITEABLE); + } + abort(); + + case SNMP_OP_ROLLBACK: + if ((bif = bridge_if_index_get(&val->var, sub)) == NULL) + return (SNMP_ERR_GENERR); + + switch (val->var.subs[sub - 1]) { + case LEAF_begemotBridgeTpAgingTime: + bridge_set_aging_time(bif, ctx->scratch->int1); + break; + + case LEAF_begemotBridgeTpMaxAddresses: + bridge_set_max_cache(bif, ctx->scratch->int1); + break; + } + return (SNMP_ERR_NOERROR); + + case SNMP_OP_COMMIT: + return (SNMP_ERR_NOERROR); + } + + switch (val->var.subs[sub - 1]) { + case LEAF_begemotBridgeTpLearnedEntryDiscards: + val->v.uint32 = bif->lrnt_drops; + break; + + case LEAF_begemotBridgeTpAgingTime: + val->v.integer = bif->age_time; + break; + + case LEAF_begemotBridgeTpMaxAddresses: + val->v.integer = bif->max_addrs; + break; + } + + return (SNMP_ERR_NOERROR); +} |