]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
SNMP: Refactor -- order + cleanup
authorVojtech Vilimek <vojtech.vilimek@nic.cz>
Thu, 15 Aug 2024 13:52:15 +0000 (15:52 +0200)
committerVojtech Vilimek <vojtech.vilimek@nic.cz>
Thu, 15 Aug 2024 14:25:48 +0000 (16:25 +0200)
The C source files are read from bottom up.

proto/snmp/bgp4_mib.c
proto/snmp/snmp.c
proto/snmp/snmp.h
proto/snmp/snmp_utils.c
proto/snmp/snmp_utils.h
proto/snmp/subagent.c
proto/snmp/subagent.h

index 5f4c703df35a5ef834902e4a6a8149f403dda411..fabe7cfba888eadb5e9e5443503c3eed702ea8ba 100644 (file)
@@ -67,6 +67,32 @@ snmp_bgp_reg_failed(struct snmp_proto *p, const struct agentx_response *res, str
   snmp_reset(p);
 }
 
+/*
+ * snmp_bgp_fsm_state - extract BGP FSM state for SNMP BGP4-MIB
+ * @bgp_proto: BGP instance
+ *
+ * Return FSM state in BGP4-MIB encoding
+ */
+static inline uint
+snmp_bgp_fsm_state(const struct bgp_proto *bgp_proto)
+{
+  const struct bgp_conn *bgp_conn = bgp_proto->conn;
+  const struct bgp_conn *bgp_in = &bgp_proto->incoming_conn;
+  const struct bgp_conn *bgp_out = &bgp_proto->outgoing_conn;
+
+  if (bgp_conn)
+    return bgp_conn->state + 1;
+
+  if (MAX(bgp_in->state, bgp_out->state) == BS_CLOSE &&
+      MIN(bgp_in->state, bgp_out->state) != BS_CLOSE)
+    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;
+}
+
+
 /*
  * snmp_bgp_notify_common - common functionaly for BGP4-MIB notifications
  * @p: SNMP protocol instance
@@ -173,31 +199,6 @@ snmp_bgp_notify_common(struct snmp_proto *p, uint type, ip4_addr ip4, char last_
   #undef OID_N_SUBID
 }
 
-/*
- * snmp_bgp_fsm_state - extract BGP FSM state for SNMP BGP4-MIB
- * @bgp_proto: BGP instance
- *
- * Return FSM state in BGP4-MIB encoding
- */
-static inline uint
-snmp_bgp_fsm_state(const struct bgp_proto *bgp_proto)
-{
-  const struct bgp_conn *bgp_conn = bgp_proto->conn;
-  const struct bgp_conn *bgp_in = &bgp_proto->incoming_conn;
-  const struct bgp_conn *bgp_out = &bgp_proto->outgoing_conn;
-
-  if (bgp_conn)
-    return bgp_conn->state + 1;
-
-  if (MAX(bgp_in->state, bgp_out->state) == BS_CLOSE &&
-      MIN(bgp_in->state, bgp_out->state) != BS_CLOSE)
-    return MIN(bgp_in->state, bgp_out->state) + 1;
-  if (MIN(bgp_in->state, bgp_out->state) == BS_CLOSE)
-    return BS_IDLE;
-
-  return MAX(bgp_in->state, bgp_out->state) + 1;
-}
-
 static void
 snmp_bgp_notify_wrapper(struct snmp_proto *p, struct bgp_proto *bgp, uint type)
 {
@@ -209,38 +210,18 @@ snmp_bgp_notify_wrapper(struct snmp_proto *p, struct bgp_proto *bgp, uint type)
   snmp_bgp_notify_common(p, type, ip4, last_error, state_val);
 }
 
-void
+void UNUSED
 snmp_bgp_notify_established(struct snmp_proto *p, struct bgp_proto *bgp)
 {
   snmp_bgp_notify_wrapper(p, bgp, BGP4_MIB_ESTABLISHED_NOTIFICATION);
 }
 
-void
+void UNUSED
 snmp_bgp_notify_backward_trans(struct snmp_proto *p, struct bgp_proto *bgp)
 {
   snmp_bgp_notify_wrapper(p, bgp, BGP4_MIB_BACKWARD_TRANS_NOTIFICATION);
 }
 
-void
-snmp_bgp4_register(struct snmp_proto *p)
-{
-  /* Register the whole BGP4-MIB::bgp root tree node */
-  struct snmp_registration *reg;
-  reg = snmp_registration_create(p, BGP4_MIB_ID);
-
-  struct oid *oid = mb_allocz(p->pool, sizeof(bgp4_mib_oid));
-  memcpy(oid, &bgp4_mib_oid, sizeof(bgp4_mib_oid));
-
-  reg->reg_hook_ok = NULL;
-  reg->reg_hook_fail = snmp_bgp_reg_failed;
-
-  /*
-   * We set both upper bound and index to zero, therefore only single OID
-   * is being registered.
-   */
-  snmp_register(p, oid, 0, 0, SNMP_REGISTER_TREE);
-}
-
 static int
 snmp_bgp_valid_ip4(const struct oid *o)
 {
@@ -269,48 +250,6 @@ ip4_to_oid(struct oid *o, ip4_addr addr)
   o->ids[8] = (tmp & 0x000000FF) >>  0;
 }
 
-static void UNUSED
-print_bgp_record(const struct bgp_proto *bgp_proto)
-{
-  struct bgp_conn *conn = bgp_proto->conn;
-
-  DBG("    name: %s", cf->name);
-  DBG(".");
-  DBG("    rem. identifier: %u", bgp_proto->remote_id);
-  DBG("    local ip: %I", config->local_ip);
-  DBG("    remote ip: %I", config->remote_ip);
-  DBG("    local port: %u", config->local_port);
-  DBG("    remote port: %u", config->remote_port);
-
-  if (conn) {
-    DBG("    state: %u", conn->state);
-    DBG("    remote as: %u", conn->remote_caps->as4_number);
-  }
-
-  DBG("    in updates: %u", bgp_proto->stats.rx_updates);
-  DBG("    out updates: %u", bgp_proto->stats.tx_updates);
-  DBG("    in total: %u", bgp_proto->stats.rx_messages);
-  DBG("    out total: %u", bgp_proto->stats.tx_messages);
-  DBG("    fsm transitions: %u",
-      bgp_proto->stats.fsm_established_transitions);
-
-  DBG("    fsm total time: -- (0)");   // not supported by bird
-  DBG("    retry interval: %u", config->connect_retry_time);
-
-  DBG("    hold configurated: %u", config->hold_time );
-  DBG("    keep alive config: %u", config->keepalive_time );
-
-  DBG("    min AS origin. int.: -- (0)");      // not supported by bird
-  DBG("    min route advertisement: %u", 0 );
-  DBG("    in update elapsed time: %u", 0 );
-
-  if (!conn)
-    DBG("  no connection established");
-
-  DBG("  outgoinin_conn state %u", bgp_proto->outgoing_conn.state + 1);
-  DBG("  incoming_conn state: %u", bgp_proto->incoming_conn.state + 1);
-}
-
 static inline enum snmp_search_res
 populate_bgp4(struct snmp_pdu *c, ip4_addr *addr, const struct bgp_proto **proto, const struct bgp_conn
 **conn, const struct bgp_stats **stats, const struct bgp_config **config)
@@ -832,6 +771,27 @@ snmp_bgp4_show_info(struct snmp_proto *p)
   HASH_WALK_END;
 }
 
+void
+snmp_bgp4_register(struct snmp_proto *p)
+{
+  /* Register the whole BGP4-MIB::bgp root tree node */
+  struct snmp_registration *reg;
+  reg = snmp_registration_create(p, BGP4_MIB_ID);
+
+  struct oid *oid = mb_allocz(p->pool, sizeof(bgp4_mib_oid));
+  memcpy(oid, &bgp4_mib_oid, sizeof(bgp4_mib_oid));
+
+  reg->reg_hook_ok = NULL;
+  reg->reg_hook_fail = snmp_bgp_reg_failed;
+
+  /*
+   * We set both upper bound and index to zero, therefore only single OID
+   * is being registered.
+   */
+  snmp_register(p, oid, 0, 0, SNMP_REGISTER_TREE);
+}
+
+
 /*
  * snmp_bgp4_start - prepare BGP4-MIB
  * @p: SNMP protocol instance holding memory pool
index fbc2cf0b448fb5dd55f8253ade07db4793d0c19e..eb01fabced3569e7ec6307dfdc63ab6953b03d82 100644 (file)
 #include "bgp4_mib.h"
 
 const char agentx_master_addr[] = AGENTX_MASTER_ADDR;
-const struct oid *agentx_available_mibs[AGENTX_MIB_COUNT + 1] = { 0 };
-
-static void snmp_start_locked(struct object_lock *lock);
-static void snmp_sock_err(sock *sk, int err);
-static void snmp_stop_timeout(timer *tm);
-static void snmp_cleanup(struct snmp_proto *p);
 
 static const char *snmp_state_str[] = {
   [SNMP_INIT]    = "acquiring address lock",
@@ -129,6 +123,109 @@ static const char *snmp_state_str[] = {
   [SNMP_DOWN]    = "protocol down",
 };
 
+
+/*
+ *    Callbacks
+ */
+
+/*
+ * snmp_sock_err - handle errors on socket by reopenning the socket
+ * @sk: socket owned by SNMP protocol instance
+ * @err: socket error code
+ */
+static void
+snmp_sock_err(sock *sk, int UNUSED err)
+{
+  struct snmp_proto *p = sk->data;
+  if (err != 0)
+    TRACE(D_EVENTS, "SNMP socket error (%d)", err);
+  snmp_set_state(p, SNMP_DOWN);
+}
+
+/*
+ * snmp_ping_timeout - send a agentx-Ping-PDU
+ * @tm: the ping_timer holding the SNMP protocol instance.
+ *
+ * Send an agentx-Ping-PDU. This function is periodically called by ping
+ * timer.
+ */
+static void
+snmp_ping_timeout(timer *tm)
+{
+  struct snmp_proto *p = tm->data;
+  snmp_ping(p);
+}
+
+
+/*
+ * snmp_stop_timeout - a timeout for non-responding master agent
+ * @tm: the startup_timer holding the SNMP protocol instance.
+ *
+ * We are trying to empty the TX buffer of communication socket. But if it is
+ * not done in reasonable amount of time, the function is called by timeout
+ * timer. We down the whole SNMP protocol with cleanup of associated data
+ * structures.
+ */
+static void
+snmp_stop_timeout(timer *tm)
+{
+  struct snmp_proto *p = tm->data;
+  snmp_set_state(p, SNMP_DOWN);
+}
+
+/*
+ * snmp_connected - start AgentX session on created socket
+ * @sk: socket owned by SNMP protocol instance
+ *
+ * Starts the AgentX communication by sending an agentx-Open-PDU.
+ * This function is internal and shouldn't be used outside the SNMP module.
+ */
+void
+snmp_connected(sock *sk)
+{
+  struct snmp_proto *p = sk->data;
+  snmp_set_state(p, SNMP_OPEN);
+}
+
+/*
+ * snmp_start_locked - open the socket on locked address
+ * @lock: object lock guarding the communication mean (address, ...)
+ *
+ * This function is called when the object lock is acquired. Main goal is to set
+ * socket parameters and try to open configured socket. Function
+ * snmp_connected() handles next stage of SNMP protocol start. When the socket
+ * coundn't be opened, a new try is scheduled after a small delay.
+ */
+static void
+snmp_start_locked(struct object_lock *lock)
+{
+  struct snmp_proto *p = lock->data;
+  if (p->startup_delay)
+  {
+    ASSERT(p->startup_timer);
+    p->startup_timer->hook = snmp_startup_timeout;
+    tm_start(p->startup_timer, p->startup_delay);
+  }
+  else
+    snmp_set_state(p, SNMP_LOCKED);
+}
+
+/*
+ * snmp_startup_timeout - start the initiliazed SNMP protocol
+ * @tm: the startup_timer holding the SNMP protocol instance.
+ *
+ * When the timer rings, the function snmp_startup() is invoked.
+ * This function is internal and shouldn't be used outside the SNMP module.
+ * Used when we delaying the start procedure, or we want to retry opening
+ * the communication socket.
+ */
+void
+snmp_startup_timeout(timer *tm)
+{
+  struct snmp_proto *p = tm->data;
+  snmp_set_state(p, SNMP_LOCKED);
+}
+
 /*
  * snmp_rx_skip - skip all received data
  * @sk: communication socket
@@ -158,6 +255,51 @@ snmp_tx_skip(sock *sk)
   snmp_set_state(p, SNMP_STOP);
 }
 
+/*
+ * snmp_cleanup - free all resources allocated by SNMP protocol
+ * @p: SNMP protocol instance
+ *
+ * This function forcefully stops and cleans all resources and memory acqiured
+ * by given SNMP protocol instance, such as timers, lists, hash tables etc.
+ */
+static inline void
+snmp_cleanup(struct snmp_proto *p)
+{
+  /* Function tm_stop() is called inside rfree() */
+  rfree(p->startup_timer);
+  p->startup_timer = NULL;
+
+  rfree(p->ping_timer);
+  p->ping_timer = NULL;
+
+  rfree(p->sock);
+  p->sock = NULL;
+
+  rfree(p->lock);
+  p->lock = NULL;
+
+  struct snmp_registration *r, *r2;
+  WALK_LIST_DELSAFE(r, r2, p->registration_queue)
+  {
+    rem_node(&r->n);
+    mb_free(r);
+    r = NULL;
+  }
+
+  HASH_FREE(p->bgp_hash);
+  rfree(p->lp);
+  p->lp = NULL;
+  /* bgp_trie is allocated exclusively from linpool lp */
+  p->bgp_trie = NULL;
+
+  struct mib_walk_state *walk = tmp_alloc(sizeof(struct mib_walk_state));
+  mib_tree_walk_init(walk, p->mib_tree);
+  (void) mib_tree_delete(p->mib_tree, walk);
+  p->mib_tree = NULL;
+
+  p->state = SNMP_DOWN;
+}
+
 /*
  * snmp_set_state - change state with associated actions
  * @p: SNMP protocol instance
@@ -303,84 +445,6 @@ snmp_set_state(struct snmp_proto *p, enum snmp_proto_state state)
   }
 }
 
-/*
- * snmp_init - preinitialize SNMP instance
- * @CF: SNMP configuration generic handle
- *
- * Returns a generic handle pointing to preinitialized SNMP procotol
- * instance.
- */
-static struct proto *
-snmp_init(struct proto_config *CF)
-{
-  struct proto *P = proto_new(CF);
-  struct snmp_proto *p = SKIP_BACK(struct snmp_proto, p, P);
-
-  p->rl_gen = (struct tbf) TBF_DEFAULT_LOG_LIMITS;
-  p->state = SNMP_DOWN;
-
-  return P;
-}
-
-/*
- * snmp_cleanup - free all resources allocated by SNMP protocol
- * @p: SNMP protocol instance
- *
- * This function forcefully stops and cleans all resources and memory acqiured
- * by given SNMP protocol instance, such as timers, lists, hash tables etc.
- */
-static inline void
-snmp_cleanup(struct snmp_proto *p)
-{
-  /* Function tm_stop() is called inside rfree() */
-  rfree(p->startup_timer);
-  p->startup_timer = NULL;
-
-  rfree(p->ping_timer);
-  p->ping_timer = NULL;
-
-  rfree(p->sock);
-  p->sock = NULL;
-
-  rfree(p->lock);
-  p->lock = NULL;
-
-  struct snmp_registration *r, *r2;
-  WALK_LIST_DELSAFE(r, r2, p->registration_queue)
-  {
-    rem_node(&r->n);
-    mb_free(r);
-    r = NULL;
-  }
-
-  HASH_FREE(p->bgp_hash);
-  rfree(p->lp);
-  p->lp = NULL;
-  /* bgp_trie is allocated exclusively from linpool lp */
-  p->bgp_trie = NULL;
-
-  struct mib_walk_state *walk = tmp_alloc(sizeof(struct mib_walk_state));
-  mib_tree_walk_init(walk, p->mib_tree);
-  (void) mib_tree_delete(p->mib_tree, walk);
-  p->mib_tree = NULL;
-
-  p->state = SNMP_DOWN;
-}
-
-/*
- * snmp_connected - start AgentX session on created socket
- * @sk: socket owned by SNMP protocol instance
- *
- * Starts the AgentX communication by sending an agentx-Open-PDU.
- * This function is internal and shouldn't be used outside the SNMP module.
- */
-void
-snmp_connected(sock *sk)
-{
-  struct snmp_proto *p = sk->data;
-  snmp_set_state(p, SNMP_OPEN);
-}
-
 /*
  * snmp_reset - reset AgentX session
  * @p: SNMP protocol instance
@@ -409,129 +473,31 @@ snmp_up(struct snmp_proto *p)
 }
 
 /*
- * snmp_sock_err - handle errors on socket by reopenning the socket
- * @sk: socket owned by SNMP protocol instance
- * @err: socket error code
- */
-static void
-snmp_sock_err(sock *sk, int UNUSED err)
-{
-  struct snmp_proto *p = sk->data;
-  if (err != 0)
-    TRACE(D_EVENTS, "SNMP socket error (%d)", err);
-  snmp_set_state(p, SNMP_DOWN);
-}
-
-/*
- * snmp_start_locked - open the socket on locked address
- * @lock: object lock guarding the communication mean (address, ...)
- *
- * This function is called when the object lock is acquired. Main goal is to set
- * socket parameters and try to open configured socket. Function
- * snmp_connected() handles next stage of SNMP protocol start. When the socket
- * coundn't be opened, a new try is scheduled after a small delay.
- */
-static void
-snmp_start_locked(struct object_lock *lock)
-{
-  struct snmp_proto *p = lock->data;
-  if (p->startup_delay)
-  {
-    ASSERT(p->startup_timer);
-    p->startup_timer->hook = snmp_startup_timeout;
-    tm_start(p->startup_timer, p->startup_delay);
-  }
-  else
-    snmp_set_state(p, SNMP_LOCKED);
-}
-
-/*
- * snmp_startup_timeout - start the initiliazed SNMP protocol
- * @tm: the startup_timer holding the SNMP protocol instance.
- *
- * When the timer rings, the function snmp_startup() is invoked.
- * This function is internal and shouldn't be used outside the SNMP module.
- * Used when we delaying the start procedure, or we want to retry opening
- * the communication socket.
- */
-void
-snmp_startup_timeout(timer *tm)
-{
-  struct snmp_proto *p = tm->data;
-  snmp_set_state(p, SNMP_LOCKED);
-}
-
-/*
- * snmp_stop_timeout - a timeout for non-responding master agent
- * @tm: the startup_timer holding the SNMP protocol instance.
- *
- * We are trying to empty the TX buffer of communication socket. But if it is
- * not done in reasonable amount of time, the function is called by timeout
- * timer. We down the whole SNMP protocol with cleanup of associated data
- * structures.
- */
-static void
-snmp_stop_timeout(timer *tm)
-{
-  struct snmp_proto *p = tm->data;
-  snmp_set_state(p, SNMP_DOWN);
-}
-
-/*
- * snmp_ping_timeout - send a agentx-Ping-PDU
- * @tm: the ping_timer holding the SNMP protocol instance.
+ * snmp_shutdown - Forcefully stop the SNMP protocol instance
+ * @P: SNMP protocol generic handle
  *
- * Send an agentx-Ping-PDU. This function is periodically called by ping
- * timer.
+ * Simple cast-like wrapper around snmp_reset(), see more info there.
  */
