From 9a75af45734ca7c8bdf624f607b4a0face8278b8 Mon Sep 17 00:00:00 2001 From: Vojtech Vilimek Date: Sat, 5 Nov 2022 16:29:00 +0100 Subject: [PATCH] tmp: compiles, first tests --- proto/snmp/Makefile | 2 + proto/snmp/bgp_mib.c | 525 +++++++++++++++++++++++++++++++++++++++-- proto/snmp/bgp_mib.h | 55 ++++- proto/snmp/snmp.c | 3 - proto/snmp/snmp.h | 3 + proto/snmp/snmp_test.c | 197 ++++++++++++++++ proto/snmp/subagent.c | 269 +++++++++++++-------- proto/snmp/subagent.h | 23 +- 8 files changed, 958 insertions(+), 119 deletions(-) create mode 100644 proto/snmp/snmp_test.c diff --git a/proto/snmp/Makefile b/proto/snmp/Makefile index 0965e448..86070d4f 100644 --- a/proto/snmp/Makefile +++ b/proto/snmp/Makefile @@ -4,4 +4,6 @@ $(all-daemon) $(cf-local) $(call proto-build,snmp_build) +tests_src := snmp_test.c +tests_targets := $(tests_targets) $(tests-target-files) tests_objs := $(tests_objs) $(src-o-files) diff --git a/proto/snmp/bgp_mib.c b/proto/snmp/bgp_mib.c index 9318d620..2b36af84 100644 --- a/proto/snmp/bgp_mib.c +++ b/proto/snmp/bgp_mib.c @@ -6,36 +6,278 @@ * (c) 2022 CZ.NIC z.s.p.o * * Can be freely distributed and used under the terms of the GNU GPL. - * - * Parts of this file were auto-generated using mib2c - * using mib2c.create-dataset.conf */ -/* -#include -#include -#include -#include -*/ - -// fix conflicts -#undef PACKAGE_BUGREPORT -#undef PACKAGE_NAME -#undef PACKAGE_STRING -#undef PACKAGE_TARNAME -#undef PACKAGE_VERSION +/* BGP_MIB states see enum BGP_INTERNAL_STATES */ #include "snmp.h" #include "subagent.h" #include "bgp_mib.h" +static const char * const debug_bgp_states[] = { + [BGP_INTERNAL_INVALID] = "BGP_INTERNAL_INVALID", + [BGP_INTERNAL_BGP] = "BGP_INTERNAL_BGP", + [BGP_INTERNAL_VERSION] = "BGP_INTERNAL_VERSION", + [BGP_INTERNAL_LOCAL_AS] = "BGP_INTERNAL_LOCAL_AS", + [BGP_INTERNAL_PEER_TABLE] = "BGP_INTERNAL_PEER_TABLE", + [BGP_INTERNAL_PEER_ENTRY] = "BGP_INTERNAL_PEER_ENTRY", + [BGP_INTERNAL_IDENTIFIER] = "BGP_INTERNAL_IDENTIFIER", + [BGP_INTERNAL_STATE] = "BGP_INTERNAL_STATE", + [BGP_INTERNAL_ADMIN_STATUS] = "BGP_INTERNAL_ADMIN_STATUS", + [BGP_INTERNAL_NEGOTIATED_VERSION] = "BGP_INTERNAL_NEGOTIATED_VERSION", + [BGP_INTERNAL_LOCAL_ADDR] = "BGP_INTERNAL_LOCAL_ADDR", + [BGP_INTERNAL_LOCAL_PORT] = "BGP_INTERNAL_LOCAL_PORT", + [BGP_INTERNAL_REMOTE_ADDR] = "BGP_INTERNAL_REMOTE_ADDR", + [BGP_INTERNAL_REMOTE_PORT] = "BGP_INTERNAL_REMOTE_PORT", + [BGP_INTERNAL_REMOTE_AS] = "BGP_INTERNAL_REMOTE_AS", + [BGP_INTERNAL_RX_UPDATES] = "BGP_INTERNAL_RX_UPDATES", + [BGP_INTERNAL_TX_UPDATES] = "BGP_INTERNAL_TX_UPDATES", + [BGP_INTERNAL_RX_MESSAGES] = "BGP_INTERNAL_RX_MESSAGES", + [BGP_INTERNAL_TX_MESSAGES] = "BGP_INTERNAL_TX_MESSAGES", + [BGP_INTERNAL_LAST_ERROR] = "BGP_INTERNAL_LAST_ERROR", + [BGP_INTERNAL_FSM_TRANSITIONS] = "BGP_INTERNAL_FSM_TRANSITIONS", + [BGP_INTERNAL_FSM_ESTABLISHED_TIME] = "BGP_INTERNAL_FSM_ESTABLISHED_TIME", + [BGP_INTERNAL_RETRY_INTERVAL] = "BGP_INTERNAL_RETRY_INTERVAL", + [BGP_INTERNAL_HOLD_TIME] = "BGP_INTERNAL_HOLD_TIME", + [BGP_INTERNAL_KEEPALIVE] = "BGP_INTERNAL_KEEPALIVE", + [BGP_INTERNAL_HOLD_TIME_CONFIGURED] = "BGP_INTERNAL_HOLD_TIME_CONFIGURED", + [BGP_INTERNAL_KEEPALIVE_CONFIGURED] = "BGP_INTERNAL_KEEPALIVE_CONFIGURED", + [BGP_INTERNAL_ORIGINATION_INTERVAL] = "BGP_INTERNAL_ORIGINATION_INTERVAL", + [BGP_INTERNAL_MIN_ROUTE_ADVERTISEMENT] = "BGP_INTERNAL_MIN_ROUTE_ADVERTISEMENT", + [BGP_INTERNAL_IN_UPDATE_ELAPSED_TIME] = "BGP_INTERNAL_IN_UPDATE_ELAPSED_TIME", + [BGP_INTERNAL_END] = "BGP_INTERNAL_END", + [BGP_INTERNAL_NO_VALUE] = "BGP_INTERNAL_NO_VALUE", +}; + void snmp_bgp_register() {} +int +snmp_bgp_valid_ip4(struct oid *o) +{ + return snmp_valid_ip4_index_safe(o, 6); +} + +static u8 +bgp_get_candidate(u32 field) +{ + const u8 translation_table[] = { + [SNMP_BGP_IDENTIFIER] = BGP_INTERNAL_IDENTIFIER, + [SNMP_BGP_STATE] = BGP_INTERNAL_STATE, + [SNMP_BGP_ADMIN_STATUS] = BGP_INTERNAL_ADMIN_STATUS, + [SNMP_BGP_NEGOTIATED_VERSION] = BGP_INTERNAL_NEGOTIATED_VERSION, + [SNMP_BGP_LOCAL_ADDR] = BGP_INTERNAL_LOCAL_ADDR, + [SNMP_BGP_LOCAL_PORT] = BGP_INTERNAL_LOCAL_PORT, + [SNMP_BGP_REMOTE_ADDR] = BGP_INTERNAL_REMOTE_ADDR, + [SNMP_BGP_REMOTE_PORT] = BGP_INTERNAL_REMOTE_PORT, + [SNMP_BGP_REMOTE_AS] = BGP_INTERNAL_REMOTE_AS, + [SNMP_BGP_RX_UPDATES] = BGP_INTERNAL_RX_UPDATES, + [SNMP_BGP_TX_UPDATES] = BGP_INTERNAL_TX_UPDATES, + [SNMP_BGP_RX_MESSAGES] = BGP_INTERNAL_RX_MESSAGES, + [SNMP_BGP_TX_MESSAGES] = BGP_INTERNAL_TX_MESSAGES, + [SNMP_BGP_LAST_ERROR] = BGP_INTERNAL_LAST_ERROR, + [SNMP_BGP_FSM_TRANSITIONS] = BGP_INTERNAL_FSM_TRANSITIONS, + [SNMP_BGP_FSM_ESTABLISHED_TIME] = BGP_INTERNAL_FSM_ESTABLISHED_TIME, + [SNMP_BGP_RETRY_INTERVAL] = BGP_INTERNAL_RETRY_INTERVAL, + [SNMP_BGP_HOLD_TIME] = BGP_INTERNAL_HOLD_TIME, + [SNMP_BGP_KEEPALIVE] = BGP_INTERNAL_KEEPALIVE, + [SNMP_BGP_HOLD_TIME_CONFIGURED] = BGP_INTERNAL_HOLD_TIME_CONFIGURED, + [SNMP_BGP_KEEPALIVE_CONFIGURED] = BGP_INTERNAL_KEEPALIVE_CONFIGURED, + [SNMP_BGP_ORIGINATION_INTERVAL] = BGP_INTERNAL_ORIGINATION_INTERVAL, + [SNMP_BGP_MIN_ROUTE_ADVERTISEMENT] = BGP_INTERNAL_MIN_ROUTE_ADVERTISEMENT, + [SNMP_BGP_IN_UPDATE_ELAPSED_TIME] = BGP_INTERNAL_IN_UPDATE_ELAPSED_TIME, + }; + + /* 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])) + return translation_table[field]; + else + return BGP_INTERNAL_NO_VALUE; +} + +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_NONE; +} + +/** + * snmp_bgp_state - linearize oid from BGP4-MIB + * @oid: prefixed object identifier from BGP4-MIB::bgp subtree + * + * Returns linearized state for Get-PDU, GetNext-PDU and GetBulk-PDU packets. + */ +u8 +snmp_bgp_state(struct oid *oid) +{ + /* already checked: + xxxxxxxx p + * (*oid): .1.3.6.1.2.1.15 + * -> BGP4-MIB::bgp (root) + */ + + u8 state = BGP_INTERNAL_NO_VALUE; + + u8 candidate; + switch (oid->n_subid) + { + default: + if (oid->n_subid < 2) + { + state = BGP_INTERNAL_INVALID; + break; + } + /* else oid->n_subid >= 2 */ + /* fall through */ + + /* between ids[6] and ids[9] should be IP address + * validity is checked later in execution because + * this field also could mean a boundry (upper or lower) + */ + case 9: + case 8: + case 7: + case 6: + case 5: + state = bgp_get_candidate(oid->ids[4]); + + /* fall through */ + + case 4: + if (oid->ids[3] == BGP4_PEER_ENTRY) + state = (state == BGP_INTERNAL_NO_VALUE) ? + BGP_INTERNAL_PEER_ENTRY : state; + else + state = BGP_INTERNAL_NO_VALUE; + + /* fall through */ + + case 3: + /* u8 candidate; */ + switch (oid->ids[2]) + { + + case SNMP_BGP_VERSION: + state = BGP_INTERNAL_VERSION; + break; + case SNMP_BGP_LOCAL_AS: + state = BGP_INTERNAL_LOCAL_AS; + break; + case SNMP_BGP_PEER_TABLE: + /* candidate avoid overriding more specific state */ + candidate = BGP_INTERNAL_PEER_TABLE; + break; + + + default: /* test fails */ + /* invalidate the state forcefully */ + if (oid->ids[2] < SNMP_BGP_VERSION) + { + state = BGP_INTERNAL_NO_VALUE; + candidate = BGP_INTERNAL_NO_VALUE; + } + + else /* oid->ids[2] > SNMP_BGP_PEER_TABLE */ + state = BGP_INTERNAL_END; + } + state = (state == BGP_INTERNAL_NO_VALUE) ? + candidate : state; + + /* fall through */ + + case 2: /* bare BGP4-MIB::bgp */ + if (state == BGP_INTERNAL_NO_VALUE || + state == BGP_INTERNAL_INVALID) + state = BGP_INTERNAL_BGP; + } + + return state; +} + +inline int +is_dynamic(u8 state) +{ + return (state >= BGP_INTERNAL_IDENTIFIER && + state <= BGP_INTERNAL_IN_UPDATE_ELAPSED_TIME); +} + +static inline int +snmp_bgp_has_value(u8 state) +{ + /* bitmap would be faster */ + if (state <= BGP_INTERNAL_BGP || + state == BGP_INTERNAL_PEER_TABLE || + state == BGP_INTERNAL_PEER_ENTRY) + return 0; /* hasn't value */ + else + { + + } + return 1; /* has value */ +} + +/** + * snmp_bgp_get_valid - only states with valid value + * @state: BGP linearized state + * + * Returns @state if has value in BGP4-MIB, zero otherwise. Used for Get-PDU + * packets. + */ +u8 +snmp_bgp_get_valid(u8 state) +{ + /* invalid + * SNMP_BGP SNMP_BGP_PEER_TABLE SNMP_BGP_PEER_ENTRY + * SNMP_BGP_FSM_ESTABLISHED_TIME SNMP_BGP_IN_UPDATE_ELAPSED_TIME + */ + if (state == 1 || state == 4 || state == 5 || + state == 21 || state == 29) + return 0; + else + return state; +} + +/** + * snmp_bgp_next_state - next state that has value + * @state: BGP linearized state + * + * Returns successor state of @state with valid value in BG4-MIB. Used for + * GetNext-PDU and GetBulk-PDU packets. + */ +u8 +snmp_bgp_next_state(u8 state) +{ + switch (state) + { + case BGP_INTERNAL_LOCAL_AS: + case BGP_INTERNAL_PEER_TABLE: + 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: + return state + 1; + } +} + int 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 || @@ -57,6 +299,253 @@ snmp_bgp_is_supported(struct oid *o) else return 0; } - else - return 0; + + return 0; +} + +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); + + /* if same state, no need to realloc anything */ + if (snmp_bgp_state(oid) == state) + return oid; + + switch (state) + { + case BGP_INTERNAL_BGP: + /* could destroy same old data */ + oid = mb_realloc(oid, sizeof(struct oid) + 2 * sizeof(u32)); + oid->n_subid = 2; + oid->ids[0] = 1; + oid->ids[1] = SNMP_BGP4_MIB; + break; + + case BGP_INTERNAL_VERSION: + oid = mb_realloc(oid, sizeof(struct oid) + 3 * sizeof(u32)); + oid->n_subid = 3; + oid->ids[2] = SNMP_BGP_VERSION; + break; + + case BGP_INTERNAL_LOCAL_AS: + oid->ids[2] = 2; + break; + + case BGP_INTERNAL_IDENTIFIER: + oid = mb_realloc(oid, sizeof(struct oid) + 9 * sizeof(u32)); + oid->n_subid = 9; + 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; + break; + +#define SNMP_UPDATE_CASE(num, update) \ + case num: \ + oid->ids[4] = update; \ + break; + + SNMP_UPDATE_CASE(BGP_INTERNAL_STATE, SNMP_BGP_STATE) + + SNMP_UPDATE_CASE(BGP_INTERNAL_ADMIN_STATUS, SNMP_BGP_ADMIN_STATUS) + + SNMP_UPDATE_CASE(BGP_INTERNAL_NEGOTIATED_VERSION, SNMP_BGP_NEGOTIATED_VERSION) + + SNMP_UPDATE_CASE(BGP_INTERNAL_LOCAL_ADDR, SNMP_BGP_LOCAL_ADDR) + + SNMP_UPDATE_CASE(BGP_INTERNAL_LOCAL_PORT, SNMP_BGP_LOCAL_PORT) + + SNMP_UPDATE_CASE(BGP_INTERNAL_REMOTE_ADDR, SNMP_BGP_REMOTE_ADDR) + + SNMP_UPDATE_CASE(BGP_INTERNAL_REMOTE_PORT, SNMP_BGP_REMOTE_PORT) + + SNMP_UPDATE_CASE(BGP_INTERNAL_REMOTE_AS, SNMP_BGP_REMOTE_AS) + + SNMP_UPDATE_CASE(BGP_INTERNAL_RX_UPDATES, SNMP_BGP_RX_UPDATES) + + SNMP_UPDATE_CASE(BGP_INTERNAL_TX_UPDATES, SNMP_BGP_TX_UPDATES) + + SNMP_UPDATE_CASE(BGP_INTERNAL_RX_MESSAGES, SNMP_BGP_RX_MESSAGES) + + SNMP_UPDATE_CASE(BGP_INTERNAL_TX_MESSAGES, SNMP_BGP_TX_MESSAGES) + + SNMP_UPDATE_CASE(BGP_INTERNAL_LAST_ERROR, SNMP_BGP_LAST_ERROR) + + SNMP_UPDATE_CASE(BGP_INTERNAL_FSM_TRANSITIONS, SNMP_BGP_FSM_TRANSITIONS) + + SNMP_UPDATE_CASE(BGP_INTERNAL_FSM_ESTABLISHED_TIME, SNMP_BGP_FSM_ESTABLISHED_TIME) + + SNMP_UPDATE_CASE(BGP_INTERNAL_RETRY_INTERVAL, SNMP_BGP_RETRY_INTERVAL) + + SNMP_UPDATE_CASE(BGP_INTERNAL_HOLD_TIME, SNMP_BGP_HOLD_TIME) + + SNMP_UPDATE_CASE(BGP_INTERNAL_KEEPALIVE, SNMP_BGP_KEEPALIVE) + + SNMP_UPDATE_CASE(BGP_INTERNAL_HOLD_TIME_CONFIGURED, SNMP_BGP_HOLD_TIME_CONFIGURED) + + SNMP_UPDATE_CASE(BGP_INTERNAL_KEEPALIVE_CONFIGURED, SNMP_BGP_KEEPALIVE_CONFIGURED) + + SNMP_UPDATE_CASE(BGP_INTERNAL_ORIGINATION_INTERVAL, SNMP_BGP_ORIGINATION_INTERVAL) + + 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) + } + + return oid; +#undef SNMP_UPDATE_CASE +} + +// 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) +{ + ip4_addr ip4 = ip4_from_oid(o_start); + ip4_addr dest = ip4_from_oid(o_end); + + net_addr *net = mb_allocz(p->p.pool, sizeof(struct net_addr)); + net_fill_ip4(net, ip4, IP4_MAX_PREFIX_LENGTH); + + log(L_INFO "dynamic part of BGP mib"); + + struct f_trie_walk_state *ws = mb_allocz(p->p.pool, + sizeof(struct f_trie_walk_state)); + + trie_walk_init(ws, p->bgp_trie, NULL); + + if (trie_walk_next(ws, net) && ip4_less(net4_prefix(net), dest)) + { + struct oid *o = mb_allocz(p->p.pool, sizeof(struct oid) + 9 * sizeof(u32)); + o->n_subid = 9; + + memcpy(o, o_start, snmp_oid_size(o_start)); + snmp_oid_ip4_index(o, net4_prefix(net)); + + mb_free(net); + mb_free(ws); + + return o; + } + + else + { + mb_free(net); + mb_free(ws); + } + + return NULL; +} + +byte * +snmp_bgp_fill(struct snmp_proto *p UNUSED, struct oid *oid, byte *buf UNUSED, +uint size UNUSED, uint contid UNUSED, int byte_ord UNUSED) +{ + u8 state = snmp_bgp_state(oid); + (void)state; + return NULL; +} + +/* 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) +{ + u8 start_state = snmp_bgp_state(o_start); + //u8 state_curr = snmp_bgp_state(o_start); + //u8 state_end = (o_end) ? snmp_bgp_state(o_end) : 0; + + if (o_start->include && snmp_bgp_has_value(start_state) && + !is_dynamic(start_state) && o_start->n_subid == 3) + { + o_start->include = 0; /* disable including for next time */ + return o_start; + } + + /* if state is_dynamic() then has more value and need find the right one */ + else if (!is_dynamic(start_state)) + { + u8 next_state = snmp_bgp_next_state(start_state); + o_start = update_bgp_oid(o_start, next_state); + + if (!is_dynamic(next_state)) + return o_start; + + else + { + struct oid *copy = o_start; + do { + /* update_bgp_oid can reallocate the underlaying struct */ + o_start = copy = update_bgp_oid(copy, next_state); + + o_start = bgp_find_dynamic_oid(p, o_start, o_end, next_state); + + next_state = snmp_bgp_next_state(next_state); + + } while (o_start != NULL && next_state < BGP_INTERNAL_END); + + return o_start; + } + } + + /* else - is_dynamic(start_state) */ + /* ... (same as do ... while above) */ + + + return NULL; + /* TODO not implemented yet */ + + /* older implementation - untested */ + /* if o_curr is in invalid state, o_curr->include does't make any + * difference; invalid state ~ no value to put in response packet + */ + /* indent \v/ */ + u8 state_curr = snmp_bgp_getnext_valid(state_curr); + + struct oid *o_curr = update_bgp_oid(o_curr, state_curr); + + /* static part of BGP4-MIB tree, not depending on BGP connections */ + if (state_curr <= 5) + { + return o_curr; + } + /* dynamic part of BGP4-MIB tree, depending on BGP connections */ + else /* state_curr > 5 */ + { + ip4_addr ip4 = ip4_from_oid(o_curr); + ip4_addr dest = ip4_from_oid(o_end); + + net_addr *net = mb_allocz(p->p.pool, sizeof(struct net_addr)); + net_fill_ip4(net, ip4, IP4_MAX_PREFIX_LENGTH); + + log(L_INFO "dynamic part of BGP mib"); + + struct f_trie_walk_state *ws = mb_allocz(p->p.pool, + sizeof(struct f_trie_walk_state)); + + struct oid *o = mb_allocz(p->p.pool, sizeof(struct oid) + 8 * sizeof(u32)); + o->n_subid = 9; + trie_walk_init(ws, p->bgp_trie, NULL); + + if (trie_walk_next(ws, net) && ip4_less(net4_prefix(net), dest)) + { + memcpy(o, o_curr, snmp_oid_size(o_curr)); + snmp_oid_ip4_index(o, net4_prefix(net)); + + mb_free(net); + mb_free(ws); + + return o; + } + + else + { + mb_free(net); + mb_free(ws); + + return NULL; + } + } } diff --git a/proto/snmp/bgp_mib.h b/proto/snmp/bgp_mib.h index 5854bcf4..1a7b6c07 100644 --- a/proto/snmp/bgp_mib.h +++ b/proto/snmp/bgp_mib.h @@ -8,7 +8,7 @@ enum BGP4_MIB { SNMP_BGP_IDENTIFIER = 1, SNMP_BGP_STATE = 2, SNMP_BGP_ADMIN_STATUS = 3, /* in read-only mode */ - SNMP_BGP_VERSION = 4, + SNMP_BGP_NEGOTIATED_VERSION = 4, SNMP_BGP_LOCAL_ADDR = 5, SNMP_BGP_LOCAL_PORT = 6, SNMP_BGP_REMOTE_ADDR = 7, @@ -34,12 +34,63 @@ enum BGP4_MIB { //void snmp_init_bgp_table(void); //void snmp_del_bgp_table(void); +struct oid; + void snmp_bgp_register(void); int snmp_bgp_is_supported(struct oid *o); -#define BGP4_MIB_VERSION 1 +int snmp_bgp_valid_ip4(struct oid *o); +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 oid *oid, byte *buf, uint size, uint contid UNUSED, int byte_ord); + +#define BGP4_MIB_VERSION 1 #define BGP4_MIB_LOCAL_AS 2 #define BGP4_PEER_TABLE 3 #define BGP4_PEER_ENTRY 1 +#define SNMP_BGP_VERSION 1 +#define SNMP_BGP_LOCAL_AS 2 +#define SNMP_BGP_PEER_TABLE 3 +#define SNMP_BGP_PEER_ENTRY 1 + +/* BGP linearized state */ +enum BGP_INTERNAL_STATES { + BGP_INTERNAL_INVALID = 0, + BGP_INTERNAL_BGP = 1, + BGP_INTERNAL_VERSION, + BGP_INTERNAL_LOCAL_AS, + BGP_INTERNAL_PEER_TABLE, + BGP_INTERNAL_PEER_ENTRY, + BGP_INTERNAL_IDENTIFIER, + BGP_INTERNAL_STATE, + BGP_INTERNAL_ADMIN_STATUS, + BGP_INTERNAL_NEGOTIATED_VERSION, + BGP_INTERNAL_LOCAL_ADDR, + BGP_INTERNAL_LOCAL_PORT, + BGP_INTERNAL_REMOTE_ADDR, + BGP_INTERNAL_REMOTE_PORT, + BGP_INTERNAL_REMOTE_AS, + BGP_INTERNAL_RX_UPDATES, + BGP_INTERNAL_TX_UPDATES, + BGP_INTERNAL_RX_MESSAGES, + BGP_INTERNAL_TX_MESSAGES, + BGP_INTERNAL_LAST_ERROR, + BGP_INTERNAL_FSM_TRANSITIONS, + BGP_INTERNAL_FSM_ESTABLISHED_TIME, + BGP_INTERNAL_RETRY_INTERVAL, + BGP_INTERNAL_HOLD_TIME, + BGP_INTERNAL_KEEPALIVE, + BGP_INTERNAL_HOLD_TIME_CONFIGURED, + BGP_INTERNAL_KEEPALIVE_CONFIGURED, + BGP_INTERNAL_ORIGINATION_INTERVAL, + BGP_INTERNAL_MIN_ROUTE_ADVERTISEMENT, + BGP_INTERNAL_IN_UPDATE_ELAPSED_TIME, + BGP_INTERNAL_END, + BGP_INTERNAL_NO_VALUE = 255, +} PACKED; + #endif diff --git a/proto/snmp/snmp.c b/proto/snmp/snmp.c index ee8dd6f7..0343168d 100644 --- a/proto/snmp/snmp.c +++ b/proto/snmp/snmp.c @@ -5,9 +5,6 @@ * (c) 2022 CZ.NIC z.s.p.o. * * Can be freely distributed and used under the terms of the GNU GPL. - * - * Parts of this file were auto-generated using mib2c - * using mib2c.create-dataset.conf */ #include "nest/bird.h" diff --git a/proto/snmp/snmp.h b/proto/snmp/snmp.h index ac2f01d6..45f24aa7 100644 --- a/proto/snmp/snmp.h +++ b/proto/snmp/snmp.h @@ -106,4 +106,7 @@ struct snmp_proto { uint errs; }; +/* fixes bugs when making tests */ +//struct protocol proto_snmp; + #endif diff --git a/proto/snmp/snmp_test.c b/proto/snmp/snmp_test.c new file mode 100644 index 00000000..dd4adc0d --- /dev/null +++ b/proto/snmp/snmp_test.c @@ -0,0 +1,197 @@ +/* + * BIRD -- Simple Network Management Protocol (SNMP) Unit tests + * + * (c) 2022 Vojtech Vilimek + * (c) 2022 CZ.NIC z.s.p.o + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#include "test/birdtest.h" + +#include "bgp_mib.h" +#include "subagent.h" +#include "snmp.h" + +#define SNMP_EXPECTED(actual, expected) \ + bt_debug("%s expected: %3u actual: %3u\n", \ + #expected, expected, actual); + +void +dump_oid(struct oid *oid) +{ + bt_debug(" OID DUMP: \n"); + bt_debug(" n_subid = %3u prefix = %3u include %s --- \n", + oid->n_subid, oid->prefix, (oid->include != 0) ? "yes" : "no" ); + + for (int i = 0; i < oid->n_subid; i++) + bt_debug(" %u: %u\n", i + 1, oid->ids[i]); + + bt_debug(" OID DUMP END\n"); +} + +void +dump_bgp_state_values(void) +{ + // TODO XXX here +} + + +static void +test_oid(struct oid *oid, uint base_size) +{ + /* tests all states one by one */ + + oid->n_subid = base_size + 2; + oid->ids[0] = 1; + oid->ids[1] = 15; // BGP4-MIB::bgp + bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_BGP); + + oid->n_subid = base_size + 3; + oid->ids[2] = 1; // BGP4-MIB::bgpVersion + bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_VERSION); + + oid->ids[2] = 2; // BGP4-MIB::bgpLocalAs + bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_LOCAL_AS); + + oid->ids[2] = 3; // BGP4-MIB::bgpPeerTable + bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_PEER_TABLE); + + bt_debug("testing BGP4-MIB::bgpPeerEntry\n"); + oid->n_subid = base_size + 4; + oid->ids[2] = 3; + oid->ids[3] = 1; // BGP4-MIB::bgpPeerEntry + dump_oid(oid); + SNMP_EXPECTED(snmp_bgp_state(oid), BGP_INTERNAL_PEER_ENTRY); + bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_PEER_ENTRY); + + oid->n_subid = base_size + 5; + oid->ids[2] = 3; + oid->ids[3] = 1; + oid->ids[4] = 1; // BGP4-MIB::bgpPeerIdentifier + bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_IDENTIFIER); + + oid->ids[4] = 2; // BGP4-MIB::bgpPeerState + bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_STATE); + + oid->ids[4] = 3; // BGP4-MIB::bgpPeerAdminStatus + bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_ADMIN_STATUS); + + oid->ids[4] = 4; // BGP4-MIB::bgpPeerNegotiatedVersion + bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_NEGOTIATED_VERSION); + + oid->ids[4] = 5; // BGP4-MIB::bgpPeerLocalAddr + bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_LOCAL_ADDR); + + oid->ids[4] = 6; // BGP4-MIB::bgpPeerLocalPort + bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_LOCAL_PORT); + + oid->ids[4] = 7; // BGP4-MIB::bgpPeerRemoteAddr + bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_REMOTE_ADDR); + + oid->ids[4] = 8; // BGP4-MIB::bgpPeerRemotePort + bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_REMOTE_PORT); + + oid->ids[4] = 9; // BGP4-MIB::bgpPeerRemoteAs + bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_REMOTE_AS); + + oid->ids[4] = 10; // BGP4-MIB::bgpPeerInUpdates + bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_RX_UPDATES); + + oid->ids[4] = 11; // BGP4-MIB::bgpPeerOutUpdates + bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_TX_UPDATES); + + oid->ids[4] = 12; // BGP4-MIB::bgpPeerInTotalMessages + bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_RX_MESSAGES); + + oid->ids[4] = 13; // BGP4-MIB::bgpPeerOutTotalMessages + bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_TX_MESSAGES); + + oid->ids[4] = 14; // BGP4-MIB::bgpPeerLastError + bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_LAST_ERROR); + + oid->ids[4] = 15; // BGP4-MIB::bgpPeerFsmEstablishedTransitions + bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_FSM_TRANSITIONS); + + oid->ids[4] = 16; // BGP4-MIB::bgpPeerFsmEstablishedTime + bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_FSM_ESTABLISHED_TIME); + + oid->ids[4] = 17; // BGP4-MIB::bgpPeerConnectionRetryInterval + bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_RETRY_INTERVAL); + + oid->ids[4] = 18; // BGP4-MIB::bgpPeerHoldTime + bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_HOLD_TIME); + + oid->ids[4] = 19; // BGP4-MIB::bgpPeerKeepAlive + bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_KEEPALIVE); + + oid->ids[4] = 20; // BGP4-MIB::bgpPeerHoldTimeConfigured + bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_HOLD_TIME_CONFIGURED); + + oid->ids[4] = 21; // BGP4-MIB::bgpPeerKeepAliveConfigured + bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_KEEPALIVE_CONFIGURED); + + oid->ids[4] = 22; // BGP4-MIB::bgpPeerMinASOriginationInterval + bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_ORIGINATION_INTERVAL); + + oid->ids[4] = 23; // BGP4-MIB::bgpPeerMinRouteAdvertisementInverval + bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_MIN_ROUTE_ADVERTISEMENT); + + oid->ids[4] = 24; // BGP4-MIB::bgpPeerInUpdateElapsedTime + bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_IN_UPDATE_ELAPSED_TIME); +} + +static int +t_s_bgp_state(void) +{ + struct oid *oid = alloca(sizeof(struct oid) + 10 * sizeof(32)); + + /* oid header */ + oid->n_subid = 0; + oid->prefix = 2; + oid->include = 0; + oid->pad = 0; + + /* test all states with expected oid length */ + bt_debug("testing precise oids\n"); + test_oid(oid, 0); + + for (int i = 0; i < 10; i++) + oid->ids[i] = (u32) bt_random(); + + /* if this subid is too high it does not match the test case + * in general test_oid() func + */ + oid->ids[2] = 0; + + /* test all states with garbage ip */ + bt_debug("testing oids with random ip index\n"); + test_oid(oid, 4); + + /* test all states with invalid ip */ + bt_debug("testing oids with invalid ip index\n"); + /* zero the states that overlap */ + oid->ids[2] = 0; + oid->ids[3] = 0; + oid->ids[4] = 0; + + oid->ids[5] = 0; + oid->ids[6] = 257; + oid->ids[7] = 127; + oid->ids[8] = 0xFFFF; + test_oid(oid, 4); + + bt_debug("testing too long oids\n"); + bt_debug("not implemented\n"); + bt_debug("exiting\n"); + return 1; +} + +int main(int argc, char **argv) +{ + bt_init(argc, argv); + + bt_test_suite(t_s_bgp_state, "Function snmp_bgp_state()"); + + return bt_exit_value(); +} diff --git a/proto/snmp/subagent.c b/proto/snmp/subagent.c index 475e6fe5..7a0ad4db 100644 --- a/proto/snmp/subagent.c +++ b/proto/snmp/subagent.c @@ -24,7 +24,6 @@ */ static int parse_response(struct snmp_proto *p, byte *buf, uint size); -static inline uint oid_size(struct oid *o); static inline uint vb_size(struct agentx_varbind *vb); static int snmp_stop_ack(sock *sk, uint size); static void do_response(struct snmp_proto *p, byte *buf, uint size); @@ -32,7 +31,7 @@ static uint parse_get_pdu(struct snmp_proto *p, byte *buf, uint size); static uint parse_gets_pdu(struct snmp_proto *p, byte *buf, uint size); static byte *prepare_response(struct snmp_proto *p, byte *buf, uint size); static void response_err_ind(byte *buf, uint err, uint ind); -static struct oid *bgp_search(struct snmp_proto *p, struct oid *o_start, struct oid *o_end, struct oid *o_curr); +static struct oid *search_mib(struct snmp_proto *p, struct oid *o_start, struct oid *o_end, struct oid *o_curr, uint contid); static struct oid *prefixize(struct snmp_proto *p, struct oid *o, int byte_ord); static inline byte *find_n_fill(struct snmp_proto *p, struct oid *o, byte *buf, uint size, uint contid, int byte_ord); static byte *no_such_object(byte *buf, struct agentx_varbind *vb); @@ -67,6 +66,25 @@ str_size(const char *str) return 4 + BIRD_ALIGN(strlen(str), 4); } +int +snmp_valid_ip4_index(struct oid *o, uint start) +{ + for (int i = 0; i < 4; i++) + if (o->ids[start + i] >= 256) + return 0; // false + + return 1; // true +} + +int +snmp_valid_ip4_index_safe(struct oid *o, uint start) +{ + if (start + 3 < o->n_subid) + return snmp_valid_ip4_index(o, start); + else + return 0; // false +} + static byte * put_str(byte *buf, const char *str) { @@ -131,8 +149,8 @@ put_oid(byte *buf, struct oid *oid) return buf + (oid->n_subid << 2); } -static void -oid_ip4_index(struct oid *o, ip4_addr addr) +void +snmp_oid_ip4_index(struct oid *o, ip4_addr addr) { u32 temp = ip4_to_u32(addr); STORE(o->ids[5], temp >> 24); @@ -166,7 +184,7 @@ open_pdu(struct snmp_proto *p, struct oid *oid) //uint pkt_size = 0; - if (size > AGENTX_HEADER_SIZE + oid_size(oid) + str_size(str)) + if (size > AGENTX_HEADER_SIZE + snmp_oid_size(oid) + str_size(str)) { log(L_INFO "open_pdu()"); @@ -196,7 +214,7 @@ open_pdu(struct snmp_proto *p, struct oid *oid) else log(L_INFO "open_pdu() insufficient size, %u <= %u ", - size, AGENTX_HEADER_SIZE + oid_size(oid) + str_size(str)); + size, AGENTX_HEADER_SIZE + snmp_oid_size(oid) + str_size(str)); } /* index allocate / deallocate pdu * / @@ -237,7 +255,7 @@ un_register_pdu(struct snmp_proto *p, struct oid *oid, uint index, uint len, u8 uint size = sk->tbsize; /* conditional +4 for upper-bound */ - if (size > AGENTX_HEADER_SIZE + oid_size(oid) + ((len > 1) ? 4 : 0)) + if (size > AGENTX_HEADER_SIZE + snmp_oid_size(oid) + ((len > 1) ? 4 : 0)) { log(L_INFO "un_register_pdu()"); struct agentx_un_register_pdu *ur; @@ -447,11 +465,11 @@ do_response(struct snmp_proto *p, byte *buf, uint size UNUSED) // register whole BGP4-MIB u32 arr_bgp[] = {1, 15, 1}; - struct oid *o = mb_allocz(p->p.pool, 4 * 4); + struct oid *o = mb_allocz(p->p.pool, 4 * sizeof(u32)); put_u8(&o->n_subid, 2); put_u8(&o->prefix, 2); - memcpy(o->ids, arr_bgp, 2 * 4); + memcpy(o->ids, arr_bgp, 2 * sizeof(u32)); snmp_register(p, o, 0, 1); @@ -466,12 +484,12 @@ do_response(struct snmp_proto *p, byte *buf, uint size UNUSED) mb_free(o); u32 arr_with_prefix[] = {1, 15, 3, 1, 1}; - struct oid *o2 = mb_allocz(p->p.pool, 10 * 4); + struct oid *o2 = mb_allocz(p->p.pool, 10 * sizeof(u32)); put_u8(&o2->n_subid, 9); - memcpy(o2->ids, arr_with_prefix, 5 * 4); + memcpy(o2->ids, arr_with_prefix, 5 * sizeof(u32)); u32 remote_addr[] = {10, 0, 0, 0}; - memcpy(o2->ids + 5, remote_addr, 4 * 4); + memcpy(o2->ids + 5, remote_addr, 4 * sizeof(u32)); STORE(o2->prefix, 2); // register first line in BGP4-MIB bgpPeerTable @@ -481,7 +499,7 @@ do_response(struct snmp_proto *p, byte *buf, uint size UNUSED) log(L_INFO "before hash walk"); HASH_WALK(p->bgp_hash, next, peer) { - oid_ip4_index(o2, ipa_to_ip4(peer->peer_ip)); + snmp_oid_ip4_index(o2, ipa_to_ip4(peer->peer_ip)); log(L_INFO ""); log(L_INFO "o2 n_subid %u prefix %u include %u", o2->n_subid, @@ -552,12 +570,12 @@ parse_get_pdu(struct snmp_proto *p, byte *buf, uint size) { struct oid *o_start, *o_end; o_start = (struct oid *) pkt; - pkt += oid_size(o_start); + pkt += snmp_oid_size(o_start); o_end = (struct oid *) pkt; // for Get-PDU always null - pkt += oid_size(o_end); + pkt += snmp_oid_size(o_end); - log(L_INFO "sizes o_start %lu o_end %lu", oid_size(o_start), - oid_size(o_end)); + log(L_INFO "sizes o_start %lu o_end %lu", snmp_oid_size(o_start), + snmp_oid_size(o_end)); log(L_INFO "o_subid: %u o_prefix %u o_include %u ---", o_start->n_subid, o_start->prefix, o_start->include); @@ -609,6 +627,21 @@ parse_get_pdu(struct snmp_proto *p, byte *buf, uint size) return 1; } +static u8 +get_mib_class(struct oid *oid) +{ + if (oid->prefix != 2 && oid->ids[0] != 1) + return SNMP_CLASS_INVALID; + + switch (oid->ids[1]) + { + case SNMP_BGP4_MIB: + return SNMP_CLASS_BGP; + + default: + return SNMP_CLASS_END; + } +} /* req is request */ static uint @@ -641,37 +674,43 @@ parse_gets_pdu(struct snmp_proto *p, byte *req, uint size) int err = 0; while (!err && pkt - req < pkt_size) { - struct oid *o_start, *o_end; - o_start = (struct oid *) pkt; - pkt += oid_size(o_start); - o_end = (struct oid *) pkt; - pkt += oid_size(o_end); - - // TODO normalize OID to prefix form + struct oid *o_start_b, *o_end_b; + o_start_b = (struct oid *) pkt; + pkt += snmp_oid_size(o_start_b); + o_end_b = (struct oid *) pkt; + pkt += snmp_oid_size(o_end_b); /* advertised size of oid is greater then size of message */ - if (oid_size(o_start) > size || oid_size(o_end) > size) + if (snmp_oid_size(o_start_b) > size || snmp_oid_size(o_end_b) > size) { log(L_INFO "too big o_start or o_end"); err = -1; /* parse error too big n_subid (greater than message) */ continue; } + /* object identifier (oid) normalization */ + struct oid *o_start = prefixize(p, o_start_b, byte_ord); + struct oid *o_end = prefixize(p, o_end_b, byte_ord); + + u8 mib_class = get_mib_class(o_start); switch (h->type) { case AGENTX_GET_PDU: log(L_INFO "type Get-PDU"); - res_pkt = find_n_fill(p, o_start, res_pkt, rsize, 0, byte_ord); + res_pkt = snmp_mib_fill(p, o_start, mib_class, res_pkt, rsize, 0, byte_ord); + //res_pkt = find_n_fill(p, o_start, res_pkt, rsize, 0, byte_ord); break; case AGENTX_GET_NEXT_PDU: log(L_INFO "type GetNext-PDU"); - o_start = bgp_search(p, o_start, o_end, NULL); + + o_start = search_mib(p, o_start, o_end, NULL, 0); if (o_start) - res_pkt = find_n_fill(p, o_start, res_pkt, rsize, 0, byte_ord); + res_pkt = snmp_mib_fill(p, o_start, mib_class, res_pkt, rsize, 0, byte_ord); + //res_pkt = find_n_fill(p, o_start, res_pkt, rsize, 0, byte_ord); else { - log(L_INFO "null o_start GetNext-PDU"); + log(L_INFO "null o_start GetNext-PDU err handling next"); err = -2; continue; } @@ -680,13 +719,17 @@ parse_gets_pdu(struct snmp_proto *p, byte *req, uint size) case AGENTX_GET_BULK_PDU: { log(L_INFO "type GetBulk-PDU"); + struct oid *o_curr = NULL; /* TODO add res packet size limiting logic */ - while ((o_curr = bgp_search(p, o_start, o_end, o_curr)) != NULL) + while ((o_curr = search_mib(p, o_start, o_end, o_curr, 0)) != NULL) { - res_pkt = find_n_fill(p, o_curr, res_pkt, rsize, 0, byte_ord); + res_pkt = snmp_mib_fill(p, o_curr, mib_class, res_pkt, rsize, 0, byte_ord); + //res_pkt = find_n_fill(p, o_curr, res_pkt, rsize, 0, byte_ord); } + mb_free(o_curr); + /* no item found */ if (res_pkt == res + sizeof(struct agentx_response)) { @@ -694,11 +737,13 @@ parse_gets_pdu(struct snmp_proto *p, byte *req, uint size) err = -2; continue; } - break; } } + mb_free(o_start); + mb_free(o_end); + ind++; } @@ -757,19 +802,29 @@ snmp_stop_subagent(struct snmp_proto *p) } /* return number of bytes used by @o */ -static inline uint -oid_size(struct oid *o) +uint +snmp_oid_size(struct oid *o) { /* faster multipication by 4 */ return 4 + (o->n_subid << 2); } +static inline int +oid_prefix(struct oid *o, u32 *prefix, uint len) +{ + for (uint i = 0; i < len; i++) + if (o->ids[i] != prefix[i]) + return 0; // false + + return 1; // true +} + /* return number of bytes used by @vb */ static inline uint vb_size(struct agentx_varbind *vb) { /* +4B for type and pad */ - return oid_size(&vb->name) + 4; + return snmp_oid_size(&vb->name) + 4; } int @@ -926,51 +981,37 @@ has_inet_prefix(struct oid *o) o->ids[3] == 1); } -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_NONE; -} - -/* tree is tree with "internet" prefix .1.3.6.1 */ +/* tree is tree with "internet" prefix .1.3.6.1 + working only with o_start, o_end allocated in heap (not from buffer)*/ static struct oid * -bgp_search(struct snmp_proto *p, struct oid *o_start, struct oid *o_end, struct oid *o_curr) +search_mib(struct snmp_proto *p, struct oid *o_start, struct oid *o_end, struct oid *o_curr, uint contid UNUSED) { - ip4_addr ip4 = ip4_from_oid(o_start); - ip4_addr dest = ip4_from_oid(o_end); + log(L_INFO "search_mib()"); - net_addr *net = mb_allocz(p->p.pool, sizeof(struct net_addr)); - net_fill_ip4(net, ip4, IP4_MAX_PREFIX_LENGTH); - - log(L_INFO "o_start n_sub %u prefix %u include %u", - o_start->n_subid, o_start->prefix, o_start->include); - for (int i = 0; i < o_start->n_subid; i++) - log(L_INFO "n_subid %u: %u", i, o_start->ids[i]); - log(L_INFO "preparing include /sive> return %d %d %d", - !o_curr,(int) o_start->include, trie_match_net(p->bgp_trie, net)); - if (!o_curr && o_start->include && trie_match_net(p->bgp_trie, net)) - return o_start; - - log(L_INFO "doesn't returned"); - - if (o_curr) - net_fill_ip4(net, dest, IP4_MAX_PREFIX_LENGTH); - - struct f_trie_walk_state *ws = mb_allocz(p->p.pool, - sizeof(struct f_trie_walk_state)); - - struct oid *o = mb_allocz(p->p.pool, sizeof(struct oid) + 8 * sizeof(u32)); - o->n_subid = 9; - trie_walk_init(ws, p->bgp_trie, NULL); - if (trie_walk_next(ws, net) && ip4_less(net4_prefix(net), dest)) - { - memcpy(o, o_start, oid_size(o_start)); - oid_ip4_index(o, net4_prefix(net)); - return o; - } - else + if (!o_start) return NULL; + + if (!o_curr) + { + o_curr = mb_alloc(p->p.pool, snmp_oid_size(o_start)); + memcpy(o_curr, o_start, snmp_oid_size(o_start)); + // XXX is it right time to free o_start right now (here) ? + } + + if (o_curr->n_subid > 1 && + o_curr->ids[0] == 1) + { + switch (o_curr->ids[1]) + { + case SNMP_BGP4_MIB: + return search_bgp_mib(p, o_curr, o_end, 0); + + default: + return NULL; + } + } + + return NULL; } static byte * @@ -1027,7 +1068,7 @@ find_bgp_one(struct bgp_proto *bp, struct oid *o, byte *pkt, uint size UNUSED, u BGP_DATA(vb, AGENTX_INTEGER, pkt); break; - case SNMP_BGP_VERSION: + case SNMP_BGP_NEGOTIATED_VERSION: if (b_state == BS_OPENCONFIRM || b_state == BS_ESTABLISHED) STORE_PTR(pkt, 4); else @@ -1090,7 +1131,7 @@ find_bgp_one(struct bgp_proto *bp, struct oid *o, byte *pkt, uint size UNUSED, u pkt += 4; /* force network order */ put_u32(pkt, - (bp->last_error_code << 8 | bp->last_error_code << 48) & 0xFFFF0000); + (bp->last_error_code << 8 | bp->last_error_code << 24) & 0xFFFF0000); /* real size is 8 but we already shifted the pkt by 4 */ BGP_DATA(vb, AGENTX_OCTET_STRING, pkt); break; @@ -1158,7 +1199,7 @@ snmp_bgp_record(struct snmp_proto *p, struct oid *o, byte *buf, uint size, uint switch (o->ids[2]) { - case BGP4_MIB_VERSION: + case SNMP_BGP_VERSION: STORE_PTR(pkt, 1); // string len pkt += 4; STORE_PTR(pkt, BGP4_VERSIONS); @@ -1166,15 +1207,15 @@ snmp_bgp_record(struct snmp_proto *p, struct oid *o, byte *buf, uint size, uint BGP_DATA(vb, AGENTX_OCTET_STRING, pkt); break; - case BGP4_MIB_LOCAL_AS: + case SNMP_BGP_LOCAL_AS: // XXX local as to use STORE_PTR(pkt, p->local_as); BGP_DATA(vb, AGENTX_INTEGER, pkt); break; - case BGP4_PEER_TABLE: + case SNMP_BGP_PEER_TABLE: /* end part of .1.3.6.1.2.1.15.3.1.x.a.b.c.d */ - if (o->n_subid < 9 || o->ids[3] != BGP4_PEER_ENTRY + if (o->n_subid < 9 || o->ids[3] != SNMP_BGP_PEER_ENTRY || o->ids[4] == 0 || o->ids[4] > 24) return no_such_object(pkt, vb); @@ -1233,7 +1274,7 @@ no_such_object(byte *buf, struct agentx_varbind *vb) return buf; } -static byte * UNUSED +static UNUSED byte * no_such_instance(byte *buf, struct agentx_varbind *vb) { vb->type = AGENTX_NO_SUCH_INSTANCE; @@ -1245,7 +1286,7 @@ find_prefixed(struct snmp_proto *p, struct oid *o, byte *buf, uint size, uint co { struct agentx_varbind *vb = (void *) buf; - memcpy(&vb->name, o, oid_size(o)); + memcpy(&vb->name, o, snmp_oid_size(o)); /* SNMPv2 mgmt mib-2 */ if (o->n_subid < 2 || (o->prefix != 2 && o->ids[0] != 1)) @@ -1254,6 +1295,7 @@ find_prefixed(struct snmp_proto *p, struct oid *o, byte *buf, uint size, uint co switch (o->ids[1]) { case SNMP_BGP4_MIB: + log(L_INFO "find_prefixed() BGP4"); return snmp_bgp_record(p, o, buf, size, contid); case SNMP_OSPFv3_MIB: @@ -1265,28 +1307,42 @@ find_prefixed(struct snmp_proto *p, struct oid *o, byte *buf, uint size, uint co } } +/** + * prefixize - return prefixed oid copy if possible + * @proto: allocation pool holder + * @oid: from packet loaded object identifier + * @byte_ord: byte order of @oid + * + * Returns prefixed (meaning with nonzero prefix field) oid copy of @oid if + * possible. NULL otherwise. Returned pointer is always allocated from @proto's + * pool not a pointer to recieve buffer (from which is most likely @oid). + */ static struct oid * -prefixize(struct snmp_proto *p, struct oid *o, int byte_ord) +prefixize(struct snmp_proto *proto, struct oid *oid, int byte_ord) { const u32 prefix[] = {1, 3, 6, 1}; - if (o->n_subid < 5) + if (oid->n_subid < 5) return NULL; for (int i = 0; i < 4; i++) - if (LOAD(o->ids[i], byte_ord) != prefix[i]) + if (LOAD(oid->ids[i], byte_ord) != prefix[i]) return NULL; - struct oid *new = mb_alloc(p->p.pool, sizeof(struct oid) + MAX((o->n_subid - 5) * 4, 0)); + if (oid->ids[4] >= 256) + return NULL; - memcpy(new, o, sizeof(struct oid)); - new->n_subid = o->n_subid - 5; + struct oid *new = mb_alloc(proto->p.pool, + sizeof(struct oid) + MAX((oid->n_subid - 5) * sizeof(u32), 0)); - if (o->ids[4] < 256) - new->prefix = o->ids[4]; - else return NULL; + memcpy(new, oid, sizeof(struct oid)); + new->n_subid = oid->n_subid - 5; - memcpy(&new->ids, &o->ids[5], new->n_subid * 4); + /* validity check before allocation => ids[4] < 256 + and can be copied to one byte new->prefix */ + new->prefix = oid->ids[4]; + + memcpy(&new->ids, &oid->ids[5], new->n_subid * sizeof(u32)); return new; } @@ -1302,6 +1358,31 @@ find_n_fill(struct snmp_proto *p, struct oid *o, byte *buf, uint size, uint cont return NULL; } +/** + * snmp_mib_fill - + */ +static byte * +snmp_mib_fill(struct snmp_proto *p, struct oid *oid, u8 mib_class, byte *buf, +uint size, uint contid, int byte_ord) +{ + struct agentx_varbind *vb = (void *) buf; + + memcpy(&vb->name, oid, snmp_oid_size(oid)); + + /* SNMPv2 mgmt mib-2 */ + if (oid->n_subid < 2 || (oid->prefix != 2 && oid->ids[0] != 1)) + return no_such_object(buf + vb_size(vb), vb); + + switch (mib_class) + { + case SNMP_CLASS_BGP: + buf = snmp_bgp_fill(p, oid, buf, size, contid, byte_ord); + break; + } + + return buf; +} + static byte * prepare_response(struct snmp_proto *p, byte *buf, uint size) { diff --git a/proto/snmp/subagent.h b/proto/snmp/subagent.h index 16da447b..808c5cfb 100644 --- a/proto/snmp/subagent.h +++ b/proto/snmp/subagent.h @@ -10,10 +10,22 @@ void snmp_ping(struct snmp_proto *p); #define AGENTX_VERSION 1 -#define SNMP_OSPF_MIB 14 /* part of oid .1.3.6.1.2.1.14 */ -#define SNMP_BGP4_MIB 15 /* part of oid .1.3.6.1.2.1.15 */ +#define SNMP_STATE_START 0 +#define SNMP_STATE_BGP 1 +#define SNMP_STATE_INVALID 2 + +#define SNMP_MIB_2 1 /* last of oid .1.3.6.1.2.1 */ +#define SNMP_OSPF_MIB 14 /* part of oid .1.3.6.1.2.1.14 */ +#define SNMP_BGP4_MIB 15 /* part of oid .1.3.6.1.2.1.15 */ #define SNMP_OSPFv3_MIB 192 /* part of oid .1.3.6.1.2.1.192 */ +enum SNMP_CLASSES { + SNMP_CLASS_INVALID = 0, + SNMP_CLASS_BGP = 1, + SNMP_CLASS_OSPF, + SNMP_CLASS_END, +}; + #define BGP4_VERSIONS 0x10 enum agentx_type { @@ -243,4 +255,11 @@ enum agentx_response_err { } PACKED; int snmp_rx(sock *sk, uint size); +int snmp_valid_ip4_index_safe(struct oid *o, uint start); +int snmp_valid_ip4_index(struct oid *o, uint start); +void snmp_oid_ip4_index(struct oid *o, ip4_addr addr); + +uint snmp_oid_size(struct oid *o); + +static byte *snmp_mib_fill(struct snmp_proto *p, struct oid *oid, u8 mib_class, byte *buf, uint size, uint contid, int byte_ord); #endif