The first iteration of MIB tree with tests.
S snmp.c
+S subagent.c
+S snmp_utils.c
+S bgp_mib.c
-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)
/* 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);
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
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) */
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;
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;
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
}
/*
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;
}
}
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)
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;
}
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;
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;
#undef SNMP_UPDATE_CASE
}
+
/**
* snmp_bgp_find_next_oid - walk bgp peer addresses and update @o_start oid
*
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);
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)
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;
}
}
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;
/* 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);
/* 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;
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;
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);
}
/*
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);
--- /dev/null
+#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
+
--- /dev/null
+#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
+
/* 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);
}
/*
- * 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
*
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;
}
/*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
+#include <stdarg.h>
+
#include "test/birdtest.h"
#include "test/bt-utils.h"
#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 = 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->n_subid = n_subid;
+ oid->prefix = prefix;
+ oid->include = include;
+ oid->reserved = 0;
- 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 int
-t_s_is_oid_empty(void)
+static inline void
+oid_init(struct oid *oid, u8 n_subid, u8 prefix, u8 include, ...)
{
- 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 *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 *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;
+ 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;
- struct oid *unprefixable = mb_alloc(&root_pool, sizeof(struct oid) + 2 * sizeof(u32));
- unprefixable->n_subid = 2;
- unprefixable->prefix = 0;
- unprefixable->include = 0;
+ va_start(ids, include);
+ oid_init2(result, n_subid, prefix, include, ids);
+ va_end(ids);
- u32 unpref[] = { 65535, 4 };
- memcpy(&unprefixable->ids, unpref, sizeof(unpref) / sizeof(unpref[0]));
+ return result;
+}
- bt_assert(snmp_is_oid_empty(unprefixable) == 0);
+static u32
+xrandom(u32 max)
+{
+ return (bt_random() % max);
+}
- mb_free(unprefixable); unprefixable = NULL;
+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));
- struct oid *unprefixable2 = mb_alloc(&root_pool, sizeof(struct oid) + 8 * sizeof(u32));
- unprefixable2->n_subid = 8;
- unprefixable2->prefix = 0;
- unprefixable2->include = 1;
+ u8 prefix = (u8) xrandom(UINT8_MAX + 1);
- u32 unpref2[] = { 1, 3, 6, 2, 1, 2, 15, 6 };
- memcpy(&unprefixable2->ids, unpref2, sizeof(unpref2) / sizeof(unpref2[0]));
+ if (!prefix)
+ return oid_create(0, 0, 0, 0);
- bt_assert(snmp_is_oid_empty(unprefixable2) == 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;
- mb_free(unprefixable2); unprefixable2 = NULL;
+ for (u32 id = 0; id < len; id++)
+ random->ids[id] = oid_random_id();
- return 1;
+ return random;
}
-static int
-t_s_prefixize(void)
+static struct oid *
+random_no_prefix_oid(void)
{
- //struct oid *nulled = NULL;
- struct snmp_proto snmp_proto;
- test_fill(&snmp_proto);
+ /* probability that the random OID is prefixable is practically zero */
+ u32 len = xrandom(OID_MAX_LEN + 1);
- //struct oid *result = snmp_prefixize(&snmp_proto, nulled);
- //bt_assert(NULL == result);
- //result != NULL ? mb_free(result) : NULL;
- struct oid *result;
+ 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;
- 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;
+ for (u32 id = 0; id < len; id++)
+ random->ids[id] = oid_random_id();
+ return random;
+}
- struct oid *prefixed = mb_alloc(&root_pool, sizeof(struct oid) + 3 * sizeof(u32));
- prefixed->n_subid = 3;
- prefixed->prefix = 100;
- prefixed->include = 1;
+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));
- u32 prefixed_arr[] = { ~((u32) 0), 0, 256 };
- memcpy(&prefixed->ids, prefixed_arr, sizeof(prefixed_arr));
+ 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;
- /* struct oid */result = snmp_prefixize(&snmp_proto, prefixed);
- bt_assert(memcmp(result, prefixed, snmp_oid_size(prefixed)) == 0);
+ for (u32 inet_id = 0; inet_id < ARRAY_SIZE(snmp_internet); inet_id++)
+ random->ids[inet_id] = snmp_internet[inet_id];
- mb_free(result); result = NULL;
- //mb_free(prefixed); prefixed = NULL;
+ 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();
- 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;
+ return random;
+}
- u32 to_prefix_arr[] = {1, 3, 6, 1, 100, ~((u32) 0), 0, 256 };
- memcpy(to_prefix->ids, to_prefix_arr, sizeof(to_prefix_arr));
+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();
+}
- 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;
+static int
+t_oid_empty(void)
+{
+ struct lp_state tmps;
+ lp_save(tmp_linpool, &tmps);
+ bt_assert(snmp_is_oid_empty(NULL) == 0);
- 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 *blank = oid_create(0, 0, 0 /* no ids */);
+ bt_assert(snmp_is_oid_empty(blank) == 1);
+ lp_restore(tmp_linpool, &tmps);
+ }
- u32 unpref[] = { 65535, 4 };
- memcpy(&unprefixable->ids, unpref, sizeof(unpref) / sizeof(unpref[0]));
- result = snmp_prefixize(&snmp_proto, unprefixable);
- bt_assert(result == 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);
+ }
- 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;
+ {
+ 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);
+ }
- 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);
+ {
+ struct oid *unprefixable = oid_create(2, 0, 0,
+ /* ids */ 65535, 4);
+ bt_assert(snmp_is_oid_empty(unprefixable) == 0);
+ lp_restore(tmp_linpool, &tmps);
+ }
- 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)
{
- /* 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 lp_state tmps;
+ lp_save(tmp_linpool, &tmps);
+ /* same length, no prefix */
+ 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);
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);
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);
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);
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
+test_snmp_oid_compare(const void *left, const void *right)
+{
+ return snmp_oid_compare(
+ *((const struct oid **) left),
+ *((const struct oid **) right)
+ );
+}
+
+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();
+ }
+}
+
+static int
+generate_oids(struct oid *oids[], struct oid *sorted[], int size, struct oid *(*generator)(void))
+{
+ generate_raw_oids(oids, size, generator);
+
+ memcpy(sorted, oids, size * sizeof(struct oid *));
+
+ qsort(sorted, (size_t) size, sizeof(struct oid *),
+ test_snmp_oid_compare);
+
+ // 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];
+ }
+
+ /* delete old pointers */
+ for (int i = last_used + 1; i < size; i++)
+ sorted[i] = NULL;
+
+ return (size > 1) ? last_used + 1 : size;
+}
+
+/* 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_s_bgp_state(void)
+t_tree_add(void)
{
- struct oid *oid = mb_alloc(&root_pool, sizeof(struct oid) + 10 * sizeof(u32));
- /* oid header */
- oid->n_subid = 0;
- oid->prefix = 2;
- oid->include = 0;
- oid->pad = 0;
+ gen_test_add(random_prefixed_oid);
+ gen_test_add(random_no_prefix_oid);
+ gen_test_add(random_prefixable_oid);
+ gen_test_add(random_oid);
- /* test all states with expected oid length */
- bt_debug("testing precise oids\n");
- test_oid(oid, 0);
+ return 1;
+}
- for (int i = 0; i < 10; i++)
- oid->ids[i] = (u32) bt_random();
+static int
+gen_test_find(struct oid *(*generator)(void))
+{
+ lp_state tmps;
+ lp_save(tmp_linpool, &tmps);
- /* if this subid is too high it does not match the test case
- * in general test_oid() func
- */
- oid->ids[2] = 0;
+ pool *pool = &root_pool;
- /* test all states with garbage ip */
- bt_debug("testing oids with random ip index\n");
- test_oid(oid, 0);
+ 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);
+ }
- /* 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;
+ tmp_flush();
+ return 1;
+}
- oid->ids[5] = 0;
- oid->ids[6] = 257;
- oid->ids[7] = 127;
- oid->ids[8] = 0xFFFF;
- test_oid(oid, 0);
+static int
+t_tree_find(void)
+{
- mb_free(oid);
+ 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()");
-
- bt_test_suite(t_s_is_oid_empty, "Function snmp_is_oid_empty()");
+ srandom(0x0000fa00);
- bt_test_suite(t_s_prefixize, "Function snmp_prefixize()");
+ 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_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();
}
*/
#include "snmp_utils.h"
+#include <stdio.h>
inline void
snmp_pdu_context(struct snmp_pdu *pdu, sock *sk)
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
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
{
ASSUME(t != AGENTX_INVALID);
STORE_U16(vb->type, t);
+ STORE_U16(vb->reserved, 0);
}
/* Internal wrapper */
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));
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;
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;
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)
{
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);
+}
/* 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);
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
/* 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);
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
"""
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
*
*/
-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)
/* 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);
/* 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);
/* 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));
}
((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);
#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);
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);
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);
* 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)
{
/* 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)
}
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
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));
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
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);
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;
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);
* 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);
/* 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);
}
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:
/* 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. */
// 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;
}
*/
/* 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)
{
return o_curr;
}
+#endif
+
+
+#if 0
/**
* snmp_prefixize - return prefixed OID copy if possible
* @proto: allocation pool holder
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)
* 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);
-
- if (c->size < snmp_varbind_hdr_size_from_oid(oid))
- snmp_manage_tbuf(p, c);
+ ASSUME(vb != NULL && *vb != NULL);
- 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;
}
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));
}
}
* 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;
}
/*
#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
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 */
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
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);