-static void
-snmp_ping_timeout(timer *tm)
+static int
+snmp_shutdown(struct proto *P)
 {
-  struct snmp_proto *p = tm->data;
-  snmp_ping(p);
+  struct snmp_proto *p = SKIP_BACK(struct snmp_proto, p, P);
+  return snmp_reset(p);
 }
 
 /*
- * snmp_start - Initialize the SNMP protocol instance
+ * snmp_show_proto_info - print basic information about SNMP protocol instance
  * @P: SNMP protocol generic handle
- *
- * The first step in AgentX subagent startup is protocol initialition.
- * We must prepare lists, find BGP peers and finally asynchronously start
- * a AgentX subagent session.
  */
-static int
-snmp_start(struct proto *P)
+static void
+snmp_show_proto_info(struct proto *P)
 {
   struct snmp_proto *p = (void *) P;
-  struct snmp_config *cf = (struct snmp_config *) P->cf;
-
-  p->local_ip = cf->local_ip;
-  p->remote_ip = cf->remote_ip;
-  p->local_port = cf->local_port;
-  p->remote_port = cf->remote_port;
-  p->bgp_local_as = cf->bgp_local_as;
-  p->bgp_local_id = cf->bgp_local_id;
-  p->timeout = cf->timeout;
-  p->startup_delay = cf->startup_delay;
-  p->verbose = cf->verbose;
 
-  p->pool = p->p.pool;
-  p->lp = lp_new(p->pool);
-  p->bgp_trie = f_new_trie(p->lp, 0);
-  p->mib_tree = mb_alloc(p->pool, sizeof(struct mib_tree));
-
-  p->startup_timer = tm_new_init(p->pool, snmp_startup_timeout, p, 0, 0);
-  p->ping_timer = tm_new_init(p->pool, snmp_ping_timeout, p, p->timeout, 0);
-
-  init_list(&p->registration_queue);
-
-  /* We create copy of bonds to BGP protocols. */
-  HASH_INIT(p->bgp_hash, p->pool, 10);
-
-  mib_tree_init(p->pool, p->mib_tree);
-  snmp_bgp4_start(p, 1);
+  cli_msg(-1006, "  SNMP state: %s", snmp_state_str[p->state]);
+  cli_msg(-1006, "  MIBs");
 
-  return snmp_set_state(p, SNMP_INIT);
+  snmp_bgp4_show_info(p);
 }
 
 /*
@@ -614,18 +580,65 @@ snmp_reconfigure(struct proto *P, struct proto_config *CF)
 }
 
 /*
- * snmp_show_proto_info - print basic information about SNMP protocol instance
+ * snmp_start - Initialize the SNMP protocol instance
  * @P: SNMP protocol generic handle
+ *
+ * The first step in AgentX subagent startup is protocol initialition.
+ * We must prepare lists, find BGP peers and finally asynchronously start
+ * a AgentX subagent session.
  */
-static void
-snmp_show_proto_info(struct proto *P)
+static int
+snmp_start(struct proto *P)
 {
   struct snmp_proto *p = (void *) P;
+  struct snmp_config *cf = (struct snmp_config *) P->cf;
 
-  cli_msg(-1006, "  SNMP state: %s", snmp_state_str[p->state]);
-  cli_msg(-1006, "  MIBs");
+  p->local_ip = cf->local_ip;
+  p->remote_ip = cf->remote_ip;
+  p->local_port = cf->local_port;
+  p->remote_port = cf->remote_port;
+  p->bgp_local_as = cf->bgp_local_as;
+  p->bgp_local_id = cf->bgp_local_id;
+  p->timeout = cf->timeout;
+  p->startup_delay = cf->startup_delay;
+  p->verbose = cf->verbose;
 
-  snmp_bgp4_show_info(p);
+  p->pool = p->p.pool;
+  p->lp = lp_new(p->pool);
+  p->bgp_trie = f_new_trie(p->lp, 0);
+  p->mib_tree = mb_alloc(p->pool, sizeof(struct mib_tree));
+
+  p->startup_timer = tm_new_init(p->pool, snmp_startup_timeout, p, 0, 0);
+  p->ping_timer = tm_new_init(p->pool, snmp_ping_timeout, p, p->timeout, 0);
+
+  init_list(&p->registration_queue);
+
+  /* We create copy of bonds to BGP protocols. */
+  HASH_INIT(p->bgp_hash, p->pool, 10);
+
+  mib_tree_init(p->pool, p->mib_tree);
+  snmp_bgp4_start(p, 1);
+
+  return snmp_set_state(p, SNMP_INIT);
+}
+
+/*
+ * snmp_init - preinitialize SNMP instance
+ * @CF: SNMP configuration generic handle
+ *
+ * Returns a generic handle pointing to preinitialized SNMP procotol
+ * instance.
+ */
+static struct proto *
+snmp_init(struct proto_config *CF)
+{
+  struct proto *P = proto_new(CF);
+  struct snmp_proto *p = SKIP_BACK(struct snmp_proto, p, P);
+
+  p->rl_gen = (struct tbf) TBF_DEFAULT_LOG_LIMITS;
+  p->state = SNMP_DOWN;
+
+  return P;
 }
 
 /*
@@ -642,19 +655,6 @@ snmp_postconfig(struct proto_config *CF)
     cf_error("local as not specified");
 }
 
-/*
- * snmp_shutdown - Forcefully stop the SNMP protocol instance
- * @P: SNMP protocol generic handle
- *
- * Simple cast-like wrapper around snmp_reset(), see more info there.
- */
-static int
-snmp_shutdown(struct proto *P)
-{
-  struct snmp_proto *p = SKIP_BACK(struct snmp_proto, p, P);
-  return snmp_reset(p);
-}
-
 
 /*
  * Protocol infrastructure
@@ -670,8 +670,8 @@ struct protocol proto_snmp = {
   .init =              snmp_init,
   .start =             snmp_start,
   .reconfigure =       snmp_reconfigure,
-  .shutdown =          snmp_shutdown,
   .show_proto_info =   snmp_show_proto_info,
+  .shutdown =          snmp_shutdown,
 };
 
 void
index 9a787c6eaa00430b05a0ae1c44b111216bb9334a..b60b0af96204b042c4c16e163bea499db3502f50 100644 (file)
@@ -53,6 +53,15 @@ enum snmp_transport_type {
   SNMP_TRANS_TCP,
 };
 
+#define SNMP_BGP_P_REGISTERING 0x01
+#define SNMP_BGP_P_REGISTERED  0x02
+
+struct snmp_bgp_peer {
+  const struct bgp_proto *bgp_proto;
+  ip4_addr peer_ip;                  /* used as hash key */
+  struct snmp_bgp_peer *next;
+};
+
 struct snmp_config {
   struct proto_config cf;
   enum snmp_transport_type trans_type;
@@ -79,21 +88,6 @@ struct snmp_config {
   int verbose;
 };
 
-#define SNMP_BGP_P_REGISTERING 0x01
-#define SNMP_BGP_P_REGISTERED  0x02
-
-struct snmp_bgp_peer {
-  const struct bgp_proto *bgp_proto;
-  ip4_addr peer_ip;                  /* used as hash key */
-  struct snmp_bgp_peer *next;
-};
-
-struct snmp_registered_oid {
-  node n;
-  struct oid *oid;
-};
-
-
 struct snmp_proto {
   struct proto p;
   struct object_lock *lock;
@@ -165,7 +159,6 @@ void snmp_connected(sock *sk);
 void snmp_startup_timeout(timer *tm);
 void snmp_reconnect(timer *tm);
 int snmp_set_state(struct snmp_proto *p, enum snmp_proto_state state);
-
 int snmp_reset(struct snmp_proto *p);
 void snmp_up(struct snmp_proto *p);
 
index 10febdd696cf211ebb33825514ba5e09aff9c1f6..42de8f9904e8a6647ecd72ca566846846b080243 100644 (file)
@@ -36,12 +36,6 @@ snmp_session(const struct snmp_proto *p, struct agentx_header *h)
   STORE_U32(h->packet_id, p->packet_id);
 }
 
-inline int
-snmp_has_context(const struct agentx_header *h)
-{
-  return h->flags & AGENTX_NON_DEFAULT_CONTEXT;
-}
-
 inline void *
 snmp_varbind_data(const struct agentx_varbind *vb)
 {
@@ -49,45 +43,6 @@ snmp_varbind_data(const struct agentx_varbind *vb)
   return (void *) &vb->name + name_size;
 }
 
-struct oid *
-snmp_varbind_set_name_len(struct snmp_pdu *c, struct agentx_varbind **vb, u8 len)
-{
-  struct oid *oid = &(*vb)->name;
-
-  if (oid->n_subid >= len)
-  {
-    c->size += (oid->n_subid - len) * sizeof(u32);
-    oid->n_subid = len;
-    return oid;
-  }
-
-  /* We need more space */
-  ASSUME(len >= oid->n_subid);
-  uint diff_size = (len - oid->n_subid) * sizeof(u32);
-
-  if (snmp_tbuf_reserve(c, diff_size))
-    oid = &(*vb)->name;
-
-  ASSERT(c->size >= diff_size);
-  c->size -= diff_size;
-  oid->n_subid = len;
-  return &(*vb)->name;
-}
-
-void
-snmp_varbind_duplicate_hdr(struct snmp_pdu *c, struct agentx_varbind **vb)
-{
-  ASSUME(vb != NULL && *vb != NULL);
-  uint hdr_size = snmp_varbind_header_size(&(*vb)->name);
-  (void) snmp_tbuf_reserve(c, hdr_size);
-
-  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
@@ -128,18 +83,6 @@ snmp_oid_is_prefixable(const struct oid *oid)
   return 1;
 }
 
-
-/**
- * snmp_pkt_len - returns size of SNMP packet payload (without header)
- * @buf: packet first byte
- * @pkt: first byte past packet end
- */
-uint
-snmp_pkt_len(const byte *start, const byte *end)
-{
-  return (end - start) - AGENTX_HEADER_SIZE;
-}
-
 /*
  * snmp_oid_copy - copy OID from one place to another
  * @dest: destination to use
@@ -190,29 +133,6 @@ snmp_oid_to_buf(struct oid *dst, const struct oid *src)
     STORE_U32(dst->ids[i], src->ids[i]);
 }
 
-/*
- * snmp_oid_duplicate - duplicate an OID from memory pool
- * @pool: pool to use
- * @oid: OID to be duplicated
- */
-struct oid *
-snmp_oid_duplicate(pool *pool, const struct oid *oid)
-{
-  struct oid *res = mb_alloc(pool, snmp_oid_size(oid));
-  memcpy(res, oid, snmp_oid_size(oid));
-  return res;
-}
-
-/**
- * snmp_oid_blank - create new null oid (blank)
- * @p: pool hodling snmp_proto structure
- */
-struct oid *
-snmp_oid_blank(struct snmp_proto *p)
-{
-  return mb_allocz(p->p.pool, sizeof(struct oid));
-}
-
 /**
  * snmp_str_size_from_len - return in-buffer octet string size
  * @len: length of C-string, returned from strlen()
@@ -278,12 +198,6 @@ snmp_varbind_header_size(const struct oid *vb_name)
   return snmp_oid_size(vb_name) + OFFSETOF(struct agentx_varbind, name);
 }
 
-/*
- * Beware that for octet string, using this function may be a bit tricky due to
- * the different byte orders cpu native/packet
- *
- *
- */
 uint
 snmp_varbind_size_unsafe(const struct agentx_varbind *vb)
 {
@@ -316,66 +230,6 @@ snmp_varbind_size_unsafe(const struct agentx_varbind *vb)
   }
 }
 
