diff --git a/proto/snmp/bgp4_mib.c b/proto/snmp/bgp4_mib.c index b3ac160e..1c1a4afb 100644 --- a/proto/snmp/bgp4_mib.c +++ b/proto/snmp/bgp4_mib.c @@ -8,12 +8,11 @@ * Can be freely distributed and used under the terms of the GNU GPL. */ -/* need to be first header file included */ -#include "bgp4_mib.h" - #include "snmp.h" #include "snmp_utils.h" #include "subagent.h" +#include "bgp4_mib.h" +#include "mib_tree.h" /* hash table macros */ #define SNMP_HASH_KEY(n) n->peer_ip @@ -27,18 +26,17 @@ /* hash table only store ip4 addresses */ #define SNMP_HASH_LESS(ip1, ip2) SNMP_HASH_LESS4(ip1,ip2) -// TODO delete me +// delete me #define SNMP_MANAGE_TBUF(...) (void)0 #define DECLARE_BGP4(addr, proto, conn, stats, config) \ ip4_addr addr; \ - const struct bgp_proto *proto; \ - const struct bgp_conn *conn; \ - const struct bgp_stats *stats; \ - const struct bgp_config *config - -#define POPULATE_BGP4(addr, proto, conn, stats, config) populate_bgp4(p, c, &(addr), &(proto), &(conn), &(stats), &(config)) + const struct bgp_proto UNUSED *proto; \ + const struct bgp_conn UNUSED *conn; \ + const struct bgp_stats UNUSED *stats; \ + const struct bgp_config UNUSED *config +#define POPULATE_BGP4(addr, proto, conn, stats, config) populate_bgp4(d, &(addr), &(proto), &(conn), &(stats), &(config)) static inline void ip4_to_oid(struct oid *oid, ip4_addr addr); @@ -378,8 +376,7 @@ snmp_bgp_notify_backward_trans(struct snmp_proto *p, struct bgp_proto *bgp) void snmp_bgp4_register(struct snmp_proto *p) { - u32 bgp_mib_prefix[] = { 1, 15 }; - // TODO + u32 bgp_mib_prefix[] = { 1, BGP4_MIB }; // TODO remove constant { /* Register the whole BGP4-MIB::bgp root tree node */ @@ -602,25 +599,31 @@ oid_state_compare(const struct oid *oid, u8 state) } static inline enum snmp_search_res -populate_bgp4(struct snmp_proto *p, struct snmp_pdu *c, ip4_addr *addr, const struct bgp_proto **proto, const struct bgp_conn +populate_bgp4(struct snmp_data *d, ip4_addr *addr, const struct bgp_proto **proto, const struct bgp_conn **conn, const struct bgp_stats **stats, const struct bgp_config **config) { - const struct oid * const oid = &c->sr_vb_start->name; + const struct oid * const oid = &d->c->sr_vb_start->name; if (snmp_bgp_valid_ip4(oid)) *addr = ip4_from_oid(oid); else + { + snmp_log("populate() invalid ip4"); return SNMP_SEARCH_NO_INSTANCE; + } - struct snmp_bgp_peer *pe = snmp_hash_find(p, *addr); + struct snmp_bgp_peer *pe = snmp_hash_find(d->p, *addr); if (!pe) + { + snmp_log("populate() hash find failed"); return SNMP_SEARCH_NO_INSTANCE; + } const struct bgp_proto *bgp_proto; *proto = bgp_proto = pe->bgp_proto; - if (ipa_is_ip4(bgp_proto->remote_ip)) + if (!ipa_is_ip4(bgp_proto->remote_ip)) { - log(L_ERR, "%s: Found BGP protocol instance with IPv6 address", bgp_proto->p.name); - c->error = AGENTX_RES_GEN_ERROR; + log(L_ERR "%s: Found BGP protocol instance with IPv6 address", bgp_proto->p.name); + d->c->error = AGENTX_RES_GEN_ERROR; return SNMP_SEARCH_NO_INSTANCE; } @@ -628,9 +631,9 @@ populate_bgp4(struct snmp_proto *p, struct snmp_pdu *c, ip4_addr *addr, const st if (!ip4_equal(proto_ip, pe->peer_ip)) { /* Here, we could be in problem as the bgp_proto IP address could be changed */ - log(L_ERR, "%s: Stored hash key IP address and peer remote address differ.", - bgp_proto->p.name); - c->error = AGENTX_RES_GEN_ERROR; + log(L_ERR "%s: Stored hash key IP address and peer remote address differ " + "(%I, %I).", bgp_proto->p.name, proto_ip, pe->peer_ip); + d->c->error = AGENTX_RES_GEN_ERROR; return SNMP_SEARCH_NO_INSTANCE; } @@ -638,528 +641,409 @@ populate_bgp4(struct snmp_proto *p, struct snmp_pdu *c, ip4_addr *addr, const st *stats = &bgp_proto->stats; *config = bgp_proto->cf; + snmp_log("populate() ok"); return SNMP_SEARCH_OK; } static enum snmp_search_res -fill_bgp_version(struct snmp_proto *p, struct snmp_pdu *c) +fill_bgp_version(struct mib_walk_state *walk UNUSED, struct snmp_data *d) { - if (c->sr_vb_start->name.n_subid != 4) - { - snmp_set_varbind_type(c->sr_vb_start, AGENTX_NO_SUCH_INSTANCE); - return SNMP_SEARCH_NO_INSTANCE; - } - - uint sz = snmp_str_size_from_len(1); - if (c->size < sz) - snmp_manage_tbuf(p, c); - - c->size -= sz; - snmp_varbind_nstr(c, BGP4_VERSIONS, 1); + snmp_log("fill ver"); + d->c->size -= snmp_str_size_from_len(1); + snmp_varbind_nstr(d->c, BGP4_VERSIONS, 1); return SNMP_SEARCH_OK; } static enum snmp_search_res -fill_local_as(struct snmp_proto *p, struct snmp_pdu *c) +fill_local_as(struct mib_walk_state *walk UNUSED, struct snmp_data *d) { - if (c->size < AGENTX_TYPE_INT_SIZE) - snmp_manage_tbuf(p, c); - - snmp_varbind_int(c, p->bgp_local_as); + snmp_log("fill as"); + snmp_varbind_int(d->c, d->p->bgp_local_as); return SNMP_SEARCH_OK; } static enum snmp_search_res -fill_peer_id(struct snmp_proto *p, struct snmp_pdu *c) +fill_peer_id(struct mib_walk_state *walk UNUSED, struct snmp_data *d) { + snmp_log("fill peer id"); enum snmp_search_res res; DECLARE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf); res = POPULATE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf); if (res != SNMP_SEARCH_OK) - { - (void) snmp_set_varbind_type(c->sr_vb_start, res); return res; - } uint fsm_state = snmp_bgp_fsm_state(bgp_proto); - if (c->size < AGENTX_TYPE_IP4_SIZE) - snmp_manage_tbuf(p, c); - if (fsm_state == BGP4_MIB_OPENCONFIRM || fsm_state == BGP4_MIB_ESTABLISHED) // TODO last - snmp_varbind_ip4(c, ip4_from_u32(bgp_proto->remote_id)); + snmp_varbind_ip4(d->c, ip4_from_u32(bgp_proto->remote_id)); else - snmp_varbind_ip4(c, IP4_NONE); + snmp_varbind_ip4(d->c, IP4_NONE); return SNMP_SEARCH_OK; } static enum snmp_search_res -fill_peer_state(struct snmp_proto *p, struct snmp_pdu *c) +fill_peer_state(struct mib_walk_state *walk UNUSED, struct snmp_data *d) { + snmp_log("fill peer state"); enum snmp_search_res res; DECLARE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf); res = POPULATE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf); if (res != SNMP_SEARCH_OK) - { - (void) snmp_set_varbind_type(c->sr_vb_start, res); return res; - } uint fsm_state = snmp_bgp_fsm_state(bgp_proto); - if (c->size < AGENTX_TYPE_INT_SIZE) - snmp_manage_tbuf(p, c); - - snmp_varbind_int(c, fsm_state); + snmp_varbind_int(d->c, fsm_state); return SNMP_SEARCH_OK; } static enum snmp_search_res -fill_admin_status(struct snmp_proto *p, struct snmp_pdu *c) +fill_admin_status(struct mib_walk_state *walk UNUSED, struct snmp_data *d) { + snmp_log("fill adm status"); enum snmp_search_res res; DECLARE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf); res = POPULATE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf); if (res != SNMP_SEARCH_OK) - { - (void) snmp_set_varbind_type(c->sr_vb_start, res); return res; - } - - if (c->size < AGENTX_TYPE_INT_SIZE) - snmp_manage_tbuf(p, c); if (bgp_proto->p.disabled) - snmp_varbind_int(c, AGENTX_ADMIN_STOP); + snmp_varbind_int(d->c, AGENTX_ADMIN_STOP); else - snmp_varbind_int(c, AGENTX_ADMIN_START); + snmp_varbind_int(d->c, AGENTX_ADMIN_START); return SNMP_SEARCH_OK; } static enum snmp_search_res -fill_neg_version(struct snmp_proto *p, struct snmp_pdu *c) +fill_neg_version(struct mib_walk_state *walk UNUSED, struct snmp_data *d) { + snmp_log("fill neg ver"); enum snmp_search_res res; DECLARE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf); res = POPULATE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf); if (res != SNMP_SEARCH_OK) - { - (void) snmp_set_varbind_type(c->sr_vb_start, res); return res; - } uint fsm_state = snmp_bgp_fsm_state(bgp_proto); - if (c->size < AGENTX_TYPE_INT_SIZE) - snmp_manage_tbuf(p, c); - if (fsm_state == BGP4_MIB_ESTABLISHED || fsm_state == BGP4_MIB_ESTABLISHED) - snmp_varbind_int(c, BGP4_MIB_NEGOTIATED_VER_VALUE); + snmp_varbind_int(d->c, BGP4_MIB_NEGOTIATED_VER_VALUE); else - snmp_varbind_int(c, BGP4_MIB_NEGOTIATED_VER_NO_VALUE); + snmp_varbind_int(d->c, BGP4_MIB_NEGOTIATED_VER_NO_VALUE); return SNMP_SEARCH_OK; } static enum snmp_search_res -fill_local_addr(struct snmp_proto *p, struct snmp_pdu *c) +fill_local_addr(struct mib_walk_state *walk UNUSED, struct snmp_data *d) { + snmp_log("bgp4_mib fill local addr"); enum snmp_search_res res; DECLARE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf); res = POPULATE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf); + + snmp_log("fill local addr result %u", res); + if (res != SNMP_SEARCH_OK) - { - (void) snmp_set_varbind_type(c->sr_vb_start, res); return res; - } - if (c->size < AGENTX_TYPE_IP4_SIZE) - snmp_manage_tbuf(p, c); - - snmp_varbind_ip4(c, ipa_to_ip4(bgp_proto->local_ip)); + snmp_varbind_ip4(d->c, ipa_to_ip4(bgp_proto->local_ip)); return SNMP_SEARCH_OK; } static enum snmp_search_res -fill_local_port(struct snmp_proto *p, struct snmp_pdu *c) +fill_local_port(struct mib_walk_state *walk UNUSED, struct snmp_data *d) { + snmp_log("bgp4_mib fill local port"); enum snmp_search_res res; DECLARE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf); res = POPULATE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf); if (res != SNMP_SEARCH_OK) - { - (void) snmp_set_varbind_type(c->sr_vb_start, res); return res; - } - if (c->size < AGENTX_TYPE_INT_SIZE) - snmp_manage_tbuf(p, c); - - snmp_varbind_int(c, bgp_conf->local_port); + snmp_varbind_int(d->c, bgp_conf->local_port); return SNMP_SEARCH_OK; } static enum snmp_search_res -fill_remove_addr(struct snmp_proto *p, struct snmp_pdu *c) +fill_remote_addr(struct mib_walk_state *walk UNUSED, struct snmp_data *d) { + snmp_log("bgp4_mib fill remote addr"); enum snmp_search_res res; DECLARE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf); res = POPULATE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf); if (res != SNMP_SEARCH_OK) - { - (void) snmp_set_varbind_type(c->sr_vb_start, res); return res; - } - if (c->size < AGENTX_TYPE_IP4_SIZE) - snmp_manage_tbuf(p, c); - - snmp_varbind_ip4(c, ipa_to_ip4(bgp_proto->remote_ip)); + snmp_varbind_ip4(d->c, ipa_to_ip4(bgp_proto->remote_ip)); return SNMP_SEARCH_OK; } static enum snmp_search_res -fill_remote_port(struct snmp_proto *p, struct snmp_pdu *c) +fill_remote_port(struct mib_walk_state *walk UNUSED, struct snmp_data *d) { + snmp_log("bgp4_mib fill remote port"); enum snmp_search_res res; DECLARE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf); res = POPULATE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf); if (res != SNMP_SEARCH_OK) - { - (void) snmp_set_varbind_type(c->sr_vb_start, res); return res; - } - if (c->size < AGENTX_TYPE_INT_SIZE) - snmp_manage_tbuf(p, c); - - snmp_varbind_int(c, bgp_conf->remote_port); + snmp_varbind_int(d->c, bgp_conf->remote_port); return SNMP_SEARCH_OK; } static enum snmp_search_res -fill_remote_as(struct snmp_proto *p, struct snmp_pdu *c) +fill_remote_as(struct mib_walk_state *walk UNUSED, struct snmp_data *d) { + snmp_log("fill rem as"); enum snmp_search_res res; DECLARE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf); res = POPULATE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf); if (res != SNMP_SEARCH_OK) - { - (void) snmp_set_varbind_type(c->sr_vb_start, res); return res; - } - if (c->size < AGENTX_TYPE_INT_SIZE) - snmp_manage_tbuf(p, c); - - snmp_varbind_int(c, bgp_proto->remote_as); + snmp_varbind_int(d->c, bgp_proto->remote_as); return SNMP_SEARCH_OK; } static enum snmp_search_res -fill_in_updates(struct snmp_proto *p, struct snmp_pdu *c) +fill_in_updates(struct mib_walk_state *walk UNUSED, struct snmp_data *d) { + snmp_log("fill in updates"); enum snmp_search_res res; DECLARE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf); res = POPULATE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf); if (res != SNMP_SEARCH_OK) - { - (void) snmp_set_varbind_type(c->sr_vb_start, res); return res; - } - if (c->size < AGENTX_TYPE_COUNTER32_SIZE) - snmp_manage_tbuf(p, c); - - snmp_varbind_counter32(c, bgp_stats->rx_updates); + snmp_varbind_counter32(d->c, bgp_stats->rx_updates); return SNMP_SEARCH_OK; } static enum snmp_search_res -fill_out_update(struct snmp_proto *p, struct snmp_pdu *c) +fill_out_updates(struct mib_walk_state *walk UNUSED, struct snmp_data *d) { + snmp_log("fill out updates"); enum snmp_search_res res; DECLARE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf); res = POPULATE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf); if (res != SNMP_SEARCH_OK) - { - (void) snmp_set_varbind_type(c->sr_vb_start, res); return res; - } - if (c->size < AGENTX_TYPE_COUNTER32_SIZE) - snmp_manage_tbuf(p, c); - - snmp_varbind_counter32(c, bgp_stats->tx_updates); + snmp_varbind_counter32(d->c, bgp_stats->tx_updates); return SNMP_SEARCH_OK; } static enum snmp_search_res -fill_in_total_msg(struct snmp_proto *p, struct snmp_pdu *c) +fill_in_total_msg(struct mib_walk_state *walk UNUSED, struct snmp_data *d) { + snmp_log("fill in total"); enum snmp_search_res res; DECLARE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf); res = POPULATE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf); if (res != SNMP_SEARCH_OK) - { - (void) snmp_set_varbind_type(c->sr_vb_start, res); return res; - } - if (c->size < AGENTX_TYPE_COUNTER32_SIZE) - snmp_manage_tbuf(p, c); - - snmp_varbind_counter32(c, bgp_stats->rx_messages); + snmp_varbind_counter32(d->c, bgp_stats->rx_messages); return SNMP_SEARCH_OK; } static enum snmp_search_res -fill_out_total_msg(struct snmp_proto *p, struct snmp_pdu *c) +fill_out_total_msg(struct mib_walk_state *walk UNUSED, struct snmp_data *d) { + snmp_log("fill out total"); enum snmp_search_res res; DECLARE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf); res = POPULATE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf); if (res != SNMP_SEARCH_OK) - { - (void) snmp_set_varbind_type(c->sr_vb_start, res); return res; - } - if (c->size < AGENTX_TYPE_COUNTER32_SIZE) - snmp_manage_tbuf(p, c); - - snmp_varbind_counter32(c, bgp_stats->tx_messages); + snmp_varbind_counter32(d->c, bgp_stats->tx_messages); return SNMP_SEARCH_OK; } static enum snmp_search_res -fill_last_err(struct snmp_proto *p, struct snmp_pdu *c) +fill_last_err(struct mib_walk_state *walk UNUSED, struct snmp_data *d) { + snmp_log("fill last err"); enum snmp_search_res res; DECLARE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf); res = POPULATE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf); if (res != SNMP_SEARCH_OK) - { - (void) snmp_set_varbind_type(c->sr_vb_start, res); return res; - } - - if (c->size < snmp_str_size_from_len(2)) - snmp_manage_tbuf(p, c); char last_error[2]; snmp_bgp_last_error(bgp_proto, last_error); - snmp_varbind_nstr(c, last_error, 2); + snmp_varbind_nstr(d->c, last_error, 2); return SNMP_SEARCH_OK; } static enum snmp_search_res -fill_established_trans(struct snmp_proto *p, struct snmp_pdu *c) +fill_established_trans(struct mib_walk_state *walk UNUSED, struct snmp_data *d) { + snmp_log("fill est trans"); enum snmp_search_res res; DECLARE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf); res = POPULATE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf); if (res != SNMP_SEARCH_OK) - { - (void) snmp_set_varbind_type(c->sr_vb_start, res); return res; - } - if (c->size < AGENTX_TYPE_COUNTER32_SIZE) - snmp_manage_tbuf(p, c); - - snmp_varbind_counter32(c, + snmp_varbind_counter32(d->c, bgp_stats->fsm_established_transitions); return SNMP_SEARCH_OK; } static enum snmp_search_res -fill_established_time(struct snmp_proto *p, struct snmp_pdu *c) +fill_established_time(struct mib_walk_state *walk UNUSED, struct snmp_data *d) { + snmp_log("fill est time"); enum snmp_search_res res; DECLARE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf); res = POPULATE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf); if (res != SNMP_SEARCH_OK) - { - (void) snmp_set_varbind_type(c->sr_vb_start, res); return res; - } - - if (c->size < AGENTX_TYPE_COUNTER32_SIZE) - snmp_manage_tbuf(p, c); - snmp_varbind_gauge32(c, + snmp_varbind_gauge32(d->c, (current_time() - bgp_proto->last_established) TO_S); return SNMP_SEARCH_OK; } static enum snmp_search_res -fill_retry_interval(struct snmp_proto *p, struct snmp_pdu *c) +fill_retry_interval(struct mib_walk_state *walk UNUSED, struct snmp_data *d) { + snmp_log("fill retry int"); enum snmp_search_res res; DECLARE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf); res = POPULATE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf); if (res != SNMP_SEARCH_OK) - { - (void) snmp_set_varbind_type(c->sr_vb_start, res); return res; - } - if (c->size < AGENTX_TYPE_INT_SIZE) - snmp_manage_tbuf(p, c); - - snmp_varbind_int(c, bgp_conf->connect_retry_time); + snmp_varbind_int(d->c, bgp_conf->connect_retry_time); return SNMP_SEARCH_OK; } static enum snmp_search_res -fill_hold_time(struct snmp_proto *p, struct snmp_pdu *c) +fill_hold_time(struct mib_walk_state *walk UNUSED, struct snmp_data *d) { + snmp_log("fill hold time"); enum snmp_search_res res; DECLARE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf); res = POPULATE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf); if (res != SNMP_SEARCH_OK) - { - (void) snmp_set_varbind_type(c->sr_vb_start, res); return res; - } - if (c->size < AGENTX_TYPE_INT_SIZE) - snmp_manage_tbuf(p, c); - - snmp_varbind_int(c, (bgp_conn) ? bgp_conn->hold_time : 0); + snmp_varbind_int(d->c, (bgp_conn) ? bgp_conn->hold_time : 0); return SNMP_SEARCH_OK; } static enum snmp_search_res -fill_keep_alive(struct snmp_proto *p, struct snmp_pdu *c) +fill_keep_alive(struct mib_walk_state *walk UNUSED, struct snmp_data *d) { + snmp_log("fill keepalive"); enum snmp_search_res res; DECLARE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf); res = POPULATE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf); if (res != SNMP_SEARCH_OK) - { - (void) snmp_set_varbind_type(c->sr_vb_start, res); return res; - } - - if (c->size < AGENTX_TYPE_INT_SIZE) - snmp_manage_tbuf(p, c); if (!bgp_conf->hold_time) - snmp_varbind_int(c, 0); + snmp_varbind_int(d->c, 0); else - snmp_varbind_int(c, + snmp_varbind_int(d->c, (bgp_conn) ? bgp_conn->keepalive_time : 0); return SNMP_SEARCH_OK; } static enum snmp_search_res -fill_hold_time_conf(struct snmp_proto *p, struct snmp_pdu *c) +fill_hold_time_conf(struct mib_walk_state *walk UNUSED, struct snmp_data *d) { + snmp_log("fill hold time c"); enum snmp_search_res res; DECLARE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf); res = POPULATE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf); if (res != SNMP_SEARCH_OK) - { - (void) snmp_set_varbind_type(c->sr_vb_start, res); return res; - } - if (c->size < AGENTX_TYPE_INT_SIZE) - snmp_manage_tbuf(p, c); - - snmp_varbind_int(c, bgp_conf->hold_time); + snmp_varbind_int(d->c, bgp_conf->hold_time); return SNMP_SEARCH_OK; } static enum snmp_search_res -fill_keep_alive_conf(struct snmp_proto *p, struct snmp_pdu *c) +fill_keep_alive_conf(struct mib_walk_state *walk UNUSED, struct snmp_data *d) { + snmp_log("fill keepalive c"); enum snmp_search_res res; DECLARE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf); res = POPULATE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf); if (res != SNMP_SEARCH_OK) - { - (void) snmp_set_varbind_type(c->sr_vb_start, res); return res; - } - - if (c->size < AGENTX_TYPE_INT_SIZE) - snmp_manage_tbuf(p, c); if (!bgp_conf->keepalive_time) - snmp_varbind_int(c, 0); + snmp_varbind_int(d->c, 0); else - snmp_varbind_int(c, + snmp_varbind_int(d->c, (bgp_conn) ? bgp_conn->keepalive_time : 0); return SNMP_SEARCH_OK; } static enum snmp_search_res -fill_min_as_org_interval(struct snmp_proto *p, struct snmp_pdu *c) +fill_min_as_org_interval(struct mib_walk_state *walk UNUSED, struct snmp_data *d) { + snmp_log("fill min org int"); enum snmp_search_res res; DECLARE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf); res = POPULATE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf); if (res != SNMP_SEARCH_OK) - { - (void) snmp_set_varbind_type(c->sr_vb_start, res); return res; - } /* value should be in 1..65535 but is not supported by bird */ - if (c->size < AGENTX_TYPE_INT_SIZE) - snmp_manage_tbuf(p, c); - - snmp_varbind_int(c, 0); + snmp_varbind_int(d->c, 0); return SNMP_SEARCH_OK; } static enum snmp_search_res -fill_route_adv_interval(struct snmp_proto *p, struct snmp_pdu *c) +fill_route_adv_interval(struct mib_walk_state *walk UNUSED, struct snmp_data *d) { + snmp_log("fill rt adv int"); enum snmp_search_res res; DECLARE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf); res = POPULATE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf); if (res != SNMP_SEARCH_OK) - { - (void) snmp_set_varbind_type(c->sr_vb_start, res); return res; - } /* value should be in 1..65535 but is not supported by bird */ - if (c->size < AGENTX_TYPE_INT_SIZE) - snmp_manage_tbuf(p, c); - - snmp_varbind_int(c, 0); + snmp_varbind_int(d->c, 0); return SNMP_SEARCH_OK; } static enum snmp_search_res -fill_in_update_elapsed_time(struct snmp_proto *p, struct snmp_pdu *c) +fill_in_update_elapsed_time(struct mib_walk_state *walk UNUSED, struct snmp_data *d) { + snmp_log("fil in elapsed"); enum snmp_search_res res; DECLARE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf); res = POPULATE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf); if (res != SNMP_SEARCH_OK) - { - (void) snmp_set_varbind_type(c->sr_vb_start, res); return res; - } - if (c->size < AGENTX_TYPE_INT_SIZE) - snmp_manage_tbuf(p, c); - - snmp_varbind_gauge32(c, + snmp_varbind_gauge32(d->c, (current_time() - bgp_proto->last_rx_update) TO_S ); return SNMP_SEARCH_OK; } +static enum snmp_search_res +fill_local_id(struct mib_walk_state *walk UNUSED, struct snmp_data *d) +{ + snmp_log("fill local id"); + snmp_varbind_ip4(d->c, d->p->bgp_local_id); + return SNMP_SEARCH_OK; +} + static struct oid * update_bgp_vb(struct snmp_proto *p, struct agentx_varbind **vb, u8 state, struct snmp_pdu *c) { @@ -1499,8 +1383,8 @@ bgp_fill_dynamic(struct snmp_proto *p, struct agentx_varbind **vb, struct snmp_p return; } - const struct bgp_conn *bgp_conn = bgp_proto->conn; - const struct bgp_stats *bgp_stats = &bgp_proto->stats; + //const struct bgp_conn *bgp_conn = bgp_proto->conn; + //const struct bgp_stats *bgp_stats = &bgp_proto->stats; const struct bgp_config *bgp_conf = bgp_proto->cf; uint fsm_state = snmp_bgp_fsm_state(bgp_proto); @@ -1513,18 +1397,24 @@ bgp_fill_dynamic(struct snmp_proto *p, struct agentx_varbind **vb, struct snmp_p { case BGP4_MIB_S_PEER_IDENTIFIER: if (c->size < AGENTX_TYPE_IP4_SIZE) + { + snmp_log("BGP4-MIB small buffer"); snmp_manage_tbuf(p, c); + } if (fsm_state == BGP4_MIB_OPENCONFIRM || fsm_state == BGP4_MIB_ESTABLISHED) // TODO last - ; //snmp_varbind_ip4(*vb, c, ip4_from_u32(bgp_proto->remote_id)); + {} //snmp_varbind_ip4(*vb, c, ip4_from_u32(bgp_proto->remote_id)); else - ; //snmp_varbind_ip4(*vb, c, IP4_NONE); + {} //snmp_varbind_ip4(*vb, c, IP4_NONE); break; case BGP4_MIB_S_STATE: if (c->size < AGENTX_TYPE_INT_SIZE) + { + snmp_log("BGP4-MIB small buffer 2"); snmp_manage_tbuf(p, c); + } //snmp_varbind_int(*vb, c, fsm_state); break; @@ -1534,9 +1424,9 @@ bgp_fill_dynamic(struct snmp_proto *p, struct agentx_varbind **vb, struct snmp_p SNMP_MANAGE_TBUF(p, vb, c); if (bgp_proto->p.disabled) - ; //snmp_varbind_int(*vb, c, AGENTX_ADMIN_STOP); + {} //snmp_varbind_int(*vb, c, AGENTX_ADMIN_STOP); else - ; //snmp_varbind_int(*vb, c, AGENTX_ADMIN_START); + {} //snmp_varbind_int(*vb, c, AGENTX_ADMIN_START); break; @@ -1547,9 +1437,9 @@ bgp_fill_dynamic(struct snmp_proto *p, struct agentx_varbind **vb, struct snmp_p uint fsm_state = snmp_bgp_fsm_state(bgp_proto); if (fsm_state == BGP4_MIB_ESTABLISHED || fsm_state == BGP4_MIB_ESTABLISHED) - ; //snmp_varbind_int(*vb, c, BGP4_MIB_NEGOTIATED_VER_VALUE); + {} //snmp_varbind_int(*vb, c, BGP4_MIB_NEGOTIATED_VER_VALUE); else - ; //snmp_varbind_int(*vb, c, BGP4_MIB_NEGOTIATED_VER_NO_VALUE); + {} //snmp_varbind_int(*vb, c, BGP4_MIB_NEGOTIATED_VER_NO_VALUE); break; @@ -1557,70 +1447,70 @@ bgp_fill_dynamic(struct snmp_proto *p, struct agentx_varbind **vb, struct snmp_p if (c->size < AGENTX_TYPE_IP4_SIZE) SNMP_MANAGE_TBUF(p, vb, c); - ; //snmp_varbind_ip4(*vb, c, ipa_to_ip4(bgp_proto->local_ip)); + {} //snmp_varbind_ip4(*vb, c, ipa_to_ip4(bgp_proto->local_ip)); break; case BGP4_MIB_S_LOCAL_PORT: if (c->size < AGENTX_TYPE_INT_SIZE) SNMP_MANAGE_TBUF(p, vb, c); - ; //snmp_varbind_int(*vb, c, bgp_conf->local_port); + {} //snmp_varbind_int(*vb, c, bgp_conf->local_port); break; case BGP4_MIB_S_REMOTE_ADDR: if (c->size < AGENTX_TYPE_IP4_SIZE) SNMP_MANAGE_TBUF(p, vb, c); - ; //snmp_varbind_ip4(*vb, c, ipa_to_ip4(bgp_proto->remote_ip)); + {} //snmp_varbind_ip4(*vb, c, ipa_to_ip4(bgp_proto->remote_ip)); break; case BGP4_MIB_S_REMOTE_PORT: if (c->size < AGENTX_TYPE_INT_SIZE) SNMP_MANAGE_TBUF(p, vb, c); - ; //snmp_varbind_int(*vb, c, bgp_conf->remote_port); + {} //snmp_varbind_int(*vb, c, bgp_conf->remote_port); break; case BGP4_MIB_S_REMOTE_AS: if (c->size < AGENTX_TYPE_INT_SIZE) SNMP_MANAGE_TBUF(p, vb, c); - ; //snmp_varbind_int(*vb, c, bgp_proto->remote_as); + {} //snmp_varbind_int(*vb, c, bgp_proto->remote_as); break; case BGP4_MIB_S_RX_UPDATES: /* bgpPeerInUpdates */ if (c->size < AGENTX_TYPE_COUNTER32_SIZE) SNMP_MANAGE_TBUF(p, vb, c); - ; //snmp_varbind_counter32(*vb, c, bgp_stats->rx_updates); + {} //snmp_varbind_counter32(*vb, c, bgp_stats->rx_updates); break; case BGP4_MIB_S_TX_UPDATES: /* bgpPeerOutUpdate */ if (c->size < AGENTX_TYPE_COUNTER32_SIZE) SNMP_MANAGE_TBUF(p, vb, c); - ; //snmp_varbind_counter32(*vb, c, bgp_stats->tx_updates); + {} //snmp_varbind_counter32(*vb, c, bgp_stats->tx_updates); break; case BGP4_MIB_S_RX_MESSAGES: /* bgpPeerInTotalMessages */ if (c->size < AGENTX_TYPE_COUNTER32_SIZE) SNMP_MANAGE_TBUF(p, vb, c); - ; //snmp_varbind_counter32(*vb, c, bgp_stats->rx_messages); + {} //snmp_varbind_counter32(*vb, c, bgp_stats->rx_messages); break; case BGP4_MIB_S_TX_MESSAGES: /* bgpPeerOutTotalMessages */ if (c->size < AGENTX_TYPE_COUNTER32_SIZE) SNMP_MANAGE_TBUF(p, vb, c); - ; //snmp_varbind_counter32(*vb, c, bgp_stats->tx_messages); + {} //snmp_varbind_counter32(*vb, c, bgp_stats->tx_messages); break; case BGP4_MIB_S_LAST_ERROR: if (c->size < snmp_str_size_from_len(2)) SNMP_MANAGE_TBUF(p, vb, c); - ; //snmp_varbind_nstr(*vb, c, last_error, 2); + {} //snmp_varbind_nstr(*vb, c, last_error, 2); break; case BGP4_MIB_S_FSM_TRANSITIONS: @@ -1659,9 +1549,9 @@ bgp_fill_dynamic(struct snmp_proto *p, struct agentx_varbind **vb, struct snmp_p SNMP_MANAGE_TBUF(p, vb, c); if (!bgp_conf->hold_time) - ; //snmp_varbind_int(*vb, c, 0); + {} //snmp_varbind_int(*vb, c, 0); else - ; //snmp_varbind_int(*vb, c, + {} //snmp_varbind_int(*vb, c, // (bgp_conn) ? bgp_conn->keepalive_time : 0); break; @@ -1678,9 +1568,9 @@ bgp_fill_dynamic(struct snmp_proto *p, struct agentx_varbind **vb, struct snmp_p if (!bgp_conf->keepalive_time) - ; //snmp_varbind_int(*vb, c, 0); + {} //snmp_varbind_int(*vb, c, 0); else - ; //snmp_varbind_int(*vb, c, + {} //snmp_varbind_int(*vb, c, // (bgp_conn) ? bgp_conn->keepalive_time : 0); break; @@ -1730,6 +1620,7 @@ bgp_fill_dynamic(struct snmp_proto *p, struct agentx_varbind **vb, struct snmp_p void bgp_fill_static(struct snmp_proto *p, struct agentx_varbind **vb, struct snmp_pdu *c, u8 state) { + (void)p; ASSUME(c->buffer == snmp_varbind_data(*vb)); struct oid *oid = &(*vb)->name; @@ -1795,6 +1686,82 @@ snmp_bgp_fill(struct snmp_proto *p, struct agentx_varbind **vb, struct snmp_pdu snmp_set_varbind_type(*vb, AGENTX_NO_SUCH_OBJECT); } +/* + * bgp4_next_peer + */ +static int UNUSED +bgp4_next_peer(struct mib_walk_state *state, struct snmp_data *data) +{ + //struct agentx_varbind *vb = data->c->sr_vb_start; + struct oid *oid = &data->c->sr_vb_start->name; + + ip4_addr ip4 = ip4_from_oid(oid); + + /* BGP4-MIB::bgpPeerIdentifier */ + STATIC_OID(9) bgp4_peer_id = { + .n_subid = 9, + .prefix = SNMP_MGMT, + .include = 0, + .reserved = 0, + .ids = { SNMP_MIB_2, BGP4_MIB, + BGP4_MIB_PEER_TABLE, BGP4_MIB_PEER_ENTRY, BGP4_MIB_PEER_IDENTIFIER, + /* IP4_NONE */ 0, 0, 0, 0 } + }; + + const struct oid *peer_oid = (const struct oid *) &bgp4_peer_id; + + if (snmp_oid_compare(oid, peer_oid) < 0 || LOAD_U8(oid->n_subid) < 9) + { + die("unreachable?"); + + int old = snmp_oid_size(oid); + int new = snmp_oid_size(peer_oid); + + if (new - old > 0 && (uint) new - old > data->c->size) + { + snmp_log("bgp4_next_peer small buffer"); + snmp_manage_tbuf(data->p, data->c); + } + + if (new > old) + data->c->buffer += (new - old); + + snmp_oid_copy(oid, peer_oid); + + STORE_U8(oid->include, 1); + } + + ASSUME(oid->n_subid == 9); + /* +1 includes empty prefix */ + + net_addr net; + net_fill_ip4(&net, ip4, IP4_MAX_PREFIX_LENGTH); + struct f_trie_walk_state ws; + + int match = trie_walk_init(&ws, data->p->bgp_trie, &net, 1); + + if (match && LOAD_U8(oid->include)) + { + STORE_U8(oid->include, 0); + ip4_to_oid(oid, ip4); + return 0; + } + + /* We skip the first match as we should not include ip address in oid */ + if (match) + (void) trie_walk_next(&ws, &net); + + if (trie_walk_next(&ws, &net)) + { + ASSUME(oid->n_subid == 9); + ip4_addr res = ipa_to_ip4(net_prefix(&net)); + ip4_to_oid(oid, res); + return 0; + } + + return 1; +} + /* * snmp_bgp_start - prepare BGP4-MIB * @p - SNMP protocol instance holding memory pool @@ -1803,7 +1770,7 @@ snmp_bgp_fill(struct snmp_proto *p, struct agentx_varbind **vb, struct snmp_pdu * It is gruaranteed that the BGP protocols exist. */ void -snmp_bgp_start(struct snmp_proto *p) +snmp_bgp4_start(struct snmp_proto *p) { struct snmp_config *cf = SKIP_BACK(struct snmp_config, cf, p->p.cf); /* Create binding to BGP protocols */ @@ -1828,4 +1795,201 @@ snmp_bgp_start(struct snmp_proto *p) snmp_hash_add_peer(p, peer); } + + const STATIC_OID(2) bgp4_mib_root = { + .n_subid = 2, + .prefix = SNMP_MGMT, + .include = 0, + .reserved = 0, + .ids = { SNMP_MIB_2, BGP4_MIB }, + }; + + const STATIC_OID(4) bgp4_mib_peer_entry = { + .n_subid = 4, + .prefix = SNMP_MGMT, + .include = 0, + .reserved = 0, + .ids = { SNMP_MIB_2, BGP4_MIB, BGP4_MIB_PEER_TABLE, BGP4_MIB_PEER_ENTRY }, + }; + + (void) mib_tree_hint(p->pool, p->mib_tree, + (const struct oid *) &bgp4_mib_root, BGP4_MIB_IDENTIFIER); + (void) mib_tree_hint(p->pool, p->mib_tree, + (const struct oid *) &bgp4_mib_peer_entry, BGP4_MIB_IN_UPDATE_ELAPSED_TIME); + + mib_node_u *node; + struct mib_leaf *leaf; + STATIC_OID(3) bgp4_var = { + .n_subid = 3, + .prefix = SNMP_MGMT, + .include = 0, + .reserved = 0, + .ids = { SNMP_MIB_2, BGP4_MIB, BGP4_MIB_VERSION }, + }; + + struct { + u32 id; + enum snmp_search_res (*filler)(struct mib_walk_state *state, struct snmp_data *data); + enum agentx_type type; + int size; + } leafs[] = { + { + .id = BGP4_MIB_VERSION, + .filler = fill_bgp_version, + .type = AGENTX_OCTET_STRING, + .size = snmp_str_size_from_len(sizeof(BGP4_VERSIONS)), + }, + { + .id = BGP4_MIB_LOCAL_AS, + .filler = fill_local_as, + .type = AGENTX_INTEGER, + }, + { + .id = BGP4_MIB_IDENTIFIER, + .filler = fill_local_id, + .type = AGENTX_IP_ADDRESS, + }, + }; + + for (uint i = 0; i < ARRAY_SIZE(leafs); i++) + { + bgp4_var.ids[ARRAY_SIZE(bgp4_var.ids) - 1] = leafs[i].id; + node = mib_tree_add(p->pool, p->mib_tree, (const struct oid *) &bgp4_var, 1); + + ASSUME(mib_node_is_leaf(node)); + leaf = &node->leaf; + + leaf->filler = leafs[i].filler; + leaf->call_next = NULL; // TODO + leaf->type = leafs[i].type; + leaf->size = leafs[i].size; + } + + STATIC_OID(5) bgp4_entry_var = { + .n_subid = 5, + .prefix = SNMP_MGMT, + .include = 0, + .reserved = 0, + .ids = { SNMP_MIB_2, BGP4_MIB, + BGP4_MIB_PEER_TABLE, BGP4_MIB_PEER_ENTRY, BGP4_MIB_PEER_IDENTIFIER }, + }; + + struct { + enum snmp_search_res (*filler)(struct mib_walk_state *state, struct snmp_data *data); + enum agentx_type type; + int size; + } entry_leafs[] = { + [BGP4_MIB_PEER_IDENTIFIER] = { + .filler = fill_peer_id, + .type = AGENTX_IP_ADDRESS, + }, + [BGP4_MIB_STATE] = { + .filler = fill_peer_state, + .type = AGENTX_INTEGER, + }, + [BGP4_MIB_ADMIN_STATUS] = { + .filler = fill_admin_status, + .type = AGENTX_INTEGER, + }, + [BGP4_MIB_NEGOTIATED_VERSION] = { + .filler = fill_neg_version, + .type = AGENTX_INTEGER, + }, + [BGP4_MIB_LOCAL_ADDR] = { + .filler = fill_local_addr, + .type = AGENTX_IP_ADDRESS, + }, + [BGP4_MIB_LOCAL_PORT] = { + .filler = fill_local_port, + .type = AGENTX_INTEGER, + }, + [BGP4_MIB_REMOTE_ADDR] = { + .filler = fill_remote_addr, + .type = AGENTX_IP_ADDRESS, + }, + [BGP4_MIB_REMOTE_PORT] = { + .filler = fill_remote_port, + .type = AGENTX_INTEGER, + }, + [BGP4_MIB_REMOTE_AS] = { + .filler = fill_remote_as, + .type = AGENTX_INTEGER, + }, + [BGP4_MIB_RX_UPDATES] = { + .filler = fill_in_updates, + .type = AGENTX_COUNTER_32, + }, + [BGP4_MIB_TX_UPDATES] = { + .filler = fill_out_updates, + .type = AGENTX_COUNTER_32, + }, + [BGP4_MIB_RX_MESSAGES] = { + .filler = fill_in_total_msg, + .type = AGENTX_COUNTER_32, + }, + [BGP4_MIB_TX_MESSAGES] = { + .filler = fill_out_total_msg, + .type = AGENTX_COUNTER_32, + }, + [BGP4_MIB_LAST_ERROR] = { + .filler = fill_last_err, + .type = AGENTX_OCTET_STRING, + .size = snmp_str_size_from_len(2), + }, + [BGP4_MIB_FSM_TRANSITIONS] = { + .filler = fill_established_trans, + .type = AGENTX_COUNTER_32, + }, + [BGP4_MIB_FSM_ESTABLISHED_TIME] = { + .filler = fill_established_time, + .type = AGENTX_GAUGE_32, + }, + [BGP4_MIB_RETRY_INTERVAL] = { + .filler = fill_retry_interval, + .type = AGENTX_INTEGER, + }, + [BGP4_MIB_HOLD_TIME] = { + .filler = fill_hold_time, + .type = AGENTX_INTEGER, + }, + [BGP4_MIB_KEEPALIVE] = { + .filler = fill_keep_alive, + .type = AGENTX_INTEGER, + }, + [BGP4_MIB_HOLD_TIME_CONFIGURED] = { + .filler = fill_hold_time_conf, + .type = AGENTX_INTEGER, + }, + [BGP4_MIB_KEEPALIVE_CONFIGURED] = { + .filler = fill_keep_alive_conf, + .type = AGENTX_INTEGER, + }, + [BGP4_MIB_ORIGINATION_INTERVAL] = { + .filler = fill_min_as_org_interval, + .type = AGENTX_INTEGER, + }, + [BGP4_MIB_MIN_ROUTE_ADVERTISEMENT] = { + .filler = fill_route_adv_interval, + .type = AGENTX_INTEGER, + }, + [BGP4_MIB_IN_UPDATE_ELAPSED_TIME] = { + .filler = fill_in_update_elapsed_time, + .type = AGENTX_GAUGE_32, + }, + }; /* struct _anon entry_leafs[] */ + + for (enum bgp4_mib_peer_entry_row e = BGP4_MIB_PEER_IDENTIFIER; + e <= BGP4_MIB_IN_UPDATE_ELAPSED_TIME; e++) + { + bgp4_entry_var.ids[ARRAY_SIZE(bgp4_entry_var.ids) - 1] = (u32) e; + node = mib_tree_add(p->pool, p->mib_tree, (const struct oid *) &bgp4_entry_var, 1); + + ASSUME(mib_node_is_leaf(node)); + leaf = &node->leaf; + + leaf->filler = entry_leafs[e].filler; + leaf->call_next = bgp4_next_peer; + leaf->type = entry_leafs[e].type; + leaf->size = entry_leafs[e].size; + } } diff --git a/proto/snmp/bgp4_mib.h b/proto/snmp/bgp4_mib.h index 2cac7d8c..b77f7f34 100644 --- a/proto/snmp/bgp4_mib.h +++ b/proto/snmp/bgp4_mib.h @@ -1,29 +1,10 @@ #ifndef _BIRD_SNMP_BGP4_MIB_H_ #define _BIRD_SNMP_BGP4_MIB_H_ -#ifdef _BIRD_SNMP_SUBAGENT_H_ -#define BIRD_SNMP_BGP4_SKIP -#endif - #include "snmp.h" #include "proto/bgp/bgp.h" - -void snmp_bgp4_register(struct snmp_proto *p); - -struct bgp4_mib { - enum snmp_tags tag; /* always BGP4_MIB, see subagent.h for more details */ - - ip4_addr addr; - const struct bgp_proto *bgp_proto; - const struct bgp_conn *bgp_conn; - const struct bgp_stats *bgp_stats; - const struct bgp_config *bgp_conf; -}; - #include "subagent.h" -#ifndef BIRD_SNMP_BGP4_SKIP - #define BGP4_MIB 15 /* peers attributes */ @@ -63,6 +44,8 @@ enum bgp4_mib_peer_entry_row { u8 snmp_bgp_get_valid(u8 state); u8 snmp_bgp_getnext_valid(u8 state); +void snmp_bgp4_register(struct snmp_proto *p); + enum snmp_search_res snmp_bgp_search(struct snmp_proto *p, struct agentx_varbind **vb_search, const struct oid *o_end, struct snmp_pdu *c); enum snmp_search_res snmp_bgp_search2(struct snmp_proto *p, struct oid **searched, const struct oid *o_end, uint contid); void snmp_bgp_fill(struct snmp_proto *p, struct agentx_varbind **vb, struct snmp_pdu *c); @@ -142,5 +125,3 @@ STATIC_ASSERT(BGP4_MIB_ESTABLISHED == BS_ESTABLISHED + 1); #define BGP4_MIB_BACKWARD_TRANS_NOTIFICATION 2 #endif - -#endif diff --git a/proto/snmp/mib_tree.c b/proto/snmp/mib_tree.c index a76723e2..709f7014 100644 --- a/proto/snmp/mib_tree.c +++ b/proto/snmp/mib_tree.c @@ -57,6 +57,27 @@ mib_tree_init(pool *p, struct mib_tree *t) (void) mib_tree_add(p, t, oid, 0); } +int +mib_tree_hint(pool *p, struct mib_tree *t, const struct oid *oid, uint size) +{ + mib_node_u *node = mib_tree_add(p, t, oid, 0); + if (!node || mib_node_is_leaf(node)) + return 0; + + struct mib_node *inner = &node->inner; + if (inner->child_len >= size + 1) + return 1; + + u32 old_len = inner->child_len; + inner->child_len = size + 1; + inner->children = realloc(inner->children, + inner->child_len * sizeof(mib_node_u *)); + + for (u32 i = old_len; i < inner->child_len; i++) + inner->children[i] = NULL; + return 1; +} + // TODO: This function does not work with leaf nodes inside the snmp_internet prefix // area @@ -602,6 +623,61 @@ mib_tree_walk_to_oid(const struct mib_walk_state *walk, struct oid *result, u32 return 0; } +/** + * mib_tree_walk_is_oid_descendant - check if OID is in walk subtree + * @walk: MIB tree walk state + * @oid: OID to use + * + * Return 0 if @walk specify same path in MIB tree as @oid, return +1 if @oid is + * in @walk subtree, return -1 otherwise. + */ +int +mib_tree_walk_is_oid_descendant(const struct mib_walk_state *walk, const struct oid *oid) +{ + /* walk stack index skipped zero prefix and OID subidentifier index */ + u32 i = 1, j = 0; + + if (!walk->stack_pos && snmp_is_oid_empty(oid)) + return 0; + + if (snmp_oid_is_prefixed(oid)) + { + for (; i < MIN(walk->stack_pos - 1, ARRAY_SIZE(snmp_internet) + 1); i++) + { + if (walk->stack[i]->empty.id != snmp_internet[i - 1]) + return -1; + } + + if (i == walk->stack_pos) + return +1; + + if (i < walk->stack_pos && + walk->stack[i]->empty.id != (u32)LOAD_U8(oid->prefix)) + return -1; + + i++; + } + + u32 ids = LOAD_U8(oid->n_subid); + for (; i < walk->stack_pos && j < ids; i++, j++) + { + if (walk->stack[i]->empty.id != LOAD_U32(oid->ids[j])) + return -1; + } + + if (i < walk->stack_pos) + return -1; + else if (i == walk->stack_pos && j == ids) + return 0; + else if (i == walk->stack_pos) + return +1; + else + { + die("unreachable"); + return -1; + } +} + mib_node_u * mib_tree_walk_next(const struct mib_tree *t, struct mib_walk_state *walk) { @@ -656,7 +732,10 @@ mib_tree_walk_next_leaf(const struct mib_tree *t, struct mib_walk_state *walk) (void)t; if (walk->stack_pos == 0) + { + snmp_log("walk next leaf no leafs"); return NULL; + } u32 next_id = 0; mib_node_u *node = walk->stack[walk->stack_pos - 1]; @@ -671,6 +750,7 @@ mib_tree_walk_next_leaf(const struct mib_tree *t, struct mib_walk_state *walk) { /* walk->stack_pos == 1, so we NULL out the last stack field */ walk->stack[--walk->stack_pos] = NULL; + snmp_log("walk next leaf single leaf"); return NULL; } @@ -680,7 +760,10 @@ continue_while: node = walk->stack[walk->stack_pos - 1]; if (mib_node_is_leaf(node)) + { + snmp_log("walk next leaf %p at level %u", node, walk->stack_pos - 1); return (struct mib_leaf *) node; + } struct mib_node *node_inner = &node->inner; for (u32 id = next_id; id < node_inner->child_len; id++) @@ -700,6 +783,7 @@ continue_while: walk->stack[--walk->stack_pos] = NULL; } + snmp_log("walk next leaf no more leafs"); return NULL; } diff --git a/proto/snmp/mib_tree.h b/proto/snmp/mib_tree.h index 14e8d7d8..cf1b94fd 100644 --- a/proto/snmp/mib_tree.h +++ b/proto/snmp/mib_tree.h @@ -26,12 +26,52 @@ struct mib_node { struct mib_walk_state; +//typedef enum snmp_search_res (*snmp_filler_hook_t)(struct mib_walk_state *state, struct snmp_data *data); + struct mib_leaf { struct mib_node_core c; - enum snmp_search_res (*filler)(struct snmp_proto *p, struct snmp_pdu *c); - //enum snmp_search_res (*filler)(struct snmp_proto_pdu *pc, struct agentx_varbind **vb); - int (*call_next)(struct snmp_proto *p, struct snmp_pdu *c, struct mib_walk_state *state); + + /** + * filler - hook for filling VarBind data value + * @state: self referencing MIB tree walk state + * @data: box holding destiantion VarBind and SNMP protocol instance + * + * If corresponding leaf node has filled in AgentX type and/or size, it is + * guaranteed that PDU buffer have enough space. Hook mustn't be NULL. + * If the leaf node has set valid type, the varbind type will be automatically + * set by the snmp_walk_fill() servicing routine. If the field type is set to + * AGENTX_INVALID, it is expected that filler() hook will also fill + * the VarBind type. + */ + enum snmp_search_res (*filler)(struct mib_walk_state *state, struct snmp_data *data); + + /** + * call_next - signal multileaf + * @state: self referencing MIB tree walk state + * @data: box holding destination VarBind and SNMP protocol insntace + * + * MIB modules can implement subtrees by a single leaf node in MIB node tree. + * When the tree is walked, the specific leaf node has to be returned multiple + * times. The @call_next hook determines if we should move to next leaf node. + * It is expected that call_next() hook may change the VarBind to be filled. + * + * Hook may be NULL meaning the leaf node is not multileaf/subtree. + * + */ + int (*call_next)(struct mib_walk_state *state, struct snmp_data *data); + + /** + * type of produced VarBind, may be replaced in packet instanciation by + * AGENTX_NO_SUCH_OBJECT, AGENTX_NO_SUCH_INSTANCE or AGENTX_END_OF_MIB_VIEW + * The field is unspecified if equal to AGENTX_INVALID. + */ enum agentx_type type; + + /* + * Specify upper bound of VarBind data size. If set to -1, all handling must + * be done in filler() hook. In all other cases the filler() hook has + * guaranteed that the space is available. + */ int size; }; @@ -70,6 +110,9 @@ mib_node_u *mib_tree_find(const struct mib_tree *tree, struct mib_walk_state *wa mib_node_u *mib_tree_walk_next(const struct mib_tree *t, struct mib_walk_state *walk); struct mib_leaf *mib_tree_walk_next_leaf(const struct mib_tree *t, struct mib_walk_state *walk); +int mib_tree_hint(pool *p, struct mib_tree *t, const struct oid *oid, uint size); +int mib_tree_walk_is_oid_descendant(const struct mib_walk_state *walk, const struct oid *oid); + static inline int mib_node_is_leaf(const mib_node_u *node) { diff --git a/proto/snmp/snmp.c b/proto/snmp/snmp.c index dcc75491..61c54dbd 100644 --- a/proto/snmp/snmp.c +++ b/proto/snmp/snmp.c @@ -117,6 +117,7 @@ #include "snmp.h" #include "subagent.h" #include "snmp_utils.h" +#include "mib_tree.h" static void snmp_start_locked(struct object_lock *lock); static void snmp_sock_err(sock *sk, int err); @@ -493,6 +494,7 @@ snmp_start(struct proto *P) p->pool = p->p.pool; p->lp = lp_new(p->pool); + p->mib_tree = mb_alloc(p->pool, sizeof(struct mib_tree)); p->bgp_trie = f_new_trie(p->lp, 0); p->startup_timer = tm_new_init(p->pool, snmp_startup_timeout, p, 0, 0); @@ -502,7 +504,9 @@ snmp_start(struct proto *P) /* We create copy of bonds to BGP protocols. */ HASH_INIT(p->bgp_hash, p->pool, 10); - snmp_bgp_start(p); + + mib_tree_init(p->pool, p->mib_tree); + snmp_bgp4_start(p); return snmp_set_state(p, SNMP_INIT); } @@ -561,7 +565,7 @@ snmp_reconfigure(struct proto *P, struct proto_config *CF) { /* Reinitialize the hash after snmp_shutdown() */ HASH_INIT(p->bgp_hash, p->pool, 10); - snmp_bgp_start(p); + snmp_bgp4_start(p); } return config_changed; diff --git a/proto/snmp/snmp.h b/proto/snmp/snmp.h index fde7f995..386e23a5 100644 --- a/proto/snmp/snmp.h +++ b/proto/snmp/snmp.h @@ -28,7 +28,7 @@ #define SNMP_RX_BUFFER_SIZE 8192 #define SNMP_TX_BUFFER_SIZE 8192 -#define SNMP_PKT_SIZE_MAX 8192 +#define SNMP_PKT_SIZE_MAX 4098 enum snmp_proto_state { SNMP_DOWN = 0, @@ -41,11 +41,6 @@ enum snmp_proto_state { SNMP_RESET, }; -enum snmp_tags { - EMPTY_TAG = 0, - BGP4_TAG, -}; - struct snmp_bond { node n; struct proto_config *config; @@ -90,8 +85,6 @@ struct snmp_registered_oid { struct oid *oid; }; -struct mib_tree; /* see mib_tree.h */ - struct snmp_proto { struct proto p; struct object_lock *lock; diff --git a/proto/snmp/snmp_test.c b/proto/snmp/snmp_test.c index 43b38fed..1ac89bd9 100644 --- a/proto/snmp/snmp_test.c +++ b/proto/snmp/snmp_test.c @@ -33,7 +33,7 @@ static int t_oid_empty(void); static int t_oid_compare(void); static int t_oid_prefixize(void); -static int t_walk_to_oid(void); +static int t_walk_oid_desc(void); static int t_tree_find(void); static int t_tree_traversal(void); static int t_tree_leafs(void); @@ -45,6 +45,7 @@ static int t_tree_delete(void); #define SMALL_TESTS_NUM 10 static int tree_sizes[] = { 0, 1, 10, 100, 1000 }; +/* smaller than theoretical maximum (2^32) to fit in memory */ #define OID_MAX_ID 16 #define SNMP_EXPECTED(actual, expected) \ @@ -183,8 +184,6 @@ random_oid(void) return random_prefixable_oid(); } - - static int t_oid_empty(void) { @@ -603,6 +602,7 @@ t_walk_to_oid(void) return 1; } + static void test_both(void *buffer, uint size, const struct oid *left, const struct oid *right, const struct oid *expected) @@ -735,6 +735,121 @@ generate_oids(struct oid *oids[], struct oid *sorted[], int size, struct oid *(* return (size > 1) ? last_used + 1 : size; } +static int +t_walk_oid_desc(void) +{ + lp_state tmps; + lp_save(tmp_linpool, &tmps); + + pool *pool = &root_pool; + + struct mib_tree storage, *tree = &storage; + mib_tree_init(pool, tree); + + STATIC_ASSERT(ARRAY_SIZE(tree_sizes) > 0); + int size = tree_sizes[ARRAY_SIZE(tree_sizes) - 1]; + ASSERT(size > 0); + struct oid **oids = mb_alloc(pool, size * sizeof(struct oid *)); + struct oid **sorted = mb_alloc(pool, size * sizeof(struct oid *)); + + (void) generate_oids(oids, sorted, size, random_oid); + + for (int i = 0; i < size; i++) + (void) mib_tree_add(pool, tree, oids[i], 0); + + for (int test = 0; test < size; test++) + { + int i = xrandom(size); + + char buffer[1024]; + struct oid *oid = (void *) buffer; + + memcpy(buffer, oids[i], snmp_oid_size(oids[i])); + + struct mib_walk_state walk; + mib_tree_walk_init(&walk, NULL); + (void) mib_tree_find(tree, &walk, oid); + + int type = xrandom(4); + switch (type) + { + case 0: + bt_assert(mib_tree_walk_is_oid_descendant(&walk, oids[i]) == 0); + break; + + case 1: + { + /* oid is longer than walk or has same length */ + u8 ids = LOAD_U8(oid->n_subid); + u32 upto = MIN(OID_MAX_LEN - ids, 16); + + if (!upto) + continue; + + u32 new = xrandom(upto) + 1; + STORE_U8(oid->n_subid, ids + new); + ASSERT(snmp_oid_size(oid) < 1024); + + for (u32 i = 0; i < new; i++) + STORE_U32(oid->ids[ids + i], xrandom(OID_MAX_ID)); + + bt_assert(mib_tree_walk_is_oid_descendant(&walk, oid) > 0); + + break; + } + case 2: + case 3: + { + /* oid is shorter than walk */ + u8 ids = LOAD_U8(oid->n_subid); + + if (ids == 0 || ids == OID_MAX_LEN) + continue; + + u32 split = (ids > 1) ? xrandom(ids - 1) + 1 : 0; + u32 ext = (type == 3) ? xrandom(MIN(OID_MAX_LEN - ids, 16)) : 0; + + STORE_U16(oid->n_subid, split + ext); + for (u32 i = 0; i < ext; i++) + STORE_U32(oid->ids[split + i], xrandom(OID_MAX_ID)); + + int no_change = 1; + for (u32 j = 0; j < MIN(ids, split + ext); j++) + { + if (LOAD_U32(oid->ids[split + j]) != LOAD_U32(oids[i]->ids[split + j])) + no_change = 1; + } + + if (no_change) + continue; + + bt_assert(mib_tree_walk_is_oid_descendant(&walk, oid) < 0); + break; + } + } + } + + { + struct mib_walk_state walk; + mib_tree_walk_init(&walk, tree); + + u32 zero = 0; + const struct oid *null_oid = (void *) &zero; + u32 index = xrandom(size); + + bt_assert(mib_tree_walk_is_oid_descendant(&walk, null_oid) == 0); + bt_assert(mib_tree_walk_is_oid_descendant(&walk, oids[index]) > 0); + (void) mib_tree_find(tree, &walk, oids[index]); + bt_assert(mib_tree_walk_is_oid_descendant(&walk, null_oid) < 0); + } + + u32 null_oid = 0; + mib_tree_remove(tree, (struct oid *) &null_oid); + lp_restore(tmp_linpool, &tmps); + + return 1; +} + static void UNUSED print_dups(const struct oid *oids[], uint size) { @@ -1686,8 +1801,8 @@ int main(int argc, char **argv) bt_init(argc, argv); bt_bird_init(); - unsigned seed = rand(); - //unsigned seed = 1000789714; + //unsigned seed = rand(); + unsigned seed = 1000789714; log("random seed is %d", seed); srandom(seed); @@ -1696,6 +1811,7 @@ int main(int argc, char **argv) bt_test_suite(t_oid_prefixize, "Function transforming OID to prefixed form"); bt_test_suite(t_oid_ancestor, "Function finding common ancestor of two OIDs"); bt_test_suite(t_walk_to_oid, "Function transforming MIB tree walk state to OID"); + bt_test_suite(t_walk_oid_desc, "Function comparing MIB tree walk to OID"); bt_test_suite(t_tree_find, "MIB tree search"); bt_test_suite(t_tree_traversal, "MIB tree traversal"); diff --git a/proto/snmp/snmp_utils.c b/proto/snmp/snmp_utils.c index 76b86684..53f862ca 100644 --- a/proto/snmp/snmp_utils.c +++ b/proto/snmp/snmp_utils.c @@ -65,6 +65,7 @@ snmp_varbind_set_name_len(struct snmp_proto *p, struct agentx_varbind **vb, u8 l uint diff_size = (len - LOAD_U8(oid->n_subid)) * sizeof(u32); if (c->size < diff_size) { + snmp_log("varbind_set_name_len small buffer"); snmp_manage_tbuf(p, c); oid = &(*vb)->name; } @@ -81,7 +82,10 @@ snmp_varbind_duplicate_hdr(struct snmp_proto *p, struct agentx_varbind **vb, str ASSUME(vb != NULL && *vb != NULL); uint hdr_size = snmp_varbind_header_size(*vb); if (c->size < hdr_size) + { + snmp_log("varbind_duplicate small buffer"); snmp_manage_tbuf(p, c); + } ASSERT(c->size >= hdr_size); byte *buffer = c->buffer; @@ -290,7 +294,7 @@ snmp_set_varbind_type(struct agentx_varbind *vb, enum agentx_type t) return SNMP_SEARCH_OK; default: - die("invalid varbind type"); + die("invalid varbind type %d", (int) t); } } @@ -644,11 +648,12 @@ snmp_oid_ip4_index(struct oid *o, uint start, ip4_addr addr) } -/** snmp_oid_compare - find the lexicographical order relation between @left and @right - * both @left and @right has to be non-blank. +/** + * snmp_oid_compare - find the lexicographical order relation between @left and @right * @left: left object id relation operant * @right: right object id relation operant * + * both @left and @right has to be non-blank. * function returns 0 if left == right, * -1 if left < right, * and 1 otherwise @@ -748,7 +753,7 @@ snmp_registration_create(struct snmp_proto *p, u8 mib_class) r->transaction_id = p->transaction_id; // TODO where is incremented? is this valid? r->packet_id = p->packet_id + 1; - log(L_INFO "using registration packet_id %u", r->packet_id); + snmp_log("using registration packet_id %u", r->packet_id); r->mib_class = mib_class; @@ -760,7 +765,7 @@ snmp_registration_create(struct snmp_proto *p, u8 mib_class) int snmp_registration_match(struct snmp_registration *r, struct agentx_header *h, u8 class) { - log(L_INFO "snmp_reg_same() r->packet_id %u p->packet_id %u", r->packet_id, h->packet_id); + snmp_log("snmp_reg_same() r->packet_id %u p->packet_id %u", r->packet_id, h->packet_id); return (r->mib_class == class) && (r->session_id == h->session_id) && @@ -961,7 +966,7 @@ snmp_oid_log(const struct oid *oid) for (int id = 0; id < oid->n_subid; id++) pos += snprintf(pos, buf + 1024 - pos, ".%u", oid->ids[id]); - log(L_INFO "%s", buf); + snmp_log("%s", buf); } @@ -1064,3 +1069,120 @@ snmp_oid_common_ancestor(const struct oid *left, const struct oid *right, struct STORE_U8(out->n_subid, subids); } +/* + * SNMP MIB tree walking + */ +struct mib_leaf * +snmp_walk_init(struct mib_tree *tree, struct mib_walk_state *walk, const struct oid *oid, struct snmp_data *data) +{ + mib_tree_walk_init(walk, tree); + + snmp_vb_to_tx(data->p, oid, data->c); + + mib_node_u *node = mib_tree_find(tree, walk, &data->c->sr_vb_start->name); + + // TODO hide me in mib_tree code + /* mib_tree_find() returns NULL if the oid is longer than existing any path */ + if (node == NULL && walk->stack_pos > 0) + node = walk->stack[walk->stack_pos - 1]; + + return (!node || !mib_node_is_leaf(node)) ? NULL : &node->leaf; +} + +// TODO alter the varbind +struct mib_leaf * +snmp_walk_next(struct mib_tree *tree, struct mib_walk_state *walk, struct snmp_data *data) +{ + ASSUME(tree && walk); + + if (!walk->stack_pos) + return NULL; + + mib_node_u *node = walk->stack[walk->stack_pos - 1]; + + int found = 0; + struct mib_leaf *leaf = &node->leaf; + if (mib_node_is_leaf(node) && LOAD_U8(data->c->sr_vb_start->name.include)) + { + found = 1; + STORE_U8(data->c->sr_vb_start->name.include, 0); + } + + if (!found && mib_node_is_leaf(node) && leaf->call_next && !leaf->call_next(walk, data)) + found = 1; + + while (!found && (leaf = mib_tree_walk_next_leaf(tree, walk)) != NULL) + { + int old = snmp_oid_size(&data->c->sr_vb_start->name); + if (mib_tree_walk_to_oid(walk, &data->c->sr_vb_start->name, 20 * sizeof(u32))) + { + snmp_log("walk_next copy failed"); + return NULL; + } + + int new = snmp_oid_size(&data->c->sr_vb_start->name); + data->c->buffer += (new - old); + + if (leaf->call_next && !leaf->call_next(walk, data)) + found = 1; + else if (!leaf->call_next) + found = 1; + } + + if (!found) + return NULL; + + + return leaf; +} + +enum snmp_search_res +snmp_walk_fill(struct mib_leaf *leaf, struct mib_walk_state *walk, struct snmp_data *data) +{ + struct agentx_varbind *vb = data->c->sr_vb_start; + + if (!leaf) + //if (!leaf || mib_tree_walk_is_oid_descendant(walk, &vb->name) < 0) + return SNMP_SEARCH_NO_OBJECT; + + uint size = 0; + if (leaf->size >= 0) + { + if (leaf->type == AGENTX_OCTET_STRING || leaf->type == AGENTX_OPAQUE || + leaf->type == AGENTX_OBJECT_ID) + { + snmp_set_varbind_type(vb, leaf->type); + size = leaf->size; + } + else if (leaf->type != AGENTX_INVALID) + { + snmp_set_varbind_type(vb, leaf->type); + size = agentx_type_size(leaf->type); + } + else + size = leaf->size; + } + + snmp_log("walk_fill got size %u based on lt %u ls %u, calling filler()", size, leaf->type, leaf->size); + + if (size >= data->c->size) + { + snmp_log("walk_fill small buffer size %d to %d", size, data->c->size); + snmp_manage_tbuf(data->p, data->c); + } + + enum snmp_search_res res = leaf->filler(walk, data); + + vb = data->c->sr_vb_start; + + if (res != SNMP_SEARCH_OK) + snmp_set_varbind_type(vb, snmp_search_res_to_type(res)); + + u16 type = snmp_load_varbind_type(vb); + /* Test that hook() did not overwrite the VarBind type to non-matching type */ + ASSUME(type == leaf->type || type == AGENTX_END_OF_MIB_VIEW || type == AGENTX_NO_SUCH_OBJECT || + type == AGENTX_NO_SUCH_INSTANCE); + + return res; +} + diff --git a/proto/snmp/snmp_utils.h b/proto/snmp/snmp_utils.h index ede6a43e..43243c68 100644 --- a/proto/snmp/snmp_utils.h +++ b/proto/snmp/snmp_utils.h @@ -2,6 +2,7 @@ #define _BIRD_SNMP_UTILS_H_ #include "subagent.h" +#include "mib_tree.h" uint snmp_pkt_len(const byte *start, const byte *end); @@ -110,4 +111,11 @@ enum agentx_type snmp_search_res_to_type(enum snmp_search_res res); #define AGENTX_TYPE_IP4_SIZE ((uint) agentx_type_size(AGENTX_IP_ADDRESS)) #define AGENTX_TYPE_COUNTER32_SIZE ((uint) agentx_type_size(AGENTX_COUNTER_32)) +/* + * SNMP MIB tree walking + */ +struct mib_leaf *snmp_walk_init(struct mib_tree *tree, struct mib_walk_state *state, const struct oid *start_rx, struct snmp_data *data); +struct mib_leaf *snmp_walk_next(struct mib_tree *tree, struct mib_walk_state *state, struct snmp_data *data); +enum snmp_search_res snmp_walk_fill(struct mib_leaf *leaf, struct mib_walk_state *state, struct snmp_data *data); + #endif diff --git a/proto/snmp/subagent.c b/proto/snmp/subagent.c index 21712dc0..b46d1ea3 100644 --- a/proto/snmp/subagent.c +++ b/proto/snmp/subagent.c @@ -74,6 +74,7 @@ static uint update_packet_size(struct agentx_header *start, byte *end); static enum snmp_search_res search_mib(struct snmp_proto *p, struct agentx_varbind **vb_search, const struct oid *o_end, struct snmp_pdu *c); +/* standard SNMP internet prefix (1.3.6.1) */ const u32 snmp_internet[] = { SNMP_ISO, SNMP_ORG, SNMP_DOD, SNMP_INTERNET }; static inline int @@ -145,7 +146,6 @@ snmp_register_ack(struct snmp_proto *p, struct agentx_response *res, u8 class) } } - /* * snmp_error - handle a malformed packet * @p: SNMP protocol instance @@ -158,7 +158,6 @@ static inline void snmp_error(struct snmp_proto *p) { snmp_reset(p); - //snmp_set_state(p, SNMP_RESET); } /* @@ -206,7 +205,10 @@ open_pdu(struct snmp_proto *p, struct oid *oid) /* Make sure that we have enough space in TX-buffer */ if (c.size < AGENTX_HEADER_SIZE + TIMEOUT_SIZE + snmp_oid_size(oid) + + snmp_str_size(cf->description)) + { + snmp_log("agentx-Open-PDU small buffer"); snmp_manage_tbuf(p, &c); + } struct agentx_header *h = (void *) c.buffer; ADVANCE(c.buffer, c.size, AGENTX_HEADER_SIZE); @@ -254,9 +256,9 @@ snmp_notify_pdu(struct snmp_proto *p, struct oid *oid, void *data, uint size, in // TODO use more readable anonymous structure decl. #define UPTIME_SIZE \ - (6 * sizeof(u32)) /* sizeof( { u32 vb_type, u32 oid_hdr, u32 ids[4] } ) */ + sizeof( struct { u32 vb_type; u32 oid_hdr; u32 ids[4]; } ) #define TRAP0_HEADER_SIZE \ - (7 * sizeof(u32)) /* sizeof( { u32 vb_type, u32 oid_hdr, u32 ids[6] } ) */ + sizeof( struct { u32 vb_type; u32 oid_hdr; u32 ids[6]; } ) uint sz = AGENTX_HEADER_SIZE + TRAP0_HEADER_SIZE + snmp_oid_size(oid) \ + size; @@ -266,7 +268,10 @@ snmp_notify_pdu(struct snmp_proto *p, struct oid *oid, void *data, uint size, in /* Make sure that we have enough space in TX-buffer */ if (c.size < sz) + { + snmp_log("agentx-Notify-PDU small buffer"); snmp_manage_tbuf(p, &c); + } struct agentx_header *h = (void *) c.buffer; ADVANCE(c.buffer, c.size, AGENTX_HEADER_SIZE); @@ -361,7 +366,10 @@ un_register_pdu(struct snmp_proto *p, struct oid *oid, u32 bound, uint index, en ((bound > 1) ? BOUND_SIZE : 0); if (c.size < sz) + { + snmp_log("agentx-Register-PDU small buffer"); snmp_manage_tbuf(p, &c); + } struct agentx_header *h = (void *) c.buffer; ADVANCE(c.buffer, c.size, AGENTX_HEADER_SIZE); @@ -443,7 +451,10 @@ close_pdu(struct snmp_proto *p, enum agentx_close_reasons reason) #define REASON_SIZE sizeof(u32) if (c.size < AGENTX_HEADER_SIZE + REASON_SIZE) + { + snmp_log("agentx-Close-PDU small buffer"); snmp_manage_tbuf(p, &c); + } struct agentx_header *h = (void *) c.buffer; ADVANCE(c.buffer, c.size, AGENTX_HEADER_SIZE); @@ -574,7 +585,10 @@ parse_test_set_pdu(struct snmp_proto *p, byte * const pkt_start) snmp_pdu_context(&c, sk); if (c.size < AGENTX_HEADER_SIZE) + { + snmp_log("agentx-TestSet-PDU small buffer"); snmp_manage_tbuf(p, &c); + } res = prepare_response(p, &c); @@ -670,7 +684,10 @@ parse_sets_pdu(struct snmp_proto *p, byte * const pkt_start, enum agentx_respons struct snmp_pdu c; snmp_pdu_context(&c, p->sock); if (c.size < sizeof(struct agentx_response)) + { + snmp_log("parse_sets_pdu small buffer"); snmp_manage_tbuf(p, &c); + } struct agentx_response *r = prepare_response(p, &c); @@ -781,15 +798,17 @@ static uint parse_pkt(struct snmp_proto *p, byte *pkt, uint size) { /* TX-buffer free space */ - ASSERT(snmp_is_active(p)); - if (!space_for_response(p->sock)) - return 0; - - ASSERT(snmp_is_active(p)); if (size < AGENTX_HEADER_SIZE) return 0; struct agentx_header *h = (void *) pkt; + if (h->flags & AGENTX_NETWORK_BYTE_ORDER) + { + TRACE(D_PACKETS, "SNMP received PDU with unexpected byte order"); + snmp_reset(p); + return 0; + } + uint pkt_size = LOAD_U32(h->payload); /* RX side checks - too big packet */ @@ -797,7 +816,7 @@ parse_pkt(struct snmp_proto *p, byte *pkt, uint size) { snmp_simple_response(p, AGENTX_RES_GEN_ERROR, 0); snmp_reset(p); - return 0; // TODO return size?? + return 0; /* no bytes parsed */ } /* This guarantees that we have the full packet already received */ @@ -827,24 +846,20 @@ parse_pkt(struct snmp_proto *p, byte *pkt, uint size) p->session_id = copy.session_id; p->transaction_id = copy.transaction_id; p->packet_id = copy.packet_id; - log(L_INFO "restoring packet_id %u from temporal state", p->packet_id); + snmp_log("restoring packet_id %u from temporal state", p->packet_id); /* * After unexpected state, we simply reset the session * only sending the agentx-Response-PDU. */ snmp_reset(p); - return 0; // return size?? + return 0; } - ASSERT(snmp_is_active(p)); if (h->flags & AGENTX_NON_DEFAULT_CONTEXT) { - // TODO add non-default context support - TRACE(D_PACKETS, "SNMP received PDU with unexpected byte order"); + TRACE(D_PACKETS, "SNMP received PDU with non-default context"); snmp_simple_response(p, AGENTX_RES_UNSUPPORTED_CONTEXT, 0); - /* We always accept the packet length as correct, up to set limit */ - // TODO limit return pkt_size + AGENTX_HEADER_SIZE; } @@ -1071,7 +1086,10 @@ snmp_get_next2(struct snmp_proto *p, struct agentx_varbind **vb_search, struct o o_start = &(*vb_search)->name; if (c->size < snmp_varbind_hdr_size_from_oid(o_start)) + { + snmp_log("get_next2 small buffer"); snmp_manage_tbuf(p, c); + } snmp_set_varbind_type(*vb_search, AGENTX_END_OF_MIB_VIEW); return 0; @@ -1146,7 +1164,9 @@ snmp_get_next3(struct snmp_proto *p, struct oid *o_start, struct oid *o_end, } if (c->size < snmp_varbind_hdr_size_from_oid(o_start)) + { snmp_manage_tbuf(p, c); + } vb = snmp_create_varbind(c->buffer, o_start); vb->type = AGENTX_END_OF_MIB_VIEW; @@ -1328,7 +1348,9 @@ snmp_oid_prefixize(struct snmp_proto *p, const struct oid *oid, struct snmp_pdu uint oid_size = snmp_oid_size(oid); if (c->size < oid_size) + { snmp_manage_tbuf(p, c); + } // TODO check if the @oid is prefixable ASSERT(c->size >= oid_size); @@ -1345,15 +1367,17 @@ snmp_oid_prefixize(struct snmp_proto *p, const struct oid *oid, struct snmp_pdu * @c: PDU context * * Create NULL initialized VarBind inside TX buffer (from @c) whose vb->name is - * @oid. The @oid is not prefixed and is prefixable, the @oid is prefixed first. - * The protocol @p is used in cases of TX buffer space shortage. + * @oid. The @oid prefixed if possible. The result is stored in @c->sr_vb_start. */ -struct agentx_varbind * +void snmp_vb_to_tx(struct snmp_proto *p, const struct oid *oid, struct snmp_pdu *c) { uint vb_hdr_size = snmp_varbind_hdr_size_from_oid(oid); if (c->size < vb_hdr_size) + { + snmp_log("SNMP vb_to_tx small buffer"); snmp_manage_tbuf(p, c); + } ASSERT(c->size >= vb_hdr_size); struct agentx_varbind *vb = (void *) c->buffer; @@ -1366,37 +1390,15 @@ snmp_vb_to_tx(struct snmp_proto *p, const struct oid *oid, struct snmp_pdu *c) u8 subids = LOAD_U8(oid->n_subid) - 5; ADVANCE(c->buffer, c->size, snmp_oid_size_from_len(subids)); (void) snmp_oid_prefixize_unsafe(&vb->name, oid); - return vb; + + c->sr_vb_start = vb; + return; } ADVANCE(c->buffer, c->size, snmp_oid_size(oid)); snmp_oid_copy2(&vb->name, oid); - return vb; -} -/* - * snmp_oid_to_scratch - allocate temporal Object Identifier in prefixed form - * @oid: prefixed Object Identifier if possible - */ -static struct oid * -snmp_oid_to_scratch(const struct oid *oid) -{ - struct oid *dest; - if (snmp_oid_is_prefixable(oid) && !snmp_oid_is_prefixed(oid)) - { - u8 subids = LOAD_U8(oid->n_subid) - 5; - uint prefixed_size = sizeof(struct oid) + (subids * sizeof(u32)); - dest = tmp_alloc(prefixed_size); - snmp_oid_prefixize_unsafe(dest, oid); - - return dest; - } - - uint oid_size = snmp_oid_size(oid); - dest = tmp_alloc(oid_size); - snmp_oid_copy2(dest, oid); - - return dest; + c->sr_vb_start = vb; } /* @@ -1429,6 +1431,7 @@ response_err_ind(struct snmp_proto *p, struct agentx_response *res, enum agentx_ { STORE_U32(res->error, (u16) err); // TODO deal with auto-incrementing of snmp_pdu context c.ind + // FIXME for packets with errors reset reset payload size to null (by move c.buffer appropriately) if (err != AGENTX_RES_NO_ERROR && err != AGENTX_RES_GEN_ERROR) { TRACE(D_PACKETS, "Last PDU resulted in error %u", err); @@ -1439,7 +1442,7 @@ response_err_ind(struct snmp_proto *p, struct agentx_response *res, enum agentx_ } else if (err == AGENTX_RES_GEN_ERROR) { - TRACE(D_PACKETS, "Last PDU resulted in error %u", err); + TRACE(D_PACKETS, "Last PDU resulted in error %u genErr", err); STORE_U32(res->index, 0); TRACE(D_PACKETS, "Storing packet size %u (was %u)", sizeof(struct agentx_response) - AGENTX_HEADER_SIZE, LOAD_U32(res->h.payload)); STORE_U32(res->h.payload, @@ -1461,59 +1464,45 @@ parse_gets_error(struct snmp_proto *p, struct snmp_pdu *c, uint len) return len + AGENTX_HEADER_SIZE; } -static enum snmp_search_res -snmp_mib_fill2(struct snmp_proto *p, struct snmp_pdu *c, mib_node_u *mib_node) -{ - if (!mib_node || !mib_node_is_leaf(mib_node)) - { - snmp_set_varbind_type(c->sr_vb_start, AGENTX_NO_SUCH_OBJECT); - ADVANCE(c->buffer, c->size, snmp_varbind_header_size(c->sr_vb_start)); - return AGENTX_NO_SUCH_OBJECT; - } - - struct mib_leaf *leaf = &mib_node->leaf; - - return leaf->filler(p, c); -} - +/* + * AgentX GetPDU, GetNextPDU and GetBulkPDU + */ void -snmp_get_pdu(struct snmp_proto *p, struct snmp_pdu *c, struct mib_walk_state *walk) +snmp_get_pdu(struct snmp_proto *p, struct snmp_pdu *c, const struct oid *o_start, struct mib_walk_state *walk) { - mib_node_u *node; - node = mib_tree_find(p->mib_tree, walk, &c->sr_vb_start->name); + snmp_log("snmp_get_pdu()"); + struct snmp_data d = { + .p = p, + .c = c, + }; - (void) snmp_mib_fill2(p, c, node); + struct mib_leaf *leaf; + leaf = snmp_walk_init(p->mib_tree, walk, o_start, &d); + + snmp_log("found node %p", leaf); + + enum snmp_search_res res; + res = snmp_walk_fill(leaf, walk, &d); + + snmp_log("fill result %u", res); + + if (res != SNMP_SEARCH_OK) + snmp_set_varbind_type(c->sr_vb_start, snmp_search_res_to_type(res)); } int -snmp_get_next_pdu(struct snmp_proto *p, struct snmp_pdu *c, struct mib_walk_state *walk) +snmp_get_next_pdu(struct snmp_proto *p, struct snmp_pdu *c, const struct oid *o_start, struct mib_walk_state *walk) { - mib_node_u *node; - node = mib_tree_find(p->mib_tree, walk, &c->sr_vb_start->name); + struct snmp_data d = { + .p = p, + .c = c, + }; - int inclusive = c->sr_vb_start->name.include; - - int move_next; - if (!node && inclusive) - move_next = 1; - else if (!node && !inclusive) - move_next = 1; - else if (node && inclusive && mib_node_is_leaf(node)) - move_next = 0; - else if (node && inclusive) - move_next = 1; - else if (node && !inclusive) - move_next = 0; - - struct mib_leaf *leaf = &node->leaf; - if (move_next && node && mib_node_is_leaf(node)) - move_next = leaf->call_next(p, c, walk); - - if (move_next) - node = (mib_node_u *) mib_tree_walk_next_leaf(p->mib_tree, walk); + snmp_walk_init(p->mib_tree, walk, o_start, &d); + struct mib_leaf *leaf = snmp_walk_next(p->mib_tree, walk, &d); enum snmp_search_res res; - res = snmp_mib_fill2(p, c, node); + res = snmp_walk_fill(leaf, walk, &d); if (res != SNMP_SEARCH_OK) snmp_set_varbind_type(c->sr_vb_start, AGENTX_END_OF_MIB_VIEW); @@ -1522,14 +1511,64 @@ snmp_get_next_pdu(struct snmp_proto *p, struct snmp_pdu *c, struct mib_walk_stat } void -snmp_get_bulk_pdu(struct snmp_proto *p, struct snmp_pdu *c, struct mib_walk_state *walk, struct agentx_bulk_state *bulk) +snmp_get_bulk_pdu(struct snmp_proto *p, struct snmp_pdu *c, const struct oid *o_start, struct mib_walk_state *walk, struct agentx_bulk_state *bulk) { if (c->index >= bulk->getbulk.non_repeaters) bulk->repeaters++; // store the o_start and o_end - bulk->has_any |= snmp_get_next_pdu(p, c, walk); + bulk->has_any |= snmp_get_next_pdu(p, c, o_start, walk); +} + +static inline const struct oid * +snmp_load_oids(byte **pkt_ptr, uint *pkt_sz, struct snmp_pdu *c) +{ + byte *pkt = *pkt_ptr; + uint pkt_size = *pkt_sz; + + uint sz; + const struct oid *start = (const struct oid *) pkt; + + if ((sz = snmp_oid_size(start)) > pkt_size) + { + snmp_log("load_oids start %u / %u", sz, pkt_size); + c->error = AGENTX_RES_PARSE_ERROR; + *pkt_ptr = pkt; + *pkt_sz = pkt_size; + return NULL; + } + + ADVANCE(pkt, pkt_size, sz); + + const struct oid *end = (const struct oid *) pkt; + if ((sz = snmp_oid_size(end)) > pkt_size) + { + snmp_log("load_oids end %u / %u", sz, pkt_size); + c->error = AGENTX_RES_PARSE_ERROR; + *pkt_ptr = pkt; + *pkt_sz = pkt_size; + return NULL; + } + + ADVANCE(pkt, pkt_size, sz); + + if (!snmp_is_oid_empty(end) && + snmp_oid_compare(start, end) > 0) + { + c->error = AGENTX_RES_GEN_ERROR; + *pkt_ptr = pkt; + *pkt_sz = pkt_size; + return NULL; + } + + ASSERT(start != NULL); + ASSERT(end != NULL); + + c->sr_o_end = end; + *pkt_ptr = pkt; + *pkt_sz = pkt_size; + return start; } /* @@ -1544,6 +1583,7 @@ snmp_get_bulk_pdu(struct snmp_proto *p, struct snmp_pdu *c, struct mib_walk_stat static uint parse_gets_pdu(struct snmp_proto *p, byte * const pkt_start) { + snmp_log("parse_gets_pdu msg"); // TODO checks for c.size underflow struct mib_walk_state walk; byte *pkt = pkt_start; @@ -1560,11 +1600,12 @@ parse_gets_pdu(struct snmp_proto *p, byte * const pkt_start) * Get-Bulk processing stops if all the varbind have type END_OF_MIB_VIEW * has_any is true if some varbind has type other than END_OF_MIB_VIEW */ - struct agentx_bulk_state bulk_state = { }; + struct agentx_bulk_state bulk_state = { 0 }; if (h->type == AGENTX_GET_BULK_PDU) { if (pkt_size < sizeof(struct agentx_getbulk)) { + snmp_log("parse_gets GetBulkPDU prepare"); c.error = AGENTX_RES_PARSE_ERROR; c.index = 0; return parse_gets_error(p, &c, pkt_size); @@ -1594,62 +1635,27 @@ parse_gets_pdu(struct snmp_proto *p, byte * const pkt_start) { lp_restore(tmp_linpool, &tmps); - /* We load search range start OID */ - const struct oid *o_start_rx = (void *) pkt; - uint sz; - if ((sz = snmp_oid_size(o_start_rx)) > pkt_size) + const struct oid *start_rx; + if (!(start_rx = snmp_load_oids(&pkt, &pkt_size, &c))) { - c.error = AGENTX_RES_PARSE_ERROR; - return parse_gets_error(p, &c, pkt_size); - } - - /* Update buffer pointer and remaining size counters. */ - ADVANCE(pkt, pkt_size, sz); - - /* - * We load search range end OID - * The exactly same process of sanity checking is preformed while loading - * the SearchRange's end OID - */ - const struct oid *o_end_rx = (void *) pkt; - if ((sz = snmp_oid_size(o_end_rx)) > pkt_size) - { - c.error = AGENTX_RES_PARSE_ERROR; - return parse_gets_error(p, &c, pkt_size); - } - - ADVANCE(pkt, pkt_size, sz); - - /* We don't too to check for oversided OID because the PDU has 8k size limit */ - - /* We create copy of OIDs outside of rx-buffer and also prefixize them */ - c.sr_vb_start = snmp_vb_to_tx(p, o_start_rx, &c); - c.sr_o_end = snmp_oid_to_scratch(o_end_rx); - - ASSERT(c.sr_vb_start); // TODO implement failed parsing logic - ASSERT(c.sr_o_end); - - if (!snmp_is_oid_empty(c.sr_o_end) && - snmp_oid_compare(&c.sr_vb_start->name, c.sr_o_end) > 0) - { - c.error = AGENTX_RES_GEN_ERROR; + snmp_log("snmp_load_oid ends with an error"); return parse_gets_error(p, &c, pkt_size); } switch (h->type) { case AGENTX_GET_PDU: - snmp_get_pdu(p, &c, &walk); + snmp_get_pdu(p, &c, start_rx, &walk); //snmp_mib_fill(p, &vb_start, &c); break; case AGENTX_GET_NEXT_PDU: - snmp_get_next_pdu(p, &c, &walk); + snmp_get_next_pdu(p, &c, start_rx, &walk); //snmp_get_next2(p, &vb_start, o_end, &c); break; case AGENTX_GET_BULK_PDU: - snmp_get_bulk_pdu(p, &c, &walk, &bulk_state); + snmp_get_bulk_pdu(p, &c, start_rx, &walk, &bulk_state); #if 0 if (c.index >= bulk_state.getbulk.non_repeaters) bulk_state.repeaters++; @@ -1663,7 +1669,7 @@ parse_gets_pdu(struct snmp_proto *p, byte * const pkt_start) break; default: - die("incorrect usage"); + die("implementation failure"); } c.sr_vb_start = NULL; @@ -1744,7 +1750,7 @@ snmp_stop_subagent(struct snmp_proto *p) int snmp_rx(sock *sk, uint size) { - log(L_INFO "snmp_rx with size %u", size); + snmp_log("snmp_rx with size %u", size); struct snmp_proto *p = sk->data; byte *pkt_start = sk->rbuf; byte *end = pkt_start + size; @@ -1780,7 +1786,7 @@ snmp_rx(sock *sk, uint size) void snmp_tx(sock *sk) { - log(L_INFO "snmp_tx()"); + snmp_log("snmp_tx()"); /* We still not have enough space */ if (!space_for_response(sk)) return; @@ -1819,7 +1825,7 @@ snmp_ping(struct snmp_proto *p) ADVANCE(c.buffer, c.size, AGENTX_HEADER_SIZE); snmp_blank_header(h, AGENTX_PING_PDU); p->packet_id++; - log(L_INFO "incrementing packet_id to %u (ping)", p->packet_id); + snmp_log("incrementing packet_id to %u (ping)", p->packet_id); snmp_session(p, h); /* sending only header */ @@ -2099,7 +2105,7 @@ snmp_manage_tbuf(struct snmp_proto *p, struct snmp_pdu *c) if (c->sr_vb_start != NULL) diff = (void *) c->sr_vb_start - (void *) sk->tbuf; - log(L_INFO "snmp_manage_tbuf2()"); + snmp_log("snmp_manage_tbuf2()"); sk_set_tbsize(sk, sk->tbsize + 2048); c->size += 2048; @@ -2123,7 +2129,7 @@ snmp_manage_tbuf2(struct snmp_proto *p, void **ptr, struct snmp_pdu *c) if (ptr) diff = *ptr - (void *) sk->tbuf; - log(L_INFO "snmp_manage_tbuf()"); + snmp_log("snmp_manage_tbuf()"); sk_set_tbsize(sk, sk->tbsize + 2048); c->size += 2048; diff --git a/proto/snmp/subagent.h b/proto/snmp/subagent.h index 93db65f2..138c43f7 100644 --- a/proto/snmp/subagent.h +++ b/proto/snmp/subagent.h @@ -1,3 +1,4 @@ + #ifndef _BIRD_SNMP_SUBAGENT_H_ #define _BIRD_SNMP_SUBAGENT_H_ @@ -51,7 +52,7 @@ enum agentx_type { AGENTX_NO_SUCH_INSTANCE = 129, AGENTX_END_OF_MIB_VIEW = 130, - AGENTX_INVALID = -1, + AGENTX_INVALID = 0, } PACKED; enum snmp_search_res { @@ -169,6 +170,15 @@ struct oid { u32 ids[]; }; +#define STATIC_OID(sbids) \ + struct { \ + u8 n_subid; \ + u8 prefix; \ + u8 include; \ + u8 reserved; \ + u32 ids[sbids]; \ + } + /* enforced by MIB tree, see mib_tree.h for more info */ #define OID_MAX_LEN 32 @@ -301,21 +311,22 @@ struct snmp_pdu { /* Search Range */ struct agentx_varbind *sr_vb_start; /* search range starting OID inside TX buffer (final storage) */ - struct oid *sr_o_end; /* search range ending OID */ + const struct oid *sr_o_end; /* search range ending OID */ /* Control */ enum agentx_response_errs error; /* storage for result of current action */ u32 index; /* index on which the error was found */ - union snmp_mibs_data *mibs_data; /* data passed from MIB search phase to MIB fill phase */ }; -#include "bgp4_mib.h" - -union snmp_mibs_data { - enum snmp_tags empty; - - struct bgp4_mib bgp4; +/* + * snmp_data - Comprehensive hadle for Agentx PDU state + * @p: SNMP protocol instance + * @c: contextual data for currrently constructed AgentX PDU + */ +struct snmp_data { + struct snmp_proto *p; + struct snmp_pdu *c; }; struct snmp_packet_info { @@ -346,16 +357,17 @@ void snmp_notify_pdu(struct snmp_proto *p, struct oid *oid, void *data, uint siz void snmp_manage_tbuf(struct snmp_proto *p, struct snmp_pdu *c); -struct agentx_varbind *snmp_vb_to_tx(struct snmp_proto *p, const struct oid *oid, struct snmp_pdu *c); +void snmp_vb_to_tx(struct snmp_proto *p, const struct oid *oid, struct snmp_pdu *c); u8 snmp_get_mib_class(const struct oid *oid); void snmp_register_mibs(struct snmp_proto *p); -void snmp_bgp_start(struct snmp_proto *p); + +/* MIB modules */ +void snmp_bgp4_start(struct snmp_proto *p); -// debug wrapper -#if 0 -#define snmp_log(...) log(L_INFO "snmp " __VA_ARGS__) +#if 1 +#define snmp_log(...) log(L_INFO "SNMP " __VA_ARGS__) #else #define snmp_log(...) do { } while(0) #endif