]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
SNMP: MIB tree
authorVojtech Vilimek <vojtech.vilimek@nic.cz>
Mon, 22 Apr 2024 10:55:33 +0000 (12:55 +0200)
committerVojtech Vilimek <vojtech.vilimek@nic.cz>
Mon, 22 Apr 2024 10:55:33 +0000 (12:55 +0200)
The first iteration of MIB tree with tests.

13 files changed:
proto/snmp/Doc
proto/snmp/Makefile
proto/snmp/bgp_mib.c
proto/snmp/bgp_mib.h
proto/snmp/mib_tree.c [new file with mode: 0644]
proto/snmp/mib_tree.h [new file with mode: 0644]
proto/snmp/snmp.c
proto/snmp/snmp_test.c
proto/snmp/snmp_utils.c
proto/snmp/snmp_utils.h
proto/snmp/splitter.py
proto/snmp/subagent.c
proto/snmp/subagent.h

index 747384279b831c27d2d24c7d7b8f162f90a2b32b..7d2ec70b64aa9728eba1f92291adc6cf3d29aa95 100644 (file)
@@ -1 +1,4 @@
 S snmp.c
+S subagent.c
+S snmp_utils.c
+S bgp_mib.c
index 24451dfc5749e5e0f776d785f426e70c7c18cb52..637815affce5a992ecb08c25b000733abb7381f3 100644 (file)
@@ -1,4 +1,4 @@
-src := snmp.c snmp_utils.c subagent.c bgp_mib.c
+src := snmp.c snmp_utils.c subagent.c bgp_mib.c mib_tree.c
 obj := $(src-o-files)
 $(all-daemon)
 $(cf-local)
