]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
SNMP: tmp ``microimprovement'' flush worktree
authorVojtech Vilimek <vojtech.vilimek@nic.cz>
Thu, 4 Jul 2024 14:33:44 +0000 (16:33 +0200)
committerVojtech Vilimek <vojtech.vilimek@nic.cz>
Thu, 4 Jul 2024 14:33:44 +0000 (16:33 +0200)
proto/snmp/bgp4_mib.c
proto/snmp/bgp4_mib.h
proto/snmp/mib_tree.c
proto/snmp/mib_tree.h
proto/snmp/snmp.c
proto/snmp/snmp.h
proto/snmp/snmp_test.c
proto/snmp/snmp_utils.c
proto/snmp/snmp_utils.h
proto/snmp/subagent.c
proto/snmp/subagent.h

index b3ac160e5d147875bdedea0a8aae0d425d1f4e5f..1c1a4afb6b5fc491365147c5a8be29024bac3343 100644 (file)
@@ -8,12 +8,11 @@
  *     Can be freely distributed and used under the terms of the GNU GPL.
  */
 
-/* need to be first header file included */
-#include "bgp4_mib.h"
-
 #include "snmp.h"
 #include "snmp_utils.h"
 #include "subagent.h"
+#include "bgp4_mib.h"
+#include "mib_tree.h"
 
 /* hash table macros */
 #define SNMP_HASH_KEY(n)  n->peer_ip
 /* hash table only store ip4 addresses */
 #define SNMP_HASH_LESS(ip1, ip2) SNMP_HASH_LESS4(ip1,ip2)
 
-// TODO delete me
+// delete me
 #define SNMP_MANAGE_TBUF(...) (void)0
 
 #define DECLARE_BGP4(addr, proto, conn, stats, config) \
   ip4_addr addr; \
-  const struct bgp_proto *proto; \
-  const struct bgp_conn *conn; \
-  const struct bgp_stats *stats; \
-  const struct bgp_config *config
-
-#define POPULATE_BGP4(addr, proto, conn, stats, config) populate_bgp4(p, c, &(addr), &(proto), &(conn), &(stats), &(config))
+  const struct bgp_proto UNUSED *proto; \
+  const struct bgp_conn UNUSED *conn; \
+  const struct bgp_stats UNUSED *stats; \
+  const struct bgp_config UNUSED *config
 
+#define POPULATE_BGP4(addr, proto, conn, stats, config) populate_bgp4(d, &(addr), &(proto), &(conn), &(stats), &(config))
 
 static inline void ip4_to_oid(struct oid *oid, ip4_addr addr);
 
