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:
parent
c7ca8b66c1
commit
aa14b546b8
@ -1 +1,4 @@
|
||||
S snmp.c
|
||||
S subagent.c
|
||||
S snmp_utils.c
|
||||
S bgp_mib.c
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -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
909
proto/snmp/mib_tree.c
Normal 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
144
proto/snmp/mib_tree.h
Normal 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
|
||||
|
@ -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
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user