index d97a0de4c16a7cff8f172af21a71d158298f766a..7559042aa42bcd96907f9ca490e7b44d2911d9de 100644 (file)
 /* 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);
 }
 
 /*
index c044c0ef2583358d41ab13ec700e38c9b4e10412..e2a6ea84eb9e274fce83a4c9efa6068296cfd42d 100644 (file)
@@ -48,8 +48,9 @@ void snmp_bgp_reg_failed(struct snmp_proto *p, struct agentx_response *r, struct
 u8 snmp_bgp_get_valid(u8 state);
 u8 snmp_bgp_getnext_valid(u8 state);
 
-enum snmp_search_res snmp_bgp_search(struct snmp_proto *p, struct oid **searched, const struct oid *o_end, uint contid);
-void snmp_bgp_fill(struct snmp_proto *p, struct agentx_varbind *vb, struct snmp_pdu *c);
+enum snmp_search_res snmp_bgp_search(struct snmp_proto *p, struct agentx_varbind **vb_search, const struct oid *o_end, struct snmp_pdu *c);
+enum snmp_search_res snmp_bgp_search2(struct snmp_proto *p, struct oid **searched, const struct oid *o_end, uint contid);
+void snmp_bgp_fill(struct snmp_proto *p, struct agentx_varbind **vb, struct snmp_pdu *c);
 //int snmp_bgp_testset(struct snmp_proto *p, const struct agentx_varbind *vb, void* tr, struct oid *oid, uint pkt_type);
 
 void snmp_bgp_notify_established(struct snmp_proto *p, struct bgp_proto *bgp);
diff --git a/proto/snmp/mib_tree.c b/proto/snmp/mib_tree.c
new file mode 100644 (file)
index 0000000..78a3e9c
--- /dev/null
@@ -0,0 +1,909 @@
+#include "mib_tree.h"
+#include "snmp_utils.h"
+
+/* TODO does the code handle leafs correctly ?! */
+
+#ifdef allocz
+#undef allocz
+#endif
+
+#define alloc(size) mb_alloc(p, size)
+#define allocz(size) mb_allocz(p, size)
+#define free(ptr) mb_free(ptr)
+#define realloc(ptr, newsize) mib_mb_realloc(p, ptr, newsize)
+
+/*
+ * mib_mb_realloc - fix mb_realloc for NULL
+ * @p: pool to use for NULL pointers
+ * @ptr: old pointer to be reallocated
+ * @size: new size of allocated memory block
+ *
+ * The mb_realloc() does not work with NULL as ptr.
+ */
+static inline void *
+mib_mb_realloc(pool *p, void *ptr, unsigned size)
+{
+  if (!ptr)
+    return mb_alloc(p, size);
+
+  return mb_realloc(ptr, size);
+}
+
+void
+mib_tree_init(pool *p, struct mib_tree *t)
+{
+  struct mib_node *node = &t->root;
+  node->c.id = 0;
+  node->c.flags = 0;
+  node->children = NULL;
+  node->child_len = 0;
+
+  struct oid *oid = tmp_alloc(
+    snmp_oid_size_from_len((uint) ARRAY_SIZE(snmp_internet)));
+  STORE_U8(oid->n_subid, ARRAY_SIZE(snmp_internet));
+  STORE_U8(oid->prefix, 0);
+  STORE_U8(oid->include, 0);
+  STORE_U8(oid->reserved, 0);
+
+  for (size_t i = 0; i < ARRAY_SIZE(snmp_internet); i++)
+    STORE_U32(oid->ids[i], snmp_internet[i]);
+
+  (void) mib_tree_add(p, t, oid, 0);
+
+  /* WTF ??
+  struct mib_walk_state walk = { };
+  (void) mib_tree_find(t, &walk, oid);
+*/
+}
+
+
+// This function does not work with leaf nodes inside the snmp_internet prefix
+// area
+// Return NULL of failure, valid mib_node_u pointer otherwise
+mib_node_u *
+mib_tree_add(pool *p, struct mib_tree *t, const struct oid *oid, int is_leaf)
+{
+  //ASSERT(snmp_oid_is_prefixed(oid) || !snmp_oid_is_prefixable(oid));
+  struct mib_walk_state walk;
+  mib_node_u *node;
+
+  /* The empty prefix is associated with the root tree node */
+  if (snmp_is_oid_empty(oid) && !is_leaf)
+    return (mib_node_u *) &t->root;
+  else if (snmp_is_oid_empty(oid))
+    return NULL;
+
+  mib_tree_walk_init(&walk);
+  node = mib_tree_find(t, &walk, oid);
+  ASSERT(walk.id_pos <= LOAD_U8(oid->n_subid) + 1);
+
+  if (node)
+  {
+    if (mib_node_is_leaf(node) == is_leaf)
+      return node;
+
+    /* we are trying to insert a leaf node in place of inner node,
+     * or vice versa */
+    return NULL;
+  }
+
+  ASSERT(walk.id_pos < LOAD_U8(oid->n_subid) + 1);
+
+  node = walk.stack[walk.stack_pos - 1];
+  /* we encounter leaf node before end of OID's id path */
+  if (mib_node_is_leaf(node))
+    return NULL;
+
+  struct mib_node *node_inner = &node->inner;
+  if (snmp_oid_is_prefixed(oid) &&
+      walk.stack_pos <= ARRAY_SIZE(snmp_internet) + 1)
+  {
+    ASSUME(walk.stack_pos && walk.stack[0] == (mib_node_u *) &t->root);
+
+    for (u32 id = walk.stack_pos - 1; id < ARRAY_SIZE(snmp_internet); id++)
+    {
+      if (snmp_internet[id] >= node_inner->child_len)
+      {
+       u32 old_len = node_inner->child_len;
+       node_inner->child_len = snmp_internet[id] + 1;
+       node_inner->children = realloc(node_inner->children,
+         node_inner->child_len * sizeof(mib_node_u *));
+
+       for (u32 i = old_len; i < node_inner->child_len; i++)
+         node_inner->children[i] = NULL;
+      }
+
+      node = allocz(sizeof(struct mib_node));
+      /* assign child into a parent's children array */
+      node_inner->children[snmp_internet[id]] = node;
+      node_inner = &node->inner;
+      node_inner->c.id = snmp_internet[id];
+      /* node_inner's fields c.flags, child_len, children defaults to zero or
+       * NULL respectively */
+      walk.stack[walk.stack_pos++] = node;
+    }
+
+    if (walk.stack_pos == ARRAY_SIZE(snmp_internet) + 1)
+    {
+      u32 old_len = node_inner->child_len;
+      node_inner->child_len = MAX(old_len, (u32) LOAD_U8(oid->prefix) + 1);
+      node_inner->children = realloc(node_inner->children,
+       node_inner->child_len * sizeof(mib_node_u *));
+
+      for (u32 i = old_len; i < node_inner->child_len; i++)
+       node_inner->children[i] = NULL;
+
+      if (is_leaf && !LOAD_U8(oid->n_subid))
+      {
+       node = allocz(sizeof(struct mib_leaf));
+       node->empty.flags = MIB_TREE_LEAF;
+      }
+      else
+      {
+       node = allocz(sizeof(struct mib_node));
+       node->empty.flags = 0;
+      }
+
+      node->empty.id = LOAD_U8(oid->prefix);
+      /* add node into the parent's children array */
+      node_inner->children[LOAD_U8(oid->prefix)] = node;
+      node_inner = &node->inner;
+      walk.stack[walk.stack_pos++] = node;
+    }
+  }
+
+  /* snmp_internet + 2 = empty + snmp_internet + prefix */
+  if (snmp_oid_is_prefixed(oid) &&
+      walk.stack_pos == ARRAY_SIZE(snmp_internet) + 2 &&
+      LOAD_U8(oid->n_subid) == 0 &&
+      mib_node_is_leaf(node) == is_leaf)
+    return node;
+
+  if (mib_node_is_leaf(node))
+    return node;
+
+  u8 subids = LOAD_U8(oid->n_subid);
+  u32 old_len = node_inner->child_len;
+  u32 child_id = oid->ids[walk.id_pos];
+  node_inner->child_len = MAX(old_len, LOAD_U32(child_id) + 1);
+  node_inner->children = realloc(node_inner->children,
+    node_inner->child_len * sizeof(mib_node_u *));
+
+  for (u32 i = old_len; i < node_inner->child_len; i++)
+    node_inner->children[i] = NULL;
+
+  struct mib_node *parent;
+  /* break to loop before last node in the oid */
+  for (; walk.id_pos < subids - 1;)
+  {
+    parent = node_inner;
+    node_inner = allocz(sizeof(struct mib_node));
+
+    parent->children[child_id] = (mib_node_u *) node_inner;
+    node_inner->c.id = child_id;
+
+    child_id = LOAD_U32(oid->ids[++walk.id_pos]);
+
+    node_inner->child_len = child_id + 1;
+    node_inner->children = allocz(node_inner->child_len * sizeof(mib_node_u *));
+    /*
+    node_inner->child_len = (child_id == 0) ? 0 : child_id;
+    node_inner->children = (child_id == 0) ? NULL
+      : allocz(node_inner->child_len * sizeof(mib_node_u *));
+    */
+  }
+
+  parent = node_inner;
+  mib_node_u *last;
+  if (is_leaf)
+  {
+    last = allocz(sizeof(struct mib_leaf));
+    struct mib_leaf *leaf = &last->leaf;
+
+    parent->children[child_id] = (mib_node_u *) leaf;
+    leaf->c.id = child_id;
+
+    //leaf->c.id = LOAD_U32(oid->ids[subids - 1]);
+    leaf->c.flags = MIB_TREE_LEAF;
+  }
+  else
+  {
+    last = allocz(sizeof(struct mib_node));
+    node_inner = &last->inner;
+
+    parent->children[child_id] = (mib_node_u *) node_inner;
+    node_inner->c.id = child_id;
+    //node_inner->c.id = LOAD_U32(oid->ids[subids - 1]);
+    /* fields c.flags, child_len and children are set by zeroed allocz() */
+  }
+
+  return last;
+}
+
+#if 0
+// TODO merge functions mib_tree_add and mib_tree_insert into one with public iface
+
+mib_node_u *
+mib_tree_add(struct snmp_proto *p, struct mib_tree *t, const struct oid *oid, uint size, int is_leaf)
+{
+  struct mib_walk_state walk = { };
+  mib_node_u *known = mib_tree_find(t, &walk, oid);
+
+  if (known)
+    return known;
+
+  known = walk.stack[walk.stack_pos];
+
+  // redundant ??, if not, would be returned from find
+  if (walk.id_pos_abs == oid->n_subid)
+    return known;
+
+  if (walk.id_pos_rel < 0)
+
+  if (walk.id_pos_abs < oid->n_subid && (u32) walk.id_pos_rel == known->id_len)
+  {
+    if (known->child_len >= oid->ids[walk.id_pos_abs]) // abs +1?
+    {
+      u32 old_len = known->child_len;
+      known->child_len = oid->ids[walk.id_pos_abs] + 1;
+      known->children = mb_realloc(known->children,
+         known->child_len * sizeof(struct mib_node *));
+
+      for (uint i = old_len; i < known->child_len; i++)
+       known->children[i] = NULL;
+    }
+
+    /* find would return it
+    if (known->children[oid->ids[]])
+      return known->children[oid->ids[]];
+    */
+
+    struct mib_node *node = mb_alloc(p->p.pool, sizeof(struct mib_node));
+    node->id_len = oid->n_subid - walk.id_pos_abs;
+    node->ids = mb_alloc(p->p.pool, node->id_len * sizeof(u32));
+    node->flags = 0;
+    node->children = NULL;
+    node->child_len = 0;
+    node->child_count = 0;
+
+    known->child_count++;
+    known->children[oid->ids[0]] = node;
+    return node;
+  }
+  else if (walk.id_pos_abs < oid->n_subid)
+  {
+    /* We known that walk.id_pos_rel < known->id_len */
+    struct mib_node *parent = mb_alloc(p->p.pool, sizeof(struct mib_node));
+    parent->id_len = known->id_len - walk.id_pos_rel;
+    parent->ids = mb_alloc(p->p.pool,
+      parent->id_len * sizeof(struct mib_node *));
+    memcpy(&parent->ids, &known->ids, parent->id_len * sizeof(struct mib_node *));
+    u32 *ids = mb_alloc(p->p.pool,
+      (known->id_len - walk.id_pos_rel) * sizeof(u32));
+    memcpy(ids, &known->ids[parent->id_len],
+      (known->id_len - parent->id_len) * sizeof(struct mib_node *));
+    mb_free(known->ids);
+    known->id_len = known->id_len - walk.id_pos_rel;
+    known->ids = ids;
+    parent->child_len = MAX(known->ids[0], oid->ids[walk.id_pos_abs]) + 1;
+    parent->children = mb_allocz(p->p.pool,
+      parent->child_len * sizeof(struct mib_node *));
+    parent->children[known->ids[0]] = known;
+
+    struct mib_node *child = mb_alloc(p->p.pool, sizeof(struct mib_node));
+    child->id_len = oid->n_subid - walk.id_pos_abs - parent->id_len;
+    child->ids = mb_alloc(p->p.pool,
+      child->id_len * sizeof(struct mib_node *));
+    memcpy(&child->ids, &oid->ids[oid->n_subid - child->id_len],
+      child->id_len * sizeof(u32));
+    // TODO test that we do not override the known
+    parent->children[child->ids[0]] = child;
+
+    return child;
+  }
+  else if (walk.id_pos_abs > oid->n_subid)
+    die("unreachable");
+
+  return NULL;
+}
+#endif
+
+/*
+int
+mib_tree_insert(struct snmp_proto *p, struct mib_tree *t, struct oid *oid)
+{
+  ASSUME(oid);
+
+  struct mib_walk_state walk = { };
+  struct mib_node *node = mib_tree_find(t, &walk, oid);
+  struct mib_leaf *leaf = NULL;
+
+  if (!node)
+  {
+    node = walk.stack[walk.stack_pos];
+
+    if (walk.id_pos_abs > oid->n_subid)
+    {
+    }
+    else / * walk.id_pos_abs <= oid->n_subid * /
+    {
+      leaf = mb_alloc(p->p.pool, sizeof(struct mib_leaf));
+      leaf->id_len = oid->n_subid - walk.id_pos_abs;
+      leaf->ids = mb_alloc(p->p.pool, leaf->id_len * sizeof(struct mib_node *));
+      memcpy(&leaf->ids, &oid->ids[oid->n_subid - leaf->id_len],
+       leaf->id_len * sizeof(u32));
+      leaf->flags = 0;
+      leaf->children = NULL;
+      leaf->child_len = 0;
+      leaf->child_count = 0;
+    }
+  }
+}
+*/
+
+#if 0
+int
+mib_tree_insert(struct snmp_proto *p, struct mib_tree *t, struct oid *oid, struct mib_leaf *leaf)
+{
+  ASSUME(oid);
+
+  struct mib_walk_state walk = { };
+  struct mib_node *node = mib_tree_find(t, &walk, oid);
+  struct mib_node *leaf_node = &leaf->n;
+
+  if (!node)
+  {
+    node = walk.stack[walk.stack_pos];
+
+    // can this really happen ??
+    if (walk.id_pos_abs > oid->n_subid)
+    {
+      struct mib_node *parent = mb_alloc(p->p.pool, sizeof(struct mib_node));
+      parent->id_len = walk.id_pos_abs - oid->n_subid; // -1?
+      parent->ids = mb_alloc(p->p.pool, parent->id_len * sizeof(u32));
+      memcpy(&parent->ids, &node->ids, parent->id_len * sizeof(u32));
+      u32 *ids = mb_alloc(p->p.pool,
+       (node->id_len - parent->id_len) * sizeof(u32));
+      node->id_len = node->id_len - parent->id_len;
+      memcpy(ids, &node->ids[parent->id_len], node->id_len * sizeof(u32));
+      mb_free(node->ids);
+      node->ids = ids;
+
+      parent->child_count = 2;
+      parent->child_len = MAX(node->ids[0], oid->ids[walk.id_pos_abs]) + 1;
+      parent->children = mb_allocz(p->p.pool,
+       parent->child_len * sizeof(struct mib_node *));
+      parent->children[node->ids[0]] = node;
+      parent->children[leaf_node->ids[0]] = leaf_node;
+      return 1;
+    }
+    else
+    {
+      mb_free(leaf_node->ids);
+      leaf_node->id_len = oid->n_subid - walk.id_pos_abs;
+      leaf_node->ids = mb_alloc(p->p.pool, leaf_node->id_len * sizeof(u32));
+      return 1;
+    }
+  }
+
+  if (mib_node_is_leaf(node))
+  {
+    struct mib_leaf *l = SKIP_BACK(struct mib_leaf, n, node);
+    insert_node(&leaf->leafs, &l->leafs);
+    return 1;
+  }
+
+  if (node->child_len > 0)
+    return 0;
+
+  // problem when node->id_len + (walk.id_pos_abs - walk.id_pos_rel) > oid->n_subid
+  if (walk.id_pos_abs < oid->n_subid) // +-1??
+  {
+    leaf_node->id_len = node->id_len - walk.id_pos_abs;
+    leaf_node->ids = mb_alloc(p->p.pool, leaf_node->id_len * sizeof(u32));
+    memcpy(&leaf_node->ids, &oid->ids[walk.id_pos_abs], leaf_node->id_len * sizeof(u32));
+    leaf_node->child_len = leaf_node->child_count = 0;
+    leaf_node->children = NULL;
+    return 1;
+  }
+  else
+    return 0;
+  return 1;
+}
+#endif
+
+/*
+int
+mib_tree_remove(struct mib_tree *tree, struct oid *oid)
+{
+  struct mib_walk_state walk = { };
+  struct mib_node *node = mib_tree_find(tree, &walk, oid);
+
+  if (!node)
+    return 0;
+
+  mib_tree_delete(&walk);
+  //mib_tree_delete(tree, &walk);
+  return 1;
+}
+*/
+
+int
+mib_tree_remove(struct mib_tree *t, const struct oid *oid)
+{
+  struct mib_walk_state walk = { };
+  mib_node_u *node = mib_tree_find(t, &walk, oid);
+
+  if (!node)
+    return 0;
+  else
+  {
+    (void) mib_tree_delete(t, &walk);
+    return 1;
+  }
+}
+
+int
+mib_tree_delete(struct mib_tree *t, struct mib_walk_state *walk)
+{
+  int deleted = 0;
+  ASSUME(t);
+
+  /* (walk->stack_pos < 2) It is impossible to delete root node */
+  if (!walk || !walk->id_pos || walk->stack_pos < 2)
+    return 0;
+
+  struct mib_node *parent = &walk->stack[walk->stack_pos - 2]->inner;
+  mib_node_u *node = walk->stack[walk->stack_pos - 1];
+
+  struct mib_walk_state delete = {
+    .id_pos = walk->id_pos,
+    .stack_pos = 2,
+    .stack = {
+      (mib_node_u *) parent,
+      node,
+      NULL,
+    },
+  };
+
+  u32 last_id = 0;
+  while (delete.stack_pos > 1)
+  {
+continue_while:          /* like outer continue, but skip always true condition */
+    parent = (struct mib_node *) delete.stack[delete.stack_pos - 2];
+
+    if (mib_node_is_leaf(node))
+    {
+      /* Free leaf node */
+      last_id = node->leaf.c.id;
+      parent->children[last_id] = NULL;
+      delete.stack[--delete.stack_pos] = NULL;
+      free(node);
+      deleted++;
+      node = delete.stack[delete.stack_pos - 1];
+      continue;          /* here, we couldn't skip the while condition */
+    }
+
+    struct mib_node *node_inner = &node->inner;
+    mib_node_u *child = NULL;
+    for (u32 id = last_id; id < node_inner->child_len; id++)
+    {
+      /* Recursively traverse child nodes */
+      child = node_inner->children[id];
+
+      if (!child)
+       continue;
+
+      last_id = 0;
+      delete.stack[delete.stack_pos++] = child;
+      parent = node_inner;
+      node = child;
+      goto continue_while;    /* outer continue */
+    }
+
+    /* Free inner node without any children */
+    last_id = node_inner->c.id;
+    parent->children[last_id] = NULL;
+    delete.stack[--delete.stack_pos] = NULL;
+    free(node_inner->children);
+    free(node_inner);
+    deleted++;
+    node = (mib_node_u *) parent;
+
+    /* skip check for deleted node in loop over children */
+    last_id++;
+  }
+
+  /* delete the node from original stack */
+  walk->stack[--walk->stack_pos] = NULL;
+
+  node = walk->stack[walk->stack_pos - 1];
+  struct mib_node *node_inner = &node->inner;
+  u32 id;
+  for (id = 0; id < node_inner->child_len; id++)
+  {
+    if (node_inner->children[id] != NULL)
+      break;
+  }
+
+  if (id == node_inner->child_len)
+  {
+    /* all the children are NULL */
+    free(node_inner->children);
+    node_inner->children = NULL;
+    node_inner->child_len = 0;
+  }
+
+  return deleted;
+}
+
+/* currently support only search with blank new walk state */
+/* requires non-NULL walk */
+/* TODO doc string, user should check if the node is not root (or at least be
+ * aware of that */
+mib_node_u *
+mib_tree_find(const struct mib_tree *t, struct mib_walk_state *walk, const struct oid *oid)
+{
+  ASSERT(t && walk);
+
+  if (!oid || snmp_is_oid_empty(oid))
+  {
+    walk->stack_pos = 1;
+    walk->stack[0] = (mib_node_u *) &t->root;
+    return (snmp_is_oid_empty(oid)) ? (mib_node_u *) &t->root : NULL;
+  }
+
+  mib_node_u *node;
+  struct mib_node *node_inner;
+
+  u8 oid_pos = walk->id_pos = 0;
+  node = walk->stack[walk->stack_pos++] = (mib_node_u *) &t->root;
+
+#if 0
+  u8 oid_pos = walk->id_pos;
+
+  if (walk->stack_pos > 0)
+    node = walk->stack[walk->stack_pos];
+  else
+    node = walk->stack[walk->stack_pos++] = (mib_node_u *) &t->root;
+
+  if (mib_node_is_leaf(node))
+  {
+    if (snmp_oid_is_prefixed(oid) && LOAD_U8(oid->n_subid) + ARRAY_SIZE(snmp_internet) + 1 == walk->id_pos)
+      return node;
+
+    else if (!snmp_oid_is_prefixed(oid) && LOAD_U8(oid->n_subid) + 1 == walk->id_pos)
+      return node;
+
+    /* it could hold that LOAD_U8(oid->n_subid) >= walk->id_pos */
+    return NULL;
+  }
+#endif
+
+  node_inner = &node->inner;
+  ASSERT(node && !mib_node_is_leaf(node));
+
+  /* Handling of prefixed OID */
+  if (snmp_oid_is_prefixed(oid))
+  {
+    uint i;
+    /* walking the snmp_internet prefix itself */
+    for (i = 0; i < ARRAY_SIZE(snmp_internet); i++)
+    {
+      if (node_inner->child_len <= snmp_internet[i])
+       return NULL;
+
+      node = node_inner->children[snmp_internet[i]];
+      node_inner = &node->inner;
+
+      if (!node)
+       return NULL;
+
+      ASSERT(node->empty.id == snmp_internet[i]);
+      walk->stack[walk->stack_pos++] = node;
+
+      if (mib_node_is_leaf(node))
+       return NULL;
+    }
+
+    /* walking the prefix continuation (OID field oid->prefix) */
+    u8 prefix = LOAD_U8(oid->prefix);
+    if (node_inner->child_len <= prefix)
+      return NULL;
+
+    node = node_inner->children[prefix];
+    node_inner = &node->inner;
+
+    if (!node)
+      return NULL;
+
+    ASSERT(node->empty.id == prefix);
+    walk->stack[walk->stack_pos++] = node;
+
+    if (mib_node_is_leaf(node) && LOAD_U8(oid->n_subid) > 0)
+      return NULL;
+  }
+
+  u8 subids = LOAD_U8(oid->n_subid);
+  if (subids == 0)
+    return (node == (mib_node_u *) &t->root) ? NULL : node;
+
+  /* loop for all OID's ids except the last one */
+  for (oid_pos = 0; oid_pos < subids - 1; oid_pos++) // remove oid_pos assignment
+  {
+    u32 id = LOAD_U32(oid->ids[oid_pos]);
+    if (node_inner->child_len <= id)
+    {
+      walk->id_pos = oid_pos;
+      return NULL;
+    }
+
+    node = node_inner->children[id];
+    node_inner = &node->inner;
+
+    if (!node)
+    {
+      walk->id_pos = oid_pos;
+      return NULL;
+    }
+
+    ASSERT(node->empty.id == id);
+    walk->stack[walk->stack_pos++] = node;
+
+    if (mib_node_is_leaf(node))
+    {
+      walk->id_pos = ++oid_pos;
+      return NULL;
+    }
+  }
+
+  walk->id_pos = oid_pos;
+  u32 last_id = LOAD_U32(oid->ids[oid_pos]);
+  if (node_inner->child_len <= last_id)
+    return NULL;
+
+  node = node_inner->children[last_id];
+  node_inner = &node->inner;
+
+  if (!node)
+    return NULL;
+
+  /* here, the check of node being a leaf is intentionally omitted
+   * because we may need to search for a inner node */
+  ASSERT(node->empty.id == last_id);
+  walk->id_pos = ++oid_pos;
+  return walk->stack[walk->stack_pos++] = node;
+}
+
+void
+mib_tree_walk_init(struct mib_walk_state *walk)
+{
+  walk->id_pos = 0;
+  walk->stack_pos = 0;
+  memset(&walk->stack, 0, sizeof(walk->stack));
+}
+
+/*
+void
+mib_node_free(mib_node_u *node)
+{
+  if (!mib_node_is_leaf(node))
+  {
+    struct mib_node *node_inner = &node->inner;
+    node_inner->child_len = 0;
+    free(node_inner->children);
+    node_inner->children = NULL;
+  }
+
+  free(node);
+}
+*/
+
+mib_node_u *
+mib_tree_walk_next(struct mib_tree *t, struct mib_walk_state *walk)
+{
+  ASSERT(t && walk);
+
+  u32 next_id = 0;
+
+  if (walk->stack_pos == 0)
+    return NULL;
+
+  mib_node_u *node = walk->stack[walk->stack_pos - 1];
+
+  if (mib_node_is_leaf(node))
+  {
+    next_id = node->leaf.c.id + 1;
+    walk->stack[--walk->stack_pos] = NULL;
+    node = walk->stack[walk->stack_pos - 1];
+  }
+
+  while (walk->stack_pos > 0)
+  {
+    node = walk->stack[walk->stack_pos - 1];
+
+    if (mib_node_is_leaf(node))
+    {
+      walk->stack[walk->stack_pos++] = node;
+      return node;
+    }
+
+    struct mib_node *node_inner = &node->inner;
+    for (u32 id = next_id; id < node_inner->child_len; id++)
+    {
+      mib_node_u *child = node_inner->children[id];
+
+      if (!child)
+       continue;
+
+      walk->stack[walk->stack_pos++] = child;
+      return child;
+    }
+
+    next_id = node_inner->c.id + 1;
+    walk->stack[--walk->stack_pos] = NULL;
+  }
+
+  return NULL;
+}
+
+#if 0
+struct mib_node *
+mib_tree_walk_next(struct mib_walk_state *walk)
+{
+  ASSUME(walk->stack[walk->stack_pos]);
+
+  if (walk->stack_pos == 0 && walk->stack[0] &&
+      walk->stack[0]->flags & (MIB_TREE_REG_ACK || MIB_TREE_REG_WAIT))
+    return walk->stack[0];
+
+  struct mib_node *node = walk->stack[walk->stack_pos];
+  u32 id;
+
+find_leaf:
+  while (!mib_node_is_leaf(node))
+  {
+    for (id = 0; id < node->child_len; id++)
+    {
+      if (node->children[id])
+      {
+       node = node->children[id];
+       walk->stack[++walk->stack_pos] = node;
+       break;
+      }
+    }
+
+    if (node->flags & (MIB_TREE_REG_ACK || MIB_TREE_REG_WAIT))
+      return node;
+  }
+
+  id = node->ids[0];
+
+  while (walk->stack_pos)
+  {
+    walk->stack[walk->stack_pos] = NULL;
+    --walk->stack_pos;
+    node = walk->stack[walk->stack_pos];
+
+    if (id + 1 != node->child_len)
+      break;
+  }
+
+  if (id + 1 == node->child_len)
+    return walk->stack[0] = NULL;
+
+  node = node->children[id + 1];
+  walk->stack_pos++;
+  walk->stack[walk->stack_pos] = node;
+  goto find_leaf;
+}
+#endif
+
+
+struct mib_leaf *
+mib_tree_walk_next_leaf(struct mib_tree *t, struct mib_walk_state *walk)
+{
+  (void) t;
+
+  if (walk->stack_pos == 0)
+    return NULL;
+
+  u32 next_id = 0;
+  mib_node_u *node = walk->stack[walk->stack_pos - 1];
+
+  if (mib_node_is_leaf(node) && walk->stack_pos > 1)
+  {
+    next_id = node->leaf.c.id + 1;
+    walk->stack[--walk->stack_pos] = NULL;
+    node = walk->stack[walk->stack_pos - 1]; // does it underflow ??
+  }
+  else if (mib_node_is_leaf(node))
+  {
+    /* walk->stack_pos == 1, so we NULL out the last stack field */
+    walk->stack[--walk->stack_pos] = NULL;
+    return NULL;
+  }
+
+  mib_node_u *parent = (walk->stack_pos <= 1) ? NULL :
+     walk->stack[walk->stack_pos - 2];
+
+  while (walk->stack_pos > 0)
+  {
+continue_while:
+    node = walk->stack[walk->stack_pos - 1];
+
+    if (mib_node_is_leaf(node))
+    {
+      walk->stack[walk->stack_pos++] = node;
+      return (struct mib_leaf *) node;
+    }
+
+    struct mib_node *node_inner = &node->inner;
+    for (u32 id = next_id; id < node_inner->child_len; id++)
+    {
+      mib_node_u *child = node_inner->children[id];
+
+      if (!child)
+       continue;
+
+      next_id = 0;
+      walk->stack[walk->stack_pos++] = child;
+      /* node is assign at the beginning of the while loop (from stack) */
+      goto continue_while;
+    }
+
+    while (walk->stack_pos > 1)        // endless loop here possible ??
+    {
+      parent = walk->stack[walk->stack_pos - 2];
+      node = walk->stack[walk->stack_pos - 1];
+
+      ASSUME(mib_node_is_leaf(node));
+      if (node->leaf.c.id + 1 == parent->inner.child_len)
+       walk->stack[--walk->stack_pos] = NULL;
+
+      next_id = node->inner.c.id + 1;
+    }
+  }
+
+  return NULL;
+}
+
+#if 0
+struct mib_leaf *
+mib_tree_next_leaf(struct mib_walk_state *walk)
+{
+  ASSUME(walk->stack[walk->stack_pos] &&
+        mib_node_is_leaf(walk->stack[walk->stack_pos]));
+
+  struct mib_node *node = walk->stack[walk->stack_pos];
+  u32 id;
+
+  while (walk->stack_pos)
+  {
+    id = node->ids[0];
+    walk->stack[walk->stack_pos] = NULL;
+    --walk->stack_pos;
+    node = walk->stack[walk->stack_pos];
+
+    if (id + 1 != node->child_len)
+      break;
+  }
+
+  if (id + 1 == node->child_len)
+    return (struct mib_leaf *) (walk->stack[0] = NULL);
+
+  id++;
+  while (!mib_node_is_leaf(node))
+  {
+    for (; id < node->child_len && !node->children[id]; id++)
+      ;
+
+    node = node->children[id];
+    walk->stack[++walk->stack_pos] = node;
+    id = 0;
+  }
+
+  return (struct mib_leaf *) node;
+}
+#endif
+
diff --git a/proto/snmp/mib_tree.h b/proto/snmp/mib_tree.h
new file mode 100644 (file)
index 0000000..bec13b2
--- /dev/null
@@ -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
+
index fe7e8f5fbadd7af820c571913b5fc8b7f239428b..e6067ff7fdebd023c00c2f9691c9c4effaa77710 100644 (file)
@@ -511,7 +511,6 @@ snmp_start(struct proto *P)
 
   /* We create copy of bonds to BGP protocols. */
   HASH_INIT(p->bgp_hash, p->pool, 10);