-/**
- * snmp_varbind_size - get size of in-buffer VarBind
- * @vb: VarBind in cpu native byte order to measure
- * @limit: upper limit of bytes that can be used
- *
- * This functions assumes valid VarBind type.
- * Return 0 for Varbinds longer than limit, Varbind's size otherwise.
- */
-uint UNUSED
-snmp_varbind_size(const struct agentx_varbind *vb, uint limit)
-{
-  if (limit < sizeof(struct agentx_varbind))
-    return 0;
-
-  if (!snmp_test_varbind_type(vb->type))
-    return 0;
-
-  enum agentx_type type = vb->type;
-  int s = agentx_type_size(type);
-  uint vb_header = snmp_varbind_header_size(&vb->name);
-
-  if (limit < vb_header)
-    return 0;
-
-  if (s == 0)
-    return vb_header;
-
-  if (s > 0 && vb_header + s <= limit)
-    return vb_header + s;
-  else if (s > 0)
-    return 0;
-
-  uint sz;
-  switch (type)
-  {
-    case AGENTX_OBJECT_ID:;
-      struct oid *oid = snmp_varbind_data(vb);
-      /* snmp_oid_size works for both native and packet byte order */
-      sz = snmp_oid_size(oid);
-      if (limit < vb_header + sz)
-       return 0;
-      else
-       return vb_header + snmp_oid_size(oid);
-
-    case AGENTX_OCTET_STRING:
-    case AGENTX_IP_ADDRESS:
-    case AGENTX_OPAQUE:;
-      struct agentx_octet_str *os = snmp_varbind_data(vb);
-      sz = snmp_get_octet_size(os);
-      if (limit < vb_header + sz)
-       return 0;
-      else
-       return vb_header + sz;
-
-    default:
-      /* This should not happen */
-      return 0;
-  }
-}
-
 /**
  * snmp_varbind_size_from_len - get size in-buffer VarBind for known OID and data
  * @n_subid: number of subidentifiers of the VarBind's OID name
@@ -429,7 +283,7 @@ snmp_test_varbind_type(u16 type)
 
 
 /**
- * snmp_oid_ip4_index - check IPv4 address validity in oid
+ * snmp_valid_ip4_index - check IPv4 address validity in oid
  * @o: object identifier holding ip address
  * @start: index of first address id
  */
@@ -534,25 +388,6 @@ snmp_put_fbyte(byte *buf, u8 data)
   return buf + 3;
 }
 
-/*
- * snmp_oid_ip4_index - OID append IPv4 index
- * @o: OID to use
- * @start: index of IP addr's MSB
- * @addr: IPv4 address to use
- *
- * The indices from start to (inclusive) start+3 are overwritten by @addr bytes.
- */
-void
-snmp_oid_ip4_index(struct oid *o, uint start, ip4_addr addr)
-{
-  u32 temp = ip4_to_u32(addr);
-  o->ids[start] = temp >> 24;
-  o->ids[start + 1] = (temp >> 16) & 0xFF;
-  o->ids[start + 2] = (temp >>  8) & 0xFF;
-  o->ids[start + 3] = temp & 0xFF;
-}
-
-
 /**
  * snmp_oid_compare - find the lexicographical order relation between @left and @right
  * @left: left object id relation operant
@@ -654,8 +489,8 @@ snmp_registration_create(struct snmp_proto *p, enum agentx_mibs mib)
   r->n.prev = r->n.next = NULL;
 
   r->session_id = p->session_id;
-  /* will be incremented by snmp_session() macro during packet assembly */
   r->transaction_id = p->transaction_id;
+  /* will be incremented by snmp_session() macro during packet assembly */
   r->packet_id = p->packet_id + 1;
   r->mib = mib;
 
@@ -673,15 +508,6 @@ snmp_registration_match(struct snmp_registration *r, struct agentx_header *h)
 }
 
 
-void UNUSED
-snmp_dump_packet(byte UNUSED *pkt, uint size)
-{
-  DBG("dump");
-  for (uint i = 0; i < size; i += 4)
-    DBG("pkt [%d]  0x%02x%02x%02x%02x", i, pkt[i],pkt[i+1],pkt[i+2],pkt[i+3]);
-  DBG("end dump");
-}
-
 /*
  * agentx_type_size - get in packet VarBind type size
  * @type: VarBind type
@@ -759,18 +585,6 @@ snmp_varbind_ip4(struct snmp_pdu *c, ip4_addr addr)
   c->buffer = snmp_put_ip4(snmp_varbind_data(c->sr_vb_start), addr);
 }
 
-#if 0
-inline byte *
-snmp_varbind_nstr2(struct snmp_pdu *c, uint size, const char *str, uint len)
-{
-  if (size < snmp_str_size_from_len(len))
-    return NULL;
-
-  c->sr_vb_start = AGENTX_OCTET_STRING;
-  return snmp_put_nstr(snmp_varbind_data(c->sr_vb_start), str, len);
-}
-#endif
-
 /*
  * snmp_varbind_nstr - fill varbind context with octet string
  * @vb: VarBind to use
@@ -894,6 +708,8 @@ snmp_oid_log(const struct oid *oid)
  * be NULL OID in cases where there is no common subid. The result could be also
  * viewed as longest common prefix. Note that if both @left and @right are
  * prefixable but not prefixed the result in @out will also not be prefixed.
+ *
+ * This function is used intensively by snmp_test.c.
  */
 void
 snmp_oid_common_ancestor(const struct oid *left, const struct oid *right, struct oid *out)
index bde4317973a2401205c90ac6d366293abc0527fb..b4010be52b5fb21c6205410b2afe419c28d91a82 100644 (file)
@@ -4,8 +4,6 @@
 #include "subagent.h"
 #include "mib_tree.h"
 
-uint snmp_pkt_len(const byte *start, const byte *end);
-
 /*
  *
  *    AgentX Variable Biding (VarBind) utils
@@ -27,7 +25,6 @@ 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);
 void snmp_oid_from_buf(struct oid *dest, const struct oid *src);
@@ -49,28 +46,27 @@ snmp_oid_is_prefixed(const struct oid *oid)
 /* type IPv4 */
 int snmp_valid_ip4_index(const struct oid *o, uint start);
 int snmp_valid_ip4_index_unsafe(const struct oid *o, uint start);
-void snmp_oid_ip4_index(struct oid *o, uint start, ip4_addr addr);
 
 /*
  *  AgentX - Variable Binding (VarBind) manupulation
  */
 uint snmp_varbind_header_size(const struct oid *vb_name);
-uint snmp_varbind_size(const struct agentx_varbind *vb, uint limit);
 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_type(u16 type);
 void *snmp_varbind_data(const struct agentx_varbind *vb);
-struct oid *snmp_varbind_set_name_len(struct snmp_pdu *c, struct agentx_varbind **vb, u8 len);
-void snmp_varbind_duplicate_hdr(struct snmp_pdu *c, struct agentx_varbind **vb);
 
 /*
  *  AgentX - PDU headers, types, contexts
  */
 void snmp_session(const struct snmp_proto *p, struct agentx_header *h);
-int snmp_has_context(const struct agentx_header *h);
 void snmp_pdu_context(struct snmp_pdu *pdu, struct snmp_proto *p, sock *sk);
-struct oid *snmp_oid_duplicate(pool *pool, const struct oid *oid);
-struct oid *snmp_oid_blank(struct snmp_proto *p);
+
+static inline int
+snmp_has_context(const struct agentx_header *h)
+{
+  return LOAD_U8(h->flags) & AGENTX_NON_DEFAULT_CONTEXT;
+}
 
 int snmp_test_close_reason(byte value);
 
@@ -105,7 +101,6 @@ byte *snmp_put_fbyte(byte *buf, u8 data);
 struct snmp_registration *snmp_registration_create(struct snmp_proto *p, enum agentx_mibs mib);
 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);
 
index 4e43245808bdc8796cd772e7c7899b74289f6a85..d94b2a799ef77f03a68aa5b158023ad4747067b2 100644 (file)
  *
  */
 
-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);
+void snmp_register_ack(struct snmp_proto *p, struct agentx_response *res);
 
 /* standard SNMP internet prefix (.1.3.6.1) */
 const u32 snmp_internet[] = { SNMP_ISO, SNMP_ORG, SNMP_DOD, SNMP_INTERNET };
 
