0
0
mirror of https://gitlab.nic.cz/labs/bird.git synced 2024-12-22 09:41:54 +00:00

SNMP: MIB tree

The first iteration of MIB tree with tests.
This commit is contained in:
Vojtech Vilimek 2024-04-22 12:55:33 +02:00
parent c7ca8b66c1
commit aa14b546b8
13 changed files with 3021 additions and 590 deletions

View File

@ -1 +1,4 @@
S snmp.c
S subagent.c
S snmp_utils.c
S bgp_mib.c

View File

@ -1,4 +1,4 @@
src := snmp.c snmp_utils.c subagent.c bgp_mib.c
src := snmp.c snmp_utils.c subagent.c bgp_mib.c mib_tree.c
obj := $(src-o-files)
$(all-daemon)
$(cf-local)

View File

@ -25,6 +25,10 @@
/* hash table only store ip4 addresses */
#define SNMP_HASH_LESS(ip1, ip2) SNMP_HASH_LESS4(ip1,ip2)
/* Simply discard type */
#define SNMP_MANAGE_TBUF(p, vb, c) snmp_manage_tbuf(p, (void **) vb, c)
static inline void ip4_to_oid(struct oid *oid, ip4_addr addr);
@ -192,11 +196,15 @@ snmp_bgp_state(const struct oid *oid)
void
snmp_bgp_reg_ok(struct snmp_proto *p, struct agentx_response *r, struct oid *oid)
{
(void)p;
(void)r;
(void)oid;
/* TODO: EXPENSIVE_CHECK() that
const struct oid *in_buf = ((void *) r) + sizeof(r);
struct oid *dup = snmp_prefixize(p, in_buf);
ASSUME(snmp_bgp_state(oid) == snmp_bgp_state(dup));
ASSUME(snmp_bgp_state(oid) == snmp_bgp_state(dup));
mb_free(dup);
*/
}
void
@ -223,10 +231,10 @@ snmp_bgp_notify_common(struct snmp_proto *p, uint type, ip4_addr ip4, char last_
u32 trap_ids[] = { 1, 0, type };
STATIC_ASSERT(ARRAY_SIZE(trap_ids) == 3);
/* additional size for trap identification, here either
/* additional size for trap identification, here either
* bgpEstablishedNotification or bgpBackwardTransNotification (see below) */
void *data = tmp_alloc(snmp_oid_size_from_len(ARRAY_SIZE(trap_ids)) + sz);
struct oid *head = data;
void *data = tmp_alloc(snmp_oid_size_from_len(ARRAY_SIZE(trap_ids)) + sz);
struct oid *head = data;
{ /* trap id BGP4-MIB::bgpEstablishedNotification (.1.3.6.1.2.15.0.1)
* or BGP4-MIB::bgpBackwardTransNotification (.1.3.6.1.2.15.0.2) */
@ -272,8 +280,8 @@ snmp_bgp_notify_common(struct snmp_proto *p, uint type, ip4_addr ip4, char last_
ip4_to_oid(addr, ip4);
}
/* We have enough space inside the TX-buffer prepared */
#define UNLIMITED 100
snmp_varbind_ip4(addr_vb, UNLIMITED, ip4);
struct snmp_pdu sink = { 0 };
snmp_varbind_ip4(addr_vb, &sink, ip4);
{ /* BGP4-MIB::bgpPeerLastError */
struct oid *error = &error_vb->name;
@ -286,7 +294,7 @@ snmp_bgp_notify_common(struct snmp_proto *p, uint type, ip4_addr ip4, char last_
error->ids[ENTRY_TYPE] = BGP4_MIB_LAST_ERROR;
ip4_to_oid(error, ip4);
}
snmp_varbind_nstr(error_vb, UNLIMITED, last_error, 2);
snmp_varbind_nstr(error_vb, &sink, last_error, 2);
{ /* BGP4-MIB::bgpPeerState */
struct oid *state = &state_vb->name;
@ -299,13 +307,12 @@ snmp_bgp_notify_common(struct snmp_proto *p, uint type, ip4_addr ip4, char last_
state->ids[ENTRY_TYPE] = BGP4_MIB_STATE;
ip4_to_oid(state, ip4);
}
snmp_varbind_int(state_vb, UNLIMITED, state_val);
snmp_varbind_int(state_vb, &sink, state_val);
/* We do not send the systemUpTime.0 */
snmp_notify_pdu(p, head, data, sz, 0);
#undef OID_N_SUBID
#undef UNLIMITED
}
/*
@ -329,7 +336,7 @@ snmp_bgp_fsm_state(const struct bgp_proto *bgp_proto)
return MIN(bgp_in->state, bgp_out->state) + 1;
if (MIN(bgp_in->state, bgp_out->state) == BS_CLOSE)
return BS_IDLE;
return MAX(bgp_in->state, bgp_out->state) + 1;
}
@ -580,11 +587,13 @@ oid_state_compare(const struct oid *oid, u8 state)
}
static struct oid *
update_bgp_oid(struct oid *oid, u8 state)
update_bgp_vb(struct snmp_proto *p, struct agentx_varbind **vb, u8 state, struct snmp_pdu *c)
{
struct oid *oid = &(*vb)->name;
if (state == BGP4_MIB_S_END || state == BGP4_MIB_S_INVALID ||
state == BGP4_MIB_S_NO_VALUE)
return oid;
return &(*vb)->name;
/* No need to reallocate anything if the OID has same lin. state */
if (snmp_bgp_state(oid) == state)
@ -607,41 +616,39 @@ update_bgp_oid(struct oid *oid, u8 state)
case BGP4_MIB_S_BGP:
/* This could potentially destroy same old data */
if (oid->n_subid != 2)
oid = mb_realloc(oid, snmp_oid_size_from_len(2));
oid = snmp_varbind_set_name_len(p, vb, 2, c);
oid->n_subid = 2;
oid->ids[0] = SNMP_MIB_2;
oid->ids[1] = BGP4_MIB;
break;
case BGP4_MIB_S_VERSION:
if (oid->n_subid != 3)
oid = mb_realloc(oid, snmp_oid_size_from_len(3));
oid = snmp_varbind_set_name_len(p, vb, 3, c);
oid->n_subid = 3;
oid->ids[2] = BGP4_MIB_VERSION;
break;
case BGP4_MIB_S_LOCAL_AS:
if (oid->n_subid != 3)
oid = mb_realloc(oid, snmp_oid_size_from_len(3));
oid =snmp_varbind_set_name_len(p, vb, 3, c);
oid->n_subid = 3;
oid->ids[2] = BGP4_MIB_LOCAL_AS;
break;
case BGP4_MIB_S_PEER_IDENTIFIER:
if (oid->n_subid != 9)
{
oid = mb_realloc(oid, snmp_oid_size_from_len(9));
u8 n_subid = LOAD_U8(oid->n_subid);
oid = snmp_varbind_set_name_len(p, vb, 9, c);
if (oid->n_subid < 6)
if (n_subid < 6)
oid->ids[5] = 0;
if (oid->n_subid < 7)
if (n_subid < 7)
oid->ids[6] = 0;
if (oid->n_subid < 8)
if (n_subid < 8)
oid->ids[7] = 0;
if (oid->n_subid < 9)
if (n_subid < 9)
oid->ids[8] = 0;
}
@ -649,24 +656,25 @@ update_bgp_oid(struct oid *oid, u8 state)
oid->ids[3] = BGP4_MIB_PEER_ENTRY;
oid->ids[4] = BGP4_MIB_PEER_IDENTIFIER;
oid->n_subid = 9;
break;
#define SNMP_UPDATE_CASE(num, update) \
case num: \
if (oid->n_subid != 9) \
{ \
oid = mb_realloc(oid, snmp_oid_size_from_len(9)); \
u8 n_subid = LOAD_U8(oid->n_subid); \
oid = snmp_varbind_set_name_len(p, vb, 9, c); \
\
if (oid->n_subid < 6) \
if (n_subid < 6) \
oid->ids[5] = 0; \
if (oid->n_subid < 7) \
if (n_subid < 7) \
oid->ids[6] = 0; \
if (oid->n_subid < 8) \
if (n_subid < 8) \
oid->ids[7] = 0; \
if (oid->n_subid < 9) \
if (n_subid < 9) \
oid->ids[8] = 0; \
} \
\
oid->n_subid = 9; \
oid->ids[4] = update; \
break;
@ -719,7 +727,7 @@ update_bgp_oid(struct oid *oid, u8 state)
case BGP4_MIB_S_IDENTIFIER:
if (oid->n_subid != 3)
oid = mb_realloc(oid, snmp_oid_size_from_len(3));
oid = snmp_varbind_set_name_len(p, vb, 3, c);
oid->n_subid = 3;
oid->ids[2] = 4;
@ -734,6 +742,7 @@ update_bgp_oid(struct oid *oid, u8 state)
#undef SNMP_UPDATE_CASE
}
/**
* snmp_bgp_find_next_oid - walk bgp peer addresses and update @o_start oid
*
@ -775,8 +784,8 @@ snmp_bgp_find_next_oid(struct snmp_proto *p, struct oid *oid, uint UNUSED contid
return 0;
}
static enum snmp_search_res
snmp_bgp_search_dynamic(struct snmp_proto *p, struct oid **searched, const struct oid *o_end, uint UNUSED contid, u8 next_state)
static enum snmp_search_res UNUSED
snmp_bgp_search_dynamic(struct snmp_proto *p, struct oid **searched, const struct oid *o_end, uint UNUSED contid, u8 next_state, struct snmp_pdu *c)
{
struct oid *oid = *searched;
u8 end_state = MIN(snmp_bgp_state(o_end), BGP4_MIB_S_PEER_TABLE_END);
@ -784,7 +793,12 @@ snmp_bgp_search_dynamic(struct snmp_proto *p, struct oid **searched, const struc
ASSUME(end_state <= BGP4_MIB_S_END);
ASSUME(oid != NULL);
oid = update_bgp_oid(oid, next_state);
// TODO TODO remove me
struct agentx_varbind data = { 0 };
struct agentx_varbind *vb = &data;
oid = update_bgp_vb(p, &vb, next_state, c);
//oid = update_bgp_oid(oid, next_state);
int found;
while (!(found = snmp_bgp_find_next_oid(p, oid, contid)) && next_state <= end_state)
@ -792,7 +806,8 @@ snmp_bgp_search_dynamic(struct snmp_proto *p, struct oid **searched, const struc
next_state = snmp_bgp_next_state(next_state);
if (next_state == BGP4_MIB_S_IDENTIFIER)
break;
oid = update_bgp_oid(oid, next_state);
//oid = update_bgp_oid(oid, next_state);
oid = update_bgp_vb(p, &vb, next_state, c);
/* In case of search for next bgp state, we want to start from beginning. */
oid->ids[5] = oid->ids[6] = oid->ids[7] = oid->ids[8] = 0;
}
@ -807,8 +822,14 @@ snmp_bgp_search_dynamic(struct snmp_proto *p, struct oid **searched, const struc
}
enum snmp_search_res
snmp_bgp_search(struct snmp_proto *p, struct oid **searched, const struct oid *o_end, uint contid)
snmp_bgp_search(struct snmp_proto *p, struct agentx_varbind **vb_search, const struct oid *o_end, struct snmp_pdu *c)
{
(void)p;
(void)vb_search;
(void)o_end;
(void)c;
return SNMP_SEARCH_END_OF_VIEW;
#if 0
enum snmp_search_res r = SNMP_SEARCH_END_OF_VIEW;
u8 bgp_state = snmp_bgp_state(*searched);
u8 state;
@ -858,41 +879,32 @@ snmp_bgp_search(struct snmp_proto *p, struct oid **searched, const struct oid *o
/* end not found */
return SNMP_SEARCH_END_OF_VIEW;
#endif
}
static byte *
bgp_fill_dynamic(struct snmp_proto UNUSED *p, struct agentx_varbind *vb,
struct snmp_pdu *c, u8 state)
static void
bgp_fill_dynamic(struct snmp_proto *p, struct agentx_varbind **vb, struct snmp_pdu *c, u8 state)
{
struct oid *oid = &vb->name;
uint size = c->size - snmp_varbind_header_size(vb);
byte *pkt;
struct oid *oid = &(*vb)->name;
//byte *pkt;
ip4_addr addr;
if (oid_state_compare(oid, state) == 0 && snmp_bgp_valid_ip4(oid))
addr = ip4_from_oid(oid);
else
{
vb->type = AGENTX_NO_SUCH_INSTANCE;
pkt = ((byte *) vb) + snmp_varbind_header_size(vb);
return pkt;
}
return snmp_set_varbind_type(*vb, AGENTX_NO_SUCH_INSTANCE);
struct snmp_bgp_peer *pe = snmp_hash_find(p, addr);
if (!pe)
{
vb->type = AGENTX_NO_SUCH_INSTANCE;
return ((byte *) vb) + snmp_varbind_header_size(vb);
}
return snmp_set_varbind_type(*vb, AGENTX_NO_SUCH_INSTANCE);
const struct bgp_proto *bgp_proto = pe->bgp_proto;
if (!ipa_is_ip4(bgp_proto->remote_ip))
{
log(L_ERR, "%s: Found BGP protocol instance with IPv6 address", bgp_proto->p.name);
vb->type = AGENTX_NO_SUCH_INSTANCE;
c->error = AGENTX_RES_GEN_ERROR;
return ((byte *) vb) + snmp_varbind_header_size(vb);
return snmp_set_varbind_type(*vb, AGENTX_NO_SUCH_INSTANCE);
}
ip4_addr proto_ip = ipa_to_ip4(bgp_proto->remote_ip);
@ -901,9 +913,8 @@ bgp_fill_dynamic(struct snmp_proto UNUSED *p, struct agentx_varbind *vb,
/* Here, we could be in problem as the bgp_proto IP address could be changed */
log(L_ERR, "%s: Stored hash key IP address and peer remote address differ.",
bgp_proto->p.name);
vb->type = AGENTX_NO_SUCH_INSTANCE;
c->error = AGENTX_RES_GEN_ERROR;
return ((byte *) vb) + snmp_varbind_header_size(vb);
return snmp_set_varbind_type(*vb, AGENTX_NO_SUCH_INSTANCE);
}
const struct bgp_conn *bgp_conn = bgp_proto->conn;
@ -914,125 +925,202 @@ bgp_fill_dynamic(struct snmp_proto UNUSED *p, struct agentx_varbind *vb,
char last_error[2];
snmp_bgp_last_error(bgp_proto, last_error);
snmp_set_varbind_type(*vb, AGENTX_NO_SUCH_INSTANCE);
switch (state)
{
case BGP4_MIB_S_PEER_IDENTIFIER:
if (c->size < AGENTX_TYPE_IP4_SIZE)
SNMP_MANAGE_TBUF(p, vb, c);
if (fsm_state == BGP4_MIB_OPENCONFIRM || fsm_state == BGP4_MIB_ESTABLISHED)
pkt = snmp_varbind_ip4(vb, size, ip4_from_u32(bgp_proto->remote_id));
// TODO last
snmp_varbind_ip4(*vb, c, ip4_from_u32(bgp_proto->remote_id));
else
pkt = snmp_varbind_ip4(vb, size, IP4_NONE);
snmp_varbind_ip4(*vb, c, IP4_NONE);
break;
case BGP4_MIB_S_STATE:
pkt = snmp_varbind_int(vb, size, fsm_state);
if (c->size < AGENTX_TYPE_INT_SIZE)
SNMP_MANAGE_TBUF(p, vb, c);
snmp_varbind_int(*vb, c, fsm_state);
break;
case BGP4_MIB_S_ADMIN_STATUS:
if (c->size < AGENTX_TYPE_INT_SIZE)
SNMP_MANAGE_TBUF(p, vb, c);
if (bgp_proto->p.disabled)
pkt = snmp_varbind_int(vb, size, AGENTX_ADMIN_STOP);
snmp_varbind_int(*vb, c, AGENTX_ADMIN_STOP);
else
pkt = snmp_varbind_int(vb, size, AGENTX_ADMIN_START);
snmp_varbind_int(*vb, c, AGENTX_ADMIN_START);
break;
case BGP4_MIB_S_NEGOTIATED_VERSION:
if (c->size < AGENTX_TYPE_INT_SIZE)
SNMP_MANAGE_TBUF(p, vb, c);
if (fsm_state == BGP4_MIB_ESTABLISHED || fsm_state == BGP4_MIB_ESTABLISHED)
pkt = snmp_varbind_int(vb, size, BGP4_MIB_NEGOTIATED_VER_VALUE);
snmp_varbind_int(*vb, c, BGP4_MIB_NEGOTIATED_VER_VALUE);
else
pkt = snmp_varbind_int(vb, size, BGP4_MIB_NEGOTIATED_VER_NO_VALUE);
snmp_varbind_int(*vb, c, BGP4_MIB_NEGOTIATED_VER_NO_VALUE);
break;
case BGP4_MIB_S_LOCAL_ADDR:
pkt = snmp_varbind_ip4(vb, size, ipa_to_ip4(bgp_proto->local_ip));
if (c->size < AGENTX_TYPE_IP4_SIZE)
SNMP_MANAGE_TBUF(p, vb, c);
snmp_varbind_ip4(*vb, c, ipa_to_ip4(bgp_proto->local_ip));
break;
case BGP4_MIB_S_LOCAL_PORT:
pkt = snmp_varbind_int(vb, size, bgp_conf->local_port);
if (c->size < AGENTX_TYPE_INT_SIZE)
SNMP_MANAGE_TBUF(p, vb, c);
snmp_varbind_int(*vb, c, bgp_conf->local_port);
break;
case BGP4_MIB_S_REMOTE_ADDR:
pkt = snmp_varbind_ip4(vb, size, ipa_to_ip4(bgp_proto->remote_ip));
if (c->size < AGENTX_TYPE_IP4_SIZE)
SNMP_MANAGE_TBUF(p, vb, c);
snmp_varbind_ip4(*vb, c, ipa_to_ip4(bgp_proto->remote_ip));
break;
case BGP4_MIB_S_REMOTE_PORT:
pkt = snmp_varbind_int(vb, size, bgp_conf->remote_port);
if (c->size < AGENTX_TYPE_INT_SIZE)
SNMP_MANAGE_TBUF(p, vb, c);
snmp_varbind_int(*vb, c, bgp_conf->remote_port);
break;
case BGP4_MIB_S_REMOTE_AS:
pkt = snmp_varbind_int(vb, size, bgp_proto->remote_as);
if (c->size < AGENTX_TYPE_INT_SIZE)
SNMP_MANAGE_TBUF(p, vb, c);
snmp_varbind_int(*vb, c, bgp_proto->remote_as);
break;
case BGP4_MIB_S_RX_UPDATES: /* bgpPeerInUpdates */
pkt = snmp_varbind_counter32(vb, size, bgp_stats->rx_updates);
if (c->size < AGENTX_TYPE_COUNTER32_SIZE)
SNMP_MANAGE_TBUF(p, vb, c);
snmp_varbind_counter32(*vb, c, bgp_stats->rx_updates);
break;
case BGP4_MIB_S_TX_UPDATES: /* bgpPeerOutUpdate */
pkt = snmp_varbind_counter32(vb, size, bgp_stats->tx_updates);
if (c->size < AGENTX_TYPE_COUNTER32_SIZE)
SNMP_MANAGE_TBUF(p, vb, c);
snmp_varbind_counter32(*vb, c, bgp_stats->tx_updates);
break;
case BGP4_MIB_S_RX_MESSAGES: /* bgpPeerInTotalMessages */
pkt = snmp_varbind_counter32(vb, size, bgp_stats->rx_messages);
if (c->size < AGENTX_TYPE_COUNTER32_SIZE)
SNMP_MANAGE_TBUF(p, vb, c);
snmp_varbind_counter32(*vb, c, bgp_stats->rx_messages);
break;
case BGP4_MIB_S_TX_MESSAGES: /* bgpPeerOutTotalMessages */
pkt = snmp_varbind_counter32(vb, size, bgp_stats->tx_messages);
if (c->size < AGENTX_TYPE_COUNTER32_SIZE)
SNMP_MANAGE_TBUF(p, vb, c);
snmp_varbind_counter32(*vb, c, bgp_stats->tx_messages);
break;
case BGP4_MIB_S_LAST_ERROR:
pkt = snmp_varbind_nstr(vb, size, last_error, 2);
if (c->size < snmp_str_size_from_len(2))
SNMP_MANAGE_TBUF(p, vb, c);
snmp_varbind_nstr(*vb, c, last_error, 2);
break;
case BGP4_MIB_S_FSM_TRANSITIONS:
pkt = snmp_varbind_counter32(vb, size,
if (c->size < AGENTX_TYPE_COUNTER32_SIZE)
SNMP_MANAGE_TBUF(p, vb, c);
snmp_varbind_counter32(*vb, c,
bgp_stats->fsm_established_transitions);
break;
case BGP4_MIB_S_FSM_ESTABLISHED_TIME:
pkt = snmp_varbind_gauge32(vb, size,
if (c->size < AGENTX_TYPE_COUNTER32_SIZE)
SNMP_MANAGE_TBUF(p, vb, c);
snmp_varbind_gauge32(*vb, c,
(current_time() - bgp_proto->last_established) TO_S);
break;
case BGP4_MIB_S_RETRY_INTERVAL: /* retry inverval value should be != 0 */
pkt = snmp_varbind_int(vb, size, bgp_conf->connect_retry_time);
if (c->size < AGENTX_TYPE_INT_SIZE)
SNMP_MANAGE_TBUF(p, vb, c);
snmp_varbind_int(*vb, c, bgp_conf->connect_retry_time);
break;
case BGP4_MIB_S_HOLD_TIME: /* hold time should be == 0 or in 3..65535 */
pkt = snmp_varbind_int(vb, size, (bgp_conn) ? bgp_conn->hold_time : 0);
if (c->size < AGENTX_TYPE_INT_SIZE)
SNMP_MANAGE_TBUF(p, vb, c);
snmp_varbind_int(*vb, c, (bgp_conn) ? bgp_conn->hold_time : 0);
break;
case BGP4_MIB_S_KEEPALIVE:
if (c->size < AGENTX_TYPE_INT_SIZE)
SNMP_MANAGE_TBUF(p, vb, c);
if (!bgp_conf->hold_time)
pkt = snmp_varbind_int(vb, size, 0);
snmp_varbind_int(*vb, c, 0);
else
pkt = snmp_varbind_int(vb, size,
snmp_varbind_int(*vb, c,
(bgp_conn) ? bgp_conn->keepalive_time : 0);
break;
case BGP4_MIB_S_HOLD_TIME_CONFIGURED:
pkt = snmp_varbind_int(vb, size, bgp_conf->hold_time);
if (c->size < AGENTX_TYPE_INT_SIZE)
SNMP_MANAGE_TBUF(p, vb, c);
snmp_varbind_int(*vb, c, bgp_conf->hold_time);
break;
case BGP4_MIB_S_KEEPALIVE_CONFIGURED:
if (c->size < AGENTX_TYPE_INT_SIZE)
SNMP_MANAGE_TBUF(p, vb, c);
if (!bgp_conf->keepalive_time)
pkt = snmp_varbind_int(vb, size, 0);
snmp_varbind_int(*vb, c, 0);
else
pkt = snmp_varbind_int(vb, size,
snmp_varbind_int(*vb, c,
(bgp_conn) ? bgp_conn->keepalive_time : 0);
break;
case BGP4_MIB_S_ORIGINATION_INTERVAL:
/* value should be in 1..65535 but is not supported by bird */
pkt = snmp_varbind_int(vb, size, 0);
if (c->size < AGENTX_TYPE_INT_SIZE)
SNMP_MANAGE_TBUF(p, vb, c);
snmp_varbind_int(*vb, c, 0);
break;
case BGP4_MIB_S_MIN_ROUTE_ADVERTISEMENT:
/* value should be in 1..65535 but is not supported by bird */
pkt = snmp_varbind_int(vb, size, 0);
if (c->size < AGENTX_TYPE_INT_SIZE)
SNMP_MANAGE_TBUF(p, vb, c);
snmp_varbind_int(*vb, c, 0);
break;
case BGP4_MIB_S_IN_UPDATE_ELAPSED_TIME:
pkt = snmp_varbind_gauge32(vb, size,
if (c->size < AGENTX_TYPE_INT_SIZE)
SNMP_MANAGE_TBUF(p, vb, c);
snmp_varbind_gauge32(*vb, c,
(current_time() - bgp_proto->last_rx_update) TO_S
);
break;
@ -1052,86 +1140,75 @@ bgp_fill_dynamic(struct snmp_proto UNUSED *p, struct agentx_varbind *vb,
case BGP4_MIB_S_NO_VALUE:
break;
}
if (!pkt)
{
vb->type = AGENTX_NO_SUCH_INSTANCE;
return ((byte *) vb) + snmp_varbind_header_size(vb);
}
return pkt;
}
static byte *
bgp_fill_static(struct snmp_proto *p, struct agentx_varbind *vb, byte *pkt, uint size
UNUSED, uint contid UNUSED, u8 state)
void
bgp_fill_static(struct snmp_proto *p, struct agentx_varbind **vb, struct snmp_pdu *c, u8 state)
{
ASSUME((void *) pkt == (void *) vb);
ASSUME(c->buffer == snmp_varbind_data(*vb));
struct oid *oid = &vb->name;
struct oid *oid = &(*vb)->name;
/*
* snmp_bgp_state() check only prefix. To be sure on OID equivalence we need to
* compare the oid->n_subid length. All BGP static fields have same n_subid.
*/
if (oid_state_compare(oid, state) < 0 || state == BGP4_MIB_S_END)
{
vb->type = AGENTX_NO_SUCH_OBJECT;
return pkt + snmp_varbind_header_size(vb);
}
snmp_set_varbind_type(*vb, AGENTX_NO_SUCH_OBJECT);
else if (oid_state_compare(oid, state) > 0)
{
vb->type = AGENTX_NO_SUCH_INSTANCE;
return pkt + snmp_varbind_header_size(vb);
}
snmp_set_varbind_type(*vb, AGENTX_NO_SUCH_INSTANCE);
switch (state)
{
case BGP4_MIB_S_VERSION:
pkt = snmp_varbind_nstr(vb, size, BGP4_VERSIONS, 1);
case BGP4_MIB_S_VERSION:;
uint sz = snmp_str_size_from_len(1);
if (c->size < sz)
SNMP_MANAGE_TBUF(p, vb, c);
c->size -= sz;
snmp_varbind_nstr(*vb, c, BGP4_VERSIONS, 1);
break;
case BGP4_MIB_S_LOCAL_AS:
pkt = snmp_varbind_int(vb, size, p->bgp_local_as);
if (c->size < AGENTX_TYPE_INT_SIZE)
SNMP_MANAGE_TBUF(p, vb, c);
snmp_varbind_int(*vb, c, p->bgp_local_as);
break;
case BGP4_MIB_S_IDENTIFIER:
pkt = snmp_varbind_ip4(vb, size, p->bgp_local_id);
if (c->size < AGENTX_TYPE_IP4_SIZE)
SNMP_MANAGE_TBUF(p, vb, c);
snmp_varbind_ip4(*vb, c, p->bgp_local_id);
break;
default:
vb->type = AGENTX_NO_SUCH_OBJECT;
pkt += snmp_varbind_header_size(vb);
snmp_set_varbind_type(*vb, AGENTX_NO_SUCH_OBJECT);
break;
}
return pkt;
}
void
snmp_bgp_fill(struct snmp_proto *p, struct agentx_varbind *vb,
struct snmp_pdu *c)
snmp_bgp_fill(struct snmp_proto *p, struct agentx_varbind **vb, struct snmp_pdu *c)
{
u8 state = snmp_bgp_state(&vb->name);
ASSERT(vb != NULL);
u8 state = snmp_bgp_state(&((*vb)->name));
byte *pkt;
if (is_static(state))
{
pkt = bgp_fill_static(p, vb, c->buffer, c->size, 0, state);
ADVANCE(c->buffer, c->size, pkt - c->buffer);
bgp_fill_static(p, vb, c, state);
return;
}
if (is_dynamic(state))
{
pkt = bgp_fill_dynamic(p, vb, c, state);
ADVANCE(c->buffer, c->size, pkt - c->buffer);
bgp_fill_dynamic(p, vb, c, state);
return;
}
vb->type = AGENTX_NO_SUCH_OBJECT;
ADVANCE(c->buffer, c->size, snmp_varbind_header_size(vb));
snmp_set_varbind_type(*vb, AGENTX_NO_SUCH_OBJECT);
}
/*

View File

@ -48,8 +48,9 @@ void snmp_bgp_reg_failed(struct snmp_proto *p, struct agentx_response *r, struct
u8 snmp_bgp_get_valid(u8 state);
u8 snmp_bgp_getnext_valid(u8 state);
enum snmp_search_res snmp_bgp_search(struct snmp_proto *p, struct oid **searched, const struct oid *o_end, uint contid);
void snmp_bgp_fill(struct snmp_proto *p, struct agentx_varbind *vb, struct snmp_pdu *c);
enum snmp_search_res snmp_bgp_search(struct snmp_proto *p, struct agentx_varbind **vb_search, const struct oid *o_end, struct snmp_pdu *c);
enum snmp_search_res snmp_bgp_search2(struct snmp_proto *p, struct oid **searched, const struct oid *o_end, uint contid);
void snmp_bgp_fill(struct snmp_proto *p, struct agentx_varbind **vb, struct snmp_pdu *c);
//int snmp_bgp_testset(struct snmp_proto *p, const struct agentx_varbind *vb, void* tr, struct oid *oid, uint pkt_type);
void snmp_bgp_notify_established(struct snmp_proto *p, struct bgp_proto *bgp);

909
proto/snmp/mib_tree.c Normal file
View File

@ -0,0 +1,909 @@
#include "mib_tree.h"
#include "snmp_utils.h"
/* TODO does the code handle leafs correctly ?! */
#ifdef allocz
#undef allocz
#endif
#define alloc(size) mb_alloc(p, size)
#define allocz(size) mb_allocz(p, size)
#define free(ptr) mb_free(ptr)
#define realloc(ptr, newsize) mib_mb_realloc(p, ptr, newsize)
/*
* mib_mb_realloc - fix mb_realloc for NULL
* @p: pool to use for NULL pointers
* @ptr: old pointer to be reallocated
* @size: new size of allocated memory block
*
* The mb_realloc() does not work with NULL as ptr.
*/
static inline void *
mib_mb_realloc(pool *p, void *ptr, unsigned size)
{
if (!ptr)
return mb_alloc(p, size);
return mb_realloc(ptr, size);
}
void
mib_tree_init(pool *p, struct mib_tree *t)
{
struct mib_node *node = &t->root;
node->c.id = 0;
node->c.flags = 0;
node->children = NULL;
node->child_len = 0;
struct oid *oid = tmp_alloc(
snmp_oid_size_from_len((uint) ARRAY_SIZE(snmp_internet)));
STORE_U8(oid->n_subid, ARRAY_SIZE(snmp_internet));
STORE_U8(oid->prefix, 0);
STORE_U8(oid->include, 0);
STORE_U8(oid->reserved, 0);
for (size_t i = 0; i < ARRAY_SIZE(snmp_internet); i++)
STORE_U32(oid->ids[i], snmp_internet[i]);
(void) mib_tree_add(p, t, oid, 0);
/* WTF ??
struct mib_walk_state walk = { };
(void) mib_tree_find(t, &walk, oid);
*/
}
// This function does not work with leaf nodes inside the snmp_internet prefix
// area
// Return NULL of failure, valid mib_node_u pointer otherwise
mib_node_u *
mib_tree_add(pool *p, struct mib_tree *t, const struct oid *oid, int is_leaf)
{
//ASSERT(snmp_oid_is_prefixed(oid) || !snmp_oid_is_prefixable(oid));
struct mib_walk_state walk;
mib_node_u *node;
/* The empty prefix is associated with the root tree node */
if (snmp_is_oid_empty(oid) && !is_leaf)
return (mib_node_u *) &t->root;
else if (snmp_is_oid_empty(oid))
return NULL;
mib_tree_walk_init(&walk);
node = mib_tree_find(t, &walk, oid);
ASSERT(walk.id_pos <= LOAD_U8(oid->n_subid) + 1);
if (node)
{
if (mib_node_is_leaf(node) == is_leaf)
return node;
/* we are trying to insert a leaf node in place of inner node,
* or vice versa */
return NULL;
}
ASSERT(walk.id_pos < LOAD_U8(oid->n_subid) + 1);
node = walk.stack[walk.stack_pos - 1];
/* we encounter leaf node before end of OID's id path */
if (mib_node_is_leaf(node))
return NULL;
struct mib_node *node_inner = &node->inner;
if (snmp_oid_is_prefixed(oid) &&
walk.stack_pos <= ARRAY_SIZE(snmp_internet) + 1)
{
ASSUME(walk.stack_pos && walk.stack[0] == (mib_node_u *) &t->root);
for (u32 id = walk.stack_pos - 1; id < ARRAY_SIZE(snmp_internet); id++)
{
if (snmp_internet[id] >= node_inner->child_len)
{
u32 old_len = node_inner->child_len;
node_inner->child_len = snmp_internet[id] + 1;
node_inner->children = realloc(node_inner->children,
node_inner->child_len * sizeof(mib_node_u *));
for (u32 i = old_len; i < node_inner->child_len; i++)
node_inner->children[i] = NULL;
}
node = allocz(sizeof(struct mib_node));
/* assign child into a parent's children array */
node_inner->children[snmp_internet[id]] = node;
node_inner = &node->inner;
node_inner->c.id = snmp_internet[id];
/* node_inner's fields c.flags, child_len, children defaults to zero or
* NULL respectively */
walk.stack[walk.stack_pos++] = node;
}
if (walk.stack_pos == ARRAY_SIZE(snmp_internet) + 1)
{
u32 old_len = node_inner->child_len;
node_inner->child_len = MAX(old_len, (u32) LOAD_U8(oid->prefix) + 1);
node_inner->children = realloc(node_inner->children,
node_inner->child_len * sizeof(mib_node_u *));
for (u32 i = old_len; i < node_inner->child_len; i++)
node_inner->children[i] = NULL;
if (is_leaf && !LOAD_U8(oid->n_subid))
{
node = allocz(sizeof(struct mib_leaf));
node->empty.flags = MIB_TREE_LEAF;
}
else
{
node = allocz(sizeof(struct mib_node));
node->empty.flags = 0;
}
node->empty.id = LOAD_U8(oid->prefix);
/* add node into the parent's children array */
node_inner->children[LOAD_U8(oid->prefix)] = node;
node_inner = &node->inner;
walk.stack[walk.stack_pos++] = node;
}
}
/* snmp_internet + 2 = empty + snmp_internet + prefix */
if (snmp_oid_is_prefixed(oid) &&
walk.stack_pos == ARRAY_SIZE(snmp_internet) + 2 &&
LOAD_U8(oid->n_subid) == 0 &&
mib_node_is_leaf(node) == is_leaf)
return node;
if (mib_node_is_leaf(node))
return node;
u8 subids = LOAD_U8(oid->n_subid);
u32 old_len = node_inner->child_len;
u32 child_id = oid->ids[walk.id_pos];
node_inner->child_len = MAX(old_len, LOAD_U32(child_id) + 1);
node_inner->children = realloc(node_inner->children,
node_inner->child_len * sizeof(mib_node_u *));
for (u32 i = old_len; i < node_inner->child_len; i++)
node_inner->children[i] = NULL;
struct mib_node *parent;
/* break to loop before last node in the oid */
for (; walk.id_pos < subids - 1;)
{
parent = node_inner;
node_inner = allocz(sizeof(struct mib_node));
parent->children[child_id] = (mib_node_u *) node_inner;
node_inner->c.id = child_id;
child_id = LOAD_U32(oid->ids[++walk.id_pos]);
node_inner->child_len = child_id + 1;
node_inner->children = allocz(node_inner->child_len * sizeof(mib_node_u *));
/*
node_inner->child_len = (child_id == 0) ? 0 : child_id;
node_inner->children = (child_id == 0) ? NULL
: allocz(node_inner->child_len * sizeof(mib_node_u *));
*/
}
parent = node_inner;
mib_node_u *last;
if (is_leaf)
{
last = allocz(sizeof(struct mib_leaf));
struct mib_leaf *leaf = &last->leaf;
parent->children[child_id] = (mib_node_u *) leaf;
leaf->c.id = child_id;
//leaf->c.id = LOAD_U32(oid->ids[subids - 1]);
leaf->c.flags = MIB_TREE_LEAF;
}
else
{
last = allocz(sizeof(struct mib_node));
node_inner = &last->inner;
parent->children[child_id] = (mib_node_u *) node_inner;
node_inner->c.id = child_id;
//node_inner->c.id = LOAD_U32(oid->ids[subids - 1]);
/* fields c.flags, child_len and children are set by zeroed allocz() */
}
return last;
}
#if 0
// TODO merge functions mib_tree_add and mib_tree_insert into one with public iface
mib_node_u *
mib_tree_add(struct snmp_proto *p, struct mib_tree *t, const struct oid *oid, uint size, int is_leaf)
{
struct mib_walk_state walk = { };
mib_node_u *known = mib_tree_find(t, &walk, oid);
if (known)
return known;
known = walk.stack[walk.stack_pos];
// redundant ??, if not, would be returned from find
if (walk.id_pos_abs == oid->n_subid)
return known;
if (walk.id_pos_rel < 0)
if (walk.id_pos_abs < oid->n_subid && (u32) walk.id_pos_rel == known->id_len)
{
if (known->child_len >= oid->ids[walk.id_pos_abs]) // abs +1?
{
u32 old_len = known->child_len;
known->child_len = oid->ids[walk.id_pos_abs] + 1;
known->children = mb_realloc(known->children,
known->child_len * sizeof(struct mib_node *));
for (uint i = old_len; i < known->child_len; i++)
known->children[i] = NULL;
}
/* find would return it
if (known->children[oid->ids[]])
return known->children[oid->ids[]];
*/
struct mib_node *node = mb_alloc(p->p.pool, sizeof(struct mib_node));
node->id_len = oid->n_subid - walk.id_pos_abs;
node->ids = mb_alloc(p->p.pool, node->id_len * sizeof(u32));
node->flags = 0;
node->children = NULL;
node->child_len = 0;
node->child_count = 0;
known->child_count++;
known->children[oid->ids[0]] = node;
return node;
}
else if (walk.id_pos_abs < oid->n_subid)
{
/* We known that walk.id_pos_rel < known->id_len */
struct mib_node *parent = mb_alloc(p->p.pool, sizeof(struct mib_node));
parent->id_len = known->id_len - walk.id_pos_rel;
parent->ids = mb_alloc(p->p.pool,
parent->id_len * sizeof(struct mib_node *));
memcpy(&parent->ids, &known->ids, parent->id_len * sizeof(struct mib_node *));
u32 *ids = mb_alloc(p->p.pool,
(known->id_len - walk.id_pos_rel) * sizeof(u32));
memcpy(ids, &known->ids[parent->id_len],
(known->id_len - parent->id_len) * sizeof(struct mib_node *));
mb_free(known->ids);
known->id_len = known->id_len - walk.id_pos_rel;
known->ids = ids;
parent->child_len = MAX(known->ids[0], oid->ids[walk.id_pos_abs]) + 1;
parent->children = mb_allocz(p->p.pool,
parent->child_len * sizeof(struct mib_node *));
parent->children[known->ids[0]] = known;
struct mib_node *child = mb_alloc(p->p.pool, sizeof(struct mib_node));
child->id_len = oid->n_subid - walk.id_pos_abs - parent->id_len;
child->ids = mb_alloc(p->p.pool,
child->id_len * sizeof(struct mib_node *));
memcpy(&child->ids, &oid->ids[oid->n_subid - child->id_len],
child->id_len * sizeof(u32));
// TODO test that we do not override the known
parent->children[child->ids[0]] = child;
return child;
}
else if (walk.id_pos_abs > oid->n_subid)
die("unreachable");
return NULL;
}
#endif
/*
int
mib_tree_insert(struct snmp_proto *p, struct mib_tree *t, struct oid *oid)
{
ASSUME(oid);
struct mib_walk_state walk = { };
struct mib_node *node = mib_tree_find(t, &walk, oid);
struct mib_leaf *leaf = NULL;
if (!node)
{
node = walk.stack[walk.stack_pos];
if (walk.id_pos_abs > oid->n_subid)
{
}
else / * walk.id_pos_abs <= oid->n_subid * /
{
leaf = mb_alloc(p->p.pool, sizeof(struct mib_leaf));
leaf->id_len = oid->n_subid - walk.id_pos_abs;
leaf->ids = mb_alloc(p->p.pool, leaf->id_len * sizeof(struct mib_node *));
memcpy(&leaf->ids, &oid->ids[oid->n_subid - leaf->id_len],
leaf->id_len * sizeof(u32));
leaf->flags = 0;
leaf->children = NULL;
leaf->child_len = 0;
leaf->child_count = 0;
}
}
}
*/
#if 0
int
mib_tree_insert(struct snmp_proto *p, struct mib_tree *t, struct oid *oid, struct mib_leaf *leaf)
{
ASSUME(oid);
struct mib_walk_state walk = { };
struct mib_node *node = mib_tree_find(t, &walk, oid);
struct mib_node *leaf_node = &leaf->n;
if (!node)
{
node = walk.stack[walk.stack_pos];
// can this really happen ??
if (walk.id_pos_abs > oid->n_subid)
{
struct mib_node *parent = mb_alloc(p->p.pool, sizeof(struct mib_node));
parent->id_len = walk.id_pos_abs - oid->n_subid; // -1?
parent->ids = mb_alloc(p->p.pool, parent->id_len * sizeof(u32));
memcpy(&parent->ids, &node->ids, parent->id_len * sizeof(u32));
u32 *ids = mb_alloc(p->p.pool,
(node->id_len - parent->id_len) * sizeof(u32));
node->id_len = node->id_len - parent->id_len;
memcpy(ids, &node->ids[parent->id_len], node->id_len * sizeof(u32));
mb_free(node->ids);
node->ids = ids;
parent->child_count = 2;
parent->child_len = MAX(node->ids[0], oid->ids[walk.id_pos_abs]) + 1;
parent->children = mb_allocz(p->p.pool,
parent->child_len * sizeof(struct mib_node *));
parent->children[node->ids[0]] = node;
parent->children[leaf_node->ids[0]] = leaf_node;
return 1;
}
else
{
mb_free(leaf_node->ids);
leaf_node->id_len = oid->n_subid - walk.id_pos_abs;
leaf_node->ids = mb_alloc(p->p.pool, leaf_node->id_len * sizeof(u32));
return 1;
}
}
if (mib_node_is_leaf(node))
{
struct mib_leaf *l = SKIP_BACK(struct mib_leaf, n, node);
insert_node(&leaf->leafs, &l->leafs);
return 1;
}
if (node->child_len > 0)
return 0;
// problem when node->id_len + (walk.id_pos_abs - walk.id_pos_rel) > oid->n_subid
if (walk.id_pos_abs < oid->n_subid) // +-1??
{
leaf_node->id_len = node->id_len - walk.id_pos_abs;
leaf_node->ids = mb_alloc(p->p.pool, leaf_node->id_len * sizeof(u32));
memcpy(&leaf_node->ids, &oid->ids[walk.id_pos_abs], leaf_node->id_len * sizeof(u32));
leaf_node->child_len = leaf_node->child_count = 0;
leaf_node->children = NULL;
return 1;
}
else
return 0;
return 1;
}
#endif
/*
int
mib_tree_remove(struct mib_tree *tree, struct oid *oid)
{
struct mib_walk_state walk = { };
struct mib_node *node = mib_tree_find(tree, &walk, oid);
if (!node)
return 0;
mib_tree_delete(&walk);
//mib_tree_delete(tree, &walk);
return 1;
}
*/
int
mib_tree_remove(struct mib_tree *t, const struct oid *oid)
{
struct mib_walk_state walk = { };
mib_node_u *node = mib_tree_find(t, &walk, oid);
if (!node)
return 0;
else
{
(void) mib_tree_delete(t, &walk);
return 1;
}
}
int
mib_tree_delete(struct mib_tree *t, struct mib_walk_state *walk)
{
int deleted = 0;
ASSUME(t);
/* (walk->stack_pos < 2) It is impossible to delete root node */
if (!walk || !walk->id_pos || walk->stack_pos < 2)
return 0;
struct mib_node *parent = &walk->stack[walk->stack_pos - 2]->inner;
mib_node_u *node = walk->stack[walk->stack_pos - 1];
struct mib_walk_state delete = {
.id_pos = walk->id_pos,
.stack_pos = 2,
.stack = {
(mib_node_u *) parent,
node,
NULL,
},
};
u32 last_id = 0;
while (delete.stack_pos > 1)
{
continue_while: /* like outer continue, but skip always true condition */
parent = (struct mib_node *) delete.stack[delete.stack_pos - 2];
if (mib_node_is_leaf(node))
{
/* Free leaf node */
last_id = node->leaf.c.id;
parent->children[last_id] = NULL;
delete.stack[--delete.stack_pos] = NULL;
free(node);
deleted++;
node = delete.stack[delete.stack_pos - 1];
continue; /* here, we couldn't skip the while condition */
}
struct mib_node *node_inner = &node->inner;
mib_node_u *child = NULL;
for (u32 id = last_id; id < node_inner->child_len; id++)
{
/* Recursively traverse child nodes */
child = node_inner->children[id];
if (!child)
continue;
last_id = 0;
delete.stack[delete.stack_pos++] = child;
parent = node_inner;
node = child;
goto continue_while; /* outer continue */
}
/* Free inner node without any children */
last_id = node_inner->c.id;
parent->children[last_id] = NULL;
delete.stack[--delete.stack_pos] = NULL;
free(node_inner->children);
free(node_inner);
deleted++;
node = (mib_node_u *) parent;
/* skip check for deleted node in loop over children */
last_id++;
}
/* delete the node from original stack */
walk->stack[--walk->stack_pos] = NULL;
node = walk->stack[walk->stack_pos - 1];
struct mib_node *node_inner = &node->inner;
u32 id;
for (id = 0; id < node_inner->child_len; id++)
{
if (node_inner->children[id] != NULL)
break;
}
if (id == node_inner->child_len)
{
/* all the children are NULL */
free(node_inner->children);
node_inner->children = NULL;
node_inner->child_len = 0;
}
return deleted;
}
/* currently support only search with blank new walk state */
/* requires non-NULL walk */
/* TODO doc string, user should check if the node is not root (or at least be
* aware of that */
mib_node_u *
mib_tree_find(const struct mib_tree *t, struct mib_walk_state *walk, const struct oid *oid)
{
ASSERT(t && walk);
if (!oid || snmp_is_oid_empty(oid))
{
walk->stack_pos = 1;
walk->stack[0] = (mib_node_u *) &t->root;
return (snmp_is_oid_empty(oid)) ? (mib_node_u *) &t->root : NULL;
}
mib_node_u *node;
struct mib_node *node_inner;
u8 oid_pos = walk->id_pos = 0;
node = walk->stack[walk->stack_pos++] = (mib_node_u *) &t->root;
#if 0
u8 oid_pos = walk->id_pos;
if (walk->stack_pos > 0)
node = walk->stack[walk->stack_pos];
else
node = walk->stack[walk->stack_pos++] = (mib_node_u *) &t->root;
if (mib_node_is_leaf(node))
{
if (snmp_oid_is_prefixed(oid) && LOAD_U8(oid->n_subid) + ARRAY_SIZE(snmp_internet) + 1 == walk->id_pos)
return node;
else if (!snmp_oid_is_prefixed(oid) && LOAD_U8(oid->n_subid) + 1 == walk->id_pos)
return node;
/* it could hold that LOAD_U8(oid->n_subid) >= walk->id_pos */
return NULL;
}
#endif
node_inner = &node->inner;
ASSERT(node && !mib_node_is_leaf(node));
/* Handling of prefixed OID */
if (snmp_oid_is_prefixed(oid))
{
uint i;
/* walking the snmp_internet prefix itself */
for (i = 0; i < ARRAY_SIZE(snmp_internet); i++)
{
if (node_inner->child_len <= snmp_internet[i])
return NULL;
node = node_inner->children[snmp_internet[i]];
node_inner = &node->inner;
if (!node)
return NULL;
ASSERT(node->empty.id == snmp_internet[i]);
walk->stack[walk->stack_pos++] = node;
if (mib_node_is_leaf(node))
return NULL;
}
/* walking the prefix continuation (OID field oid->prefix) */
u8 prefix = LOAD_U8(oid->prefix);
if (node_inner->child_len <= prefix)
return NULL;
node = node_inner->children[prefix];
node_inner = &node->inner;
if (!node)
return NULL;
ASSERT(node->empty.id == prefix);
walk->stack[walk->stack_pos++] = node;
if (mib_node_is_leaf(node) && LOAD_U8(oid->n_subid) > 0)
return NULL;
}
u8 subids = LOAD_U8(oid->n_subid);
if (subids == 0)
return (node == (mib_node_u *) &t->root) ? NULL : node;
/* loop for all OID's ids except the last one */
for (oid_pos = 0; oid_pos < subids - 1; oid_pos++) // remove oid_pos assignment
{
u32 id = LOAD_U32(oid->ids[oid_pos]);
if (node_inner->child_len <= id)
{
walk->id_pos = oid_pos;
return NULL;
}
node = node_inner->children[id];
node_inner = &node->inner;
if (!node)
{
walk->id_pos = oid_pos;
return NULL;
}
ASSERT(node->empty.id == id);
walk->stack[walk->stack_pos++] = node;
if (mib_node_is_leaf(node))
{
walk->id_pos = ++oid_pos;
return NULL;
}
}
walk->id_pos = oid_pos;
u32 last_id = LOAD_U32(oid->ids[oid_pos]);
if (node_inner->child_len <= last_id)
return NULL;
node = node_inner->children[last_id];
node_inner = &node->inner;
if (!node)
return NULL;
/* here, the check of node being a leaf is intentionally omitted
* because we may need to search for a inner node */
ASSERT(node->empty.id == last_id);
walk->id_pos = ++oid_pos;
return walk->stack[walk->stack_pos++] = node;
}
void
mib_tree_walk_init(struct mib_walk_state *walk)
{
walk->id_pos = 0;
walk->stack_pos = 0;
memset(&walk->stack, 0, sizeof(walk->stack));
}
/*
void
mib_node_free(mib_node_u *node)
{
if (!mib_node_is_leaf(node))
{
struct mib_node *node_inner = &node->inner;
node_inner->child_len = 0;
free(node_inner->children);
node_inner->children = NULL;
}
free(node);
}
*/
mib_node_u *
mib_tree_walk_next(struct mib_tree *t, struct mib_walk_state *walk)
{
ASSERT(t && walk);
u32 next_id = 0;
if (walk->stack_pos == 0)
return NULL;
mib_node_u *node = walk->stack[walk->stack_pos - 1];
if (mib_node_is_leaf(node))
{
next_id = node->leaf.c.id + 1;
walk->stack[--walk->stack_pos] = NULL;
node = walk->stack[walk->stack_pos - 1];
}
while (walk->stack_pos > 0)
{
node = walk->stack[walk->stack_pos - 1];
if (mib_node_is_leaf(node))
{
walk->stack[walk->stack_pos++] = node;
return node;
}
struct mib_node *node_inner = &node->inner;
for (u32 id = next_id; id < node_inner->child_len; id++)
{
mib_node_u *child = node_inner->children[id];
if (!child)
continue;
walk->stack[walk->stack_pos++] = child;
return child;
}
next_id = node_inner->c.id + 1;
walk->stack[--walk->stack_pos] = NULL;
}
return NULL;
}
#if 0
struct mib_node *
mib_tree_walk_next(struct mib_walk_state *walk)
{
ASSUME(walk->stack[walk->stack_pos]);
if (walk->stack_pos == 0 && walk->stack[0] &&
walk->stack[0]->flags & (MIB_TREE_REG_ACK || MIB_TREE_REG_WAIT))
return walk->stack[0];
struct mib_node *node = walk->stack[walk->stack_pos];
u32 id;
find_leaf:
while (!mib_node_is_leaf(node))
{
for (id = 0; id < node->child_len; id++)
{
if (node->children[id])
{
node = node->children[id];
walk->stack[++walk->stack_pos] = node;
break;
}
}
if (node->flags & (MIB_TREE_REG_ACK || MIB_TREE_REG_WAIT))
return node;
}
id = node->ids[0];
while (walk->stack_pos)
{
walk->stack[walk->stack_pos] = NULL;
--walk->stack_pos;
node = walk->stack[walk->stack_pos];
if (id + 1 != node->child_len)
break;
}
if (id + 1 == node->child_len)
return walk->stack[0] = NULL;
node = node->children[id + 1];
walk->stack_pos++;
walk->stack[walk->stack_pos] = node;
goto find_leaf;
}
#endif
struct mib_leaf *
mib_tree_walk_next_leaf(struct mib_tree *t, struct mib_walk_state *walk)
{
(void) t;
if (walk->stack_pos == 0)
return NULL;
u32 next_id = 0;
mib_node_u *node = walk->stack[walk->stack_pos - 1];
if (mib_node_is_leaf(node) && walk->stack_pos > 1)
{
next_id = node->leaf.c.id + 1;
walk->stack[--walk->stack_pos] = NULL;
node = walk->stack[walk->stack_pos - 1]; // does it underflow ??
}
else if (mib_node_is_leaf(node))
{
/* walk->stack_pos == 1, so we NULL out the last stack field */
walk->stack[--walk->stack_pos] = NULL;
return NULL;
}
mib_node_u *parent = (walk->stack_pos <= 1) ? NULL :
walk->stack[walk->stack_pos - 2];
while (walk->stack_pos > 0)
{
continue_while:
node = walk->stack[walk->stack_pos - 1];
if (mib_node_is_leaf(node))
{
walk->stack[walk->stack_pos++] = node;
return (struct mib_leaf *) node;
}
struct mib_node *node_inner = &node->inner;
for (u32 id = next_id; id < node_inner->child_len; id++)
{
mib_node_u *child = node_inner->children[id];
if (!child)
continue;
next_id = 0;
walk->stack[walk->stack_pos++] = child;
/* node is assign at the beginning of the while loop (from stack) */
goto continue_while;
}
while (walk->stack_pos > 1) // endless loop here possible ??
{
parent = walk->stack[walk->stack_pos - 2];
node = walk->stack[walk->stack_pos - 1];
ASSUME(mib_node_is_leaf(node));
if (node->leaf.c.id + 1 == parent->inner.child_len)
walk->stack[--walk->stack_pos] = NULL;
next_id = node->inner.c.id + 1;
}
}
return NULL;
}
#if 0
struct mib_leaf *
mib_tree_next_leaf(struct mib_walk_state *walk)
{
ASSUME(walk->stack[walk->stack_pos] &&
mib_node_is_leaf(walk->stack[walk->stack_pos]));
struct mib_node *node = walk->stack[walk->stack_pos];
u32 id;
while (walk->stack_pos)
{
id = node->ids[0];
walk->stack[walk->stack_pos] = NULL;
--walk->stack_pos;
node = walk->stack[walk->stack_pos];
if (id + 1 != node->child_len)
break;
}
if (id + 1 == node->child_len)
return (struct mib_leaf *) (walk->stack[0] = NULL);
id++;
while (!mib_node_is_leaf(node))
{
for (; id < node->child_len && !node->children[id]; id++)
;
node = node->children[id];
walk->stack[++walk->stack_pos] = node;
id = 0;
}
return (struct mib_leaf *) node;
}
#endif

144
proto/snmp/mib_tree.h Normal file
View File

@ -0,0 +1,144 @@
#ifndef _BIRD_SNMP_MIB_TREE_
#define _BIRD_SNMP_MIB_TREE_
#include "lib/resource.h"
#include "lib/lists.h"
#include "lib/birdlib.h"
#include "subagent.h"
#define MIB_TREE_NO_FLAGS 0x00
#define MIB_TREE_LEAF 0x01
#define MIB_TREE_HAS_HOOKS 0x02
typedef union mib_node_union mib_node_u;
struct mib_node_core {
u32 id;
u8 flags;
};
struct mib_node {
struct mib_node_core c;
mib_node_u **children;
u32 child_len;
};
struct mib_leaf {
struct mib_node_core c;
enum snmp_search_res (*filler)(struct snmp_proto_pdu *pc, struct agentx_varbind **vb);
enum agentx_type type;
int size;
};
union mib_node_union {
struct mib_node_core empty;
struct mib_node inner;
struct mib_leaf leaf;
};
/*
cannonical names
find
walk_init
walk_next
init
insert
delete / remove
*/
/*
* The stack size include empty prefix (mib tree root).
*/
#define MIB_WALK_STACK_SIZE 33
STATIC_ASSERT(OID_MAX_LEN < MIB_WALK_STACK_SIZE);
struct mib_walk_state {
u8 id_pos; /* points after last matching subid in OID */
u32 stack_pos; /* points after last valid stack node */
mib_node_u *stack[MIB_WALK_STACK_SIZE];
};
struct mib_tree {
struct mib_node root;
};
void mib_tree_init(pool *p, struct mib_tree *t);
void mib_tree_walk_init(struct mib_walk_state *state);
//void mib_node_free(mib_node_u *node);
//void mib_tree_free(struct mib_tree *tree);
mib_node_u *mib_tree_add(pool *p, struct mib_tree *tree, const struct oid *oid, int is_leaf);
int mib_tree_remove(struct mib_tree *t, const struct oid *oid);
int mib_tree_delete(struct mib_tree *t, struct mib_walk_state *state);
mib_node_u *mib_tree_find(const struct mib_tree *tree, struct mib_walk_state *walk, const struct oid *oid);
mib_node_u *mib_tree_next(struct mib_tree *tree, mib_node_u *end);
static inline int
mib_node_is_leaf(const mib_node_u *node)
{
return node->empty.flags & MIB_TREE_LEAF;
}
/*
WALK OID ID POS !!!
assert on STACK SIZE overflow resp fix entering the too long OIDs
Enumerace divnych pripadu
OID { n_subid 0, prefix 0 } ids NULL
OID { n_subid 0, prefix 2 } ids NULL <- todle je divny
OID { n_subid 1, prefix 0 } ids { 1 }
OID { n_subid 2, prefix 0 } ids { 1, 31 }
OID { n_subid 3, prefix 0 } ids { 1, 30, 32 }
OID { n_subid 7, prefix 0 } ids { 1, 2, 3, 4, 5, 6, 7 }
OID { n_subid 1, prefix 4 } ids { 8 }
OID { n_subid 2, prefix 19 } ids { 3, 2 }
OID { n_subid 3, prefix 5 } ids { 3, 9, 1 }
OID { n_subid 4, prefix 2 } ids { 1, 15, 1, 2 } <- obecny priklad
hledani
odstraneni
odstraneni stromu/podstromu
nasledovnik
nasledovnik list
pridani do vrcholu do stromu
TODO
add
next
next leaf
find with non-empty walk state
je opravdu potreba mit v vsech funkcich argument stromu (struct mib_tree *t) ?
>> walk, walk_init, next, next_leaf <<
otestovat neprefixovane OID v prefixovanem strome
a prefixove OID v neprefixovanem strome
TESTING TREES
s internet prefixem
- jinak prazdny
- jeden vrchol
- dva vrcholy
- 3, 4,
- rand() vrcholu
cesta, vidlicka, hrabe
bez internet prefixu
- uplne prazdny
- jediny vrchol (0, 1, 300, rand())
- dva vrcholy
- tri vrcholy
- ctyri vrcholy
- pet vrcholu jako v internet ale s prefixem = 300
- rand() vrcholu rand() hodnot
cesta vidlicka hrabe
*/
#endif

View File

@ -511,7 +511,6 @@ snmp_start(struct proto *P)
/* We create copy of bonds to BGP protocols. */
HASH_INIT(p->bgp_hash, p->pool, 10);
snmp_bgp_start(p);
return snmp_set_state(p, SNMP_INIT);
@ -550,7 +549,7 @@ skip:
}
/*
* snmp_reconfigure - Test if SNMP instance is reconfigurable
* snmp_reconfigure - Indicate instance reconfigurability
* @P - SNMP protocol generic handle, current state
* @CF - SNMP protocol configuration generic handle carring new values
*
@ -565,16 +564,16 @@ snmp_reconfigure(struct proto *P, struct proto_config *CF)
const struct snmp_config *new = SKIP_BACK(struct snmp_config, cf, CF);
/* We are searching for configuration changes */
int retval = snmp_reconfigure_logic(p, new);
int config_changed = snmp_reconfigure_logic(p, new);
if (retval)
if (config_changed)
{
/* Reinitialize the hash after snmp_shutdown() */
HASH_INIT(p->bgp_hash, p->pool, 10);
snmp_bgp_start(p);
}
return retval;
return config_changed;
}
/*

File diff suppressed because it is too large Load Diff

View File

@ -9,6 +9,7 @@
*/
#include "snmp_utils.h"
#include <stdio.h>
inline void
snmp_pdu_context(struct snmp_pdu *pdu, sock *sk)
@ -45,23 +46,90 @@ snmp_varbind_data(const struct agentx_varbind *vb)
return (void *)&vb->name + name_size;
}
struct oid *
snmp_varbind_set_name_len(struct snmp_proto *p, struct agentx_varbind **vb, u8 len, struct snmp_pdu *c)
{
struct oid *oid = &(*vb)->name;
if (LOAD_U8(oid->n_subid) >= len)
{
c->size += (LOAD_U8(oid->n_subid) - len) * sizeof(u32);
STORE_U8(oid->n_subid, len);
return oid;
}
/* We need more space */
ASSUME(len >= LOAD_U8(oid->n_subid));
uint diff_size = (len - LOAD_U8(oid->n_subid)) * sizeof(u32);
if (c->size < diff_size)
{
snmp_manage_tbuf(p, (void **) vb, c);
oid = &(*vb)->name;
}
ASSERT(c->size >= diff_size);
c->size -= diff_size;
STORE_U8(oid->n_subid, len);
return &(*vb)->name;
}
void
snmp_varbind_duplicate_hdr(struct snmp_proto *p, struct agentx_varbind **vb, struct snmp_pdu *c)
{
ASSUME(vb != NULL && *vb != NULL);
uint hdr_size = snmp_varbind_header_size(*vb);
if (c->size < hdr_size)
snmp_manage_tbuf(p, (void **) vb, c);
ASSERT(c->size >= hdr_size);
byte *buffer = c->buffer;
ADVANCE(c->buffer, c->size, hdr_size);
memcpy(buffer, *vb, hdr_size);
*vb = (struct agentx_varbind *) buffer;
}
/**
* snmp_is_oid_empty - check if oid is null-valued
* @oid: object identifier to check
*
* Test if the oid header is full of zeroes. For NULL-pointer @oid returns 0.
* We ignore include field to prevent weird behaviour.
*/
int
inline int
snmp_is_oid_empty(const struct oid *oid)
{
/* We intentionaly ignore padding that should be zeroed */
if (oid != NULL)
return LOAD_U8(oid->n_subid) == 0 && LOAD_U8(oid->prefix) == 0 &&
LOAD_U8(oid->include) == 0;
return LOAD_U8(oid->n_subid) == 0 && LOAD_U8(oid->prefix) == 0;
// && LOAD_U8(oid->include) == 0;
else
return 0;
}
/*
* snmp_oid_is_prefixable - check for prefixed form conversion possibility
* @oid: object identfier to check
*
* Check if it is possible to convert @oid to prefixed form. The condition of
* that is standart .1.3.6.1 internet prefix and 5-th id that fits in one byte.
*/
inline int
snmp_oid_is_prefixable(const struct oid *oid)
{
if (oid->n_subid < 5)
return 0;
for (int i = 0; i < 4; i++)
if (LOAD_U32(oid->ids[i]) != snmp_internet[i])
return 0;
if (LOAD_U32(oid->ids[4]) >= 256)
return 0;
return 1;
}
/**
* snmp_pkt_len - returns size of SNMP packet payload (without header)
* @buf: packet first byte
@ -90,6 +158,20 @@ snmp_oid_copy(struct oid *dest, const struct oid *src)
STORE_U32(dest->ids[i], src->ids[i]);
}
/* this function assumes enougth space inside dest is allocated */
void
snmp_oid_copy2(struct oid *dest, const struct oid *src)
{
/* The STORE_U8() and LOAD_U8() cancel out */
dest->n_subid = src->n_subid;
dest->prefix = src->prefix;
dest->include = src->include ? 1 : 0;
dest->reserved = 0;
/* The STORE_U32() and LOAD_U32 cancel out */
memcpy(dest->ids, src->ids, LOAD_U8(src->n_subid) * sizeof(u32));
}
/*
* snmp_oid_duplicate - duplicate an OID from memory pool
* @pool: pool to use
@ -181,6 +263,7 @@ snmp_set_varbind_type(struct agentx_varbind *vb, enum agentx_type t)
{
ASSUME(t != AGENTX_INVALID);
STORE_U16(vb->type, t);
STORE_U16(vb->reserved, 0);
}
/* Internal wrapper */
@ -470,7 +553,8 @@ byte *
snmp_put_ip4(byte *buf, ip4_addr addr)
{
/* octet string has size 4 bytes */
STORE_PTR(buf, 4);
STATIC_ASSERT(sizeof(ip4_addr) == sizeof(u32));
STORE_PTR(buf, sizeof(ip4_addr));
/* Always use Network byte order */
put_u32(buf+4, ip4_to_u32(addr));
@ -544,46 +628,74 @@ snmp_oid_ip4_index(struct oid *o, uint start, ip4_addr addr)
int
snmp_oid_compare(const struct oid *left, const struct oid *right)
{
if (left->prefix == 0 && right->prefix == 0)
const u8 left_subids = LOAD_U8(left->n_subid);
const u8 right_subids = LOAD_U8(right->n_subid);
const u8 left_prefix = LOAD_U8(left->prefix);
const u8 right_prefix = LOAD_U8(right->prefix);
if (left_prefix == 0 && right_prefix == 0)
goto test_ids;
if (right->prefix == 0)
if (right_prefix == 0)
return (-1) * snmp_oid_compare(right, left);
if (left->prefix == 0)
if (left_prefix == 0)
{
for (int i = 0; i < 4; i++)
if (left->ids[i] < snmp_internet[i])
size_t bound = MIN((size_t) left_subids, ARRAY_SIZE(snmp_internet));
for (size_t idx = 0; idx < bound; idx++)
{
u32 id = LOAD_U32(left->ids[idx]);
if (id < snmp_internet[idx])
return -1;
else if (left->ids[i] > snmp_internet[i])
else if (id > snmp_internet[idx])
return 1;
}
for (int i = 0; i < MIN(left->n_subid - 4, right->n_subid); i++)
if (left->ids[i + 4] < right->ids[i])
if (left_subids <= ARRAY_SIZE(snmp_internet))
return -1;
if (LOAD_U32(left->ids[4]) < (u32) right_prefix)
return -1;
else if (LOAD_U32(left->ids[4]) > (u32) right_prefix)
return 1;
int limit = MIN(left_subids - (int) ARRAY_SIZE(snmp_internet),
(int) right_subids);
for (int i = 0; i < limit; i++)
{
u32 left_id = LOAD_U32(left->ids[i + ARRAY_SIZE(snmp_internet)]);
u32 right_id = LOAD_U32(right->ids[i]);
if (left_id < right_id)
return -1;
else if (left->ids[i + 4] > right->ids[i])
else if (left_id > right_id)
return 1;
}
goto all_same;
}
if (left->prefix < right->prefix)
if (left_prefix < right_prefix)
return -1;
else if (left->prefix > right->prefix)
else if (left_prefix > right_prefix)
return 1;
test_ids:
for (int i = 0; i < MIN(left->n_subid, right->n_subid); i++)
if (left->ids[i] < right->ids[i])
{
u32 left_id = LOAD_U32(left->ids[i]);
u32 right_id = LOAD_U32(right->ids[i]);
if (left_id < right_id)
return -1;
else if (left->ids[i] > right->ids[i])
else if (left_id > right_id)
return 1;
}
all_same:
/* shorter sequence is before longer in lexicografical order */
if (left->n_subid < right->n_subid)
if (left_subids < right_subids)
return -1;
else if (left->n_subid > right->n_subid)
else if (left_subids > right_subids)
return 1;
else
return 0;
@ -665,60 +777,52 @@ agentx_type_size(enum agentx_type type)
return -1;
}
static inline byte *
snmp_varbind_type32(struct agentx_varbind *vb, uint size, enum agentx_type type, u32 val)
static inline void
snmp_varbind_type32(struct agentx_varbind *vb, struct snmp_pdu *c, enum agentx_type type, u32 val)
{
ASSUME(agentx_type_size(type) == 4); /* type has 4B representation */
if (size < (uint) agentx_type_size(type))
return NULL;
ASSUME(agentx_type_size(type) == 4); /* type as 4B representation */
snmp_set_varbind_type(vb, type);
u32 *data = snmp_varbind_data(vb);
STORE_PTR(data, val); /* note that the data has u32 type */
STORE_PTR(data, val);
data++;
return (byte *) data;
c->buffer = (byte *) data;
}
inline byte *
snmp_varbind_int(struct agentx_varbind *vb, uint size, u32 val)
inline void
snmp_varbind_int(struct agentx_varbind *vb, struct snmp_pdu *c, u32 val)
{
return snmp_varbind_type32(vb, size, AGENTX_INTEGER, val);
snmp_varbind_type32(vb, c, AGENTX_INTEGER, val);
}
inline byte *
snmp_varbind_counter32(struct agentx_varbind *vb, uint size, u32 val)
inline void
snmp_varbind_counter32(struct agentx_varbind *vb, struct snmp_pdu *c, u32 val)
{
return snmp_varbind_type32(vb, size, AGENTX_COUNTER_32, val);
snmp_varbind_type32(vb, c, AGENTX_COUNTER_32, val);
}
inline byte *
snmp_varbind_ticks(struct agentx_varbind *vb, uint size, u32 val)
inline void
snmp_varbind_ticks(struct agentx_varbind *vb, struct snmp_pdu *c, u32 val)
{
return snmp_varbind_type32(vb, size, AGENTX_TIME_TICKS, val);
snmp_varbind_type32(vb, c, AGENTX_TIME_TICKS, val);
}
inline byte *
snmp_varbind_gauge32(struct agentx_varbind *vb, uint size, s64 val)
inline void
snmp_varbind_gauge32(struct agentx_varbind *vb, struct snmp_pdu *c, s64 time)
{
return snmp_varbind_type32(vb, size, AGENTX_GAUGE_32,
MAX(0, MIN(val, UINT32_MAX)));
snmp_varbind_type32(vb, c, AGENTX_GAUGE_32, MAX(0, MIN(time, UINT32_MAX)));
}
inline byte *
snmp_varbind_ip4(struct agentx_varbind *vb, uint size, ip4_addr addr)
inline void
snmp_varbind_ip4(struct agentx_varbind *vb, struct snmp_pdu *c, ip4_addr addr)
{
if (size < snmp_str_size_from_len(4))
return NULL;
snmp_set_varbind_type(vb, AGENTX_IP_ADDRESS);
return snmp_put_ip4(snmp_varbind_data(vb), addr);
c->buffer = snmp_put_ip4(snmp_varbind_data(vb), addr);
}
// TODO doc string, we have already the varbind prepared
inline byte *
snmp_varbind_nstr(struct agentx_varbind *vb, uint size, const char *str, uint len)
snmp_varbind_nstr2(struct agentx_varbind *vb, uint size, const char *str, uint len)
{
if (size < snmp_str_size_from_len(len))
return NULL;
@ -727,6 +831,24 @@ snmp_varbind_nstr(struct agentx_varbind *vb, uint size, const char *str, uint le
return snmp_put_nstr(snmp_varbind_data(vb), str, len);
}
/*
* snmp_varbind_nstr - fill varbind context with octet string
* @vb: VarBind to use
* @c: PDU information
* @str: C-string to put as the VarBind data
* @len: length of the string @str
*
* Beware: this function assumes there is enough space in the underlaying
* TX-buffer. The caller has to provide that, see snmp_str_size_from_len() for
* more info.
*/
void
snmp_varbind_nstr(struct agentx_varbind *vb, struct snmp_pdu *c, const char *str, uint len)
{
snmp_set_varbind_type(vb, AGENTX_OCTET_STRING);
c->buffer = snmp_put_nstr(snmp_varbind_data(vb), str, len);
}
inline enum agentx_type
snmp_search_res_to_type(enum snmp_search_res r)
{
@ -786,3 +908,124 @@ snmp_oid_dump(const struct oid *oid)
log(L_WARN "OID DUMP END ====");
log(L_WARN);
}
void UNUSED
snmp_oid_log(const struct oid *oid)
{
char buf[1024] = { };
char *pos = buf;
if (snmp_oid_is_prefixed(oid))
{
for (uint i = 0; i < ARRAY_SIZE(snmp_internet); i++)
pos += snprintf(pos, buf + 1024 - pos, ".%u", snmp_internet[i]);
pos += snprintf(pos, buf + 1024 - pos, ".%u", oid->prefix);
}
for (int id = 0; id < oid->n_subid; id++)
pos += snprintf(pos, buf + 1024 - pos, ".%u", oid->ids[id]);
log(L_INFO "%s", buf);
}
/*
* snmp_oid_common_ancestor - find a common ancestor
* @left: first OID
* @right: second OID
* @out: buffer for result
*
* The @out must be large enough to always fit the resulting OID, a safe value
* is minimum between number of left subids and right subids. The result might
* be NULL OID in cases where there is no common subid. The result could be also
* viewed as longest common prefix.
*/
void
snmp_oid_common_ancestor(const struct oid *left, const struct oid *right, struct oid *out)
{
ASSERT(left && right && out);
STORE_U8(out->include, 0);
STORE_U8(out->reserved, 0);
u32 offset = 0;
u8 left_ids = LOAD_U8(left->n_subid), right_ids = LOAD_U8(right->n_subid);
int l = snmp_oid_is_prefixed(left), r = snmp_oid_is_prefixed(right);
if (l && r)
{
if (LOAD_U8(left->prefix) != LOAD_U8(right->prefix))
{
STORE_U8(out->n_subid, 4);
STORE_U8(out->prefix, 0);
for (uint id = 0; id < ARRAY_SIZE(snmp_internet); id++)
STORE_U32(out->ids[id], snmp_internet[id]);
return;
}
STORE_U8(out->prefix, LOAD_U8(left->prefix));
}
else if (!l && r)
{
if (left_ids == 0)
{
/* finish creating NULL OID */
STORE_U8(out->n_subid, 0);
STORE_U8(out->prefix, 0);
return;
}
for (uint id = 0; id < MIN(ARRAY_SIZE(snmp_internet), left_ids); id++)
{
if (LOAD_U32(left->ids[id]) != snmp_internet[id])
{
STORE_U8(out->n_subid, id);
STORE_U8(out->prefix, 0);
return;
}
STORE_U32(out->ids[id], snmp_internet[id]);
}
if (left_ids <= ARRAY_SIZE(snmp_internet))
{
STORE_U8(out->n_subid, left_ids);
return;
}
/* index 4 is conresponding to the prefix in prefixed OID */
if (LOAD_U32(left->ids[4]) != (u32) LOAD_U8(right->prefix))
{
STORE_U8(out->n_subid, ARRAY_SIZE(snmp_internet));
return;
}
/* delete snmp_internet from out->ids and store OID prefix */
offset = ARRAY_SIZE(snmp_internet) + 1;
STORE_U8(out->n_subid, LOAD_U8(out->n_subid) - ARRAY_SIZE(snmp_internet));
STORE_U8(out->prefix, LOAD_U8(right->prefix));
}
else if (l && !r)
{
snmp_oid_common_ancestor(right, left, out);
return;
}
ASSERT(offset <= left_ids);
u8 subids = 0;
for (u32 id = 0; id < MIN(left_ids - offset, right_ids); id++)
{
if (left->ids[offset + id] == right->ids[id])
{
subids++;
STORE_U32(out->ids[id], LOAD_U32(right->ids[id]));
}
else
break;
}
STORE_U8(out->n_subid, subids);
}

View File

@ -24,10 +24,19 @@ size_t snmp_str_size(const char *str);
/* type OID - Object Identifier */
int snmp_is_oid_empty(const struct oid *oid);
int snmp_oid_is_prefixable(const struct oid *oid);
uint snmp_oid_size(const struct oid *o);
size_t snmp_oid_size_from_len(uint n_subid);
void snmp_oid_copy(struct oid *dest, const struct oid *src);
void snmp_oid_copy2(struct oid *dest, const struct oid *src);
int snmp_oid_compare(const struct oid *first, const struct oid *second);
void snmp_oid_common_ancestor(const struct oid *left, const struct oid *right, struct oid *result);
static inline int
snmp_oid_is_prefixed(const struct oid *oid)
{
return LOAD_U8(oid->prefix) != 0;
}
/* type IPv4 */
int snmp_valid_ip4_index(const struct oid *o, uint start);
@ -44,6 +53,8 @@ uint snmp_varbind_size_unsafe(const struct agentx_varbind *vb);
size_t snmp_varbind_size_from_len(uint n_subid, enum agentx_type t, uint len);
int snmp_test_varbind(const struct agentx_varbind *vb);
void *snmp_varbind_data(const struct agentx_varbind *vb);
struct oid *snmp_varbind_set_name_len(struct snmp_proto *p, struct agentx_varbind **vb, u8 len, struct snmp_pdu *c);
void snmp_varbind_duplicate_hdr(struct snmp_proto *p, struct agentx_varbind **vb, struct snmp_pdu *c);
/*
* AgentX - PDU headers, types, contexts
@ -63,12 +74,12 @@ int snmp_test_close_reason(byte value);
/* Functions filling buffer a typed value */
struct agentx_varbind *snmp_create_varbind(byte *buf, struct oid *oid);
struct agentx_varbind *snmp_create_varbind_null(byte *buf);
byte *snmp_varbind_int(struct agentx_varbind *vb, uint size, u32 val);
byte *snmp_varbind_counter32(struct agentx_varbind *vb, uint size, u32 val);
byte *snmp_varbind_gauge32(struct agentx_varbind *vb, uint size, s64 val);
byte *snmp_varbind_ticks(struct agentx_varbind *vb, uint size, u32 val);
byte *snmp_varbind_ip4(struct agentx_varbind *vb, uint size, ip4_addr addr);
byte *snmp_varbind_nstr(struct agentx_varbind *vb, uint size, const char *str, uint len);
void snmp_varbind_int(struct agentx_varbind *vb, struct snmp_pdu *c, u32 val);
void snmp_varbind_counter32(struct agentx_varbind *vb, struct snmp_pdu *c, u32 val);
void snmp_varbind_gauge32(struct agentx_varbind *vb, struct snmp_pdu *c, s64 time);
void snmp_varbind_ticks(struct agentx_varbind *vb, struct snmp_pdu *c, u32 val);
void snmp_varbind_ip4(struct agentx_varbind *vb, struct snmp_pdu *c, ip4_addr addr);
void snmp_varbind_nstr(struct agentx_varbind *vb, struct snmp_pdu *c, const char *str, uint len);
/* Raw */
byte *snmp_no_such_object(byte *buf, struct agentx_varbind *vb, struct oid *oid);
@ -91,7 +102,12 @@ int snmp_registration_match(struct snmp_registration *r, struct agentx_header *h
void snmp_dump_packet(byte *pkt, uint size);
void snmp_oid_dump(const struct oid *oid);
void snmp_oid_log(const struct oid *oid);
enum agentx_type snmp_search_res_to_type(enum snmp_search_res res);
#define AGENTX_TYPE_INT_SIZE ((uint) agentx_type_size(AGENTX_INTEGER))
#define AGENTX_TYPE_IP4_SIZE ((uint) agentx_type_size(AGENTX_IP_ADDRESS))
#define AGENTX_TYPE_COUNTER32_SIZE ((uint) agentx_type_size(AGENTX_COUNTER_32))
#endif

View File

@ -2,7 +2,8 @@
"""
A very simple script used to test that BIRD does not segfaults on randomly split
AgentX PDUs.
AgentX PDUs. A split is when the AgentX PDUs do not align with socket's
write()/read() boundaries.
"""
import socket

View File

@ -61,16 +61,19 @@
*
*/
static void snmp_mib_fill2(struct snmp_proto *p, struct oid *oid, struct snmp_pdu *c);
//static void snmp_mib_fill(struct snmp_proto *p, struct oid *oid, struct snmp_pdu *c);
static void snmp_mib_fill(struct snmp_proto *p, struct agentx_varbind **vb, struct snmp_pdu *c);
static uint parse_response(struct snmp_proto *p, byte *buf);
static void do_response(struct snmp_proto *p, byte *buf);
static uint parse_gets_pdu(struct snmp_proto *p, byte *pkt);
static struct agentx_response *prepare_response(struct snmp_proto *p, struct snmp_pdu *c);
static void response_err_ind(struct snmp_proto *p, struct agentx_response *res, enum agentx_response_errs err, u16 ind);
static uint update_packet_size(struct agentx_header *start, byte *end);
static struct oid *search_mib(struct snmp_proto *p, const struct oid *o_start, const struct oid *o_end, struct oid *o_curr, struct snmp_pdu *c, enum snmp_search_res *result);
//static struct oid *search_mib2(struct snmp_proto *p, const struct oid *o_start, const struct oid *o_end, struct oid *o_curr, struct snmp_pdu *c, enum snmp_search_res *result);
u32 snmp_internet[] = { SNMP_ISO, SNMP_ORG, SNMP_DOD, SNMP_INTERNET };
static enum snmp_search_res search_mib(struct snmp_proto *p, struct agentx_varbind **vb_search, const struct oid *o_end, struct snmp_pdu *c);
const u32 snmp_internet[] = { SNMP_ISO, SNMP_ORG, SNMP_DOD, SNMP_INTERNET };
static inline int
snmp_is_active(struct snmp_proto *p)
@ -244,7 +247,7 @@ open_pdu(struct snmp_proto *p, struct oid *oid)
/* Make sure that we have enough space in TX-buffer */
if (c.size < AGENTX_HEADER_SIZE + TIMEOUT_SIZE + snmp_oid_size(oid) +
+ snmp_str_size(cf->description))
snmp_manage_tbuf(p, &c);
snmp_manage_tbuf(p, NULL, &c);
struct agentx_header *h = (void *) c.buffer;
ADVANCE(c.buffer, c.size, AGENTX_HEADER_SIZE);
@ -303,7 +306,7 @@ snmp_notify_pdu(struct snmp_proto *p, struct oid *oid, void *data, uint size, in
/* Make sure that we have enough space in TX-buffer */
if (c.size < sz)
snmp_manage_tbuf(p, &c);
snmp_manage_tbuf(p, NULL, &c);
struct agentx_header *h = (void *) c.buffer;
ADVANCE(c.buffer, c.size, AGENTX_HEADER_SIZE);
@ -329,7 +332,7 @@ snmp_notify_pdu(struct snmp_proto *p, struct oid *oid, void *data, uint size, in
/* TODO use time from last reconfiguration instead? [config->load_time] */
btime uptime = current_time() - boot_time;
snmp_varbind_ticks(vb, c.size, (uptime TO_S) / 100);
snmp_varbind_ticks(vb, &c, (uptime TO_S) / 100);
ASSUME(snmp_test_varbind(vb));
ADVANCE(c.buffer, c.size, snmp_varbind_size_unsafe(vb));
}
@ -398,7 +401,7 @@ un_register_pdu(struct snmp_proto *p, struct oid *oid, u32 bound, uint index, en
((bound > 1) ? BOUND_SIZE : 0);
if (c.size < sz)
snmp_manage_tbuf(p, &c);
snmp_manage_tbuf(p, NULL, &c);
struct agentx_header *h = (void *) c.buffer;
ADVANCE(c.buffer, c.size, AGENTX_HEADER_SIZE);
@ -480,7 +483,7 @@ close_pdu(struct snmp_proto *p, enum agentx_close_reasons reason)
#define REASON_SIZE sizeof(u32)
if (c.size < AGENTX_HEADER_SIZE + REASON_SIZE)
snmp_manage_tbuf(p, &c);
snmp_manage_tbuf(p, NULL, &c);
struct agentx_header *h = (void *) c.buffer;
ADVANCE(c.buffer, c.size, AGENTX_HEADER_SIZE);
@ -611,7 +614,7 @@ parse_test_set_pdu(struct snmp_proto *p, byte * const pkt_start)
snmp_pdu_context(&c, sk);
if (c.size < AGENTX_HEADER_SIZE)
snmp_manage_tbuf(p, &c);
snmp_manage_tbuf(p, NULL, &c);
res = prepare_response(p, &c);
@ -708,7 +711,7 @@ parse_sets_pdu(struct snmp_proto *p, byte * const pkt_start, enum agentx_respons
struct snmp_pdu c;
snmp_pdu_context(&c, p->sock);
if (c.size < sizeof(struct agentx_response))
snmp_manage_tbuf(p, &c);
snmp_manage_tbuf(p, NULL, &c);
struct agentx_response *r = prepare_response(p, &c);
@ -1069,11 +1072,67 @@ snmp_get_mib_class(const struct oid *oid)
* Return 0 if the created VarBind type is endOfMibView, 1 otherwise.
*/
static int
snmp_get_next2(struct snmp_proto *p, struct oid *o_start, struct oid *o_end,
snmp_get_next2(struct snmp_proto *p, struct agentx_varbind **vb_search, struct oid *o_end, struct snmp_pdu *c)
{
enum snmp_search_res r;
//struct oid *o_copy = search_mib(p, o_start, o_end, NULL, c, &r);
r = search_mib(p, vb_search, o_end, c);
struct oid *o_start = &(*vb_search)->name;
switch (r)
{
case SNMP_SEARCH_NO_OBJECT:
case SNMP_SEARCH_NO_INSTANCE:
case SNMP_SEARCH_END_OF_VIEW:;
snmp_set_varbind_type(*vb_search, AGENTX_END_OF_MIB_VIEW);
return 0;
case SNMP_SEARCH_OK:
default:
break;
}
// TODO TODO different API
snmp_mib_fill(p, vb_search, c);
/* override the error for GetNext-PDU object not find */
switch ((*vb_search)->type)
{
case AGENTX_NO_SUCH_OBJECT:
case AGENTX_NO_SUCH_INSTANCE:
case AGENTX_END_OF_MIB_VIEW:
(*vb_search)->type = AGENTX_END_OF_MIB_VIEW;
return 0;
default:
return 1;
}
o_start = &(*vb_search)->name;
if (c->size < snmp_varbind_hdr_size_from_oid(o_start))
snmp_manage_tbuf(p, (void **) vb_search, c);
snmp_set_varbind_type(*vb_search, AGENTX_END_OF_MIB_VIEW);
return 0;
}
#if 0
/*
* snmp_get_next - process single agentx-GetNext-PDU search range
* @p: SNMP protocol instance
* @o_start: SearchRange start OID
* @o_end: SearchRange end OID
* @c: transmit PDU context to use
*
* Return 0 if the created VarBind type is endOfMibView, 1 otherwise.
*/
static int
snmp_get_next3(struct snmp_proto *p, struct oid *o_start, struct oid *o_end,
struct snmp_pdu *c)
{
enum snmp_search_res r;
struct oid *o_copy = search_mib(p, o_start, o_end, NULL, c, &r);
struct oid *o_copy = search_mib2(p, o_start, o_end, NULL, c, &r);
struct agentx_varbind *vb = NULL;
switch (r)
@ -1110,7 +1169,7 @@ snmp_get_next2(struct snmp_proto *p, struct oid *o_start, struct oid *o_end,
{
/* basicaly snmp_create_varbind(c->buffer, o_copy), but without any copying */
vb = (void *) c->buffer;
snmp_mib_fill2(p, o_copy, c);
snmp_mib_fill(p, o_copy, c);
/* override the error for GetNext-PDU object not find */
switch (vb->type)
@ -1127,14 +1186,16 @@ snmp_get_next2(struct snmp_proto *p, struct oid *o_start, struct oid *o_end,
}
if (c->size < snmp_varbind_hdr_size_from_oid(o_start))
snmp_manage_tbuf(p, c);
snmp_manage_tbuf(p, (void **) vb, c);
vb = snmp_create_varbind(c->buffer, o_start);
vb->type = AGENTX_END_OF_MIB_VIEW;
ADVANCE(c->buffer, c->size, snmp_varbind_header_size(vb));
return 0;
}
#endif
#if 0
/*
* snmp_get_bulk - process one iteration of get bulk PDU
* @p: SNMP protocol instance
@ -1183,8 +1244,8 @@ snmp_get_bulk2(struct snmp_proto *p, struct oid *o_start, struct oid *o_end,
vb->type = AGENTX_END_OF_MIB_VIEW;
if (r == SNMP_SEARCH_OK)
/* the varbind will be recreated inside the snmp_mib_fill2() */
snmp_mib_fill2(p, o_curr, c);
/* the varbind will be recreated inside the snmp_mib_fill() */
snmp_mib_fill(p, o_curr, c);
else
ADVANCE(c->buffer, c->size, snmp_varbind_header_size(vb));
@ -1201,6 +1262,182 @@ snmp_get_bulk2(struct snmp_proto *p, struct oid *o_start, struct oid *o_end,
return 1;
}
}
#endif
#if 0
static int
snmp_get_bulk2(struct snmp_proto *p, struct agentx_varbind **vb_search, const struct oid *o_end, struct agentx_bulk_state *state, struct snmp_pdu *c)
{
//struct oid *o_curr = NULL;
//struct oid *o_predecessor = NULL;
enum snmp_search_res r;
uint i = 0;
r = search_mib(p, vb_search, o_end, c);
while (r != SNMP_SEARCH_END_OF_VIEW && i < state->repetition)
{
snmp_mib_fill(p, vb_search, c);
snmp_varbind_duplicate_hdr(p, vb_search, c);
// renew all pointers here
r = search_mib(p, vb_search, o_end, c);
}
return r == SNMP_SEARCH_END_OF_VIEW;
do
{
r = search_mib(p, vb_search, o_end, c);
snmp_mib_fill(p, vb_search, ;
o_predecessor = o_curr;
o_curr = search_mib(p, o_start, o_end, o_curr, c, &r);
i++;
} while (r != SNMP_SEARCH_END_OF_VIEW && i < state->repetition);
// TODO check if the approach below works
// it need to generate varbinds that will be only of type endOfMibView
/* Object Identifier fall-backs */
if (!o_curr)
o_curr = o_predecessor;
if (!o_curr)
o_curr = o_start;
uint sz = snmp_varbind_hdr_size_from_oid(o_curr);
if (c->size < sz)
{
c->error = AGENTX_RES_GEN_ERROR;
return 0;
}
/* we need the varbind handle to be able to override it's type */
struct agentx_varbind *vb = (void *) c->buffer;
vb->type = AGENTX_END_OF_MIB_VIEW;
if (r == SNMP_SEARCH_OK)
/* the varbind will be recreated inside the snmp_mib_fill() */
snmp_mib_fill(p, o_curr, c);
else
ADVANCE(c->buffer, c->size, snmp_varbind_header_size(vb));
/* override the error for GetBulk-PDU object not found */
switch (vb->type)
{
case AGENTX_NO_SUCH_OBJECT:
case AGENTX_NO_SUCH_INSTANCE:
case AGENTX_END_OF_MIB_VIEW:
vb->type = AGENTX_END_OF_MIB_VIEW;
return 0;
default:
return 1;
}
}
#endif
static inline struct oid *
snmp_oid_prefixize_unsafe(struct oid *dest, const struct oid *src)
{
u8 subids = LOAD_U8(src->n_subid) - 5;
dest->n_subid = subids;
STORE_U8(dest->prefix, (u8) LOAD_U32(src->ids[ARRAY_SIZE(snmp_internet)]));
STORE_U8(dest->include, (LOAD_U8(src->include)) ? 1 : 0);
STORE_U8(dest->reserved, 0);
/* The LOAD_U32() and STORE_U32() cancel out */
memcpy(&dest->ids[0], &src->ids[5], subids * sizeof(u32));
return dest;
}
#if 0
/*
* snmp_oid_prefixize - convert oid to prefixed form
* @p: SNMP protocol instance
* @oid: object identifier to convert
* @c: PDU context
*
* The function assumes that the supplied @oid is prefixable. The resulting OID
* is allocated from PDU buffer inside @c.
*/
struct oid *
snmp_oid_prefixize(struct snmp_proto *p, const struct oid *oid, struct snmp_pdu *c)
{
/* It may be beneficial to move to snmp_utils.c */
uint subids = LOAD_U8(oid->n_subid) - 5;
uint oid_size = snmp_oid_size(oid);
if (c->size < oid_size)
snmp_manage_tbuf(p, NULL, c);
// TODO check if the @oid is prefixable
ASSERT(c->size >= oid_size);
struct oid *result = c->buffer;
ADVANCE(c->buffer, c->size, oid_size);
return snmp_oid_prefixize_unsafe(result, oid);
}
#endif
/*
* snmp_vb_to_tx - create varbind from RX buffer OID
* @p: SNMP protocol instance
* @oid: object identifier located in RX buffer
* @c: PDU context
*
* Create NULL initialized VarBind inside TX buffer (from @c) whose vb->name is
* @oid. The @oid is not prefixed and is prefixable, the @oid is prefixed first.
* The protocol @p is used in cases of TX buffer space shortage.
*/
struct agentx_varbind *
snmp_vb_to_tx(struct snmp_proto *p, const struct oid *oid, struct snmp_pdu *c)
{
uint vb_hdr_size = snmp_varbind_hdr_size_from_oid(oid);
if (c->size < vb_hdr_size)
snmp_manage_tbuf(p, NULL, c);
ASSERT(c->size >= vb_hdr_size);
struct agentx_varbind *vb = (void *) c->buffer;
ADVANCE(c->buffer, c->size, sizeof(struct agentx_varbind) - sizeof(struct oid));
/* Move the c->buffer so that is points at &vb->name */
snmp_set_varbind_type(vb, AGENTX_NULL);
if (snmp_oid_is_prefixable(oid) && !snmp_oid_is_prefixed(oid))
{
u8 subids = LOAD_U8(oid->n_subid) - 5;
ADVANCE(c->buffer, c->size, snmp_oid_size_from_len(subids));
(void) snmp_oid_prefixize_unsafe(&vb->name, oid);
return vb;
}
ADVANCE(c->buffer, c->size, snmp_oid_size(oid));
snmp_oid_copy2(&vb->name, oid);
return vb;
}
/*
* snmp_oid_to_scratch - allocate temporal Object Identifier in prefixed form
* @oid: prefixed Object Identifier if possible
*/
static struct oid *
snmp_oid_to_scratch(const struct oid *oid)
{
struct oid *dest;
if (snmp_oid_is_prefixable(oid) && !snmp_oid_is_prefixed(oid))
{
u8 subids = LOAD_U8(oid->n_subid) - 5;
uint prefixed_size = sizeof(struct oid) + (subids * sizeof(u32));
dest = tmp_alloc(prefixed_size);
snmp_oid_prefixize_unsafe(dest, oid);
return dest;
}
uint oid_size = snmp_oid_size(oid);
dest = tmp_alloc(oid_size);
snmp_oid_copy2(dest, oid);
return dest;
}
/*
* update_packet_size - set PDU size
@ -1252,7 +1489,7 @@ response_err_ind(struct snmp_proto *p, struct agentx_response *res, enum agentx_
STORE_U32(res->index, 0);
}
static uint
static inline uint
parse_gets_error(struct snmp_proto *p, struct snmp_pdu *c, uint len)
{
TRACE(D_PACKETS, "SNMP error %u while parsing gets PDU", c->error);
@ -1276,7 +1513,9 @@ static uint
parse_gets_pdu(struct snmp_proto *p, byte * const pkt_start)
{
// TODO checks for c.size underflow
struct oid *o_start = NULL, *o_end = NULL;
//struct agentx_varbind *vb = NULL;
struct agentx_varbind *vb_start = NULL;
struct oid *o_end = NULL;
byte *pkt = pkt_start;
struct agentx_header *h = (void *) pkt;
@ -1318,12 +1557,16 @@ parse_gets_pdu(struct snmp_proto *p, byte * const pkt_start)
struct agentx_response *response_header = prepare_response(p, &c);
lp_state tmps;
lp_save(tmp_linpool, &tmps);
while (c.error == AGENTX_RES_NO_ERROR && pkt_size > 0)
{
lp_restore(tmp_linpool, &tmps);
/* We load search range start OID */
const struct oid *o_start_b = (void *) pkt;
const struct oid *o_start_rx = (void *) pkt;
uint sz;
if ((sz = snmp_oid_size(o_start_b)) > pkt_size)
if ((sz = snmp_oid_size(o_start_rx)) > pkt_size)
{
c.error = AGENTX_RES_PARSE_ERROR;
return parse_gets_error(p, &c, pkt_size);
@ -1337,8 +1580,8 @@ parse_gets_pdu(struct snmp_proto *p, byte * const pkt_start)
* The exactly same process of sanity checking is preformed while loading
* the SearchRange's end OID
*/
const struct oid *o_end_b = (void *) pkt;
if ((sz = snmp_oid_size(o_end_b)) > pkt_size)
const struct oid *o_end_rx = (void *) pkt;
if ((sz = snmp_oid_size(o_end_rx)) > pkt_size)
{
c.error = AGENTX_RES_PARSE_ERROR;
return parse_gets_error(p, &c, pkt_size);
@ -1349,17 +1592,15 @@ parse_gets_pdu(struct snmp_proto *p, byte * const pkt_start)
/* We don't too to check for oversided OID because the PDU has 8k size limit */
/* We create copy of OIDs outside of rx-buffer and also prefixize them */
o_start = snmp_prefixize(p, o_start_b);
o_end = snmp_prefixize(p, o_end_b);
vb_start = snmp_vb_to_tx(p, o_start_rx, &c);
o_end = snmp_oid_to_scratch(o_end_rx);
ASSERT(o_start);
ASSERT(vb_start);
ASSERT(o_end);
if (!snmp_is_oid_empty(o_end) && snmp_oid_compare(o_start, o_end) > 0)
if (!snmp_is_oid_empty(o_end) && snmp_oid_compare(&vb_start->name, o_end) > 0)
{
c.error = AGENTX_RES_GEN_ERROR;
mb_free(o_start);
mb_free(o_end);
return parse_gets_error(p, &c, pkt_size);
}
@ -1367,11 +1608,11 @@ parse_gets_pdu(struct snmp_proto *p, byte * const pkt_start)
switch (h->type)
{
case AGENTX_GET_PDU:
snmp_mib_fill2(p, o_start, &c);
snmp_mib_fill(p, &vb_start, &c);
break;
case AGENTX_GET_NEXT_PDU:
snmp_get_next2(p, o_start, o_end, &c);
snmp_get_next2(p, &vb_start, o_end, &c);
break;
case AGENTX_GET_BULK_PDU:
@ -1382,35 +1623,35 @@ parse_gets_pdu(struct snmp_proto *p, byte * const pkt_start)
/* The behavior of GetBulk pdu in the first iteration is
* identical to GetNext pdu. */
has_any = snmp_get_next2(p, o_start, o_end, &c) | has_any;
has_any = snmp_get_next2(p, &vb_start, o_end, &c) || has_any;
break;
default:
die("incorrect usage");
}
mb_free(o_start);
o_start = NULL;
mb_free(o_end);
vb_start = NULL;
o_end = NULL;
c.index++;
} /* while (c.error == AGENTX_RES_NO_ERROR && size > 0) */
lp_restore(tmp_linpool, &tmps);
if (h->type == AGENTX_GET_BULK_PDU)
{
#if 0
for (bulk_state.repetition++;
has_any && bulk_state.repetition < bulk_state.getbulk.max_repetitions;
bulk_state.repetition++)
{
// TODO find propper start and end
struct oid *start = NULL;
struct oid *end = NULL;
vb_start = snmp_vb_copy_to_tx(p, vb_start, &c);
has_any = 0;
for (bulk_state.index = 0; bulk_state.index < bulk_state.repeaters;
bulk_state.repeaters++)
has_any = snmp_get_bulk2(p, start, end, &bulk_state, &c) || has_any;
has_any = snmp_get_bulk2(p, &vb_start, end, &bulk_state, &c) || has_any;
}
#endif
}
/* We update the error, index pair on the beginning of the packet. */
@ -1422,8 +1663,6 @@ parse_gets_pdu(struct snmp_proto *p, byte * const pkt_start)
// TODO think through the error state
/* number of bytes parsed from RX-buffer */
mb_free(o_start);
mb_free(o_end);
return pkt - pkt_start;
}
@ -1589,8 +1828,74 @@ snmp_search_check_end_oid(const struct oid *found, const struct oid *bound)
*/
/* tree is tree with "internet" prefix .1.3.6.1
working only with o_start, o_end allocated in heap (not from buffer)*/
static enum snmp_search_res
search_mib(struct snmp_proto *p, struct agentx_varbind **vb_search, const struct oid *o_end, struct snmp_pdu *c)
{
ASSUME(vb_search != NULL);
struct oid *o_start = &(*vb_search)->name;
ASSUME(o_start != NULL);
(void)p;
(void)o_end;
(void)c;
// TODO TODO
#if 0
enum snmp_search_res r;
switch (o_curr->ids[1])
{
case SNMP_BGP4_MIB:
r = snmp_bgp_search(p, &o_curr, o_end, 0);
if (r == SNMP_SEARCH_OK)
{
*result = r;
break;
return o_curr;
}
// TODO add early break for o_end less then thinkable maximum in each tree
/* fall through */
default:
if (o_curr) mb_free(o_curr);
o_curr = snmp_oid_duplicate(p->pool, o_start);
*result = SNMP_SEARCH_END_OF_VIEW;
break;
}
if (o_end == blank)
/* cast drops const qualifier */
mb_free((struct oid *)blank);
return o_curr;
#endif
return SNMP_SEARCH_NO_OBJECT;
}
/*
* search_mib - search for successor of given OID
* @p: SNMP protocol instance
* @o_start: search starting OID
* @o_end: search ending OID
* @o_curr: current OID inside @o_start, @o_end interval
* @c: transmit PDU context to use
* @result: search result state
*
* Perform a search in MIB tree in SearchRange from @o_start to @o_end.
* If the @o_start has set include the search is inclusive, the @o_end has
* always the include flag cleared. For agentx-GetNext-PDU, the o_curr is always
* NULL, for agentx-GetBulk-PDU it could have non-NULL value. In such case the
* @o_curr effectively replaces the role of @o_start. It is mandatory to pass
* @o_start and @o_end only allocated from @p protocol's memory pool.
*
* Return found OID or NULL.
*/
/* tree is tree with "internet" prefix .1.3.6.1
working only with o_start, o_end allocated in heap (not from buffer)*/
#if 0
static struct oid *
search_mib(struct snmp_proto *p, const struct oid *o_start, const struct oid *o_end,
search_mib2(struct snmp_proto *p, const struct oid *o_start, const struct oid *o_end,
struct oid *o_curr, struct snmp_pdu UNUSED *c,
enum snmp_search_res *result)
{
@ -1647,7 +1952,11 @@ search_mib(struct snmp_proto *p, const struct oid *o_start, const struct oid *o_
return o_curr;
}
#endif
#if 0
/**
* snmp_prefixize - return prefixed OID copy if possible
* @proto: allocation pool holder
@ -1698,11 +2007,12 @@ snmp_prefixize(struct snmp_proto *proto, const struct oid *oid)
memcpy(&new->ids, &oid->ids[5], new->n_subid * sizeof(u32));
return new;
}
#endif
/*
* snmp_mib_fill - append a AgentX VarBind to PDU
* @p: SNMP protocol instance
* @oid: OID to use as VarBind v.name
* @vb: indirect pointer to destination varbind
* @c: transmit PDU context to use
*
* Append new AgentX VarBind at the end of created PDU. The content (v.data)
@ -1710,19 +2020,16 @@ snmp_prefixize(struct snmp_proto *proto, const struct oid *oid)
* created only if the v.name matches some variable name precisely.
*/
static void
snmp_mib_fill2(struct snmp_proto *p, struct oid *oid, struct snmp_pdu *c)
snmp_mib_fill(struct snmp_proto *p, struct agentx_varbind **vb, struct snmp_pdu *c)
{
ASSUME(oid != NULL);
ASSUME(vb != NULL && *vb != NULL);
if (c->size < snmp_varbind_hdr_size_from_oid(oid))
snmp_manage_tbuf(p, c);
struct agentx_varbind *vb = snmp_create_varbind(c->buffer, oid);
struct oid *oid = &((*vb)->name);
if (oid->n_subid < 2 || (oid->prefix != SNMP_MGMT && oid->ids[0] != SNMP_MIB_2))
{
vb->type = AGENTX_NO_SUCH_OBJECT;
ADVANCE(c->buffer, c->size, snmp_varbind_header_size(vb));
snmp_set_varbind_type(*vb, AGENTX_NO_SUCH_OBJECT);
ADVANCE(c->buffer, c->size, snmp_varbind_header_size(*vb));
return;
}
@ -1737,8 +2044,8 @@ snmp_mib_fill2(struct snmp_proto *p, struct oid *oid, struct snmp_pdu *c)
case SNMP_CLASS_END:
default:
break;
vb->type = AGENTX_NO_SUCH_OBJECT;
ADVANCE(c->buffer, c->size, snmp_varbind_header_size(vb));
(*vb)->type = AGENTX_NO_SUCH_OBJECT;
ADVANCE(c->buffer, c->size, snmp_varbind_header_size(*vb));
}
}
@ -1751,13 +2058,20 @@ snmp_mib_fill2(struct snmp_proto *p, struct oid *oid, struct snmp_pdu *c)
* are invalidated!
*/
void
snmp_manage_tbuf(struct snmp_proto UNUSED *p, struct snmp_pdu *c)
snmp_manage_tbuf(struct snmp_proto *p, void **ptr, struct snmp_pdu *c)
{
sock *sk = p->sock;
int diff;
if (ptr)
diff = *ptr - (void *) sk->tbuf;
log(L_INFO "snmp_manage_tbuf()");
sk_set_tbsize(sk, sk->tbsize + 2048);
c->size += 2048;
if (ptr)
*ptr = sk->tbuf + diff;
}
/*

View File

@ -25,7 +25,7 @@ void snmp_ping(struct snmp_proto *p);
#define SNMP_BGP4_MIB 15 /* part of oid .1.3.6.1.2.1.15 */
#define SNMP_OSPFv3_MIB 192 /* part of oid .1.3.6.1.2.1.192 */
extern u32 snmp_internet[4];
extern const u32 snmp_internet[4];
#define SNMP_DEFAULT_CONTEXT 0
@ -169,6 +169,9 @@ struct oid {
u32 ids[];
};
/* enforced by MIB tree, see mib_tree.h for more info */
#define OID_MAX_LEN 32
struct agentx_varbind {
u16 type;
u16 reserved; /* always zero filled */
@ -297,6 +300,11 @@ struct snmp_pdu {
u32 index; /* index on which the error was found */
};
struct snmp_proto_pdu {
struct snmp_proto *p;
struct snmp_pdu *c;
};
struct snmp_packet_info {
node n;
u8 type; // enum type
@ -323,9 +331,9 @@ void snmp_register(struct snmp_proto *p, struct oid *oid, uint index, uint len,
void snmp_unregister(struct snmp_proto *p, struct oid *oid, uint index, uint len, uint contid);
void snmp_notify_pdu(struct snmp_proto *p, struct oid *oid, void *data, uint size, int include_uptime);
void snmp_manage_tbuf(struct snmp_proto *p, struct snmp_pdu *c);
void snmp_manage_tbuf(struct snmp_proto *p, void **ptr, struct snmp_pdu *c);
struct oid *snmp_prefixize(struct snmp_proto *p, const struct oid *o);
struct agentx_varbind *snmp_vb_to_tx(struct snmp_proto *p, const struct oid *oid, struct snmp_pdu *c);
u8 snmp_get_mib_class(const struct oid *oid);
void snmp_register_mibs(struct snmp_proto *p);