-
   snmp_bgp_start(p);
 
   return snmp_set_state(p, SNMP_INIT);
@@ -550,7 +549,7 @@ skip:
 }
 
 /*
- * snmp_reconfigure - Test if SNMP instance is reconfigurable
+ * snmp_reconfigure - Indicate instance reconfigurability
  * @P - SNMP protocol generic handle, current state
  * @CF - SNMP protocol configuration generic handle carring new values
  *
@@ -565,16 +564,16 @@ snmp_reconfigure(struct proto *P, struct proto_config *CF)
   const struct snmp_config *new = SKIP_BACK(struct snmp_config, cf, CF);
 
   /* We are searching for configuration changes */
-  int retval = snmp_reconfigure_logic(p, new);
+  int config_changed = snmp_reconfigure_logic(p, new);
 
-  if (retval)
+  if (config_changed)
   {
     /* Reinitialize the hash after snmp_shutdown() */
     HASH_INIT(p->bgp_hash, p->pool, 10);
     snmp_bgp_start(p);
   }
 
-  return retval;
+  return config_changed;
 }
 
 /*
index 08ea95ab9c9473c8a86cf0a32a37aa0775448c14..e6681278251169dead5d09f74a5c866f6b4aeeb4 100644 (file)
@@ -7,6 +7,8 @@
  *     Can be freely distributed and used under the terms of the GNU GPL.
  */
 
+#include <stdarg.h>
+
 #include "test/birdtest.h"
 #include "test/bt-utils.h"
 
 #include "subagent.h"
 #include "snmp.h"
 #include "snmp_utils.h"
+#include "mib_tree.h"
+
+// TODO test walk state stack overflow
+// TODO hint for child len alloc size
+
+static int t_oid_empty(void);
+static int t_oid_compare(void);
+static int t_oid_prefixize(void);
+static int t_tree_find(void);
+static int t_tree_traversal(void);
+static int t_tree_leafs(void);
+static int t_tree_add(void);
+static int t_tree_delete(void);
+
+#define SNMP_BUFFER_SIZE 1024
+#define TESTS_NUM   20
+#define SMALL_TESTS_NUM 10
+static int tree_sizes[] = { 0, 1, 10, 100, 1000 };
+
+#define OID_MAX_ID 16
 
 #define SNMP_EXPECTED(actual, expected) \
   bt_debug("%s  expected: %3u   actual: %3u\n", \
     #expected, expected, actual);
 
-#define OID_ALLOCATE(size) mb_alloc(&root_pool, sizeof(struct oid) + (size) * sizeof (u32))
-
-#define OID_INIT(oid, n_subid_, prefix_, include_, arr_)      \
-  (oid)->n_subid = (n_subid_);                               \
-  (oid)->prefix = (prefix_);                                 \
-  (oid)->include = (include_);                               \
-  memcpy((oid)->ids, (arr_), sizeof(arr_));                  \
-
-void
-test_fill(struct snmp_proto *p)
+static inline struct oid *
+oid_allocate(uint size)
 {
-  ((struct proto *) p)->pool = &root_pool;
+  return tmp_alloc(sizeof(struct oid) + size * sizeof(u32));
 }
 
-static void UNUSED
-test_oid(struct oid *oid, uint base_size)
+static inline void
+oid_init2(struct oid *oid, u8 n_subid, u8 prefix, u8 include, va_list ids)
 {
-#if 0
-  /* tests all states one by one */
-
-  oid->n_subid = base_size + 2;
-  oid->ids[base_size + 0] = 1;
-  oid->ids[base_size + 1] = 15;  // BGP4-MIB::bgp
-  bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_BGP);
-
-  oid->n_subid = base_size + 3;
-  oid->ids[base_size + 2] = 1;   // BGP4-MIB::bgpVersion
-  snmp_oid_dump(oid);
-  bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_VERSION);
-
-  oid->ids[base_size + 2] = 2;   // BGP4-MIB::bgpLocalAs
-  bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_LOCAL_AS);
-
-  oid->ids[base_size + 2] = 3;   // BGP4-MIB::bgpPeerTable
-  bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_PEER_TABLE);
-
-  bt_debug("testing BGP4-MIB::bgpPeerEntry\n");
-  oid->n_subid = base_size + 4;
-  bt_debug("arith\n");
-  oid->ids[base_size + 2] = 3;
-  oid->ids[base_size + 3] = 1;   // BGP4-MIB::bgpPeerEntry
-  bt_debug("dumpping\n");
-  bt_debug("after dump, assertion\n");
-  // SNMP_EXPECTED(snmp_bgp_state(oid), BGP_INTERNAL_PEER_ENTRY);
-  bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_PEER_ENTRY);
-  bt_debug("finish\n");
-
-  oid->n_subid = base_size + 5;
-  oid->ids[base_size + 2] = 3;
-  oid->ids[base_size + 3] = 1;
-  oid->ids[base_size + 4] = 1;    // BGP4-MIB::bgpPeerIdentifier
-  bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_IDENTIFIER);
-
-  oid->ids[base_size + 4] = 2;    // BGP4-MIB::bgpPeerState
-  bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_STATE);
-
-  oid->ids[base_size + 4] = 3;    // BGP4-MIB::bgpPeerAdminStatus
-  bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_ADMIN_STATUS);
-
-  bt_debug("     1/4\n");
-  oid->ids[base_size + 4] = 4;    // BGP4-MIB::bgpPeerNegotiatedVersion
-  bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_NEGOTIATED_VERSION);
-
-  oid->ids[base_size + 4] = 5;    // BGP4-MIB::bgpPeerLocalAddr
-  bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_LOCAL_ADDR);
-
-  oid->ids[base_size + 4] = 6;    // BGP4-MIB::bgpPeerLocalPort
-  bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_LOCAL_PORT);
+  oid->n_subid = n_subid;
+  oid->prefix = prefix;
+  oid->include = include;
+  oid->reserved = 0;
 
-  oid->ids[base_size + 4] = 7;    // BGP4-MIB::bgpPeerRemoteAddr
-  bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_REMOTE_ADDR);
-
-  oid->ids[base_size + 4] = 8;    // BGP4-MIB::bgpPeerRemotePort
-  bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_REMOTE_PORT);
-
-  oid->ids[base_size + 4] = 9;    // BGP4-MIB::bgpPeerRemoteAs
-  bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_REMOTE_AS);
-
-  oid->ids[base_size + 4] = 10;   // BGP4-MIB::bgpPeerInUpdates
-  bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_RX_UPDATES);
-
-  bt_debug("   1/2 \n");
-  oid->ids[base_size + 4] = 11;   // BGP4-MIB::bgpPeerOutUpdates
-  bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_TX_UPDATES);
-
-  oid->ids[base_size + 4] = 12;   // BGP4-MIB::bgpPeerInTotalMessages
-  bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_RX_MESSAGES);
-
-  oid->ids[base_size + 4] = 13;   // BGP4-MIB::bgpPeerOutTotalMessages
-  bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_TX_MESSAGES);
-
-  oid->ids[base_size + 4] = 14;   // BGP4-MIB::bgpPeerLastError
-  bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_LAST_ERROR);
-
-  oid->ids[base_size + 4] = 15;   // BGP4-MIB::bgpPeerFsmEstablishedTransitions
-  bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_FSM_TRANSITIONS);
-
-  oid->ids[base_size + 4] = 16;   // BGP4-MIB::bgpPeerFsmEstablishedTime
-  bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_FSM_ESTABLISHED_TIME);
-
-  oid->ids[base_size + 4] = 17;   // BGP4-MIB::bgpPeerConnectionRetryInterval
-  bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_RETRY_INTERVAL);
-  bt_debug( "     3/4\n");
-
-  oid->ids[base_size + 4] = 18;   // BGP4-MIB::bgpPeerHoldTime
-  bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_HOLD_TIME);
-
-  oid->ids[base_size + 4] = 19;   // BGP4-MIB::bgpPeerKeepAlive
-  bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_KEEPALIVE);
-
-  oid->ids[base_size + 4] = 20;   // BGP4-MIB::bgpPeerHoldTimeConfigured
-  bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_HOLD_TIME_CONFIGURED);
-
-  oid->ids[base_size + 4] = 21;   // BGP4-MIB::bgpPeerKeepAliveConfigured
-  bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_KEEPALIVE_CONFIGURED);
-
-  oid->ids[base_size + 4] = 22;   // BGP4-MIB::bgpPeerMinASOriginationInterval
-  bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_ORIGINATION_INTERVAL);
-
-  oid->ids[base_size + 4] = 23;   // BGP4-MIB::bgpPeerMinRouteAdvertisementInverval
-  bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_MIN_ROUTE_ADVERTISEMENT);
-
-  oid->ids[base_size + 4] = 24;   // BGP4-MIB::bgpPeerInUpdateElapsedTime
-  bt_assert(snmp_bgp_state(oid) == BGP_INTERNAL_IN_UPDATE_ELAPSED_TIME);
-
-  bt_debug("testing BGP4-MIB::bgpPeerEntry end\n");
-#endif
+  for (u8 i = 0; i < n_subid; i++)
+  {
+    u32 id = va_arg(ids, u32);
+    oid->ids[i] = id;
+  }
 }
 