+/*
+ * update_packet_size - set PDU size
+ * @start: pointer to PDU data start (excluding header size)
+ * @end: pointer after the last PDU byte
+ *
+ * Return number of bytes in TX buffer (including header size).
+ */
+static inline uint
+update_packet_size(struct agentx_header *start, byte *end)
+{
+  /* pkt_len */
+  uint s = (end - (byte *) start) - AGENTX_HEADER_SIZE;
+  STORE_U32(start->payload, s);
+  return AGENTX_HEADER_SIZE + s;
+}
+
+/*
+ * response_err_ind - update response error and index
+ * @p: SNMP protocol instance
+ * @res: response PDU header
+ * @err: error status
+ * @ind: index of error, ignored for noAgentXError
+ *
+ * Update agentx-Response-PDU header fields res.error and it's res.index. If the
+ * error is not noError, also set the corrent response PDU payload size.
+ */
+static inline void
+response_err_ind(struct snmp_proto *p, struct agentx_response *res, enum agentx_response_errs err, u16 ind)
+{
+  STORE_U16(res->error, (u16) err);
+  if (err != AGENTX_RES_NO_ERROR && err != AGENTX_RES_GEN_ERROR)
+  {
+    if (p->verbose)
+      TRACE(D_PACKETS, "SNMP last PDU resulted in error %u", err);
+    STORE_U16(res->index, ind);
+    /* Reset VarBindList to null */
+    STORE_U32(res->h.payload,
+      sizeof(struct agentx_response) - AGENTX_HEADER_SIZE);
+  }
+  else if (err == AGENTX_RES_GEN_ERROR)
+  {
+    if (p->verbose)
+      TRACE(D_PACKETS, "SNMP last PDU resulted in error genErr");
+    STORE_U16(res->index, 0);
+    /* Reset VarBindList to null */
+    STORE_U32(res->h.payload,
+      sizeof(struct agentx_response) - AGENTX_HEADER_SIZE);
+  }
+  else
+    STORE_U16(res->index, 0);
+}
+
+/*
+ * snmp_varbind_leave - transform VarBind to packet byte order
+ * @vb: prepared VarBind in cpu native byte order
+ */
+void
+snmp_varbind_leave(struct agentx_varbind *vb)
+{
+  STORE_U16(vb->type, vb->type);
+
+  /* Does nothing */
+  STORE_U16(vb->reserved, 0);
+  struct oid *oid = &vb->name;
+  STORE_U8(oid->n_subid, oid->n_subid);
+  STORE_U8(oid->prefix, oid->prefix);
+  STORE_U8(oid->include, oid->include);
+  STORE_U8(oid->reserved, 0);
+
+  for (u8 i = 0; i < oid->n_subid; i++)
+    STORE_U32(oid->ids[i], oid->ids[i]);
+}
+
+/*
+ * snmp_tbuf_reserve - conditionally grow the TX buffer
+ * @c: transmit PDU context
+ * @size: size to make available
+ *
+ * Return non-zero if the buffer was relocated.
+ */
+int
+snmp_tbuf_reserve(struct snmp_pdu *c, size_t size)
+{
+  if (size >= c->size)
+  {
+    struct snmp_proto *p = c->p;
+    sock *sk = p->sock;
+
+    int start_diff;
+    if (c->sr_vb_start != NULL)
+      start_diff = (char *) c->sr_vb_start - (char *) sk->tbuf;
+
+    sk_set_tbsize(sk, sk->tbsize + 2048);
+    c->size += 2048;
+
+    if (c->sr_vb_start != NULL)
+      c->sr_vb_start = (struct agentx_varbind *) (sk->tbuf + start_diff);
+
+    return 1;
+  }
+
+  return 0;
+}
+
+/*
+ * refresh_ids - Copy current ids from packet to protocol
+ * @p: SNMP protocol instance
+ * @h: PDU header with new transaction_id and packet_id ids.
+ */
+static inline void
+refresh_ids(struct snmp_proto *p, struct agentx_header *h)
+{
+  p->transaction_id = LOAD_U32(h->transaction_id);
+  p->packet_id = LOAD_U32(h->packet_id);
+}
+
 /*
  * snmp_header - store packet header into buffer
  * @h: pointer to created packet header in TX buffer
@@ -74,111 +185,240 @@ snmp_blank_header(struct agentx_header *h, enum agentx_pdu_types type)
 }
 
 /*
- * snmp_register_ack - handle registration response
+ * prepare_response - fill buffer with AgentX PDU header
  * @p: SNMP protocol instance
- * @res: header of agentx-Response-PDU
+ * @c: transmit PDU context to use
+ *
+ * Prepare known parts of AgentX packet header into the TX buffer held by @c.
  */
-void
-snmp_register_ack(struct snmp_proto *p, struct agentx_response *res)
+static struct agentx_response *
+prepare_response(struct snmp_proto *p, struct snmp_pdu *c)
 {
-  struct snmp_registration *reg;
-  WALK_LIST(reg, p->registration_queue)
-  {
-    if (snmp_registration_match(reg, &res->h))
-    {
-      rem_node(&reg->n);
+  struct agentx_response *r = (void *) c->buffer;
+  struct agentx_header *h = &r->h;
 
-      if (res->error == AGENTX_RES_NO_ERROR && reg->reg_hook_ok)
-       reg->reg_hook_ok(p, res, reg);
-      else if (res->error != AGENTX_RES_NO_ERROR && reg->reg_hook_fail)
-       reg->reg_hook_fail(p, res, reg);
+  snmp_blank_header(h, AGENTX_RESPONSE_PDU);
+  snmp_session(p, h);
 
-      mb_free(reg);
-      break;
-    }
-  }
+  /* protocol doesn't care about subagent upTime */
+  STORE_U32(r->uptime, 0);
+  STORE_U16(r->error, AGENTX_RES_NO_ERROR);
+  STORE_U16(r->index, 0);
 
-  if (EMPTY_LIST(p->registration_queue))
-    snmp_up(p);
+  ADVANCE(c->buffer, c->size, sizeof(struct agentx_response));
+  return r;
 }
 
 /*
- * snmp_simple_response - send an agentx-Response-PDU with no data payload
- * @p: SNMP protocol instance
- * @error: response PDU error fields value
- * @index: response PDU error index field value
+ * snmp_oid_prefixize_unsafe - normalize OID to prefixed form
+ * @dest: destination for normalized OID in native byte order
+ * @src: source OID in packet byte order
  *
- * This function assumes that the buffer has enough space to fill
- * in the agentx-Response-PDU.
+ * Note that again, snmp_oid_prefixize_unsafe is intended to copy Object
+ * Identifier from RX buffer to TX buffer but also optionally swap the byte
+ * order from packet b.o. to cpu native b.o. This is done to simplify the code
+ * dealing with OIDs.
  */
-static void
-snmp_simple_response(struct snmp_proto *p, enum agentx_response_errs error, u16 index)
+static inline void
+snmp_oid_prefixize_unsafe(struct oid *dest, const struct oid *src)
 {
-  sock *sk = p->sock;
-  struct snmp_pdu c;
-  snmp_pdu_context(&c, p, sk);
-
-  ASSUME(c.size >= sizeof(struct agentx_response));
+  dest->n_subid = LOAD_U8(src->n_subid) - 5;
+  dest->prefix = (u8) LOAD_U32(src->ids[ARRAY_SIZE(snmp_internet)]);
+  dest->include = (LOAD_U8(src->include)) ? 1 : 0;
+  dest->reserved = 0;
 
-  struct agentx_response *res = prepare_response(p, &c);
-  response_err_ind(p, res, error, index);
-  sk_send(sk, sizeof(struct agentx_response));
+  /* The LOAD_U32() and STORE_U32() cancel out */
+  for (u8 i = 0; i < dest->n_subid; i++)
+    dest->ids[i] = LOAD_U32(src->ids[i + 5]);
 }
 
 /*
- * open_pdu - send an agentx-Open-PDU
- * @p: SNMP protocol instance
- * @oid: PDU OID description field value
+ * snmp_vb_to_tx - create VarBind in TX buffer from RX buffer OID
+ * @c: PDU context
+ * @oid: Object Identifier located in RX buffer with packet byte order
  *
- * Other fields are filled based on @p configuration (timeout, subagent
- * description).
+ * Create a NULL initialized VarBind inside TX buffer (from @c) whose name
+ * is @oid. Because we want to simplify code dealing with OIDs, the byte order
+ * of the name is optionally swapped to match cpu native byte order.
  */
-static void
-open_pdu(struct snmp_proto *p, struct oid *oid)
+struct agentx_varbind *
+snmp_vb_to_tx(struct snmp_pdu *c, const struct oid *oid)
 {
-  const struct snmp_config *cf = SKIP_BACK(struct snmp_config, cf, p->p.cf);
-  sock *sk = p->sock;
+  uint vb_hdr_size = snmp_varbind_header_size(oid);
+  (void) snmp_tbuf_reserve(c, vb_hdr_size);
 
-  TRACE(D_PACKETS, "SNMP sending agentx-Open-PDU");
+  ASSERT(c->size >= vb_hdr_size);
+  struct agentx_varbind *vb = (struct agentx_varbind *) 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 */
+  vb->type = AGENTX_NULL;
 
-  struct snmp_pdu c;
-  snmp_pdu_context(&c, p, sk);
+  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));
+    snmp_oid_prefixize_unsafe(&vb->name, oid);
 
-#define TIMEOUT_SIZE sizeof(u32) /* 1B timeout, 3B zero padding */
+    return vb;
+  }
 
-  /* Make sure that we have enough space in TX buffer */
-  uint s = AGENTX_HEADER_SIZE + TIMEOUT_SIZE + snmp_oid_size(oid) +
-    snmp_str_size(cf->description);
+  ADVANCE(c->buffer, c->size, snmp_oid_size(oid));
+  snmp_oid_from_buf(&vb->name, oid);
 
-  (void) snmp_tbuf_reserve(&c, s);
+  return vb;
+}
 
-  struct agentx_header *h = (void *) c.buffer;
-  ADVANCE(c.buffer, c.size, AGENTX_HEADER_SIZE);
-  snmp_blank_header(h, AGENTX_OPEN_PDU);
+// TODO XXX docstring
+int
+snmp_load_oids(byte **pkt_ptr, uint *pkt_sz, struct snmp_pdu *c)
+{
+  byte *pkt = *pkt_ptr;
+  uint pkt_size = *pkt_sz;
 
-  STORE_U32(h->session_id, 1);
-  STORE_U32(h->transaction_id, 1);
-  STORE_U32(h->packet_id, 1);
+  uint sz;
+  /* in packet byte order */
+  const struct oid *start_buf = (const struct oid *) pkt;
+  if ((sz = snmp_oid_size(start_buf)) > pkt_size ||
+      LOAD_U8(start_buf->n_subid) >= OID_MAX_LEN)
+  {
+    c->error = AGENTX_RES_PARSE_ERROR;
+    *pkt_ptr = pkt;
+    *pkt_sz = pkt_size;
+    return 0;
+  }
 
-  c.size -= (4 + snmp_oid_size(oid) + snmp_str_size(cf->description));
+  ADVANCE(pkt, pkt_size, sz);
 
-  if (p->timeout >= 1 S && p->timeout <= 255 S)
-    /* use p->timeout ceiled up to whole second */
-    c.buffer = snmp_put_fbyte(c.buffer,
-      (p->timeout % (1 S) == 0) ? p->timeout TO_S : p->timeout TO_S + 1);
-  /* out of range fallbacks */
-  else if (p->timeout < 1 TO_US)
-    c.buffer = snmp_put_fbyte(c.buffer, (u8) 1);
-  else /* p->timeout > 255 TO_US */
-    c.buffer = snmp_put_fbyte(c.buffer, (u8) 255);
+  /* in packet byte order */
+  const struct oid *end_buf = (const struct oid *) pkt;
+  if ((sz = snmp_oid_size(end_buf)) > pkt_size ||
+      LOAD_U8(end_buf->n_subid) >= OID_MAX_LEN)
+  {
+    c->error = AGENTX_RES_PARSE_ERROR;
+    *pkt_ptr = pkt;
+    *pkt_sz = pkt_size;
+    return 0;
+  }
 
-  snmp_oid_to_buf((struct oid *) c.buffer, oid);
-  c.buffer += snmp_oid_size(oid);
-  c.buffer = snmp_put_str(c.buffer, cf->description);
+  /* in cpu native byte order */
+  struct agentx_varbind *start_vb = snmp_vb_to_tx(c, start_buf);
 
-  s = update_packet_size(h, c.buffer);
+  /* in cpu native byte order */
+  struct oid *end_oid = tmp_alloc(sz);
+  snmp_oid_from_buf(end_oid, end_buf);
+
+  ADVANCE(pkt, pkt_size, sz);
+
+  if (!snmp_is_oid_empty(end_oid) &&
+      snmp_oid_compare(&start_vb->name, end_oid) > 0)
+  {
+    c->error = AGENTX_RES_GEN_ERROR;
+    *pkt_ptr = pkt;
+    *pkt_sz = pkt_size;
+    return 0;
+  }
+
+  ASSERT(start_vb != NULL);
+  ASSERT(end_oid != NULL);
+
+  c->sr_vb_start = start_vb;
+  c->sr_o_end = end_oid;
+  *pkt_ptr = pkt;
+  *pkt_sz = pkt_size;
+  return 1; /* ok */
+}
+
+/*
+ * close_pdu - send an agentx-Close-PDU
+ * @p: SNMP protocol instance
+ * @reason: reason for closure
+ */
+static void
+close_pdu(struct snmp_proto *p, enum agentx_close_reasons reason)
+{
+  sock *sk = p->sock;
+  struct snmp_pdu c;
+  snmp_pdu_context(&c, p, sk);
+
+  TRACE(D_PACKETS, "SNMP sending agentx-Close-PDU with reason %u", reason);
+
+#define REASON_SIZE sizeof(u32)
+  (void) snmp_tbuf_reserve(&c, AGENTX_HEADER_SIZE + REASON_SIZE);
+
+  struct agentx_header *h = (void *) c.buffer;
+  ADVANCE(c.buffer, c.size, AGENTX_HEADER_SIZE);
+  snmp_blank_header(h, AGENTX_CLOSE_PDU);
+  p->packet_id++;
+  snmp_session(p, h);
+
+  (void) snmp_put_fbyte(c.buffer, (u8) reason);
+  ADVANCE(c.buffer, c.size, REASON_SIZE);
+
+  uint s = update_packet_size(h, c.buffer);
   sk_send(sk, s);
-#undef TIMEOUT_SIZE
+#undef REASON_SIZE
+}
+
+/*
+ * ping_pdu - send an agentx-Ping-PDU
+ * @p: SNMP protocol instance
+ */
+void
+snmp_ping(struct snmp_proto *p)
+{
+  /* ping_pdu */
+  if (!snmp_is_active(p))
+    return;
+
+  sock *sk = p->sock;
+  struct snmp_pdu c;
+  snmp_pdu_context(&c, p, sk);
+
+  if (c.size < AGENTX_HEADER_SIZE)
+    return;
+
+  int unused = sk->tbuf + sk->tbsize - c.buffer;
+  if (unused < AGENTX_HEADER_SIZE)
+    return;
+
+  struct agentx_header *h = (void *) c.buffer;
+  ADVANCE(c.buffer, c.size, AGENTX_HEADER_SIZE);
+  snmp_blank_header(h, AGENTX_PING_PDU);
+  p->packet_id++;
+  snmp_session(p, h);
+  if (p->verbose)
+    TRACE(D_PACKETS, "SNMP sending agentx-Ping-PDU");
+  p->ignore_ping_id = p->packet_id;
+
+  /* sending only header */
+  uint s = update_packet_size(h, (byte *) h + AGENTX_HEADER_SIZE);
+
+  if (p->packet_id)
+    p->pings++;
+  sk_send(sk, s);
+}
+
+/*
+ * snmp_simple_response - send an agentx-Response-PDU with no data payload
+ * @p: SNMP protocol instance
+ * @error: response PDU error fields value
+ * @index: response PDU error index field value
+ *
+ * This function assumes that the buffer has enough space to fill
+ * in the agentx-Response-PDU.
+ */
+static void
+snmp_simple_response(struct snmp_proto *p, enum agentx_response_errs error, u16 index)
+{
+  sock *sk = p->sock;
+  struct snmp_pdu c;
+  snmp_pdu_context(&c, p, sk);
+
+  ASSUME(c.size >= sizeof(struct agentx_response));
+
+  struct agentx_response *res = prepare_response(p, &c);
+  response_err_ind(p, res, error, index);
+  sk_send(sk, sizeof(struct agentx_response));
 }
 
 /*
@@ -343,6 +583,22 @@ un_register_pdu(struct snmp_proto *p, struct oid *oid, u32 bound, uint index, en
 #undef BOUND_SIZE
 }
 
+/*
+ * snmp_unregister - send an agentx-Unregister-PDU
+ * @p: SNMP protocol instance
+ * @oid: OID to uregister
+ * @bound: OIDs unregistration upper bound
+ * @index: OIDs unregistration n_subid index
+ *
+ * For more detailed description see un_register_pdu() function.
+ */
+void UNUSED
+snmp_unregister(struct snmp_proto *p, struct oid *oid, u32 bound, uint index)
+{
+  TRACE(D_PACKETS, "SNMP sending agentx-Unregister-PDU");
+  un_register_pdu(p, oid, bound, index, AGENTX_UNREGISTER_PDU, 0);
+}
+
 /*
  * snmp_register - send an agentx-Register-PDU
  * @p: SNMP protocol instance
@@ -361,50 +617,59 @@ snmp_register(struct snmp_proto *p, struct oid *oid, u32 bound, uint index, u8 i
 }
 
 /*
- * snmp_unregister - send an agentx-Unregister-PDU
+ * open_pdu - send an agentx-Open-PDU
  * @p: SNMP protocol instance
- * @oid: OID to uregister
- * @bound: OIDs unregistration upper bound
- * @index: OIDs unregistration n_subid index
+ * @oid: PDU OID description field value
  *
- * For more detailed description see un_register_pdu() function.
- */
-void UNUSED
-snmp_unregister(struct snmp_proto *p, struct oid *oid, u32 bound, uint index)
-{
-  TRACE(D_PACKETS, "SNMP sending agentx-Unregister-PDU");
-  un_register_pdu(p, oid, bound, index, AGENTX_UNREGISTER_PDU, 0);
-}
-
-/*
- * close_pdu - send an agentx-Close-PDU
- * @p: SNMP protocol instance
- * @reason: reason for closure
+ * Other fields are filled based on @p configuration (timeout, subagent
+ * description).
  */
 static void
