Index: head/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_if.c =================================================================== --- head/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_if.c (revision 308560) +++ head/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_if.c (revision 308561) @@ -1,1479 +1,1479 @@ /*- * Copyright (c) 2006 Shteryana Shopova * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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)); + "gettimeofday 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 occurred 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, ETHER_ADDR_LEN); 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 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 (ifp->physaddr == NULL) { if (bridge_get_basemac(ifp->name, mac, sizeof(mac)) == NULL) { syslog(LOG_ERR, "bridge attach new %s failed - " "no bridge mac address", ifp->name); return (-1); } } else bcopy(ifp->physaddr, &mac, sizeof(mac)); if ((bif = bridge_new_bif(ifp->name, ifp->sysindex, 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) { 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_dot1dBaseBridgeAddress: return (string_get(value, bif->br_addr.octet, ETHER_ADDR_LEN)); case LEAF_dot1dBaseNumPorts: value->v.integer = bif->num_ports; return (SNMP_ERR_NOERROR); case LEAF_dot1dBaseType: value->v.integer = bif->br_type; return (SNMP_ERR_NOERROR); } abort(); case SNMP_OP_SET: return (SNMP_ERR_NOT_WRITEABLE); case SNMP_OP_GETNEXT: case SNMP_OP_ROLLBACK: case SNMP_OP_COMMIT: break; } abort(); } int op_dot1d_stp(struct snmp_context *ctx, struct snmp_value *val, 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 (val->var.subs[sub - 1]) { case LEAF_dot1dStpProtocolSpecification: val->v.integer = bif->prot_spec; return (SNMP_ERR_NOERROR); case LEAF_dot1dStpPriority: val->v.integer = bif->priority; return (SNMP_ERR_NOERROR); case LEAF_dot1dStpTimeSinceTopologyChange: if (bridge_get_time_since_tc(bif, &(val->v.uint32)) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case LEAF_dot1dStpTopChanges: val->v.uint32 = bif->top_changes; return (SNMP_ERR_NOERROR); case LEAF_dot1dStpDesignatedRoot: return (string_get(val, bif->design_root, SNMP_BRIDGE_ID_LEN)); case LEAF_dot1dStpRootCost: val->v.integer = bif->root_cost; return (SNMP_ERR_NOERROR); case LEAF_dot1dStpRootPort: val->v.integer = bif->root_port; return (SNMP_ERR_NOERROR); case LEAF_dot1dStpMaxAge: val->v.integer = bif->max_age; return (SNMP_ERR_NOERROR); case LEAF_dot1dStpHelloTime: val->v.integer = bif->hello_time; return (SNMP_ERR_NOERROR); case LEAF_dot1dStpHoldTime: val->v.integer = bif->hold_time; return (SNMP_ERR_NOERROR); case LEAF_dot1dStpForwardDelay: val->v.integer = bif->fwd_delay; return (SNMP_ERR_NOERROR); case LEAF_dot1dStpBridgeMaxAge: val->v.integer = bif->bridge_max_age; return (SNMP_ERR_NOERROR); case LEAF_dot1dStpBridgeHelloTime: val->v.integer = bif->bridge_hello_time; return (SNMP_ERR_NOERROR); case LEAF_dot1dStpBridgeForwardDelay: val->v.integer = bif->bridge_fwd_delay; return (SNMP_ERR_NOERROR); case LEAF_dot1dStpVersion: val->v.integer = bif->stp_version; return (SNMP_ERR_NOERROR); case LEAF_dot1dStpTxHoldCount: val->v.integer = bif->tx_hold_count; return (SNMP_ERR_NOERROR); } abort(); case SNMP_OP_GETNEXT: abort(); case SNMP_OP_SET: switch (val->var.subs[sub - 1]) { case LEAF_dot1dStpPriority: if (val->v.integer > SNMP_BRIDGE_MAX_PRIORITY || val->v.integer % 4096 != 0) return (SNMP_ERR_WRONG_VALUE); ctx->scratch->int1 = bif->priority; if (bridge_set_priority(bif, val->v.integer) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case LEAF_dot1dStpBridgeMaxAge: if (val->v.integer < SNMP_BRIDGE_MIN_MAGE || val->v.integer > SNMP_BRIDGE_MAX_MAGE) return (SNMP_ERR_WRONG_VALUE); ctx->scratch->int1 = bif->bridge_max_age; if (bridge_set_maxage(bif, val->v.integer) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case LEAF_dot1dStpBridgeHelloTime: if (val->v.integer < SNMP_BRIDGE_MIN_HTIME || val->v.integer > SNMP_BRIDGE_MAX_HTIME) return (SNMP_ERR_WRONG_VALUE); ctx->scratch->int1 = bif->bridge_hello_time; if (bridge_set_hello_time(bif, val->v.integer) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case LEAF_dot1dStpBridgeForwardDelay: if (val->v.integer < SNMP_BRIDGE_MIN_FDELAY || val->v.integer > SNMP_BRIDGE_MAX_FDELAY) return (SNMP_ERR_WRONG_VALUE); ctx->scratch->int1 = bif->bridge_fwd_delay; if (bridge_set_forward_delay(bif, val->v.integer) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case LEAF_dot1dStpVersion: if (val->v.integer != dot1dStpVersion_stpCompatible && val->v.integer != dot1dStpVersion_rstp) return (SNMP_ERR_WRONG_VALUE); ctx->scratch->int1 = bif->stp_version; if (bridge_set_stp_version(bif, val->v.integer) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case LEAF_dot1dStpTxHoldCount: if (val->v.integer < SNMP_BRIDGE_MIN_TXHC || val->v.integer > SNMP_BRIDGE_MAX_TXHC) return (SNMP_ERR_WRONG_VALUE); ctx->scratch->int1 = bif->tx_hold_count; if (bridge_set_tx_hold_count(bif, val->v.integer) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); 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); } abort(); case SNMP_OP_ROLLBACK: switch (val->var.subs[sub - 1]) { case LEAF_dot1dStpPriority: bridge_set_priority(bif, ctx->scratch->int1); break; case LEAF_dot1dStpBridgeMaxAge: bridge_set_maxage(bif, ctx->scratch->int1); break; case LEAF_dot1dStpBridgeHelloTime: bridge_set_hello_time(bif, ctx->scratch->int1); break; case LEAF_dot1dStpBridgeForwardDelay: bridge_set_forward_delay(bif, ctx->scratch->int1); break; case LEAF_dot1dStpVersion: bridge_set_stp_version(bif, ctx->scratch->int1); break; case LEAF_dot1dStpTxHoldCount: bridge_set_tx_hold_count(bif, ctx->scratch->int1); break; } return (SNMP_ERR_NOERROR); case SNMP_OP_COMMIT: return (SNMP_ERR_NOERROR); } abort(); } 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; return (SNMP_ERR_NOERROR); case LEAF_dot1dTpAgingTime: value->v.integer = bif->age_time; return (SNMP_ERR_NOERROR); } abort(); case SNMP_OP_GETNEXT: abort(); case SNMP_OP_SET: switch (value->var.subs[sub - 1]) { case LEAF_dot1dTpLearnedEntryDiscards: return (SNMP_ERR_NOT_WRITEABLE); case LEAF_dot1dTpAgingTime: if (value->v.integer < SNMP_BRIDGE_MIN_AGE_TIME || value->v.integer > SNMP_BRIDGE_MAX_AGE_TIME) return (SNMP_ERR_WRONG_VALUE); ctx->scratch->int1 = bif->age_time; if (bridge_set_aging_time(bif, value->v.integer) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); } abort(); case SNMP_OP_ROLLBACK: if (value->var.subs[sub - 1] == LEAF_dot1dTpAgingTime) bridge_set_aging_time(bif, ctx->scratch->int1); return (SNMP_ERR_NOERROR); case SNMP_OP_COMMIT: return (SNMP_ERR_NOERROR); } abort(); } /* * 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) { struct bridge_if *bif; 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); goto get; 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); goto get; 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)); } abort(); get: switch (val->var.subs[sub - 1]) { case LEAF_begemotBridgeBaseName: return (string_get(val, bif->bif_name, -1)); case LEAF_begemotBridgeBaseAddress: return (string_get(val, bif->br_addr.octet, ETHER_ADDR_LEN)); case LEAF_begemotBridgeBaseNumPorts: val->v.integer = bif->num_ports; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeBaseType: val->v.integer = bif->br_type; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeBaseStatus: val->v.integer = bif->if_status; return (SNMP_ERR_NOERROR); } abort(); } int op_begemot_stp(struct snmp_context *ctx, struct snmp_value *val, uint sub, uint iidx __unused, enum snmp_op op) { struct bridge_if *bif; 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); goto get; 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); goto get; 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: if (val->v.integer > SNMP_BRIDGE_MAX_PRIORITY || val->v.integer % 4096 != 0) return (SNMP_ERR_WRONG_VALUE); ctx->scratch->int1 = bif->priority; if (bridge_set_priority(bif, val->v.integer) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpBridgeMaxAge: if (val->v.integer < SNMP_BRIDGE_MIN_MAGE || val->v.integer > SNMP_BRIDGE_MAX_MAGE) return (SNMP_ERR_WRONG_VALUE); ctx->scratch->int1 = bif->bridge_max_age; if (bridge_set_maxage(bif, val->v.integer) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpBridgeHelloTime: if (val->v.integer < SNMP_BRIDGE_MIN_HTIME || val->v.integer > SNMP_BRIDGE_MAX_HTIME) return (SNMP_ERR_WRONG_VALUE); ctx->scratch->int1 = bif->bridge_hello_time; if (bridge_set_hello_time(bif, val->v.integer) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpBridgeForwardDelay: if (val->v.integer < SNMP_BRIDGE_MIN_FDELAY || val->v.integer > SNMP_BRIDGE_MAX_FDELAY) return (SNMP_ERR_WRONG_VALUE); ctx->scratch->int1 = bif->bridge_fwd_delay; if (bridge_set_forward_delay(bif, val->v.integer) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpVersion: if (val->v.integer != begemotBridgeStpVersion_stpCompatible && val->v.integer != begemotBridgeStpVersion_rstp) return (SNMP_ERR_WRONG_VALUE); ctx->scratch->int1 = bif->stp_version; if (bridge_set_stp_version(bif, val->v.integer) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpTxHoldCount: if (val->v.integer < SNMP_BRIDGE_MIN_TXHC || val->v.integer > SNMP_BRIDGE_MAX_TXHC) return (SNMP_ERR_WRONG_VALUE); ctx->scratch->int1 = bif->tx_hold_count; if (bridge_set_tx_hold_count(bif, val->v.integer) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); 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); } abort(); 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; case LEAF_begemotBridgeStpVersion: bridge_set_stp_version(bif, ctx->scratch->int1); break; case LEAF_begemotBridgeStpTxHoldCount: bridge_set_tx_hold_count(bif, ctx->scratch->int1); break; } return (SNMP_ERR_NOERROR); case SNMP_OP_COMMIT: return (SNMP_ERR_NOERROR); } abort(); get: switch (val->var.subs[sub - 1]) { case LEAF_begemotBridgeStpProtocolSpecification: val->v.integer = bif->prot_spec; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpPriority: val->v.integer = bif->priority; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpTimeSinceTopologyChange: if (bridge_get_time_since_tc(bif, &(val->v.uint32)) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpTopChanges: val->v.uint32 = bif->top_changes; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpDesignatedRoot: return (string_get(val, bif->design_root, SNMP_BRIDGE_ID_LEN)); case LEAF_begemotBridgeStpRootCost: val->v.integer = bif->root_cost; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpRootPort: val->v.integer = bif->root_port; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpMaxAge: val->v.integer = bif->max_age; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpHelloTime: val->v.integer = bif->hello_time; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpHoldTime: val->v.integer = bif->hold_time; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpForwardDelay: val->v.integer = bif->fwd_delay; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpBridgeMaxAge: val->v.integer = bif->bridge_max_age; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpBridgeHelloTime: val->v.integer = bif->bridge_hello_time; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpBridgeForwardDelay: val->v.integer = bif->bridge_fwd_delay; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpVersion: val->v.integer = bif->stp_version; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpTxHoldCount: val->v.integer = bif->tx_hold_count; return (SNMP_ERR_NOERROR); } abort(); } 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; 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); goto get; 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); goto get; 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: if (val->v.integer < SNMP_BRIDGE_MIN_AGE_TIME || val->v.integer > SNMP_BRIDGE_MAX_AGE_TIME) return (SNMP_ERR_WRONG_VALUE); 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); } abort(); get: switch (val->var.subs[sub - 1]) { case LEAF_begemotBridgeTpLearnedEntryDiscards: val->v.uint32 = bif->lrnt_drops; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeTpAgingTime: val->v.integer = bif->age_time; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeTpMaxAddresses: val->v.integer = bif->max_addrs; return (SNMP_ERR_NOERROR); } abort(); }