-static int
-t_s_is_oid_empty(void)
+static inline void
+oid_init(struct oid *oid, u8 n_subid, u8 prefix, u8 include, ...)
 {
-  bt_assert(snmp_is_oid_empty(NULL) == 0);
-
-  struct oid *blank = mb_alloc(&root_pool, sizeof(struct oid));
-  blank->n_subid = 0;
-  blank->prefix = 0;
-  blank->include = 0;
-
-  bt_assert(snmp_is_oid_empty(blank) == 1);
-
-  mb_free(blank); blank = NULL;
-
-
-  struct oid *prefixed = mb_alloc(&root_pool, sizeof(struct oid) + 3 * sizeof(u32));
-  prefixed->n_subid = 3;
-  prefixed->prefix = 100;
-  prefixed->include = 1;
-
-  u32 prefixed_arr[] = { ~((u32) 0), 0, 256 };
-  memcpy(&prefixed->ids, prefixed_arr, sizeof(prefixed_arr) /
-    sizeof(prefixed_arr[0]));
-
-  bt_assert(snmp_is_oid_empty(prefixed) == 0);
-
-  mb_free(prefixed); prefixed = NULL;
-
-
-  struct oid *to_prefix = mb_alloc(&root_pool, sizeof(struct oid) + 8 * sizeof(u32));
-  to_prefix->n_subid = 8;
-  to_prefix->prefix = 0;
-  to_prefix->include = 1;
-
-  u32 to_prefix_arr[] = {1, 3, 6, 1, 100, ~((u32) 0), 0, 256 };
-  memcpy(&to_prefix->n_subid, to_prefix_arr, sizeof(to_prefix_arr) /
-    sizeof(to_prefix_arr[0]));
-
-  bt_assert(snmp_is_oid_empty(to_prefix) == 0);
-
-  mb_free(to_prefix); to_prefix = NULL;
+  va_list ids;
+  va_start(ids, include);
+  oid_init2(oid, n_subid, prefix, include, ids);
+  va_end(ids);
+}
 
+static inline struct oid *
+oid_create(u8 n_subid, u8 prefix, u8 include, ...)
+{
+  struct oid *result = tmp_alloc(snmp_oid_size_from_len(n_subid));
+  va_list ids;
 
-  struct oid *unprefixable = mb_alloc(&root_pool, sizeof(struct oid) + 2 * sizeof(u32));
-  unprefixable->n_subid = 2;
-  unprefixable->prefix = 0;
-  unprefixable->include = 0;
+  va_start(ids, include);
+  oid_init2(result, n_subid, prefix, include, ids);
+  va_end(ids);
 
-  u32 unpref[] = { 65535, 4 };
-  memcpy(&unprefixable->ids, unpref, sizeof(unpref) / sizeof(unpref[0]));
+  return result;
+}
 
-  bt_assert(snmp_is_oid_empty(unprefixable) == 0);
+static u32
+xrandom(u32 max)
+{
+  return (bt_random() % max);
+}
 
-  mb_free(unprefixable); unprefixable = NULL;
+static u32
+oid_random_id(void)
+{
+  return (bt_random() % (OID_MAX_ID));
+}
 
+static struct oid *
+random_prefixed_oid(void)
+{
+  u32 len = xrandom(OID_MAX_LEN + 1 - ARRAY_SIZE(snmp_internet));
 
-  struct oid *unprefixable2 = mb_alloc(&root_pool, sizeof(struct oid) + 8 * sizeof(u32));
-  unprefixable2->n_subid = 8;
-  unprefixable2->prefix = 0;
-  unprefixable2->include = 1;
+  u8 prefix = (u8) xrandom(UINT8_MAX + 1);
 
-  u32 unpref2[] = { 1, 3, 6, 2, 1, 2, 15, 6 };
-  memcpy(&unprefixable2->ids, unpref2, sizeof(unpref2) / sizeof(unpref2[0]));
+  if (!prefix)
+    return oid_create(0, 0, 0, 0);
 
-  bt_assert(snmp_is_oid_empty(unprefixable2) == 0);
+  struct oid *random = tmp_alloc(snmp_oid_size_from_len(len));
+  /* (xrandom(2) * bt_random()) has 0.5 probability to have value 0 and
+   * 0.5 to have random u32 (including zero) */
+  oid_init(random, 0, prefix, xrandom(2) * bt_random());
+  random->n_subid = len;
 
-  mb_free(unprefixable2); unprefixable2 = NULL;
+  for (u32 id = 0; id < len; id++)
+    random->ids[id] = oid_random_id();
 
-  return 1;
+  return random;
 }
 
-static int
-t_s_prefixize(void)
+static struct oid *
+random_no_prefix_oid(void)
 {
-  //struct oid *nulled = NULL;
-  struct snmp_proto snmp_proto;
-  test_fill(&snmp_proto);
+  /* probability that the random OID is prefixable is practically zero */
+  u32 len = xrandom(OID_MAX_LEN + 1);
 
-  //struct oid *result = snmp_prefixize(&snmp_proto, nulled);
-  //bt_assert(NULL == result);
-  //result != NULL ? mb_free(result) : NULL;
-  struct oid *result;
+  struct oid *random = tmp_alloc(snmp_oid_size_from_len(len));
+  /* (xrandom(2) * bt_random()) has 0.5 probability to have value 0 and
+   * 0.5 to have random u32 (including zero) */
+  oid_init(random, 0, 0, xrandom(2) * bt_random());
+  random->n_subid = len;
 
-  struct oid *blank = mb_allocz(&root_pool, sizeof(struct oid));
-  /* here the byte order should not matter */
-  result = snmp_prefixize(&snmp_proto, blank);
-  bt_assert(snmp_is_oid_empty(result) == 1);
-
-  mb_free(result); result = NULL;
-  mb_free(blank); blank = NULL;
+  for (u32 id = 0; id < len; id++)
+    random->ids[id] = oid_random_id();
 
+  return random;
+}
 
-  struct oid *prefixed = mb_alloc(&root_pool, sizeof(struct oid) + 3 * sizeof(u32));
-  prefixed->n_subid = 3;
-  prefixed->prefix = 100;
-  prefixed->include = 1;
+static struct oid *
+random_prefixable_oid(void)
+{
+  /* generate the len without the snmp_internet prefix included and prefix ID */
+  u32 len = xrandom(OID_MAX_LEN + 1 - (ARRAY_SIZE(snmp_internet) + 1));
 
-  u32 prefixed_arr[] = { ~((u32) 0), 0, 256 };
-  memcpy(&prefixed->ids, prefixed_arr, sizeof(prefixed_arr));
+  struct oid *random = tmp_alloc(
+      snmp_oid_size_from_len(len + ARRAY_SIZE(snmp_internet) + 1));
+  /* (xrandom(2) * bt_random()) has 0.5 probability to have value 0 and
+   * 0.5 to have random u32 (including zero) */
+  oid_init(random, 0, 0, xrandom(2) * bt_random());
+  random->n_subid = len + ARRAY_SIZE(snmp_internet) + 1;
 
-  /* struct oid */result = snmp_prefixize(&snmp_proto, prefixed);
-  bt_assert(memcmp(result, prefixed, snmp_oid_size(prefixed)) == 0);
+  for (u32 inet_id = 0; inet_id < ARRAY_SIZE(snmp_internet); inet_id++)
+    random->ids[inet_id] = snmp_internet[inet_id];
 
-  mb_free(result); result = NULL;
-  //mb_free(prefixed); prefixed = NULL;
+  random->ids[ARRAY_SIZE(snmp_internet)] = xrandom(UINT8_MAX + 1);
 
+  for (u32 id = 0; id < len; id++)
+    random->ids[id + ARRAY_SIZE(snmp_internet) + 1] = oid_random_id();
 
-  struct oid *to_prefix = mb_alloc(&root_pool, sizeof(struct oid) + 8 * sizeof(u32));
-  to_prefix->n_subid = 8;
-  to_prefix->prefix = 0;
-  to_prefix->include = 1;
+  return random;
+}
 
-  u32 to_prefix_arr[] = {1, 3, 6, 1, 100, ~((u32) 0), 0, 256 };
-  memcpy(to_prefix->ids, to_prefix_arr, sizeof(to_prefix_arr));
+static struct oid *
+random_oid(void)
+{
+  u32 option = xrandom(3);
+
+  if (option == 0)
+    return random_prefixed_oid();
+  else if (option == 1)
+    return random_no_prefix_oid();
+  else
+    return random_prefixable_oid();
+}
 
-  result = snmp_prefixize(&snmp_proto, to_prefix);
 
-  bt_assert(memcmp(result, prefixed, snmp_oid_size(prefixed)) == 0);
 