-close_pdu(struct snmp_proto *p, enum agentx_close_reasons reason)
+open_pdu(struct snmp_proto *p, struct oid *oid)
 {
+  const struct snmp_config *cf = SKIP_BACK(struct snmp_config, cf, p->p.cf);
   sock *sk = p->sock;
+
+  TRACE(D_PACKETS, "SNMP sending agentx-Open-PDU");
+
   struct snmp_pdu c;
   snmp_pdu_context(&c, p, sk);
 
-  TRACE(D_PACKETS, "SNMP sending agentx-Close-PDU with reason %u", reason);
+#define TIMEOUT_SIZE sizeof(u32) /* 1B timeout, 3B zero padding */
 
-#define REASON_SIZE sizeof(u32)
-  (void) snmp_tbuf_reserve(&c, AGENTX_HEADER_SIZE + REASON_SIZE);
+  /* Make sure that we have enough space in TX buffer */
+  uint s = AGENTX_HEADER_SIZE + TIMEOUT_SIZE + snmp_oid_size(oid) +
+    snmp_str_size(cf->description);
+
+  (void) snmp_tbuf_reserve(&c, s);
 
   struct agentx_header *h = (void *) c.buffer;
   ADVANCE(c.buffer, c.size, AGENTX_HEADER_SIZE);
-  snmp_blank_header(h, AGENTX_CLOSE_PDU);
-  p->packet_id++;
-  snmp_session(p, h);
+  snmp_blank_header(h, AGENTX_OPEN_PDU);
 
-  (void) snmp_put_fbyte(c.buffer, (u8) reason);
-  ADVANCE(c.buffer, c.size, REASON_SIZE);
+  STORE_U32(h->session_id, 1);
+  STORE_U32(h->transaction_id, 1);
+  STORE_U32(h->packet_id, 1);
 
-  uint s = update_packet_size(h, c.buffer);
+  c.size -= (4 + snmp_oid_size(oid) + snmp_str_size(cf->description));
+
+  if (p->timeout >= 1 S && p->timeout <= 255 S)
+    /* use p->timeout ceiled up to whole second */
+    c.buffer = snmp_put_fbyte(c.buffer,
+      (p->timeout % (1 S) == 0) ? p->timeout TO_S : p->timeout TO_S + 1);
+  /* out of range fallbacks */
+  else if (p->timeout < 1 TO_US)
+    c.buffer = snmp_put_fbyte(c.buffer, (u8) 1);
+  else /* p->timeout > 255 TO_US */
+    c.buffer = snmp_put_fbyte(c.buffer, (u8) 255);
+
+  snmp_oid_to_buf((struct oid *) c.buffer, oid);
+  c.buffer += snmp_oid_size(oid);
+  c.buffer = snmp_put_str(c.buffer, cf->description);
+
+  s = update_packet_size(h, c.buffer);
   sk_send(sk, s);
-#undef REASON_SIZE
+#undef TIMEOUT_SIZE
 }
 
 /*
@@ -446,105 +711,34 @@ parse_close_pdu(struct snmp_proto *p, byte * const pkt_start)
   return pkt_size + AGENTX_HEADER_SIZE;
 }
 
-
-/*
- * refresh_ids - Copy current ids from packet to protocol
- * @p: SNMP protocol instance
- * @h: PDU header with new transaction_id and packet_id ids.
- */
-static inline void
-refresh_ids(struct snmp_proto *p, struct agentx_header *h)
-{
-  p->transaction_id = LOAD_U32(h->transaction_id);
-  p->packet_id = LOAD_U32(h->packet_id);
-}
-
 /*
- * parse_test_set_pdu - parse an agentx-TestSet-PDU in buffer
+ * parse_sets_pdu - common functionality for commit set and undo set PDUs
  * @p: SNMP protocol instance
- * @pkt_start: first byte of test set PDU
- * @size: number of bytes received from a socket
+ * @pkt_start: pointer to first byte of on of set related PDU
+ * @error: error status to use
  *
  * Return number of bytes parsed from RX buffer.
  */