@@ -378,8 +376,7 @@ snmp_bgp_notify_backward_trans(struct snmp_proto *p, struct bgp_proto *bgp)
 void
 snmp_bgp4_register(struct snmp_proto *p)
 {
-  u32 bgp_mib_prefix[] = { 1, 15 };
-  // TODO
+  u32 bgp_mib_prefix[] = { 1, BGP4_MIB }; // TODO remove constant
 
   {
     /* Register the whole BGP4-MIB::bgp root tree node */
@@ -602,25 +599,31 @@ oid_state_compare(const struct oid *oid, u8 state)
 }
 
 static inline enum snmp_search_res
-populate_bgp4(struct snmp_proto *p, struct snmp_pdu *c, ip4_addr *addr, const struct bgp_proto **proto, const struct bgp_conn
+populate_bgp4(struct snmp_data *d, ip4_addr *addr, const struct bgp_proto **proto, const struct bgp_conn
 **conn, const struct bgp_stats **stats, const struct bgp_config **config)
 {
-  const struct oid * const oid = &c->sr_vb_start->name;
+  const struct oid * const oid = &d->c->sr_vb_start->name;
   if (snmp_bgp_valid_ip4(oid))
     *addr = ip4_from_oid(oid);
   else
+  {
+    snmp_log("populate() invalid ip4");
     return SNMP_SEARCH_NO_INSTANCE;
+  }
 
-  struct snmp_bgp_peer *pe = snmp_hash_find(p, *addr);
+  struct snmp_bgp_peer *pe = snmp_hash_find(d->p, *addr);
   if (!pe)
+  {
+    snmp_log("populate() hash find failed");
     return SNMP_SEARCH_NO_INSTANCE;
+  }
 
   const struct bgp_proto *bgp_proto;
   *proto = bgp_proto = pe->bgp_proto;
-  if (ipa_is_ip4(bgp_proto->remote_ip))
+  if (!ipa_is_ip4(bgp_proto->remote_ip))
   {
-    log(L_ERR, "%s: Found BGP protocol instance with IPv6 address", bgp_proto->p.name);
-    c->error = AGENTX_RES_GEN_ERROR;
+    log(L_ERR "%s: Found BGP protocol instance with IPv6 address", bgp_proto->p.name);
+    d->c->error = AGENTX_RES_GEN_ERROR;
     return SNMP_SEARCH_NO_INSTANCE;
   }
 
@@ -628,9 +631,9 @@ populate_bgp4(struct snmp_proto *p, struct snmp_pdu *c, ip4_addr *addr, const st
   if (!ip4_equal(proto_ip, pe->peer_ip))
   {
     /* Here, we could be in problem as the bgp_proto IP address could be changed */
-    log(L_ERR, "%s: Stored hash key IP address and peer remote address differ.",
-      bgp_proto->p.name);
-    c->error = AGENTX_RES_GEN_ERROR;
+    log(L_ERR "%s: Stored hash key IP address and peer remote address differ "
+      "(%I, %I).", bgp_proto->p.name, proto_ip, pe->peer_ip);
+    d->c->error = AGENTX_RES_GEN_ERROR;
     return SNMP_SEARCH_NO_INSTANCE;
   }
 
@@ -638,528 +641,409 @@ populate_bgp4(struct snmp_proto *p, struct snmp_pdu *c, ip4_addr *addr, const st
   *stats = &bgp_proto->stats;
   *config = bgp_proto->cf;
 
+  snmp_log("populate() ok");
   return SNMP_SEARCH_OK;
 }
 
 static enum snmp_search_res
-fill_bgp_version(struct snmp_proto *p, struct snmp_pdu *c)
+fill_bgp_version(struct mib_walk_state *walk UNUSED, struct snmp_data *d)
 {
-  if (c->sr_vb_start->name.n_subid != 4)
-  {
-    snmp_set_varbind_type(c->sr_vb_start, AGENTX_NO_SUCH_INSTANCE);
-    return SNMP_SEARCH_NO_INSTANCE;
-  }
-
-  uint sz = snmp_str_size_from_len(1);
-  if (c->size < sz)
-    snmp_manage_tbuf(p, c);
-
-  c->size -= sz;
-  snmp_varbind_nstr(c, BGP4_VERSIONS, 1);
+  snmp_log("fill ver");
+  d->c->size -= snmp_str_size_from_len(1);
+  snmp_varbind_nstr(d->c, BGP4_VERSIONS, 1);
   return SNMP_SEARCH_OK;
 }
 
 static enum snmp_search_res
-fill_local_as(struct snmp_proto *p, struct snmp_pdu *c)
+fill_local_as(struct mib_walk_state *walk UNUSED, struct snmp_data *d)
 {
-  if (c->size < AGENTX_TYPE_INT_SIZE)
-    snmp_manage_tbuf(p, c);
-
-  snmp_varbind_int(c, p->bgp_local_as);
+  snmp_log("fill as");
+  snmp_varbind_int(d->c, d->p->bgp_local_as);
   return SNMP_SEARCH_OK;
 }
 
 static enum snmp_search_res
-fill_peer_id(struct snmp_proto *p, struct snmp_pdu *c)
+fill_peer_id(struct mib_walk_state *walk UNUSED, struct snmp_data *d)
 {
+  snmp_log("fill peer id");
   enum snmp_search_res res;
   DECLARE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
   res = POPULATE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
   if (res != SNMP_SEARCH_OK)
-  {
-    (void) snmp_set_varbind_type(c->sr_vb_start, res);
     return res;
-  }
 
   uint fsm_state = snmp_bgp_fsm_state(bgp_proto);
 
-  if (c->size < AGENTX_TYPE_IP4_SIZE)
-    snmp_manage_tbuf(p, c);
-
   if (fsm_state == BGP4_MIB_OPENCONFIRM || fsm_state == BGP4_MIB_ESTABLISHED)
     // TODO last
-    snmp_varbind_ip4(c, ip4_from_u32(bgp_proto->remote_id));
+    snmp_varbind_ip4(d->c, ip4_from_u32(bgp_proto->remote_id));
   else
-    snmp_varbind_ip4(c, IP4_NONE);
+    snmp_varbind_ip4(d->c, IP4_NONE);
   return SNMP_SEARCH_OK;
 }
 
 static enum snmp_search_res
-fill_peer_state(struct snmp_proto *p, struct snmp_pdu *c)
+fill_peer_state(struct mib_walk_state *walk UNUSED, struct snmp_data *d)
 {
+  snmp_log("fill peer state");
   enum snmp_search_res res;
   DECLARE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
   res = POPULATE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
   if (res != SNMP_SEARCH_OK)
-  {
-    (void) snmp_set_varbind_type(c->sr_vb_start, res);
     return res;
-  }
 
   uint fsm_state = snmp_bgp_fsm_state(bgp_proto);
 
-  if (c->size < AGENTX_TYPE_INT_SIZE)
-    snmp_manage_tbuf(p, c);
-
-  snmp_varbind_int(c, fsm_state);
+  snmp_varbind_int(d->c, fsm_state);
   return SNMP_SEARCH_OK;
 }
 
 static enum snmp_search_res
-fill_admin_status(struct snmp_proto *p, struct snmp_pdu *c)
+fill_admin_status(struct mib_walk_state *walk UNUSED, struct snmp_data *d)
 {
+  snmp_log("fill adm status");
   enum snmp_search_res res;
   DECLARE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
   res = POPULATE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
   if (res != SNMP_SEARCH_OK)
-  {
-    (void) snmp_set_varbind_type(c->sr_vb_start, res);
     return res;
-  }
-
-  if (c->size < AGENTX_TYPE_INT_SIZE)
-    snmp_manage_tbuf(p, c);
 
   if (bgp_proto->p.disabled)
-    snmp_varbind_int(c, AGENTX_ADMIN_STOP);
+    snmp_varbind_int(d->c, AGENTX_ADMIN_STOP);
   else
-    snmp_varbind_int(c, AGENTX_ADMIN_START);
+    snmp_varbind_int(d->c, AGENTX_ADMIN_START);
   return SNMP_SEARCH_OK;
 }
 
 static enum snmp_search_res
-fill_neg_version(struct snmp_proto *p, struct snmp_pdu *c)
+fill_neg_version(struct mib_walk_state *walk UNUSED, struct snmp_data *d)
 {
+  snmp_log("fill neg ver");
   enum snmp_search_res res;
   DECLARE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
   res = POPULATE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
   if (res != SNMP_SEARCH_OK)
-  {
-    (void) snmp_set_varbind_type(c->sr_vb_start, res);
     return res;
-  }
 
   uint fsm_state = snmp_bgp_fsm_state(bgp_proto);
 
-  if (c->size < AGENTX_TYPE_INT_SIZE)
-    snmp_manage_tbuf(p, c);
-
   if (fsm_state == BGP4_MIB_ESTABLISHED || fsm_state == BGP4_MIB_ESTABLISHED)
-    snmp_varbind_int(c, BGP4_MIB_NEGOTIATED_VER_VALUE);
+    snmp_varbind_int(d->c, BGP4_MIB_NEGOTIATED_VER_VALUE);
   else
-    snmp_varbind_int(c, BGP4_MIB_NEGOTIATED_VER_NO_VALUE);
+    snmp_varbind_int(d->c, BGP4_MIB_NEGOTIATED_VER_NO_VALUE);
   return SNMP_SEARCH_OK;
 }
 
 static enum snmp_search_res
-fill_local_addr(struct snmp_proto *p, struct snmp_pdu *c)
+fill_local_addr(struct mib_walk_state *walk UNUSED, struct snmp_data *d)
 {
+  snmp_log("bgp4_mib fill local addr");
   enum snmp_search_res res;
   DECLARE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
   res = POPULATE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
+
+  snmp_log("fill local addr result %u", res);
+
   if (res != SNMP_SEARCH_OK)
-  {
-    (void) snmp_set_varbind_type(c->sr_vb_start, res);
     return res;
-  }
-
-  if (c->size < AGENTX_TYPE_IP4_SIZE)
-    snmp_manage_tbuf(p, c);
 
-  snmp_varbind_ip4(c, ipa_to_ip4(bgp_proto->local_ip));
+  snmp_varbind_ip4(d->c, ipa_to_ip4(bgp_proto->local_ip));
   return SNMP_SEARCH_OK;
 }
 
 static enum snmp_search_res
-fill_local_port(struct snmp_proto *p, struct snmp_pdu *c)
+fill_local_port(struct mib_walk_state *walk UNUSED, struct snmp_data *d)
 {
+  snmp_log("bgp4_mib fill local port");
   enum snmp_search_res res;
   DECLARE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
   res = POPULATE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
   if (res != SNMP_SEARCH_OK)
-  {
-    (void) snmp_set_varbind_type(c->sr_vb_start, res);
     return res;
-  }
-
-  if (c->size < AGENTX_TYPE_INT_SIZE)
-    snmp_manage_tbuf(p, c);
 
-  snmp_varbind_int(c, bgp_conf->local_port);
+  snmp_varbind_int(d->c, bgp_conf->local_port);
   return SNMP_SEARCH_OK;
 }
 
 static enum snmp_search_res
-fill_remove_addr(struct snmp_proto *p, struct snmp_pdu *c)
+fill_remote_addr(struct mib_walk_state *walk UNUSED, struct snmp_data *d)
 {
+  snmp_log("bgp4_mib fill remote addr");
   enum snmp_search_res res;
   DECLARE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
   res = POPULATE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
   if (res != SNMP_SEARCH_OK)
-  {
-    (void) snmp_set_varbind_type(c->sr_vb_start, res);
     return res;
-  }
 
-  if (c->size < AGENTX_TYPE_IP4_SIZE)
-    snmp_manage_tbuf(p, c);
-
-  snmp_varbind_ip4(c, ipa_to_ip4(bgp_proto->remote_ip));
+  snmp_varbind_ip4(d->c, ipa_to_ip4(bgp_proto->remote_ip));
   return SNMP_SEARCH_OK;
 }
 
 static enum snmp_search_res
-fill_remote_port(struct snmp_proto *p, struct snmp_pdu *c)
+fill_remote_port(struct mib_walk_state *walk UNUSED, struct snmp_data *d)
 {
+  snmp_log("bgp4_mib fill remote port");
   enum snmp_search_res res;
   DECLARE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
   res = POPULATE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
   if (res != SNMP_SEARCH_OK)
-  {
-    (void) snmp_set_varbind_type(c->sr_vb_start, res);
     return res;
-  }
 
-  if (c->size < AGENTX_TYPE_INT_SIZE)
-    snmp_manage_tbuf(p, c);
-
-  snmp_varbind_int(c, bgp_conf->remote_port);
+  snmp_varbind_int(d->c, bgp_conf->remote_port);
   return SNMP_SEARCH_OK;
 }
 
 static enum snmp_search_res
-fill_remote_as(struct snmp_proto *p, struct snmp_pdu *c)
+fill_remote_as(struct mib_walk_state *walk UNUSED, struct snmp_data *d)
 {
+  snmp_log("fill rem as");
   enum snmp_search_res res;
   DECLARE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
   res = POPULATE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
   if (res != SNMP_SEARCH_OK)
-  {
-    (void) snmp_set_varbind_type(c->sr_vb_start, res);
     return res;
-  }
 
-  if (c->size < AGENTX_TYPE_INT_SIZE)
-    snmp_manage_tbuf(p, c);
-
-  snmp_varbind_int(c, bgp_proto->remote_as);
+  snmp_varbind_int(d->c, bgp_proto->remote_as);
   return SNMP_SEARCH_OK;
 }
 
 static enum snmp_search_res
-fill_in_updates(struct snmp_proto *p, struct snmp_pdu *c)
+fill_in_updates(struct mib_walk_state *walk UNUSED, struct snmp_data *d)
 {
+  snmp_log("fill in updates");
   enum snmp_search_res res;
   DECLARE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
   res = POPULATE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
   if (res != SNMP_SEARCH_OK)
-  {
-    (void) snmp_set_varbind_type(c->sr_vb_start, res);
     return res;
-  }
 
-  if (c->size < AGENTX_TYPE_COUNTER32_SIZE)
-    snmp_manage_tbuf(p, c);
-
-  snmp_varbind_counter32(c, bgp_stats->rx_updates);
+  snmp_varbind_counter32(d->c, bgp_stats->rx_updates);
   return SNMP_SEARCH_OK;
 }
 
 static enum snmp_search_res
-fill_out_update(struct snmp_proto *p, struct snmp_pdu *c)
+fill_out_updates(struct mib_walk_state *walk UNUSED, struct snmp_data *d)
 {
+  snmp_log("fill out updates");
   enum snmp_search_res res;
   DECLARE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
   res = POPULATE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
   if (res != SNMP_SEARCH_OK)
-  {
-    (void) snmp_set_varbind_type(c->sr_vb_start, res);
     return res;
-  }
-
-  if (c->size < AGENTX_TYPE_COUNTER32_SIZE)
-    snmp_manage_tbuf(p, c);
 
-  snmp_varbind_counter32(c, bgp_stats->tx_updates);
+  snmp_varbind_counter32(d->c, bgp_stats->tx_updates);
   return SNMP_SEARCH_OK;
 }
 
 static enum snmp_search_res
-fill_in_total_msg(struct snmp_proto *p, struct snmp_pdu *c)
+fill_in_total_msg(struct mib_walk_state *walk UNUSED, struct snmp_data *d)
 {
+  snmp_log("fill in total");
   enum snmp_search_res res;
   DECLARE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
   res = POPULATE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
   if (res != SNMP_SEARCH_OK)
-  {
-    (void) snmp_set_varbind_type(c->sr_vb_start, res);
     return res;
-  }
-
-  if (c->size < AGENTX_TYPE_COUNTER32_SIZE)
-    snmp_manage_tbuf(p, c);
 
-  snmp_varbind_counter32(c, bgp_stats->rx_messages);
+  snmp_varbind_counter32(d->c, bgp_stats->rx_messages);
   return SNMP_SEARCH_OK;
 }
 
 static enum snmp_search_res
-fill_out_total_msg(struct snmp_proto *p, struct snmp_pdu *c)
+fill_out_total_msg(struct mib_walk_state *walk UNUSED, struct snmp_data *d)
 {
+  snmp_log("fill out total");
   enum snmp_search_res res;
   DECLARE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
   res = POPULATE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
   if (res != SNMP_SEARCH_OK)
-  {
-    (void) snmp_set_varbind_type(c->sr_vb_start, res);
     return res;
-  }
-
-  if (c->size < AGENTX_TYPE_COUNTER32_SIZE)
-    snmp_manage_tbuf(p, c);
 
-  snmp_varbind_counter32(c, bgp_stats->tx_messages);
+  snmp_varbind_counter32(d->c, bgp_stats->tx_messages);
   return SNMP_SEARCH_OK;
 }
 
 static enum snmp_search_res
-fill_last_err(struct snmp_proto *p, struct snmp_pdu *c)
+fill_last_err(struct mib_walk_state *walk UNUSED, struct snmp_data *d)
 {
+  snmp_log("fill last err");
   enum snmp_search_res res;
   DECLARE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
   res = POPULATE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
   if (res != SNMP_SEARCH_OK)
-  {
-    (void) snmp_set_varbind_type(c->sr_vb_start, res);
     return res;
-  }
-
-  if (c->size < snmp_str_size_from_len(2))
-    snmp_manage_tbuf(p, c);
 
   char last_error[2];
   snmp_bgp_last_error(bgp_proto, last_error);
 
-  snmp_varbind_nstr(c, last_error, 2);
+  snmp_varbind_nstr(d->c, last_error, 2);
   return SNMP_SEARCH_OK;
 }
 
 static enum snmp_search_res
-fill_established_trans(struct snmp_proto *p, struct snmp_pdu *c)
+fill_established_trans(struct mib_walk_state *walk UNUSED, struct snmp_data *d)
 {
+  snmp_log("fill est trans");
   enum snmp_search_res res;
   DECLARE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
   res = POPULATE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
   if (res != SNMP_SEARCH_OK)
-  {
-    (void) snmp_set_varbind_type(c->sr_vb_start, res);
     return res;
-  }
 
-  if (c->size < AGENTX_TYPE_COUNTER32_SIZE)
-    snmp_manage_tbuf(p, c);
-
-  snmp_varbind_counter32(c,
+  snmp_varbind_counter32(d->c,
       bgp_stats->fsm_established_transitions);
   return SNMP_SEARCH_OK;
 }
 
 static enum snmp_search_res
-fill_established_time(struct snmp_proto *p, struct snmp_pdu *c)
+fill_established_time(struct mib_walk_state *walk UNUSED, struct snmp_data *d)
 {
+  snmp_log("fill est time");
   enum snmp_search_res res;
   DECLARE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
   res = POPULATE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
   if (res != SNMP_SEARCH_OK)
-  {
-    (void) snmp_set_varbind_type(c->sr_vb_start, res);
     return res;
-  }
-
-  if (c->size < AGENTX_TYPE_COUNTER32_SIZE)
-    snmp_manage_tbuf(p, c);
 
 
-  snmp_varbind_gauge32(c,
+  snmp_varbind_gauge32(d->c,
        (current_time() - bgp_proto->last_established) TO_S);
   return SNMP_SEARCH_OK;
 }
 
 static enum snmp_search_res
-fill_retry_interval(struct snmp_proto *p, struct snmp_pdu *c)
+fill_retry_interval(struct mib_walk_state *walk UNUSED, struct snmp_data *d)
 {
+  snmp_log("fill retry int");
   enum snmp_search_res res;
   DECLARE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
   res = POPULATE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
   if (res != SNMP_SEARCH_OK)
-  {
-    (void) snmp_set_varbind_type(c->sr_vb_start, res);
     return res;
-  }
-
-  if (c->size < AGENTX_TYPE_INT_SIZE)
-    snmp_manage_tbuf(p, c);
 
-  snmp_varbind_int(c, bgp_conf->connect_retry_time);
+  snmp_varbind_int(d->c, bgp_conf->connect_retry_time);
   return SNMP_SEARCH_OK;
 }
 
 static enum snmp_search_res
-fill_hold_time(struct snmp_proto *p, struct snmp_pdu *c)
+fill_hold_time(struct mib_walk_state *walk UNUSED, struct snmp_data *d)
 {
+  snmp_log("fill hold time");
   enum snmp_search_res res;
   DECLARE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
   res = POPULATE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
   if (res != SNMP_SEARCH_OK)
-  {
-    (void) snmp_set_varbind_type(c->sr_vb_start, res);
     return res;
-  }
-
-  if (c->size < AGENTX_TYPE_INT_SIZE)
-    snmp_manage_tbuf(p, c);
 
-  snmp_varbind_int(c, (bgp_conn) ?  bgp_conn->hold_time : 0);
+  snmp_varbind_int(d->c, (bgp_conn) ?  bgp_conn->hold_time : 0);
   return SNMP_SEARCH_OK;
 }
 
 static enum snmp_search_res
-fill_keep_alive(struct snmp_proto *p, struct snmp_pdu *c)
+fill_keep_alive(struct mib_walk_state *walk UNUSED, struct snmp_data *d)
 {
+  snmp_log("fill keepalive");
   enum snmp_search_res res;
   DECLARE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
   res = POPULATE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
   if (res != SNMP_SEARCH_OK)
-  {
-    (void) snmp_set_varbind_type(c->sr_vb_start, res);
     return res;
-  }
-
-  if (c->size < AGENTX_TYPE_INT_SIZE)
-    snmp_manage_tbuf(p, c);
 
   if (!bgp_conf->hold_time)
-    snmp_varbind_int(c, 0);
+    snmp_varbind_int(d->c, 0);
   else
-    snmp_varbind_int(c,
+    snmp_varbind_int(d->c,
       (bgp_conn) ? bgp_conn->keepalive_time : 0);
   return SNMP_SEARCH_OK;
 }
 
 static enum snmp_search_res
-fill_hold_time_conf(struct snmp_proto *p, struct snmp_pdu *c)
+fill_hold_time_conf(struct mib_walk_state *walk UNUSED, struct snmp_data *d)
 {
+  snmp_log("fill hold time c");
   enum snmp_search_res res;
   DECLARE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
   res = POPULATE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
   if (res != SNMP_SEARCH_OK)
-  {
-    (void) snmp_set_varbind_type(c->sr_vb_start, res);
     return res;
-  }
 
-  if (c->size < AGENTX_TYPE_INT_SIZE)
-    snmp_manage_tbuf(p, c);
-
-  snmp_varbind_int(c, bgp_conf->hold_time);
+  snmp_varbind_int(d->c, bgp_conf->hold_time);
   return SNMP_SEARCH_OK;
 }
 
 static enum snmp_search_res
-fill_keep_alive_conf(struct snmp_proto *p, struct snmp_pdu *c)
+fill_keep_alive_conf(struct mib_walk_state *walk UNUSED, struct snmp_data *d)
 {
+  snmp_log("fill keepalive c");
   enum snmp_search_res res;
   DECLARE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
   res = POPULATE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
   if (res != SNMP_SEARCH_OK)
-  {
-    (void) snmp_set_varbind_type(c->sr_vb_start, res);
     return res;
-  }
-
-  if (c->size < AGENTX_TYPE_INT_SIZE)
-    snmp_manage_tbuf(p, c);
 
 
   if (!bgp_conf->keepalive_time)
-    snmp_varbind_int(c, 0);
+    snmp_varbind_int(d->c, 0);
   else
-    snmp_varbind_int(c,
+    snmp_varbind_int(d->c,
       (bgp_conn) ? bgp_conn->keepalive_time : 0);
   return SNMP_SEARCH_OK;
 }
 
 static enum snmp_search_res
-fill_min_as_org_interval(struct snmp_proto *p, struct snmp_pdu *c)
+fill_min_as_org_interval(struct mib_walk_state *walk UNUSED, struct snmp_data *d)
 {
+  snmp_log("fill min org int");
   enum snmp_search_res res;
   DECLARE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
   res = POPULATE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
   if (res != SNMP_SEARCH_OK)
-  {
-    (void) snmp_set_varbind_type(c->sr_vb_start, res);
     return res;
-  }
 
   /* value should be in 1..65535 but is not supported by bird */
-  if (c->size < AGENTX_TYPE_INT_SIZE)
-    snmp_manage_tbuf(p, c);
-
-  snmp_varbind_int(c, 0);
+  snmp_varbind_int(d->c, 0);
   return SNMP_SEARCH_OK;
 }
 
 static enum snmp_search_res
-fill_route_adv_interval(struct snmp_proto *p, struct snmp_pdu *c)
+fill_route_adv_interval(struct mib_walk_state *walk UNUSED, struct snmp_data *d)
 {
+  snmp_log("fill rt adv int");
   enum snmp_search_res res;
   DECLARE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
   res = POPULATE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
   if (res != SNMP_SEARCH_OK)
-  {
-    (void) snmp_set_varbind_type(c->sr_vb_start, res);
     return res;
-  }
 
   /* value should be in 1..65535 but is not supported by bird */
-  if (c->size < AGENTX_TYPE_INT_SIZE)
-    snmp_manage_tbuf(p, c);
-
-  snmp_varbind_int(c, 0);
+  snmp_varbind_int(d->c, 0);
   return SNMP_SEARCH_OK;
 }
 
 static enum snmp_search_res
-fill_in_update_elapsed_time(struct snmp_proto *p, struct snmp_pdu *c)
+fill_in_update_elapsed_time(struct mib_walk_state *walk UNUSED, struct snmp_data *d)
 {
+  snmp_log("fil in elapsed");
   enum snmp_search_res res;
   DECLARE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
   res = POPULATE_BGP4(addr, bgp_proto, bgp_conn, bgp_stats, bgp_conf);
   if (res != SNMP_SEARCH_OK)
-  {
-    (void) snmp_set_varbind_type(c->sr_vb_start, res);
     return res;
-  }
-
-  if (c->size < AGENTX_TYPE_INT_SIZE)
-    snmp_manage_tbuf(p, c);
 
-  snmp_varbind_gauge32(c,
+  snmp_varbind_gauge32(d->c,
     (current_time() - bgp_proto->last_rx_update) TO_S
   );
   return SNMP_SEARCH_OK;
 }
 
+static enum snmp_search_res
+fill_local_id(struct mib_walk_state *walk UNUSED, struct snmp_data *d)
+{
+  snmp_log("fill local id");
+  snmp_varbind_ip4(d->c, d->p->bgp_local_id);
+  return SNMP_SEARCH_OK;
+}
+
 static struct oid *
 update_bgp_vb(struct snmp_proto *p, struct agentx_varbind **vb, u8 state, struct snmp_pdu *c)
 {
@@ -1499,8 +1383,8 @@ bgp_fill_dynamic(struct snmp_proto *p, struct agentx_varbind **vb, struct snmp_p
     return;
   }
 
-  const struct bgp_conn *bgp_conn = bgp_proto->conn;
-  const struct bgp_stats *bgp_stats = &bgp_proto->stats;
+  //const struct bgp_conn *bgp_conn = bgp_proto->conn;
+  //const struct bgp_stats *bgp_stats = &bgp_proto->stats;
   const struct bgp_config *bgp_conf = bgp_proto->cf;
 
   uint fsm_state = snmp_bgp_fsm_state(bgp_proto);
@@ -1513,18 +1397,24 @@ bgp_fill_dynamic(struct snmp_proto *p, struct agentx_varbind **vb, struct snmp_p
   {
     case BGP4_MIB_S_PEER_IDENTIFIER:
       if (c->size < AGENTX_TYPE_IP4_SIZE)
+      {
+       snmp_log("BGP4-MIB small buffer");
        snmp_manage_tbuf(p, c);
+      }
 
       if (fsm_state == BGP4_MIB_OPENCONFIRM || fsm_state == BGP4_MIB_ESTABLISHED)
        // TODO last
-       ; //snmp_varbind_ip4(*vb, c, ip4_from_u32(bgp_proto->remote_id));
+       {} //snmp_varbind_ip4(*vb, c, ip4_from_u32(bgp_proto->remote_id));
       else
-       ; //snmp_varbind_ip4(*vb, c, IP4_NONE);
+       {} //snmp_varbind_ip4(*vb, c, IP4_NONE);
       break;
 
     case BGP4_MIB_S_STATE:
       if (c->size < AGENTX_TYPE_INT_SIZE)
+      {
+       snmp_log("BGP4-MIB small buffer 2");
        snmp_manage_tbuf(p, c);
+      }
 
       //snmp_varbind_int(*vb, c, fsm_state);
       break;
@@ -1534,9 +1424,9 @@ bgp_fill_dynamic(struct snmp_proto *p, struct agentx_varbind **vb, struct snmp_p
        SNMP_MANAGE_TBUF(p, vb, c);
 
       if (bgp_proto->p.disabled)
-       ; //snmp_varbind_int(*vb, c, AGENTX_ADMIN_STOP);
+       {} //snmp_varbind_int(*vb, c, AGENTX_ADMIN_STOP);
       else
-       ; //snmp_varbind_int(*vb, c, AGENTX_ADMIN_START);
+       {} //snmp_varbind_int(*vb, c, AGENTX_ADMIN_START);
 
       break;
 
@@ -1547,9 +1437,9 @@ bgp_fill_dynamic(struct snmp_proto *p, struct agentx_varbind **vb, struct snmp_p
       uint fsm_state = snmp_bgp_fsm_state(bgp_proto);
 
       if (fsm_state == BGP4_MIB_ESTABLISHED || fsm_state == BGP4_MIB_ESTABLISHED)
-       ; //snmp_varbind_int(*vb, c, BGP4_MIB_NEGOTIATED_VER_VALUE);
+       {} //snmp_varbind_int(*vb, c, BGP4_MIB_NEGOTIATED_VER_VALUE);
       else
-       ; //snmp_varbind_int(*vb, c, BGP4_MIB_NEGOTIATED_VER_NO_VALUE);
+       {} //snmp_varbind_int(*vb, c, BGP4_MIB_NEGOTIATED_VER_NO_VALUE);
 
       break;
 
@@ -1557,70 +1447,70 @@ bgp_fill_dynamic(struct snmp_proto *p, struct agentx_varbind **vb, struct snmp_p
       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));
+      {} //snmp_varbind_ip4(*vb, c, ipa_to_ip4(bgp_proto->local_ip));
       break;
 
     case BGP4_MIB_S_LOCAL_PORT:
       if (c->size < AGENTX_TYPE_INT_SIZE)
        SNMP_MANAGE_TBUF(p, vb, c);
 
-      ; //snmp_varbind_int(*vb, c, bgp_conf->local_port);
+      {} //snmp_varbind_int(*vb, c, bgp_conf->local_port);
       break;
 
     case BGP4_MIB_S_REMOTE_ADDR:
       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));
+      {} //snmp_varbind_ip4(*vb, c, ipa_to_ip4(bgp_proto->remote_ip));
       break;
 
     case BGP4_MIB_S_REMOTE_PORT:
       if (c->size < AGENTX_TYPE_INT_SIZE)
        SNMP_MANAGE_TBUF(p, vb, c);
 
-      ; //snmp_varbind_int(*vb, c, bgp_conf->remote_port);
+      {} //snmp_varbind_int(*vb, c, bgp_conf->remote_port);
       break;
 
     case BGP4_MIB_S_REMOTE_AS:
       if (c->size < AGENTX_TYPE_INT_SIZE)
        SNMP_MANAGE_TBUF(p, vb, c);
 
-      ; //snmp_varbind_int(*vb, c, bgp_proto->remote_as);
+      {} //snmp_varbind_int(*vb, c, bgp_proto->remote_as);
       break;
 
     case BGP4_MIB_S_RX_UPDATES:          /* bgpPeerInUpdates */
       if (c->size < AGENTX_TYPE_COUNTER32_SIZE)
        SNMP_MANAGE_TBUF(p, vb, c);
 
-      ; //snmp_varbind_counter32(*vb, c, bgp_stats->rx_updates);
+      {} //snmp_varbind_counter32(*vb, c, bgp_stats->rx_updates);
       break;
 
     case BGP4_MIB_S_TX_UPDATES:          /* bgpPeerOutUpdate */
       if (c->size < AGENTX_TYPE_COUNTER32_SIZE)
        SNMP_MANAGE_TBUF(p, vb, c);
 
-      ; //snmp_varbind_counter32(*vb, c, bgp_stats->tx_updates);
+      {} //snmp_varbind_counter32(*vb, c, bgp_stats->tx_updates);
       break;
 
     case BGP4_MIB_S_RX_MESSAGES:  /* bgpPeerInTotalMessages */
       if (c->size < AGENTX_TYPE_COUNTER32_SIZE)
        SNMP_MANAGE_TBUF(p, vb, c);
 
-      ; //snmp_varbind_counter32(*vb, c, bgp_stats->rx_messages);
+      {} //snmp_varbind_counter32(*vb, c, bgp_stats->rx_messages);
       break;
 
     case BGP4_MIB_S_TX_MESSAGES:  /* bgpPeerOutTotalMessages */
       if (c->size < AGENTX_TYPE_COUNTER32_SIZE)
        SNMP_MANAGE_TBUF(p, vb, c);
 
-      ; //snmp_varbind_counter32(*vb, c, bgp_stats->tx_messages);
+      {} //snmp_varbind_counter32(*vb, c, bgp_stats->tx_messages);
       break;
 
     case BGP4_MIB_S_LAST_ERROR:
       if (c->size < snmp_str_size_from_len(2))
        SNMP_MANAGE_TBUF(p, vb, c);
 
-      ; //snmp_varbind_nstr(*vb, c, last_error, 2);
+      {} //snmp_varbind_nstr(*vb, c, last_error, 2);
       break;
 
     case BGP4_MIB_S_FSM_TRANSITIONS:
@@ -1659,9 +1549,9 @@ bgp_fill_dynamic(struct snmp_proto *p, struct agentx_varbind **vb, struct snmp_p
        SNMP_MANAGE_TBUF(p, vb, c);
 
       if (!bgp_conf->hold_time)
-       ; //snmp_varbind_int(*vb, c, 0);
+       {} //snmp_varbind_int(*vb, c, 0);
       else
-       ; //snmp_varbind_int(*vb, c,
+       {} //snmp_varbind_int(*vb, c,
        //  (bgp_conn) ? bgp_conn->keepalive_time : 0);
       break;
 
@@ -1678,9 +1568,9 @@ bgp_fill_dynamic(struct snmp_proto *p, struct agentx_varbind **vb, struct snmp_p
 
 
       if (!bgp_conf->keepalive_time)
-       ; //snmp_varbind_int(*vb, c, 0);
+       {} //snmp_varbind_int(*vb, c, 0);
       else
-       ; //snmp_varbind_int(*vb, c,
+       {} //snmp_varbind_int(*vb, c,
          // (bgp_conn) ? bgp_conn->keepalive_time : 0);
       break;
 
@@ -1730,6 +1620,7 @@ bgp_fill_dynamic(struct snmp_proto *p, struct agentx_varbind **vb, struct snmp_p
 void
 bgp_fill_static(struct snmp_proto *p, struct agentx_varbind **vb, struct snmp_pdu *c, u8 state)
 {
+  (void)p;
   ASSUME(c->buffer == snmp_varbind_data(*vb));
 
   struct oid *oid = &(*vb)->name;
@@ -1795,6 +1686,82 @@ snmp_bgp_fill(struct snmp_proto *p, struct agentx_varbind **vb, struct snmp_pdu
   snmp_set_varbind_type(*vb, AGENTX_NO_SUCH_OBJECT);
 }
 
+/*
+ * bgp4_next_peer
+ */
+static int UNUSED
+bgp4_next_peer(struct mib_walk_state *state, struct snmp_data *data)
+{
+  //struct agentx_varbind *vb = data->c->sr_vb_start;
+  struct oid *oid = &data->c->sr_vb_start->name;
+
+  ip4_addr ip4 = ip4_from_oid(oid);
+
+  /* BGP4-MIB::bgpPeerIdentifier */
+  STATIC_OID(9) bgp4_peer_id = {
+    .n_subid = 9,
+    .prefix = SNMP_MGMT,
+    .include = 0,
+    .reserved = 0,
+    .ids = { SNMP_MIB_2, BGP4_MIB,
+      BGP4_MIB_PEER_TABLE, BGP4_MIB_PEER_ENTRY, BGP4_MIB_PEER_IDENTIFIER,
+      /* IP4_NONE */ 0, 0, 0, 0 }
+  };
+
+  const struct oid *peer_oid = (const struct oid *) &bgp4_peer_id;
+
+  if (snmp_oid_compare(oid, peer_oid) < 0 || LOAD_U8(oid->n_subid) < 9)
+  {
+    die("unreachable?");
+
+    int old = snmp_oid_size(oid);
+    int new = snmp_oid_size(peer_oid);
+
+    if (new - old > 0 && (uint) new - old > data->c->size)
+    {
+      snmp_log("bgp4_next_peer small buffer");
+      snmp_manage_tbuf(data->p, data->c);
+    }
+
+    if (new > old)
+      data->c->buffer += (new - old);
+
+    snmp_oid_copy(oid, peer_oid);
+
+    STORE_U8(oid->include, 1);
+  }
+
+  ASSUME(oid->n_subid == 9);
+  /* +1 includes empty prefix */
+
+  net_addr net;
+  net_fill_ip4(&net, ip4, IP4_MAX_PREFIX_LENGTH);
+  struct f_trie_walk_state ws;
+
+  int match = trie_walk_init(&ws, data->p->bgp_trie, &net, 1);
+
+  if (match && LOAD_U8(oid->include))
+  {
+    STORE_U8(oid->include, 0);
+    ip4_to_oid(oid, ip4);
+    return 0;
+  }
+
+  /* We skip the first match as we should not include ip address in oid */
+  if (match)
+   (void) trie_walk_next(&ws, &net);
+
+  if (trie_walk_next(&ws, &net))
+  {
+    ASSUME(oid->n_subid == 9);
+    ip4_addr res = ipa_to_ip4(net_prefix(&net));
+    ip4_to_oid(oid, res);
+    return 0;
+  }
+
+  return 1;
+}
+
 /*
  * snmp_bgp_start - prepare BGP4-MIB
  * @p - SNMP protocol instance holding memory pool
@@ -1803,7 +1770,7 @@ snmp_bgp_fill(struct snmp_proto *p, struct agentx_varbind **vb, struct snmp_pdu
  * It is gruaranteed that the BGP protocols exist.
  */
 void
-snmp_bgp_start(struct snmp_proto *p)
+snmp_bgp4_start(struct snmp_proto *p)
 {
   struct snmp_config *cf = SKIP_BACK(struct snmp_config, cf, p->p.cf);
   /* Create binding to BGP protocols */
@@ -1828,4 +1795,201 @@ snmp_bgp_start(struct snmp_proto *p)
 
     snmp_hash_add_peer(p, peer);
   }
+
+  const STATIC_OID(2) bgp4_mib_root = {
+    .n_subid = 2,
+    .prefix = SNMP_MGMT,
+    .include = 0,
+    .reserved = 0,
+    .ids = { SNMP_MIB_2, BGP4_MIB },
+  };
+
+  const STATIC_OID(4) bgp4_mib_peer_entry = {
+    .n_subid = 4,
+    .prefix = SNMP_MGMT,
+    .include = 0,
+    .reserved = 0,
+    .ids = { SNMP_MIB_2, BGP4_MIB, BGP4_MIB_PEER_TABLE, BGP4_MIB_PEER_ENTRY },
+  };
+
+  (void) mib_tree_hint(p->pool, p->mib_tree,
+    (const struct oid *) &bgp4_mib_root, BGP4_MIB_IDENTIFIER);
+  (void) mib_tree_hint(p->pool, p->mib_tree,
+    (const struct oid *) &bgp4_mib_peer_entry, BGP4_MIB_IN_UPDATE_ELAPSED_TIME);
+
+  mib_node_u *node;
+  struct mib_leaf *leaf;
+  STATIC_OID(3) bgp4_var = {
+    .n_subid = 3,
+    .prefix = SNMP_MGMT,
+    .include = 0,
+    .reserved = 0,
+    .ids = { SNMP_MIB_2, BGP4_MIB, BGP4_MIB_VERSION },
+  };
+
+  struct {
+    u32 id;
+    enum snmp_search_res (*filler)(struct mib_walk_state *state, struct snmp_data *data);
+    enum agentx_type type;
+    int size;
+  } leafs[] = {
+    {
+      .id = BGP4_MIB_VERSION,
+      .filler = fill_bgp_version,
+      .type = AGENTX_OCTET_STRING,
+      .size = snmp_str_size_from_len(sizeof(BGP4_VERSIONS)),
+    },
+    {
+      .id =  BGP4_MIB_LOCAL_AS,
+      .filler = fill_local_as,
+      .type = AGENTX_INTEGER,
+    },
+    {
+      .id =  BGP4_MIB_IDENTIFIER,
+      .filler = fill_local_id,
+      .type = AGENTX_IP_ADDRESS,
+    },
+  };
+
+  for (uint i = 0; i < ARRAY_SIZE(leafs); i++)
+  {
+    bgp4_var.ids[ARRAY_SIZE(bgp4_var.ids) - 1] = leafs[i].id;
+    node = mib_tree_add(p->pool, p->mib_tree, (const struct oid *) &bgp4_var, 1);
+
+    ASSUME(mib_node_is_leaf(node));
+    leaf = &node->leaf;
+
+    leaf->filler = leafs[i].filler;
+    leaf->call_next = NULL; // TODO
+    leaf->type = leafs[i].type;
+    leaf->size = leafs[i].size;
+  }
+
+  STATIC_OID(5) bgp4_entry_var = {
+    .n_subid = 5,
+    .prefix = SNMP_MGMT,
+    .include = 0,
+    .reserved = 0,
+    .ids = { SNMP_MIB_2, BGP4_MIB,
+       BGP4_MIB_PEER_TABLE, BGP4_MIB_PEER_ENTRY, BGP4_MIB_PEER_IDENTIFIER },
+  };
+
+  struct {
+      enum snmp_search_res (*filler)(struct mib_walk_state *state, struct snmp_data *data);
+      enum agentx_type type;
+      int size;
+  } entry_leafs[] = {
+    [BGP4_MIB_PEER_IDENTIFIER] = {
+      .filler =        fill_peer_id,
+      .type = AGENTX_IP_ADDRESS,
+    },
+    [BGP4_MIB_STATE] = {
+      .filler = fill_peer_state,
+      .type = AGENTX_INTEGER,
+    },
+    [BGP4_MIB_ADMIN_STATUS] = {
+      .filler = fill_admin_status,
+      .type = AGENTX_INTEGER,
+    },
+    [BGP4_MIB_NEGOTIATED_VERSION] = {
+      .filler = fill_neg_version,
+      .type = AGENTX_INTEGER,
+    },
+    [BGP4_MIB_LOCAL_ADDR] = {
+      .filler = fill_local_addr,
+      .type = AGENTX_IP_ADDRESS,
+    },
+    [BGP4_MIB_LOCAL_PORT] = {
+      .filler = fill_local_port,
+      .type = AGENTX_INTEGER,
+    },
+    [BGP4_MIB_REMOTE_ADDR] = {
+      .filler = fill_remote_addr,
+      .type = AGENTX_IP_ADDRESS,
+    },
+    [BGP4_MIB_REMOTE_PORT] = {
+      .filler = fill_remote_port,
+      .type = AGENTX_INTEGER,
+    },
+    [BGP4_MIB_REMOTE_AS] = {
+      .filler = fill_remote_as,
+      .type = AGENTX_INTEGER,
+    },
+    [BGP4_MIB_RX_UPDATES] = {
+      .filler = fill_in_updates,
+      .type = AGENTX_COUNTER_32,
+    },
+    [BGP4_MIB_TX_UPDATES] = {
+      .filler = fill_out_updates,
+      .type = AGENTX_COUNTER_32,
+    },
+    [BGP4_MIB_RX_MESSAGES] = {
+      .filler = fill_in_total_msg,
+      .type = AGENTX_COUNTER_32,
+    },
+    [BGP4_MIB_TX_MESSAGES] = {
+      .filler = fill_out_total_msg,
+      .type = AGENTX_COUNTER_32,
+    },
+    [BGP4_MIB_LAST_ERROR] = {
+      .filler = fill_last_err,
+      .type = AGENTX_OCTET_STRING,
+      .size = snmp_str_size_from_len(2),
+    },
+    [BGP4_MIB_FSM_TRANSITIONS] = {
+      .filler = fill_established_trans,
+      .type = AGENTX_COUNTER_32,
+    },
+    [BGP4_MIB_FSM_ESTABLISHED_TIME] = {
+      .filler = fill_established_time,
+      .type = AGENTX_GAUGE_32,
+    },
+    [BGP4_MIB_RETRY_INTERVAL] = {
+      .filler = fill_retry_interval,
+      .type = AGENTX_INTEGER,
+    },
+    [BGP4_MIB_HOLD_TIME] = {
+      .filler = fill_hold_time,
+      .type = AGENTX_INTEGER,
+    },
+    [BGP4_MIB_KEEPALIVE] = {
+      .filler = fill_keep_alive,
+      .type = AGENTX_INTEGER,
+    },
+    [BGP4_MIB_HOLD_TIME_CONFIGURED] = {
+      .filler = fill_hold_time_conf,
+      .type = AGENTX_INTEGER,
+    },
+    [BGP4_MIB_KEEPALIVE_CONFIGURED] = {
+      .filler = fill_keep_alive_conf,
+      .type = AGENTX_INTEGER,
+    },
+    [BGP4_MIB_ORIGINATION_INTERVAL] = {
+      .filler = fill_min_as_org_interval,
+      .type = AGENTX_INTEGER,
+    },
+    [BGP4_MIB_MIN_ROUTE_ADVERTISEMENT] = {
+      .filler = fill_route_adv_interval,
+      .type = AGENTX_INTEGER,
+    },
+    [BGP4_MIB_IN_UPDATE_ELAPSED_TIME] = {
+      .filler = fill_in_update_elapsed_time,
+      .type = AGENTX_GAUGE_32,
+    },
+  }; /* struct _anon entry_leafs[] */
+
+  for (enum bgp4_mib_peer_entry_row e = BGP4_MIB_PEER_IDENTIFIER;
+      e <= BGP4_MIB_IN_UPDATE_ELAPSED_TIME; e++)
+  {
+    bgp4_entry_var.ids[ARRAY_SIZE(bgp4_entry_var.ids) - 1] = (u32) e;
+    node = mib_tree_add(p->pool, p->mib_tree, (const struct oid *) &bgp4_entry_var, 1);
+
+    ASSUME(mib_node_is_leaf(node));
+    leaf = &node->leaf;
+
+    leaf->filler = entry_leafs[e].filler;
+    leaf->call_next = bgp4_next_peer;
+    leaf->type = entry_leafs[e].type;
+    leaf->size = entry_leafs[e].size;
+  }
 }
index 2cac7d8caf1264c654f515ac65530262609d2212..b77f7f34cc5a56c82fbabc077b8a237ca2224ec8 100644 (file)
@@ -1,29 +1,10 @@
 #ifndef _BIRD_SNMP_BGP4_MIB_H_
 #define _BIRD_SNMP_BGP4_MIB_H_
 
-#ifdef _BIRD_SNMP_SUBAGENT_H_
-#define BIRD_SNMP_BGP4_SKIP
-#endif
-
 #include "snmp.h"
 #include "proto/bgp/bgp.h"
-
-void snmp_bgp4_register(struct snmp_proto *p);
-
-struct bgp4_mib {
-  enum snmp_tags tag; /* always BGP4_MIB, see subagent.h for more details */
-
-  ip4_addr addr;
-  const struct bgp_proto *bgp_proto;
-  const struct bgp_conn *bgp_conn;
-  const struct bgp_stats *bgp_stats;
-  const struct bgp_config *bgp_conf;
-};
-
 #include "subagent.h"
 
-#ifndef BIRD_SNMP_BGP4_SKIP
-
 #define BGP4_MIB 15
 
 /* peers attributes */
@@ -63,6 +44,8 @@ enum bgp4_mib_peer_entry_row {
 u8 snmp_bgp_get_valid(u8 state);
 u8 snmp_bgp_getnext_valid(u8 state);
 
+void snmp_bgp4_register(struct snmp_proto *p);
+
 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);
@@ -142,5 +125,3 @@ STATIC_ASSERT(BGP4_MIB_ESTABLISHED == BS_ESTABLISHED + 1);
 #define BGP4_MIB_BACKWARD_TRANS_NOTIFICATION 2
 
 #endif
-
-#endif
index a76723e2bbef2160fe5e8a3c39831bae7a12cdc0..709f7014bc523b41adaa6c508d2a93030eaac0a3 100644 (file)
@@ -57,6 +57,27 @@ mib_tree_init(pool *p, struct mib_tree *t)
   (void) mib_tree_add(p, t, oid, 0);
 }
 
+int
+mib_tree_hint(pool *p, struct mib_tree *t, const struct oid *oid, uint size)
+{
+  mib_node_u *node = mib_tree_add(p, t, oid, 0);
+  if (!node || mib_node_is_leaf(node))
+    return 0;
+
+  struct mib_node *inner = &node->inner;
+  if (inner->child_len >= size + 1)
+    return 1;
+
+  u32 old_len = inner->child_len;
+  inner->child_len = size + 1;
+  inner->children = realloc(inner->children,
+    inner->child_len * sizeof(mib_node_u *));
+
+  for (u32 i = old_len; i < inner->child_len; i++)
+    inner->children[i] = NULL;
+  return 1;
+}
+
 
 // TODO: This function does not work with leaf nodes inside the snmp_internet prefix
 // area
@@ -602,6 +623,61 @@ mib_tree_walk_to_oid(const struct mib_walk_state *walk, struct oid *result, u32
   return 0;
 }
 
+/**
+ * mib_tree_walk_is_oid_descendant - check if OID is in walk subtree
+ * @walk: MIB tree walk state
+ * @oid: OID to use
+ *
+ * Return 0 if @walk specify same path in MIB tree as @oid, return +1 if @oid is
+ * in @walk subtree, return -1 otherwise.
+ */
+int
+mib_tree_walk_is_oid_descendant(const struct mib_walk_state *walk, const struct oid *oid)
+{
+  /* walk stack index skipped zero prefix and OID subidentifier index */
+  u32 i = 1, j = 0;
+
+  if (!walk->stack_pos && snmp_is_oid_empty(oid))
+    return 0;
+
+  if (snmp_oid_is_prefixed(oid))
+  {
+    for (; i < MIN(walk->stack_pos - 1, ARRAY_SIZE(snmp_internet) + 1); i++)
+    {
+      if (walk->stack[i]->empty.id != snmp_internet[i - 1])
+       return -1;
+    }
+
+    if (i == walk->stack_pos)
+      return +1;
+
+    if (i < walk->stack_pos &&
+       walk->stack[i]->empty.id != (u32)LOAD_U8(oid->prefix))
+      return -1;
+
+    i++;
+  }
+
+  u32 ids = LOAD_U8(oid->n_subid);
+  for (; i < walk->stack_pos && j < ids; i++, j++)
+  {
+    if (walk->stack[i]->empty.id != LOAD_U32(oid->ids[j]))
+      return -1;
+  }
+
+  if (i < walk->stack_pos)
+    return -1;
+  else if (i == walk->stack_pos && j == ids)
+    return 0;
+  else if (i == walk->stack_pos)
+    return +1;
+  else
+  {
+    die("unreachable");
+    return -1;
+  }
+}
+
 mib_node_u *
 mib_tree_walk_next(const struct mib_tree *t, struct mib_walk_state *walk)
 {
@@ -656,7 +732,10 @@ mib_tree_walk_next_leaf(const struct mib_tree *t, struct mib_walk_state *walk)
   (void)t;
 
   if (walk->stack_pos == 0)
+  {
+    snmp_log("walk next leaf no leafs");
     return NULL;
+  }
 
   u32 next_id = 0;
   mib_node_u *node = walk->stack[walk->stack_pos - 1];
@@ -671,6 +750,7 @@ mib_tree_walk_next_leaf(const struct mib_tree *t, struct mib_walk_state *walk)
   {
     /* walk->stack_pos == 1, so we NULL out the last stack field */
     walk->stack[--walk->stack_pos] = NULL;
+    snmp_log("walk next leaf single leaf");
     return NULL;
   }
 
@@ -680,7 +760,10 @@ continue_while:
     node = walk->stack[walk->stack_pos - 1];
 
     if (mib_node_is_leaf(node))
+    {
+      snmp_log("walk next leaf %p at level %u", node, walk->stack_pos - 1);
       return (struct mib_leaf *) node;
+    }
 
     struct mib_node *node_inner = &node->inner;
     for (u32 id = next_id; id < node_inner->child_len; id++)
@@ -700,6 +783,7 @@ continue_while:
     walk->stack[--walk->stack_pos] = NULL;
   }
 
+  snmp_log("walk next leaf no more leafs");
   return NULL;
 }
 
index 14e8d7d8f68e74ab6c9b2440db603d4f140d2745..cf1b94fd444a9c6a57b1310f8539561be281b1e3 100644 (file)
@@ -26,12 +26,52 @@ struct mib_node {
 
 struct mib_walk_state;
 
+//typedef enum snmp_search_res (*snmp_filler_hook_t)(struct mib_walk_state *state, struct snmp_data *data);
+
 struct mib_leaf {
   struct mib_node_core c;
-  enum snmp_search_res (*filler)(struct snmp_proto *p, struct snmp_pdu *c);
-  //enum snmp_search_res (*filler)(struct snmp_proto_pdu *pc, struct agentx_varbind **vb);
-  int (*call_next)(struct snmp_proto *p, struct snmp_pdu *c, struct mib_walk_state *state);
+
+  /**
+   * filler - hook for filling VarBind data value
+   * @state: self referencing MIB tree walk state
+   * @data: box holding destiantion VarBind and SNMP protocol instance
+   *
+   * If corresponding leaf node has filled in AgentX type and/or size, it is
+   * guaranteed that PDU buffer have enough space. Hook mustn't be NULL.
+   * If the leaf node has set valid type, the varbind type will be automatically
+   * set by the snmp_walk_fill() servicing routine. If the field type is set to
+   * AGENTX_INVALID, it is expected that filler() hook will also fill
+   * the VarBind type.
+   */
+  enum snmp_search_res (*filler)(struct mib_walk_state *state, struct snmp_data *data);
+
+  /**
+   * call_next - signal multileaf
+   * @state: self referencing MIB tree walk state
+   * @data: box holding destination VarBind and SNMP protocol insntace
+   *
+   * MIB modules can implement subtrees by a single leaf node in MIB node tree.
+   * When the tree is walked, the specific leaf node has to be returned multiple
+   * times. The @call_next hook determines if we should move to next leaf node.
+   * It is expected that call_next() hook may change the VarBind to be filled.
+   *
+   * Hook may be NULL meaning the leaf node is not multileaf/subtree.
+   *
+   */
+  int (*call_next)(struct mib_walk_state *state, struct snmp_data *data);
+
+  /**
+   * type of produced VarBind, may be replaced in packet instanciation by
+   * AGENTX_NO_SUCH_OBJECT, AGENTX_NO_SUCH_INSTANCE or AGENTX_END_OF_MIB_VIEW
+   * The field is unspecified if equal to AGENTX_INVALID.
+   */
   enum agentx_type type;
+
+  /*
+   * Specify upper bound of VarBind data size. If set to -1, all handling must
+   * be done in filler() hook. In all other cases the filler() hook has
+   * guaranteed that the space is available.
+   */
   int size;
 };
 
@@ -70,6 +110,9 @@ mib_node_u *mib_tree_find(const struct mib_tree *tree, struct mib_walk_state *wa
 mib_node_u *mib_tree_walk_next(const struct mib_tree *t, struct mib_walk_state *walk);
 struct mib_leaf *mib_tree_walk_next_leaf(const struct mib_tree *t, struct mib_walk_state *walk);
 
+int mib_tree_hint(pool *p, struct mib_tree *t, const struct oid *oid, uint size);
+int mib_tree_walk_is_oid_descendant(const struct mib_walk_state *walk, const struct oid *oid);
+
 static inline int
 mib_node_is_leaf(const mib_node_u *node)
 {
index dcc75491b643524a35caa67e3a57a03697ea2d2d..61c54dbd8e82fba540656de385bff06916d1c2d0 100644 (file)
 #include "snmp.h"
 #include "subagent.h"
 #include "snmp_utils.h"
+#include "mib_tree.h"
 
 static void snmp_start_locked(struct object_lock *lock);
 static void snmp_sock_err(sock *sk, int err);
@@ -493,6 +494,7 @@ snmp_start(struct proto *P)
 
   p->pool = p->p.pool;
   p->lp = lp_new(p->pool);
+  p->mib_tree = mb_alloc(p->pool, sizeof(struct mib_tree));
   p->bgp_trie = f_new_trie(p->lp, 0);
 
   p->startup_timer = tm_new_init(p->pool, snmp_startup_timeout, p, 0, 0);
@@ -502,7 +504,9 @@ 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);
+
+  mib_tree_init(p->pool, p->mib_tree);
+  snmp_bgp4_start(p);
 
   return snmp_set_state(p, SNMP_INIT);
 }
@@ -561,7 +565,7 @@ snmp_reconfigure(struct proto *P, struct proto_config *CF)
   {
     /* Reinitialize the hash after snmp_shutdown() */
     HASH_INIT(p->bgp_hash, p->pool, 10);
-    snmp_bgp_start(p);
+    snmp_bgp4_start(p);
   }
 
   return config_changed;
index fde7f99502f17055faf7f017171a413390cee43f..386e23a5b5e55e73be5d37df7b918118a5d9cfe8 100644 (file)
@@ -28,7 +28,7 @@
 
 #define SNMP_RX_BUFFER_SIZE 8192
 #define SNMP_TX_BUFFER_SIZE 8192
-#define SNMP_PKT_SIZE_MAX 8192
+#define SNMP_PKT_SIZE_MAX 4098
 
 enum snmp_proto_state {
   SNMP_DOWN = 0,
@@ -41,11 +41,6 @@ enum snmp_proto_state {
   SNMP_RESET,
 };
 
-enum snmp_tags {
-  EMPTY_TAG = 0,
-  BGP4_TAG,
-};
-
 struct snmp_bond {
   node n;
   struct proto_config *config;
@@ -90,8 +85,6 @@ struct snmp_registered_oid {
   struct oid *oid;
 };
 
-struct mib_tree;      /* see mib_tree.h */
-
 struct snmp_proto {
   struct proto p;
   struct object_lock *lock;
index 43b38fed160998d8ae0384d720ae8586c52d41e2..1ac89bd9438ea7e8bb12e6ff2ce85a26f2a1070c 100644 (file)
@@ -33,7 +33,7 @@
 static int t_oid_empty(void);
 static int t_oid_compare(void);
 static int t_oid_prefixize(void);
-static int t_walk_to_oid(void);
+static int t_walk_oid_desc(void);
 static int t_tree_find(void);
 static int t_tree_traversal(void);
 static int t_tree_leafs(void);
@@ -45,6 +45,7 @@ static int t_tree_delete(void);
 #define SMALL_TESTS_NUM 10
 static int tree_sizes[] = { 0, 1, 10, 100, 1000 };
 
+/* smaller than theoretical maximum (2^32) to fit in memory */
 #define OID_MAX_ID 16
 
 #define SNMP_EXPECTED(actual, expected) \
@@ -183,8 +184,6 @@ random_oid(void)
     return random_prefixable_oid();
 }
 
-
-
 static int
 t_oid_empty(void)
 {
@@ -603,6 +602,7 @@ t_walk_to_oid(void)
   return 1;
 }
 
+
 static void
 test_both(void *buffer, uint size, const struct oid *left, const struct oid
 *right, const struct oid *expected)
@@ -735,6 +735,121 @@ generate_oids(struct oid *oids[], struct oid *sorted[], int size, struct oid *(*
   return (size > 1) ? last_used + 1 : size;
 }
 
+static int
+t_walk_oid_desc(void)
+{
+  lp_state tmps;
+  lp_save(tmp_linpool, &tmps);
+
+  pool *pool = &root_pool;
+
+  struct mib_tree storage, *tree = &storage;
+  mib_tree_init(pool, tree);
+
+  STATIC_ASSERT(ARRAY_SIZE(tree_sizes) > 0);
+  int size = tree_sizes[ARRAY_SIZE(tree_sizes) - 1];
+  ASSERT(size > 0);
+  struct oid **oids = mb_alloc(pool, size * sizeof(struct oid *));
+  struct oid **sorted = mb_alloc(pool, size * sizeof(struct oid *));
+
+  (void) generate_oids(oids, sorted, size, random_oid);
+
+  for (int i = 0; i < size; i++)
+    (void) mib_tree_add(pool, tree, oids[i], 0);
+
+  for (int test = 0; test < size; test++)
+  {
+    int i = xrandom(size);
+
+    char buffer[1024];
+    struct oid *oid = (void *) buffer;
+
+    memcpy(buffer, oids[i], snmp_oid_size(oids[i]));
+
+    struct mib_walk_state walk;
+    mib_tree_walk_init(&walk, NULL);
+    (void) mib_tree_find(tree, &walk, oid);
+
+    int type = xrandom(4);
+    switch (type)
+    {
+      case 0:
+       bt_assert(mib_tree_walk_is_oid_descendant(&walk, oids[i]) == 0);
+       break;
+
+      case 1:
+      {
+       /* oid is longer than walk or has same length */
+       u8 ids = LOAD_U8(oid->n_subid);
+       u32 upto = MIN(OID_MAX_LEN - ids, 16);
+
+       if (!upto)
+         continue;
+
+       u32 new = xrandom(upto) + 1;
+       STORE_U8(oid->n_subid, ids + new);
+       ASSERT(snmp_oid_size(oid) < 1024);
+
+       for (u32 i = 0; i < new; i++)
+         STORE_U32(oid->ids[ids + i], xrandom(OID_MAX_ID));
+
+       bt_assert(mib_tree_walk_is_oid_descendant(&walk, oid) > 0);
+
+       break;
+      }
+      case 2:
+      case 3:
+      {
+       /* oid is shorter than walk */
+       u8 ids = LOAD_U8(oid->n_subid);
+
+       if (ids == 0 || ids == OID_MAX_LEN)
+         continue;
+
+       u32 split = (ids > 1) ? xrandom(ids - 1) + 1 : 0;
+       u32 ext = (type == 3) ? xrandom(MIN(OID_MAX_LEN - ids, 16)) : 0;
+
+       STORE_U16(oid->n_subid, split + ext);
+       for (u32 i = 0; i < ext; i++)
+         STORE_U32(oid->ids[split + i], xrandom(OID_MAX_ID));
+
+       int no_change = 1;
+       for (u32 j = 0; j < MIN(ids, split + ext); j++)
+       {
+         if (LOAD_U32(oid->ids[split + j]) != LOAD_U32(oids[i]->ids[split + j]))
+           no_change = 1;
+       }
+
+       if (no_change)
+         continue;
+
+       bt_assert(mib_tree_walk_is_oid_descendant(&walk, oid) < 0);
+       break;
+      }
+    }
+  }
+
+  {
+    struct mib_walk_state walk;
+    mib_tree_walk_init(&walk, tree);
+
+    u32 zero = 0;
+    const struct oid *null_oid = (void *) &zero;
+    u32 index = xrandom(size);
+
+    bt_assert(mib_tree_walk_is_oid_descendant(&walk, null_oid) == 0);
+    bt_assert(mib_tree_walk_is_oid_descendant(&walk, oids[index]) > 0);
+    (void) mib_tree_find(tree, &walk, oids[index]);
+    bt_assert(mib_tree_walk_is_oid_descendant(&walk, null_oid) < 0);
+  }
+
+  u32 null_oid = 0;
+  mib_tree_remove(tree, (struct oid *) &null_oid);
+  lp_restore(tmp_linpool, &tmps);
+
+  return 1;
+}
+
 static void UNUSED
 print_dups(const struct oid *oids[], uint size)
 {
@@ -1686,8 +1801,8 @@ int main(int argc, char **argv)
   bt_init(argc, argv);
   bt_bird_init();
 
-  unsigned seed = rand();
-  //unsigned seed = 1000789714;
+  //unsigned seed = rand();
+  unsigned seed = 1000789714;
   log("random seed is %d", seed);
   srandom(seed);
 
@@ -1696,6 +1811,7 @@ int main(int argc, char **argv)
   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_walk_to_oid, "Function transforming MIB tree walk state to OID");
+  bt_test_suite(t_walk_oid_desc, "Function comparing MIB tree walk to OID");
 
   bt_test_suite(t_tree_find, "MIB tree search");
   bt_test_suite(t_tree_traversal, "MIB tree traversal");
index 76b866848ef16a02f283940ddb4527bd262afeb4..53f862cab9091465b81071f11d467bc1022d58a6 100644 (file)
@@ -65,6 +65,7 @@ snmp_varbind_set_name_len(struct snmp_proto *p, struct agentx_varbind **vb, u8 l
   uint diff_size = (len - LOAD_U8(oid->n_subid)) * sizeof(u32);
   if (c->size < diff_size)
   {
+    snmp_log("varbind_set_name_len small buffer");
     snmp_manage_tbuf(p, c);
     oid = &(*vb)->name;
   }
@@ -81,7 +82,10 @@ snmp_varbind_duplicate_hdr(struct snmp_proto *p, struct agentx_varbind **vb, str
   ASSUME(vb != NULL && *vb != NULL);
   uint hdr_size = snmp_varbind_header_size(*vb);
   if (c->size < hdr_size)
+  {
+    snmp_log("varbind_duplicate small buffer");
     snmp_manage_tbuf(p, c);
+  }
 
   ASSERT(c->size >= hdr_size);
   byte *buffer = c->buffer;
@@ -290,7 +294,7 @@ snmp_set_varbind_type(struct agentx_varbind *vb, enum agentx_type t)
       return SNMP_SEARCH_OK;
 
     default:
-      die("invalid varbind type");
+      die("invalid varbind type %d", (int) t);
   }
 }
 
@@ -644,11 +648,12 @@ snmp_oid_ip4_index(struct oid *o, uint start, ip4_addr addr)
 }
 
 
-/** snmp_oid_compare - find the lexicographical order relation between @left and @right
- * both @left and @right has to be non-blank.
+/**
+ * snmp_oid_compare - find the lexicographical order relation between @left and @right
  * @left: left object id relation operant
  * @right: right object id relation operant
  *
+ * both @left and @right has to be non-blank.
  * function returns 0 if left == right,
  *   -1 if left < right,
  *   and 1 otherwise
@@ -748,7 +753,7 @@ snmp_registration_create(struct snmp_proto *p, u8 mib_class)
   r->transaction_id = p->transaction_id;
   // TODO where is incremented? is this valid?
   r->packet_id = p->packet_id + 1;
-  log(L_INFO "using registration packet_id %u", r->packet_id);
+  snmp_log("using registration packet_id %u", r->packet_id);
 
   r->mib_class = mib_class;
 
@@ -760,7 +765,7 @@ snmp_registration_create(struct snmp_proto *p, u8 mib_class)
 int
 snmp_registration_match(struct snmp_registration *r, struct agentx_header *h, u8 class)
 {
-  log(L_INFO "snmp_reg_same() r->packet_id %u p->packet_id %u", r->packet_id, h->packet_id);
+  snmp_log("snmp_reg_same() r->packet_id %u p->packet_id %u", r->packet_id, h->packet_id);
   return
     (r->mib_class == class) &&
     (r->session_id == h->session_id) &&
@@ -961,7 +966,7 @@ snmp_oid_log(const struct oid *oid)
   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_log("%s", buf);
 }
 
 
@@ -1064,3 +1069,120 @@ snmp_oid_common_ancestor(const struct oid *left, const struct oid *right, struct
   STORE_U8(out->n_subid, subids);
 }
 
+/*
+ * SNMP MIB tree walking
+ */
+struct mib_leaf *
+snmp_walk_init(struct mib_tree *tree, struct mib_walk_state *walk, const struct oid *oid, struct snmp_data *data)
+{
+  mib_tree_walk_init(walk, tree);
+
+  snmp_vb_to_tx(data->p, oid, data->c);
+
+  mib_node_u *node = mib_tree_find(tree, walk, &data->c->sr_vb_start->name);
+
+  // TODO hide me in mib_tree code
+  /* mib_tree_find() returns NULL if the oid is longer than existing any path */
+  if (node == NULL && walk->stack_pos > 0)
+    node = walk->stack[walk->stack_pos - 1];
+
+  return (!node || !mib_node_is_leaf(node)) ? NULL : &node->leaf;
+}
+
+// TODO alter the varbind
+struct mib_leaf *
+snmp_walk_next(struct mib_tree *tree, struct mib_walk_state *walk, struct snmp_data *data)
+{
+  ASSUME(tree && walk);
+
+  if (!walk->stack_pos)
+    return NULL;
+
+  mib_node_u *node = walk->stack[walk->stack_pos - 1];
+
+  int found = 0;
+  struct mib_leaf *leaf = &node->leaf;
+  if (mib_node_is_leaf(node) && LOAD_U8(data->c->sr_vb_start->name.include))
+  {
+    found = 1;
+    STORE_U8(data->c->sr_vb_start->name.include, 0);
+  }
+
+  if (!found && mib_node_is_leaf(node) && leaf->call_next && !leaf->call_next(walk, data))
+    found = 1;
+
+  while (!found && (leaf = mib_tree_walk_next_leaf(tree, walk)) != NULL)
+  {
+    int old = snmp_oid_size(&data->c->sr_vb_start->name);
+    if (mib_tree_walk_to_oid(walk, &data->c->sr_vb_start->name, 20 * sizeof(u32)))
+    {
+      snmp_log("walk_next copy failed");
+      return NULL;
+    }
+
+    int new = snmp_oid_size(&data->c->sr_vb_start->name);
+    data->c->buffer += (new - old);
+
+    if (leaf->call_next && !leaf->call_next(walk, data))
+      found = 1;
+    else if (!leaf->call_next)
+      found = 1;
+  }
+
+  if (!found)
+    return NULL;
+
+
+  return leaf;
+}
+
+enum snmp_search_res
+snmp_walk_fill(struct mib_leaf *leaf, struct mib_walk_state *walk, struct snmp_data *data)
+{
+  struct agentx_varbind *vb = data->c->sr_vb_start;
+
+  if (!leaf)
+  //if (!leaf || mib_tree_walk_is_oid_descendant(walk, &vb->name) < 0)
+    return SNMP_SEARCH_NO_OBJECT;
+
+  uint size = 0;
+  if (leaf->size >= 0)
+  {
+    if (leaf->type == AGENTX_OCTET_STRING || leaf->type == AGENTX_OPAQUE ||
+         leaf->type == AGENTX_OBJECT_ID)
+    {
+      snmp_set_varbind_type(vb, leaf->type);
+      size = leaf->size;
+    }
+    else if (leaf->type != AGENTX_INVALID)
+    {
+      snmp_set_varbind_type(vb, leaf->type);
+      size = agentx_type_size(leaf->type);
+    }
+    else
+      size = leaf->size;
+  }
+
+  snmp_log("walk_fill got size %u based on lt %u ls %u, calling filler()", size, leaf->type, leaf->size);
+
+  if (size >= data->c->size)
+  {
+    snmp_log("walk_fill small buffer size %d to %d", size, data->c->size);
+    snmp_manage_tbuf(data->p, data->c);
+  }
+
+  enum snmp_search_res res = leaf->filler(walk, data);
+
+  vb = data->c->sr_vb_start;
+
+  if (res != SNMP_SEARCH_OK)
+    snmp_set_varbind_type(vb, snmp_search_res_to_type(res));
+
+  u16 type = snmp_load_varbind_type(vb);
+  /* Test that hook() did not overwrite the VarBind type to non-matching type */
+  ASSUME(type == leaf->type || type == AGENTX_END_OF_MIB_VIEW || type == AGENTX_NO_SUCH_OBJECT ||
+    type == AGENTX_NO_SUCH_INSTANCE);
+
+  return res;
+}
+
index ede6a43e6cdc5951a34c6f3f1415918682f447ba..43243c68400451e91f33340ef4800a92b1ffd4bf 100644 (file)
@@ -2,6 +2,7 @@
 #define _BIRD_SNMP_UTILS_H_
 
 #include "subagent.h"
+#include "mib_tree.h"
 
 uint snmp_pkt_len(const byte *start, const byte *end);
 
@@ -110,4 +111,11 @@ enum agentx_type snmp_search_res_to_type(enum snmp_search_res res);
 #define AGENTX_TYPE_IP4_SIZE ((uint) agentx_type_size(AGENTX_IP_ADDRESS))
 #define AGENTX_TYPE_COUNTER32_SIZE ((uint) agentx_type_size(AGENTX_COUNTER_32))
 
+/*
+ * SNMP MIB tree walking
+ */
+struct mib_leaf *snmp_walk_init(struct mib_tree *tree, struct mib_walk_state *state, const struct oid *start_rx, struct snmp_data *data);
+struct mib_leaf *snmp_walk_next(struct mib_tree *tree, struct mib_walk_state *state, struct snmp_data *data);
+enum snmp_search_res snmp_walk_fill(struct mib_leaf *leaf, struct mib_walk_state *state, struct snmp_data *data);
+
 #endif
index 21712dc09ca3f38921639afcc27fd5620f876330..b46d1ea3c7b678f71671752d1a1168ef1b4a0abb 100644 (file)
@@ -74,6 +74,7 @@ static uint update_packet_size(struct agentx_header *start, byte *end);
 
 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);
 
+/* standard SNMP internet prefix (1.3.6.1) */
 const u32 snmp_internet[] = { SNMP_ISO, SNMP_ORG, SNMP_DOD, SNMP_INTERNET };
 
 static inline int
@@ -145,7 +146,6 @@ snmp_register_ack(struct snmp_proto *p, struct agentx_response *res, u8 class)
   }
 }
 
-
 /*
  * snmp_error - handle a malformed packet
  * @p: SNMP protocol instance
@@ -158,7 +158,6 @@ static inline void
 snmp_error(struct snmp_proto *p)
 {
   snmp_reset(p);
-  //snmp_set_state(p, SNMP_RESET);
 }
 
 /*
@@ -206,7 +205,10 @@ 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_log("agentx-Open-PDU small buffer");
     snmp_manage_tbuf(p, &c);
+  }
 
   struct agentx_header *h = (void *) c.buffer;
   ADVANCE(c.buffer, c.size, AGENTX_HEADER_SIZE);
@@ -254,9 +256,9 @@ snmp_notify_pdu(struct snmp_proto *p, struct oid *oid, void *data, uint size, in
 
 // TODO use more readable anonymous structure decl.
 #define UPTIME_SIZE \
-  (6 * sizeof(u32)) /* sizeof( { u32 vb_type, u32 oid_hdr, u32 ids[4] } ) */
+  sizeof( struct { u32 vb_type; u32 oid_hdr; u32 ids[4]; } )
 #define TRAP0_HEADER_SIZE \
-  (7 * sizeof(u32)) /* sizeof( { u32 vb_type, u32 oid_hdr, u32 ids[6] } ) */
+  sizeof( struct { u32 vb_type; u32 oid_hdr; u32 ids[6]; } )
 
   uint sz = AGENTX_HEADER_SIZE + TRAP0_HEADER_SIZE + snmp_oid_size(oid) \
     + size;
@@ -266,7 +268,10 @@ 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_log("agentx-Notify-PDU small buffer");
     snmp_manage_tbuf(p, &c);
+  }
 
   struct agentx_header *h = (void *) c.buffer;
   ADVANCE(c.buffer, c.size, AGENTX_HEADER_SIZE);
@@ -361,7 +366,10 @@ 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_log("agentx-Register-PDU small buffer");
     snmp_manage_tbuf(p, &c);
+  }
 
   struct agentx_header *h = (void *) c.buffer;
   ADVANCE(c.buffer, c.size, AGENTX_HEADER_SIZE);
@@ -443,7 +451,10 @@ 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_log("agentx-Close-PDU small buffer");
     snmp_manage_tbuf(p, &c);
+  }
 
   struct agentx_header *h = (void *) c.buffer;
   ADVANCE(c.buffer, c.size, AGENTX_HEADER_SIZE);
@@ -574,7 +585,10 @@ parse_test_set_pdu(struct snmp_proto *p, byte * const pkt_start)
   snmp_pdu_context(&c, sk);
 
   if (c.size < AGENTX_HEADER_SIZE)
+  {
+    snmp_log("agentx-TestSet-PDU small buffer");
     snmp_manage_tbuf(p, &c);
+  }
 
   res = prepare_response(p, &c);
 
@@ -670,7 +684,10 @@ 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_log("parse_sets_pdu small buffer");
     snmp_manage_tbuf(p, &c);
+  }
 
   struct agentx_response *r = prepare_response(p, &c);
 
@@ -781,15 +798,17 @@ static uint
 parse_pkt(struct snmp_proto *p, byte *pkt, uint size)
 {
   /* TX-buffer free space */
-  ASSERT(snmp_is_active(p));
-  if (!space_for_response(p->sock))
-    return 0;
-
-  ASSERT(snmp_is_active(p));
   if (size < AGENTX_HEADER_SIZE)
     return 0;
 
   struct agentx_header *h = (void *) pkt;
+  if (h->flags & AGENTX_NETWORK_BYTE_ORDER)
+  {
+    TRACE(D_PACKETS, "SNMP received PDU with unexpected byte order");
+    snmp_reset(p);
+    return 0;
+  }
+
   uint pkt_size = LOAD_U32(h->payload);
 
   /* RX side checks - too big packet */
@@ -797,7 +816,7 @@ parse_pkt(struct snmp_proto *p, byte *pkt, uint size)
   {
     snmp_simple_response(p, AGENTX_RES_GEN_ERROR, 0);
     snmp_reset(p);
-    return 0; // TODO return size??
+    return 0; /* no bytes parsed */
   }
 
   /* This guarantees that we have the full packet already received */
@@ -827,24 +846,20 @@ parse_pkt(struct snmp_proto *p, byte *pkt, uint size)
     p->session_id = copy.session_id;
     p->transaction_id = copy.transaction_id;
     p->packet_id = copy.packet_id;
-    log(L_INFO "restoring packet_id %u from temporal state", p->packet_id);
+    snmp_log("restoring packet_id %u from temporal state", p->packet_id);
 
     /*
      * After unexpected state, we simply reset the session
      * only sending the agentx-Response-PDU.
      */
     snmp_reset(p);
-    return 0; // return size??
+    return 0;
   }
 
-  ASSERT(snmp_is_active(p));
   if (h->flags & AGENTX_NON_DEFAULT_CONTEXT)
   {
-    // TODO add non-default context support
-    TRACE(D_PACKETS, "SNMP received PDU with unexpected byte order");
+    TRACE(D_PACKETS, "SNMP received PDU with non-default context");
     snmp_simple_response(p, AGENTX_RES_UNSUPPORTED_CONTEXT, 0);
-    /* We always accept the packet length as correct, up to set limit */
-    // TODO limit
     return pkt_size + AGENTX_HEADER_SIZE;
   }
 
@@ -1071,7 +1086,10 @@ snmp_get_next2(struct snmp_proto *p, struct agentx_varbind **vb_search, struct o
 
   o_start = &(*vb_search)->name;
   if (c->size < snmp_varbind_hdr_size_from_oid(o_start))
+  {
+    snmp_log("get_next2 small buffer");
     snmp_manage_tbuf(p, c);
+  }
 
   snmp_set_varbind_type(*vb_search, AGENTX_END_OF_MIB_VIEW);
   return 0;
@@ -1146,7 +1164,9 @@ snmp_get_next3(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);
+  }
 
   vb = snmp_create_varbind(c->buffer, o_start);
   vb->type = AGENTX_END_OF_MIB_VIEW;
@@ -1328,7 +1348,9 @@ snmp_oid_prefixize(struct snmp_proto *p, const struct oid *oid, struct snmp_pdu
   uint oid_size = snmp_oid_size(oid);
 
   if (c->size < oid_size)
+  {
     snmp_manage_tbuf(p, c);
+  }
 
   // TODO check if the @oid is prefixable
   ASSERT(c->size >= oid_size);
@@ -1345,15 +1367,17 @@ snmp_oid_prefixize(struct snmp_proto *p, const struct oid *oid, struct snmp_pdu
  * @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.
+ * @oid. The @oid prefixed if possible. The result is stored in @c->sr_vb_start.
  */
-struct agentx_varbind *
+void
 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_log("SNMP vb_to_tx small buffer");
     snmp_manage_tbuf(p, c);
+  }
 
   ASSERT(c->size >= vb_hdr_size);
   struct agentx_varbind *vb = (void *) c->buffer;
@@ -1366,37 +1390,15 @@ snmp_vb_to_tx(struct snmp_proto *p, const struct oid *oid, struct snmp_pdu *c)
     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;
+
+    c->sr_vb_start = vb;
+    return;
   }
 
   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;
+  c->sr_vb_start = vb;
 }
 
 /*
@@ -1429,6 +1431,7 @@ response_err_ind(struct snmp_proto *p, struct agentx_response *res, enum agentx_
 {
   STORE_U32(res->error, (u16) err);
   // TODO deal with auto-incrementing of snmp_pdu context c.ind
+  // FIXME for packets with errors reset reset payload size to null (by move c.buffer appropriately)
   if (err != AGENTX_RES_NO_ERROR && err != AGENTX_RES_GEN_ERROR)
   {
     TRACE(D_PACKETS, "Last PDU resulted in error %u", err);
@@ -1439,7 +1442,7 @@ response_err_ind(struct snmp_proto *p, struct agentx_response *res, enum agentx_
   }
   else if (err == AGENTX_RES_GEN_ERROR)
   {
-    TRACE(D_PACKETS, "Last PDU resulted in error %u", err);
+    TRACE(D_PACKETS, "Last PDU resulted in error %u genErr", err);
     STORE_U32(res->index, 0);
     TRACE(D_PACKETS, "Storing packet size %u (was %u)", sizeof(struct agentx_response) - AGENTX_HEADER_SIZE, LOAD_U32(res->h.payload));
     STORE_U32(res->h.payload,
@@ -1461,59 +1464,45 @@ parse_gets_error(struct snmp_proto *p, struct snmp_pdu *c, uint len)
   return len + AGENTX_HEADER_SIZE;
 }
 
-static enum snmp_search_res
-snmp_mib_fill2(struct snmp_proto *p, struct snmp_pdu *c, mib_node_u *mib_node)
+/*
+ * AgentX GetPDU, GetNextPDU and GetBulkPDU
+ */
+void
+snmp_get_pdu(struct snmp_proto *p, struct snmp_pdu *c, const struct oid *o_start, struct mib_walk_state *walk)
 {
-  if (!mib_node || !mib_node_is_leaf(mib_node))
-  {
-    snmp_set_varbind_type(c->sr_vb_start, AGENTX_NO_SUCH_OBJECT);
-    ADVANCE(c->buffer, c->size, snmp_varbind_header_size(c->sr_vb_start));
-    return AGENTX_NO_SUCH_OBJECT;
-  }
+  snmp_log("snmp_get_pdu()");
+  struct snmp_data d = {
+    .p = p,
+    .c = c,
+  };
 
-  struct mib_leaf *leaf = &mib_node->leaf;
+  struct mib_leaf *leaf;
+  leaf = snmp_walk_init(p->mib_tree, walk, o_start, &d);
 
-  return leaf->filler(p, c);
-}
+  snmp_log("found node %p", leaf);
 
-void
-snmp_get_pdu(struct snmp_proto *p, struct snmp_pdu *c, struct mib_walk_state *walk)
-{
-  mib_node_u *node;
-  node = mib_tree_find(p->mib_tree, walk, &c->sr_vb_start->name);
+  enum snmp_search_res res;
+  res = snmp_walk_fill(leaf, walk, &d);
+
+  snmp_log("fill result %u", res);
 
-  (void) snmp_mib_fill2(p, c, node);
+  if (res != SNMP_SEARCH_OK)
+    snmp_set_varbind_type(c->sr_vb_start, snmp_search_res_to_type(res));
 }
 
 int
-snmp_get_next_pdu(struct snmp_proto *p, struct snmp_pdu *c, struct mib_walk_state *walk)
+snmp_get_next_pdu(struct snmp_proto *p, struct snmp_pdu *c, const struct oid *o_start, struct mib_walk_state *walk)
 {
-  mib_node_u *node;
-  node = mib_tree_find(p->mib_tree, walk, &c->sr_vb_start->name);
-
-  int inclusive = c->sr_vb_start->name.include;
-
-  int move_next;
-  if (!node && inclusive)
-    move_next = 1;
-  else if (!node && !inclusive)
-    move_next = 1;
-  else if (node && inclusive && mib_node_is_leaf(node))
-    move_next = 0;
-  else if (node && inclusive)
-    move_next = 1;
-  else if (node && !inclusive)
-    move_next = 0;
-
-  struct mib_leaf *leaf = &node->leaf;
-  if (move_next && node && mib_node_is_leaf(node))
-    move_next = leaf->call_next(p, c, walk);
-
-  if (move_next)
-    node = (mib_node_u *) mib_tree_walk_next_leaf(p->mib_tree, walk);
+  struct snmp_data d = {
+    .p = p,
+    .c = c,
+  };
+
+  snmp_walk_init(p->mib_tree, walk, o_start, &d);
+  struct mib_leaf *leaf = snmp_walk_next(p->mib_tree, walk, &d);
 
   enum snmp_search_res res;
-  res = snmp_mib_fill2(p, c, node);
+  res = snmp_walk_fill(leaf, walk, &d);
 
   if (res != SNMP_SEARCH_OK)
     snmp_set_varbind_type(c->sr_vb_start, AGENTX_END_OF_MIB_VIEW);
@@ -1522,14 +1511,64 @@ snmp_get_next_pdu(struct snmp_proto *p, struct snmp_pdu *c, struct mib_walk_stat
 }
 
 void
-snmp_get_bulk_pdu(struct snmp_proto *p, struct snmp_pdu *c, struct mib_walk_state *walk, struct agentx_bulk_state *bulk)
+snmp_get_bulk_pdu(struct snmp_proto *p, struct snmp_pdu *c, const struct oid *o_start, struct mib_walk_state *walk, struct agentx_bulk_state *bulk)
 {
   if (c->index >= bulk->getbulk.non_repeaters)
     bulk->repeaters++;
 
   // store the o_start and o_end
 
-  bulk->has_any |= snmp_get_next_pdu(p, c, walk);
+  bulk->has_any |= snmp_get_next_pdu(p, c, o_start, walk);
+}
+
+static inline const struct oid *
+snmp_load_oids(byte **pkt_ptr, uint *pkt_sz, struct snmp_pdu *c)
+{
+  byte *pkt = *pkt_ptr;
+  uint pkt_size = *pkt_sz;
+
+  uint sz;
+  const struct oid *start = (const struct oid *) pkt;
+
+  if ((sz = snmp_oid_size(start)) > pkt_size)
+  {
+    snmp_log("load_oids start %u / %u", sz, pkt_size);
+    c->error = AGENTX_RES_PARSE_ERROR;
+    *pkt_ptr = pkt;
+    *pkt_sz = pkt_size;
+    return NULL;
+  }
+
+  ADVANCE(pkt, pkt_size, sz);
+
+  const struct oid *end = (const struct oid *) pkt;
+  if ((sz = snmp_oid_size(end)) > pkt_size)
+  {
+    snmp_log("load_oids end %u / %u", sz, pkt_size);
+    c->error = AGENTX_RES_PARSE_ERROR;
+    *pkt_ptr = pkt;
+    *pkt_sz = pkt_size;
+    return NULL;
+  }
+
+  ADVANCE(pkt, pkt_size, sz);
+
+  if (!snmp_is_oid_empty(end) &&
+      snmp_oid_compare(start, end) > 0)
+  {
+    c->error = AGENTX_RES_GEN_ERROR;
+    *pkt_ptr = pkt;
+    *pkt_sz = pkt_size;
+    return NULL;
+  }
+
+  ASSERT(start != NULL);
+  ASSERT(end != NULL);
+
+  c->sr_o_end = end;
+  *pkt_ptr = pkt;
+  *pkt_sz = pkt_size;
+  return start;
 }
 
 /*
@@ -1544,6 +1583,7 @@ snmp_get_bulk_pdu(struct snmp_proto *p, struct snmp_pdu *c, struct mib_walk_stat
 static uint
 parse_gets_pdu(struct snmp_proto *p, byte * const pkt_start)
 {
+  snmp_log("parse_gets_pdu msg");
   // TODO checks for c.size underflow
   struct mib_walk_state walk;
   byte *pkt = pkt_start;
@@ -1560,11 +1600,12 @@ parse_gets_pdu(struct snmp_proto *p, byte * const pkt_start)
    * Get-Bulk processing stops if all the varbind have type END_OF_MIB_VIEW
    * has_any is true if some varbind has type other than END_OF_MIB_VIEW
    */
-  struct agentx_bulk_state bulk_state = { };
+  struct agentx_bulk_state bulk_state = { };
   if (h->type == AGENTX_GET_BULK_PDU)
   {
     if (pkt_size < sizeof(struct agentx_getbulk))
     {
+      snmp_log("parse_gets GetBulkPDU prepare");
       c.error = AGENTX_RES_PARSE_ERROR;
       c.index = 0;
       return parse_gets_error(p, &c, pkt_size);
@@ -1594,62 +1635,27 @@ parse_gets_pdu(struct snmp_proto *p, byte * const pkt_start)
   {
     lp_restore(tmp_linpool, &tmps);
 
-    /* We load search range start OID */
-    const struct oid *o_start_rx = (void *) pkt;
-    uint sz;
-    if ((sz = snmp_oid_size(o_start_rx)) > pkt_size)
-    {
-      c.error = AGENTX_RES_PARSE_ERROR;
-      return parse_gets_error(p, &c, pkt_size);
-    }
-
-    /* Update buffer pointer and remaining size counters. */
-    ADVANCE(pkt, pkt_size, sz);
-
-    /*
-     * We load search range end OID
-     * The exactly same process of sanity checking is preformed while loading
-     * the SearchRange's end OID
-     */
-    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);
-    }
-
-    ADVANCE(pkt, pkt_size, sz);
-
-    /* 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 */
-    c.sr_vb_start = snmp_vb_to_tx(p, o_start_rx, &c);
-    c.sr_o_end = snmp_oid_to_scratch(o_end_rx);
-
-    ASSERT(c.sr_vb_start); // TODO implement failed parsing logic
-    ASSERT(c.sr_o_end);
-
-    if (!snmp_is_oid_empty(c.sr_o_end) &&
-       snmp_oid_compare(&c.sr_vb_start->name, c.sr_o_end) > 0)
+    const struct oid *start_rx;
+    if (!(start_rx = snmp_load_oids(&pkt, &pkt_size, &c)))
     {
-      c.error = AGENTX_RES_GEN_ERROR;
+      snmp_log("snmp_load_oid ends with an error");
       return parse_gets_error(p, &c, pkt_size);
     }
 
     switch (h->type)
     {
       case AGENTX_GET_PDU:
-       snmp_get_pdu(p, &c, &walk);
+       snmp_get_pdu(p, &c, start_rx, &walk);
        //snmp_mib_fill(p, &vb_start, &c);
        break;
 
       case AGENTX_GET_NEXT_PDU:
-       snmp_get_next_pdu(p, &c, &walk);
+       snmp_get_next_pdu(p, &c, start_rx, &walk);
        //snmp_get_next2(p, &vb_start, o_end, &c);
        break;
 
       case AGENTX_GET_BULK_PDU:
-       snmp_get_bulk_pdu(p, &c, &walk, &bulk_state);
+       snmp_get_bulk_pdu(p, &c, start_rx, &walk, &bulk_state);
        #if 0
        if (c.index >= bulk_state.getbulk.non_repeaters)
          bulk_state.repeaters++;
@@ -1663,7 +1669,7 @@ parse_gets_pdu(struct snmp_proto *p, byte * const pkt_start)
        break;
 
       default:
-       die("incorrect usage");
+       die("implementation failure");
     }
 
     c.sr_vb_start = NULL;
@@ -1744,7 +1750,7 @@ snmp_stop_subagent(struct snmp_proto *p)
 int
 snmp_rx(sock *sk, uint size)
 {
-  log(L_INFO "snmp_rx with size %u", size);
+  snmp_log("snmp_rx with size %u", size);
   struct snmp_proto *p = sk->data;
   byte *pkt_start = sk->rbuf;
   byte *end = pkt_start + size;
@@ -1780,7 +1786,7 @@ snmp_rx(sock *sk, uint size)
 void
 snmp_tx(sock *sk)
 {
-  log(L_INFO "snmp_tx()");
+  snmp_log("snmp_tx()");
   /* We still not have enough space */
   if (!space_for_response(sk))
     return;
@@ -1819,7 +1825,7 @@ snmp_ping(struct snmp_proto *p)
   ADVANCE(c.buffer, c.size, AGENTX_HEADER_SIZE);
   snmp_blank_header(h, AGENTX_PING_PDU);
   p->packet_id++;
-  log(L_INFO "incrementing packet_id to %u (ping)", p->packet_id);
+  snmp_log("incrementing packet_id to %u (ping)", p->packet_id);
   snmp_session(p, h);
 
   /* sending only header */
@@ -2099,7 +2105,7 @@ snmp_manage_tbuf(struct snmp_proto *p, struct snmp_pdu *c)
   if (c->sr_vb_start != NULL)
     diff = (void *) c->sr_vb_start - (void *) sk->tbuf;
 
-  log(L_INFO "snmp_manage_tbuf2()");
+  snmp_log("snmp_manage_tbuf2()");
   sk_set_tbsize(sk, sk->tbsize + 2048);
   c->size += 2048;
 
@@ -2123,7 +2129,7 @@ snmp_manage_tbuf2(struct snmp_proto *p, void **ptr, struct snmp_pdu *c)
   if (ptr)
     diff = *ptr - (void *) sk->tbuf;
 
-  log(L_INFO "snmp_manage_tbuf()");
+  snmp_log("snmp_manage_tbuf()");
   sk_set_tbsize(sk, sk->tbsize + 2048);
   c->size += 2048;
 
index 93db65f29ec1a6aa155809bb82c7baa51a1750ff..138c43f7d06472cd4c3448f12ecfd3d71728f9d5 100644 (file)
@@ -1,3 +1,4 @@
+
 #ifndef _BIRD_SNMP_SUBAGENT_H_
 #define _BIRD_SNMP_SUBAGENT_H_
 
@@ -51,7 +52,7 @@ enum agentx_type {
   AGENTX_NO_SUCH_INSTANCE   = 129,
   AGENTX_END_OF_MIB_VIEW    = 130,
 
-  AGENTX_INVALID           =  -1,
+  AGENTX_INVALID           =   0,
 } PACKED;
 
 enum snmp_search_res {
@@ -169,6 +170,15 @@ struct oid {
   u32 ids[];
 };
 
+#define STATIC_OID(sbids)                                                    \
+  struct {                                                                   \
+    u8 n_subid;                                                                      \
+    u8 prefix;                                                               \
+    u8 include;                                                                      \
+    u8 reserved;                                                             \
+    u32 ids[sbids];                                                          \
+  }
+
 /* enforced by MIB tree, see mib_tree.h for more info */
 #define OID_MAX_LEN 32
 
@@ -301,21 +311,22 @@ struct snmp_pdu {
 
   /* Search Range */
   struct agentx_varbind *sr_vb_start; /* search range starting OID inside TX buffer (final storage) */
-  struct oid *sr_o_end;                    /* search range ending OID */
+  const struct oid *sr_o_end;        /* search range ending OID */
 
   /* Control */
   enum agentx_response_errs error;  /* storage for result of current action */
   u32 index;                       /* index on which the error was found */
 
-  union snmp_mibs_data *mibs_data;  /* data passed from MIB search phase to MIB fill phase */
 };
 
-#include "bgp4_mib.h"
-
-union snmp_mibs_data {
-  enum snmp_tags empty;
-
-  struct bgp4_mib bgp4;
+/*
+ * snmp_data - Comprehensive hadle for Agentx PDU state
+ * @p: SNMP protocol instance
+ * @c: contextual data for currrently constructed AgentX PDU
+ */
+struct snmp_data {
+  struct snmp_proto *p;
+  struct snmp_pdu *c;
 };
 
 struct snmp_packet_info {
@@ -346,16 +357,17 @@ void snmp_notify_pdu(struct snmp_proto *p, struct oid *oid, void *data, uint siz
 
 void snmp_manage_tbuf(struct snmp_proto *p, struct snmp_pdu *c);
 
-struct agentx_varbind *snmp_vb_to_tx(struct snmp_proto *p, const struct oid *oid, struct snmp_pdu *c);
+void 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);
-void snmp_bgp_start(struct snmp_proto *p);
 
+/* MIB modules */
+void snmp_bgp4_start(struct snmp_proto *p);
 
-// debug wrapper
-#if 0
-#define snmp_log(...) log(L_INFO "snmp " __VA_ARGS__)
+
+#if 1
+#define snmp_log(...) log(L_INFO "SNMP " __VA_ARGS__)
 #else
 #define snmp_log(...) do { } while(0)
 #endif