-  mb_free(result); result = NULL;
-  mb_free(to_prefix); to_prefix = NULL;
+static int
+t_oid_empty(void)
+{
+  struct lp_state tmps;
+  lp_save(tmp_linpool, &tmps);
 
+  bt_assert(snmp_is_oid_empty(NULL) == 0);
 
-  struct oid *unprefixable = mb_alloc(&root_pool, sizeof(struct oid) + 2 * sizeof(u32));
-  unprefixable->n_subid = 2;
-  unprefixable->prefix = 0;
-  unprefixable->include = 0;
+  {
+    struct oid *blank = oid_create(0, 0, 0 /* no ids */);
+    bt_assert(snmp_is_oid_empty(blank) == 1);
+    lp_restore(tmp_linpool, &tmps);
+  }
 
-  u32 unpref[] = { 65535, 4 };
-  memcpy(&unprefixable->ids, unpref, sizeof(unpref) / sizeof(unpref[0]));
 
-  result = snmp_prefixize(&snmp_proto, unprefixable);
-  bt_assert(result == NULL);
+  {
+    struct oid *prefixed = oid_create(3, 100, 1,
+      /* ids */ ~((u32) 0), 0, 256);
+    bt_assert(snmp_is_oid_empty(prefixed) == 0);
+    lp_restore(tmp_linpool, &tmps);
+  }
 
-  result != NULL ? mb_free(result) : NULL;
 
-  struct oid *unprefixable2 = mb_alloc(&root_pool, sizeof(struct oid) + 8 * sizeof(u32));
-  unprefixable2->n_subid = 8;
-  unprefixable2->prefix = 0;
-  unprefixable2->include = 1;
+  {
+    struct oid *to_prefix = oid_create(8, 0, 1,
+      /* ids */ 1, 3, 6, 1, 100, ~((u32) 0), 0, 256);
+    bt_assert(snmp_is_oid_empty(to_prefix) == 0);
+    lp_restore(tmp_linpool, &tmps);
+  }
 
-  u32 unpref2[] = { 1, 3, 6, 2, 1, 2, 15, 6 };
-  memcpy(&unprefixable2->ids, unpref2, sizeof(unpref2) / sizeof(unpref2[0]));
 
-  result = snmp_prefixize(&snmp_proto, unprefixable2);
-  bt_assert(result == NULL);
+  {
+    struct oid *unprefixable = oid_create(2, 0, 0,
+      /* ids */ 65535, 4);
+    bt_assert(snmp_is_oid_empty(unprefixable) == 0);
+    lp_restore(tmp_linpool, &tmps);
+  }
 
-  result != NULL ? mb_free(result) : NULL;
+  {
+    struct oid *unprefixable2 = oid_create(8, 0, 1,
+      /* ids */ 1, 3, 6, 2, 1, 2, 15, 6);
+    bt_assert(snmp_is_oid_empty(unprefixable2) == 0);
+    lp_restore(tmp_linpool, &tmps);
+  }
 
+  tmp_flush();
   return 1;
 }
 
 static int
 t_oid_compare(void)
 {
-  /* same length, no prefix */
-  struct oid *l1 = OID_ALLOCATE(5);
-  {
-    u32 arr[] = { 1, 2, 3, 4, 5 };
-    OID_INIT(l1, 5, 0, 1, arr);
-  }
+  struct lp_state tmps;
+  lp_save(tmp_linpool, &tmps);
 
+  /* same length, no prefix */
+  struct oid *l1 = oid_create(5, 0, 1,
+      /* ids */ 1, 2, 3, 4, 5);
 
-  struct oid *r1 = OID_ALLOCATE(5);
-  {
-    u32 arr[] = { 1, 2, 3, 4, 6 };
-    OID_INIT(r1, 5, 0, 0, arr);
-  }
+  struct oid *r1 = oid_create(5, 0, 0,
+      /* ids */ 1, 2, 3, 4, 6);
 
   bt_assert(snmp_oid_compare(l1, r1) == -1);
   bt_assert(snmp_oid_compare(r1, l1) ==  1);
@@ -343,23 +263,15 @@ t_oid_compare(void)
   bt_assert(snmp_oid_compare(l1, l1) ==  0);
   bt_assert(snmp_oid_compare(r1, r1) ==  0);
 
-  mb_free(l1);
-  mb_free(r1);
-
+  lp_restore(tmp_linpool, &tmps);
 
 
   /* different length, no prefix */
-  l1 = OID_ALLOCATE(4);
-  {
-    u32 arr[] = { 1, 2, 3, 4 };
-    OID_INIT(l1, 4, 0, 0, arr);
-  }
+  l1 = oid_create(4, 0, 0,
+      /* ids */ 1, 2, 3, 4);
 
-  r1 = OID_ALLOCATE(5);
-  {
-    u32 arr[] = { 1, 2, 3, 4, 1 };
-    OID_INIT(r1, 5, 0, 1, arr);
-  }
+  r1 = oid_create(5, 0, 1,
+      /* ids */ 1, 2, 3, 4, 1);
 
   bt_assert(snmp_oid_compare(l1, r1) == -1);
   bt_assert(snmp_oid_compare(r1, l1) ==  1);
@@ -387,23 +299,15 @@ t_oid_compare(void)
   bt_assert(snmp_oid_compare(l1, l1) ==  0);
   bt_assert(snmp_oid_compare(r1, r1) ==  0);
 
-  mb_free(l1);
-  mb_free(r1);
-
+  lp_restore(tmp_linpool, &tmps);
 
 
   /* inverse order different length, no prefix */
-  l1 = OID_ALLOCATE(4);
-  {
-    u32 arr[] = { 1, 2, 3, 5 };
-    OID_INIT(l1, 4, 0, 0, arr);
-  }
+  l1 = oid_create(4, 0, 0,
+      /* ids */ 1, 2, 3, 5);
 
-  r1 = OID_ALLOCATE(5);
-  {
-    u32 arr[] = { 1, 2, 3, 4, 1 };
-    OID_INIT(r1, 5, 0, 0, arr);
-  }
+  r1 = oid_create(5, 0, 0,
+      /* ids */ 1, 2, 3, 4, 1);
 
   bt_assert(snmp_oid_compare(l1, r1) ==  1);
   bt_assert(snmp_oid_compare(r1, l1) == -1);
@@ -428,24 +332,16 @@ t_oid_compare(void)
   bt_assert(snmp_oid_compare(l1, r1) ==  1);
   bt_assert(snmp_oid_compare(r1, l1) == -1);
 
-  mb_free(l1);
-  mb_free(r1);
-
+  lp_restore(tmp_linpool, &tmps);
 
 
 /* ==== MIXED PREFIXED / NON PREFIXED OID compare ==== */
   /* same length, mixed */
-  l1 = OID_ALLOCATE(6);  /* OID .1.2.17.3.21.4 */
-  {
-    u32 arr[] = { 1, 2, 17, 3, 21, 4 };
-    OID_INIT(l1, 6, 0, 1, arr);
-  }
+  l1 = oid_create(6, 0, 1,
+      /* ids */ 1, 2, 17, 3, 21, 4);
 
-  r1 = OID_ALLOCATE(1);  /* OID .1.3.6.1.5.3 */
-  {
-    u32 arr[] = { 3 };
-    OID_INIT(r1, 1, 5, 1, arr);
-  }
+  r1 = oid_create(1, 5, 1,
+      /* ids */ 3);
 
   bt_assert(snmp_oid_compare(l1, r1) == -1);
   bt_assert(snmp_oid_compare(r1, l1) ==  1);
@@ -453,70 +349,890 @@ t_oid_compare(void)
   bt_assert(snmp_oid_compare(l1, l1) ==  0);
   bt_assert(snmp_oid_compare(r1, r1) ==  0);
 
-  mb_free(l1);
-  mb_free(r1);
+  lp_restore(tmp_linpool, &tmps);
+
+  struct oid *super = oid_create(4, 0, 0, /* ids */ 1, 3, 6, 1);
+  struct oid *weird = oid_create(4, 70, 0, /* ids */ 9, 10, 10, 12);
+
+  bt_assert(snmp_oid_compare(super, weird) != 0);
+
+  tmp_flush();
+  return 1;
+}
+
+static struct oid *
+snmp_oid_prefixize(struct snmp_proto *p, const struct oid *oid, struct snmp_pdu *c)
+{
+  struct agentx_varbind *vb = snmp_vb_to_tx(p, oid, c);
+  bt_assert(vb->reserved == 0);
+  return &vb->name;
+}
+
+/*
+ * t_oid_prefixize - test prefixing aspect of function snmp_vb_to_tx()
+ */
+static int
+t_oid_prefixize(void)
+{
+  lp_state tmps = { };
+  struct snmp_proto *snmp_proto = NULL;
+
+  byte *buffer = tmp_alloc(SNMP_BUFFER_SIZE);
+  const struct snmp_pdu copy = {
+    .buffer = buffer,
+    .size = SNMP_BUFFER_SIZE,
+    .error = AGENTX_RES_NO_ERROR,
+    .index = 0,
+  };
+  struct snmp_pdu c;
+
+  lp_save(tmp_linpool, &tmps);
+
+
+  /* testing prefixable OIDs */
+  for (int test = 0; test < TESTS_NUM; test++)
+  {
+    const struct oid *oid = random_prefixable_oid();
+
+    u8 subids = oid->n_subid;
+    u8 include = oid->include;
+    u32 pid = oid->ids[ARRAY_SIZE(snmp_internet)];
+
+    /* reset to the default snmp_pdu */
+    c = copy; memset(buffer, 0, snmp_oid_size(oid) + 8);
+
+    struct oid *new = snmp_oid_prefixize(snmp_proto, oid, &c);
+
+    bt_assert(new->n_subid == subids - (ARRAY_SIZE(snmp_internet) + 1));
+    bt_assert(new->prefix == pid);
+    bt_assert(!!new->include == !!include);
+    bt_assert(new->reserved == 0);
+
+    for (u32 i = 0; i < new->n_subid; i++)
+    {
+      bt_assert(new->ids[i] == oid->ids[i + ARRAY_SIZE(snmp_internet) + 1]);
+    }
+
+    for (u32 j = 0; j < ARRAY_SIZE(snmp_internet); j++)
+      bt_assert(oid->ids[j] == snmp_internet[j]);
+
+    lp_restore(tmp_linpool, &tmps);
+  }
+
+  /* testing already prefixed OIDs */
+  for (int test = 0; test < TESTS_NUM; test++)
+  {
+    const struct oid *prefixed = random_prefixed_oid();
+
+    /* reset to the default snmp_pdu */
+    c = copy; memset(buffer, 0, snmp_oid_size(prefixed) + 8);
+
+    struct oid *new = snmp_oid_prefixize(snmp_proto, prefixed, &c);
+
+    bt_assert(new->n_subid == prefixed->n_subid);
+    bt_assert(new->prefix == prefixed->prefix);
+    bt_assert(!!new->include == !!prefixed->include);
+    bt_assert(new->reserved == 0);
+    bt_assert(!memcmp(&new->ids[0], &prefixed->ids[0], new->n_subid * sizeof(u32)));
+
+    lp_restore(tmp_linpool, &tmps);
+  }
+
+  lp_restore(tmp_linpool, &tmps);
+
+  /* testing non-prefixable OIDs */
+  for (int test = 0; test < TESTS_NUM; test++)
+  {
+    const struct oid *oid = random_no_prefix_oid();
+
+    /* test that the OID is _really_ not prefixable */
+    if (oid->n_subid > ARRAY_SIZE(snmp_internet) &&
+       oid->ids[ARRAY_SIZE(snmp_internet) + 1] <= UINT8_MAX)
+    {
+      for (u32 i = 0; i < ARRAY_SIZE(snmp_internet); i++)
+       if (oid->ids[i] != snmp_internet[i]) goto continue_testing;
+
+      break; /* outer for loop */
+    }
+
+continue_testing:
+
+    /* reset to the default snmp_pdu */
+    c = copy; memset(buffer, 0, snmp_oid_size(oid) + 8);
+
+    struct oid *new = snmp_oid_prefixize(snmp_proto, oid, &c);
+
+    bt_assert(new->n_subid == oid->n_subid);
+    bt_assert(new->prefix == oid->prefix);
+    bt_assert(!!new->include == !!oid->include);
+    bt_assert(new->reserved == 0);
+    bt_assert(!memcmp(&new->ids[0], &oid->ids[0], new->n_subid * sizeof(u32)));
+
+    lp_restore(tmp_linpool, &tmps);
+  }
+
+  for (int test = 0; test < SMALL_TESTS_NUM; test++)
+  {
+    const struct oid *oid;
+    {
+      struct oid *work = random_prefixable_oid();
+
+      /* include also the prefix ID (at index 4) */
+      u32 index = xrandom(ARRAY_SIZE(snmp_internet) + 1);
+      /* change randomly picked id at index from 0..5 (included) */
+      u32 random = bt_random();
+      if (index == ARRAY_SIZE(snmp_internet) && random > 255)
+       work->ids[index] = random;
+      else if (index != ARRAY_SIZE(snmp_internet) && work->ids[index] != random)
+       work->ids[index] = random;
+      else
+       continue;
+      oid = work;
+    }
+
+    /* reset to the default snmp_pdu */
+    c = copy; memset(buffer, 0, snmp_oid_size(oid) + 8);
+
+    struct oid *new = snmp_oid_prefixize(snmp_proto, oid, &c);
+
+    bt_assert(new->n_subid == oid->n_subid);
+    bt_assert(new->prefix == oid->prefix);
+    bt_assert(!!new->include == !!oid->include);
+    bt_assert(new->reserved == 0);
+    bt_assert(!memcmp(&new->ids[0], &oid->ids[0], new->n_subid * sizeof(u32)));
+
+    lp_restore(tmp_linpool, &tmps);
+  }
+
+  tmp_flush();
+  return 1;
+}
+
+static void
+test_both(void *buffer, uint size, const struct oid *left, const struct oid
+*right, const struct oid *expected)
+{
+  memset(buffer, 0, size);
+  snmp_oid_common_ancestor(left, right, buffer);
+  bt_assert(snmp_oid_compare(buffer, expected) == 0);
+
+  memset(buffer, 0, size);
+  snmp_oid_common_ancestor(right, left, buffer);
+  bt_assert(snmp_oid_compare(buffer, expected) == 0);
+}
+
+#define TEST_BOTH(l, r, e) test_both(buffer, 1024, l, r, e)
+static int
+t_oid_ancestor(void)
+{
+  const struct oid *null = oid_create(0, 0, 0);
+  const struct oid *shorter = oid_create(3, 15, 0, /* ids */ 192, 1, 7);
+  const struct oid *prefixed = oid_create(4, 15, 0, /* ids */ 192, 1, 7, 82);
+  const struct oid *no_prefix = oid_create(9, 0, 0, /* ids */ 1, 3, 6, 1, 15, 192, 1, 7, 82);
+  const struct oid *outside = oid_create(7, 0, 0, /* ids */ 4, 3, 2, 1, 8, 0, 2);
+  const struct oid *prefix_only =  oid_create(0, 15, 0);
+  const struct oid *prefix_only2 = oid_create(0, 9, 0);
+  const struct oid *partial = oid_create(3, 0, 0, /* ids */ 1, 3, 6);
+  const struct oid *no_inet = oid_create(5, 0, 0, /* ids */ 1, 3, 6, 2, 5);
+
+  const struct oid *inet = oid_create(4, 0, 0, /* ids */ 1, 3, 6, 1);
+
+
+  const struct oid *oids[] = {
+    null, shorter, prefixed, no_prefix, outside, prefix_only, partial, no_inet, inet
+  };
+
+  char buffer[1024];
+
+  /* skip null oid */
+  for (size_t o = 1; o < ARRAY_SIZE(oids); o++)
+    TEST_BOTH(null, oids[o], null);
+
+  for (size_t o = 0; o < ARRAY_SIZE(oids); o++)
+    TEST_BOTH(oids[o], oids[o], oids[o]);
+
+  TEST_BOTH(partial, no_prefix, partial);
+  TEST_BOTH(partial, prefixed, partial);
+  TEST_BOTH(partial, prefix_only, partial);
+  TEST_BOTH(partial, prefix_only2, partial);
+
+  TEST_BOTH(prefix_only2, prefixed, inet);
+  TEST_BOTH(prefix_only2, no_prefix, inet);
+
+  TEST_BOTH(prefix_only2, inet, inet);
+
+  TEST_BOTH(prefix_only, prefix_only2, inet);
+
+  TEST_BOTH(prefix_only, prefixed, prefix_only);
+  TEST_BOTH(prefix_only, no_prefix, prefix_only);
+
+  TEST_BOTH(prefix_only, inet, inet);
+
+  /* skip null oid */
+  for (size_t o = 1; o < ARRAY_SIZE(oids); o++)
+  {
+    if (oids[o] == outside) continue;
+
+    TEST_BOTH(outside, oids[o], null);
+  }
+
+  TEST_BOTH(no_inet, partial, partial);
+  TEST_BOTH(no_inet, inet, partial);
+  TEST_BOTH(no_inet, prefix_only, partial);
+  TEST_BOTH(no_inet, prefix_only2, partial);
+  TEST_BOTH(no_inet, prefixed, partial);
+  TEST_BOTH(no_inet, no_prefix, partial);
+
+  TEST_BOTH(shorter, prefixed, shorter);
+  TEST_BOTH(shorter, no_prefix, shorter);
+
+  return 1;
+}
+
+static int
+test_snmp_oid_compare(const void *left, const void *right)
+{
+  return snmp_oid_compare(
+    *((const struct oid **) left),
+    *((const struct oid **) right)
+  );
+}
+
+static void
+generate_raw_oids(struct oid *oids[], int size, struct oid *(*generator)(void))
+{
+  for (int i = 0; i < size; i++)
+  {
+    /* binary version of ~5% */
+    if (i > 0 && xrandom(256) <= 13)
+    {
+      /* at this chance, we create a copy instead of generating new oid */
+      oids[i] = tmp_alloc(snmp_oid_size(oids[i-1]));
+      memcpy(oids[i], oids[i-1], snmp_oid_size(oids[i-1]));
+    }
+    else
+      oids[i] = generator();
+  }
+}
+
+static int
+generate_oids(struct oid *oids[], struct oid *sorted[], int size, struct oid *(*generator)(void))
+{
+  generate_raw_oids(oids, size, generator);
+
+  memcpy(sorted, oids, size * sizeof(struct oid *));
+
+  qsort(sorted, (size_t) size, sizeof(struct oid *),
+      test_snmp_oid_compare);
+
+  // test sizes 0, 1, 2, 10, ...
+  int last_used = 0;
+  for (int index = 0; index < size; index++)
+  {
+    if (snmp_oid_compare(sorted[last_used], sorted[index]) != 0)
+      sorted[++last_used] = sorted[index];
+  }
+
+  /* delete old pointers */
+  for (int i = last_used + 1; i < size; i++)
+    sorted[i] = NULL;
+
+  return (size > 1) ? last_used + 1 : size;
+}
+
+/* checks if the last two oids are same, but one is leaf and the other is not */
+static inline int UNUSED
+corner_case(struct oid **oids, int oid_idx, struct oid **leafs, int leaf_idx, int is_leaf)
+{
+  const struct oid **oids_c = (const struct oid **) oids;
+  const struct oid **leafs_c = (const struct oid **) leafs;
+  if (oid_idx == 0)
+    return 0;
+
+  /* if the current (last) OID from oids is not leaf */
+  if (!is_leaf && leaf_idx > 0 &&
+      /* and is same as the last leaf */
+      snmp_oid_compare(oids_c[oid_idx], leafs_c[leaf_idx - 1]) == 0)
+    return 1;  /* then return true */
+
+
+  /* if the current (last) OID from oids is a leaf */
+  if (is_leaf && oid_idx > 0 &&
+      /* and is same as previous OID */
+      snmp_oid_compare(oids_c[oid_idx], oids_c[oid_idx - 1]) == 0)
+    return 1;  /* then return true */
+
+  return 0; /* false */
+}
+
+static void UNUSED
+print_dups(const struct oid *oids[], uint size)
+{
+  for (uint i = 0; i < size; i++)
+    for (uint j = i + 1; j < size; j++)
+      if (snmp_oid_compare(oids[i], oids[j]) == 0)
+       log(L_WARN "pair (%u, %u)", i, j);
+}
+
+static void UNUSED
+print_all(const struct oid *oids[], uint size)
+{
+  for (uint i = 0; i < size; i++)
+    snmp_oid_log(oids[i]);
+}
+
+static inline int
+oid_is_leaf(const struct oid *oid, const struct oid *leafs[], uint leaf_idx)
+{
+  for (uint l = 0; l < leaf_idx; l++)
+    if (snmp_oid_compare(oid, leafs[l]) == 0)
+      return 1;
+
+  return 0;
+}
+
+static int
+all_invalid(const struct oid *oids[], const byte *invalid, uint size, uint index)
+{
+  if (!invalid[index])
+    return 0;
+
+  for (uint i = 0; i < size; i++)
+  {
+    if (i == index) continue;
+
+    if (snmp_oid_compare(oids[i], oids[index]) == 0 &&
+       !invalid[i])
+      return 0;
+  }
+
+  return 1;
+}
+
+static int
+count_error(const struct oid *oids[], const byte *invalid, uint size)
+{
+  int error = 0;
+  for (uint i = 0; i < size; i++)
+  {
+    if (!invalid[i]) continue;
+
+    int skip = 0;
+    for (uint j = 0; j < i; j++)
+    {
+      if (snmp_oid_compare(oids[i], oids[j]) == 0)
+      {
+       skip = 1;
+       break;
+      }
+    }
+
+    if (skip) continue;
+
+    if (all_invalid(oids, invalid, size, i))
+      error++;
+  }
+
+  return error;
+}
+
+static int
+gen_test_add(struct oid *(*generator)(void))
+{
+  lp_state tmps;
+  lp_save(tmp_linpool, &tmps);
+
+  pool *pool = &root_pool;
+
+  for (int test = 0; test < TESTS_NUM; test++)
+  {
+    size_t tsz = ARRAY_SIZE(tree_sizes);
+
+    int size = tree_sizes[test % tsz];
+    int with_leafs = (test % (2 * tsz)) < tsz;
+    int no_inet_prefix = (test % (4 * tsz)) < (2 * tsz);
+
+    struct oid **oids = mb_alloc(pool, size * sizeof(struct oid *));
+    byte *types = mb_alloc(pool, size * sizeof(byte));
+    byte *invalid_hist = mb_alloc(pool, size & sizeof(byte));
+    struct oid **sorted = mb_alloc(pool, size * sizeof(struct oid *));
+    struct oid **leafs = (with_leafs) ? mb_alloc(pool, size * sizeof(struct oid *))
+      : NULL;
+    int leaf_idx = 0;
+    int empty_prefix_added = 0;
+    int distinct = generate_oids(oids, sorted, size, generator);
+
+    struct mib_tree storage, *tree = &storage;
+    mib_tree_init(pool, tree);
+
+    if (no_inet_prefix)
+    {
+      /* remove the node .1 and all children */
+      const struct oid *inet_pref = oid_create(1, 0, 0, /* ids */ 1);
+      mib_tree_remove(tree, inet_pref);
+    }
+
+    int invalid_counter = 0;
+    int counter = 0;
+    int cut = 0;
+    for (int i = 0; i < size; i++)
+    {
+      int invalid = 0;
+      int is_leaf = (with_leafs) ? (int) xrandom(2) : 0;
+      types[i] = (byte) is_leaf;
+
+      int will_cut = 0;
+      int oid_nulled = snmp_is_oid_empty(oids[i]);
+
+      if (oid_nulled && is_leaf)
+       invalid = 1;
+
+      /* check existence of ancestor node of a new leaf */
+      for (int oi = 0; !invalid && !oid_nulled && oi < i; oi++)
+      {
+       char buffer[1024];
+       struct oid *o = (void *) buffer;
+
+       if (invalid_hist[oi])
+         continue;
+
+       int other_is_leaf = (int) types[oi];
+
+       if (snmp_oid_compare(oids[oi], oids[i]) == 0 &&
+           !snmp_is_oid_empty(oids[i]))
+       {
+         if (other_is_leaf == is_leaf)
+           will_cut = 1;
+         else if (other_is_leaf != is_leaf)
+           invalid = 1;
+
+         break;
+       }
+
+       snmp_oid_common_ancestor(oids[oi], oids[i], o);
+
+       if ((snmp_oid_compare(oids[i], o) == 0 && is_leaf) ||
+           (snmp_oid_compare(oids[oi], o) == 0 && other_is_leaf))
+       {
+         invalid = 1;
+         break;
+       }
+      }
+
+      if (!invalid && will_cut)
+       cut++;
+
+      if (is_leaf && !invalid)
+       /* leafs could have duplicates */
+       leafs[leaf_idx++] = oids[i];
+
+      mib_node_u *node = mib_tree_add(pool, tree, oids[i], is_leaf);
+
+      bt_assert((node == NULL) == invalid);
+
+      invalid_hist[i] = 0;
+      if (invalid)
+      {
+       invalid_hist[i] = 1;
+       invalid_counter++;
+      }
+
+      if (node != NULL && (!snmp_is_oid_empty(oids[i]) || !empty_prefix_added))
+       counter++;
+
+      if (snmp_is_oid_empty(oids[i]) && !is_leaf)
+       empty_prefix_added = 1;
+    }
+
+    int error = count_error((const struct oid **) oids, invalid_hist, size);
+    bt_assert(counter - cut == distinct - error);
+
+    lp_restore(tmp_linpool, &tmps);
+    mb_free(oids);
+    mb_free(sorted);
+    mb_free(leafs);
+  }
 
   return 1;
 }
 
 static int