-static inline uint
-parse_test_set_pdu(struct snmp_proto *p, byte * const pkt_start)
+static uint
+parse_sets_pdu(struct snmp_proto *p, byte * const pkt_start, enum agentx_response_errs err)
 {
-  TRACE(D_PACKETS, "SNMP received agentx-TestSet-PDU");
-  byte *pkt = pkt_start;  /* pointer to agentx-TestSet-PDU in RX buffer */
-  uint s; /* final packat size */
-  struct agentx_response *res; /* pointer to reponse in TX buffer */
-
+  byte *pkt = pkt_start;
   /* Presence of full header is guaranteed by parse_pkt() caller */
   struct agentx_header *h = (void *) pkt;
   pkt += AGENTX_HEADER_SIZE;
+  uint pkt_size = LOAD_U32(h->payload);
+
+  if (pkt_size != 0)
+  {
+    TRACE(D_PACKETS, "SNMP received PDU is malformed (size)");
+    snmp_simple_response(p, AGENTX_RES_PARSE_ERROR, 0);
+    snmp_reset(p);
+    return 0;
+  }
 
-  sock *sk = p->sock;
   struct snmp_pdu c;
-  snmp_pdu_context(&c, p, sk);
-
-  (void) snmp_tbuf_reserve(&c, AGENTX_HEADER_SIZE);
-
-  res = prepare_response(p, &c);
-
-  /* 0 if there is piece, that we cannot set */
-  int all_possible = 0;
-  /* the all_possible is currently hard-coded with no support for writing to mib
-   * variables, when implementing the mentioned support, change the initializer
-   * to 1
-   */
-  s = update_packet_size(h, c.buffer);
-
-  if (c.error != AGENTX_RES_NO_ERROR)
-  {
-    response_err_ind(p, res, c.error, c.index + 1);
-    snmp_reset(p);
-  }
-  else if (all_possible)
-  {
-    /* All values in the agentx-TestSet-PDU are OK, realy to commit them */
-    response_err_ind(p, res, AGENTX_RES_NO_ERROR, 0);
-  }
-  else
-  {
-    // Currently the only reachable branch
-    TRACE(D_PACKETS, "SNMP SET action failed (not writable)");
-    /* This is a recoverable error, we do not need to reset the connection */
-    response_err_ind(p, res, AGENTX_RES_NOT_WRITABLE, c.index + 1);
-  }
-
-  sk_send(sk, s);
-  return pkt - pkt_start;
-}
-
-/*
- * parse_sets_pdu - common functionality for commit set and undo set PDUs
- * @p: SNMP protocol instance
- * @pkt_start: pointer to first byte of on of set related PDU
- * @error: error status to use
- *
- * Return number of bytes parsed from RX buffer.
- */
-static uint
-parse_sets_pdu(struct snmp_proto *p, byte * const pkt_start, enum agentx_response_errs err)
-{
-  byte *pkt = pkt_start;
-  /* Presence of full header is guaranteed by parse_pkt() caller */
-  struct agentx_header *h = (void *) pkt;
-  pkt += AGENTX_HEADER_SIZE;
-  uint pkt_size = LOAD_U32(h->payload);
-
-  if (pkt_size != 0)
-  {
-    TRACE(D_PACKETS, "SNMP received PDU is malformed (size)");
-    snmp_simple_response(p, AGENTX_RES_PARSE_ERROR, 0);
-    snmp_reset(p);
-    return 0;
-  }
-
-  struct snmp_pdu c;
-  snmp_pdu_context(&c, p, p->sock);
-  (void) snmp_tbuf_reserve(&c, sizeof(struct agentx_response));
+  snmp_pdu_context(&c, p, p->sock);
+  (void) snmp_tbuf_reserve(&c, sizeof(struct agentx_response));
 
   struct agentx_response *r = prepare_response(p, &c);
 
@@ -567,38 +761,6 @@ parse_sets_pdu(struct snmp_proto *p, byte * const pkt_start, enum agentx_respons
   return pkt - pkt_start;
 }
 
-/*
- * parse_commit_set_pdu - parse an agentx-CommitSet-PDU
- * @p: SNMP protocol instance
- * @pkt: pointer to first byte of PDU inside RX buffer
- *
- * Return number of bytes parsed from RX buffer.
- */
-static inline uint
-parse_commit_set_pdu(struct snmp_proto *p, byte *pkt)
-{
-  // don't forget to free resoures allocated by parse_test_set_pdu()
-  //mb_free(tr);
-  TRACE(D_PACKETS, "SNMP received agentx-CommitSet-PDU");
-  return parse_sets_pdu(p, pkt, AGENTX_RES_COMMIT_FAILED);
-}
-
-/*
- * parse_undo_set_pdu - parse an agentx-UndoSet-PDU
- * @p: SNMP protocol instance
- * @pkt: pointer to first byte of PDU inside RX buffer
- *
- * Return number of bytes parsed from buffer.
- */
-static inline uint
-parse_undo_set_pdu(struct snmp_proto *p, byte *pkt)
-{
-  // don't forget to free resources allocated by parse_test_set_pdu()
-  //mb_free(tr);
-  TRACE(D_PACKETS, "SNMP received agentx-UndoSet-PDU");
-  return parse_sets_pdu(p, pkt, AGENTX_RES_UNDO_FAILED);
-}
-
 /*
  * parse_cleanup_set_pdu - parse an agentx-CleanupSet-PDU
  * @p: SNMP protocol instance
@@ -631,500 +793,143 @@ parse_cleanup_set_pdu(struct snmp_proto *p, byte * const pkt_start)
 }
 
 /*
- * space_for_response - check if TX buffer has space for agentx-Response-PDU
- * @sk: communication socket owned by SNMP protocol instance
+ * parse_undo_set_pdu - parse an agentx-UndoSet-PDU
+ * @p: SNMP protocol instance
+ * @pkt: pointer to first byte of PDU inside RX buffer
  *
- * In some cases we send only the AgentX header but if we want to signal an
- * error, we need at least space for agentx-Response-PDU. This simplifies the
- * PDU space requirements testing.
+ * Return number of bytes parsed from buffer.
  */
-static inline int
-space_for_response(const sock *sk)
+static inline uint
+parse_undo_set_pdu(struct snmp_proto *p, byte *pkt)
 {
-  return (
-    (uint) (sk->tbuf + sk->tbsize - sk->tpos) >= sizeof(struct agentx_response)
-  );
+  // don't forget to free resources allocated by parse_test_set_pdu()
+  //mb_free(tr);
+  TRACE(D_PACKETS, "SNMP received agentx-UndoSet-PDU");
+  return parse_sets_pdu(p, pkt, AGENTX_RES_UNDO_FAILED);
 }
 
-/**
- * parse_pkt - parse received AgentX packet
+/*
+ * parse_commit_set_pdu - parse an agentx-CommitSet-PDU
  * @p: SNMP protocol instance
- * @pkt: first byte of PDU inside RX buffer
+ * @pkt: pointer to first byte of PDU inside RX buffer
+ *
+ * Return number of bytes parsed from RX buffer.
+ */
+static inline uint
+parse_commit_set_pdu(struct snmp_proto *p, byte *pkt)
+{
+  // don't forget to free resoures allocated by parse_test_set_pdu()
+  //mb_free(tr);
+  TRACE(D_PACKETS, "SNMP received agentx-CommitSet-PDU");
+  return parse_sets_pdu(p, pkt, AGENTX_RES_COMMIT_FAILED);
+}
+
+/*
+ * parse_test_set_pdu - parse an agentx-TestSet-PDU in buffer
+ * @p: SNMP protocol instance
+ * @pkt_start: first byte of test set PDU
  * @size: number of bytes received from a socket
  *
  * Return number of bytes parsed from RX buffer.
  */
-static uint
-parse_pkt(struct snmp_proto *p, byte *pkt, uint size)
+static inline uint
+parse_test_set_pdu(struct snmp_proto *p, byte * const pkt_start)
 {
-  if (size < AGENTX_HEADER_SIZE)
-    return 0;
+  TRACE(D_PACKETS, "SNMP received agentx-TestSet-PDU");
+  byte *pkt = pkt_start;  /* pointer to agentx-TestSet-PDU in RX buffer */
+  uint s; /* final packat size */
+  struct agentx_response *res; /* pointer to reponse in TX buffer */
 
-  struct agentx_header *h = (struct agentx_header *) pkt;
-  if (h->flags & AGENTX_NETWORK_BYTE_ORDER != SNMP_BYTE_ORDER)
-  {
-    TRACE(D_PACKETS, "SNMP received PDU with unexpected byte order");
-    if (h->type != AGENTX_RESPONSE_PDU)
-      snmp_simple_response(p, AGENTX_RES_GEN_ERROR, 0);
-    snmp_reset(p);
-    return 0;
-  }
+  /* Presence of full header is guaranteed by parse_pkt() caller */
+  struct agentx_header *h = (void *) pkt;
+  pkt += AGENTX_HEADER_SIZE;
 
-  u32 pkt_size = LOAD_U32(h->payload);
+  sock *sk = p->sock;
+  struct snmp_pdu c;
+  snmp_pdu_context(&c, p, sk);
 
-  /* RX side checks - too big packet */
-  if (pkt_size > SNMP_PKT_SIZE_MAX)
-  {
-    TRACE(D_PACKETS, "SNMP received PDU is too long");
-    if (h->type != AGENTX_RESPONSE_PDU)
-      snmp_simple_response(p, AGENTX_RES_GEN_ERROR, 0);
-    snmp_reset(p);
-    return 0;
-  }
+  (void) snmp_tbuf_reserve(&c, AGENTX_HEADER_SIZE);
 
-  /* This guarantees that we have the full packet already received */
-  if (size < pkt_size + AGENTX_HEADER_SIZE)
-    return 0; /* no bytes parsed */
+  res = prepare_response(p, &c);
 
-  /*
-   * We need to see the responses for PDU such as
-   * agentx-Open-PDU, agentx-Register-PDU, ...
-   * even when we are outside the SNMP_CONNECTED state
+  /* 0 if there is piece, that we cannot set */
+  int all_possible = 0;
+  /* the all_possible is currently hard-coded with no support for writing to mib
+   * variables, when implementing the mentioned support, change the initializer
+   * to 1
    */
-  if (h->type == AGENTX_RESPONSE_PDU)
-    return parse_response(p, pkt);
+  s = update_packet_size(h, c.buffer);
 
-  ASSERT(snmp_is_active(p));
-  if (p->state != SNMP_CONN ||
-      p->session_id != LOAD_U32(h->session_id))
+  if (c.error != AGENTX_RES_NO_ERROR)
   {
-    struct agentx_header copy = {
-      .session_id = p->session_id,
-      .transaction_id = p->transaction_id,
-      .packet_id = p->packet_id,
-    };
-
-    TRACE(D_PACKETS, "SNMP received PDU with unknown session id");
-    snmp_simple_response(p, AGENTX_RES_NOT_OPEN, 0);
-
-    p->session_id = copy.session_id;
-    p->transaction_id = copy.transaction_id;
-    p->packet_id = copy.packet_id;
-
-    /*
-     * After unexpected state, we simply reset the session
-     * only sending the agentx-Response-PDU.
-     */
+    response_err_ind(p, res, c.error, c.index + 1);
     snmp_reset(p);
-    return 0;
   }
-
-  if (h->flags & AGENTX_NON_DEFAULT_CONTEXT)
+  else if (all_possible)
   {
-    TRACE(D_PACKETS, "SNMP received PDU with non-default context");
-    snmp_simple_response(p, AGENTX_RES_UNSUPPORTED_CONTEXT, 0);
-    snmp_reset(p);
-    return 0;
+    /* All values in the agentx-TestSet-PDU are OK, realy to commit them */
+    response_err_ind(p, res, AGENTX_RES_NO_ERROR, 0);
   }
-
-  refresh_ids(p, h);
-  switch (LOAD_U8(h->type))
+  else
   {
-    case AGENTX_GET_PDU:
-      TRACE(D_PACKETS, "SNMP received agentx-Get-PDU");
-      return parse_gets_pdu(p, pkt);
+    // Currently the only reachable branch
+    TRACE(D_PACKETS, "SNMP SET action failed (not writable)");
+    /* This is a recoverable error, we do not need to reset the connection */
+    response_err_ind(p, res, AGENTX_RES_NOT_WRITABLE, c.index + 1);
+  }
 
-    case AGENTX_GET_NEXT_PDU:
-      TRACE(D_PACKETS, "SNMP received agentx-GetNext-PDU");
-      return parse_gets_pdu(p, pkt);
+  sk_send(sk, s);
+  return pkt - pkt_start;
+}
 
-    case AGENTX_GET_BULK_PDU:
-      TRACE(D_PACKETS, "SNMP received agentx-GetBulk-PDU");
-      return parse_gets_pdu(p, pkt);
+/*
+ * AgentX GetPDU, GetNextPDU and GetBulkPDU
+ */
 
-    case AGENTX_CLOSE_PDU:
-      return parse_close_pdu(p, pkt);
+/* agentx-Get-PDU */
+void
+snmp_get_pdu(struct snmp_proto *p, struct snmp_pdu *c, struct mib_walk_state *walk)
+{
+  struct mib_leaf *leaf;
+  leaf = snmp_walk_init(p->mib_tree, walk, c);
 
-    case AGENTX_TEST_SET_PDU:
-      return parse_test_set_pdu(p, pkt);
-
-    case AGENTX_COMMIT_SET_PDU:
-      return parse_commit_set_pdu(p, pkt);
-
-    case AGENTX_UNDO_SET_PDU:
-      return parse_undo_set_pdu(p, pkt);
-
-    case AGENTX_CLEANUP_SET_PDU:
-      return parse_cleanup_set_pdu(p, pkt);
-
-    default:
-      /* We reset the connection for malformed packet (Unknown packet type) */
-      TRACE(D_PACKETS, "SNMP received PDU with unknown type (%u)", LOAD_U8(h->type));
-      snmp_reset(p);
-      return 0;
-  }
-}
-
-
-/*
- * parse_response - parse an agentx-Response-PDU
- * @p: SNMP protocol instance
- * @res: pointer of agentx-Response-PDU header in RX buffer
- *
- * Return number of bytes parsed from RX buffer.
- */
-static uint
-parse_response(struct snmp_proto *p, byte *res)
-{
-  struct agentx_response *r = (void *) res;
-  struct agentx_header *h = (void *) r;
-
-  uint pkt_size = LOAD_U32(h->payload);
-
-  if (p->ignore_ping_id && LOAD_U32(h->packet_id) == p->ignore_ping_id)
-  {
-    p->pings--;
-    p->ignore_ping_id = 0;
-  }
-
-  /* Number of agentx-Ping-PDU without response */
-  if (p->pings > 5)
-    snmp_reset(p);
-
-  switch (r->error)
-  {
-    case AGENTX_RES_NO_ERROR:
-      if (p->verbose || LOAD_U32(h->packet_id) != p->ignore_ping_id)
-       TRACE(D_PACKETS, "SNMP received agentx-Response-PDU");
-      do_response(p, res);
-      break;
-
-    /* Registration errors */
-    case AGENTX_RES_DUPLICATE_REGISTER:
-    case AGENTX_RES_REQUEST_DENIED:
-    case AGENTX_RES_UNKNOWN_REGISTER:
-      TRACE(D_PACKETS, "SNMP received agentx-Response-PDU with error %u", r->error);
-      snmp_register_ack(p, r);
-      break;
-
-    /*
-     * We found ourselves in an unexpected situation. To enter a well defined
-     * state as well as give the AgentX master agent room to fix the errors on
-     * his side, we perform a hard reset of the connections.
-     */
-    case AGENTX_RES_NOT_OPEN:
-    case AGENTX_RES_OPEN_FAILED:
-    case AGENTX_RES_UNKNOWN_AGENT_CAPS:
-    case AGENTX_RES_UNSUPPORTED_CONTEXT:  /* currently we don't use contexts */
-    case AGENTX_RES_PARSE_ERROR:
-    case AGENTX_RES_PROCESSING_ERR:
-    default:
-      TRACE(D_PACKETS, "SNMP received agentx-Response-PDU with unexepected error %u", r->error);
-      snmp_reset(p);
-      break;
-  }
-
-  return pkt_size + AGENTX_HEADER_SIZE;
-}
-
-/*
- * snmp_register_mibs - register all MIB subtrees
- * @p: SNMP protocol instance
- */
-void
-snmp_register_mibs(struct snmp_proto *p)
-{
-  snmp_bgp4_register(p);
-  ASSUME(!EMPTY_LIST(p->registration_queue));
-}
-
-/*
- * do_response - act on agentx-Response-PDU and protocol state
- * @p: SNMP protocol instance
- * @pkt: RX buffer with PDU bytes
- *
- * Return number of bytes parsed from RX buffer.
- */
-static void
-do_response(struct snmp_proto *p, byte *pkt)
-{
-  struct agentx_response *r = (void *) pkt;
-  struct agentx_header *h = (void *) r;
-
-  switch (p->state)
-  {
-    case SNMP_INIT:
-    case SNMP_LOCKED:
-      /* silent drop of received packet */
-      break;
-
-    case SNMP_OPEN:
-      /* copy session info from received packet */
-      p->session_id = LOAD_U32(h->session_id);
-      refresh_ids(p, h);
-
-      tm_start(p->ping_timer, 0);
-
-      /* the state needs to be changed before sending registering PDUs to
-       * use correct do_response action on them
-       */
-      snmp_set_state(p, SNMP_REGISTER);
-      break;
-
-    case SNMP_REGISTER:;
-      snmp_register_ack(p, r);
-      break;
-
-    case SNMP_CONN:
-      break;
-
-    case SNMP_STOP:
-    case SNMP_DOWN:
-      break;
-
-    default:
-      die("unkonwn SNMP state");
-  }
-}
-
-/*
- * snmp_oid_prefixize_unsafe - normalize OID to prefixed form
- * @dest: destination for normalized OID in native byte order
- * @src: source OID in packet byte order
- *
- * Note that again, snmp_oid_prefixize_unsafe is intended to copy Object
- * Identifier from RX buffer to TX buffer but also optionally swap the byte
- * order from packet b.o. to cpu native b.o. This is done to simplify the code
- * dealing with OIDs.
- */
-static inline void
-snmp_oid_prefixize_unsafe(struct oid *dest, const struct oid *src)
-{
-  dest->n_subid = LOAD_U8(src->n_subid) - 5;
-  dest->prefix = (u8) LOAD_U32(src->ids[ARRAY_SIZE(snmp_internet)]);
-  dest->include = (LOAD_U8(src->include)) ? 1 : 0;
-  dest->reserved = 0;
-
-  /* The LOAD_U32() and STORE_U32() cancel out */
-  for (u8 i = 0; i < dest->n_subid; i++)
-    dest->ids[i] = LOAD_U32(src->ids[i + 5]);
-}
-
-/*
- * snmp_vb_to_tx - create VarBind in TX buffer from RX buffer OID
- * @c: PDU context
- * @oid: Object Identifier located in RX buffer with packet byte order
- *
- * Create a NULL initialized VarBind inside TX buffer (from @c) whose name
- * is @oid. Because we want to simplify code dealing with OIDs, the byte order
- * of the name is optionally swapped to match cpu native byte order.
- */
-struct agentx_varbind *
-snmp_vb_to_tx(struct snmp_pdu *c, const struct oid *oid)
-{
-  uint vb_hdr_size = snmp_varbind_header_size(oid);
-  (void) snmp_tbuf_reserve(c, vb_hdr_size);
-
-  ASSERT(c->size >= vb_hdr_size);
-  struct agentx_varbind *vb = (struct agentx_varbind *) 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 */
-  vb->type = 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));
-    snmp_oid_prefixize_unsafe(&vb->name, oid);
-
-    return vb;
-  }
-
-  ADVANCE(c->buffer, c->size, snmp_oid_size(oid));
-  snmp_oid_from_buf(&vb->name, oid);
-
-  return vb;
-}
-
-/*
- * snmp_varbind_leave - transform VarBind to packet byte order
- * @vb: prepared VarBind in cpu native byte order
- */
-void
-snmp_varbind_leave(struct agentx_varbind *vb)
-{
-  STORE_U16(vb->type, vb->type);
-
-  /* Does nothing */
-  STORE_U16(vb->reserved, 0);
-  struct oid *oid = &vb->name;
-  STORE_U8(oid->n_subid, oid->n_subid);
-  STORE_U8(oid->prefix, oid->prefix);
-  STORE_U8(oid->include, oid->include);
-  STORE_U8(oid->reserved, 0);
-
-  for (u8 i = 0; i < oid->n_subid; i++)
-    STORE_U32(oid->ids[i], oid->ids[i]);
-}
-
-/*
- * update_packet_size - set PDU size
- * @start: pointer to PDU data start (excluding header size)
- * @end: pointer after the last PDU byte
- *
- * Return number of bytes in TX buffer (including header size).
- */
-static inline uint
-update_packet_size(struct agentx_header *start, byte *end)
-{
-  uint s = snmp_pkt_len((byte *) start, end);
-  STORE_U32(start->payload, s);
-  return AGENTX_HEADER_SIZE + s;
-}
-
-/*
- * response_err_ind - update response error and index
- * @p: SNMP protocol instance
- * @res: response PDU header
- * @err: error status
- * @ind: index of error, ignored for noAgentXError
- *
- * Update agentx-Response-PDU header fields res.error and it's res.index. If the
- * error is not noError, also set the corrent response PDU payload size.
- */
-static inline void
-response_err_ind(struct snmp_proto *p, struct agentx_response *res, enum agentx_response_errs err, u16 ind)
-{
-  STORE_U16(res->error, (u16) err);
-  if (err != AGENTX_RES_NO_ERROR && err != AGENTX_RES_GEN_ERROR)
-  {
-    if (p->verbose)
-      TRACE(D_PACKETS, "SNMP last PDU resulted in error %u", err);
-    STORE_U16(res->index, ind);
-    /* Reset VarBindList to null */
-    STORE_U32(res->h.payload,
-      sizeof(struct agentx_response) - AGENTX_HEADER_SIZE);
-  }
-  else if (err == AGENTX_RES_GEN_ERROR)
-  {
-    if (p->verbose)
-      TRACE(D_PACKETS, "SNMP last PDU resulted in error genErr");
-    STORE_U16(res->index, 0);
-    /* Reset VarBindList to null */
-    STORE_U32(res->h.payload,
-      sizeof(struct agentx_response) - AGENTX_HEADER_SIZE);
-  }
-  else
-    STORE_U16(res->index, 0);
-}
-
-/*
- * AgentX GetPDU, GetNextPDU and GetBulkPDU
- */
-
-/* agentx-Get-PDU */
-void
-snmp_get_pdu(struct snmp_proto *p, struct snmp_pdu *c, struct mib_walk_state *walk)
-{
-  struct mib_leaf *leaf;
-  leaf = snmp_walk_init(p->mib_tree, walk, c);
-
-  enum snmp_search_res res;
-  res = snmp_walk_fill(leaf, walk, c);
+  enum snmp_search_res res;
+  res = snmp_walk_fill(leaf, walk, c);
 
   if (res != SNMP_SEARCH_OK)
-    c->sr_vb_start->type = snmp_search_res_to_type(res);
-}
-
-/* agentx-GetNext-PDU */
-int
-snmp_get_next_pdu(struct snmp_proto *p, struct snmp_pdu *c, struct mib_walk_state *walk)
-{
-  (void) snmp_walk_init(p->mib_tree, walk, c);
-  struct mib_leaf *leaf = snmp_walk_next(p->mib_tree, walk, c);
-
-  enum snmp_search_res res;
-  res = snmp_walk_fill(leaf, walk, c);
-
-  if (res != SNMP_SEARCH_OK)
-    c->sr_vb_start->type = AGENTX_END_OF_MIB_VIEW;
-
-  return res == SNMP_SEARCH_OK;
-}
-
-/* agentx-GetBulk-PDU */
-void
-snmp_get_bulk_pdu(struct snmp_proto *p, struct snmp_pdu *c, struct mib_walk_state *walk)
-{
-  /* TODO */
-  (void) p;
-  (void) c;
-  (void) walk;
-  //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);
-}
-
-int
-snmp_load_oids(byte **pkt_ptr, uint *pkt_sz, struct snmp_pdu *c)
-{
-  byte *pkt = *pkt_ptr;
-  uint pkt_size = *pkt_sz;
-
-  uint sz;
-  /* in packet byte order */
-  const struct oid *start_buf = (const struct oid *) pkt;
-  if ((sz = snmp_oid_size(start_buf)) > pkt_size ||
-      LOAD_U8(start_buf->n_subid) >= OID_MAX_LEN)
-  {
-    c->error = AGENTX_RES_PARSE_ERROR;
-    *pkt_ptr = pkt;
-    *pkt_sz = pkt_size;
-    return 0;
-  }
-
-  ADVANCE(pkt, pkt_size, sz);
-
-  /* in packet byte order */
-  const struct oid *end_buf = (const struct oid *) pkt;
-  if ((sz = snmp_oid_size(end_buf)) > pkt_size ||
-      LOAD_U8(end_buf->n_subid) >= OID_MAX_LEN)
-  {
-    c->error = AGENTX_RES_PARSE_ERROR;
-    *pkt_ptr = pkt;
-    *pkt_sz = pkt_size;
-    return 0;
-  }
+    c->sr_vb_start->type = snmp_search_res_to_type(res);
+}
 
