diff --git a/proto/snmp/Doc b/proto/snmp/Doc index 74738427..7d2ec70b 100644 --- a/proto/snmp/Doc +++ b/proto/snmp/Doc @@ -1 +1,4 @@ S snmp.c +S subagent.c +S snmp_utils.c +S bgp_mib.c diff --git a/proto/snmp/Makefile b/proto/snmp/Makefile index 24451dfc..637815af 100644 --- a/proto/snmp/Makefile +++ b/proto/snmp/Makefile @@ -1,4 +1,4 @@ -src := snmp.c snmp_utils.c subagent.c bgp_mib.c +src := snmp.c snmp_utils.c subagent.c bgp_mib.c mib_tree.c obj := $(src-o-files) $(all-daemon) $(cf-local) diff --git a/proto/snmp/bgp_mib.c b/proto/snmp/bgp_mib.c index d97a0de4..7559042a 100644 --- a/proto/snmp/bgp_mib.c +++ b/proto/snmp/bgp_mib.c @@ -25,6 +25,10 @@ /* hash table only store ip4 addresses */ #define SNMP_HASH_LESS(ip1, ip2) SNMP_HASH_LESS4(ip1,ip2) + +/* Simply discard type */ +#define SNMP_MANAGE_TBUF(p, vb, c) snmp_manage_tbuf(p, (void **) vb, c) + static inline void ip4_to_oid(struct oid *oid, ip4_addr addr); @@ -192,11 +196,15 @@ snmp_bgp_state(const struct oid *oid) void snmp_bgp_reg_ok(struct snmp_proto *p, struct agentx_response *r, struct oid *oid) { + (void)p; + (void)r; + (void)oid; + /* TODO: EXPENSIVE_CHECK() that const struct oid *in_buf = ((void *) r) + sizeof(r); struct oid *dup = snmp_prefixize(p, in_buf); - - ASSUME(snmp_bgp_state(oid) == snmp_bgp_state(dup)); + ASSUME(snmp_bgp_state(oid) == snmp_bgp_state(dup)); mb_free(dup); + */ } void @@ -223,10 +231,10 @@ snmp_bgp_notify_common(struct snmp_proto *p, uint type, ip4_addr ip4, char last_ u32 trap_ids[] = { 1, 0, type }; STATIC_ASSERT(ARRAY_SIZE(trap_ids) == 3); - /* additional size for trap identification, here either + /* additional size for trap identification, here either * bgpEstablishedNotification or bgpBackwardTransNotification (see below) */ - void *data = tmp_alloc(snmp_oid_size_from_len(ARRAY_SIZE(trap_ids)) + sz); - struct oid *head = data; + void *data = tmp_alloc(snmp_oid_size_from_len(ARRAY_SIZE(trap_ids)) + sz); + struct oid *head = data; { /* trap id BGP4-MIB::bgpEstablishedNotification (.1.3.6.1.2.15.0.1) * or BGP4-MIB::bgpBackwardTransNotification (.1.3.6.1.2.15.0.2) */ @@ -272,8 +280,8 @@ snmp_bgp_notify_common(struct snmp_proto *p, uint type, ip4_addr ip4, char last_ ip4_to_oid(addr, ip4); } /* We have enough space inside the TX-buffer prepared */ - #define UNLIMITED 100 - snmp_varbind_ip4(addr_vb, UNLIMITED, ip4); + struct snmp_pdu sink = { 0 }; + snmp_varbind_ip4(addr_vb, &sink, ip4); { /* BGP4-MIB::bgpPeerLastError */ struct oid *error = &error_vb->name; @@ -286,7 +294,7 @@ snmp_bgp_notify_common(struct snmp_proto *p, uint type, ip4_addr ip4, char last_ error->ids[ENTRY_TYPE] = BGP4_MIB_LAST_ERROR; ip4_to_oid(error, ip4); } - snmp_varbind_nstr(error_vb, UNLIMITED, last_error, 2); + snmp_varbind_nstr(error_vb, &sink, last_error, 2); { /* BGP4-MIB::bgpPeerState */ struct oid *state = &state_vb->name; @@ -299,13 +307,12 @@ snmp_bgp_notify_common(struct snmp_proto *p, uint type, ip4_addr ip4, char last_ state->ids[ENTRY_TYPE] = BGP4_MIB_STATE; ip4_to_oid(state, ip4); } - snmp_varbind_int(state_vb, UNLIMITED, state_val); + snmp_varbind_int(state_vb, &sink, state_val); /* We do not send the systemUpTime.0 */ snmp_notify_pdu(p, head, data, sz, 0); #undef OID_N_SUBID - #undef UNLIMITED } /* @@ -329,7 +336,7 @@ snmp_bgp_fsm_state(const struct bgp_proto *bgp_proto) return MIN(bgp_in->state, bgp_out->state) + 1; if (MIN(bgp_in->state, bgp_out->state) == BS_CLOSE) return BS_IDLE; - + return MAX(bgp_in->state, bgp_out->state) + 1; } @@ -580,11 +587,13 @@ oid_state_compare(const struct oid *oid, u8 state) } static struct oid * -update_bgp_oid(struct oid *oid, u8 state) +update_bgp_vb(struct snmp_proto *p, struct agentx_varbind **vb, u8 state, struct snmp_pdu *c) { + struct oid *oid = &(*vb)->name; + if (state == BGP4_MIB_S_END || state == BGP4_MIB_S_INVALID || state == BGP4_MIB_S_NO_VALUE) - return oid; + return &(*vb)->name; /* No need to reallocate anything if the OID has same lin. state */ if (snmp_bgp_state(oid) == state) @@ -607,41 +616,39 @@ update_bgp_oid(struct oid *oid, u8 state) case BGP4_MIB_S_BGP: /* This could potentially destroy same old data */ if (oid->n_subid != 2) - oid = mb_realloc(oid, snmp_oid_size_from_len(2)); + oid = snmp_varbind_set_name_len(p, vb, 2, c); - oid->n_subid = 2; oid->ids[0] = SNMP_MIB_2; oid->ids[1] = BGP4_MIB; break; case BGP4_MIB_S_VERSION: if (oid->n_subid != 3) - oid = mb_realloc(oid, snmp_oid_size_from_len(3)); + oid = snmp_varbind_set_name_len(p, vb, 3, c); - oid->n_subid = 3; oid->ids[2] = BGP4_MIB_VERSION; break; case BGP4_MIB_S_LOCAL_AS: if (oid->n_subid != 3) - oid = mb_realloc(oid, snmp_oid_size_from_len(3)); + oid =snmp_varbind_set_name_len(p, vb, 3, c); - oid->n_subid = 3; oid->ids[2] = BGP4_MIB_LOCAL_AS; break; case BGP4_MIB_S_PEER_IDENTIFIER: if (oid->n_subid != 9) { - oid = mb_realloc(oid, snmp_oid_size_from_len(9)); + u8 n_subid = LOAD_U8(oid->n_subid); + oid = snmp_varbind_set_name_len(p, vb, 9, c); - if (oid->n_subid < 6) + if (n_subid < 6) oid->ids[5] = 0; - if (oid->n_subid < 7) + if (n_subid < 7) oid->ids[6] = 0; - if (oid->n_subid < 8) + if (n_subid < 8) oid->ids[7] = 0; - if (oid->n_subid < 9) + if (n_subid < 9) oid->ids[8] = 0; } @@ -649,24 +656,25 @@ update_bgp_oid(struct oid *oid, u8 state) oid->ids[3] = BGP4_MIB_PEER_ENTRY; oid->ids[4] = BGP4_MIB_PEER_IDENTIFIER; - oid->n_subid = 9; break; #define SNMP_UPDATE_CASE(num, update) \ case num: \ if (oid->n_subid != 9) \ { \ - oid = mb_realloc(oid, snmp_oid_size_from_len(9)); \ + u8 n_subid = LOAD_U8(oid->n_subid); \ + oid = snmp_varbind_set_name_len(p, vb, 9, c); \ \ - if (oid->n_subid < 6) \ + if (n_subid < 6) \ oid->ids[5] = 0; \ - if (oid->n_subid < 7) \ + if (n_subid < 7) \ oid->ids[6] = 0; \ - if (oid->n_subid < 8) \ + if (n_subid < 8) \ oid->ids[7] = 0; \ - if (oid->n_subid < 9) \ + if (n_subid < 9) \ oid->ids[8] = 0; \ } \ + \ oid->n_subid = 9; \ oid->ids[4] = update; \ break; @@ -719,7 +727,7 @@ update_bgp_oid(struct oid *oid, u8 state) case BGP4_MIB_S_IDENTIFIER: if (oid->n_subid != 3) - oid = mb_realloc(oid, snmp_oid_size_from_len(3)); + oid = snmp_varbind_set_name_len(p, vb, 3, c); oid->n_subid = 3; oid->ids[2] = 4; @@ -734,6 +742,7 @@ update_bgp_oid(struct oid *oid, u8 state) #undef SNMP_UPDATE_CASE } + /** * snmp_bgp_find_next_oid - walk bgp peer addresses and update @o_start oid * @@ -775,8 +784,8 @@ snmp_bgp_find_next_oid(struct snmp_proto *p, struct oid *oid, uint UNUSED contid 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) +static enum snmp_search_res UNUSED +snmp_bgp_search_dynamic(struct snmp_proto *p, struct oid **searched, const struct oid *o_end, uint UNUSED contid, u8 next_state, struct snmp_pdu *c) { struct oid *oid = *searched; u8 end_state = MIN(snmp_bgp_state(o_end), BGP4_MIB_S_PEER_TABLE_END); @@ -784,7 +793,12 @@ snmp_bgp_search_dynamic(struct snmp_proto *p, struct oid **searched, const struc ASSUME(end_state <= BGP4_MIB_S_END); ASSUME(oid != NULL); - oid = update_bgp_oid(oid, next_state); + // TODO TODO remove me + struct agentx_varbind data = { 0 }; + struct agentx_varbind *vb = &data; + + oid = update_bgp_vb(p, &vb, next_state, c); + //oid = update_bgp_oid(oid, next_state); int found; while (!(found = snmp_bgp_find_next_oid(p, oid, contid)) && next_state <= end_state) @@ -792,7 +806,8 @@ snmp_bgp_search_dynamic(struct snmp_proto *p, struct oid **searched, const struc next_state = snmp_bgp_next_state(next_state); if (next_state == BGP4_MIB_S_IDENTIFIER) break; - oid = update_bgp_oid(oid, next_state); + //oid = update_bgp_oid(oid, next_state); + oid = update_bgp_vb(p, &vb, next_state, c); /* In case of search for next bgp state, we want to start from beginning. */ oid->ids[5] = oid->ids[6] = oid->ids[7] = oid->ids[8] = 0; } @@ -807,8 +822,14 @@ snmp_bgp_search_dynamic(struct snmp_proto *p, struct oid **searched, const struc } enum snmp_search_res -snmp_bgp_search(struct snmp_proto *p, struct oid **searched, const struct oid *o_end, uint contid) +snmp_bgp_search(struct snmp_proto *p, struct agentx_varbind **vb_search, const struct oid *o_end, struct snmp_pdu *c) { + (void)p; + (void)vb_search; + (void)o_end; + (void)c; + return SNMP_SEARCH_END_OF_VIEW; +#if 0 enum snmp_search_res r = SNMP_SEARCH_END_OF_VIEW; u8 bgp_state = snmp_bgp_state(*searched); u8 state; @@ -858,41 +879,32 @@ snmp_bgp_search(struct snmp_proto *p, struct oid **searched, const struct oid *o /* end not found */ return SNMP_SEARCH_END_OF_VIEW; +#endif } -static byte * -bgp_fill_dynamic(struct snmp_proto UNUSED *p, struct agentx_varbind *vb, - struct snmp_pdu *c, u8 state) +static void +bgp_fill_dynamic(struct snmp_proto *p, struct agentx_varbind **vb, struct snmp_pdu *c, u8 state) { - struct oid *oid = &vb->name; - uint size = c->size - snmp_varbind_header_size(vb); - byte *pkt; + struct oid *oid = &(*vb)->name; + //byte *pkt; ip4_addr addr; if (oid_state_compare(oid, state) == 0 && snmp_bgp_valid_ip4(oid)) addr = ip4_from_oid(oid); else - { - vb->type = AGENTX_NO_SUCH_INSTANCE; - pkt = ((byte *) vb) + snmp_varbind_header_size(vb); - return pkt; - } + return snmp_set_varbind_type(*vb, AGENTX_NO_SUCH_INSTANCE); struct snmp_bgp_peer *pe = snmp_hash_find(p, addr); if (!pe) - { - vb->type = AGENTX_NO_SUCH_INSTANCE; - return ((byte *) vb) + snmp_varbind_header_size(vb); - } + return snmp_set_varbind_type(*vb, AGENTX_NO_SUCH_INSTANCE); const struct bgp_proto *bgp_proto = pe->bgp_proto; if (!ipa_is_ip4(bgp_proto->remote_ip)) { log(L_ERR, "%s: Found BGP protocol instance with IPv6 address", bgp_proto->p.name); - vb->type = AGENTX_NO_SUCH_INSTANCE; c->error = AGENTX_RES_GEN_ERROR; - return ((byte *) vb) + snmp_varbind_header_size(vb); + return snmp_set_varbind_type(*vb, AGENTX_NO_SUCH_INSTANCE); } ip4_addr proto_ip = ipa_to_ip4(bgp_proto->remote_ip); @@ -901,9 +913,8 @@ bgp_fill_dynamic(struct snmp_proto UNUSED *p, struct agentx_varbind *vb, /* 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); - vb->type = AGENTX_NO_SUCH_INSTANCE; c->error = AGENTX_RES_GEN_ERROR; - return ((byte *) vb) + snmp_varbind_header_size(vb); + return snmp_set_varbind_type(*vb, AGENTX_NO_SUCH_INSTANCE); } const struct bgp_conn *bgp_conn = bgp_proto->conn; @@ -914,125 +925,202 @@ bgp_fill_dynamic(struct snmp_proto UNUSED *p, struct agentx_varbind *vb, char last_error[2]; snmp_bgp_last_error(bgp_proto, last_error); + + snmp_set_varbind_type(*vb, AGENTX_NO_SUCH_INSTANCE); switch (state) { case BGP4_MIB_S_PEER_IDENTIFIER: + if (c->size < AGENTX_TYPE_IP4_SIZE) + SNMP_MANAGE_TBUF(p, vb, c); + if (fsm_state == BGP4_MIB_OPENCONFIRM || fsm_state == BGP4_MIB_ESTABLISHED) - pkt = snmp_varbind_ip4(vb, size, ip4_from_u32(bgp_proto->remote_id)); + // TODO last + snmp_varbind_ip4(*vb, c, ip4_from_u32(bgp_proto->remote_id)); else - pkt = snmp_varbind_ip4(vb, size, IP4_NONE); + snmp_varbind_ip4(*vb, c, IP4_NONE); break; case BGP4_MIB_S_STATE: - pkt = snmp_varbind_int(vb, size, fsm_state); + if (c->size < AGENTX_TYPE_INT_SIZE) + SNMP_MANAGE_TBUF(p, vb, c); + + snmp_varbind_int(*vb, c, fsm_state); break; case BGP4_MIB_S_ADMIN_STATUS: + if (c->size < AGENTX_TYPE_INT_SIZE) + SNMP_MANAGE_TBUF(p, vb, c); + if (bgp_proto->p.disabled) - pkt = snmp_varbind_int(vb, size, AGENTX_ADMIN_STOP); + snmp_varbind_int(*vb, c, AGENTX_ADMIN_STOP); else - pkt = snmp_varbind_int(vb, size, AGENTX_ADMIN_START); + snmp_varbind_int(*vb, c, AGENTX_ADMIN_START); break; case BGP4_MIB_S_NEGOTIATED_VERSION: + if (c->size < AGENTX_TYPE_INT_SIZE) + SNMP_MANAGE_TBUF(p, vb, c); + if (fsm_state == BGP4_MIB_ESTABLISHED || fsm_state == BGP4_MIB_ESTABLISHED) - pkt = snmp_varbind_int(vb, size, BGP4_MIB_NEGOTIATED_VER_VALUE); + snmp_varbind_int(*vb, c, BGP4_MIB_NEGOTIATED_VER_VALUE); else - pkt = snmp_varbind_int(vb, size, BGP4_MIB_NEGOTIATED_VER_NO_VALUE); + snmp_varbind_int(*vb, c, BGP4_MIB_NEGOTIATED_VER_NO_VALUE); break; case BGP4_MIB_S_LOCAL_ADDR: - pkt = snmp_varbind_ip4(vb, size, ipa_to_ip4(bgp_proto->local_ip)); + 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)); break; case BGP4_MIB_S_LOCAL_PORT: - pkt = snmp_varbind_int(vb, size, bgp_conf->local_port); + if (c->size < AGENTX_TYPE_INT_SIZE) + SNMP_MANAGE_TBUF(p, vb, c); + + snmp_varbind_int(*vb, c, bgp_conf->local_port); break; case BGP4_MIB_S_REMOTE_ADDR: - pkt = snmp_varbind_ip4(vb, size, ipa_to_ip4(bgp_proto->remote_ip)); + 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)); break; case BGP4_MIB_S_REMOTE_PORT: - pkt = snmp_varbind_int(vb, size, bgp_conf->remote_port); + if (c->size < AGENTX_TYPE_INT_SIZE) + SNMP_MANAGE_TBUF(p, vb, c); + + snmp_varbind_int(*vb, c, bgp_conf->remote_port); break; case BGP4_MIB_S_REMOTE_AS: - pkt = snmp_varbind_int(vb, size, bgp_proto->remote_as); + if (c->size < AGENTX_TYPE_INT_SIZE) + SNMP_MANAGE_TBUF(p, vb, c); + + snmp_varbind_int(*vb, c, bgp_proto->remote_as); break; case BGP4_MIB_S_RX_UPDATES: /* bgpPeerInUpdates */ - pkt = snmp_varbind_counter32(vb, size, bgp_stats->rx_updates); + if (c->size < AGENTX_TYPE_COUNTER32_SIZE) + SNMP_MANAGE_TBUF(p, vb, c); + + snmp_varbind_counter32(*vb, c, bgp_stats->rx_updates); break; case BGP4_MIB_S_TX_UPDATES: /* bgpPeerOutUpdate */ - pkt = snmp_varbind_counter32(vb, size, bgp_stats->tx_updates); + if (c->size < AGENTX_TYPE_COUNTER32_SIZE) + SNMP_MANAGE_TBUF(p, vb, c); + + snmp_varbind_counter32(*vb, c, bgp_stats->tx_updates); break; case BGP4_MIB_S_RX_MESSAGES: /* bgpPeerInTotalMessages */ - pkt = snmp_varbind_counter32(vb, size, bgp_stats->rx_messages); + if (c->size < AGENTX_TYPE_COUNTER32_SIZE) + SNMP_MANAGE_TBUF(p, vb, c); + + snmp_varbind_counter32(*vb, c, bgp_stats->rx_messages); break; case BGP4_MIB_S_TX_MESSAGES: /* bgpPeerOutTotalMessages */ - pkt = snmp_varbind_counter32(vb, size, bgp_stats->tx_messages); + if (c->size < AGENTX_TYPE_COUNTER32_SIZE) + SNMP_MANAGE_TBUF(p, vb, c); + + snmp_varbind_counter32(*vb, c, bgp_stats->tx_messages); break; case BGP4_MIB_S_LAST_ERROR: - pkt = snmp_varbind_nstr(vb, size, last_error, 2); + if (c->size < snmp_str_size_from_len(2)) + SNMP_MANAGE_TBUF(p, vb, c); + + snmp_varbind_nstr(*vb, c, last_error, 2); break; case BGP4_MIB_S_FSM_TRANSITIONS: - pkt = snmp_varbind_counter32(vb, size, + if (c->size < AGENTX_TYPE_COUNTER32_SIZE) + SNMP_MANAGE_TBUF(p, vb, c); + + snmp_varbind_counter32(*vb, c, bgp_stats->fsm_established_transitions); break; case BGP4_MIB_S_FSM_ESTABLISHED_TIME: - pkt = snmp_varbind_gauge32(vb, size, + if (c->size < AGENTX_TYPE_COUNTER32_SIZE) + SNMP_MANAGE_TBUF(p, vb, c); + + + snmp_varbind_gauge32(*vb, c, (current_time() - bgp_proto->last_established) TO_S); break; case BGP4_MIB_S_RETRY_INTERVAL: /* retry inverval value should be != 0 */ - pkt = snmp_varbind_int(vb, size, bgp_conf->connect_retry_time); + if (c->size < AGENTX_TYPE_INT_SIZE) + SNMP_MANAGE_TBUF(p, vb, c); + + snmp_varbind_int(*vb, c, bgp_conf->connect_retry_time); break; case BGP4_MIB_S_HOLD_TIME: /* hold time should be == 0 or in 3..65535 */ - pkt = snmp_varbind_int(vb, size, (bgp_conn) ? bgp_conn->hold_time : 0); + if (c->size < AGENTX_TYPE_INT_SIZE) + SNMP_MANAGE_TBUF(p, vb, c); + + snmp_varbind_int(*vb, c, (bgp_conn) ? bgp_conn->hold_time : 0); break; case BGP4_MIB_S_KEEPALIVE: + if (c->size < AGENTX_TYPE_INT_SIZE) + SNMP_MANAGE_TBUF(p, vb, c); + if (!bgp_conf->hold_time) - pkt = snmp_varbind_int(vb, size, 0); + snmp_varbind_int(*vb, c, 0); else - pkt = snmp_varbind_int(vb, size, + snmp_varbind_int(*vb, c, (bgp_conn) ? bgp_conn->keepalive_time : 0); break; case BGP4_MIB_S_HOLD_TIME_CONFIGURED: - pkt = snmp_varbind_int(vb, size, bgp_conf->hold_time); + if (c->size < AGENTX_TYPE_INT_SIZE) + SNMP_MANAGE_TBUF(p, vb, c); + + snmp_varbind_int(*vb, c, bgp_conf->hold_time); break; case BGP4_MIB_S_KEEPALIVE_CONFIGURED: + if (c->size < AGENTX_TYPE_INT_SIZE) + SNMP_MANAGE_TBUF(p, vb, c); + + if (!bgp_conf->keepalive_time) - pkt = snmp_varbind_int(vb, size, 0); + snmp_varbind_int(*vb, c, 0); else - pkt = snmp_varbind_int(vb, size, + snmp_varbind_int(*vb, c, (bgp_conn) ? bgp_conn->keepalive_time : 0); break; case BGP4_MIB_S_ORIGINATION_INTERVAL: /* value should be in 1..65535 but is not supported by bird */ - pkt = snmp_varbind_int(vb, size, 0); + if (c->size < AGENTX_TYPE_INT_SIZE) + SNMP_MANAGE_TBUF(p, vb, c); + + snmp_varbind_int(*vb, c, 0); break; case BGP4_MIB_S_MIN_ROUTE_ADVERTISEMENT: /* value should be in 1..65535 but is not supported by bird */ - pkt = snmp_varbind_int(vb, size, 0); + if (c->size < AGENTX_TYPE_INT_SIZE) + SNMP_MANAGE_TBUF(p, vb, c); + + snmp_varbind_int(*vb, c, 0); break; case BGP4_MIB_S_IN_UPDATE_ELAPSED_TIME: - pkt = snmp_varbind_gauge32(vb, size, + if (c->size < AGENTX_TYPE_INT_SIZE) + SNMP_MANAGE_TBUF(p, vb, c); + + snmp_varbind_gauge32(*vb, c, (current_time() - bgp_proto->last_rx_update) TO_S ); break; @@ -1052,86 +1140,75 @@ bgp_fill_dynamic(struct snmp_proto UNUSED *p, struct agentx_varbind *vb, case BGP4_MIB_S_NO_VALUE: 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, u8 state) +void +bgp_fill_static(struct snmp_proto *p, struct agentx_varbind **vb, struct snmp_pdu *c, u8 state) { - ASSUME((void *) pkt == (void *) vb); + ASSUME(c->buffer == snmp_varbind_data(*vb)); - struct oid *oid = &vb->name; + struct oid *oid = &(*vb)->name; /* * 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_state_compare(oid, state) < 0 || state == BGP4_MIB_S_END) - { - vb->type = AGENTX_NO_SUCH_OBJECT; - return pkt + snmp_varbind_header_size(vb); - } + snmp_set_varbind_type(*vb, AGENTX_NO_SUCH_OBJECT); else if (oid_state_compare(oid, state) > 0) - { - vb->type = AGENTX_NO_SUCH_INSTANCE; - return pkt + snmp_varbind_header_size(vb); - } + snmp_set_varbind_type(*vb, AGENTX_NO_SUCH_INSTANCE); switch (state) { - case BGP4_MIB_S_VERSION: - pkt = snmp_varbind_nstr(vb, size, BGP4_VERSIONS, 1); + case BGP4_MIB_S_VERSION:; + uint sz = snmp_str_size_from_len(1); + if (c->size < sz) + SNMP_MANAGE_TBUF(p, vb, c); + + c->size -= sz; + snmp_varbind_nstr(*vb, c, BGP4_VERSIONS, 1); break; case BGP4_MIB_S_LOCAL_AS: - pkt = snmp_varbind_int(vb, size, p->bgp_local_as); + if (c->size < AGENTX_TYPE_INT_SIZE) + SNMP_MANAGE_TBUF(p, vb, c); + + snmp_varbind_int(*vb, c, p->bgp_local_as); break; case BGP4_MIB_S_IDENTIFIER: - pkt = snmp_varbind_ip4(vb, size, p->bgp_local_id); + if (c->size < AGENTX_TYPE_IP4_SIZE) + SNMP_MANAGE_TBUF(p, vb, c); + + snmp_varbind_ip4(*vb, c, p->bgp_local_id); break; default: - vb->type = AGENTX_NO_SUCH_OBJECT; - pkt += snmp_varbind_header_size(vb); + snmp_set_varbind_type(*vb, AGENTX_NO_SUCH_OBJECT); break; } - - return pkt; } void -snmp_bgp_fill(struct snmp_proto *p, struct agentx_varbind *vb, - struct snmp_pdu *c) +snmp_bgp_fill(struct snmp_proto *p, struct agentx_varbind **vb, struct snmp_pdu *c) { - u8 state = snmp_bgp_state(&vb->name); + ASSERT(vb != NULL); + u8 state = snmp_bgp_state(&((*vb)->name)); - byte *pkt; if (is_static(state)) { - pkt = bgp_fill_static(p, vb, c->buffer, c->size, 0, state); - ADVANCE(c->buffer, c->size, pkt - c->buffer); + bgp_fill_static(p, vb, c, state); return; } if (is_dynamic(state)) { - pkt = bgp_fill_dynamic(p, vb, c, state); - ADVANCE(c->buffer, c->size, pkt - c->buffer); + bgp_fill_dynamic(p, vb, c, state); return; } - vb->type = AGENTX_NO_SUCH_OBJECT; - ADVANCE(c->buffer, c->size, snmp_varbind_header_size(vb)); + snmp_set_varbind_type(*vb, AGENTX_NO_SUCH_OBJECT); } /* diff --git a/proto/snmp/bgp_mib.h b/proto/snmp/bgp_mib.h index c044c0ef..e2a6ea84 100644 --- a/proto/snmp/bgp_mib.h +++ b/proto/snmp/bgp_mib.h @@ -48,8 +48,9 @@ void snmp_bgp_reg_failed(struct snmp_proto *p, struct agentx_response *r, struct u8 snmp_bgp_get_valid(u8 state); u8 snmp_bgp_getnext_valid(u8 state); -enum snmp_search_res snmp_bgp_search(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); +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); //int snmp_bgp_testset(struct snmp_proto *p, const struct agentx_varbind *vb, void* tr, struct oid *oid, uint pkt_type); void snmp_bgp_notify_established(struct snmp_proto *p, struct bgp_proto *bgp); diff --git a/proto/snmp/mib_tree.c b/proto/snmp/mib_tree.c new file mode 100644 index 00000000..78a3e9cb --- /dev/null +++ b/proto/snmp/mib_tree.c @@ -0,0 +1,909 @@ +#include "mib_tree.h" +#include "snmp_utils.h" + +/* TODO does the code handle leafs correctly ?! */ + +#ifdef allocz +#undef allocz +#endif + +#define alloc(size) mb_alloc(p, size) +#define allocz(size) mb_allocz(p, size) +#define free(ptr) mb_free(ptr) +#define realloc(ptr, newsize) mib_mb_realloc(p, ptr, newsize) + +/* + * mib_mb_realloc - fix mb_realloc for NULL + * @p: pool to use for NULL pointers + * @ptr: old pointer to be reallocated + * @size: new size of allocated memory block + * + * The mb_realloc() does not work with NULL as ptr. + */ +static inline void * +mib_mb_realloc(pool *p, void *ptr, unsigned size) +{ + if (!ptr) + return mb_alloc(p, size); + + return mb_realloc(ptr, size); +} + +void +mib_tree_init(pool *p, struct mib_tree *t) +{ + struct mib_node *node = &t->root; + node->c.id = 0; + node->c.flags = 0; + node->children = NULL; + node->child_len = 0; + + struct oid *oid = tmp_alloc( + snmp_oid_size_from_len((uint) ARRAY_SIZE(snmp_internet))); + STORE_U8(oid->n_subid, ARRAY_SIZE(snmp_internet)); + STORE_U8(oid->prefix, 0); + STORE_U8(oid->include, 0); + STORE_U8(oid->reserved, 0); + + for (size_t i = 0; i < ARRAY_SIZE(snmp_internet); i++) + STORE_U32(oid->ids[i], snmp_internet[i]); + + (void) mib_tree_add(p, t, oid, 0); + + /* WTF ?? + struct mib_walk_state walk = { }; + (void) mib_tree_find(t, &walk, oid); +*/ +} + + +// This function does not work with leaf nodes inside the snmp_internet prefix +// area +// Return NULL of failure, valid mib_node_u pointer otherwise +mib_node_u * +mib_tree_add(pool *p, struct mib_tree *t, const struct oid *oid, int is_leaf) +{ + //ASSERT(snmp_oid_is_prefixed(oid) || !snmp_oid_is_prefixable(oid)); + struct mib_walk_state walk; + mib_node_u *node; + + /* The empty prefix is associated with the root tree node */ + if (snmp_is_oid_empty(oid) && !is_leaf) + return (mib_node_u *) &t->root; + else if (snmp_is_oid_empty(oid)) + return NULL; + + mib_tree_walk_init(&walk); + node = mib_tree_find(t, &walk, oid); + ASSERT(walk.id_pos <= LOAD_U8(oid->n_subid) + 1); + + if (node) + { + if (mib_node_is_leaf(node) == is_leaf) + return node; + + /* we are trying to insert a leaf node in place of inner node, + * or vice versa */ + return NULL; + } + + ASSERT(walk.id_pos < LOAD_U8(oid->n_subid) + 1); + + node = walk.stack[walk.stack_pos - 1]; + /* we encounter leaf node before end of OID's id path */ + if (mib_node_is_leaf(node)) + return NULL; + + struct mib_node *node_inner = &node->inner; + if (snmp_oid_is_prefixed(oid) && + walk.stack_pos <= ARRAY_SIZE(snmp_internet) + 1) + { + ASSUME(walk.stack_pos && walk.stack[0] == (mib_node_u *) &t->root); + + for (u32 id = walk.stack_pos - 1; id < ARRAY_SIZE(snmp_internet); id++) + { + if (snmp_internet[id] >= node_inner->child_len) + { + u32 old_len = node_inner->child_len; + node_inner->child_len = snmp_internet[id] + 1; + node_inner->children = realloc(node_inner->children, + node_inner->child_len * sizeof(mib_node_u *)); + + for (u32 i = old_len; i < node_inner->child_len; i++) + node_inner->children[i] = NULL; + } + + node = allocz(sizeof(struct mib_node)); + /* assign child into a parent's children array */ + node_inner->children[snmp_internet[id]] = node; + node_inner = &node->inner; + node_inner->c.id = snmp_internet[id]; + /* node_inner's fields c.flags, child_len, children defaults to zero or + * NULL respectively */ + walk.stack[walk.stack_pos++] = node; + } + + if (walk.stack_pos == ARRAY_SIZE(snmp_internet) + 1) + { + u32 old_len = node_inner->child_len; + node_inner->child_len = MAX(old_len, (u32) LOAD_U8(oid->prefix) + 1); + node_inner->children = realloc(node_inner->children, + node_inner->child_len * sizeof(mib_node_u *)); + + for (u32 i = old_len; i < node_inner->child_len; i++) + node_inner->children[i] = NULL; + + if (is_leaf && !LOAD_U8(oid->n_subid)) + { + node = allocz(sizeof(struct mib_leaf)); + node->empty.flags = MIB_TREE_LEAF; + } + else + { + node = allocz(sizeof(struct mib_node)); + node->empty.flags = 0; + } + + node->empty.id = LOAD_U8(oid->prefix); + /* add node into the parent's children array */ + node_inner->children[LOAD_U8(oid->prefix)] = node; + node_inner = &node->inner; + walk.stack[walk.stack_pos++] = node; + } + } + + /* snmp_internet + 2 = empty + snmp_internet + prefix */ + if (snmp_oid_is_prefixed(oid) && + walk.stack_pos == ARRAY_SIZE(snmp_internet) + 2 && + LOAD_U8(oid->n_subid) == 0 && + mib_node_is_leaf(node) == is_leaf) + return node; + + if (mib_node_is_leaf(node)) + return node; + + u8 subids = LOAD_U8(oid->n_subid); + u32 old_len = node_inner->child_len; + u32 child_id = oid->ids[walk.id_pos]; + node_inner->child_len = MAX(old_len, LOAD_U32(child_id) + 1); + node_inner->children = realloc(node_inner->children, + node_inner->child_len * sizeof(mib_node_u *)); + + for (u32 i = old_len; i < node_inner->child_len; i++) + node_inner->children[i] = NULL; + + struct mib_node *parent; + /* break to loop before last node in the oid */ + for (; walk.id_pos < subids - 1;) + { + parent = node_inner; + node_inner = allocz(sizeof(struct mib_node)); + + parent->children[child_id] = (mib_node_u *) node_inner; + node_inner->c.id = child_id; + + child_id = LOAD_U32(oid->ids[++walk.id_pos]); + + node_inner->child_len = child_id + 1; + node_inner->children = allocz(node_inner->child_len * sizeof(mib_node_u *)); + /* + node_inner->child_len = (child_id == 0) ? 0 : child_id; + node_inner->children = (child_id == 0) ? NULL + : allocz(node_inner->child_len * sizeof(mib_node_u *)); + */ + } + + parent = node_inner; + mib_node_u *last; + if (is_leaf) + { + last = allocz(sizeof(struct mib_leaf)); + struct mib_leaf *leaf = &last->leaf; + + parent->children[child_id] = (mib_node_u *) leaf; + leaf->c.id = child_id; + + //leaf->c.id = LOAD_U32(oid->ids[subids - 1]); + leaf->c.flags = MIB_TREE_LEAF; + } + else + { + last = allocz(sizeof(struct mib_node)); + node_inner = &last->inner; + + parent->children[child_id] = (mib_node_u *) node_inner; + node_inner->c.id = child_id; + //node_inner->c.id = LOAD_U32(oid->ids[subids - 1]); + /* fields c.flags, child_len and children are set by zeroed allocz() */ + } + + return last; +} + +#if 0 +// TODO merge functions mib_tree_add and mib_tree_insert into one with public iface + +mib_node_u * +mib_tree_add(struct snmp_proto *p, struct mib_tree *t, const struct oid *oid, uint size, int is_leaf) +{ + struct mib_walk_state walk = { }; + mib_node_u *known = mib_tree_find(t, &walk, oid); + + if (known) + return known; + + known = walk.stack[walk.stack_pos]; + + // redundant ??, if not, would be returned from find + if (walk.id_pos_abs == oid->n_subid) + return known; + + if (walk.id_pos_rel < 0) + + if (walk.id_pos_abs < oid->n_subid && (u32) walk.id_pos_rel == known->id_len) + { + if (known->child_len >= oid->ids[walk.id_pos_abs]) // abs +1? + { + u32 old_len = known->child_len; + known->child_len = oid->ids[walk.id_pos_abs] + 1; + known->children = mb_realloc(known->children, + known->child_len * sizeof(struct mib_node *)); + + for (uint i = old_len; i < known->child_len; i++) + known->children[i] = NULL; + } + + /* find would return it + if (known->children[oid->ids[]]) + return known->children[oid->ids[]]; + */ + + struct mib_node *node = mb_alloc(p->p.pool, sizeof(struct mib_node)); + node->id_len = oid->n_subid - walk.id_pos_abs; + node->ids = mb_alloc(p->p.pool, node->id_len * sizeof(u32)); + node->flags = 0; + node->children = NULL; + node->child_len = 0; + node->child_count = 0; + + known->child_count++; + known->children[oid->ids[0]] = node; + return node; + } + else if (walk.id_pos_abs < oid->n_subid) + { + /* We known that walk.id_pos_rel < known->id_len */ + struct mib_node *parent = mb_alloc(p->p.pool, sizeof(struct mib_node)); + parent->id_len = known->id_len - walk.id_pos_rel; + parent->ids = mb_alloc(p->p.pool, + parent->id_len * sizeof(struct mib_node *)); + memcpy(&parent->ids, &known->ids, parent->id_len * sizeof(struct mib_node *)); + u32 *ids = mb_alloc(p->p.pool, + (known->id_len - walk.id_pos_rel) * sizeof(u32)); + memcpy(ids, &known->ids[parent->id_len], + (known->id_len - parent->id_len) * sizeof(struct mib_node *)); + mb_free(known->ids); + known->id_len = known->id_len - walk.id_pos_rel; + known->ids = ids; + parent->child_len = MAX(known->ids[0], oid->ids[walk.id_pos_abs]) + 1; + parent->children = mb_allocz(p->p.pool, + parent->child_len * sizeof(struct mib_node *)); + parent->children[known->ids[0]] = known; + + struct mib_node *child = mb_alloc(p->p.pool, sizeof(struct mib_node)); + child->id_len = oid->n_subid - walk.id_pos_abs - parent->id_len; + child->ids = mb_alloc(p->p.pool, + child->id_len * sizeof(struct mib_node *)); + memcpy(&child->ids, &oid->ids[oid->n_subid - child->id_len], + child->id_len * sizeof(u32)); + // TODO test that we do not override the known + parent->children[child->ids[0]] = child; + + return child; + } + else if (walk.id_pos_abs > oid->n_subid) + die("unreachable"); + + return NULL; +} +#endif + +/* +int +mib_tree_insert(struct snmp_proto *p, struct mib_tree *t, struct oid *oid) +{ + ASSUME(oid); + + struct mib_walk_state walk = { }; + struct mib_node *node = mib_tree_find(t, &walk, oid); + struct mib_leaf *leaf = NULL; + + if (!node) + { + node = walk.stack[walk.stack_pos]; + + if (walk.id_pos_abs > oid->n_subid) + { + } + else / * walk.id_pos_abs <= oid->n_subid * / + { + leaf = mb_alloc(p->p.pool, sizeof(struct mib_leaf)); + leaf->id_len = oid->n_subid - walk.id_pos_abs; + leaf->ids = mb_alloc(p->p.pool, leaf->id_len * sizeof(struct mib_node *)); + memcpy(&leaf->ids, &oid->ids[oid->n_subid - leaf->id_len], + leaf->id_len * sizeof(u32)); + leaf->flags = 0; + leaf->children = NULL; + leaf->child_len = 0; + leaf->child_count = 0; + } + } +} +*/ + +#if 0 +int +mib_tree_insert(struct snmp_proto *p, struct mib_tree *t, struct oid *oid, struct mib_leaf *leaf) +{ + ASSUME(oid); + + struct mib_walk_state walk = { }; + struct mib_node *node = mib_tree_find(t, &walk, oid); + struct mib_node *leaf_node = &leaf->n; + + if (!node) + { + node = walk.stack[walk.stack_pos]; + + // can this really happen ?? + if (walk.id_pos_abs > oid->n_subid) + { + struct mib_node *parent = mb_alloc(p->p.pool, sizeof(struct mib_node)); + parent->id_len = walk.id_pos_abs - oid->n_subid; // -1? + parent->ids = mb_alloc(p->p.pool, parent->id_len * sizeof(u32)); + memcpy(&parent->ids, &node->ids, parent->id_len * sizeof(u32)); + u32 *ids = mb_alloc(p->p.pool, + (node->id_len - parent->id_len) * sizeof(u32)); + node->id_len = node->id_len - parent->id_len; + memcpy(ids, &node->ids[parent->id_len], node->id_len * sizeof(u32)); + mb_free(node->ids); + node->ids = ids; + + parent->child_count = 2; + parent->child_len = MAX(node->ids[0], oid->ids[walk.id_pos_abs]) + 1; + parent->children = mb_allocz(p->p.pool, + parent->child_len * sizeof(struct mib_node *)); + parent->children[node->ids[0]] = node; + parent->children[leaf_node->ids[0]] = leaf_node; + return 1; + } + else + { + mb_free(leaf_node->ids); + leaf_node->id_len = oid->n_subid - walk.id_pos_abs; + leaf_node->ids = mb_alloc(p->p.pool, leaf_node->id_len * sizeof(u32)); + return 1; + } + } + + if (mib_node_is_leaf(node)) + { + struct mib_leaf *l = SKIP_BACK(struct mib_leaf, n, node); + insert_node(&leaf->leafs, &l->leafs); + return 1; + } + + if (node->child_len > 0) + return 0; + + // problem when node->id_len + (walk.id_pos_abs - walk.id_pos_rel) > oid->n_subid + if (walk.id_pos_abs < oid->n_subid) // +-1?? + { + leaf_node->id_len = node->id_len - walk.id_pos_abs; + leaf_node->ids = mb_alloc(p->p.pool, leaf_node->id_len * sizeof(u32)); + memcpy(&leaf_node->ids, &oid->ids[walk.id_pos_abs], leaf_node->id_len * sizeof(u32)); + leaf_node->child_len = leaf_node->child_count = 0; + leaf_node->children = NULL; + return 1; + } + else + return 0; + return 1; +} +#endif + +/* +int +mib_tree_remove(struct mib_tree *tree, struct oid *oid) +{ + struct mib_walk_state walk = { }; + struct mib_node *node = mib_tree_find(tree, &walk, oid); + + if (!node) + return 0; + + mib_tree_delete(&walk); + //mib_tree_delete(tree, &walk); + return 1; +} +*/ + +int +mib_tree_remove(struct mib_tree *t, const struct oid *oid) +{ + struct mib_walk_state walk = { }; + mib_node_u *node = mib_tree_find(t, &walk, oid); + + if (!node) + return 0; + else + { + (void) mib_tree_delete(t, &walk); + return 1; + } +} + +int +mib_tree_delete(struct mib_tree *t, struct mib_walk_state *walk) +{ + int deleted = 0; + ASSUME(t); + + /* (walk->stack_pos < 2) It is impossible to delete root node */ + if (!walk || !walk->id_pos || walk->stack_pos < 2) + return 0; + + struct mib_node *parent = &walk->stack[walk->stack_pos - 2]->inner; + mib_node_u *node = walk->stack[walk->stack_pos - 1]; + + struct mib_walk_state delete = { + .id_pos = walk->id_pos, + .stack_pos = 2, + .stack = { + (mib_node_u *) parent, + node, + NULL, + }, + }; + + u32 last_id = 0; + while (delete.stack_pos > 1) + { +continue_while: /* like outer continue, but skip always true condition */ + parent = (struct mib_node *) delete.stack[delete.stack_pos - 2]; + + if (mib_node_is_leaf(node)) + { + /* Free leaf node */ + last_id = node->leaf.c.id; + parent->children[last_id] = NULL; + delete.stack[--delete.stack_pos] = NULL; + free(node); + deleted++; + node = delete.stack[delete.stack_pos - 1]; + continue; /* here, we couldn't skip the while condition */ + } + + struct mib_node *node_inner = &node->inner; + mib_node_u *child = NULL; + for (u32 id = last_id; id < node_inner->child_len; id++) + { + /* Recursively traverse child nodes */ + child = node_inner->children[id]; + + if (!child) + continue; + + last_id = 0; + delete.stack[delete.stack_pos++] = child; + parent = node_inner; + node = child; + goto continue_while; /* outer continue */ + } + + /* Free inner node without any children */ + last_id = node_inner->c.id; + parent->children[last_id] = NULL; + delete.stack[--delete.stack_pos] = NULL; + free(node_inner->children); + free(node_inner); + deleted++; + node = (mib_node_u *) parent; + + /* skip check for deleted node in loop over children */ + last_id++; + } + + /* delete the node from original stack */ + walk->stack[--walk->stack_pos] = NULL; + + node = walk->stack[walk->stack_pos - 1]; + struct mib_node *node_inner = &node->inner; + u32 id; + for (id = 0; id < node_inner->child_len; id++) + { + if (node_inner->children[id] != NULL) + break; + } + + if (id == node_inner->child_len) + { + /* all the children are NULL */ + free(node_inner->children); + node_inner->children = NULL; + node_inner->child_len = 0; + } + + return deleted; +} + +/* currently support only search with blank new walk state */ +/* requires non-NULL walk */ +/* TODO doc string, user should check if the node is not root (or at least be + * aware of that */ +mib_node_u * +mib_tree_find(const struct mib_tree *t, struct mib_walk_state *walk, const struct oid *oid) +{ + ASSERT(t && walk); + + if (!oid || snmp_is_oid_empty(oid)) + { + walk->stack_pos = 1; + walk->stack[0] = (mib_node_u *) &t->root; + return (snmp_is_oid_empty(oid)) ? (mib_node_u *) &t->root : NULL; + } + + mib_node_u *node; + struct mib_node *node_inner; + + u8 oid_pos = walk->id_pos = 0; + node = walk->stack[walk->stack_pos++] = (mib_node_u *) &t->root; + +#if 0 + u8 oid_pos = walk->id_pos; + + if (walk->stack_pos > 0) + node = walk->stack[walk->stack_pos]; + else + node = walk->stack[walk->stack_pos++] = (mib_node_u *) &t->root; + + if (mib_node_is_leaf(node)) + { + if (snmp_oid_is_prefixed(oid) && LOAD_U8(oid->n_subid) + ARRAY_SIZE(snmp_internet) + 1 == walk->id_pos) + return node; + + else if (!snmp_oid_is_prefixed(oid) && LOAD_U8(oid->n_subid) + 1 == walk->id_pos) + return node; + + /* it could hold that LOAD_U8(oid->n_subid) >= walk->id_pos */ + return NULL; + } +#endif + + node_inner = &node->inner; + ASSERT(node && !mib_node_is_leaf(node)); + + /* Handling of prefixed OID */ + if (snmp_oid_is_prefixed(oid)) + { + uint i; + /* walking the snmp_internet prefix itself */ + for (i = 0; i < ARRAY_SIZE(snmp_internet); i++) + { + if (node_inner->child_len <= snmp_internet[i]) + return NULL; + + node = node_inner->children[snmp_internet[i]]; + node_inner = &node->inner; + + if (!node) + return NULL; + + ASSERT(node->empty.id == snmp_internet[i]); + walk->stack[walk->stack_pos++] = node; + + if (mib_node_is_leaf(node)) + return NULL; + } + + /* walking the prefix continuation (OID field oid->prefix) */ + u8 prefix = LOAD_U8(oid->prefix); + if (node_inner->child_len <= prefix) + return NULL; + + node = node_inner->children[prefix]; + node_inner = &node->inner; + + if (!node) + return NULL; + + ASSERT(node->empty.id == prefix); + walk->stack[walk->stack_pos++] = node; + + if (mib_node_is_leaf(node) && LOAD_U8(oid->n_subid) > 0) + return NULL; + } + + u8 subids = LOAD_U8(oid->n_subid); + if (subids == 0) + return (node == (mib_node_u *) &t->root) ? NULL : node; + + /* loop for all OID's ids except the last one */ + for (oid_pos = 0; oid_pos < subids - 1; oid_pos++) // remove oid_pos assignment + { + u32 id = LOAD_U32(oid->ids[oid_pos]); + if (node_inner->child_len <= id) + { + walk->id_pos = oid_pos; + return NULL; + } + + node = node_inner->children[id]; + node_inner = &node->inner; + + if (!node) + { + walk->id_pos = oid_pos; + return NULL; + } + + ASSERT(node->empty.id == id); + walk->stack[walk->stack_pos++] = node; + + if (mib_node_is_leaf(node)) + { + walk->id_pos = ++oid_pos; + return NULL; + } + } + + walk->id_pos = oid_pos; + u32 last_id = LOAD_U32(oid->ids[oid_pos]); + if (node_inner->child_len <= last_id) + return NULL; + + node = node_inner->children[last_id]; + node_inner = &node->inner; + + if (!node) + return NULL; + + /* here, the check of node being a leaf is intentionally omitted + * because we may need to search for a inner node */ + ASSERT(node->empty.id == last_id); + walk->id_pos = ++oid_pos; + return walk->stack[walk->stack_pos++] = node; +} + +void +mib_tree_walk_init(struct mib_walk_state *walk) +{ + walk->id_pos = 0; + walk->stack_pos = 0; + memset(&walk->stack, 0, sizeof(walk->stack)); +} + +/* +void +mib_node_free(mib_node_u *node) +{ + if (!mib_node_is_leaf(node)) + { + struct mib_node *node_inner = &node->inner; + node_inner->child_len = 0; + free(node_inner->children); + node_inner->children = NULL; + } + + free(node); +} +*/ + +mib_node_u * +mib_tree_walk_next(struct mib_tree *t, struct mib_walk_state *walk) +{ + ASSERT(t && walk); + + u32 next_id = 0; + + if (walk->stack_pos == 0) + return NULL; + + mib_node_u *node = walk->stack[walk->stack_pos - 1]; + + if (mib_node_is_leaf(node)) + { + next_id = node->leaf.c.id + 1; + walk->stack[--walk->stack_pos] = NULL; + node = walk->stack[walk->stack_pos - 1]; + } + + while (walk->stack_pos > 0) + { + node = walk->stack[walk->stack_pos - 1]; + + if (mib_node_is_leaf(node)) + { + walk->stack[walk->stack_pos++] = node; + return node; + } + + struct mib_node *node_inner = &node->inner; + for (u32 id = next_id; id < node_inner->child_len; id++) + { + mib_node_u *child = node_inner->children[id]; + + if (!child) + continue; + + walk->stack[walk->stack_pos++] = child; + return child; + } + + next_id = node_inner->c.id + 1; + walk->stack[--walk->stack_pos] = NULL; + } + + return NULL; +} + +#if 0 +struct mib_node * +mib_tree_walk_next(struct mib_walk_state *walk) +{ + ASSUME(walk->stack[walk->stack_pos]); + + if (walk->stack_pos == 0 && walk->stack[0] && + walk->stack[0]->flags & (MIB_TREE_REG_ACK || MIB_TREE_REG_WAIT)) + return walk->stack[0]; + + struct mib_node *node = walk->stack[walk->stack_pos]; + u32 id; + +find_leaf: + while (!mib_node_is_leaf(node)) + { + for (id = 0; id < node->child_len; id++) + { + if (node->children[id]) + { + node = node->children[id]; + walk->stack[++walk->stack_pos] = node; + break; + } + } + + if (node->flags & (MIB_TREE_REG_ACK || MIB_TREE_REG_WAIT)) + return node; + } + + id = node->ids[0]; + + while (walk->stack_pos) + { + walk->stack[walk->stack_pos] = NULL; + --walk->stack_pos; + node = walk->stack[walk->stack_pos]; + + if (id + 1 != node->child_len) + break; + } + + if (id + 1 == node->child_len) + return walk->stack[0] = NULL; + + node = node->children[id + 1]; + walk->stack_pos++; + walk->stack[walk->stack_pos] = node; + goto find_leaf; +} +#endif + + +struct mib_leaf * +mib_tree_walk_next_leaf(struct mib_tree *t, struct mib_walk_state *walk) +{ + (void) t; + + if (walk->stack_pos == 0) + return NULL; + + u32 next_id = 0; + mib_node_u *node = walk->stack[walk->stack_pos - 1]; + + if (mib_node_is_leaf(node) && walk->stack_pos > 1) + { + next_id = node->leaf.c.id + 1; + walk->stack[--walk->stack_pos] = NULL; + node = walk->stack[walk->stack_pos - 1]; // does it underflow ?? + } + else if (mib_node_is_leaf(node)) + { + /* walk->stack_pos == 1, so we NULL out the last stack field */ + walk->stack[--walk->stack_pos] = NULL; + return NULL; + } + + mib_node_u *parent = (walk->stack_pos <= 1) ? NULL : + walk->stack[walk->stack_pos - 2]; + + while (walk->stack_pos > 0) + { +continue_while: + node = walk->stack[walk->stack_pos - 1]; + + if (mib_node_is_leaf(node)) + { + walk->stack[walk->stack_pos++] = node; + return (struct mib_leaf *) node; + } + + struct mib_node *node_inner = &node->inner; + for (u32 id = next_id; id < node_inner->child_len; id++) + { + mib_node_u *child = node_inner->children[id]; + + if (!child) + continue; + + next_id = 0; + walk->stack[walk->stack_pos++] = child; + /* node is assign at the beginning of the while loop (from stack) */ + goto continue_while; + } + + while (walk->stack_pos > 1) // endless loop here possible ?? + { + parent = walk->stack[walk->stack_pos - 2]; + node = walk->stack[walk->stack_pos - 1]; + + ASSUME(mib_node_is_leaf(node)); + if (node->leaf.c.id + 1 == parent->inner.child_len) + walk->stack[--walk->stack_pos] = NULL; + + next_id = node->inner.c.id + 1; + } + } + + return NULL; +} + +#if 0 +struct mib_leaf * +mib_tree_next_leaf(struct mib_walk_state *walk) +{ + ASSUME(walk->stack[walk->stack_pos] && + mib_node_is_leaf(walk->stack[walk->stack_pos])); + + struct mib_node *node = walk->stack[walk->stack_pos]; + u32 id; + + while (walk->stack_pos) + { + id = node->ids[0]; + walk->stack[walk->stack_pos] = NULL; + --walk->stack_pos; + node = walk->stack[walk->stack_pos]; + + if (id + 1 != node->child_len) + break; + } + + if (id + 1 == node->child_len) + return (struct mib_leaf *) (walk->stack[0] = NULL); + + id++; + while (!mib_node_is_leaf(node)) + { + for (; id < node->child_len && !node->children[id]; id++) + ; + + node = node->children[id]; + walk->stack[++walk->stack_pos] = node; + id = 0; + } + + return (struct mib_leaf *) node; +} +#endif + diff --git a/proto/snmp/mib_tree.h b/proto/snmp/mib_tree.h new file mode 100644 index 00000000..bec13b25 --- /dev/null +++ b/proto/snmp/mib_tree.h @@ -0,0 +1,144 @@ +#ifndef _BIRD_SNMP_MIB_TREE_ +#define _BIRD_SNMP_MIB_TREE_ + +#include "lib/resource.h" +#include "lib/lists.h" +#include "lib/birdlib.h" + +#include "subagent.h" + +#define MIB_TREE_NO_FLAGS 0x00 +#define MIB_TREE_LEAF 0x01 +#define MIB_TREE_HAS_HOOKS 0x02 + +typedef union mib_node_union mib_node_u; + +struct mib_node_core { + u32 id; + u8 flags; +}; + +struct mib_node { + struct mib_node_core c; + mib_node_u **children; + u32 child_len; +}; + +struct mib_leaf { + struct mib_node_core c; + enum snmp_search_res (*filler)(struct snmp_proto_pdu *pc, struct agentx_varbind **vb); + enum agentx_type type; + int size; +}; + +union mib_node_union { + struct mib_node_core empty; + struct mib_node inner; + struct mib_leaf leaf; +}; + +/* +cannonical names + find + walk_init + walk_next + init + insert + delete / remove + */ + +/* + * The stack size include empty prefix (mib tree root). + */ +#define MIB_WALK_STACK_SIZE 33 +STATIC_ASSERT(OID_MAX_LEN < MIB_WALK_STACK_SIZE); + +struct mib_walk_state { + u8 id_pos; /* points after last matching subid in OID */ + u32 stack_pos; /* points after last valid stack node */ + mib_node_u *stack[MIB_WALK_STACK_SIZE]; +}; + +struct mib_tree { + struct mib_node root; +}; + +void mib_tree_init(pool *p, struct mib_tree *t); +void mib_tree_walk_init(struct mib_walk_state *state); +//void mib_node_free(mib_node_u *node); +//void mib_tree_free(struct mib_tree *tree); + +mib_node_u *mib_tree_add(pool *p, struct mib_tree *tree, const struct oid *oid, int is_leaf); +int mib_tree_remove(struct mib_tree *t, const struct oid *oid); +int mib_tree_delete(struct mib_tree *t, struct mib_walk_state *state); +mib_node_u *mib_tree_find(const struct mib_tree *tree, struct mib_walk_state *walk, const struct oid *oid); +mib_node_u *mib_tree_next(struct mib_tree *tree, mib_node_u *end); + +static inline int +mib_node_is_leaf(const mib_node_u *node) +{ + return node->empty.flags & MIB_TREE_LEAF; +} + +/* +WALK OID ID POS !!! +assert on STACK SIZE overflow resp fix entering the too long OIDs + +Enumerace divnych pripadu + +OID { n_subid 0, prefix 0 } ids NULL +OID { n_subid 0, prefix 2 } ids NULL <- todle je divny +OID { n_subid 1, prefix 0 } ids { 1 } +OID { n_subid 2, prefix 0 } ids { 1, 31 } +OID { n_subid 3, prefix 0 } ids { 1, 30, 32 } +OID { n_subid 7, prefix 0 } ids { 1, 2, 3, 4, 5, 6, 7 } +OID { n_subid 1, prefix 4 } ids { 8 } +OID { n_subid 2, prefix 19 } ids { 3, 2 } +OID { n_subid 3, prefix 5 } ids { 3, 9, 1 } +OID { n_subid 4, prefix 2 } ids { 1, 15, 1, 2 } <- obecny priklad + +hledani +odstraneni +odstraneni stromu/podstromu +nasledovnik +nasledovnik list +pridani do vrcholu do stromu + +TODO +add +next +next leaf +find with non-empty walk state + +je opravdu potreba mit v vsech funkcich argument stromu (struct mib_tree *t) ? + >> walk, walk_init, next, next_leaf << + +otestovat neprefixovane OID v prefixovanem strome +a prefixove OID v neprefixovanem strome + +TESTING TREES + +s internet prefixem + - jinak prazdny + - jeden vrchol + - dva vrcholy + - 3, 4, + - rand() vrcholu + + cesta, vidlicka, hrabe + +bez internet prefixu + - uplne prazdny + - jediny vrchol (0, 1, 300, rand()) + - dva vrcholy + - tri vrcholy + - ctyri vrcholy + - pet vrcholu jako v internet ale s prefixem = 300 + - rand() vrcholu rand() hodnot + + cesta vidlicka hrabe + + */ + +#endif + diff --git a/proto/snmp/snmp.c b/proto/snmp/snmp.c index fe7e8f5f..e6067ff7 100644 --- a/proto/snmp/snmp.c +++ b/proto/snmp/snmp.c @@ -511,7 +511,6 @@ 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); return snmp_set_state(p, SNMP_INIT); @@ -550,7 +549,7 @@ skip: } /* - * snmp_reconfigure - Test if SNMP instance is reconfigurable + * snmp_reconfigure - Indicate instance reconfigurability * @P - SNMP protocol generic handle, current state * @CF - SNMP protocol configuration generic handle carring new values * @@ -565,16 +564,16 @@ snmp_reconfigure(struct proto *P, struct proto_config *CF) const struct snmp_config *new = SKIP_BACK(struct snmp_config, cf, CF); /* We are searching for configuration changes */ - int retval = snmp_reconfigure_logic(p, new); + int config_changed = snmp_reconfigure_logic(p, new); - if (retval) + if (config_changed) { /* Reinitialize the hash after snmp_shutdown() */ HASH_INIT(p->bgp_hash, p->pool, 10); snmp_bgp_start(p); } - return retval; + return config_changed; } /* diff --git a/proto/snmp/snmp_test.c b/proto/snmp/snmp_test.c index 08ea95ab..e6681278 100644 --- a/proto/snmp/snmp_test.c +++ b/proto/snmp/snmp_test.c @@ -7,6 +7,8 @@ * Can be freely distributed and used under the terms of the GNU GPL. */ +#include + #include "test/birdtest.h" #include "test/bt-utils.h" @@ -14,308 +16,226 @@ #include "subagent.h" #include "snmp.h" #include "snmp_utils.h" +#include "mib_tree.h" + +// TODO test walk state stack overflow +// TODO hint for child len alloc size + +static int t_oid_empty(void); +static int t_oid_compare(void); +static int t_oid_prefixize(void); +static int t_tree_find(void); +static int t_tree_traversal(void); +static int t_tree_leafs(void); +static int t_tree_add(void); +static int t_tree_delete(void); + +#define SNMP_BUFFER_SIZE 1024 +#define TESTS_NUM 20 +#define SMALL_TESTS_NUM 10 +static int tree_sizes[] = { 0, 1, 10, 100, 1000 }; + +#define OID_MAX_ID 16 #define SNMP_EXPECTED(actual, expected) \ bt_debug("%s expected: %3u actual: %3u\n", \ #expected, expected, actual); -#define OID_ALLOCATE(size) mb_alloc(&root_pool, sizeof(struct oid) + (size) * sizeof (u32)) - -#define OID_INIT(oid, n_subid_, prefix_, include_, arr_) \ - (oid)->n_subid = (n_subid_); \ - (oid)->prefix = (prefix_); \ - (oid)->include = (include_); \ - memcpy((oid)->ids, (arr_), sizeof(arr_)); \ - -void -test_fill(struct snmp_proto *p) +static inline struct oid * +oid_allocate(uint size) { - ((struct proto *) p)->pool = &root_pool; + return tmp_alloc(sizeof(struct oid) + size * sizeof(u32)); } -static void UNUSED -test_oid(struct oid *oid, uint base_size) +static inline void +oid_init2(struct oid *oid, u8 n_subid, u8 prefix, u8 include, va_list ids) { -#if 0 - /* tests all states one by one */ + oid->n_subid = n_subid; + oid->prefix = prefix; + oid->include = include; + oid->reserved = 0; - oid->n_subid = base_size + 2; - oid->ids[base_size + 0] = 1; - oid->ids[base_size + 1] = 15; // BGP4-MIB::bgp - bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_BGP); - - oid->n_subid = base_size + 3; - oid->ids[base_size + 2] = 1; // BGP4-MIB::bgpVersion - snmp_oid_dump(oid); - bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_VERSION); - - oid->ids[base_size + 2] = 2; // BGP4-MIB::bgpLocalAs - bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_LOCAL_AS); - - oid->ids[base_size + 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; - bt_debug("arith\n"); - oid->ids[base_size + 2] = 3; - oid->ids[base_size + 3] = 1; // BGP4-MIB::bgpPeerEntry - bt_debug("dumpping\n"); - bt_debug("after dump, assertion\n"); - // SNMP_EXPECTED(snmp_bgp_state(oid), BGP_INTERNAL_PEER_ENTRY); - bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_PEER_ENTRY); - bt_debug("finish\n"); - - oid->n_subid = base_size + 5; - oid->ids[base_size + 2] = 3; - oid->ids[base_size + 3] = 1; - oid->ids[base_size + 4] = 1; // BGP4-MIB::bgpPeerIdentifier - bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_IDENTIFIER); - - oid->ids[base_size + 4] = 2; // BGP4-MIB::bgpPeerState - bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_STATE); - - oid->ids[base_size + 4] = 3; // BGP4-MIB::bgpPeerAdminStatus - bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_ADMIN_STATUS); - - bt_debug(" 1/4\n"); - oid->ids[base_size + 4] = 4; // BGP4-MIB::bgpPeerNegotiatedVersion - bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_NEGOTIATED_VERSION); - - oid->ids[base_size + 4] = 5; // BGP4-MIB::bgpPeerLocalAddr - bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_LOCAL_ADDR); - - oid->ids[base_size + 4] = 6; // BGP4-MIB::bgpPeerLocalPort - bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_LOCAL_PORT); - - oid->ids[base_size + 4] = 7; // BGP4-MIB::bgpPeerRemoteAddr - bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_REMOTE_ADDR); - - oid->ids[base_size + 4] = 8; // BGP4-MIB::bgpPeerRemotePort - bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_REMOTE_PORT); - - oid->ids[base_size + 4] = 9; // BGP4-MIB::bgpPeerRemoteAs - bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_REMOTE_AS); - - oid->ids[base_size + 4] = 10; // BGP4-MIB::bgpPeerInUpdates - bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_RX_UPDATES); - - bt_debug(" 1/2 \n"); - oid->ids[base_size + 4] = 11; // BGP4-MIB::bgpPeerOutUpdates - bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_TX_UPDATES); - - oid->ids[base_size + 4] = 12; // BGP4-MIB::bgpPeerInTotalMessages - bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_RX_MESSAGES); - - oid->ids[base_size + 4] = 13; // BGP4-MIB::bgpPeerOutTotalMessages - bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_TX_MESSAGES); - - oid->ids[base_size + 4] = 14; // BGP4-MIB::bgpPeerLastError - bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_LAST_ERROR); - - oid->ids[base_size + 4] = 15; // BGP4-MIB::bgpPeerFsmEstablishedTransitions - bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_FSM_TRANSITIONS); - - oid->ids[base_size + 4] = 16; // BGP4-MIB::bgpPeerFsmEstablishedTime - bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_FSM_ESTABLISHED_TIME); - - oid->ids[base_size + 4] = 17; // BGP4-MIB::bgpPeerConnectionRetryInterval - bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_RETRY_INTERVAL); - bt_debug( " 3/4\n"); - - oid->ids[base_size + 4] = 18; // BGP4-MIB::bgpPeerHoldTime - bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_HOLD_TIME); - - oid->ids[base_size + 4] = 19; // BGP4-MIB::bgpPeerKeepAlive - bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_KEEPALIVE); - - oid->ids[base_size + 4] = 20; // BGP4-MIB::bgpPeerHoldTimeConfigured - bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_HOLD_TIME_CONFIGURED); - - oid->ids[base_size + 4] = 21; // BGP4-MIB::bgpPeerKeepAliveConfigured - bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_KEEPALIVE_CONFIGURED); - - oid->ids[base_size + 4] = 22; // BGP4-MIB::bgpPeerMinASOriginationInterval - bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_ORIGINATION_INTERVAL); - - oid->ids[base_size + 4] = 23; // BGP4-MIB::bgpPeerMinRouteAdvertisementInverval - bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_MIN_ROUTE_ADVERTISEMENT); - - oid->ids[base_size + 4] = 24; // BGP4-MIB::bgpPeerInUpdateElapsedTime - bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_IN_UPDATE_ELAPSED_TIME); - - bt_debug("testing BGP4-MIB::bgpPeerEntry end\n"); -#endif + for (u8 i = 0; i < n_subid; i++) + { + u32 id = va_arg(ids, u32); + oid->ids[i] = id; + } } +static inline void +oid_init(struct oid *oid, u8 n_subid, u8 prefix, u8 include, ...) +{ + va_list ids; + va_start(ids, include); + oid_init2(oid, n_subid, prefix, include, ids); + va_end(ids); +} + +static inline struct oid * +oid_create(u8 n_subid, u8 prefix, u8 include, ...) +{ + struct oid *result = tmp_alloc(snmp_oid_size_from_len(n_subid)); + va_list ids; + + va_start(ids, include); + oid_init2(result, n_subid, prefix, include, ids); + va_end(ids); + + return result; +} + +static u32 +xrandom(u32 max) +{ + return (bt_random() % max); +} + +static u32 +oid_random_id(void) +{ + return (bt_random() % (OID_MAX_ID)); +} + +static struct oid * +random_prefixed_oid(void) +{ + u32 len = xrandom(OID_MAX_LEN + 1 - ARRAY_SIZE(snmp_internet)); + + u8 prefix = (u8) xrandom(UINT8_MAX + 1); + + if (!prefix) + return oid_create(0, 0, 0, 0); + + struct oid *random = tmp_alloc(snmp_oid_size_from_len(len)); + /* (xrandom(2) * bt_random()) has 0.5 probability to have value 0 and + * 0.5 to have random u32 (including zero) */ + oid_init(random, 0, prefix, xrandom(2) * bt_random()); + random->n_subid = len; + + for (u32 id = 0; id < len; id++) + random->ids[id] = oid_random_id(); + + return random; +} + +static struct oid * +random_no_prefix_oid(void) +{ + /* probability that the random OID is prefixable is practically zero */ + u32 len = xrandom(OID_MAX_LEN + 1); + + struct oid *random = tmp_alloc(snmp_oid_size_from_len(len)); + /* (xrandom(2) * bt_random()) has 0.5 probability to have value 0 and + * 0.5 to have random u32 (including zero) */ + oid_init(random, 0, 0, xrandom(2) * bt_random()); + random->n_subid = len; + + for (u32 id = 0; id < len; id++) + random->ids[id] = oid_random_id(); + + return random; +} + +static struct oid * +random_prefixable_oid(void) +{ + /* generate the len without the snmp_internet prefix included and prefix ID */ + u32 len = xrandom(OID_MAX_LEN + 1 - (ARRAY_SIZE(snmp_internet) + 1)); + + struct oid *random = tmp_alloc( + snmp_oid_size_from_len(len + ARRAY_SIZE(snmp_internet) + 1)); + /* (xrandom(2) * bt_random()) has 0.5 probability to have value 0 and + * 0.5 to have random u32 (including zero) */ + oid_init(random, 0, 0, xrandom(2) * bt_random()); + random->n_subid = len + ARRAY_SIZE(snmp_internet) + 1; + + for (u32 inet_id = 0; inet_id < ARRAY_SIZE(snmp_internet); inet_id++) + random->ids[inet_id] = snmp_internet[inet_id]; + + random->ids[ARRAY_SIZE(snmp_internet)] = xrandom(UINT8_MAX + 1); + + for (u32 id = 0; id < len; id++) + random->ids[id + ARRAY_SIZE(snmp_internet) + 1] = oid_random_id(); + + return random; +} + +static struct oid * +random_oid(void) +{ + u32 option = xrandom(3); + + if (option == 0) + return random_prefixed_oid(); + else if (option == 1) + return random_no_prefix_oid(); + else + return random_prefixable_oid(); +} + + + static int -t_s_is_oid_empty(void) +t_oid_empty(void) { + struct lp_state tmps; + lp_save(tmp_linpool, &tmps); + bt_assert(snmp_is_oid_empty(NULL) == 0); - struct oid *blank = mb_alloc(&root_pool, sizeof(struct oid)); - blank->n_subid = 0; - blank->prefix = 0; - blank->include = 0; - - bt_assert(snmp_is_oid_empty(blank) == 1); - - mb_free(blank); blank = NULL; + { + struct oid *blank = oid_create(0, 0, 0 /* no ids */); + bt_assert(snmp_is_oid_empty(blank) == 1); + lp_restore(tmp_linpool, &tmps); + } - struct oid *prefixed = mb_alloc(&root_pool, sizeof(struct oid) + 3 * sizeof(u32)); - prefixed->n_subid = 3; - prefixed->prefix = 100; - prefixed->include = 1; - - u32 prefixed_arr[] = { ~((u32) 0), 0, 256 }; - memcpy(&prefixed->ids, prefixed_arr, sizeof(prefixed_arr) / - sizeof(prefixed_arr[0])); - - bt_assert(snmp_is_oid_empty(prefixed) == 0); - - mb_free(prefixed); prefixed = NULL; + { + struct oid *prefixed = oid_create(3, 100, 1, + /* ids */ ~((u32) 0), 0, 256); + bt_assert(snmp_is_oid_empty(prefixed) == 0); + lp_restore(tmp_linpool, &tmps); + } - struct oid *to_prefix = mb_alloc(&root_pool, sizeof(struct oid) + 8 * sizeof(u32)); - to_prefix->n_subid = 8; - to_prefix->prefix = 0; - to_prefix->include = 1; - - u32 to_prefix_arr[] = {1, 3, 6, 1, 100, ~((u32) 0), 0, 256 }; - memcpy(&to_prefix->n_subid, to_prefix_arr, sizeof(to_prefix_arr) / - sizeof(to_prefix_arr[0])); - - bt_assert(snmp_is_oid_empty(to_prefix) == 0); - - mb_free(to_prefix); to_prefix = NULL; + { + struct oid *to_prefix = oid_create(8, 0, 1, + /* ids */ 1, 3, 6, 1, 100, ~((u32) 0), 0, 256); + bt_assert(snmp_is_oid_empty(to_prefix) == 0); + lp_restore(tmp_linpool, &tmps); + } - struct oid *unprefixable = mb_alloc(&root_pool, sizeof(struct oid) + 2 * sizeof(u32)); - unprefixable->n_subid = 2; - unprefixable->prefix = 0; - unprefixable->include = 0; + { + struct oid *unprefixable = oid_create(2, 0, 0, + /* ids */ 65535, 4); + bt_assert(snmp_is_oid_empty(unprefixable) == 0); + lp_restore(tmp_linpool, &tmps); + } - u32 unpref[] = { 65535, 4 }; - memcpy(&unprefixable->ids, unpref, sizeof(unpref) / sizeof(unpref[0])); - - bt_assert(snmp_is_oid_empty(unprefixable) == 0); - - mb_free(unprefixable); unprefixable = NULL; - - - struct oid *unprefixable2 = mb_alloc(&root_pool, sizeof(struct oid) + 8 * sizeof(u32)); - unprefixable2->n_subid = 8; - unprefixable2->prefix = 0; - unprefixable2->include = 1; - - u32 unpref2[] = { 1, 3, 6, 2, 1, 2, 15, 6 }; - memcpy(&unprefixable2->ids, unpref2, sizeof(unpref2) / sizeof(unpref2[0])); - - bt_assert(snmp_is_oid_empty(unprefixable2) == 0); - - mb_free(unprefixable2); unprefixable2 = NULL; - - return 1; -} - -static int -t_s_prefixize(void) -{ - //struct oid *nulled = NULL; - struct snmp_proto snmp_proto; - test_fill(&snmp_proto); - - //struct oid *result = snmp_prefixize(&snmp_proto, nulled); - //bt_assert(NULL == result); - //result != NULL ? mb_free(result) : NULL; - struct oid *result; - - struct oid *blank = mb_allocz(&root_pool, sizeof(struct oid)); - /* here the byte order should not matter */ - result = snmp_prefixize(&snmp_proto, blank); - bt_assert(snmp_is_oid_empty(result) == 1); - - mb_free(result); result = NULL; - mb_free(blank); blank = NULL; - - - struct oid *prefixed = mb_alloc(&root_pool, sizeof(struct oid) + 3 * sizeof(u32)); - prefixed->n_subid = 3; - prefixed->prefix = 100; - prefixed->include = 1; - - u32 prefixed_arr[] = { ~((u32) 0), 0, 256 }; - memcpy(&prefixed->ids, prefixed_arr, sizeof(prefixed_arr)); - - /* struct oid */result = snmp_prefixize(&snmp_proto, prefixed); - bt_assert(memcmp(result, prefixed, snmp_oid_size(prefixed)) == 0); - - mb_free(result); result = NULL; - //mb_free(prefixed); prefixed = NULL; - - - struct oid *to_prefix = mb_alloc(&root_pool, sizeof(struct oid) + 8 * sizeof(u32)); - to_prefix->n_subid = 8; - to_prefix->prefix = 0; - to_prefix->include = 1; - - u32 to_prefix_arr[] = {1, 3, 6, 1, 100, ~((u32) 0), 0, 256 }; - memcpy(to_prefix->ids, to_prefix_arr, sizeof(to_prefix_arr)); - - result = snmp_prefixize(&snmp_proto, to_prefix); - - bt_assert(memcmp(result, prefixed, snmp_oid_size(prefixed)) == 0); - - mb_free(result); result = NULL; - mb_free(to_prefix); to_prefix = NULL; - - - struct oid *unprefixable = mb_alloc(&root_pool, sizeof(struct oid) + 2 * sizeof(u32)); - unprefixable->n_subid = 2; - unprefixable->prefix = 0; - unprefixable->include = 0; - - u32 unpref[] = { 65535, 4 }; - memcpy(&unprefixable->ids, unpref, sizeof(unpref) / sizeof(unpref[0])); - - result = snmp_prefixize(&snmp_proto, unprefixable); - bt_assert(result == NULL); - - result != NULL ? mb_free(result) : NULL; - - struct oid *unprefixable2 = mb_alloc(&root_pool, sizeof(struct oid) + 8 * sizeof(u32)); - unprefixable2->n_subid = 8; - unprefixable2->prefix = 0; - unprefixable2->include = 1; - - u32 unpref2[] = { 1, 3, 6, 2, 1, 2, 15, 6 }; - memcpy(&unprefixable2->ids, unpref2, sizeof(unpref2) / sizeof(unpref2[0])); - - result = snmp_prefixize(&snmp_proto, unprefixable2); - bt_assert(result == NULL); - - result != NULL ? mb_free(result) : NULL; + { + struct oid *unprefixable2 = oid_create(8, 0, 1, + /* ids */ 1, 3, 6, 2, 1, 2, 15, 6); + bt_assert(snmp_is_oid_empty(unprefixable2) == 0); + lp_restore(tmp_linpool, &tmps); + } + tmp_flush(); return 1; } static int t_oid_compare(void) { + struct lp_state tmps; + lp_save(tmp_linpool, &tmps); + /* same length, no prefix */ - struct oid *l1 = OID_ALLOCATE(5); - { - u32 arr[] = { 1, 2, 3, 4, 5 }; - OID_INIT(l1, 5, 0, 1, arr); - } + struct oid *l1 = oid_create(5, 0, 1, + /* ids */ 1, 2, 3, 4, 5); - - struct oid *r1 = OID_ALLOCATE(5); - { - u32 arr[] = { 1, 2, 3, 4, 6 }; - OID_INIT(r1, 5, 0, 0, arr); - } + struct oid *r1 = oid_create(5, 0, 0, + /* ids */ 1, 2, 3, 4, 6); bt_assert(snmp_oid_compare(l1, r1) == -1); bt_assert(snmp_oid_compare(r1, l1) == 1); @@ -343,23 +263,15 @@ t_oid_compare(void) bt_assert(snmp_oid_compare(l1, l1) == 0); bt_assert(snmp_oid_compare(r1, r1) == 0); - mb_free(l1); - mb_free(r1); - + lp_restore(tmp_linpool, &tmps); /* different length, no prefix */ - l1 = OID_ALLOCATE(4); - { - u32 arr[] = { 1, 2, 3, 4 }; - OID_INIT(l1, 4, 0, 0, arr); - } + l1 = oid_create(4, 0, 0, + /* ids */ 1, 2, 3, 4); - r1 = OID_ALLOCATE(5); - { - u32 arr[] = { 1, 2, 3, 4, 1 }; - OID_INIT(r1, 5, 0, 1, arr); - } + r1 = oid_create(5, 0, 1, + /* ids */ 1, 2, 3, 4, 1); bt_assert(snmp_oid_compare(l1, r1) == -1); bt_assert(snmp_oid_compare(r1, l1) == 1); @@ -387,23 +299,15 @@ t_oid_compare(void) bt_assert(snmp_oid_compare(l1, l1) == 0); bt_assert(snmp_oid_compare(r1, r1) == 0); - mb_free(l1); - mb_free(r1); - + lp_restore(tmp_linpool, &tmps); /* inverse order different length, no prefix */ - l1 = OID_ALLOCATE(4); - { - u32 arr[] = { 1, 2, 3, 5 }; - OID_INIT(l1, 4, 0, 0, arr); - } + l1 = oid_create(4, 0, 0, + /* ids */ 1, 2, 3, 5); - r1 = OID_ALLOCATE(5); - { - u32 arr[] = { 1, 2, 3, 4, 1 }; - OID_INIT(r1, 5, 0, 0, arr); - } + r1 = oid_create(5, 0, 0, + /* ids */ 1, 2, 3, 4, 1); bt_assert(snmp_oid_compare(l1, r1) == 1); bt_assert(snmp_oid_compare(r1, l1) == -1); @@ -428,24 +332,16 @@ t_oid_compare(void) bt_assert(snmp_oid_compare(l1, r1) == 1); bt_assert(snmp_oid_compare(r1, l1) == -1); - mb_free(l1); - mb_free(r1); - + lp_restore(tmp_linpool, &tmps); /* ==== MIXED PREFIXED / NON PREFIXED OID compare ==== */ /* same length, mixed */ - l1 = OID_ALLOCATE(6); /* OID .1.2.17.3.21.4 */ - { - u32 arr[] = { 1, 2, 17, 3, 21, 4 }; - OID_INIT(l1, 6, 0, 1, arr); - } + l1 = oid_create(6, 0, 1, + /* ids */ 1, 2, 17, 3, 21, 4); - r1 = OID_ALLOCATE(1); /* OID .1.3.6.1.5.3 */ - { - u32 arr[] = { 3 }; - OID_INIT(r1, 1, 5, 1, arr); - } + r1 = oid_create(1, 5, 1, + /* ids */ 3); bt_assert(snmp_oid_compare(l1, r1) == -1); bt_assert(snmp_oid_compare(r1, l1) == 1); @@ -453,70 +349,890 @@ t_oid_compare(void) bt_assert(snmp_oid_compare(l1, l1) == 0); bt_assert(snmp_oid_compare(r1, r1) == 0); - mb_free(l1); - mb_free(r1); + lp_restore(tmp_linpool, &tmps); + + struct oid *super = oid_create(4, 0, 0, /* ids */ 1, 3, 6, 1); + struct oid *weird = oid_create(4, 70, 0, /* ids */ 9, 10, 10, 12); + + bt_assert(snmp_oid_compare(super, weird) != 0); + + tmp_flush(); + return 1; +} + +static struct oid * +snmp_oid_prefixize(struct snmp_proto *p, const struct oid *oid, struct snmp_pdu *c) +{ + struct agentx_varbind *vb = snmp_vb_to_tx(p, oid, c); + bt_assert(vb->reserved == 0); + return &vb->name; +} + +/* + * t_oid_prefixize - test prefixing aspect of function snmp_vb_to_tx() + */ +static int +t_oid_prefixize(void) +{ + lp_state tmps = { }; + struct snmp_proto *snmp_proto = NULL; + + byte *buffer = tmp_alloc(SNMP_BUFFER_SIZE); + const struct snmp_pdu copy = { + .buffer = buffer, + .size = SNMP_BUFFER_SIZE, + .error = AGENTX_RES_NO_ERROR, + .index = 0, + }; + struct snmp_pdu c; + + lp_save(tmp_linpool, &tmps); + + + /* testing prefixable OIDs */ + for (int test = 0; test < TESTS_NUM; test++) + { + const struct oid *oid = random_prefixable_oid(); + + u8 subids = oid->n_subid; + u8 include = oid->include; + u32 pid = oid->ids[ARRAY_SIZE(snmp_internet)]; + + /* reset to the default snmp_pdu */ + c = copy; memset(buffer, 0, snmp_oid_size(oid) + 8); + + struct oid *new = snmp_oid_prefixize(snmp_proto, oid, &c); + + bt_assert(new->n_subid == subids - (ARRAY_SIZE(snmp_internet) + 1)); + bt_assert(new->prefix == pid); + bt_assert(!!new->include == !!include); + bt_assert(new->reserved == 0); + + for (u32 i = 0; i < new->n_subid; i++) + { + bt_assert(new->ids[i] == oid->ids[i + ARRAY_SIZE(snmp_internet) + 1]); + } + + for (u32 j = 0; j < ARRAY_SIZE(snmp_internet); j++) + bt_assert(oid->ids[j] == snmp_internet[j]); + + lp_restore(tmp_linpool, &tmps); + } + + /* testing already prefixed OIDs */ + for (int test = 0; test < TESTS_NUM; test++) + { + const struct oid *prefixed = random_prefixed_oid(); + + /* reset to the default snmp_pdu */ + c = copy; memset(buffer, 0, snmp_oid_size(prefixed) + 8); + + struct oid *new = snmp_oid_prefixize(snmp_proto, prefixed, &c); + + bt_assert(new->n_subid == prefixed->n_subid); + bt_assert(new->prefix == prefixed->prefix); + bt_assert(!!new->include == !!prefixed->include); + bt_assert(new->reserved == 0); + bt_assert(!memcmp(&new->ids[0], &prefixed->ids[0], new->n_subid * sizeof(u32))); + + lp_restore(tmp_linpool, &tmps); + } + + lp_restore(tmp_linpool, &tmps); + + /* testing non-prefixable OIDs */ + for (int test = 0; test < TESTS_NUM; test++) + { + const struct oid *oid = random_no_prefix_oid(); + + /* test that the OID is _really_ not prefixable */ + if (oid->n_subid > ARRAY_SIZE(snmp_internet) && + oid->ids[ARRAY_SIZE(snmp_internet) + 1] <= UINT8_MAX) + { + for (u32 i = 0; i < ARRAY_SIZE(snmp_internet); i++) + if (oid->ids[i] != snmp_internet[i]) goto continue_testing; + + break; /* outer for loop */ + } + +continue_testing: + + /* reset to the default snmp_pdu */ + c = copy; memset(buffer, 0, snmp_oid_size(oid) + 8); + + struct oid *new = snmp_oid_prefixize(snmp_proto, oid, &c); + + bt_assert(new->n_subid == oid->n_subid); + bt_assert(new->prefix == oid->prefix); + bt_assert(!!new->include == !!oid->include); + bt_assert(new->reserved == 0); + bt_assert(!memcmp(&new->ids[0], &oid->ids[0], new->n_subid * sizeof(u32))); + + lp_restore(tmp_linpool, &tmps); + } + + for (int test = 0; test < SMALL_TESTS_NUM; test++) + { + const struct oid *oid; + { + struct oid *work = random_prefixable_oid(); + + /* include also the prefix ID (at index 4) */ + u32 index = xrandom(ARRAY_SIZE(snmp_internet) + 1); + /* change randomly picked id at index from 0..5 (included) */ + u32 random = bt_random(); + if (index == ARRAY_SIZE(snmp_internet) && random > 255) + work->ids[index] = random; + else if (index != ARRAY_SIZE(snmp_internet) && work->ids[index] != random) + work->ids[index] = random; + else + continue; + oid = work; + } + + /* reset to the default snmp_pdu */ + c = copy; memset(buffer, 0, snmp_oid_size(oid) + 8); + + struct oid *new = snmp_oid_prefixize(snmp_proto, oid, &c); + + bt_assert(new->n_subid == oid->n_subid); + bt_assert(new->prefix == oid->prefix); + bt_assert(!!new->include == !!oid->include); + bt_assert(new->reserved == 0); + bt_assert(!memcmp(&new->ids[0], &oid->ids[0], new->n_subid * sizeof(u32))); + + lp_restore(tmp_linpool, &tmps); + } + + tmp_flush(); + return 1; +} + +static void +test_both(void *buffer, uint size, const struct oid *left, const struct oid +*right, const struct oid *expected) +{ + memset(buffer, 0, size); + snmp_oid_common_ancestor(left, right, buffer); + bt_assert(snmp_oid_compare(buffer, expected) == 0); + + memset(buffer, 0, size); + snmp_oid_common_ancestor(right, left, buffer); + bt_assert(snmp_oid_compare(buffer, expected) == 0); +} + +#define TEST_BOTH(l, r, e) test_both(buffer, 1024, l, r, e) +static int +t_oid_ancestor(void) +{ + const struct oid *null = oid_create(0, 0, 0); + const struct oid *shorter = oid_create(3, 15, 0, /* ids */ 192, 1, 7); + const struct oid *prefixed = oid_create(4, 15, 0, /* ids */ 192, 1, 7, 82); + const struct oid *no_prefix = oid_create(9, 0, 0, /* ids */ 1, 3, 6, 1, 15, 192, 1, 7, 82); + const struct oid *outside = oid_create(7, 0, 0, /* ids */ 4, 3, 2, 1, 8, 0, 2); + const struct oid *prefix_only = oid_create(0, 15, 0); + const struct oid *prefix_only2 = oid_create(0, 9, 0); + const struct oid *partial = oid_create(3, 0, 0, /* ids */ 1, 3, 6); + const struct oid *no_inet = oid_create(5, 0, 0, /* ids */ 1, 3, 6, 2, 5); + + const struct oid *inet = oid_create(4, 0, 0, /* ids */ 1, 3, 6, 1); + + + const struct oid *oids[] = { + null, shorter, prefixed, no_prefix, outside, prefix_only, partial, no_inet, inet + }; + + char buffer[1024]; + + /* skip null oid */ + for (size_t o = 1; o < ARRAY_SIZE(oids); o++) + TEST_BOTH(null, oids[o], null); + + for (size_t o = 0; o < ARRAY_SIZE(oids); o++) + TEST_BOTH(oids[o], oids[o], oids[o]); + + TEST_BOTH(partial, no_prefix, partial); + TEST_BOTH(partial, prefixed, partial); + TEST_BOTH(partial, prefix_only, partial); + TEST_BOTH(partial, prefix_only2, partial); + + TEST_BOTH(prefix_only2, prefixed, inet); + TEST_BOTH(prefix_only2, no_prefix, inet); + + TEST_BOTH(prefix_only2, inet, inet); + + TEST_BOTH(prefix_only, prefix_only2, inet); + + TEST_BOTH(prefix_only, prefixed, prefix_only); + TEST_BOTH(prefix_only, no_prefix, prefix_only); + + TEST_BOTH(prefix_only, inet, inet); + + /* skip null oid */ + for (size_t o = 1; o < ARRAY_SIZE(oids); o++) + { + if (oids[o] == outside) continue; + + TEST_BOTH(outside, oids[o], null); + } + + TEST_BOTH(no_inet, partial, partial); + TEST_BOTH(no_inet, inet, partial); + TEST_BOTH(no_inet, prefix_only, partial); + TEST_BOTH(no_inet, prefix_only2, partial); + TEST_BOTH(no_inet, prefixed, partial); + TEST_BOTH(no_inet, no_prefix, partial); + + TEST_BOTH(shorter, prefixed, shorter); + TEST_BOTH(shorter, no_prefix, shorter); return 1; } static int -t_s_bgp_state(void) +test_snmp_oid_compare(const void *left, const void *right) { - struct oid *oid = mb_alloc(&root_pool, sizeof(struct oid) + 10 * sizeof(u32)); + return snmp_oid_compare( + *((const struct oid **) left), + *((const struct oid **) right) + ); +} - /* oid header */ - oid->n_subid = 0; - oid->prefix = 2; - oid->include = 0; - oid->pad = 0; +static void +generate_raw_oids(struct oid *oids[], int size, struct oid *(*generator)(void)) +{ + for (int i = 0; i < size; i++) + { + /* binary version of ~5% */ + if (i > 0 && xrandom(256) <= 13) + { + /* at this chance, we create a copy instead of generating new oid */ + oids[i] = tmp_alloc(snmp_oid_size(oids[i-1])); + memcpy(oids[i], oids[i-1], snmp_oid_size(oids[i-1])); + } + else + oids[i] = generator(); + } +} - /* test all states with expected oid length */ - bt_debug("testing precise oids\n"); - test_oid(oid, 0); +static int +generate_oids(struct oid *oids[], struct oid *sorted[], int size, struct oid *(*generator)(void)) +{ + generate_raw_oids(oids, size, generator); - for (int i = 0; i < 10; i++) - oid->ids[i] = (u32) bt_random(); + memcpy(sorted, oids, size * sizeof(struct oid *)); - /* if this subid is too high it does not match the test case - * in general test_oid() func - */ - oid->ids[2] = 0; + qsort(sorted, (size_t) size, sizeof(struct oid *), + test_snmp_oid_compare); - /* test all states with garbage ip */ - bt_debug("testing oids with random ip index\n"); - test_oid(oid, 0); + // test sizes 0, 1, 2, 10, ... + int last_used = 0; + for (int index = 0; index < size; index++) + { + if (snmp_oid_compare(sorted[last_used], sorted[index]) != 0) + sorted[++last_used] = sorted[index]; + } - /* 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; + /* delete old pointers */ + for (int i = last_used + 1; i < size; i++) + sorted[i] = NULL; - oid->ids[5] = 0; - oid->ids[6] = 257; - oid->ids[7] = 127; - oid->ids[8] = 0xFFFF; - test_oid(oid, 0); + return (size > 1) ? last_used + 1 : size; +} - mb_free(oid); +/* checks if the last two oids are same, but one is leaf and the other is not */ +static inline int UNUSED +corner_case(struct oid **oids, int oid_idx, struct oid **leafs, int leaf_idx, int is_leaf) +{ + const struct oid **oids_c = (const struct oid **) oids; + const struct oid **leafs_c = (const struct oid **) leafs; + if (oid_idx == 0) + return 0; + + /* if the current (last) OID from oids is not leaf */ + if (!is_leaf && leaf_idx > 0 && + /* and is same as the last leaf */ + snmp_oid_compare(oids_c[oid_idx], leafs_c[leaf_idx - 1]) == 0) + return 1; /* then return true */ + + + /* if the current (last) OID from oids is a leaf */ + if (is_leaf && oid_idx > 0 && + /* and is same as previous OID */ + snmp_oid_compare(oids_c[oid_idx], oids_c[oid_idx - 1]) == 0) + return 1; /* then return true */ + + return 0; /* false */ +} + +static void UNUSED +print_dups(const struct oid *oids[], uint size) +{ + for (uint i = 0; i < size; i++) + for (uint j = i + 1; j < size; j++) + if (snmp_oid_compare(oids[i], oids[j]) == 0) + log(L_WARN "pair (%u, %u)", i, j); +} + +static void UNUSED +print_all(const struct oid *oids[], uint size) +{ + for (uint i = 0; i < size; i++) + snmp_oid_log(oids[i]); +} + +static inline int +oid_is_leaf(const struct oid *oid, const struct oid *leafs[], uint leaf_idx) +{ + for (uint l = 0; l < leaf_idx; l++) + if (snmp_oid_compare(oid, leafs[l]) == 0) + return 1; + + return 0; +} + +static int +all_invalid(const struct oid *oids[], const byte *invalid, uint size, uint index) +{ + if (!invalid[index]) + return 0; + + for (uint i = 0; i < size; i++) + { + if (i == index) continue; + + if (snmp_oid_compare(oids[i], oids[index]) == 0 && + !invalid[i]) + return 0; + } return 1; } +static int +count_error(const struct oid *oids[], const byte *invalid, uint size) +{ + int error = 0; + for (uint i = 0; i < size; i++) + { + if (!invalid[i]) continue; + + int skip = 0; + for (uint j = 0; j < i; j++) + { + if (snmp_oid_compare(oids[i], oids[j]) == 0) + { + skip = 1; + break; + } + } + + if (skip) continue; + + if (all_invalid(oids, invalid, size, i)) + error++; + } + + return error; +} + +static int +gen_test_add(struct oid *(*generator)(void)) +{ + lp_state tmps; + lp_save(tmp_linpool, &tmps); + + pool *pool = &root_pool; + + for (int test = 0; test < TESTS_NUM; test++) + { + size_t tsz = ARRAY_SIZE(tree_sizes); + + int size = tree_sizes[test % tsz]; + int with_leafs = (test % (2 * tsz)) < tsz; + int no_inet_prefix = (test % (4 * tsz)) < (2 * tsz); + + struct oid **oids = mb_alloc(pool, size * sizeof(struct oid *)); + byte *types = mb_alloc(pool, size * sizeof(byte)); + byte *invalid_hist = mb_alloc(pool, size & sizeof(byte)); + struct oid **sorted = mb_alloc(pool, size * sizeof(struct oid *)); + struct oid **leafs = (with_leafs) ? mb_alloc(pool, size * sizeof(struct oid *)) + : NULL; + int leaf_idx = 0; + int empty_prefix_added = 0; + int distinct = generate_oids(oids, sorted, size, generator); + + struct mib_tree storage, *tree = &storage; + mib_tree_init(pool, tree); + + if (no_inet_prefix) + { + /* remove the node .1 and all children */ + const struct oid *inet_pref = oid_create(1, 0, 0, /* ids */ 1); + mib_tree_remove(tree, inet_pref); + } + + int invalid_counter = 0; + int counter = 0; + int cut = 0; + for (int i = 0; i < size; i++) + { + int invalid = 0; + int is_leaf = (with_leafs) ? (int) xrandom(2) : 0; + types[i] = (byte) is_leaf; + + int will_cut = 0; + int oid_nulled = snmp_is_oid_empty(oids[i]); + + if (oid_nulled && is_leaf) + invalid = 1; + + /* check existence of ancestor node of a new leaf */ + for (int oi = 0; !invalid && !oid_nulled && oi < i; oi++) + { + char buffer[1024]; + struct oid *o = (void *) buffer; + + if (invalid_hist[oi]) + continue; + + int other_is_leaf = (int) types[oi]; + + if (snmp_oid_compare(oids[oi], oids[i]) == 0 && + !snmp_is_oid_empty(oids[i])) + { + if (other_is_leaf == is_leaf) + will_cut = 1; + else if (other_is_leaf != is_leaf) + invalid = 1; + + break; + } + + snmp_oid_common_ancestor(oids[oi], oids[i], o); + + if ((snmp_oid_compare(oids[i], o) == 0 && is_leaf) || + (snmp_oid_compare(oids[oi], o) == 0 && other_is_leaf)) + { + invalid = 1; + break; + } + } + + if (!invalid && will_cut) + cut++; + + if (is_leaf && !invalid) + /* leafs could have duplicates */ + leafs[leaf_idx++] = oids[i]; + + mib_node_u *node = mib_tree_add(pool, tree, oids[i], is_leaf); + + bt_assert((node == NULL) == invalid); + + invalid_hist[i] = 0; + if (invalid) + { + invalid_hist[i] = 1; + invalid_counter++; + } + + if (node != NULL && (!snmp_is_oid_empty(oids[i]) || !empty_prefix_added)) + counter++; + + if (snmp_is_oid_empty(oids[i]) && !is_leaf) + empty_prefix_added = 1; + } + + int error = count_error((const struct oid **) oids, invalid_hist, size); + bt_assert(counter - cut == distinct - error); + + lp_restore(tmp_linpool, &tmps); + mb_free(oids); + mb_free(sorted); + mb_free(leafs); + } + + return 1; +} + +static int +t_tree_add(void) +{ + + gen_test_add(random_prefixed_oid); + gen_test_add(random_no_prefix_oid); + gen_test_add(random_prefixable_oid); + gen_test_add(random_oid); + + return 1; +} + +static int +gen_test_find(struct oid *(*generator)(void)) +{ + lp_state tmps; + lp_save(tmp_linpool, &tmps); + + pool *pool = &root_pool; + + for (int test = 0; test < TESTS_NUM; test++) + { + size_t tsz = ARRAY_SIZE(tree_sizes); + + int size = tree_sizes[test % tsz]; + int with_leafs = (test % (2 * tsz)) < tsz; + int no_inet_prefix = (test % (4 * tsz)) < (2 * tsz); + + struct oid **oids = mb_alloc(pool, size * sizeof(struct oid *)); + mib_node_u **nodes = mb_alloc(pool, size * sizeof(mib_node_u *)); + struct oid **searched = mb_alloc(pool, size * sizeof(struct oid *)); + byte *types = mb_alloc(pool, size * sizeof(byte)); + + /* enough to hold snmp_internet copy */ + uint longest_inet_pref_len = 0; + struct oid *longest_inet_pref = oid_create(4, 0, 0, /* ids */ 0, 0, 0, 0); + + generate_raw_oids(oids, size, generator); + generate_raw_oids(searched, size, generator); + + struct mib_tree storage, *tree = &storage; + mib_tree_init(pool, tree); + + if (no_inet_prefix) + { + /* remove the node .1 and all children */ + const struct oid *inet_pref = oid_create(1, 0, 0, /* ids */ 1); + mib_tree_remove(tree, inet_pref); + } + + for (int i = 0; i < size; i++) + types[i] = (byte) ((with_leafs) ? xrandom(2) : 0); + + /* + * by default initialized MIB tree will have internet prefix have inserted + */ + if (!no_inet_prefix) + { + memcpy(longest_inet_pref->ids, snmp_internet, sizeof(snmp_internet)); + longest_inet_pref_len = 4; + } + + for (int i = 0; i < size; i++) + { + nodes[i] = mib_tree_add(pool, tree, oids[i], types[i]); + + if (nodes[i] == NULL) continue; + + if (snmp_oid_is_prefixed(oids[i])) + { + memcpy(longest_inet_pref->ids, snmp_internet, sizeof(snmp_internet)); + longest_inet_pref_len = 4; + } + else + { + for (uint j = 0; j < MIN(LOAD_U8(oids[i]->n_subid), + ARRAY_SIZE(snmp_internet)); j++) + { + if (LOAD_U32(oids[i]->ids[j]) == snmp_internet[j] && + j >= longest_inet_pref_len) + { + longest_inet_pref->ids[j] = snmp_internet[j]; + longest_inet_pref_len = j + 1; + } + else if (LOAD_U32(oids[i]->ids[j]) == snmp_internet[j]) + ; + else + break; + } + } + } + + for (int i = 0; i < size; i++) + { + for (int j = 0; j < size; j++) + { + if (nodes[i] != NULL && + nodes[j] != NULL && + snmp_oid_compare(oids[i], oids[j]) == 0) + bt_assert(nodes[i] == nodes[j]); + } + } + + for (int i = 0; i < size; i++) + { + /* + * This solves cases where we tried to insert + * both leaf and inner node for same OID. + * Result of insertion should be NULL in cases + * when the insertion is inconsistent with the current tree state. + * (the first insertion wins) + */ + int expected_precise = 1; + mib_node_u *expected = nodes[i]; + for (int j = 0; j < size; j++) + { + if (i == j) continue; + + if (snmp_oid_compare(oids[i], oids[j]) == 0 && + types[i] != types[j] && nodes[i] == NULL) + { + expected = nodes[j]; + break; + } + + char buf[1024]; + struct oid *o = (void *) buf; + + snmp_oid_common_ancestor(oids[i], oids[j], o); + + /* oids[j] lies on path from root to oids[i] */ + if (snmp_oid_compare(oids[i], o) == 0 && + nodes[j] != NULL && + expected == NULL) + { + expected_precise = 0; + break; + } + } + + struct mib_walk_state walk; + mib_tree_walk_init(&walk); + mib_node_u *found = mib_tree_find(tree, &walk, oids[i]); + + if (expected_precise) + bt_assert(found == expected); + else + /* found is an auto-inserted node on path to some dest OID */ + bt_assert(found != NULL); + } + + for (int search = 0; search < size; search++) + { + int has_node = 0; + for (int stored = 0; stored < size; stored++) + { + char buf[1024]; + struct oid *o = (void *) buf; + snmp_oid_common_ancestor(oids[stored], searched[search], o); + + if (nodes[stored] != NULL && snmp_oid_compare(searched[search], o) == 0) + { + has_node = 1; + break; + } + } + + const struct oid *oid = searched[search]; + if (!has_node && !snmp_oid_is_prefixed(oid)) + { + for (uint i = 0; i < MIN(ARRAY_SIZE(snmp_internet), + LOAD_U8(oid->n_subid)); i++) + { + if (longest_inet_pref->ids[i] != 0 && + longest_inet_pref->ids[i] == oid->ids[i]) + has_node = 1; + else + { + has_node = 0; + break; + } + } + + if (has_node && LOAD_U8(oid->n_subid) > ARRAY_SIZE(snmp_internet)) + has_node = 0; + } + + struct mib_walk_state walk; + mib_tree_walk_init(&walk); + mib_node_u *found = mib_tree_find(tree, &walk, searched[search]); + bt_assert(has_node == (found != NULL)); + } + + lp_restore(tmp_linpool, &tmps); + mb_free(oids); + mb_free(nodes); + mb_free(searched); + mb_free(types); + } + + tmp_flush(); + return 1; +} + +static int +t_tree_find(void) +{ + + gen_test_find(random_prefixed_oid); + gen_test_find(random_no_prefix_oid); + gen_test_find(random_prefixable_oid); + gen_test_find(random_oid); + + return 1; +} + +static int +delete_cleanup(const struct oid *oid, struct oid *oids[], mib_node_u *valid[], int size) +{ + uint counter = 0; + for (int i = 0; i < size; i++) + { + char buf[1024]; + struct oid *o = (void *) buf; + + if (oid == oids[i]) + { + counter++; + continue; + } + + snmp_oid_common_ancestor(oid, oids[i], o); + + if (snmp_oid_compare(oid, o) == 0) + { + valid[i] = NULL; + counter++; + } + } + + return counter; +} + +static int +gen_test_delete(struct oid *(*generator)(void)) +{ + lp_state tmps; + lp_save(tmp_linpool, &tmps); + + pool *pool = &root_pool; + + for (int test = 0; test < TESTS_NUM; test++) + { + size_t tsz = ARRAY_SIZE(tree_sizes); + + int size = tree_sizes[test % tsz]; + int with_leafs = (test % (2 * tsz)) < tsz; + int no_iet_prefix = (test % (4 * tsz)) < (2 * tsz); + + struct oid **oids = mb_alloc(pool, size * sizeof(struct oid *)); + mib_node_u **nodes = mb_alloc(pool, size * sizeof(struct mib_node_u *)); + byte *types = mb_alloc(pool, size * sizeof(byte)); + + struct mib_tree storage, *tree = &storage; + mib_tree_init(tree); + + generate_raw_oids(oids, size, generator); + + for (int i = 0; i < size; i++) + { + int is_leaf; + is_leaf = types[i] = (byte) (with_leafs) ? xrandom(2) : 0; + nodes[i] = mib_tree_add(pool, tree, oids[i], is_leaf); + } + + for (int round = 0; round < size / 4; round++) + { + int i = xrandom(size); + + mib_tree_walk_state walk; + mib_tree_walk_walk_init(&walk); + mib_node_u *node = mib_tree_find(tree, walk, oids[i]); + + int deleted = mib_tree_delete(tree, walk); + + int invalid_counter = 0; + for (int j = 0; j < size; j++) + { + if (oids[i] == oids[j]) + { + mib_node_u *node = mib_tree_find(oids[j]); + bt_assert(node == NULL); + invalid_counter++; + continue; + } + + char buf[1024]; + struct oid *o = (void *) buf; + + // TODO check that new invalid oids is == or below the deleted one */ + + mib_node_u *node = mib_tree_find(oids[j]); + if (node != nodes[j]) + { + nodes[j] = NULL; + invalid_counter++; + } + } + + } + + lp_restore(tmp_linpool, &tmps); + mb_free(oids); + mb_free(nodes); + } + + tmp_flush(); + + return 1; +} + +static int +t_tree_delete(void) +{ + + gen_test_delete(random_prefixed_oid); + gen_test_delete(random_no_prefix_oid); + gen_test_delete(random_prefixable_oid); + gen_test_delete(random_oid); + + return 1; +} + +static int +t_tree_remove(void) +{ + return 0; /* failed */ +} + + +static int +t_tree_traversal(void) +{ + return 0; /* failed */ +} + +static int +t_tree_leafs(void) +{ + return 0; /* failed */ +} + +static int +t_tree_all(void) +{ + /* random sequences of insertion/deletion */ + return 0; /* failed */ +} + + int main(int argc, char **argv) { bt_init(argc, argv); - bt_bird_init(); - //bt_test_suite(t_s_bgp_state, "Function snmp_bgp_state()"); + srandom(0x0000fa00); - bt_test_suite(t_s_is_oid_empty, "Function snmp_is_oid_empty()"); + bt_test_suite(t_oid_empty, "Function that determines if the OID is empty"); + bt_test_suite(t_oid_compare, "Function defining lexicographical order on OIDs"); + 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_s_prefixize, "Function snmp_prefixize()"); - - bt_test_suite(t_oid_compare, "Function snmp_oid_compare()"); + bt_test_suite(t_tree_find, "MIB tree search"); + bt_test_suite(t_tree_traversal, "MIB tree traversal"); + bt_test_suite(t_tree_leafs, "MIB tree leafs traversal"); + bt_test_suite(t_tree_add, "MIB tree insertion"); + bt_test_suite(t_tree_delete, "MIB tree removal"); return bt_exit_value(); } diff --git a/proto/snmp/snmp_utils.c b/proto/snmp/snmp_utils.c index 5e706dbb..dc0823de 100644 --- a/proto/snmp/snmp_utils.c +++ b/proto/snmp/snmp_utils.c @@ -9,6 +9,7 @@ */ #include "snmp_utils.h" +#include inline void snmp_pdu_context(struct snmp_pdu *pdu, sock *sk) @@ -45,23 +46,90 @@ snmp_varbind_data(const struct agentx_varbind *vb) return (void *)&vb->name + name_size; } +struct oid * +snmp_varbind_set_name_len(struct snmp_proto *p, struct agentx_varbind **vb, u8 len, struct snmp_pdu *c) +{ + struct oid *oid = &(*vb)->name; + + if (LOAD_U8(oid->n_subid) >= len) + { + c->size += (LOAD_U8(oid->n_subid) - len) * sizeof(u32); + STORE_U8(oid->n_subid, len); + return oid; + } + + /* We need more space */ + ASSUME(len >= LOAD_U8(oid->n_subid)); + uint diff_size = (len - LOAD_U8(oid->n_subid)) * sizeof(u32); + if (c->size < diff_size) + { + snmp_manage_tbuf(p, (void **) vb, c); + oid = &(*vb)->name; + } + + ASSERT(c->size >= diff_size); + c->size -= diff_size; + STORE_U8(oid->n_subid, len); + return &(*vb)->name; +} + +void +snmp_varbind_duplicate_hdr(struct snmp_proto *p, struct agentx_varbind **vb, struct snmp_pdu *c) +{ + ASSUME(vb != NULL && *vb != NULL); + uint hdr_size = snmp_varbind_header_size(*vb); + if (c->size < hdr_size) + snmp_manage_tbuf(p, (void **) vb, c); + + ASSERT(c->size >= hdr_size); + byte *buffer = c->buffer; + ADVANCE(c->buffer, c->size, hdr_size); + memcpy(buffer, *vb, hdr_size); + *vb = (struct agentx_varbind *) buffer; +} + /** * snmp_is_oid_empty - check if oid is null-valued * @oid: object identifier to check * * Test if the oid header is full of zeroes. For NULL-pointer @oid returns 0. + * We ignore include field to prevent weird behaviour. */ -int +inline int snmp_is_oid_empty(const struct oid *oid) { /* We intentionaly ignore padding that should be zeroed */ if (oid != NULL) - return LOAD_U8(oid->n_subid) == 0 && LOAD_U8(oid->prefix) == 0 && - LOAD_U8(oid->include) == 0; + return LOAD_U8(oid->n_subid) == 0 && LOAD_U8(oid->prefix) == 0; + // && LOAD_U8(oid->include) == 0; else return 0; } +/* + * snmp_oid_is_prefixable - check for prefixed form conversion possibility + * @oid: object identfier to check + * + * Check if it is possible to convert @oid to prefixed form. The condition of + * that is standart .1.3.6.1 internet prefix and 5-th id that fits in one byte. + */ +inline int +snmp_oid_is_prefixable(const struct oid *oid) +{ + if (oid->n_subid < 5) + return 0; + + for (int i = 0; i < 4; i++) + if (LOAD_U32(oid->ids[i]) != snmp_internet[i]) + return 0; + + if (LOAD_U32(oid->ids[4]) >= 256) + return 0; + + return 1; +} + + /** * snmp_pkt_len - returns size of SNMP packet payload (without header) * @buf: packet first byte @@ -90,6 +158,20 @@ snmp_oid_copy(struct oid *dest, const struct oid *src) STORE_U32(dest->ids[i], src->ids[i]); } +/* this function assumes enougth space inside dest is allocated */ +void +snmp_oid_copy2(struct oid *dest, const struct oid *src) +{ + /* The STORE_U8() and LOAD_U8() cancel out */ + dest->n_subid = src->n_subid; + dest->prefix = src->prefix; + dest->include = src->include ? 1 : 0; + dest->reserved = 0; + + /* The STORE_U32() and LOAD_U32 cancel out */ + memcpy(dest->ids, src->ids, LOAD_U8(src->n_subid) * sizeof(u32)); +} + /* * snmp_oid_duplicate - duplicate an OID from memory pool * @pool: pool to use @@ -181,6 +263,7 @@ snmp_set_varbind_type(struct agentx_varbind *vb, enum agentx_type t) { ASSUME(t != AGENTX_INVALID); STORE_U16(vb->type, t); + STORE_U16(vb->reserved, 0); } /* Internal wrapper */ @@ -470,7 +553,8 @@ byte * snmp_put_ip4(byte *buf, ip4_addr addr) { /* octet string has size 4 bytes */ - STORE_PTR(buf, 4); + STATIC_ASSERT(sizeof(ip4_addr) == sizeof(u32)); + STORE_PTR(buf, sizeof(ip4_addr)); /* Always use Network byte order */ put_u32(buf+4, ip4_to_u32(addr)); @@ -544,46 +628,74 @@ snmp_oid_ip4_index(struct oid *o, uint start, ip4_addr addr) int snmp_oid_compare(const struct oid *left, const struct oid *right) { - if (left->prefix == 0 && right->prefix == 0) + const u8 left_subids = LOAD_U8(left->n_subid); + const u8 right_subids = LOAD_U8(right->n_subid); + + const u8 left_prefix = LOAD_U8(left->prefix); + const u8 right_prefix = LOAD_U8(right->prefix); + + if (left_prefix == 0 && right_prefix == 0) goto test_ids; - if (right->prefix == 0) + if (right_prefix == 0) return (-1) * snmp_oid_compare(right, left); - if (left->prefix == 0) + if (left_prefix == 0) { - for (int i = 0; i < 4; i++) - if (left->ids[i] < snmp_internet[i]) + size_t bound = MIN((size_t) left_subids, ARRAY_SIZE(snmp_internet)); + for (size_t idx = 0; idx < bound; idx++) + { + u32 id = LOAD_U32(left->ids[idx]); + if (id < snmp_internet[idx]) return -1; - else if (left->ids[i] > snmp_internet[i]) + else if (id > snmp_internet[idx]) return 1; + } - for (int i = 0; i < MIN(left->n_subid - 4, right->n_subid); i++) - if (left->ids[i + 4] < right->ids[i]) + if (left_subids <= ARRAY_SIZE(snmp_internet)) + return -1; + + if (LOAD_U32(left->ids[4]) < (u32) right_prefix) + return -1; + else if (LOAD_U32(left->ids[4]) > (u32) right_prefix) + return 1; + + int limit = MIN(left_subids - (int) ARRAY_SIZE(snmp_internet), + (int) right_subids); + for (int i = 0; i < limit; i++) + { + u32 left_id = LOAD_U32(left->ids[i + ARRAY_SIZE(snmp_internet)]); + u32 right_id = LOAD_U32(right->ids[i]); + if (left_id < right_id) return -1; - else if (left->ids[i + 4] > right->ids[i]) + else if (left_id > right_id) return 1; + } goto all_same; } - if (left->prefix < right->prefix) + if (left_prefix < right_prefix) return -1; - else if (left->prefix > right->prefix) + else if (left_prefix > right_prefix) return 1; test_ids: for (int i = 0; i < MIN(left->n_subid, right->n_subid); i++) - if (left->ids[i] < right->ids[i]) + { + u32 left_id = LOAD_U32(left->ids[i]); + u32 right_id = LOAD_U32(right->ids[i]); + if (left_id < right_id) return -1; - else if (left->ids[i] > right->ids[i]) + else if (left_id > right_id) return 1; + } all_same: /* shorter sequence is before longer in lexicografical order */ - if (left->n_subid < right->n_subid) + if (left_subids < right_subids) return -1; - else if (left->n_subid > right->n_subid) + else if (left_subids > right_subids) return 1; else return 0; @@ -665,60 +777,52 @@ agentx_type_size(enum agentx_type type) return -1; } -static inline byte * -snmp_varbind_type32(struct agentx_varbind *vb, uint size, enum agentx_type type, u32 val) +static inline void +snmp_varbind_type32(struct agentx_varbind *vb, struct snmp_pdu *c, enum agentx_type type, u32 val) { - ASSUME(agentx_type_size(type) == 4); /* type has 4B representation */ - - if (size < (uint) agentx_type_size(type)) - return NULL; + ASSUME(agentx_type_size(type) == 4); /* type as 4B representation */ snmp_set_varbind_type(vb, type); u32 *data = snmp_varbind_data(vb); - STORE_PTR(data, val); /* note that the data has u32 type */ + STORE_PTR(data, val); data++; - return (byte *) data; + c->buffer = (byte *) data; } -inline byte * -snmp_varbind_int(struct agentx_varbind *vb, uint size, u32 val) +inline void +snmp_varbind_int(struct agentx_varbind *vb, struct snmp_pdu *c, u32 val) { - return snmp_varbind_type32(vb, size, AGENTX_INTEGER, val); + snmp_varbind_type32(vb, c, AGENTX_INTEGER, val); } - -inline byte * -snmp_varbind_counter32(struct agentx_varbind *vb, uint size, u32 val) +inline void +snmp_varbind_counter32(struct agentx_varbind *vb, struct snmp_pdu *c, u32 val) { - return snmp_varbind_type32(vb, size, AGENTX_COUNTER_32, val); + snmp_varbind_type32(vb, c, AGENTX_COUNTER_32, val); } -inline byte * -snmp_varbind_ticks(struct agentx_varbind *vb, uint size, u32 val) +inline void +snmp_varbind_ticks(struct agentx_varbind *vb, struct snmp_pdu *c, u32 val) { - return snmp_varbind_type32(vb, size, AGENTX_TIME_TICKS, val); + snmp_varbind_type32(vb, c, AGENTX_TIME_TICKS, val); } -inline byte * -snmp_varbind_gauge32(struct agentx_varbind *vb, uint size, s64 val) +inline void +snmp_varbind_gauge32(struct agentx_varbind *vb, struct snmp_pdu *c, s64 time) { - return snmp_varbind_type32(vb, size, AGENTX_GAUGE_32, - MAX(0, MIN(val, UINT32_MAX))); + snmp_varbind_type32(vb, c, AGENTX_GAUGE_32, MAX(0, MIN(time, UINT32_MAX))); } -inline byte * -snmp_varbind_ip4(struct agentx_varbind *vb, uint size, ip4_addr addr) +inline void +snmp_varbind_ip4(struct agentx_varbind *vb, struct snmp_pdu *c, ip4_addr addr) { - if (size < snmp_str_size_from_len(4)) - return NULL; - snmp_set_varbind_type(vb, AGENTX_IP_ADDRESS); - return snmp_put_ip4(snmp_varbind_data(vb), addr); + c->buffer = snmp_put_ip4(snmp_varbind_data(vb), addr); } // TODO doc string, we have already the varbind prepared inline byte * -snmp_varbind_nstr(struct agentx_varbind *vb, uint size, const char *str, uint len) +snmp_varbind_nstr2(struct agentx_varbind *vb, uint size, const char *str, uint len) { if (size < snmp_str_size_from_len(len)) return NULL; @@ -727,6 +831,24 @@ snmp_varbind_nstr(struct agentx_varbind *vb, uint size, const char *str, uint le return snmp_put_nstr(snmp_varbind_data(vb), str, len); } +/* + * snmp_varbind_nstr - fill varbind context with octet string + * @vb: VarBind to use + * @c: PDU information + * @str: C-string to put as the VarBind data + * @len: length of the string @str + * + * Beware: this function assumes there is enough space in the underlaying + * TX-buffer. The caller has to provide that, see snmp_str_size_from_len() for + * more info. + */ +void +snmp_varbind_nstr(struct agentx_varbind *vb, struct snmp_pdu *c, const char *str, uint len) +{ + snmp_set_varbind_type(vb, AGENTX_OCTET_STRING); + c->buffer = snmp_put_nstr(snmp_varbind_data(vb), str, len); +} + inline enum agentx_type snmp_search_res_to_type(enum snmp_search_res r) { @@ -786,3 +908,124 @@ snmp_oid_dump(const struct oid *oid) log(L_WARN "OID DUMP END ===="); log(L_WARN); } + +void UNUSED +snmp_oid_log(const struct oid *oid) +{ + char buf[1024] = { }; + char *pos = buf; + + if (snmp_oid_is_prefixed(oid)) + { + for (uint i = 0; i < ARRAY_SIZE(snmp_internet); i++) + pos += snprintf(pos, buf + 1024 - pos, ".%u", snmp_internet[i]); + + pos += snprintf(pos, buf + 1024 - pos, ".%u", oid->prefix); + } + + 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_oid_common_ancestor - find a common ancestor + * @left: first OID + * @right: second OID + * @out: buffer for result + * + * The @out must be large enough to always fit the resulting OID, a safe value + * is minimum between number of left subids and right subids. The result might + * be NULL OID in cases where there is no common subid. The result could be also + * viewed as longest common prefix. + */ +void +snmp_oid_common_ancestor(const struct oid *left, const struct oid *right, struct oid *out) +{ + ASSERT(left && right && out); + + STORE_U8(out->include, 0); + STORE_U8(out->reserved, 0); + + u32 offset = 0; + u8 left_ids = LOAD_U8(left->n_subid), right_ids = LOAD_U8(right->n_subid); + + int l = snmp_oid_is_prefixed(left), r = snmp_oid_is_prefixed(right); + if (l && r) + { + if (LOAD_U8(left->prefix) != LOAD_U8(right->prefix)) + { + STORE_U8(out->n_subid, 4); + STORE_U8(out->prefix, 0); + + for (uint id = 0; id < ARRAY_SIZE(snmp_internet); id++) + STORE_U32(out->ids[id], snmp_internet[id]); + + return; + } + + STORE_U8(out->prefix, LOAD_U8(left->prefix)); + } + else if (!l && r) + { + if (left_ids == 0) + { + /* finish creating NULL OID */ + STORE_U8(out->n_subid, 0); + STORE_U8(out->prefix, 0); + return; + } + + for (uint id = 0; id < MIN(ARRAY_SIZE(snmp_internet), left_ids); id++) + { + if (LOAD_U32(left->ids[id]) != snmp_internet[id]) + { + STORE_U8(out->n_subid, id); + STORE_U8(out->prefix, 0); + return; + } + + STORE_U32(out->ids[id], snmp_internet[id]); + } + + if (left_ids <= ARRAY_SIZE(snmp_internet)) + { + STORE_U8(out->n_subid, left_ids); + return; + } + + /* index 4 is conresponding to the prefix in prefixed OID */ + if (LOAD_U32(left->ids[4]) != (u32) LOAD_U8(right->prefix)) + { + STORE_U8(out->n_subid, ARRAY_SIZE(snmp_internet)); + return; + } + + /* delete snmp_internet from out->ids and store OID prefix */ + offset = ARRAY_SIZE(snmp_internet) + 1; + STORE_U8(out->n_subid, LOAD_U8(out->n_subid) - ARRAY_SIZE(snmp_internet)); + STORE_U8(out->prefix, LOAD_U8(right->prefix)); + } + else if (l && !r) + { + snmp_oid_common_ancestor(right, left, out); + return; + } + + ASSERT(offset <= left_ids); + + u8 subids = 0; + for (u32 id = 0; id < MIN(left_ids - offset, right_ids); id++) + { + if (left->ids[offset + id] == right->ids[id]) + { + subids++; + STORE_U32(out->ids[id], LOAD_U32(right->ids[id])); + } + else + break; + } + STORE_U8(out->n_subid, subids); +} diff --git a/proto/snmp/snmp_utils.h b/proto/snmp/snmp_utils.h index 3026f77a..c01d82fb 100644 --- a/proto/snmp/snmp_utils.h +++ b/proto/snmp/snmp_utils.h @@ -24,10 +24,19 @@ size_t snmp_str_size(const char *str); /* type OID - Object Identifier */ int snmp_is_oid_empty(const struct oid *oid); +int snmp_oid_is_prefixable(const struct oid *oid); uint snmp_oid_size(const struct oid *o); size_t snmp_oid_size_from_len(uint n_subid); void snmp_oid_copy(struct oid *dest, const struct oid *src); +void snmp_oid_copy2(struct oid *dest, const struct oid *src); int snmp_oid_compare(const struct oid *first, const struct oid *second); +void snmp_oid_common_ancestor(const struct oid *left, const struct oid *right, struct oid *result); + +static inline int +snmp_oid_is_prefixed(const struct oid *oid) +{ + return LOAD_U8(oid->prefix) != 0; +} /* type IPv4 */ int snmp_valid_ip4_index(const struct oid *o, uint start); @@ -44,6 +53,8 @@ uint snmp_varbind_size_unsafe(const struct agentx_varbind *vb); size_t snmp_varbind_size_from_len(uint n_subid, enum agentx_type t, uint len); int snmp_test_varbind(const struct agentx_varbind *vb); void *snmp_varbind_data(const struct agentx_varbind *vb); +struct oid *snmp_varbind_set_name_len(struct snmp_proto *p, struct agentx_varbind **vb, u8 len, struct snmp_pdu *c); +void snmp_varbind_duplicate_hdr(struct snmp_proto *p, struct agentx_varbind **vb, struct snmp_pdu *c); /* * AgentX - PDU headers, types, contexts @@ -63,12 +74,12 @@ int snmp_test_close_reason(byte value); /* Functions filling buffer a typed value */ struct agentx_varbind *snmp_create_varbind(byte *buf, struct oid *oid); struct agentx_varbind *snmp_create_varbind_null(byte *buf); -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_ticks(struct agentx_varbind *vb, uint size, u32 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_varbind_int(struct agentx_varbind *vb, struct snmp_pdu *c, u32 val); +void snmp_varbind_counter32(struct agentx_varbind *vb, struct snmp_pdu *c, u32 val); +void snmp_varbind_gauge32(struct agentx_varbind *vb, struct snmp_pdu *c, s64 time); +void snmp_varbind_ticks(struct agentx_varbind *vb, struct snmp_pdu *c, u32 val); +void snmp_varbind_ip4(struct agentx_varbind *vb, struct snmp_pdu *c, ip4_addr addr); +void snmp_varbind_nstr(struct agentx_varbind *vb, struct snmp_pdu *c, const char *str, uint len); /* Raw */ byte *snmp_no_such_object(byte *buf, struct agentx_varbind *vb, struct oid *oid); @@ -91,7 +102,12 @@ int snmp_registration_match(struct snmp_registration *r, struct agentx_header *h void snmp_dump_packet(byte *pkt, uint size); void snmp_oid_dump(const struct oid *oid); +void snmp_oid_log(const struct oid *oid); enum agentx_type snmp_search_res_to_type(enum snmp_search_res res); +#define AGENTX_TYPE_INT_SIZE ((uint) agentx_type_size(AGENTX_INTEGER)) +#define AGENTX_TYPE_IP4_SIZE ((uint) agentx_type_size(AGENTX_IP_ADDRESS)) +#define AGENTX_TYPE_COUNTER32_SIZE ((uint) agentx_type_size(AGENTX_COUNTER_32)) + #endif diff --git a/proto/snmp/splitter.py b/proto/snmp/splitter.py index ebd4f342..052777f0 100755 --- a/proto/snmp/splitter.py +++ b/proto/snmp/splitter.py @@ -2,7 +2,8 @@ """ A very simple script used to test that BIRD does not segfaults on randomly split -AgentX PDUs. +AgentX PDUs. A split is when the AgentX PDUs do not align with socket's +write()/read() boundaries. """ import socket diff --git a/proto/snmp/subagent.c b/proto/snmp/subagent.c index c25155b5..0892e890 100644 --- a/proto/snmp/subagent.c +++ b/proto/snmp/subagent.c @@ -61,16 +61,19 @@ * */ -static void snmp_mib_fill2(struct snmp_proto *p, struct oid *oid, struct snmp_pdu *c); +//static void snmp_mib_fill(struct snmp_proto *p, struct oid *oid, struct snmp_pdu *c); +static void snmp_mib_fill(struct snmp_proto *p, struct agentx_varbind **vb, struct snmp_pdu *c); static uint parse_response(struct snmp_proto *p, byte *buf); static void do_response(struct snmp_proto *p, byte *buf); static uint parse_gets_pdu(struct snmp_proto *p, byte *pkt); static struct agentx_response *prepare_response(struct snmp_proto *p, struct snmp_pdu *c); static void response_err_ind(struct snmp_proto *p, struct agentx_response *res, enum agentx_response_errs err, u16 ind); static uint update_packet_size(struct agentx_header *start, byte *end); -static struct oid *search_mib(struct snmp_proto *p, const struct oid *o_start, const struct oid *o_end, struct oid *o_curr, struct snmp_pdu *c, enum snmp_search_res *result); +//static struct oid *search_mib2(struct snmp_proto *p, const struct oid *o_start, const struct oid *o_end, struct oid *o_curr, struct snmp_pdu *c, enum snmp_search_res *result); -u32 snmp_internet[] = { SNMP_ISO, SNMP_ORG, SNMP_DOD, SNMP_INTERNET }; +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); + +const u32 snmp_internet[] = { SNMP_ISO, SNMP_ORG, SNMP_DOD, SNMP_INTERNET }; static inline int snmp_is_active(struct snmp_proto *p) @@ -244,7 +247,7 @@ 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_manage_tbuf(p, &c); + snmp_manage_tbuf(p, NULL, &c); struct agentx_header *h = (void *) c.buffer; ADVANCE(c.buffer, c.size, AGENTX_HEADER_SIZE); @@ -303,7 +306,7 @@ 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_manage_tbuf(p, &c); + snmp_manage_tbuf(p, NULL, &c); struct agentx_header *h = (void *) c.buffer; ADVANCE(c.buffer, c.size, AGENTX_HEADER_SIZE); @@ -329,7 +332,7 @@ snmp_notify_pdu(struct snmp_proto *p, struct oid *oid, void *data, uint size, in /* TODO use time from last reconfiguration instead? [config->load_time] */ btime uptime = current_time() - boot_time; - snmp_varbind_ticks(vb, c.size, (uptime TO_S) / 100); + snmp_varbind_ticks(vb, &c, (uptime TO_S) / 100); ASSUME(snmp_test_varbind(vb)); ADVANCE(c.buffer, c.size, snmp_varbind_size_unsafe(vb)); } @@ -398,7 +401,7 @@ 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_manage_tbuf(p, &c); + snmp_manage_tbuf(p, NULL, &c); struct agentx_header *h = (void *) c.buffer; ADVANCE(c.buffer, c.size, AGENTX_HEADER_SIZE); @@ -480,7 +483,7 @@ 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_manage_tbuf(p, &c); + snmp_manage_tbuf(p, NULL, &c); struct agentx_header *h = (void *) c.buffer; ADVANCE(c.buffer, c.size, AGENTX_HEADER_SIZE); @@ -611,7 +614,7 @@ parse_test_set_pdu(struct snmp_proto *p, byte * const pkt_start) snmp_pdu_context(&c, sk); if (c.size < AGENTX_HEADER_SIZE) - snmp_manage_tbuf(p, &c); + snmp_manage_tbuf(p, NULL, &c); res = prepare_response(p, &c); @@ -708,7 +711,7 @@ 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_manage_tbuf(p, &c); + snmp_manage_tbuf(p, NULL, &c); struct agentx_response *r = prepare_response(p, &c); @@ -1069,11 +1072,67 @@ snmp_get_mib_class(const struct oid *oid) * Return 0 if the created VarBind type is endOfMibView, 1 otherwise. */ static int -snmp_get_next2(struct snmp_proto *p, struct oid *o_start, struct oid *o_end, +snmp_get_next2(struct snmp_proto *p, struct agentx_varbind **vb_search, struct oid *o_end, struct snmp_pdu *c) +{ + enum snmp_search_res r; + + //struct oid *o_copy = search_mib(p, o_start, o_end, NULL, c, &r); + r = search_mib(p, vb_search, o_end, c); + struct oid *o_start = &(*vb_search)->name; + + switch (r) + { + case SNMP_SEARCH_NO_OBJECT: + case SNMP_SEARCH_NO_INSTANCE: + case SNMP_SEARCH_END_OF_VIEW:; + snmp_set_varbind_type(*vb_search, AGENTX_END_OF_MIB_VIEW); + return 0; + + case SNMP_SEARCH_OK: + default: + break; + } + + // TODO TODO different API + snmp_mib_fill(p, vb_search, c); + + /* override the error for GetNext-PDU object not find */ + switch ((*vb_search)->type) + { + case AGENTX_NO_SUCH_OBJECT: + case AGENTX_NO_SUCH_INSTANCE: + case AGENTX_END_OF_MIB_VIEW: + (*vb_search)->type = AGENTX_END_OF_MIB_VIEW; + return 0; + + default: + return 1; + } + + o_start = &(*vb_search)->name; + if (c->size < snmp_varbind_hdr_size_from_oid(o_start)) + snmp_manage_tbuf(p, (void **) vb_search, c); + + snmp_set_varbind_type(*vb_search, AGENTX_END_OF_MIB_VIEW); + return 0; +} + +#if 0 +/* + * snmp_get_next - process single agentx-GetNext-PDU search range + * @p: SNMP protocol instance + * @o_start: SearchRange start OID + * @o_end: SearchRange end OID + * @c: transmit PDU context to use + * + * Return 0 if the created VarBind type is endOfMibView, 1 otherwise. + */ +static int +snmp_get_next3(struct snmp_proto *p, struct oid *o_start, struct oid *o_end, struct snmp_pdu *c) { enum snmp_search_res r; - struct oid *o_copy = search_mib(p, o_start, o_end, NULL, c, &r); + struct oid *o_copy = search_mib2(p, o_start, o_end, NULL, c, &r); struct agentx_varbind *vb = NULL; switch (r) @@ -1110,7 +1169,7 @@ snmp_get_next2(struct snmp_proto *p, struct oid *o_start, struct oid *o_end, { /* basicaly snmp_create_varbind(c->buffer, o_copy), but without any copying */ vb = (void *) c->buffer; - snmp_mib_fill2(p, o_copy, c); + snmp_mib_fill(p, o_copy, c); /* override the error for GetNext-PDU object not find */ switch (vb->type) @@ -1127,14 +1186,16 @@ snmp_get_next2(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); + snmp_manage_tbuf(p, (void **) vb, c); vb = snmp_create_varbind(c->buffer, o_start); vb->type = AGENTX_END_OF_MIB_VIEW; ADVANCE(c->buffer, c->size, snmp_varbind_header_size(vb)); return 0; } +#endif +#if 0 /* * snmp_get_bulk - process one iteration of get bulk PDU * @p: SNMP protocol instance @@ -1183,8 +1244,8 @@ snmp_get_bulk2(struct snmp_proto *p, struct oid *o_start, struct oid *o_end, vb->type = AGENTX_END_OF_MIB_VIEW; if (r == SNMP_SEARCH_OK) - /* the varbind will be recreated inside the snmp_mib_fill2() */ - snmp_mib_fill2(p, o_curr, c); + /* the varbind will be recreated inside the snmp_mib_fill() */ + snmp_mib_fill(p, o_curr, c); else ADVANCE(c->buffer, c->size, snmp_varbind_header_size(vb)); @@ -1201,6 +1262,182 @@ snmp_get_bulk2(struct snmp_proto *p, struct oid *o_start, struct oid *o_end, return 1; } } +#endif + +#if 0 +static int +snmp_get_bulk2(struct snmp_proto *p, struct agentx_varbind **vb_search, const struct oid *o_end, struct agentx_bulk_state *state, struct snmp_pdu *c) +{ + //struct oid *o_curr = NULL; + //struct oid *o_predecessor = NULL; + enum snmp_search_res r; + + uint i = 0; + r = search_mib(p, vb_search, o_end, c); + while (r != SNMP_SEARCH_END_OF_VIEW && i < state->repetition) + { + snmp_mib_fill(p, vb_search, c); + + snmp_varbind_duplicate_hdr(p, vb_search, c); + // renew all pointers here + r = search_mib(p, vb_search, o_end, c); + } + + return r == SNMP_SEARCH_END_OF_VIEW; + do + { + r = search_mib(p, vb_search, o_end, c); + snmp_mib_fill(p, vb_search, ; + o_predecessor = o_curr; + o_curr = search_mib(p, o_start, o_end, o_curr, c, &r); + i++; + } while (r != SNMP_SEARCH_END_OF_VIEW && i < state->repetition); + + // TODO check if the approach below works + // it need to generate varbinds that will be only of type endOfMibView + /* Object Identifier fall-backs */ + if (!o_curr) + o_curr = o_predecessor; + + if (!o_curr) + o_curr = o_start; + + uint sz = snmp_varbind_hdr_size_from_oid(o_curr); + + if (c->size < sz) + { + c->error = AGENTX_RES_GEN_ERROR; + return 0; + } + + /* we need the varbind handle to be able to override it's type */ + struct agentx_varbind *vb = (void *) c->buffer; + vb->type = AGENTX_END_OF_MIB_VIEW; + + if (r == SNMP_SEARCH_OK) + /* the varbind will be recreated inside the snmp_mib_fill() */ + snmp_mib_fill(p, o_curr, c); + else + ADVANCE(c->buffer, c->size, snmp_varbind_header_size(vb)); + + /* override the error for GetBulk-PDU object not found */ + switch (vb->type) + { + case AGENTX_NO_SUCH_OBJECT: + case AGENTX_NO_SUCH_INSTANCE: + case AGENTX_END_OF_MIB_VIEW: + vb->type = AGENTX_END_OF_MIB_VIEW; + return 0; + + default: + return 1; + } +} +#endif + +static inline struct oid * +snmp_oid_prefixize_unsafe(struct oid *dest, const struct oid *src) +{ + u8 subids = LOAD_U8(src->n_subid) - 5; + dest->n_subid = subids; + STORE_U8(dest->prefix, (u8) LOAD_U32(src->ids[ARRAY_SIZE(snmp_internet)])); + STORE_U8(dest->include, (LOAD_U8(src->include)) ? 1 : 0); + STORE_U8(dest->reserved, 0); + + /* The LOAD_U32() and STORE_U32() cancel out */ + memcpy(&dest->ids[0], &src->ids[5], subids * sizeof(u32)); + + return dest; +} + +#if 0 +/* + * snmp_oid_prefixize - convert oid to prefixed form + * @p: SNMP protocol instance + * @oid: object identifier to convert + * @c: PDU context + * + * The function assumes that the supplied @oid is prefixable. The resulting OID + * is allocated from PDU buffer inside @c. + */ +struct oid * +snmp_oid_prefixize(struct snmp_proto *p, const struct oid *oid, struct snmp_pdu *c) +{ + /* It may be beneficial to move to snmp_utils.c */ + uint subids = LOAD_U8(oid->n_subid) - 5; + uint oid_size = snmp_oid_size(oid); + + if (c->size < oid_size) + snmp_manage_tbuf(p, NULL, c); + + // TODO check if the @oid is prefixable + ASSERT(c->size >= oid_size); + struct oid *result = c->buffer; + ADVANCE(c->buffer, c->size, oid_size); + return snmp_oid_prefixize_unsafe(result, oid); +} +#endif + +/* + * snmp_vb_to_tx - create varbind from RX buffer OID + * @p: SNMP protocol instance + * @oid: object identifier located in RX buffer + * @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. + */ +struct agentx_varbind * +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_manage_tbuf(p, NULL, c); + + ASSERT(c->size >= vb_hdr_size); + struct agentx_varbind *vb = (void *) c->buffer; + ADVANCE(c->buffer, c->size, sizeof(struct agentx_varbind) - sizeof(struct oid)); + /* Move the c->buffer so that is points at &vb->name */ + snmp_set_varbind_type(vb, AGENTX_NULL); + + if (snmp_oid_is_prefixable(oid) && !snmp_oid_is_prefixed(oid)) + { + 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; + } + + 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; +} /* * update_packet_size - set PDU size @@ -1252,7 +1489,7 @@ response_err_ind(struct snmp_proto *p, struct agentx_response *res, enum agentx_ STORE_U32(res->index, 0); } -static uint +static inline uint parse_gets_error(struct snmp_proto *p, struct snmp_pdu *c, uint len) { TRACE(D_PACKETS, "SNMP error %u while parsing gets PDU", c->error); @@ -1276,7 +1513,9 @@ static uint parse_gets_pdu(struct snmp_proto *p, byte * const pkt_start) { // TODO checks for c.size underflow - struct oid *o_start = NULL, *o_end = NULL; + //struct agentx_varbind *vb = NULL; + struct agentx_varbind *vb_start = NULL; + struct oid *o_end = NULL; byte *pkt = pkt_start; struct agentx_header *h = (void *) pkt; @@ -1318,12 +1557,16 @@ parse_gets_pdu(struct snmp_proto *p, byte * const pkt_start) struct agentx_response *response_header = prepare_response(p, &c); + lp_state tmps; + lp_save(tmp_linpool, &tmps); while (c.error == AGENTX_RES_NO_ERROR && pkt_size > 0) { + lp_restore(tmp_linpool, &tmps); + /* We load search range start OID */ - const struct oid *o_start_b = (void *) pkt; + const struct oid *o_start_rx = (void *) pkt; uint sz; - if ((sz = snmp_oid_size(o_start_b)) > pkt_size) + if ((sz = snmp_oid_size(o_start_rx)) > pkt_size) { c.error = AGENTX_RES_PARSE_ERROR; return parse_gets_error(p, &c, pkt_size); @@ -1337,8 +1580,8 @@ parse_gets_pdu(struct snmp_proto *p, byte * const pkt_start) * The exactly same process of sanity checking is preformed while loading * the SearchRange's end OID */ - const struct oid *o_end_b = (void *) pkt; - if ((sz = snmp_oid_size(o_end_b)) > pkt_size) + 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); @@ -1349,17 +1592,15 @@ parse_gets_pdu(struct snmp_proto *p, byte * const pkt_start) /* 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 */ - o_start = snmp_prefixize(p, o_start_b); - o_end = snmp_prefixize(p, o_end_b); + vb_start = snmp_vb_to_tx(p, o_start_rx, &c); + o_end = snmp_oid_to_scratch(o_end_rx); - ASSERT(o_start); + ASSERT(vb_start); ASSERT(o_end); - if (!snmp_is_oid_empty(o_end) && snmp_oid_compare(o_start, o_end) > 0) + if (!snmp_is_oid_empty(o_end) && snmp_oid_compare(&vb_start->name, o_end) > 0) { c.error = AGENTX_RES_GEN_ERROR; - mb_free(o_start); - mb_free(o_end); return parse_gets_error(p, &c, pkt_size); } @@ -1367,11 +1608,11 @@ parse_gets_pdu(struct snmp_proto *p, byte * const pkt_start) switch (h->type) { case AGENTX_GET_PDU: - snmp_mib_fill2(p, o_start, &c); + snmp_mib_fill(p, &vb_start, &c); break; case AGENTX_GET_NEXT_PDU: - snmp_get_next2(p, o_start, o_end, &c); + snmp_get_next2(p, &vb_start, o_end, &c); break; case AGENTX_GET_BULK_PDU: @@ -1382,35 +1623,35 @@ parse_gets_pdu(struct snmp_proto *p, byte * const pkt_start) /* The behavior of GetBulk pdu in the first iteration is * identical to GetNext pdu. */ - has_any = snmp_get_next2(p, o_start, o_end, &c) | has_any; + has_any = snmp_get_next2(p, &vb_start, o_end, &c) || has_any; break; default: die("incorrect usage"); } - mb_free(o_start); - o_start = NULL; - mb_free(o_end); + vb_start = NULL; o_end = NULL; c.index++; } /* while (c.error == AGENTX_RES_NO_ERROR && size > 0) */ + lp_restore(tmp_linpool, &tmps); + if (h->type == AGENTX_GET_BULK_PDU) { + #if 0 for (bulk_state.repetition++; has_any && bulk_state.repetition < bulk_state.getbulk.max_repetitions; bulk_state.repetition++) { - // TODO find propper start and end - struct oid *start = NULL; - struct oid *end = NULL; + vb_start = snmp_vb_copy_to_tx(p, vb_start, &c); has_any = 0; for (bulk_state.index = 0; bulk_state.index < bulk_state.repeaters; bulk_state.repeaters++) - has_any = snmp_get_bulk2(p, start, end, &bulk_state, &c) || has_any; + has_any = snmp_get_bulk2(p, &vb_start, end, &bulk_state, &c) || has_any; } + #endif } /* We update the error, index pair on the beginning of the packet. */ @@ -1422,8 +1663,6 @@ parse_gets_pdu(struct snmp_proto *p, byte * const pkt_start) // TODO think through the error state /* number of bytes parsed from RX-buffer */ - mb_free(o_start); - mb_free(o_end); return pkt - pkt_start; } @@ -1589,8 +1828,74 @@ snmp_search_check_end_oid(const struct oid *found, const struct oid *bound) */ /* tree is tree with "internet" prefix .1.3.6.1 working only with o_start, o_end allocated in heap (not from buffer)*/ +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) +{ + ASSUME(vb_search != NULL); + struct oid *o_start = &(*vb_search)->name; + ASSUME(o_start != NULL); + + (void)p; + (void)o_end; + (void)c; + // TODO TODO +#if 0 + enum snmp_search_res r; + switch (o_curr->ids[1]) + { + case SNMP_BGP4_MIB: + r = snmp_bgp_search(p, &o_curr, o_end, 0); + + if (r == SNMP_SEARCH_OK) + { + *result = r; + break; + return o_curr; + } + + // TODO add early break for o_end less then thinkable maximum in each tree + + /* fall through */ + + default: + if (o_curr) mb_free(o_curr); + o_curr = snmp_oid_duplicate(p->pool, o_start); + *result = SNMP_SEARCH_END_OF_VIEW; + break; + } + + if (o_end == blank) + /* cast drops const qualifier */ + mb_free((struct oid *)blank); + + return o_curr; +#endif + return SNMP_SEARCH_NO_OBJECT; +} + +/* + * search_mib - search for successor of given OID + * @p: SNMP protocol instance + * @o_start: search starting OID + * @o_end: search ending OID + * @o_curr: current OID inside @o_start, @o_end interval + * @c: transmit PDU context to use + * @result: search result state + * + * Perform a search in MIB tree in SearchRange from @o_start to @o_end. + * If the @o_start has set include the search is inclusive, the @o_end has + * always the include flag cleared. For agentx-GetNext-PDU, the o_curr is always + * NULL, for agentx-GetBulk-PDU it could have non-NULL value. In such case the + * @o_curr effectively replaces the role of @o_start. It is mandatory to pass + * @o_start and @o_end only allocated from @p protocol's memory pool. + * + * Return found OID or NULL. + */ +/* tree is tree with "internet" prefix .1.3.6.1 + working only with o_start, o_end allocated in heap (not from buffer)*/ +#if 0 static struct oid * -search_mib(struct snmp_proto *p, const struct oid *o_start, const struct oid *o_end, +search_mib2(struct snmp_proto *p, const struct oid *o_start, const struct oid *o_end, struct oid *o_curr, struct snmp_pdu UNUSED *c, enum snmp_search_res *result) { @@ -1647,7 +1952,11 @@ search_mib(struct snmp_proto *p, const struct oid *o_start, const struct oid *o_ return o_curr; } +#endif + + +#if 0 /** * snmp_prefixize - return prefixed OID copy if possible * @proto: allocation pool holder @@ -1698,11 +2007,12 @@ snmp_prefixize(struct snmp_proto *proto, const struct oid *oid) memcpy(&new->ids, &oid->ids[5], new->n_subid * sizeof(u32)); return new; } +#endif /* * snmp_mib_fill - append a AgentX VarBind to PDU * @p: SNMP protocol instance - * @oid: OID to use as VarBind v.name + * @vb: indirect pointer to destination varbind * @c: transmit PDU context to use * * Append new AgentX VarBind at the end of created PDU. The content (v.data) @@ -1710,19 +2020,16 @@ snmp_prefixize(struct snmp_proto *proto, const struct oid *oid) * created only if the v.name matches some variable name precisely. */ static void -snmp_mib_fill2(struct snmp_proto *p, struct oid *oid, struct snmp_pdu *c) +snmp_mib_fill(struct snmp_proto *p, struct agentx_varbind **vb, struct snmp_pdu *c) { - ASSUME(oid != NULL); + ASSUME(vb != NULL && *vb != NULL); - if (c->size < snmp_varbind_hdr_size_from_oid(oid)) - snmp_manage_tbuf(p, c); - - struct agentx_varbind *vb = snmp_create_varbind(c->buffer, oid); + struct oid *oid = &((*vb)->name); if (oid->n_subid < 2 || (oid->prefix != SNMP_MGMT && oid->ids[0] != SNMP_MIB_2)) { - vb->type = AGENTX_NO_SUCH_OBJECT; - ADVANCE(c->buffer, c->size, snmp_varbind_header_size(vb)); + snmp_set_varbind_type(*vb, AGENTX_NO_SUCH_OBJECT); + ADVANCE(c->buffer, c->size, snmp_varbind_header_size(*vb)); return; } @@ -1737,8 +2044,8 @@ snmp_mib_fill2(struct snmp_proto *p, struct oid *oid, struct snmp_pdu *c) case SNMP_CLASS_END: default: break; - vb->type = AGENTX_NO_SUCH_OBJECT; - ADVANCE(c->buffer, c->size, snmp_varbind_header_size(vb)); + (*vb)->type = AGENTX_NO_SUCH_OBJECT; + ADVANCE(c->buffer, c->size, snmp_varbind_header_size(*vb)); } } @@ -1751,13 +2058,20 @@ snmp_mib_fill2(struct snmp_proto *p, struct oid *oid, struct snmp_pdu *c) * are invalidated! */ void -snmp_manage_tbuf(struct snmp_proto UNUSED *p, struct snmp_pdu *c) +snmp_manage_tbuf(struct snmp_proto *p, void **ptr, struct snmp_pdu *c) { sock *sk = p->sock; + int diff; + if (ptr) + diff = *ptr - (void *) sk->tbuf; log(L_INFO "snmp_manage_tbuf()"); sk_set_tbsize(sk, sk->tbsize + 2048); c->size += 2048; + + + if (ptr) + *ptr = sk->tbuf + diff; } /* diff --git a/proto/snmp/subagent.h b/proto/snmp/subagent.h index b08519ff..9701ba41 100644 --- a/proto/snmp/subagent.h +++ b/proto/snmp/subagent.h @@ -25,7 +25,7 @@ void snmp_ping(struct snmp_proto *p); #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 */ -extern u32 snmp_internet[4]; +extern const u32 snmp_internet[4]; #define SNMP_DEFAULT_CONTEXT 0 @@ -169,6 +169,9 @@ struct oid { u32 ids[]; }; +/* enforced by MIB tree, see mib_tree.h for more info */ +#define OID_MAX_LEN 32 + struct agentx_varbind { u16 type; u16 reserved; /* always zero filled */ @@ -297,6 +300,11 @@ struct snmp_pdu { u32 index; /* index on which the error was found */ }; +struct snmp_proto_pdu { + struct snmp_proto *p; + struct snmp_pdu *c; +}; + struct snmp_packet_info { node n; u8 type; // enum type @@ -323,9 +331,9 @@ 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, uint contid); void snmp_notify_pdu(struct snmp_proto *p, struct oid *oid, void *data, uint size, int include_uptime); -void snmp_manage_tbuf(struct snmp_proto *p, struct snmp_pdu *c); +void snmp_manage_tbuf(struct snmp_proto *p, void **ptr, struct snmp_pdu *c); -struct oid *snmp_prefixize(struct snmp_proto *p, const struct oid *o); +struct agentx_varbind *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);