-t_s_bgp_state(void)
+t_tree_add(void)
 {
-  struct oid *oid = mb_alloc(&root_pool, sizeof(struct oid) + 10 * sizeof(u32));
 
-  /* oid header */
-  oid->n_subid = 0;
-  oid->prefix = 2;
-  oid->include = 0;
-  oid->pad = 0;
+  gen_test_add(random_prefixed_oid);
+  gen_test_add(random_no_prefix_oid);
+  gen_test_add(random_prefixable_oid);
+  gen_test_add(random_oid);
 
-  /* test all states with expected oid length */
-  bt_debug("testing precise oids\n");
-  test_oid(oid, 0);
+  return 1;
+}
 
-  for (int i = 0; i < 10; i++)
-    oid->ids[i] = (u32) bt_random();
+static int
+gen_test_find(struct oid *(*generator)(void))
+{
+  lp_state tmps;
+  lp_save(tmp_linpool, &tmps);
 
-  /* if this subid is too high it does not match the test case
-   * in general test_oid() func
-   */
-  oid->ids[2] = 0;
+  pool *pool = &root_pool;
 
-  /* test all states with garbage ip */
-  bt_debug("testing oids with random ip index\n");
-  test_oid(oid, 0);
+  for (int test = 0; test < TESTS_NUM; test++)
+  {
+    size_t tsz = ARRAY_SIZE(tree_sizes);
+
+    int size = tree_sizes[test % tsz];
+    int with_leafs = (test % (2 * tsz)) < tsz;
+    int no_inet_prefix = (test % (4 * tsz)) < (2 * tsz);
+
+    struct oid **oids = mb_alloc(pool, size * sizeof(struct oid *));
+    mib_node_u **nodes = mb_alloc(pool, size * sizeof(mib_node_u *));
+    struct oid **searched = mb_alloc(pool, size * sizeof(struct oid *));
+    byte *types = mb_alloc(pool, size * sizeof(byte));
+
+    /* enough to hold snmp_internet copy */
+    uint longest_inet_pref_len = 0;
+    struct oid *longest_inet_pref = oid_create(4, 0, 0, /* ids */ 0, 0, 0, 0);
+
+    generate_raw_oids(oids, size, generator);
+    generate_raw_oids(searched, size, generator);
+
+    struct mib_tree storage, *tree = &storage;
+    mib_tree_init(pool, tree);
+
+    if (no_inet_prefix)
+    {
+      /* remove the node .1 and all children */
+      const struct oid *inet_pref = oid_create(1, 0, 0, /* ids */ 1);
+      mib_tree_remove(tree, inet_pref);
+    }
+
+    for (int i = 0; i < size; i++)
+      types[i] = (byte) ((with_leafs) ? xrandom(2) : 0);
+
+    /*
+     * by default initialized MIB tree will have internet prefix have inserted
+     */
+    if (!no_inet_prefix)
+    {
+      memcpy(longest_inet_pref->ids, snmp_internet, sizeof(snmp_internet));
+      longest_inet_pref_len = 4;
+    }
+
+    for (int i = 0; i < size; i++)
+    {
+      nodes[i] = mib_tree_add(pool, tree, oids[i], types[i]);
+
+      if (nodes[i] == NULL) continue;
+
+      if (snmp_oid_is_prefixed(oids[i]))
+      {
+       memcpy(longest_inet_pref->ids, snmp_internet, sizeof(snmp_internet));
+       longest_inet_pref_len = 4;
+      }
+      else
+      {
+       for (uint j = 0; j < MIN(LOAD_U8(oids[i]->n_subid),
+           ARRAY_SIZE(snmp_internet)); j++)
+       {
+         if (LOAD_U32(oids[i]->ids[j]) == snmp_internet[j] &&
+             j >= longest_inet_pref_len)
+         {
+           longest_inet_pref->ids[j] = snmp_internet[j];
+           longest_inet_pref_len = j + 1;
+         }
+         else if (LOAD_U32(oids[i]->ids[j]) == snmp_internet[j])
+           ;
+         else
+           break;
+       }
+      }
+    }
+
+    for (int i = 0; i < size; i++)
+    {
+      for (int j = 0; j < size; j++)
+      {
+       if (nodes[i] != NULL &&
+           nodes[j] != NULL &&
+           snmp_oid_compare(oids[i], oids[j]) == 0)
+         bt_assert(nodes[i] == nodes[j]);
+      }
+    }
+
+    for (int i = 0; i < size; i++)
+    {
+      /*
+       * This solves cases where we tried to insert
+       * both leaf and inner node for same OID.
+       * Result of insertion should be NULL in cases
+       * when the insertion is inconsistent with the current tree state.
+       * (the first insertion wins)
+       */
+      int expected_precise = 1;
+      mib_node_u *expected = nodes[i];
+      for (int j = 0; j < size; j++)
+      {
+       if (i == j) continue;
+
+       if (snmp_oid_compare(oids[i], oids[j]) == 0 &&
+           types[i] != types[j] && nodes[i] == NULL)
+       {
+         expected = nodes[j];
+         break;
+       }
+
+       char buf[1024];
+       struct oid *o = (void *) buf;
+
+       snmp_oid_common_ancestor(oids[i], oids[j], o);
+
+       /* oids[j] lies on path from root to oids[i] */
+       if (snmp_oid_compare(oids[i], o) == 0 &&
+           nodes[j] != NULL &&
+           expected == NULL)
+       {
+         expected_precise = 0;
+         break;
+       }
+      }
+
+      struct mib_walk_state walk;
+      mib_tree_walk_init(&walk);
+      mib_node_u *found = mib_tree_find(tree, &walk, oids[i]);
+
+      if (expected_precise)
+       bt_assert(found == expected);
+      else
+       /* found is an auto-inserted node on path to some dest OID */
+       bt_assert(found != NULL);
+    }
+
+    for (int search = 0; search < size; search++)
+    {
+      int has_node = 0;
+      for (int stored = 0; stored < size; stored++)
+      {
+       char buf[1024];
+       struct oid *o = (void *) buf;
+       snmp_oid_common_ancestor(oids[stored], searched[search], o);
+
+       if (nodes[stored] != NULL && snmp_oid_compare(searched[search], o) == 0)
+       {
+         has_node = 1;
+         break;
+       }
+      }
+
+      const struct oid *oid = searched[search];
+      if (!has_node && !snmp_oid_is_prefixed(oid))
+      {
+       for (uint i = 0; i < MIN(ARRAY_SIZE(snmp_internet),
+           LOAD_U8(oid->n_subid)); i++)
+       {
+         if (longest_inet_pref->ids[i] != 0 &&
+             longest_inet_pref->ids[i] == oid->ids[i])
+           has_node = 1;
+         else
+         {
+           has_node = 0;
+           break;
+         }
+       }
+
+       if (has_node && LOAD_U8(oid->n_subid) > ARRAY_SIZE(snmp_internet))
+         has_node = 0;
+      }
+
+      struct mib_walk_state walk;
+      mib_tree_walk_init(&walk);
+      mib_node_u *found = mib_tree_find(tree, &walk, searched[search]);
+      bt_assert(has_node == (found != NULL));
+    }
+
+    lp_restore(tmp_linpool, &tmps);
+    mb_free(oids);
+    mb_free(nodes);
+    mb_free(searched);
+    mb_free(types);
+  }
 
-  /* test all states with invalid ip */
-  bt_debug("testing oids with invalid ip index\n");
-  /* zero the states that overlap */
-  oid->ids[2] = 0;
-  oid->ids[3] = 0;
-  oid->ids[4] = 0;
+  tmp_flush();
+  return 1;
+}
 
-  oid->ids[5] = 0;
-  oid->ids[6] = 257;
-  oid->ids[7] = 127;
-  oid->ids[8] = 0xFFFF;
-  test_oid(oid, 0);
+static int
+t_tree_find(void)
+{
 
-  mb_free(oid);
+  gen_test_find(random_prefixed_oid);
+  gen_test_find(random_no_prefix_oid);
+  gen_test_find(random_prefixable_oid);
+  gen_test_find(random_oid);
 
   return 1;
 }
 