-  /* in cpu native byte order */
-  struct agentx_varbind *start_vb = snmp_vb_to_tx(c, start_buf);
+/* agentx-GetNext-PDU */
+int
+snmp_get_next_pdu(struct snmp_proto *p, struct snmp_pdu *c, struct mib_walk_state *walk)
+{
+  (void) snmp_walk_init(p->mib_tree, walk, c);
+  struct mib_leaf *leaf = snmp_walk_next(p->mib_tree, walk, c);
 
-  /* in cpu native byte order */
-  struct oid *end_oid = tmp_alloc(sz);
-  snmp_oid_from_buf(end_oid, end_buf);
+  enum snmp_search_res res;
+  res = snmp_walk_fill(leaf, walk, c);
 
-  ADVANCE(pkt, pkt_size, sz);
+  if (res != SNMP_SEARCH_OK)
+    c->sr_vb_start->type = AGENTX_END_OF_MIB_VIEW;
 
-  if (!snmp_is_oid_empty(end_oid) &&
-      snmp_oid_compare(&start_vb->name, end_oid) > 0)
-  {
-    c->error = AGENTX_RES_GEN_ERROR;
-    *pkt_ptr = pkt;
-    *pkt_sz = pkt_size;
-    return 0;
-  }
+  return res == SNMP_SEARCH_OK;
+}
 
-  ASSERT(start_vb != NULL);
-  ASSERT(end_oid != NULL);
+/* agentx-GetBulk-PDU */
+void
+snmp_get_bulk_pdu(struct snmp_proto *p, struct snmp_pdu *c, struct mib_walk_state *walk)
+{
+  /* TODO */
+  (void) p;
+  (void) c;
+  (void) walk;
+  //if (c->index >= bulk->getbulk.non_repeaters)
+  //  bulk->repeaters++;
 
-  c->sr_vb_start = start_vb;
-  c->sr_o_end = end_oid;
-  *pkt_ptr = pkt;
-  *pkt_sz = pkt_size;
-  return 1; /* ok */
+  // store the o_start and o_end
+
+  //bulk->has_any |= snmp_get_next_pdu(p, c, walk);
 }
 
 /*
@@ -1200,69 +1005,311 @@ parse_gets_pdu(struct snmp_proto *p, byte * const pkt_start)
       return 0;
     }
 
-    switch (h->type)
-    {
-      case AGENTX_GET_PDU:
-       snmp_get_pdu(p, &c, &walk);
-       break;
+    switch (h->type)
+    {
+      case AGENTX_GET_PDU:
+       snmp_get_pdu(p, &c, &walk);
+       break;
+
+      case AGENTX_GET_NEXT_PDU:
+       snmp_get_next_pdu(p, &c, &walk);
+       break;
+
+      case AGENTX_GET_BULK_PDU:
+       snmp_get_bulk_pdu(p, &c, &walk);
+       break;
+
+      default:
+       die("implementation failure");
+    }
+
+    snmp_varbind_leave(c.sr_vb_start);
+
+    c.sr_vb_start = NULL;
+    c.sr_o_end = NULL;
+
+    c.index++;
+  } /* while (c.error == AGENTX_RES_NO_ERROR && size > 0) */
+
+  lp_restore(tmp_linpool, &tmps);
+
+#if 0
+  if (h->type == AGENTX_GET_BULK_PDU)
+  {
+    // TODO: an error for now
+    die("bulk");
+  }
+#endif
+
+  /* We update the error, index pair on the beginning of the packet. */
+  response_err_ind(p, response_header, c.error, c.index + 1);
+  uint s = update_packet_size(&response_header->h, c.buffer);
+
+  /* We send the message in TX buffer. */
+  sk_send(sk, s);
+
+  // TODO think through the error state
+
+  /* number of bytes parsed from RX buffer */
+  return pkt - pkt_start;
+}
+
+/*
+ * do_response - act on agentx-Response-PDU and protocol state
+ * @p: SNMP protocol instance
+ * @pkt: RX buffer with PDU bytes
+ *
+ * Return number of bytes parsed from RX buffer.
+ */
+static void
+do_response(struct snmp_proto *p, byte *pkt)
+{
+  struct agentx_response *r = (void *) pkt;
+  struct agentx_header *h = (void *) r;
+
+  switch (p->state)
+  {
+    case SNMP_INIT:
+    case SNMP_LOCKED:
+      /* silent drop of received packet */
+      break;
+
+    case SNMP_OPEN:
+      /* copy session info from received packet */
+      p->session_id = LOAD_U32(h->session_id);
+      refresh_ids(p, h);
+
+      tm_start(p->ping_timer, 0);
+
+      /* the state needs to be changed before sending registering PDUs to
+       * use correct do_response action on them
+       */
+      snmp_set_state(p, SNMP_REGISTER);
+      break;
+
+    case SNMP_REGISTER:;
+      snmp_register_ack(p, r);
+      break;
+
+    case SNMP_CONN:
+      break;
+
+    case SNMP_STOP:
+    case SNMP_DOWN:
+      break;
+
+    default:
+      die("unkonwn SNMP state");
+  }
+}
+
+/*
+ * parse_response - parse an agentx-Response-PDU
+ * @p: SNMP protocol instance
+ * @res: pointer of agentx-Response-PDU header in RX buffer
+ *
+ * Return number of bytes parsed from RX buffer.
+ */
+static uint
+parse_response(struct snmp_proto *p, byte *res)
+{
+  struct agentx_response *r = (void *) res;
+  struct agentx_header *h = (void *) r;
+
+  uint pkt_size = LOAD_U32(h->payload);
+
+  if (p->ignore_ping_id && LOAD_U32(h->packet_id) == p->ignore_ping_id)
+  {
+    p->pings--;
+    p->ignore_ping_id = 0;
+  }
+
+  /* Number of agentx-Ping-PDU without response */
+  if (p->pings > 5)
+    snmp_reset(p);
+
+  switch (r->error)
+  {
+    case AGENTX_RES_NO_ERROR:
+      if (p->verbose || LOAD_U32(h->packet_id) != p->ignore_ping_id)
+       TRACE(D_PACKETS, "SNMP received agentx-Response-PDU");
+      do_response(p, res);
+      break;
+
+    /* Registration errors */
+    case AGENTX_RES_DUPLICATE_REGISTER:
+    case AGENTX_RES_REQUEST_DENIED:
+    case AGENTX_RES_UNKNOWN_REGISTER:
+      TRACE(D_PACKETS, "SNMP received agentx-Response-PDU with error %u", r->error);
+      snmp_register_ack(p, r);
+      break;
+
+    /*
+     * We found ourselves in an unexpected situation. To enter a well defined
+     * state as well as give the AgentX master agent room to fix the errors on
+     * his side, we perform a hard reset of the connections.
+     */
+    case AGENTX_RES_NOT_OPEN:
+    case AGENTX_RES_OPEN_FAILED:
+    case AGENTX_RES_UNKNOWN_AGENT_CAPS:
+    case AGENTX_RES_UNSUPPORTED_CONTEXT:  /* currently we don't use contexts */
+    case AGENTX_RES_PARSE_ERROR:
+    case AGENTX_RES_PROCESSING_ERR:
+    default:
+      TRACE(D_PACKETS, "SNMP received agentx-Response-PDU with unexepected error %u", r->error);
+      snmp_reset(p);
+      break;
+  }
+
+  return pkt_size + AGENTX_HEADER_SIZE;
+}
+
+/**
+ * parse_pkt - parse received AgentX packet
+ * @p: SNMP protocol instance
+ * @pkt: first byte of PDU inside RX buffer
+ * @size: number of bytes received from a socket
+ *
+ * Return number of bytes parsed from RX buffer.
+ */
+static uint
+parse_pkt(struct snmp_proto *p, byte *pkt, uint size)
+{
+  if (size < AGENTX_HEADER_SIZE)
+    return 0;
+
+  struct agentx_header *h = (struct agentx_header *) pkt;
+  if (h->flags & AGENTX_NETWORK_BYTE_ORDER != SNMP_BYTE_ORDER)
+  {
+    TRACE(D_PACKETS, "SNMP received PDU with unexpected byte order");
+    if (h->type != AGENTX_RESPONSE_PDU)
+      snmp_simple_response(p, AGENTX_RES_GEN_ERROR, 0);
+    snmp_reset(p);
+    return 0;
+  }
+
+  u32 pkt_size = LOAD_U32(h->payload);
+
+  /* RX side checks - too big packet */
+  if (pkt_size > SNMP_PKT_SIZE_MAX)
+  {
+    TRACE(D_PACKETS, "SNMP received PDU is too long");
+    if (h->type != AGENTX_RESPONSE_PDU)
+      snmp_simple_response(p, AGENTX_RES_GEN_ERROR, 0);
+    snmp_reset(p);
+    return 0;
+  }
+
+  /* This guarantees that we have the full packet already received */
+  if (size < pkt_size + AGENTX_HEADER_SIZE)
+    return 0; /* no bytes parsed */
+
+  /*
+   * We need to see the responses for PDU such as
+   * agentx-Open-PDU, agentx-Register-PDU, ...
+   * even when we are outside the SNMP_CONNECTED state
+   */
+  if (h->type == AGENTX_RESPONSE_PDU)
+    return parse_response(p, pkt);
+
+  ASSERT(snmp_is_active(p));
+  if (p->state != SNMP_CONN ||
+      p->session_id != LOAD_U32(h->session_id))
+  {
+    struct agentx_header copy = {
+      .session_id = p->session_id,
+      .transaction_id = p->transaction_id,
+      .packet_id = p->packet_id,
+    };
+
+    TRACE(D_PACKETS, "SNMP received PDU with unknown session id");
+    snmp_simple_response(p, AGENTX_RES_NOT_OPEN, 0);
 
-      case AGENTX_GET_NEXT_PDU:
-       snmp_get_next_pdu(p, &c, &walk);
-       break;
+    p->session_id = copy.session_id;
+    p->transaction_id = copy.transaction_id;
+    p->packet_id = copy.packet_id;
 
-      case AGENTX_GET_BULK_PDU:
-       snmp_get_bulk_pdu(p, &c, &walk);
-       break;
+    /*
+     * After unexpected state, we simply reset the session
+     * only sending the agentx-Response-PDU.
+     */
+    snmp_reset(p);
+    return 0;
+  }
 
-      default:
-       die("implementation failure");
-    }
+  if (h->flags & AGENTX_NON_DEFAULT_CONTEXT)
+  {
+    TRACE(D_PACKETS, "SNMP received PDU with non-default context");
+    snmp_simple_response(p, AGENTX_RES_UNSUPPORTED_CONTEXT, 0);
+    snmp_reset(p);
+    return 0;
+  }
 
