From 9f8950d4d7a61dd09dcb5003a0e143e4279fe01b Mon Sep 17 00:00:00 2001 From: Vojtech Vilimek Date: Wed, 26 Jul 2023 14:02:23 +0200 Subject: [PATCH] changes in bgp_mib.c API (mainly) --- proto/snmp/bgp_mib.c | 617 ++++++++++++++++++++++++++++------------ proto/snmp/bgp_mib.h | 10 +- proto/snmp/snmp_utils.c | 251 +++++++++++++--- proto/snmp/snmp_utils.h | 36 ++- proto/snmp/subagent.h | 85 ++++-- 5 files changed, 759 insertions(+), 240 deletions(-) diff --git a/proto/snmp/bgp_mib.c b/proto/snmp/bgp_mib.c index 12fe859ed..b46772d02 100644 --- a/proto/snmp/bgp_mib.c +++ b/proto/snmp/bgp_mib.c @@ -194,17 +194,23 @@ bgp_get_candidate(u32 field) /* first value is in secord cell of array translation_table (as the * SNMP_BPG_IDENTIFIER == 1 */ - if (field > 0 && field < sizeof(translation_table) / sizeof(translation_table[0])) + if (field > 0 && field <= sizeof(translation_table) / sizeof(translation_table[0])) return translation_table[field]; + if (field == 0) + return BGP_INTERNAL_INVALID; else - return BGP_INTERNAL_NO_VALUE; + return BGP_INTERNAL_END; } static inline struct ip4_addr ip4_from_oid(const struct oid *o) { - return (o->n_subid == 9) ? ip4_build(o->ids[5], o->ids[6], o->ids[7], -o->ids[8]) : ip4_from_u32(0xFFFFFFFF); + return ip4_build( + o->n_subid > 5 ? (o->ids[5] & 0xff) : 0, + o->n_subid > 6 ? (o->ids[6] & 0xff) : 0, + o->n_subid > 7 ? (o->ids[7] & 0xff) : 0, + o->n_subid > 8 ? (o->ids[8] & 0xff) : 0 + ); } static void @@ -283,6 +289,9 @@ snmp_bgp_state(struct oid *oid) * -> BGP4-MIB::bgp (root) */ + if (snmp_is_oid_empty(oid)) + return BGP_INTERNAL_END; + u8 state = BGP_INTERNAL_NO_VALUE; u8 candidate; @@ -374,15 +383,10 @@ snmp_bgp_has_value(u8 state) if (state <= BGP_INTERNAL_BGP || state == BGP_INTERNAL_PEER_TABLE || state == BGP_INTERNAL_PEER_ENTRY || - - /* unsupported fields */ - state == BGP_INTERNAL_FSM_ESTABLISHED_TIME || - state == BGP_INTERNAL_ORIGINATION_INTERVAL || - state == BGP_INTERNAL_MIN_ROUTE_ADVERTISEMENT || - state == BGP_INTERNAL_IN_UPDATE_ELAPSED_TIME) - return 0; /* hasn't value */ + state >= BGP_INTERNAL_END) + return 0; else - return 1; /* has value */ + return 1; } /** @@ -423,15 +427,8 @@ snmp_bgp_next_state(u8 state) case BGP_INTERNAL_PEER_ENTRY: return BGP_INTERNAL_IDENTIFIER; - case BGP_INTERNAL_FSM_TRANSITIONS: - case BGP_INTERNAL_FSM_ESTABLISHED_TIME: - return BGP_INTERNAL_RETRY_INTERVAL; - - case BGP_INTERNAL_IN_UPDATE_ELAPSED_TIME: - case BGP_INTERNAL_END: - return BGP_INTERNAL_END; default: @@ -445,20 +442,19 @@ snmp_bgp_is_supported(struct oid *o) /* most likely not functioning */ if (o->prefix == 2 && o->n_subid > 0 && o->ids[0] == 1) { - if (o->n_subid == 2 && o->ids[1] == BGP4_MIB_VERSION || - o->ids[1] == BGP4_MIB_LOCAL_AS) + if (o->n_subid == 2 && (o->ids[1] == BGP4_MIB_VERSION || + o->ids[1] == BGP4_MIB_LOCAL_AS)) return 1; else if (o->n_subid > 2 && o->ids[1] == BGP4_PEER_TABLE && o->ids[2] == BGP4_PEER_ENTRY) { if (o->n_subid == 3) return 1; - if (o->n_subid == 8 && - o->ids[3] > 0 && + if (o->n_subid == 8 && o->ids[3] > 0) /* do not include bgpPeerInUpdatesElapsedTime and bgpPeerFsmEstablishedTime */ - o->ids[3] < SNMP_BGP_IN_UPDATE_ELAPSED_TIME && - o->ids[3] != SNMP_BGP_FSM_ESTABLISHED_TIME) + //&& o->ids[3] < SNMP_BGP_IN_UPDATE_ELAPSED_TIME + //&& o->ids[3] != SNMP_BGP_FSM_ESTABLISHED_TIME) return 1; } else @@ -468,50 +464,127 @@ snmp_bgp_is_supported(struct oid *o) return 0; } +static int +oid_state_compare(const struct oid *oid, u8 state) +{ + ASSUME(oid != NULL); + if (state >= BGP_INTERNAL_IDENTIFIER && + state <= BGP_INTERNAL_IN_UPDATE_ELAPSED_TIME) + return (oid->n_subid > 9) - (oid->n_subid < 9); + if (state >= BGP_INTERNAL_VERSION && state <= BGP_INTERNAL_PEER_TABLE) + return (oid->n_subid > 3) - (oid->n_subid < 3); + if (state == BGP_INTERNAL_PEER_ENTRY) + return (oid->n_subid > 4) - (oid->n_subid < 4); + if (state == BGP_INTERNAL_BGP) + return (oid->n_subid > 2) - (oid->n_subid < 2); + + return -1; +} + static struct oid * update_bgp_oid(struct oid *oid, u8 state) { - ASSERT (state != BGP_INTERNAL_INVALID); - ASSERT (state != BGP_INTERNAL_NO_VALUE); - ASSERT (state != BGP_INTERNAL_END); + snmp_log("update_bgp_oid()"); + if (state == BGP_INTERNAL_END || state == BGP_INTERNAL_INVALID || + state == BGP_INTERNAL_NO_VALUE) + return oid; /* if same state, no need to realloc anything */ if (snmp_bgp_state(oid) == state) - return oid; + { + if (state >= BGP_INTERNAL_IDENTIFIER && + state <= BGP_INTERNAL_IN_UPDATE_ELAPSED_TIME && + oid->n_subid == 9) + return oid; + if (state >= BGP_INTERNAL_VERSION && + state <= BGP_INTERNAL_PEER_TABLE && oid->n_subid == 3) + return oid; + if (state == BGP_INTERNAL_PEER_ENTRY && oid->n_subid == 4) + return oid; + if (state == BGP_INTERNAL_BGP && oid->n_subid == 2) + return oid; + } + snmp_log("update work"); switch (state) { case BGP_INTERNAL_BGP: /* could destroy same old data */ - oid = mb_realloc(oid, snmp_oid_sizeof(2)); + if (oid->n_subid != 2) + { + snmp_log("realloc"); + oid = mb_realloc(oid, snmp_oid_sizeof(2)); + snmp_log("/realloc"); + } + oid->n_subid = 2; oid->ids[0] = 1; oid->ids[1] = SNMP_BGP4_MIB; break; case BGP_INTERNAL_VERSION: - oid = mb_realloc(oid, snmp_oid_sizeof(3)); + if (oid->n_subid != 3) + { snmp_log("realloc"); + oid = mb_realloc(oid, snmp_oid_sizeof(3)); + snmp_log("/realloc"); } + oid->n_subid = 3; oid->ids[2] = SNMP_BGP_VERSION; break; case BGP_INTERNAL_LOCAL_AS: + if (oid->n_subid != 3) + { snmp_log("realloc"); + oid = mb_realloc(oid, snmp_oid_sizeof(3)); + snmp_log("/realloc"); } + + oid->n_subid = 3; oid->ids[2] = 2; break; case BGP_INTERNAL_IDENTIFIER: - oid = mb_realloc(oid, snmp_oid_sizeof(9)); - oid->n_subid = 9; + if (oid->n_subid != 9) + { + snmp_log("realloc"); + oid = mb_realloc(oid, snmp_oid_sizeof(9)); + snmp_log("/realloc"); + + if (oid->n_subid < 6) + oid->ids[5] = 0; + if (oid->n_subid < 7) + oid->ids[6] = 0; + if (oid->n_subid < 8) + oid->ids[7] = 0; + if (oid->n_subid < 9) + oid->ids[8] = 0; + } + oid->ids[2] = SNMP_BGP_PEER_TABLE; oid->ids[3] = SNMP_BGP_PEER_ENTRY; + oid->ids[4] = SNMP_BGP_IDENTIFIER; - /* zero the ip */ - oid->ids[5] = oid->ids[6] = oid->ids[7] = oid->ids[8] = 0; + oid->n_subid = 9; break; -#define SNMP_UPDATE_CASE(num, update) \ - case num: \ - oid->ids[4] = update; \ +#define SNMP_UPDATE_CASE(num, update) \ + case num: \ + if (oid->n_subid != 9) \ + { \ + snmp_log("realloc"); \ + oid = mb_realloc(oid, snmp_oid_sizeof(9)); \ + snmp_log("/realloc"); \ + \ + if (oid->n_subid < 6) \ + oid->ids[5] = 0; \ + if (oid->n_subid < 7) \ + oid->ids[6] = 0; \ + if (oid->n_subid < 8) \ + oid->ids[7] = 0; \ + if (oid->n_subid < 9) \ + oid->ids[8] = 0; \ + } \ + oid->n_subid = 9; \ + oid->ids[4] = update; \ break; SNMP_UPDATE_CASE(BGP_INTERNAL_STATE, SNMP_BGP_STATE) @@ -559,6 +632,9 @@ update_bgp_oid(struct oid *oid, u8 state) SNMP_UPDATE_CASE(BGP_INTERNAL_MIN_ROUTE_ADVERTISEMENT, SNMP_BGP_MIN_ROUTE_ADVERTISEMENT) SNMP_UPDATE_CASE(BGP_INTERNAL_IN_UPDATE_ELAPSED_TIME, SNMP_BGP_IN_UPDATE_ELAPSED_TIME) + + default: + die("update unavailable"); } return oid; @@ -567,37 +643,50 @@ update_bgp_oid(struct oid *oid, u8 state) // TODO test bgp_find_dynamic_oid static struct oid * -bgp_find_dynamic_oid(struct snmp_proto *p, struct oid *o_start, struct oid *o_end, u8 state UNUSED) +bgp_find_dynamic_oid(struct snmp_proto *p, struct oid *o_start, const struct oid *o_end, u8 start_state) { + ASSUME(o_start != NULL); + ASSUME(o_end != NULL); + snmp_log("bgp_find_dynamic_oid()"); ip4_addr ip4 = ip4_from_oid(o_start); - /* dest is 255.255.255.255 if o_end is empty */ - ip4_addr dest = ip4_from_oid(o_end); + ip4_addr dest; + + if (o_start->n_subid < 9) + o_start->include = 1; + + int check_dest = snmp_is_oid_empty(o_end); + if (check_dest) + { + u8 end_state = snmp_bgp_state(o_end); + dest = (start_state == end_state && o_end->n_subid > 5) ? + ip4_from_oid(o_end) : + ip4_from_u32(0xFFFFFFFF); + } snmp_log("ip addresses build (ip4) %I (dest) %I", ipa_from_ip4(ip4), ipa_from_ip4(dest)); - // why am I allocated dynamically ?! - net_addr *net = mb_allocz(p->p.pool, sizeof(struct net_addr)); - net_fill_ip4(net, ip4, IP4_MAX_PREFIX_LENGTH); + net_addr net; + net_fill_ip4(&net, ip4, IP4_MAX_PREFIX_LENGTH); snmp_log("dynamic part of BGP mib"); - // why am I allocated dynamically ?! - struct f_trie_walk_state *ws = mb_allocz(p->p.pool, - sizeof(struct f_trie_walk_state)); + struct f_trie_walk_state ws; - trie_walk_init(ws, p->bgp_trie, NULL); + // TODO move to newer API + trie_walk_init(&ws, p->bgp_trie, NULL, 0); snmp_log("walk init"); - if (trie_walk_next(ws, net)) // && ip4_less(net4_prefix(net), dest)) + if (trie_walk_next(&ws, &net)) // && ip4_less(net4_prefix(net), dest)) { snmp_log("trie_walk_next() returned true"); /* * if the o_end is empty then there are no conditions on the ip4 addr */ - int cmp = ip4_compare(net4_prefix(net), dest); + //int cmp = (check_dest) ? ip4_compare(net4_prefix(&net), dest) : ; + int cmp = ip4_compare(net4_prefix(&net), dest); if (cmp < 0 || (cmp == 0 && snmp_is_oid_empty(o_end))) { snmp_log("ip4_less() returned true"); @@ -605,10 +694,7 @@ bgp_find_dynamic_oid(struct snmp_proto *p, struct oid *o_start, struct oid *o_en o->n_subid = 9; memcpy(o, o_start, snmp_oid_size(o_start)); - snmp_oid_ip4_index(o, 5, net4_prefix(net)); - - mb_free(net); - mb_free(ws); + snmp_oid_ip4_index(o, 5, net4_prefix(&net)); return o; } @@ -616,9 +702,7 @@ bgp_find_dynamic_oid(struct snmp_proto *p, struct oid *o_start, struct oid *o_en // delete me else { - snmp_log("ip4_less() returned false for %I >= %I", net4_prefix(net), dest); - mb_free(net); - mb_free(ws); + snmp_log("ip4_less() returned false for %I >= %I", net4_prefix(&net), dest); } // delete me end } @@ -626,8 +710,6 @@ bgp_find_dynamic_oid(struct snmp_proto *p, struct oid *o_start, struct oid *o_en else { snmp_log("trie_walk_next() returned false, cleaning"); - mb_free(net); - mb_free(ws); } return NULL; @@ -652,6 +734,9 @@ UNUSED, u8 current_state) snmp_oid_dump(o_start); next_state = snmp_bgp_next_state(next_state); + /* search in next state is done from beginning */ + o_start->ids[5] = o_start->ids[6] = o_start->ids[7] = o_start->ids[8] = 0; + o_start->include = 1; snmp_log("looping"); } while (o_start == NULL && next_state < BGP_INTERNAL_END); @@ -659,9 +744,208 @@ UNUSED, u8 current_state) return o_start; } +/** + * snmp_bgp_find_next_oid - walk bgp peer addresses and update @o_start oid + * + * @p: + * @oid: + * @contid: + */ +static int +snmp_bgp_find_next_oid(struct snmp_proto *p, struct oid *oid, uint UNUSED contid) +{ + // TODO add o_end paramenter for faster searches + ip4_addr ip4 = ip4_from_oid(oid); + //ip_add4 dest = ip4_from_u32(0xFFFFFFFF); + + net_addr net; + net_fill_ip4(&net, ip4, IP4_MAX_PREFIX_LENGTH); + struct f_trie_walk_state ws; + + int match = trie_walk_init(&ws, p->bgp_trie, &net, 1); + + snmp_log("match %d include %u", match, oid->include); + if (match && oid->include) + { + oid->include = 0; + return 1; + } + + /* We skip the first match as we should not include ip address in oid */ + if (match) + { + snmp_log("continue"); + trie_walk_next(&ws, &net); + } + + if (trie_walk_next(&ws, &net)) + { + snmp_oid_dump(oid); + snmp_log("setting up"); + u32 res = ipa_to_u32(net_prefix(&net)); + + ASSUME(oid->n_subid == 9); + oid->ids[5] = (res & 0xFF000000) >> 24; + oid->ids[6] = (res & 0x00FF0000) >> 16; + oid->ids[7] = (res & 0x0000FF00) >> 8; + oid->ids[8] = (res & 0x000000FF) >> 0; + return 1; + } + + snmp_log("bad"); + return 0; +} + +static enum snmp_search_res +snmp_bgp_search_dynamic(struct snmp_proto *p, struct oid **searched, const struct oid *o_end, uint UNUSED contid, u8 next_state) +{ + struct oid *oid = *searched; + snmp_log(" **searched = 0x%p *oid = 0x%p", searched, oid); + snmp_oid_dump(*searched); + snmp_oid_dump(oid); + u8 end_state = snmp_bgp_state(o_end); + + snmp_log("before assumption %s [%u] < %u INTERNAL_END", debug_bgp_states[end_state], end_state, BGP_INTERNAL_END); + // failed + ASSUME(end_state <= BGP_INTERNAL_END); + snmp_log("before assupmtion oid 0x%p != NULL (0x0)", oid); + ASSUME(oid != NULL); + + oid = update_bgp_oid(oid, next_state); + + snmp_log("update bgp oid to state %s [%d]", debug_bgp_states[next_state], next_state); + snmp_oid_dump(*searched); + snmp_oid_dump(oid); + + int found; + while (!(found = snmp_bgp_find_next_oid(p, oid, contid)) && next_state <= end_state) + { + snmp_log("loop"); + + next_state = snmp_bgp_next_state(next_state); + if (next_state == BGP_INTERNAL_END) + break; + oid = update_bgp_oid(oid, next_state); + /* in search for next bgp state, we want to start from beginning */ + oid->ids[5] = oid->ids[6] = oid->ids[7] = oid->ids[8] = 0; + } + + if (next_state <= end_state) + { + *searched = oid; + return SNMP_SEARCH_OK; + } + + // free in the caller ?! + mb_free(oid); + *searched = NULL; + return SNMP_SEARCH_END_OF_VIEW; +} + +enum snmp_search_res +snmp_bgp_search2(struct snmp_proto *p, struct oid **searched, const struct oid *o_end, uint contid) +{ + u8 bgp_state = snmp_bgp_state(*searched); + struct oid *oid = *searched; + snmp_log("snmp_bgp_search2() with state %s [%d]", debug_bgp_states[bgp_state], bgp_state); + + /* TODO remove todo below, then remove this code */ + if (is_dynamic(bgp_state)) + { + snmp_log("returning oid with dynamic state"); + return snmp_bgp_search_dynamic(p, searched, o_end, contid, bgp_state); + //return snmp_bgp_search_dynamic(p, searched, contid, result, bgp_state); + } + + /* TODO snmp_bgp_has_value is false only for state which are not dynamic */ + if (!snmp_bgp_has_value(bgp_state) || !oid->include) + { + bgp_state = snmp_bgp_next_state(bgp_state); + snmp_log("altering searched oid with next state %s [%d]", debug_bgp_states[bgp_state], bgp_state); + snmp_oid_dump(*searched); + snmp_log("after oid update:"); + snmp_oid_dump(*searched); + + /* zero the ip address section for previously non-dynamic oid (search all peers) */ + for (int i = 5; i < MIN(9, oid->n_subid); i++) + oid->ids[i] = 0; + } + + if (is_dynamic(bgp_state)) + { + snmp_log("returning oid with dynamic state 2"); + return snmp_bgp_search_dynamic(p, searched, o_end, contid, bgp_state); + //return snmp_bgp_search_dynamic(p, o_start, contid, result, bgp_state); + } + + oid = *searched = update_bgp_oid(*searched, bgp_state); + if (oid->n_subid == 3 && oid->ids[2] >= SNMP_BGP_VERSION && + oid->ids[2] <= SNMP_BGP_LOCAL_AS) + { + snmp_log("oid matches static state"); + oid->include = 0; + return SNMP_SEARCH_OK; + } + + snmp_log("reached unguarded code, returning END_OF_VIEW"); + /* TODO */ + //if ( + + + return SNMP_SEARCH_END_OF_VIEW; + // return SNMP_NO_SUCH_OBJECT; + +#if 0 + if (is_dynamic(bgp_state)) + return snmp_bgp_search_dynamic(p, o_start, contid, res); + + if (!snmp_bgp_has_value(bgp_state)) + { + bgp_state = x; + } + print_bgp_record_all(p); + + if (o_start->include) + return snmp_bgp_search_included(p, o_start, contid, result, bgp_state); + + u8 next_state = snmp_bgp_next_state(bgp_state); + if (!is_dynamic(next_state)) + { + o_start = update_bgp_oid(o_start, next_state); + snmp_log("next state is also not dynamic"); + *res = o_start; + return SNMP_SEARCH_OK; + } + + return search_bgp_dynamic(p, o_start, o_end, contid, bgp_state); + + + if (o_start->include && snmp_bgp_has_value(bgp_state)) + && !is_dynamic(bgp_state)) + { + if (o_start->n_subid == 3) + { + o_start->include = 0; + *result = o_start; + return SNMP_SEARCH_OK; + } + else if (o_start->n_subid > 3) + return SNMP_SEARCH_NO_INSTANCE; + else + return SNMP_SEARCH_NO_OBJECT; + } + else if (o_start->include && snmp_bgp_has_value(bgp_state)) + && is_dynamic(bgp_state)) + return search_bgp_dynamic1(p, o_start, contid, result); + else if (o_start + + / * o_start is not inclusive * / +#endif +} + /* o_start could be o_curr, but has basically same meaning for searching */ struct oid * -search_bgp_mib(struct snmp_proto *p, struct oid *o_start, struct oid *o_end, uint contid UNUSED) +snmp_bgp_search(struct snmp_proto *p, struct oid *o_start, struct oid *o_end, uint contid UNUSED) { u8 start_state = snmp_bgp_state(o_start); //u8 state_curr = snmp_bgp_state(o_start); @@ -674,14 +958,14 @@ search_bgp_mib(struct snmp_proto *p, struct oid *o_start, struct oid *o_end, uin if (o_start->include && snmp_bgp_has_value(start_state) && !is_dynamic(start_state) && o_start->n_subid == 3) { - snmp_log("search_bgp_mib() first search element (due to include field) returned"); + snmp_log("snmp_bgp_search() first search element (due to include field) returned"); o_start->include = 0; /* disable including for next time */ return o_start; } else if (o_start->include && snmp_bgp_has_value(start_state) && - is_dynamic(start_state)) + is_dynamic(start_state)) { - snmp_log("search_bgp_mib() first search element matched dynamic entry!"); + snmp_log("snmp_bgp_search() first search element matched dynamic entry!"); return search_bgp_dynamic(p, o_start, o_end, contid, start_state); } @@ -728,19 +1012,21 @@ search_bgp_mib(struct snmp_proto *p, struct oid *o_start, struct oid *o_end, uin } static byte * -bgp_fill_dynamic(struct snmp_proto *p, struct agentx_varbind *vb, byte *pkt, uint size -UNUSED, uint contid UNUSED, int byte_ord UNUSED, u8 state) +bgp_fill_dynamic(struct snmp_proto UNUSED *p, struct agentx_varbind *vb, + struct snmp_pdu_context *c, u8 state) { - //snmp_log("bgp_fill_dynamic() valid ip %s", snmp_bgp_valid_ip4(oid) ? "true" : "false"); - struct oid *oid = &vb->name; + uint size = c->size - snmp_varbind_header_size(vb); + uint UNUSED contid = c->context; + byte *pkt; ip_addr addr; - if (snmp_bgp_valid_ip4(oid)) + if (oid_state_compare(oid, state) == 0 && snmp_bgp_valid_ip4(oid)) addr = ipa_from_ip4(ip4_from_oid(oid)); else { - vb->type = AGENTX_NO_SUCH_OBJECT; + vb->type = AGENTX_NO_SUCH_INSTANCE; + pkt = ((byte *) vb) + snmp_varbind_header_size(vb); return pkt; } @@ -754,25 +1040,23 @@ UNUSED, uint contid UNUSED, int byte_ord UNUSED, u8 state) { proto = ((struct proto_config *) pe->config)->proto; if (proto->proto == &proto_bgp && - ipa_equal(addr, ((struct bgp_proto *) proto)->remote_ip)) + ipa_equal(addr, ((struct bgp_proto *) proto)->remote_ip)) { bgp_proto = (struct bgp_proto *) proto; snmp_log("bgp_dynamic_fill() using bgp_proto %p", bgp_proto); } - /* binded bgp protocol not found */ else { die("Binded bgp protocol not found!"); - vb->type = AGENTX_NO_SUCH_OBJECT; - return pkt; + vb->type = AGENTX_NO_SUCH_INSTANCE; + return ((byte *) vb) + snmp_varbind_header_size(vb); } } - else { - vb->type = AGENTX_NO_SUCH_OBJECT; - return pkt; + vb->type = AGENTX_NO_SUCH_INSTANCE; + return ((byte *) vb) + snmp_varbind_header_size(vb); } struct bgp_conn *bgp_conn = bgp_proto->conn; @@ -794,164 +1078,129 @@ UNUSED, uint contid UNUSED, int byte_ord UNUSED, u8 state) else bgp_state = MAX(bgp_in->state, bgp_out->state); - btime now; + char last_error[2] = { bgp_proto->last_error_code & 0x00FF0000 >> 16, + bgp_proto->last_error_code & 0x000000FF }; switch (state) { - case BGP_INTERNAL_IDENTIFIER: if (bgp_state == BS_OPENCONFIRM || bgp_state == BS_ESTABLISHED) - { - snmp_put_ip4(pkt, bgp_proto->remote_ip); - pkt += 4; - /* the inserted ip has size 8 bytes, the BGP_DATA will increment by 4B */ - BGP_DATA(vb, AGENTX_IP_ADDRESS, pkt); - } + pkt = snmp_varbind_ip4(vb, size, ipa_to_ip4(bgp_proto->remote_ip)); else - { - snmp_put_blank(pkt); /* stores 4B of zeroes */ - BGP_DATA(vb, AGENTX_IP_ADDRESS, pkt); - } + pkt = snmp_varbind_ip4(vb, size, IP4_NONE); break; case BGP_INTERNAL_STATE: - STORE_PTR(pkt, bgp_state); - BGP_DATA(vb, AGENTX_INTEGER, pkt); + pkt = snmp_varbind_int(vb, size, bgp_state); break; case BGP_INTERNAL_ADMIN_STATUS: /* struct proto ~ (struct proto *) bgp_proto */ if (proto->disabled) - STORE_PTR(pkt, AGENTX_ADMIN_STOP); + pkt = snmp_varbind_int(vb, size, AGENTX_ADMIN_STOP); else - STORE_PTR(pkt, AGENTX_ADMIN_START); + pkt = snmp_varbind_int(vb, size, AGENTX_ADMIN_START); - BGP_DATA(vb, AGENTX_INTEGER, pkt); break; case BGP_INTERNAL_NEGOTIATED_VERSION: if (bgp_state == BS_OPENCONFIRM || bgp_state == BS_ESTABLISHED) - STORE_PTR(pkt, 4); // TODO replace with MACRO + pkt = snmp_varbind_int(vb, size, SNMP_BGP_NEGOTIATED_VER_VALUE); else - STORE_PTR(pkt, 0); /* zero dictated by rfc */ + pkt = snmp_varbind_int(vb, size, SNMP_BGP_NEGOTIATED_VER_NO_VALUE); - BGP_DATA(vb, AGENTX_INTEGER, pkt); break; case BGP_INTERNAL_LOCAL_ADDR: // TODO XXX bgp_proto->link_addr & zero local_ip - snmp_put_ip4(pkt, bgp_proto->local_ip); - pkt += 4; - /* the inserted ip has size 8 bytes, the BGP_DATA will increment by 4B */ - BGP_DATA(vb, AGENTX_IP_ADDRESS, pkt); + pkt = snmp_varbind_ip4(vb, size, ipa_to_ip4(bgp_proto->local_ip)); break; case BGP_INTERNAL_LOCAL_PORT: - STORE_PTR(pkt, bgp_conf->local_port); - BGP_DATA(vb, AGENTX_INTEGER, pkt); + pkt = snmp_varbind_int(vb, size, bgp_conf->local_port); break; case BGP_INTERNAL_REMOTE_ADDR: - snmp_put_ip4(pkt, bgp_proto->remote_ip); - pkt += 4; - /* the inserted ip has size 8 bytes, the BGP_DATA will increment by 4B */ - BGP_DATA(vb, AGENTX_IP_ADDRESS, pkt); + pkt = snmp_varbind_ip4(vb, size, ipa_to_ip4(bgp_proto->remote_ip)); break; case BGP_INTERNAL_REMOTE_PORT: - STORE_PTR(pkt, bgp_conf->remote_port); - BGP_DATA(vb, AGENTX_INTEGER, pkt); + pkt = snmp_varbind_int(vb, size, bgp_conf->remote_port); break; case BGP_INTERNAL_REMOTE_AS: - STORE_PTR(pkt, bgp_proto->remote_as); - BGP_DATA(vb, AGENTX_INTEGER, pkt); + pkt = snmp_varbind_int(vb, size, bgp_proto->remote_as); break; /* IN UPDATES */ case BGP_INTERNAL_RX_UPDATES: - STORE_PTR(pkt, bgp_stats->rx_updates); - BGP_DATA(vb, AGENTX_COUNTER_32, pkt); + pkt = snmp_varbind_counter32(vb, size, bgp_stats->rx_updates); break; /* OUT UPDATES */ case BGP_INTERNAL_TX_UPDATES: - STORE_PTR(pkt, bgp_stats->tx_updates); - BGP_DATA(vb, AGENTX_COUNTER_32, pkt); + pkt = snmp_varbind_counter32(vb, size, bgp_stats->tx_updates); break; /* IN MESSAGES */ case BGP_INTERNAL_RX_MESSAGES: - STORE_PTR(pkt, bgp_stats->rx_messages); - BGP_DATA(vb, AGENTX_COUNTER_32, pkt); + pkt = snmp_varbind_counter32(vb, size, bgp_stats->rx_messages); break; /* OUT MESSAGES */ case BGP_INTERNAL_TX_MESSAGES: - STORE_PTR(pkt, bgp_stats->tx_messages); - BGP_DATA(vb, AGENTX_COUNTER_32, pkt); + pkt = snmp_varbind_counter32(vb, size, bgp_stats->tx_messages); break; case BGP_INTERNAL_LAST_ERROR: - STORE_PTR(pkt, 2); - pkt += 4; - - if (bgp_proto->last_error_code) - { - /* force network order */ - put_u32(pkt, bgp_proto->last_error_code & 0x00FF0000 << 8 | - bgp_proto->last_error_code & 0x000000FF << 24); - } - else - snmp_put_blank(pkt); - - BGP_DATA(vb, AGENTX_OCTET_STRING, pkt); + pkt = snmp_varbind_nstr(vb, size, last_error, 2); break; // TODO finish me here case BGP_INTERNAL_FSM_TRANSITIONS: - STORE_PTR(pkt, bgp_stats->fsm_established_transitions); - BGP_DATA(vb, AGENTX_COUNTER_32, pkt); + pkt = snmp_varbind_counter32(vb, size, + bgp_stats->fsm_established_transitions); break; case BGP_INTERNAL_FSM_ESTABLISHED_TIME: + pkt = snmp_varbind_gauge32(vb, size, + (current_time() - bgp_proto->last_established) TO_S); break; case BGP_INTERNAL_RETRY_INTERVAL: // retry interval != 0 - STORE_PTR(pkt, bgp_conf->connect_retry_time); - BGP_DATA(vb, AGENTX_INTEGER, pkt); + pkt = snmp_varbind_int(vb, size, bgp_conf->connect_retry_time); break; case BGP_INTERNAL_HOLD_TIME: // (0, 3..65535) - STORE_PTR(pkt, bgp_conn->hold_time); - BGP_DATA(vb, AGENTX_INTEGER, pkt); + pkt = snmp_varbind_int(vb, size, bgp_conn->hold_time); break; case BGP_INTERNAL_KEEPALIVE: - STORE_PTR(pkt, bgp_conn->keepalive_time); - BGP_DATA(vb, AGENTX_INTEGER, pkt); + pkt = snmp_varbind_int(vb, size, bgp_conn->keepalive_time); break; case BGP_INTERNAL_HOLD_TIME_CONFIGURED: - STORE_PTR(pkt, bgp_conf->hold_time); - BGP_DATA(vb, AGENTX_INTEGER, pkt); + pkt = snmp_varbind_int(vb, size, bgp_conf->hold_time); break; + case BGP_INTERNAL_KEEPALIVE_CONFIGURED: - STORE_PTR(pkt, bgp_conf->keepalive_time); - BGP_DATA(vb, AGENTX_INTEGER, pkt); + pkt = snmp_varbind_int(vb, size, bgp_conf->keepalive_time); break; - // finish me here case BGP_INTERNAL_ORIGINATION_INTERVAL: + // (1..65535) but is not supported + pkt = snmp_varbind_int(vb, size, 0); break; + case BGP_INTERNAL_MIN_ROUTE_ADVERTISEMENT: + // (1..65535) but is not supported + pkt = snmp_varbind_int(vb, size, 0); break; case BGP_INTERNAL_IN_UPDATE_ELAPSED_TIME: - now = current_time(); - STORE_PTR(pkt, (now - bgp_proto->last_rx_update) TO_S ); - BGP_DATA(vb, AGENTX_GAUGE_32, pkt); + pkt = snmp_varbind_gauge32(vb, size, (current_time() + - bgp_proto->last_rx_update) TO_S); break; case BGP_INTERNAL_END: @@ -970,78 +1219,96 @@ UNUSED, uint contid UNUSED, int byte_ord UNUSED, u8 state) break; } + if (!pkt) + { + vb->type = AGENTX_NO_SUCH_INSTANCE; + return ((byte *) vb) + snmp_varbind_header_size(vb); + } + return pkt; } + static byte * bgp_fill_static(struct snmp_proto *p, struct agentx_varbind *vb, byte *pkt, uint size UNUSED, uint contid UNUSED, int byte_ord UNUSED, u8 state) { snmp_log("snmp bgp_fill_static ()\n"); byte *temp = pkt; + snmp_log("bgp_fill_static: vb->type %u, ptk %02x", vb->type, *((u32 *) pkt)); struct oid *oid = &vb->name; + snmp_oid_dump(oid); + snmp_log("bgp_fill_static"); /* snmp_bgp_state() check only prefix. To be sure on oid equivalence we need to * compare the oid->n_subid length. All BGP static fields have same n_subid. */ - if (oid->n_subid != 3) + if (oid_state_compare(oid, state) < 0 || state == BGP_INTERNAL_END) { vb->type = AGENTX_NO_SUCH_OBJECT; - return pkt; + return ((byte *) vb) + snmp_varbind_header_size(vb); + } + else if (oid_state_compare(oid, state) > 0) + { + vb->type = AGENTX_NO_SUCH_INSTANCE; + return ((byte *) vb) + snmp_varbind_header_size(vb); } switch (state) { case BGP_INTERNAL_VERSION: - STORE_PTR(pkt, 1); /* store string len */ - pkt += 4; - STORE_PTR(pkt, BGP4_VERSIONS); - - /* real size is 8 but we already shifted the pkt by 4 */ - BGP_DATA(vb, AGENTX_OCTET_STRING, pkt); + pkt = snmp_varbind_nstr(vb, size, BGP4_VERSIONS, 1); break; case BGP_INTERNAL_LOCAL_AS: - // XXX local as to use - - STORE_PTR(pkt, p->local_as); - BGP_DATA(vb, AGENTX_INTEGER, pkt); + pkt = snmp_varbind_int(vb, size, p->local_as); break; case BGP_INTERNAL_BGP: + default: vb->type = AGENTX_NO_SUCH_OBJECT; + pkt = ((byte *) vb) + snmp_varbind_header_size(vb); + break; } + snmp_log("bgp_fill_static: type %u packet %p", vb->type, pkt); + snmp_oid_dump(oid); + snmp_log("snmp ended with non empty pkt %u starting from %p to %p\n", pkt - temp, temp, pkt); + snmp_dump_packet(temp, pkt - temp); return pkt; } -byte * -snmp_bgp_fill(struct snmp_proto *p, struct agentx_varbind *vb, byte *buf UNUSED, -uint size UNUSED, uint contid UNUSED, int byte_ord UNUSED) +void +snmp_bgp_fill(struct snmp_proto *p, struct agentx_varbind *vb, + struct snmp_pdu_context *c) { u8 state = snmp_bgp_state(&vb->name); - //snmp_log("snmp_bgp_fill() state %u is dynamic %s has value %s", state, is_dynamic(state) ? "true" : "false", snmp_bgp_has_value(state) ? "true" : "false"); + //byte *tmp; + byte *pkt; if (!is_dynamic(state)) - return bgp_fill_static(p, vb, buf, size, contid, byte_ord, state); + { + //return bgp_fill_static(p, vb, c->buffer, c->size, c->context, c->byte_ord, state); + pkt = bgp_fill_static(p, vb, c->buffer, c->size, c->context, c->byte_ord, state); + ADVANCE(c->buffer, c->size, pkt - c->buffer); + return; + } if (is_dynamic(state) && snmp_bgp_has_value(state)) - return bgp_fill_dynamic(p, vb, buf, size, contid, byte_ord, state); - - else { - return buf; + pkt = bgp_fill_dynamic(p, vb, c, state); + ADVANCE(c->buffer, c->size, pkt - c->buffer); + return; } - /* + else { - snmp_log("has no value"); - struct agentx_varbind *vb = snmp_create_varbind(buf, oid); - buf += snmp_varbind_size(vb); - vb->type = AGENTX_NO_SUCH_OBJECT; - return buf; + die("snmp_bgp_fill unreachable"); + // AGENTX_NO_SUCH_OBJECT + ((void) c->buffer); + return; } - */ } + diff --git a/proto/snmp/bgp_mib.h b/proto/snmp/bgp_mib.h index d2db74673..e27f1d511 100644 --- a/proto/snmp/bgp_mib.h +++ b/proto/snmp/bgp_mib.h @@ -32,6 +32,10 @@ enum BGP4_MIB { SNMP_BGP_IN_UPDATE_ELAPSED_TIME = 24, /* UNSUPPORTED */ } PACKED; +/* version of BGP, here BGP-4 */ +#define SNMP_BGP_NEGOTIATED_VER_VALUE 4 +#define SNMP_BGP_NEGOTIATED_VER_NO_VALUE 0 + //void snmp_init_bgp_table(void); //void snmp_del_bgp_table(void); @@ -45,8 +49,10 @@ u8 snmp_bgp_state(struct oid *o); u8 snmp_bgp_get_valid(u8 state); u8 snmp_bgp_getnext_valid(u8 state); -struct oid *search_bgp_mib(struct snmp_proto *p , struct oid *o_start, struct oid *o_end, uint contid); -byte * snmp_bgp_fill(struct snmp_proto *p, struct agentx_varbind *vb, byte *buf, uint size, uint contid UNUSED, int byte_ord); +struct oid *snmp_bgp_search(struct snmp_proto *p, struct oid *o_start, struct oid *o_end, uint contid); +enum snmp_search_res snmp_bgp_search2(struct snmp_proto *p, struct oid **searched, const struct oid *o_end, uint contid); +//byte * snmp_bgp_fill(struct snmp_proto *p, struct agentx_varbind *vb, byte *buf, uint size, uint contid UNUSED, int byte_ord); +void snmp_bgp_fill(struct snmp_proto *p, struct agentx_varbind *vb, struct snmp_pdu_context *c); #define BGP4_MIB_VERSION 1 #define BGP4_MIB_LOCAL_AS 2 diff --git a/proto/snmp/snmp_utils.c b/proto/snmp/snmp_utils.c index 3a06b0c9e..853697d5b 100644 --- a/proto/snmp/snmp_utils.c +++ b/proto/snmp/snmp_utils.c @@ -10,6 +10,8 @@ #include "snmp_utils.h" +int agentx_type_size(enum agentx_type t); + /** * snmp_is_oid_empty - check if oid is null-valued * @oid: object identifier to check @@ -17,7 +19,7 @@ * Test if the oid header is full of zeroes. For @oid NULL returns 0. */ int -snmp_is_oid_empty(struct oid *oid) +snmp_is_oid_empty(const struct oid *oid) { if (oid != NULL) return oid->n_subid == 0 && oid->prefix == 0 && oid->include == 0; @@ -30,10 +32,38 @@ snmp_is_oid_empty(struct oid *oid) * @buf: packet first byte * @pkt: first byte past packet end */ -size_t -snmp_pkt_len(byte *buf, byte *pkt) +uint +snmp_pkt_len(byte *start, byte *end) +{ + snmp_log("snmp_pkt_len start 0x%p end 0x%p res %u", start, end, (end - start) +- AGENTX_HEADER_SIZE); + return (end - start) - AGENTX_HEADER_SIZE; +} + +/** + * + * used for copying oid to in buffer oid @dest + */ +void snmp_oid_copy(struct oid *dest, const struct oid *src) +{ + STORE_U8(dest->n_subid, src->n_subid); + STORE_U8(dest->prefix, src->prefix); + STORE_U8(dest->include, src->include); + STORE_U8(dest->pad, 0); + + for (int i = 0; i < src->n_subid; i++) + STORE_U32(dest->ids[i], src->ids[i]); +} + +/** + * + */ +struct oid * +snmp_oid_duplicate(pool *pool, const struct oid *oid) { - return (pkt - buf) - AGENTX_HEADER_SIZE; + struct oid *res = mb_alloc(pool, snmp_oid_size(oid)); + memcpy(res, oid, snmp_oid_size(oid)); + return res; } /** @@ -46,6 +76,12 @@ snmp_oid_blank(struct snmp_proto *p) return mb_allocz(p->p.pool, sizeof(struct oid)); } +size_t +snmp_str_size_from_len(uint len) +{ + return 4 + BIRD_ALIGN(len, 4); +} + /** * snmp_str_size - return in packet size of supplied string * @str: measured string @@ -53,10 +89,10 @@ snmp_oid_blank(struct snmp_proto *p) * Returned value is string length aligned to 4 byte with 32bit length * annotation included. */ -size_t +inline size_t snmp_str_size(const char *str) { - return 4 + BIRD_ALIGN(strlen(str), 4); + return snmp_str_size_from_len(strlen(str)); } /** @@ -64,7 +100,7 @@ snmp_str_size(const char *str) * @o: object identifier to use */ uint -snmp_oid_size(struct oid *o) +snmp_oid_size(const struct oid *o) { return 4 + (o->n_subid * 4); } @@ -79,28 +115,62 @@ snmp_oid_sizeof(uint n_subid) return sizeof(struct oid) + n_subid * sizeof(u32); } +uint snmp_varbind_hdr_size_from_oid(struct oid *oid) +{ + return snmp_oid_size(oid) + 4; +} + /** * snmp_vb_size - measure size of varbind in bytes * @vb: variable binding to use */ uint -snmp_varbind_size(struct agentx_varbind *vb) +snmp_varbind_header_size(struct agentx_varbind *vb) { - return snmp_oid_size(&vb->name) + 4; + return snmp_varbind_hdr_size_from_oid(&vb->name); +} + +uint +snmp_varbind_size(struct agentx_varbind *vb, int byte_ord) +{ + uint hdr_size = snmp_varbind_header_size(vb); + int s = agentx_type_size(vb->type); + + if (s >= 0) + return hdr_size + (uint) s; + + void *data = ((void *) vb) + hdr_size; + + if (vb->type == AGENTX_OBJECT_ID) + return hdr_size + snmp_oid_size((struct oid *) data); + + /* + * Load length of octet string + * (AGENTX_OCTET_STRING, AGENTX_IP_ADDRESS, AGENTX_OPAQUE) + */ + return hdr_size + snmp_str_size_from_len(LOAD_PTR(data, byte_ord)); +} + +inline uint +snmp_context_size(struct agentx_context *c) +{ + return (c && c->length) ? snmp_str_size_from_len(c->length) : 0; } struct agentx_varbind * snmp_create_varbind(byte *buf, struct oid *oid) { struct agentx_varbind *vb = (void*) buf; + vb->pad = 0; memcpy(&vb->name, oid, snmp_oid_size(oid)); return vb; } -byte *snmp_fix_varbind(struct agentx_varbind *vb, struct oid *new) +byte * +snmp_fix_varbind(struct agentx_varbind *vb, struct oid *new) { memcpy(&vb->name, new, snmp_oid_size(new)); - return (void *) vb + snmp_varbind_size(vb); + return (void *) vb + snmp_varbind_header_size(vb); } /** @@ -109,7 +179,7 @@ byte *snmp_fix_varbind(struct agentx_varbind *vb, struct oid *new) * @start: index of first address id */ int -snmp_valid_ip4_index(struct oid *o, uint start) +snmp_valid_ip4_index(const struct oid *o, uint start) { if (start + 3 < o->n_subid) return snmp_valid_ip4_index_unsafe(o, start); @@ -126,7 +196,7 @@ snmp_valid_ip4_index(struct oid *o, uint start) * length sufficiency is done. */ int -snmp_valid_ip4_index_unsafe(struct oid *o, uint start) +snmp_valid_ip4_index_unsafe(const struct oid *o, uint start) { for (int i = 0; i < 4; i++) if (o->ids[start + i] >= 256) @@ -135,6 +205,23 @@ snmp_valid_ip4_index_unsafe(struct oid *o, uint start) return 1; // true } +byte * +snmp_put_nstr(byte *buf, const char *str, uint len) +{ + uint alen = BIRD_ALIGN(len, 4); + + // TODO check for '\0' in the str bytes? + STORE_PTR(buf, len); + buf += 4; + memcpy(buf, str, len); + + /* Insert zero padding in the gap at the end */ + for (uint i = 0; i < alen - len; i++) + buf[len + i] = 0x00; + + return buf + alen; +} + /** * snmp_put_str - put string into SNMP PDU transcieve buffer * @buf: pointer to first unoccupied buffer byte @@ -148,28 +235,16 @@ byte * snmp_put_str(byte *buf, const char *str) { uint len = strlen(str); - uint slen = BIRD_ALIGN(len, 4); - - if (len > MAX_STR) - return NULL; - - STORE_PTR(buf, len); - - memcpy(buf + 4, str, len); - - for (uint i = 0; i < slen - len; i++) - buf[len + i] = 0x00; // PADDING - - return buf + snmp_str_size(str); + return snmp_put_nstr(buf, str, len); } byte * -snmp_put_ip4(byte *buf, ip_addr addr) +snmp_put_ip4(byte *buf, ip4_addr addr) { /* octet string has size 4 bytes */ STORE_PTR(buf, 4); - put_u32(buf+4, ipa_to_u32(addr)); + put_u32(buf+4, ip4_to_u32(addr)); return buf + 8; } @@ -231,10 +306,10 @@ void snmp_oid_ip4_index(struct oid *o, uint start, ip4_addr addr) { u32 temp = ip4_to_u32(addr); - STORE(o->ids[start], temp >> 24); - STORE(o->ids[start + 1], (temp >> 16) & 0xFF); - STORE(o->ids[start + 2], (temp >> 8) & 0xFF); - STORE(o->ids[start + 3], temp & 0xFF); + STORE_U32(o->ids[start], temp >> 24); + STORE_U32(o->ids[start + 1], (temp >> 16) & 0xFF); + STORE_U32(o->ids[start + 2], (temp >> 8) & 0xFF); + STORE_U32(o->ids[start + 3], temp & 0xFF); } void snmp_oid_dump(struct oid *oid) @@ -278,7 +353,7 @@ void snmp_oid_dump(struct oid *oid) * and 1 otherwise */ int -snmp_oid_compare(struct oid *left, struct oid *right) +snmp_oid_compare(const struct oid *left, const struct oid *right) { const u32 INTERNET_PREFIX[] = {1, 3, 6, 1}; @@ -404,3 +479,113 @@ snmp_dump_packet(byte *pkt, uint size) snmp_log("pkt [%d] 0x%02x%02x%02x%02x", i, pkt[i],pkt[i+1],pkt[i+2],pkt[i+3]); snmp_log("end dump"); } + +/* + * Returns length of agentx_type @type in bytes. + * Variable length types result in -1. + */ +int +agentx_type_size(enum agentx_type type) +{ + /* + * AGENTX_NULL, AGENTX_NO_SUCH_OBJECT, AGENTX_NO_SUCH_INSTANCE, + * AGENTX_END_OF_MIB_VIEW + */ + if (type >= AGENTX_NO_SUCH_OBJECT || type == AGENTX_NULL) + return 0; + + /* AGENTX_INTEGER, AGENTX_COUNTER_32, AGENTX_GAUGE_32, AGENTX_TIME_TICKS */ + if (type >= AGENTX_COUNTER_32 && type <= AGENTX_TIME_TICKS || + type == AGENTX_INTEGER) + return 4; + + /* AGENTX_COUNTER_64 */ + if (type == AGENTX_COUNTER_64) + return 8; + + /* AGENTX_OBJECT_ID, AGENTX_OCTET_STRING, AGENTX_IP_ADDRESS, AGENTX_OPAQUE */ + else + return -1; +} + +static inline byte * +snmp_varbind_type32(struct agentx_varbind *vb, uint size, enum agentx_type type, u32 val) +{ + ASSUME(agentx_type_size(type) == 4); /* type has 4B representation */ + + if (size < (uint) agentx_type_size(type)) + { + snmp_log("varbind type32 returned NULL"); + return NULL; + } + + vb->type = type; + u32 *data = SNMP_VB_DATA(vb); + snmp_log("varbind type32 vb data 0x%p (from vb 0x%p)", data, (void *) vb); + *data = val; + return (byte *)(data + 1); +} + +inline byte * +snmp_varbind_int(struct agentx_varbind *vb, uint size, u32 val) +{ + return snmp_varbind_type32(vb, size, AGENTX_INTEGER, val); +} + + +inline byte * +snmp_varbind_counter32(struct agentx_varbind *vb, uint size, u32 val) +{ + return snmp_varbind_type32(vb, size, AGENTX_COUNTER_32, val); +} + +inline byte * +snmp_varbind_gauge32(struct agentx_varbind *vb, uint size, s64 val) +{ + return snmp_varbind_type32(vb, size, AGENTX_GAUGE_32, + MAX(0, MIN(val, UINT32_MAX))); +} + +inline byte * +snmp_varbind_ip4(struct agentx_varbind *vb, uint size, ip4_addr addr) +{ + if (size < snmp_str_size_from_len(4)) + { + snmp_log("varbind ip4 NULL"); + return NULL; + } + + vb->type = AGENTX_IP_ADDRESS; + snmp_log("snmp_varbind_ip4 vb data 0x%p (from vb 0x%p)", SNMP_VB_DATA(vb), (void +*) vb); + return snmp_put_ip4(SNMP_VB_DATA(vb), addr); +} + +inline byte * +snmp_varbind_nstr(struct agentx_varbind *vb, uint size, const char *str, uint len) +{ + if (size < snmp_str_size_from_len(len)) + { + snmp_log("varbind nstr NULL"); + return NULL; + } + + vb->type = AGENTX_OCTET_STRING; + //die("snmp_varbind_nstr() %p.data = %p", vb, SNMP_VB_DATA(vb)); + snmp_log("snmp_varbind_nstr vb data 0x%p (from vb 0x%p)", SNMP_VB_DATA(vb), (void *) vb); + //snmp_log("snmp_varbind_nstr() %p.data = %p", vb, SNMP_VB_DATA(vb)); + return snmp_put_nstr(SNMP_VB_DATA(vb), str, len); +} + +inline enum agentx_type +snmp_search_res_to_type(enum snmp_search_res r) +{ + ASSUME(r != SNMP_SEARCH_OK); + static enum agentx_type type_arr[] = { + [SNMP_SEARCH_NO_OBJECT] = AGENTX_NO_SUCH_OBJECT, + [SNMP_SEARCH_NO_INSTANCE] = AGENTX_NO_SUCH_INSTANCE, + [SNMP_SEARCH_END_OF_VIEW] = AGENTX_END_OF_MIB_VIEW, + }; + + return type_arr[r]; +} diff --git a/proto/snmp/snmp_utils.h b/proto/snmp/snmp_utils.h index 913cf7174..d32477d0d 100644 --- a/proto/snmp/snmp_utils.h +++ b/proto/snmp/snmp_utils.h @@ -3,30 +3,38 @@ #include "subagent.h" -size_t snmp_pkt_len(byte *buf, byte *pkt); +uint snmp_pkt_len(byte *start, byte *end); +size_t snmp_str_size_from_len(uint len); size_t snmp_str_size(const char *str); -int snmp_is_oid_empty(struct oid *oid); -int snmp_valid_ip4_index(struct oid *o, uint start); -int snmp_valid_ip4_index_unsafe(struct oid *o, uint start); -uint snmp_oid_size(struct oid *o); +int snmp_is_oid_empty(const struct oid *oid); +int snmp_valid_ip4_index(const struct oid *o, uint start); +int snmp_valid_ip4_index_unsafe(const struct oid *o, uint start); +uint snmp_oid_size(const struct oid *o); size_t snmp_oid_sizeof(uint n_subid); -uint snmp_varbind_size(struct agentx_varbind *vb); +uint snmp_varbind_hdr_size_from_oid(struct oid *oid); +uint snmp_varbind_header_size(struct agentx_varbind *vb); +uint snmp_varbind_size(struct agentx_varbind *vb, int byte_ord); +uint snmp_context_size(struct agentx_context *c); +void snmp_oid_copy(struct oid *dest, const struct oid *src); + +struct oid *snmp_oid_duplicate(pool *pool, const struct oid *oid); struct oid *snmp_oid_blank(struct snmp_proto *p); struct agentx_varbind *snmp_create_varbind(byte* buf, struct oid *oid); byte *snmp_fix_varbind(struct agentx_varbind *vb, struct oid *new); -int snmp_oid_compare(struct oid *first, struct oid *second); +int snmp_oid_compare(const struct oid *first, const struct oid *second); byte *snmp_no_such_object(byte *buf, struct agentx_varbind *vb, struct oid *oid); byte *snmp_no_such_instance(byte *buf, struct agentx_varbind *vb, struct oid *oid); byte *snmp_put_str(byte *buf, const char *str); +byte *snmp_put_nstr(byte *buf, const char *str, uint len); byte *snmp_put_blank(byte *buf); byte *snmp_put_oid(byte *buf, struct oid *oid); -byte *snmp_put_ip4(byte *buf, ip_addr ip4); +byte *snmp_put_ip4(byte *buf, ip4_addr ip4); byte *snmp_put_fbyte(byte *buf, u8 data); @@ -34,13 +42,19 @@ void snmp_oid_ip4_index(struct oid *o, uint start, ip4_addr addr); void snmp_oid_dump(struct oid *oid); -int snmp_oid_compare(struct oid *left, struct oid *right); - -struct oid *snmp_prefixize(struct snmp_proto *p, struct oid *o, int byte_ord); +//struct oid *snmp_prefixize(struct snmp_proto *p, struct oid *o, int byte_ord); struct snmp_register *snmp_register_create(struct snmp_proto *p, u8 mib_class); void snmp_register_ack(struct snmp_proto *p, struct agentx_header *h); +byte *snmp_varbind_int(struct agentx_varbind *vb, uint size, u32 val); +byte *snmp_varbind_counter32(struct agentx_varbind *vb, uint size, u32 val); +byte *snmp_varbind_gauge32(struct agentx_varbind *vb, uint size, s64 val); +byte *snmp_varbind_ip4(struct agentx_varbind *vb, uint size, ip4_addr addr); +byte *snmp_varbind_nstr(struct agentx_varbind *vb, uint size, const char *str, uint len); + void snmp_dump_packet(byte *pkt, uint size); + +enum agentx_type snmp_search_res_to_type(enum snmp_search_res res); #endif diff --git a/proto/snmp/subagent.h b/proto/snmp/subagent.h index a0313fc13..6478f5990 100644 --- a/proto/snmp/subagent.h +++ b/proto/snmp/subagent.h @@ -26,7 +26,7 @@ enum SNMP_CLASSES { SNMP_CLASS_END, }; -#define BGP4_VERSIONS 0x10 +#define BGP4_VERSIONS ((char[]) { 0x10 }) enum agentx_type { AGENTX_INTEGER = 2, @@ -44,6 +44,13 @@ enum agentx_type { AGENTX_END_OF_MIB_VIEW = 130, } PACKED; +enum snmp_search_res { + SNMP_SEARCH_OK = 0, + SNMP_SEARCH_NO_OBJECT = 1, + SNMP_SEARCH_NO_INSTANCE = 2, + SNMP_SEARCH_END_OF_VIEW = 3, +}; + #define AGENTX_ADMIN_STOP 1 #define AGENTX_ADMIN_START 2 @@ -53,18 +60,15 @@ enum agentx_type { #define SNMP_NATIVE #ifdef SNMP_NATIVE -#define STORE(v,c) (v) = (u32) (c) -#define STORE_16(v,c) (v) = (u16) (c) -#define STORE_PTR(v,c) *((u32 *) (v)) = (u32) (c) -#define SNMP_UPDATE(h,l) \ - STORE((h)->payload, l) - +#define STORE_U32(dest, val) ((dest) = (u32) (val)) +#define STORE_U16(dest, val) ((dest) = (u16) (val)) +#define STORE_U8(dest, val) ((dest) = (u8) (val)) +#define STORE_PTR(ptr, val) (*((u32 *) (ptr)) = (u32) (val)) #else -#define STORE(v, c) put_u32(&v, c) -#define STORE_16(v,c) put_u32(&v, c) -#define STORE_PTR(v,c) put_u32(v, c) -#define SNMP_UPDATE(h,l) \ - STORE(h->payload, l) +#define STORE_U32(dest, val) put_u32(&(dest), (val)) +#define STORE_U16(dest, val) put_u16(&(dest), (val)) +#define STORE_U8(dest, val) put_u8(&(dest), (val)) +#define STORE_PTR(ptr, val) put_u32(ptr, val) #endif /* storing byte (u8) is always the same */ @@ -82,18 +86,18 @@ enum agentx_type { #endif #define SNMP_B_HEADER(h, t) SNMP_HEADER(h, t, AGENTX_FLAG_BLANK) - -#define SNMP_SESSION(h, p) \ - STORE(h->session_id, p->session_id); \ - STORE(h->transaction_id, p->transaction_id); \ - p->transaction_id++; \ - STORE(h->packet_id, p->packet_id); +#define SNMP_BLANK_HEADER(h, t) SNMP_HEADER(h, t, AGENTX_FLAG_BLANK) #define SNMP_CREATE(b, t, n) \ n = (void *) (b); \ memset(n, 0, sizeof(t)); \ (b) += sizeof(t); +#define SNMP_SESSION(h, p) \ + STORE_U32(h->session_id, p->session_id); \ + STORE_U32(h->transaction_id, p->transaction_id); \ + STORE_U32(h->packet_id, p->packet_id) + #define LOAD(v, bo) ((bo) ? get_u32(&v) : (u32) (v)) #define LOAD_16(v, bo) ((bo) ? get_u16(&v) : (u16) (v)) #define LOAD_PTR(v, bo) ((bo) ? get_u32(v) : (u32) *(v)) @@ -125,7 +129,17 @@ enum agentx_type { (varbind)->type = type_; \ packet += offset; -#define BGP_DATA(varbind, type_, packet) BGP_DATA_(varbind, type_, packet, 4) +#define SNMP_PUT_OID(buf, size, oid, byte_ord) \ + ({ \ + struct agentx_varbind *vb = (void *) buf; \ + SNMP_FILL_VARBIND(vb, oid, byte_ord); \ + }) + +#define SNMP_FILL_VARBIND(vb, oid, byte_ord) \ + snmp_oid_copy(&(vb)->name, (oid), (byte_ord)), snmp_oid_size((oid)) + +#define SNMP_VB_DATA(varbind) \ + (((void *)(varbind)) + snmp_varbind_header_size(varbind)) struct agentx_header { u8 version; @@ -273,12 +287,45 @@ enum agentx_response_err { AGENTX_RES_PROCESSING_ERR = 268, } PACKED; +struct agentx_context { + char *context; /* string name of this context */ + uint length; /* normal strlen() size */ + /* add buffered context hash? */ +}; + +struct snmp_pdu_context { + byte *buffer; /* pointer to buffer */ + uint size; /* unused space in buffer */ + uint context; /* context hash */ + int byte_ord; /* flag signaling NETWORK_BYTE_ORDER */ + enum agentx_response_err error; /* storage for result of current action */ +}; + +struct agentx_alloc_context { + u8 is_instance; /* flag INSTANCE_REGISTRATION */ + u8 new_index; /* flag NEW_INDEX */ + u8 any_index; /* flag ANY_INDEX */ + char *context; /* context to allocate in */ + uint clen; /* length of context string */ +}; + +struct additional_buffer { + node n; + byte *buf; /* pointer to buffer data */ + byte *pos; /* position of first unused byte */ +}; + int snmp_rx(sock *sk, uint size); int snmp_rx_stop(sock *sk, uint size); void snmp_down(struct snmp_proto *p); void snmp_register(struct snmp_proto *p, struct oid *oid, uint index, uint len); void snmp_unregister(struct snmp_proto *p, struct oid *oid, uint index, uint len); +void snmp_manage_tbuf(struct snmp_proto *p, struct snmp_pdu_context *c); + +struct oid *snmp_prefixize(struct snmp_proto *p, const struct oid *o, int byte_ord); +u8 snmp_get_mib_class(const struct oid *oid); + // debug wrapper #define snmp_log(...) log(L_INFO "snmp " __VA_ARGS__) -- 2.47.2