+static int
+delete_cleanup(const struct oid *oid, struct oid *oids[], mib_node_u *valid[], int size)
+{
+  uint counter = 0;
+  for (int i = 0; i < size; i++)
+  {
+    char buf[1024];
+    struct oid *o = (void *) buf;
+
+    if (oid == oids[i])
+    {
+      counter++;
+      continue;
+    }
+
+    snmp_oid_common_ancestor(oid, oids[i], o);
+
+    if (snmp_oid_compare(oid, o) == 0)
+    {
+      valid[i] = NULL;
+      counter++;
+    }
+  }
+
+  return counter;
+}
+
+static int
+gen_test_delete(struct oid *(*generator)(void))
+{
+  lp_state tmps;
+  lp_save(tmp_linpool, &tmps);
+
+  pool *pool = &root_pool;
+
+  for (int test = 0; test < TESTS_NUM; test++)
+  {
+    size_t tsz = ARRAY_SIZE(tree_sizes);
+
+    int size = tree_sizes[test % tsz];
+    int with_leafs = (test % (2 * tsz)) < tsz;
+    int no_iet_prefix = (test % (4 * tsz)) < (2 * tsz);
+
+    struct oid **oids = mb_alloc(pool, size * sizeof(struct oid *));
+    mib_node_u **nodes = mb_alloc(pool, size * sizeof(struct mib_node_u *));
+    byte *types = mb_alloc(pool, size * sizeof(byte));
+
+    struct mib_tree storage, *tree = &storage;
+    mib_tree_init(tree);
+
+    generate_raw_oids(oids, size, generator);
+
+    for (int i = 0; i < size; i++)
+    {
+      int is_leaf;
+      is_leaf = types[i] = (byte) (with_leafs) ? xrandom(2) : 0;
+      nodes[i] = mib_tree_add(pool, tree, oids[i], is_leaf);
+    }
+
+    for (int round = 0; round < size / 4; round++)
+    {
+      int i = xrandom(size);
+
+      mib_tree_walk_state walk;
+      mib_tree_walk_walk_init(&walk);
+      mib_node_u *node = mib_tree_find(tree, walk, oids[i]);
+
+      int deleted = mib_tree_delete(tree, walk);
+
+      int invalid_counter = 0;
+      for (int j = 0; j < size; j++)
+      {
+       if (oids[i] == oids[j])
+       {
+         mib_node_u *node = mib_tree_find(oids[j]);
+         bt_assert(node == NULL);
+         invalid_counter++;
+         continue;
+       }
+
+       char buf[1024];
+       struct oid *o = (void *) buf;
+
+       // TODO check that new invalid oids is == or below the deleted one */
+
+       mib_node_u *node = mib_tree_find(oids[j]);
+       if (node != nodes[j])
+       {
+         nodes[j] = NULL;
+         invalid_counter++;
+       }
+      }
+
+    }
+
+    lp_restore(tmp_linpool, &tmps);
+    mb_free(oids);
+    mb_free(nodes);
+  }
+
+  tmp_flush();
+
+  return 1;
+}
+
+static int
+t_tree_delete(void)
+{
+
+  gen_test_delete(random_prefixed_oid);
+  gen_test_delete(random_no_prefix_oid);
+  gen_test_delete(random_prefixable_oid);
+  gen_test_delete(random_oid);
+
+  return 1;
+}
+
+static int
+t_tree_remove(void)
+{
+  return 0; /* failed */
+}
+
+
+static int
+t_tree_traversal(void)
+{
+  return 0; /* failed */
+}
+
+static int
+t_tree_leafs(void)
+{
+  return 0; /* failed */
+}
+
+static int
+t_tree_all(void)
+{
+  /* random sequences of insertion/deletion */
+  return 0; /* failed */
+}
+
+
 int main(int argc, char **argv)
 {
   bt_init(argc, argv);
-
   bt_bird_init();
 
-  //bt_test_suite(t_s_bgp_state, "Function snmp_bgp_state()");
-
-  bt_test_suite(t_s_is_oid_empty, "Function snmp_is_oid_empty()");
+  srandom(0x0000fa00);
 
-  bt_test_suite(t_s_prefixize, "Function snmp_prefixize()");
+  bt_test_suite(t_oid_empty, "Function that determines if the OID is empty");
+  bt_test_suite(t_oid_compare, "Function defining lexicographical order on OIDs");
+  bt_test_suite(t_oid_prefixize, "Function transforming OID to prefixed form");
+  bt_test_suite(t_oid_ancestor, "Function finding common ancestor of two OIDs");
 
-  bt_test_suite(t_oid_compare, "Function snmp_oid_compare()");
+  bt_test_suite(t_tree_find, "MIB tree search");
+  bt_test_suite(t_tree_traversal, "MIB tree traversal");
+  bt_test_suite(t_tree_leafs, "MIB tree leafs traversal");
+  bt_test_suite(t_tree_add, "MIB tree insertion");
+  bt_test_suite(t_tree_delete, "MIB tree removal");
 
   return bt_exit_value();
 }
index 5e706dbb454bf91a874e4c7cbab02af9df37a7bb..dc0823de0aed61f7ebda2368cb8d51dee78d7a3a 100644 (file)
@@ -9,6 +9,7 @@
  */
 
 #include "snmp_utils.h"
+#include <stdio.h>
 
 inline void
 snmp_pdu_context(struct snmp_pdu *pdu, sock *sk)
@@ -45,23 +46,90 @@ snmp_varbind_data(const struct agentx_varbind *vb)
   return (void *)&vb->name + name_size;
 }
 
+struct oid *
+snmp_varbind_set_name_len(struct snmp_proto *p, struct agentx_varbind **vb, u8 len, struct snmp_pdu *c)
+{
+  struct oid *oid = &(*vb)->name;
+
+  if (LOAD_U8(oid->n_subid) >= len)
+  {
+    c->size += (LOAD_U8(oid->n_subid) - len) * sizeof(u32);
+    STORE_U8(oid->n_subid, len);
+    return oid;
+  }
+
+  /* We need more space */
+  ASSUME(len >= LOAD_U8(oid->n_subid));
+  uint diff_size = (len - LOAD_U8(oid->n_subid)) * sizeof(u32);
+  if (c->size < diff_size)
+  {
+    snmp_manage_tbuf(p, (void **) vb, c);
+    oid = &(*vb)->name;
+  }
+
+  ASSERT(c->size >= diff_size);
+  c->size -= diff_size;
+  STORE_U8(oid->n_subid, len);
+  return &(*vb)->name;
+}
+
+void
+snmp_varbind_duplicate_hdr(struct snmp_proto *p, struct agentx_varbind **vb, struct snmp_pdu *c)
+{
+  ASSUME(vb != NULL && *vb != NULL);
+  uint hdr_size = snmp_varbind_header_size(*vb);
+  if (c->size < hdr_size)
+    snmp_manage_tbuf(p, (void **) vb, c);
+
+  ASSERT(c->size >= hdr_size);
+  byte *buffer = c->buffer;
+  ADVANCE(c->buffer, c->size, hdr_size);
+  memcpy(buffer, *vb, hdr_size);
+  *vb = (struct agentx_varbind *) buffer;
+}
+
 /**
  * snmp_is_oid_empty - check if oid is null-valued
  * @oid: object identifier to check
  *
  * Test if the oid header is full of zeroes. For NULL-pointer @oid returns 0.
+ * We ignore include field to prevent weird behaviour.
  */
-int
+inline int
 snmp_is_oid_empty(const struct oid *oid)
 {
   /* We intentionaly ignore padding that should be zeroed */
   if (oid != NULL)
-    return LOAD_U8(oid->n_subid) == 0 && LOAD_U8(oid->prefix) == 0 &&
-       LOAD_U8(oid->include) == 0;
+    return LOAD_U8(oid->n_subid) == 0 && LOAD_U8(oid->prefix) == 0;
+       // && LOAD_U8(oid->include) == 0;
   else
     return 0;
 }
 
+/*
+ * snmp_oid_is_prefixable - check for prefixed form conversion possibility
+ * @oid: object identfier to check
+ *
+ * Check if it is possible to convert @oid to prefixed form. The condition of
+ * that is standart .1.3.6.1 internet prefix and 5-th id that fits in one byte.
+ */
+inline int
+snmp_oid_is_prefixable(const struct oid *oid)
+{
+  if (oid->n_subid < 5)
+    return 0;
+
+  for (int i = 0; i < 4; i++)
+    if (LOAD_U32(oid->ids[i]) != snmp_internet[i])
+      return 0;
+
+  if (LOAD_U32(oid->ids[4]) >= 256)
+    return 0;
+
+  return 1;
+}
+
+
 /**
  * snmp_pkt_len - returns size of SNMP packet payload (without header)
  * @buf: packet first byte
@@ -90,6 +158,20 @@ snmp_oid_copy(struct oid *dest, const struct oid *src)
     STORE_U32(dest->ids[i], src->ids[i]);
 }
 
+/* this function assumes enougth space inside dest is allocated */
+void
+snmp_oid_copy2(struct oid *dest, const struct oid *src)
+{
+  /* The STORE_U8() and LOAD_U8() cancel out */
+  dest->n_subid = src->n_subid;
+  dest->prefix = src->prefix;
+  dest->include = src->include ? 1 : 0;
+  dest->reserved = 0;
+
+  /* The STORE_U32() and LOAD_U32 cancel out */
+  memcpy(dest->ids, src->ids, LOAD_U8(src->n_subid) * sizeof(u32));
+}
+
 /*
  * snmp_oid_duplicate - duplicate an OID from memory pool
  * @pool: pool to use
@@ -181,6 +263,7 @@ snmp_set_varbind_type(struct agentx_varbind *vb, enum agentx_type t)
 {
   ASSUME(t != AGENTX_INVALID);
   STORE_U16(vb->type, t);
+  STORE_U16(vb->reserved, 0);
 }
 
 /* Internal wrapper */