-    snmp_varbind_leave(c.sr_vb_start);
+  refresh_ids(p, h);
+  switch (LOAD_U8(h->type))
+  {
+    case AGENTX_GET_PDU:
+      TRACE(D_PACKETS, "SNMP received agentx-Get-PDU");
+      return parse_gets_pdu(p, pkt);
 
-    c.sr_vb_start = NULL;
-    c.sr_o_end = NULL;
+    case AGENTX_GET_NEXT_PDU:
+      TRACE(D_PACKETS, "SNMP received agentx-GetNext-PDU");
+      return parse_gets_pdu(p, pkt);
 
-    c.index++;
-  } /* while (c.error == AGENTX_RES_NO_ERROR && size > 0) */
+    case AGENTX_GET_BULK_PDU:
+      TRACE(D_PACKETS, "SNMP received agentx-GetBulk-PDU");
+      return parse_gets_pdu(p, pkt);
 
-  lp_restore(tmp_linpool, &tmps);
+    case AGENTX_CLOSE_PDU:
+      return parse_close_pdu(p, pkt);
 
-#if 0
-  if (h->type == AGENTX_GET_BULK_PDU)
-  {
-    // TODO: an error for now
-    die("bulk");
-  }
-#endif
+    case AGENTX_TEST_SET_PDU:
+      return parse_test_set_pdu(p, pkt);
 
-  /* We update the error, index pair on the beginning of the packet. */
-  response_err_ind(p, response_header, c.error, c.index + 1);
-  uint s = update_packet_size(&response_header->h, c.buffer);
+    case AGENTX_COMMIT_SET_PDU:
+      return parse_commit_set_pdu(p, pkt);
 
-  /* We send the message in TX buffer. */
-  sk_send(sk, s);
+    case AGENTX_UNDO_SET_PDU:
+      return parse_undo_set_pdu(p, pkt);
 
-  // TODO think through the error state
+    case AGENTX_CLEANUP_SET_PDU:
+      return parse_cleanup_set_pdu(p, pkt);
 
-  /* number of bytes parsed from RX buffer */
-  return pkt - pkt_start;
+    default:
+      /* We reset the connection for malformed packet (Unknown packet type) */
+      TRACE(D_PACKETS, "SNMP received PDU with unknown type (%u)", LOAD_U8(h->type));
+      snmp_reset(p);
+      return 0;
+  }
 }
 
 /*
- * snmp_start_subagent - send session open request
+ * snmp_register_ack - handle registration response
  * @p: SNMP protocol instance
- *
- * Send agentx-Open-PDU with configured OID and string description.
+ * @res: header of agentx-Response-PDU
  */
 void
-snmp_start_subagent(struct snmp_proto *p)
+snmp_register_ack(struct snmp_proto *p, struct agentx_response *res)
 {
-  ASSUME(p->state == SNMP_OPEN);
+  struct snmp_registration *reg;
+  WALK_LIST(reg, p->registration_queue)
+  {
+    if (snmp_registration_match(reg, &res->h))
+    {
+      rem_node(&reg->n);
 
-  /* blank oid means unsupported */
-  STATIC_OID(0) blank = { 0 };
-  open_pdu(p, (struct oid *) &blank);
+      if (res->error == AGENTX_RES_NO_ERROR && reg->reg_hook_ok)
+       reg->reg_hook_ok(p, res, reg);
+      else if (res->error != AGENTX_RES_NO_ERROR && reg->reg_hook_fail)
+       reg->reg_hook_fail(p, res, reg);
+
+      mb_free(reg);
+      break;
+    }
+  }
+
+  if (EMPTY_LIST(p->registration_queue))
+    snmp_up(p);
 }
 
 /*
@@ -1279,6 +1326,33 @@ snmp_stop_subagent(struct snmp_proto *p)
   close_pdu(p, AGENTX_CLOSE_SHUTDOWN);
 }
 
+/*
+ * snmp_register_mibs - register all MIB subtrees
+ * @p: SNMP protocol instance
+ */
+void
+snmp_register_mibs(struct snmp_proto *p)
+{
+  snmp_bgp4_register(p);
+  ASSUME(!EMPTY_LIST(p->registration_queue));
+}
+
+/*
+ * snmp_start_subagent - send session open request
+ * @p: SNMP protocol instance
+ *
+ * Send agentx-Open-PDU with configured OID and string description.
+ */
+void
+snmp_start_subagent(struct snmp_proto *p)
+{
+  ASSUME(p->state == SNMP_OPEN);
+
+  /* blank oid means unsupported */
+  STATIC_OID(0) blank = { 0 };
+  open_pdu(p, (struct oid *) &blank);
+}
+
 /*
  * snmp_rx - handle received PDUs in RX buffer in normal operation
  * @sk: communication socket
@@ -1312,6 +1386,22 @@ snmp_rx(sock *sk, uint size)
   return 0;
 }
 
+/*
+ * space_for_response - check if TX buffer has space for agentx-Response-PDU
+ * @sk: communication socket owned by SNMP protocol instance
+ *
+ * In some cases we send only the AgentX header but if we want to signal an
+ * error, we need at least space for agentx-Response-PDU. This simplifies the
+ * PDU space requirements testing.
+ */
+static inline int
+space_for_response(const sock *sk)
+{
+  return (
+    (uint) (sk->tbuf + sk->tbsize - sk->tpos) >= sizeof(struct agentx_response)
+  );
+}
+
 /*
  * snmp_tx - handle TX buffer
  * @sk: communication socket owned by SNMP protocol instance
@@ -1333,97 +1423,3 @@ snmp_tx(sock *sk)
   snmp_rx(sk, sk->tpos - sk->tbuf);
 }
 
-
-/*
- * snmp_ping - send an agentx-Ping-PDU
- * @p: SNMP protocol instance
- */
-void
-snmp_ping(struct snmp_proto *p)
-{
-  if (!snmp_is_active(p))
-    return;
-
-  sock *sk = p->sock;
-  struct snmp_pdu c;
-  snmp_pdu_context(&c, p, sk);
-
-  if (c.size < AGENTX_HEADER_SIZE)
-    return;
-
-  int unused = sk->tbuf + sk->tbsize - c.buffer;
-  if (unused < AGENTX_HEADER_SIZE)
-    return;
-
-  struct agentx_header *h = (void *) c.buffer;
-  ADVANCE(c.buffer, c.size, AGENTX_HEADER_SIZE);
-  snmp_blank_header(h, AGENTX_PING_PDU);
-  p->packet_id++;
-  snmp_session(p, h);
-  if (p->verbose)
-    TRACE(D_PACKETS, "SNMP sending agentx-Ping-PDU");
-  p->ignore_ping_id = p->packet_id;
-
-  /* sending only header */
-  uint s = update_packet_size(h, (byte *) h + AGENTX_HEADER_SIZE);
-
-  if (p->packet_id)
-    p->pings++;
-  sk_send(sk, s);
-}
-
-/*
- * snmp_tbuf_reserve - conditionally grow the TX buffer
- * @c: transmit PDU context
- * @size: size to make available
- *
- * Return non-zero if the buffer was relocated.
- */
-int
-snmp_tbuf_reserve(struct snmp_pdu *c, size_t size)
-{
-  if (size >= c->size)
-  {
-    struct snmp_proto *p = c->p;
-    sock *sk = p->sock;
-
-    int start_diff;
-    if (c->sr_vb_start != NULL)
-      start_diff = (char *) c->sr_vb_start - (char *) sk->tbuf;
-
-    sk_set_tbsize(sk, sk->tbsize + 2048);
-    c->size += 2048;
-
-    if (c->sr_vb_start != NULL)
-      c->sr_vb_start = (struct agentx_varbind *) (sk->tbuf + start_diff);
-
-    return 1;
-  }
-
-  return 0;
-}
-
-/*
- * prepare_response - fill buffer with AgentX PDU header
- * @p: SNMP protocol instance
- * @c: transmit PDU context to use
- *
- * Prepare known parts of AgentX packet header into the TX buffer held by @c.
- */
-static struct agentx_response *
-prepare_response(struct snmp_proto *p, struct snmp_pdu *c)
-{
-  struct agentx_response *r = (void *) c->buffer;
-  struct agentx_header *h = &r->h;
-
-  snmp_blank_header(h, AGENTX_RESPONSE_PDU);
-  snmp_session(p, h);
-
-  /* protocol doesn't care about subagent upTime */
-  STORE_U32(r->uptime, 0);
-  STORE_U16(r->error, AGENTX_RES_NO_ERROR);
-  STORE_U16(r->index, 0);
-
-  ADVANCE(c->buffer, c->size, sizeof(struct agentx_response));
-  return r;
-}
index 3e922d929e716c59a24bdc5a18e50b496fe3e8c2..18bd1df11719a935eb9423566944ce02f0bc7422 100644 (file)
@@ -6,10 +6,6 @@
 #include "snmp.h"
 #include "lib/macro.h"
 
-void snmp_start_subagent(struct snmp_proto *p);
-void snmp_stop_subagent(struct snmp_proto *p);
-void snmp_ping(struct snmp_proto *p);
-
 #define AGENTX_VERSION              1
 
 /* standard snmp internet prefix */
@@ -196,11 +192,6 @@ struct agentx_varbind {
   /* AgentX variable binding data optionally here */
 };
 
-struct agentx_search_range {
-  struct oid *start;
-  struct oid *end;
-};
-
 /* AgentX Octet String */
 struct agentx_octet_str {
   u32 length;
@@ -333,6 +324,7 @@ struct snmp_pdu {
   u32 index;                       /* index on which the error was found */
 };
 
+#if 0
 struct snmp_packet_info {
   node n;
   u8 type; // enum type
@@ -341,14 +333,18 @@ struct snmp_packet_info {
   u32 packet_id;
   void *data;
 };
+#endif
 
+void snmp_start_subagent(struct snmp_proto *p);
+void snmp_stop_subagent(struct snmp_proto *p);
+void snmp_ping(struct snmp_proto *p);
 int snmp_rx(sock *sk, uint size);
 void snmp_tx(sock *sk);
-int snmp_rx_stop(sock *sk, uint size);
 void snmp_register(struct snmp_proto *p, struct oid *oid, uint index, uint len, u8 is_instance);
 void snmp_unregister(struct snmp_proto *p, struct oid *oid, uint index, uint len);
 void snmp_notify_pdu(struct snmp_proto *p, struct oid *oid, void *data, uint size, int include_uptime);
-
+void snmp_register_mibs(struct snmp_proto *p);
+struct agentx_varbind *snmp_vb_to_tx(struct snmp_pdu *c, const struct oid *oid);
 int snmp_tbuf_reserve(struct snmp_pdu *c, size_t bytes);
 
 static inline int
@@ -359,9 +355,5 @@ snmp_is_active(const struct snmp_proto *p)
       p->state == SNMP_CONN;
 }
 
-struct agentx_varbind *snmp_vb_to_tx(struct snmp_pdu *c, const struct oid *oid);
-u8 snmp_get_mib_class(const struct oid *oid);
-
-void snmp_register_mibs(struct snmp_proto *p);
 
 #endif