@@ -470,7 +553,8 @@ byte *
 snmp_put_ip4(byte *buf, ip4_addr addr)
 {
   /* octet string has size 4 bytes */
-  STORE_PTR(buf, 4);
+  STATIC_ASSERT(sizeof(ip4_addr) == sizeof(u32));
+  STORE_PTR(buf, sizeof(ip4_addr));
 
   /* Always use Network byte order */
   put_u32(buf+4, ip4_to_u32(addr));
@@ -544,46 +628,74 @@ snmp_oid_ip4_index(struct oid *o, uint start, ip4_addr addr)
 int
 snmp_oid_compare(const struct oid *left, const struct oid *right)
 {
-  if (left->prefix == 0 && right->prefix == 0)
+  const u8 left_subids = LOAD_U8(left->n_subid);
+  const u8 right_subids = LOAD_U8(right->n_subid);
+
+  const u8 left_prefix = LOAD_U8(left->prefix);
+  const u8 right_prefix = LOAD_U8(right->prefix);
+
+  if (left_prefix == 0 && right_prefix == 0)
     goto test_ids;
 
-  if (right->prefix == 0)
+  if (right_prefix == 0)
     return (-1) * snmp_oid_compare(right, left);
 
-  if (left->prefix == 0)
+  if (left_prefix == 0)
   {
-    for (int i = 0; i < 4; i++)
-      if (left->ids[i] < snmp_internet[i])
+    size_t bound = MIN((size_t) left_subids, ARRAY_SIZE(snmp_internet));
+    for (size_t idx = 0; idx < bound; idx++)
+    {
+      u32 id = LOAD_U32(left->ids[idx]);
+      if (id < snmp_internet[idx])
        return -1;
-      else if (left->ids[i] > snmp_internet[i])
+      else if (id > snmp_internet[idx])
        return 1;
+    }
 
-    for (int i = 0; i < MIN(left->n_subid - 4, right->n_subid); i++)
-      if (left->ids[i + 4] < right->ids[i])
+    if (left_subids <= ARRAY_SIZE(snmp_internet))
+      return -1;
+
+    if (LOAD_U32(left->ids[4]) < (u32) right_prefix)
+      return -1;
+    else if (LOAD_U32(left->ids[4]) > (u32) right_prefix) 
+      return 1;
+
+    int limit = MIN(left_subids - (int) ARRAY_SIZE(snmp_internet),
+      (int) right_subids);
+    for (int i = 0; i < limit; i++)
+    {
+      u32 left_id = LOAD_U32(left->ids[i + ARRAY_SIZE(snmp_internet)]);
+      u32 right_id = LOAD_U32(right->ids[i]);
+      if (left_id < right_id)
        return -1;
-      else if (left->ids[i + 4] > right->ids[i])
+      else if (left_id > right_id)
        return 1;
+    }
 
     goto all_same;
   }
 
-  if (left->prefix < right->prefix)
+  if (left_prefix < right_prefix)
     return -1;
-  else if (left->prefix > right->prefix)
+  else if (left_prefix > right_prefix)
     return 1;
 
 test_ids:
   for (int i = 0; i < MIN(left->n_subid, right->n_subid); i++)
-    if (left->ids[i] < right->ids[i])
+  {
+    u32 left_id = LOAD_U32(left->ids[i]);
+    u32 right_id = LOAD_U32(right->ids[i]);
+    if (left_id < right_id)
       return -1;
-    else if (left->ids[i] > right->ids[i])
+    else if (left_id > right_id)
       return 1;
+  }
 
 all_same:
   /* shorter sequence is before longer in lexicografical order  */
-  if (left->n_subid < right->n_subid)
+  if (left_subids < right_subids)
     return -1;
-  else if (left->n_subid > right->n_subid)
+  else if (left_subids > right_subids)
     return 1;
   else
     return 0;
@@ -665,60 +777,52 @@ agentx_type_size(enum agentx_type type)
     return -1;
 }
 
-static inline byte *
-snmp_varbind_type32(struct agentx_varbind *vb, uint size, enum agentx_type type, u32 val)
+static inline void
+snmp_varbind_type32(struct agentx_varbind *vb, struct snmp_pdu *c, enum agentx_type type, u32 val)
 {
-  ASSUME(agentx_type_size(type) == 4); /* type has 4B representation */
-
-  if (size < (uint) agentx_type_size(type))
-    return NULL;
+  ASSUME(agentx_type_size(type) == 4); /* type as 4B representation */
 
   snmp_set_varbind_type(vb, type);
   u32 *data = snmp_varbind_data(vb);
-  STORE_PTR(data, val); /* note that the data has u32 type */
+  STORE_PTR(data, val);
   data++;
-  return (byte *) data;
+  c->buffer = (byte *) data;
 }
 
-inline byte *
-snmp_varbind_int(struct agentx_varbind *vb, uint size, u32 val)
+inline void
+snmp_varbind_int(struct agentx_varbind *vb, struct snmp_pdu *c, u32 val)
 {
-  return snmp_varbind_type32(vb, size, AGENTX_INTEGER, val);
+  snmp_varbind_type32(vb, c, AGENTX_INTEGER, val);
 }
 
-
-inline byte *
-snmp_varbind_counter32(struct agentx_varbind *vb, uint size, u32 val)
+inline void
+snmp_varbind_counter32(struct agentx_varbind *vb, struct snmp_pdu *c, u32 val)
 {
-  return snmp_varbind_type32(vb, size, AGENTX_COUNTER_32, val);
+  snmp_varbind_type32(vb, c, AGENTX_COUNTER_32, val);
 }
 
-inline byte *
-snmp_varbind_ticks(struct agentx_varbind *vb, uint size, u32 val)
+inline void
+snmp_varbind_ticks(struct agentx_varbind *vb, struct snmp_pdu *c, u32 val)
 {
-  return snmp_varbind_type32(vb, size, AGENTX_TIME_TICKS, val);
+  snmp_varbind_type32(vb, c, AGENTX_TIME_TICKS, val);
 }
 
-inline byte *
-snmp_varbind_gauge32(struct agentx_varbind *vb, uint size, s64 val)
+inline void
+snmp_varbind_gauge32(struct agentx_varbind *vb, struct snmp_pdu *c, s64 time)
 {
-  return snmp_varbind_type32(vb, size, AGENTX_GAUGE_32,
-                            MAX(0, MIN(val, UINT32_MAX)));
+  snmp_varbind_type32(vb, c, AGENTX_GAUGE_32, MAX(0, MIN(time, UINT32_MAX)));
 }
 
-inline byte *
-snmp_varbind_ip4(struct agentx_varbind *vb, uint size, ip4_addr addr)
+inline void
+snmp_varbind_ip4(struct agentx_varbind *vb, struct snmp_pdu *c, ip4_addr addr)
 {
-  if (size < snmp_str_size_from_len(4))
-    return NULL;
-
   snmp_set_varbind_type(vb, AGENTX_IP_ADDRESS);
-  return snmp_put_ip4(snmp_varbind_data(vb), addr);
+  c->buffer = snmp_put_ip4(snmp_varbind_data(vb), addr);
 }
 
 // TODO doc string, we have already the varbind prepared
 inline byte *
-snmp_varbind_nstr(struct agentx_varbind *vb, uint size, const char *str, uint len)
+snmp_varbind_nstr2(struct agentx_varbind *vb, uint size, const char *str, uint len)
 {
   if (size < snmp_str_size_from_len(len))
     return NULL;
@@ -727,6 +831,24 @@ snmp_varbind_nstr(struct agentx_varbind *vb, uint size, const char *str, uint le
   return snmp_put_nstr(snmp_varbind_data(vb), str, len);
 }
 
+/*
+ * snmp_varbind_nstr - fill varbind context with octet string
+ * @vb: VarBind to use
+ * @c: PDU information
+ * @str: C-string to put as the VarBind data
+ * @len: length of the string @str
+ *
+ * Beware: this function assumes there is enough space in the underlaying
+ * TX-buffer. The caller has to provide that, see snmp_str_size_from_len() for
+ * more info.
+ */
+void
+snmp_varbind_nstr(struct agentx_varbind *vb, struct snmp_pdu *c, const char *str, uint len)
+{
+  snmp_set_varbind_type(vb, AGENTX_OCTET_STRING);
+  c->buffer = snmp_put_nstr(snmp_varbind_data(vb), str, len);
+}
+
 inline enum agentx_type
 snmp_search_res_to_type(enum snmp_search_res r)
 {
@@ -786,3 +908,124 @@ snmp_oid_dump(const struct oid *oid)
   log(L_WARN "OID DUMP END ====");
   log(L_WARN);
 }
+
+void UNUSED
+snmp_oid_log(const struct oid *oid)
+{
+  char buf[1024] = { };
+  char *pos = buf;
+
+  if (snmp_oid_is_prefixed(oid))
+  {
+    for (uint i = 0; i < ARRAY_SIZE(snmp_internet); i++)
+      pos += snprintf(pos, buf + 1024 - pos, ".%u", snmp_internet[i]);
+
+    pos += snprintf(pos, buf + 1024 - pos, ".%u", oid->prefix);
+  }
+
+  for (int id = 0; id < oid->n_subid; id++)
+    pos += snprintf(pos, buf + 1024 - pos, ".%u", oid->ids[id]);
+
+  log(L_INFO "%s", buf);
+}
+
+
+/*
+ * snmp_oid_common_ancestor - find a common ancestor
+ * @left: first OID
+ * @right: second OID
+ * @out: buffer for result
+ *
+ * The @out must be large enough to always fit the resulting OID, a safe value
+ * is minimum between number of left subids and right subids. The result might
+ * be NULL OID in cases where there is no common subid. The result could be also
+ * viewed as longest common prefix.
+ */
+void
+snmp_oid_common_ancestor(const struct oid *left, const struct oid *right, struct oid *out)
+{
+  ASSERT(left && right && out);
+
+  STORE_U8(out->include, 0);
+  STORE_U8(out->reserved, 0);
+
+  u32 offset = 0;
+  u8 left_ids = LOAD_U8(left->n_subid), right_ids = LOAD_U8(right->n_subid);
+
+  int l = snmp_oid_is_prefixed(left), r = snmp_oid_is_prefixed(right);
+  if (l && r)
+  {
+    if (LOAD_U8(left->prefix) != LOAD_U8(right->prefix))
+    {
+      STORE_U8(out->n_subid, 4);
+      STORE_U8(out->prefix, 0);
+
+      for (uint id = 0; id < ARRAY_SIZE(snmp_internet); id++)
+       STORE_U32(out->ids[id], snmp_internet[id]);
+
+      return;
+    }
+
+    STORE_U8(out->prefix, LOAD_U8(left->prefix));
+  }
+  else if (!l && r)
+  {
+    if (left_ids == 0)
+    {
+      /* finish creating NULL OID */
+      STORE_U8(out->n_subid, 0);
+      STORE_U8(out->prefix, 0);
+      return;
+    }
+
+    for (uint id = 0; id < MIN(ARRAY_SIZE(snmp_internet), left_ids); id++)
+    {
+      if (LOAD_U32(left->ids[id]) != snmp_internet[id])
+      {
+       STORE_U8(out->n_subid, id);
+       STORE_U8(out->prefix, 0);
+       return;
+      }
+
+      STORE_U32(out->ids[id], snmp_internet[id]);
+    }
+
+    if (left_ids <= ARRAY_SIZE(snmp_internet))
+    {
+      STORE_U8(out->n_subid, left_ids);
+      return;
+    }
+
+    /* index 4 is conresponding to the prefix in prefixed OID */
+    if (LOAD_U32(left->ids[4]) != (u32) LOAD_U8(right->prefix))
+    {
+      STORE_U8(out->n_subid, ARRAY_SIZE(snmp_internet));
+      return;
+    }
+
+    /* delete snmp_internet from out->ids and store OID prefix */
+    offset = ARRAY_SIZE(snmp_internet) + 1;
+    STORE_U8(out->n_subid, LOAD_U8(out->n_subid) - ARRAY_SIZE(snmp_internet));
+    STORE_U8(out->prefix, LOAD_U8(right->prefix));
+  }
+  else if (l && !r)
+  {
+    snmp_oid_common_ancestor(right, left, out);
+    return;
+  }
+
+  ASSERT(offset <= left_ids);
+
+  u8 subids = 0;
+  for (u32 id = 0; id < MIN(left_ids - offset, right_ids); id++)
+  {
+    if (left->ids[offset + id] == right->ids[id])
+    {
+      subids++;
+      STORE_U32(out->ids[id], LOAD_U32(right->ids[id]));
+    }
+    else
+      break;
+  }
+  STORE_U8(out->n_subid, subids);
+}
index 3026f77a5dd075d245d48b3b6b207865e92c7135..c01d82fb42664d25971fa57538865c9f3a7cab19 100644 (file)
@@ -24,10 +24,19 @@ size_t snmp_str_size(const char *str);
 
 /* type OID - Object Identifier */
 int snmp_is_oid_empty(const struct oid *oid);
+int snmp_oid_is_prefixable(const struct oid *oid);
 uint snmp_oid_size(const struct oid *o);
 size_t snmp_oid_size_from_len(uint n_subid);
 void snmp_oid_copy(struct oid *dest, const struct oid *src);
+void snmp_oid_copy2(struct oid *dest, const struct oid *src);
 int snmp_oid_compare(const struct oid *first, const struct oid *second);
+void snmp_oid_common_ancestor(const struct oid *left, const struct oid *right, struct oid *result);
+
+static inline int
+snmp_oid_is_prefixed(const struct oid *oid)
+{
+  return LOAD_U8(oid->prefix) != 0;
+}
 
 /* type IPv4 */
 int snmp_valid_ip4_index(const struct oid *o, uint start);
@@ -44,6 +53,8 @@ uint snmp_varbind_size_unsafe(const struct agentx_varbind *vb);
 size_t snmp_varbind_size_from_len(uint n_subid, enum agentx_type t, uint len);
 int snmp_test_varbind(const struct agentx_varbind *vb);
 void *snmp_varbind_data(const struct agentx_varbind *vb);
+struct oid *snmp_varbind_set_name_len(struct snmp_proto *p, struct agentx_varbind **vb, u8 len, struct snmp_pdu *c);
+void snmp_varbind_duplicate_hdr(struct snmp_proto *p, struct agentx_varbind **vb, struct snmp_pdu *c);
 
 /*
  *  AgentX - PDU headers, types, contexts
@@ -63,12 +74,12 @@ int snmp_test_close_reason(byte value);
 /* Functions filling buffer a typed value */
 struct agentx_varbind *snmp_create_varbind(byte *buf, struct oid *oid);
 struct agentx_varbind *snmp_create_varbind_null(byte *buf);
-byte *snmp_varbind_int(struct agentx_varbind *vb, uint size, u32 val);
-byte *snmp_varbind_counter32(struct agentx_varbind *vb, uint size, u32 val);
-byte *snmp_varbind_gauge32(struct agentx_varbind *vb, uint size, s64 val);
-byte *snmp_varbind_ticks(struct agentx_varbind *vb, uint size, u32 val);
-byte *snmp_varbind_ip4(struct agentx_varbind *vb, uint size, ip4_addr addr);
-byte *snmp_varbind_nstr(struct agentx_varbind *vb, uint size, const char *str, uint len);
+void snmp_varbind_int(struct agentx_varbind *vb, struct snmp_pdu *c, u32 val);
+void snmp_varbind_counter32(struct agentx_varbind *vb, struct snmp_pdu *c, u32 val);
+void snmp_varbind_gauge32(struct agentx_varbind *vb, struct snmp_pdu *c, s64 time);
+void snmp_varbind_ticks(struct agentx_varbind *vb, struct snmp_pdu *c, u32 val);
+void snmp_varbind_ip4(struct agentx_varbind *vb, struct snmp_pdu *c, ip4_addr addr);
+void snmp_varbind_nstr(struct agentx_varbind *vb, struct snmp_pdu *c, const char *str, uint len);
 
 /* Raw */
 byte *snmp_no_such_object(byte *buf, struct agentx_varbind *vb, struct oid *oid);
@@ -91,7 +102,12 @@ int snmp_registration_match(struct snmp_registration *r, struct agentx_header *h
 
 void snmp_dump_packet(byte *pkt, uint size);
 void snmp_oid_dump(const struct oid *oid);
+void snmp_oid_log(const struct oid *oid);
 
 enum agentx_type snmp_search_res_to_type(enum snmp_search_res res);
 
+#define AGENTX_TYPE_INT_SIZE ((uint) agentx_type_size(AGENTX_INTEGER))
+#define AGENTX_TYPE_IP4_SIZE ((uint) agentx_type_size(AGENTX_IP_ADDRESS))
+#define AGENTX_TYPE_COUNTER32_SIZE ((uint) agentx_type_size(AGENTX_COUNTER_32))
+
 #endif
index ebd4f342ef405f39cdcdaf4dec96248dc04346ae..052777f035ec17b7bb9c2b59f7d8e990e6202348 100755 (executable)
@@ -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
index c25155b50fbe053f955ce68d44b9ee0fab2f96ee..0892e8904d0b3135f432064d9b5ad8cab9c5d3de 100644 (file)
  *
  */
 
-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);
-
-  if (c->size < snmp_varbind_hdr_size_from_oid(oid))
-    snmp_manage_tbuf(p, c);
+  ASSUME(vb != NULL && *vb != NULL);
 
-  struct agentx_varbind *vb = snmp_create_varbind(c->buffer, oid);
+  struct oid *oid = &((*vb)->name);
 
   if (oid->n_subid < 2 || (oid->prefix != SNMP_MGMT && oid->ids[0] != SNMP_MIB_2))
   {
-    vb->type = AGENTX_NO_SUCH_OBJECT;
-    ADVANCE(c->buffer, c->size, snmp_varbind_header_size(vb));
+    snmp_set_varbind_type(*vb, AGENTX_NO_SUCH_OBJECT);
+    ADVANCE(c->buffer, c->size, snmp_varbind_header_size(*vb));
     return;
   }
 
@@ -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;
 }
 
 /*
index b08519ff0aa6ac9f151b33635442f7a2d299b5a4..9701ba41412b714f559b9e7a509e637aa2d0fe4d 100644 (file)
@@ -25,7 +25,7 @@ void snmp_ping(struct snmp_proto *p);
 #define SNMP_BGP4_MIB   15           /* part of oid .1.3.6.1.2.1.15  */
 #define SNMP_OSPFv3_MIB        192           /* part of oid .1.3.6.1.2.1.192 */
 
-extern u32 snmp_internet[4];
+extern const u32 snmp_internet[4];
 
 #define SNMP_DEFAULT_CONTEXT 0
 
@@ -169,6 +169,9 @@ struct oid {
   u32 ids[];
 };
 
+/* enforced by MIB tree, see mib_tree.h for more info */
+#define OID_MAX_LEN 32
+
 struct agentx_varbind {
   u16 type;
   u16 reserved; /* always zero filled */
@@ -297,6 +300,11 @@ struct snmp_pdu {
   u32 index;                       /* index on which the error was found */
 };
 
+struct snmp_proto_pdu {
+  struct snmp_proto *p;
+  struct snmp_pdu *c;
+};
+
 struct snmp_packet_info {
   node n;
   u8 type; // enum type
@@ -323,9 +331,9 @@ void snmp_register(struct snmp_proto *p, struct oid *oid, uint index, uint len,
 void snmp_unregister(struct snmp_proto *p, struct oid *oid, uint index, uint len, uint contid);
 void snmp_notify_pdu(struct snmp_proto *p, struct oid *oid, void *data, uint size, int include_uptime);
 
-void snmp_manage_tbuf(struct snmp_proto *p, struct snmp_pdu *c);
+void snmp_manage_tbuf(struct snmp_proto *p, void **ptr, struct snmp_pdu *c);
 
-struct oid *snmp_prefixize(struct snmp_proto *p, const struct oid *o);
+struct agentx_varbind *snmp_vb_to_tx(struct snmp_proto *p, const struct oid *oid, struct snmp_pdu *c);
 u8 snmp_get_mib_class(const struct oid *oid);
 
 void snmp_register_mibs(struct snmp_proto *p);