]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
SNMP: dead end
authorVojtech Vilimek <vojtech.vilimek@nic.cz>
Wed, 10 Jan 2024 11:21:46 +0000 (12:21 +0100)
committerVojtech Vilimek <vojtech.vilimek@nic.cz>
Wed, 10 Jan 2024 11:21:46 +0000 (12:21 +0100)
The code contains hard to debug bug where we periodically do some kind of error.
The problem is caused by weird values after the AgentX PDU header, exactly where
the first OID should lie. We expend value less than 15 but we found values like
0x00003b47. We found possible cause on assignment to socket's receive buffer
position in proto/snmp/subagent.c:snmp_rx at line 1569. An erroneous behavior
may have been caused by off-by-on error. More investigation is needed to gain
full picture.

To resolve this issue we use much more simpler approach. We will set max packet
size and wait until whole packet has arrived.

proto/snmp/bgp_mib.c
proto/snmp/bgp_mib.h
proto/snmp/config.Y
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 7eb7d61368b728501be21c7ef8094e3001dd83e0..c55af2901a4254246056f3df3d282a136babab8f 100644 (file)
 #include "subagent.h"
 #include "bgp_mib.h"
 
+STATIC_ASSERT(BGP_MIB_IDLE == BS_IDLE + 1);
+STATIC_ASSERT(BGP_MIB_CONNECT == BS_CONNECT + 1);
+STATIC_ASSERT(BGP_MIB_ACTIVE == BS_ACTIVE + 1);
+STATIC_ASSERT(BGP_MIB_OPENSENT == BS_OPENSENT + 1);
+STATIC_ASSERT(BGP_MIB_OPENCONFIRM == BS_OPENCONFIRM + 1);
+STATIC_ASSERT(BGP_MIB_ESTABLISHED == BS_ESTABLISHED + 1);
+
 /* hash table macros */
 #define SNMP_HASH_KEY(n)  n->peer_ip
 #define SNMP_HASH_NEXT(n) n->next
 
 static inline void ip4_to_oid(struct oid *oid, ip4_addr addr);
 
-/* BGP_MIB states see enum BGP_INTERNAL_STATES */
-static const char * const debug_bgp_states[] UNUSED = {
-  [BGP_INTERNAL_INVALID]                = "BGP_INTERNAL_INVALID",
-  [BGP_INTERNAL_BGP]                    = "BGP_INTERNAL_BGP",
-  [BGP_INTERNAL_VERSION]                = "BGP_INTERNAL_VERSION",
-  [BGP_INTERNAL_LOCAL_AS]               = "BGP_INTERNAL_LOCAL_AS",
-  [BGP_INTERNAL_PEER_TABLE]             = "BGP_INTERNAL_PEER_TABLE",
-  [BGP_INTERNAL_PEER_ENTRY]             = "BGP_INTERNAL_PEER_ENTRY",
-  [BGP_INTERNAL_PEER_IDENTIFIER]        = "BGP_INTERNAL_PEER_IDENTIFIER",
-  [BGP_INTERNAL_STATE]                  = "BGP_INTERNAL_STATE",
-  [BGP_INTERNAL_ADMIN_STATUS]           = "BGP_INTERNAL_ADMIN_STATUS",
-  [BGP_INTERNAL_NEGOTIATED_VERSION]     = "BGP_INTERNAL_NEGOTIATED_VERSION",
-  [BGP_INTERNAL_LOCAL_ADDR]             = "BGP_INTERNAL_LOCAL_ADDR",
-  [BGP_INTERNAL_LOCAL_PORT]             = "BGP_INTERNAL_LOCAL_PORT",
-  [BGP_INTERNAL_REMOTE_ADDR]            = "BGP_INTERNAL_REMOTE_ADDR",
-  [BGP_INTERNAL_REMOTE_PORT]            = "BGP_INTERNAL_REMOTE_PORT",
-  [BGP_INTERNAL_REMOTE_AS]              = "BGP_INTERNAL_REMOTE_AS",
-  [BGP_INTERNAL_RX_UPDATES]             = "BGP_INTERNAL_RX_UPDATES",
-  [BGP_INTERNAL_TX_UPDATES]             = "BGP_INTERNAL_TX_UPDATES",
-  [BGP_INTERNAL_RX_MESSAGES]            = "BGP_INTERNAL_RX_MESSAGES",
-  [BGP_INTERNAL_TX_MESSAGES]            = "BGP_INTERNAL_TX_MESSAGES",
-  [BGP_INTERNAL_LAST_ERROR]             = "BGP_INTERNAL_LAST_ERROR",
-  [BGP_INTERNAL_FSM_TRANSITIONS]        = "BGP_INTERNAL_FSM_TRANSITIONS",
-  [BGP_INTERNAL_FSM_ESTABLISHED_TIME]   = "BGP_INTERNAL_FSM_ESTABLISHED_TIME",
-  [BGP_INTERNAL_RETRY_INTERVAL]                 = "BGP_INTERNAL_RETRY_INTERVAL",
-  [BGP_INTERNAL_HOLD_TIME]              = "BGP_INTERNAL_HOLD_TIME",
-  [BGP_INTERNAL_KEEPALIVE]              = "BGP_INTERNAL_KEEPALIVE",
-  [BGP_INTERNAL_HOLD_TIME_CONFIGURED]   = "BGP_INTERNAL_HOLD_TIME_CONFIGURED",
-  [BGP_INTERNAL_KEEPALIVE_CONFIGURED]   = "BGP_INTERNAL_KEEPALIVE_CONFIGURED",
-  [BGP_INTERNAL_ORIGINATION_INTERVAL]   = "BGP_INTERNAL_ORIGINATION_INTERVAL",
-  [BGP_INTERNAL_MIN_ROUTE_ADVERTISEMENT] = "BGP_INTERNAL_MIN_ROUTE_ADVERTISEMENT",
-  [BGP_INTERNAL_IN_UPDATE_ELAPSED_TIME]         = "BGP_INTERNAL_IN_UPDATE_ELAPSED_TIME",
-  [BGP_INTERNAL_PEER_TABLE_END]                 = "BGP_INTERNAL_PEER_TABLE_END",
-  [BGP_INTERNAL_IDENTIFIER]             = "BGP_INTERNAL_IDENTIFIER",
-  [BGP_INTERNAL_END]                    = "BGP_INTERNAL_END",
-  [BGP_INTERNAL_NO_VALUE]               = "BGP_INTERNAL_NO_VALUE",
-};
+
+static inline void
+snmp_hash_add_peer(struct snmp_proto *p, struct snmp_bgp_peer *peer)
+{
+  HASH_INSERT(p->bgp_hash, SNMP_HASH, peer);
+}
+
+static inline struct snmp_bgp_peer *
+snmp_hash_find(struct snmp_proto *p, ip4_addr key)
+{
+  return HASH_FIND(p->bgp_hash, SNMP_HASH, key);
+}
+
+static inline void
+snmp_bgp_last_error(const struct bgp_proto *bgp, char err[2])
+{
+  err[0] = bgp->last_error_code & 0x00FF0000 >> 16;
+  err[1] = bgp->last_error_code & 0x000000FF;
+}
 
 static u8
 bgp_get_candidate(u32 field)
@@ -107,25 +96,6 @@ bgp_get_candidate(u32 field)
     return BGP_INTERNAL_PEER_TABLE_END;
 }
 
-static inline void
-snmp_hash_add_peer(struct snmp_proto *p, struct snmp_bgp_peer *peer)
-{
-  HASH_INSERT(p->bgp_hash, SNMP_HASH, peer);
-}
-
-static inline struct snmp_bgp_peer *
-snmp_hash_find(struct snmp_proto *p, ip4_addr key)
-{
-  return HASH_FIND(p->bgp_hash, SNMP_HASH, key);
-}
-
-static inline void
-snmp_bgp_last_error(const struct bgp_proto *bgp, char err[2])
-{
-  err[0] = bgp->last_error_code & 0x00FF0000 >> 16;
-  err[1] = bgp->last_error_code & 0x000000FF;
-}
-
 /**
  * snmp_bgp_state - linearize oid from BGP4-MIB
  * @oid: prefixed object identifier from BGP4-MIB::bgp subtree
@@ -252,12 +222,13 @@ snmp_bgp_reg_failed(struct snmp_proto *p, struct agentx_response UNUSED *r, stru
 static void
 snmp_bgp_notify_common(struct snmp_proto *p, uint type, ip4_addr ip4, char last_error[], uint state_val)
 {
-  // TODO remove heap allocation, put the data on stack
-
 #define SNMP_OID_SIZE_FROM_LEN(x) (sizeof(struct oid) + (x) * sizeof(u32))
 
+  /* OIDs, VB type headers, octet string, ip4 address, integer */
+  uint sz = 3 * SNMP_OID_SIZE_FROM_LEN(9) + 3 * 4 + 8 + 8 + 4;
+
   /* trap OID bgpEstablishedNotification (.1.3.6.1.2.1.0.1) */
-  struct oid *head = mb_alloc(p->pool, SNMP_OID_SIZE_FROM_LEN(3));
+  struct oid *head = mb_alloc(p->pool, SNMP_OID_SIZE_FROM_LEN(3)) + sz;
   head->n_subid = 3;
   head->prefix = 2;
   head->include = head->pad = 0;
@@ -266,15 +237,13 @@ snmp_bgp_notify_common(struct snmp_proto *p, uint type, ip4_addr ip4, char last_
   for (uint i = 0; i < head->n_subid; i++)
     head->ids[i] = trap_ids[i];
 
-  /* OIDs, VB type headers, octet string, ip4 address, integer */
-  uint sz = 3 * SNMP_OID_SIZE_FROM_LEN(9) + 3 * 4 + 8 + 8 + 4;
 
-  /* Paylaod OIDs */
+  void *data = (void *) head + sz;
 
-  void *data = mb_alloc(p->pool, sz);
   struct agentx_varbind *addr_vb = data;
+  // TODO remove magic constants; use measuring functions instead
   /* +4 for varbind header, +8 for octet string */
-  struct agentx_varbind *error_vb = data + SNMP_OID_SIZE_FROM_LEN(9)  + 4 + 8;
+  struct agentx_varbind *error_vb = data + SNMP_OID_SIZE_FROM_LEN(9) + 4 + 8;
   struct agentx_varbind *state_vb = (void *) error_vb + SNMP_OID_SIZE_FROM_LEN(9) + 4 + 8;
 
   addr_vb->pad = error_vb->pad = state_vb->pad = 0;
@@ -330,7 +299,7 @@ snmp_bgp_fsm_state(const struct bgp_proto *bgp_proto)
   const struct bgp_conn *bgp_out = &bgp_proto->outgoing_conn;
 
   if (bgp_conn)
-    return bgp_conn->state;
+    return bgp_conn->state + 1;
 
   if (MAX(bgp_in->state, bgp_out->state) == BS_CLOSE &&
       MIN(bgp_in->state, bgp_out->state) != BS_CLOSE)
@@ -512,7 +481,7 @@ snmp_bgp_has_value(u8 state)
  * @state: BGP linearized state
  *
  * Returns @state if has value in BGP4-MIB, zero otherwise. Used for Get-PDU
- * ackets.
+ * packets.
  */
 u8
 snmp_bgp_get_valid(u8 state)
@@ -753,7 +722,6 @@ update_bgp_oid(struct oid *oid, u8 state)
 #undef SNMP_UPDATE_CASE
 }
 
-// TODO test bgp_find_dynamic_oid
 static struct oid *
 bgp_find_dynamic_oid(struct snmp_proto *p, struct oid *o_start, const struct oid *o_end, u8 start_state)
 {
@@ -782,20 +750,20 @@ bgp_find_dynamic_oid(struct snmp_proto *p, struct oid *o_start, const struct oid
 
   trie_walk_init(&ws, p->bgp_trie, NULL, 0);
 
-  if (trie_walk_next(&ws, &net))
+  if (!trie_walk_next(&ws, &net))
+    return NULL;
+
+  /*
+   * If the o_end is empty, then there are no conditions on the ip4 address.
+   */
+  int cmp = ip4_compare(net4_prefix(&net), dest);
+  if (cmp < 0 || (cmp == 0 && snmp_is_oid_empty(o_end)))
   {
-    /*
-     * If the o_end is empty, then there are no conditions on the ip4 address.
-     */
-    int cmp = ip4_compare(net4_prefix(&net), dest);
-    if (cmp < 0 || (cmp == 0 && snmp_is_oid_empty(o_end)))
-    {
-      // TODO repair
-      struct oid *o = snmp_oid_duplicate(p->pool, o_start);
-      snmp_oid_ip4_index(o, 5, net4_prefix(&net));
+    // TODO repair
+    struct oid *o = snmp_oid_duplicate(p->pool, o_start);
+    snmp_oid_ip4_index(o, 5, net4_prefix(&net));
 
-      return o;
-    }
+    return o;
   }
 
   return NULL;
@@ -1005,8 +973,6 @@ bgp_fill_dynamic(struct snmp_proto UNUSED *p, struct agentx_varbind *vb,
     return pkt;
   }
 
-  // TODO XXX deal with possible change of (remote) ip; BGP should restart and
-  // disappear
   struct snmp_bgp_peer *pe = snmp_hash_find(p, addr);
 
   if (!pe)
@@ -1018,7 +984,6 @@ bgp_fill_dynamic(struct snmp_proto UNUSED *p, struct agentx_varbind *vb,
   const struct bgp_proto *bgp_proto = pe->bgp_proto;
   if (!ipa_is_ip4(bgp_proto->remote_ip))
   {
-    // TODO XXX: serious issue here
     log(L_ERR, "%s: Found BGP protocol instance with IPv6 address", bgp_proto->p.name);
     vb->type = AGENTX_NO_SUCH_INSTANCE;
     c->error = AGENTX_RES_GEN_ERROR;
@@ -1028,7 +993,6 @@ bgp_fill_dynamic(struct snmp_proto UNUSED *p, struct agentx_varbind *vb,
   ip4_addr proto_ip = ipa_to_ip4(bgp_proto->remote_ip);
   if (!ip4_equal(proto_ip, pe->peer_ip))
   {
-    // TODO XXX:
     /* 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);
@@ -1041,21 +1005,21 @@ bgp_fill_dynamic(struct snmp_proto UNUSED *p, struct agentx_varbind *vb,
   const struct bgp_stats *bgp_stats = &bgp_proto->stats;
   const struct bgp_config *bgp_conf = bgp_proto->cf;
 
-  uint bgp_state = snmp_bgp_fsm_state(bgp_proto);
+  uint fsm_state = snmp_bgp_fsm_state(bgp_proto);
 
   char last_error[2];
   snmp_bgp_last_error(bgp_proto, last_error);
   switch (state)
   {
     case BGP_INTERNAL_PEER_IDENTIFIER:
-      if (bgp_state == BS_OPENCONFIRM || bgp_state == BS_ESTABLISHED)
+      if (fsm_state == BGP_MIB_OPENCONFIRM || fsm_state == BGP_MIB_ESTABLISHED)
        pkt = snmp_varbind_ip4(vb, size, ip4_from_u32(bgp_proto->remote_id));
       else
        pkt = snmp_varbind_ip4(vb, size, IP4_NONE);
       break;
 
     case BGP_INTERNAL_STATE:
-      pkt = snmp_varbind_int(vb, size, bgp_state);
+      pkt = snmp_varbind_int(vb, size, fsm_state);
       break;
 
     case BGP_INTERNAL_ADMIN_STATUS:
@@ -1067,7 +1031,7 @@ bgp_fill_dynamic(struct snmp_proto UNUSED *p, struct agentx_varbind *vb,
       break;
 
     case BGP_INTERNAL_NEGOTIATED_VERSION:
-      if (bgp_state == BS_OPENCONFIRM || bgp_state == BS_ESTABLISHED)
+      if (fsm_state == BGP_MIB_ESTABLISHED || fsm_state == BGP_MIB_ESTABLISHED)
        pkt = snmp_varbind_int(vb, size, SNMP_BGP_NEGOTIATED_VER_VALUE);
       else
        pkt = snmp_varbind_int(vb, size, SNMP_BGP_NEGOTIATED_VER_NO_VALUE);
@@ -1075,7 +1039,6 @@ bgp_fill_dynamic(struct snmp_proto UNUSED *p, struct agentx_varbind *vb,
       break;
 
     case BGP_INTERNAL_LOCAL_ADDR:
-      // TODO XXX bgp_proto->link_addr & zero local_ip
       pkt = snmp_varbind_ip4(vb, size, ipa_to_ip4(bgp_proto->local_ip));
       break;
 
@@ -1235,8 +1198,7 @@ UNUSED, uint contid UNUSED, u8 state)
       break;
 
     case BGP_INTERNAL_IDENTIFIER:
-      // TODO make a check
-      pkt = snmp_varbind_ip4(vb, size, ipa_to_ip4(p->bgp_local_id));
+      pkt = snmp_varbind_ip4(vb, size, p->bgp_local_id);
       break;
 
     default:
index c3eaa98721b3dfdc13d3ae0439264b83042d8aa4..4fcd96aebb624d4a664fbea0f053feed8fabbef1 100644 (file)
@@ -33,9 +33,12 @@ enum BGP4_MIB_PEER_TABLE {
 } PACKED;
 
 /* version of BGP, here BGP-4 */
+#define BGP4_VERSIONS ((char[]) { 0x10 }) /* OID bgp.bgpVersion */
+/* for OID bgp.bgpPeerTable.bgpPeerEntry.bgpPeerNegotiatedVersion */
 #define SNMP_BGP_NEGOTIATED_VER_VALUE 4
 #define SNMP_BGP_NEGOTIATED_VER_NO_VALUE 0
 
+
 void snmp_bgp_register(struct snmp_proto *p);
 void snmp_bgp_reg_ok(struct snmp_proto *p, struct agentx_response *r, struct oid *oid);
 void snmp_bgp_reg_failed(struct snmp_proto *p, struct agentx_response *r, struct oid *oid);
@@ -96,4 +99,13 @@ enum BGP_INTERNAL_STATES {
   BGP_INTERNAL_NO_VALUE = 255,
 } PACKED;
 
+enum bgp4_mib_bgp_states {
+  BGP_MIB_IDLE = 1,
+  BGP_MIB_CONNECT = 2,
+  BGP_MIB_ACTIVE = 3,
+  BGP_MIB_OPENSENT = 4,
+  BGP_MIB_OPENCONFIRM = 5,
+  BGP_MIB_ESTABLISHED = 6,
+};
+
 #endif
index 05e7cf38d0d1f750057eaef651e01d418bc68b1a..9067cf80c0fe3070c71bdc2a4085276ef4e4a593 100644 (file)
@@ -40,10 +40,10 @@ snmp_proto_item:
     if (($3 < 1) || ($3 > 65535)) cf_error("Invalid port number");
     SNMP_CFG->remote_port = $3;
   }
- | LOCAL ID ipa { SNMP_CFG->bgp_local_id = $3; }
- | LOCAL ADDRESS ipa { SNMP_CFG->local_ip = $3; }
- | REMOTE ADDRESS ipa {
-    if (ipa_zero($3)) cf_error("Invalid remote ip address");
+ | LOCAL ID IP4 { SNMP_CFG->bgp_local_id = $3; }
+ | LOCAL ADDRESS IP4 { SNMP_CFG->local_ip = $3; }
+ | REMOTE ADDRESS IP4 {
+    if (ip4_zero($3)) cf_error("Invalid remote ip address");
     SNMP_CFG->remote_ip = $3;
   }
  | LOCAL AS expr {
@@ -86,9 +86,9 @@ snmp_proto_start: proto_start SNMP
   init_list(&SNMP_CFG->bgp_entries);
   SNMP_CFG->bonds = 0;
 
-  SNMP_CFG->local_ip = IPA_NONE;
-  SNMP_CFG->remote_ip = ipa_build4(127,0,0,1);
-  SNMP_CFG->bgp_local_id = IPA_NONE;
+  SNMP_CFG->local_ip = IP4_NONE;
+  SNMP_CFG->remote_ip = ip4_build(127,0,0,1);
+  SNMP_CFG->bgp_local_id = IP4_NONE;
   SNMP_CFG->local_port = 0;
   SNMP_CFG->remote_port = 705;
   SNMP_CFG->bgp_local_as = 0;
index 85fcd797cc52f28d899a06f659a60929660ba653..fbff7270bb6f9b65635722d73b0246547b899e4e 100644 (file)
@@ -1,6 +1,5 @@
 /*
- *     BIRD -- Simple Network Management Protocol (SNMP)
- *
+ *     BIRD -- Simple Network Management Protocol (SNMP) *
  *      (c) 2022 Vojtech Vilimek <vojtech.vilimek@nic.cz>
  *      (c) 2022 CZ.NIC z.s.p.o.
  *
@@ -135,11 +134,12 @@ static void snmp_cleanup(struct snmp_proto *p);
 static int
 snmp_rx_skip(sock UNUSED *sk, uint UNUSED size)
 {
+  log(L_INFO "snmp_rx_skip with size %u", size);
   return 1;
 }
 
 /*
- * snmp_tx - handle empty TX-buffer during session reset
+ * snmp_tx_skip - handle empty TX-buffer during session reset
  * @sk: communication socket
  *
  * The socket tx_hook is called when the TX-buffer is empty, i.e. all data was
@@ -147,10 +147,11 @@ snmp_rx_skip(sock UNUSED *sk, uint UNUSED size)
  * resetting the established session. If called we direcly reset the session.
  */
 static void
-snmp_tx(sock *sk)
+snmp_tx_skip(sock *sk)
 {
+  log(L_INFO "snmp_tx_skip()");
   struct snmp_proto *p = sk->data;
-  snmp_sock_disconnect(p, 1);
+  snmp_set_state(p, SNMP_DOWN);
 }
 
 /*
@@ -168,11 +169,15 @@ snmp_set_state(struct snmp_proto *p, enum snmp_proto_state state)
   p->state = state;
 
   if (last == SNMP_RESET)
+  {
     rfree(p->sock);
+    p->sock = NULL;
+  }
 
   switch (state)
   {
   case SNMP_INIT:
+    debug("snmp -> SNMP_INIT\n");
     ASSERT(last == SNMP_DOWN);
     struct object_lock *lock;
     lock = p->lock = olock_new(p->pool);
@@ -190,11 +195,13 @@ snmp_set_state(struct snmp_proto *p, enum snmp_proto_state state)
     break;
 
   case SNMP_LOCKED:
+    debug("snmp -> SNMP_LOCKED\n");
     ASSERT(last == SNMP_INIT || SNMP_RESET);
+    snmp_unset_header(p);
     sock *s = sk_new(p->pool);
     s->type = SK_TCP_ACTIVE;
-    s->saddr = p->local_ip;
-    s->daddr = p->remote_ip;
+    s->saddr = ipa_from_ip4(p->local_ip);
+    s->daddr = ipa_from_ip4(p->remote_ip);
     s->dport = p->remote_port;
     s->rbsize = SNMP_RX_BUFFER_SIZE;
     s->tbsize = SNMP_TX_BUFFER_SIZE;
@@ -216,32 +223,40 @@ snmp_set_state(struct snmp_proto *p, enum snmp_proto_state state)
     break;
 
   case SNMP_OPEN:
+    debug("snmp -> SNMP_OPEN\n");
     ASSERT(last == SNMP_LOCKED);
     p->sock->rx_hook = snmp_rx;
     p->sock->tx_hook = NULL;
+    log(L_WARN " Staring subagent %u %u %u %u", p->header_offset, p->last_index, p->last_size, p->last_pkt_id);
     snmp_start_subagent(p);
     // handle no response (for long time)
     break;
 
   case SNMP_REGISTER:
+    debug("snmp -> SNMP_REGISTER\n");
     ASSERT(last == SNMP_OPEN);
     snmp_register_mibs(p);
     break;
 
   case SNMP_CONN:
+    debug("snmp -> SNMP_CONN\n");
     ASSERT(last == SNMP_REGISTER);
     proto_notify_state(&p->p, PS_UP);
     break;
 
   case SNMP_STOP:
+    debug("snmp -> SNMP_STOP\n");
     ASSUME(last == SNMP_REGISTER || last == SNMP_CONN);
     snmp_stop_subagent(p);
+    p->sock->rx_hook = snmp_rx_skip;
+    p->sock->tx_hook = snmp_tx_skip;
     p->startup_timer->hook = snmp_stop_timeout;
     tm_start(p->startup_timer, p->timeout);
     proto_notify_state(&p->p, PS_STOP);
     break;
 
   case SNMP_DOWN:
+    debug("snmp -> SNMP_DOWN\n");
     //ASSUME(last == SNMP_STOP || SNMP_INIT || SNMP_LOCKED || SNMP_OPEN);
     snmp_cleanup(p);
     // FIXME: handle the state in which we call proto_notify_state and
@@ -250,10 +265,11 @@ snmp_set_state(struct snmp_proto *p, enum snmp_proto_state state)
     break;
 
   case SNMP_RESET:
+    debug("snmp -> SNMP_RESET\n");
     ASSUME(last == SNMP_REGISTER || last == SNMP_CONN);
-    ASSERT(p->sock);
+    ASSUME(p->sock);
     p->sock->rx_hook = snmp_rx_skip;
-    p->sock->tx_hook = snmp_tx;
+    p->sock->tx_hook = snmp_tx_skip;
     break;
 
   default:
@@ -276,8 +292,7 @@ snmp_init(struct proto_config *CF)
 
   p->rl_gen = (struct tbf) TBF_DEFAULT_LOG_LIMITS;
 
-  /* default starting state */
-  //p->state = SNMP_DOWN; // (0)
+  p->state = SNMP_DOWN;
 
   return P;
 }
@@ -380,10 +395,6 @@ snmp_sock_disconnect(struct snmp_proto *p, int reconnect)
     return;
   }
 
-  //proto_notify_state(&p->p, PS_START);
-  rfree(p->sock);
-  p->sock = NULL;
-
   // TODO - wouldn't be better to use inter jump state RESET (soft reset) ?
   snmp_set_state(p, SNMP_DOWN);
 
@@ -419,7 +430,6 @@ static void
 snmp_start_locked(struct object_lock *lock)
 {
   struct snmp_proto *p = lock->data;
-log(L_INFO "SNMP startup timer or btime %t ? %ld", p->startup_delay,   p->startup_timer);
   if (p->startup_delay)
   {
     ASSERT(p->startup_timer);
@@ -445,6 +455,9 @@ snmp_reconnect(timer *tm)
   // TODO
   snmp_set_state(p, SNMP_DOWN);
   return;
+  /* reset the connection */
+  snmp_sock_disconnect(p, 1);
+  // TODO better snmp_set_state(SNMP_DOWM); ??
   if (p->state == SNMP_STOP ||
       p->state == SNMP_DOWN)
     return;
@@ -589,30 +602,22 @@ snmp_start(struct proto *P)
 
   snmp_bgp_start(p);
 
-  p->last_header = NULL;
+  snmp_unset_header(p);
 
   snmp_set_state(p, SNMP_INIT);
 
   return PS_START;
 }
 
-/*
- * snmp_reconfigure - Test if SNMP instance is reconfigurable
- * @P - SNMP protocol generic handle, current state
- * @CF - SNMP protocol configuration generic handle carring new values
- *
- * We accept the reconfiguration if the new configuration @CF is identical with
- * the currently deployed. Otherwise we deny reconfiguration because
- * the implementation would be cumbersome.
- */
-static int
-snmp_reconfigure(struct proto *P, struct proto_config *CF)
+static inline int
+snmp_reconfigure_logic(struct snmp_proto *p, const struct snmp_config *new)
 {
-  // remove lost bonds, add newly created
-  struct snmp_proto *p = SKIP_BACK(struct snmp_proto, p, P);
-  const struct snmp_config *new = SKIP_BACK(struct snmp_config, cf, CF);
   const struct snmp_config *old = SKIP_BACK(struct snmp_config, cf, p->p.cf);
 
+  if (old->bonds != new->bonds)
+    return 0;
+
+  uint bonds = old->bonds;
   struct snmp_bond *b1, *b2;
   WALK_LIST(b1, new->bgp_entries)
   {
@@ -623,9 +628,13 @@ snmp_reconfigure(struct proto *P, struct proto_config *CF)
     }
 
     return 0;
-skip:;
+skip:
+    bonds--;
   }
 
+  if (bonds != 0)
+    return 0;
+
   return !memcmp(((byte *) old) + sizeof(struct proto_config),
       ((byte *) new) + sizeof(struct proto_config),
       OFFSETOF(struct snmp_config, description) - sizeof(struct proto_config))
@@ -633,8 +642,36 @@ skip:;
 }
 
 /*
- * snmp_show_proto_info - Print basic information about SNMP protocol instance
- * @P - SNMP protocol generic handle
+ * snmp_reconfigure - Test if SNMP instance is reconfigurable
+ * @P - SNMP protocol generic handle, current state
+ * @CF - SNMP protocol configuration generic handle carring new values
+ *
+ * We accept the reconfiguration if the new configuration @CF is identical with
+ * the currently deployed. Otherwise we deny reconfiguration because
+ * the implementation would be cumbersome.
+ */
+static int
+snmp_reconfigure(struct proto *P, struct proto_config *CF)
+{
+  // remove lost bonds, add newly created
+  struct snmp_proto *p = SKIP_BACK(struct snmp_proto, p, P);
+  const struct snmp_config *new = SKIP_BACK(struct snmp_config, cf, CF);
+
+  int retval = snmp_reconfigure_logic(p, new);
+
+  if (retval) {
+    /* Reinitialize the hash after snmp_shutdown() */
+    HASH_INIT(p->bgp_hash, p->pool, 10);
+    snmp_bgp_start(p);
+    snmp_unset_header(p);
+  }
+
+  return retval;
+}
+
+/*
+ * snmp_show_proto_info - print basic information about SNMP protocol instance
+ * @P: SNMP protocol generic handle
  */
 static void
 snmp_show_proto_info(struct proto *P)
@@ -646,22 +683,26 @@ snmp_show_proto_info(struct proto *P)
 
   // TODO move me into the bgp_mib.c
   cli_msg(-1006, "    BGP4-MIB");
-  cli_msg(-1006, "      enabled true");          // TODO
+  // TODO enabled
   cli_msg(-1006, "      Local AS %u", p->bgp_local_as);
   cli_msg(-1006, "      Local router id %R", p->bgp_local_id);
   cli_msg(-1006, "      BGP peers");
 
+  if (p->state == SNMP_DOWN || p->state == SNMP_RESET)
+    return;
+
   HASH_WALK(p->bgp_hash, next, peer)
   {
     cli_msg(-1006, "    protocol name: %s", peer->bgp_proto->p.name);
-    cli_msg(-1006, "    remote IPv4 address: %I4", peer->peer_ip);
+    cli_msg(-1006, "    Remote IPv4 address: %I4", peer->peer_ip);
+    cli_msg(-1006, "    Remote router id %R", peer->bgp_proto->remote_id);
   }
   HASH_WALK_END;
 }
 
 /*
  * snmp_postconfig - Check configuration correctness
- * @CF - SNMP procotol configuration generic handle
+ * @CF: SNMP procotol configuration generic handle
  */
 static void
 snmp_postconfig(struct proto_config *CF)
index eecd01c30c14ea4ccedcb8377ba4f4805235d773..35f7817dde523b921748b1090040bd8663654839 100644 (file)
@@ -12,6 +12,7 @@
 
 #include "lib/ip.h"
 #include "lib/socket.h"
+#include "lib/resource.h"
 #include "lib/timer.h"
 #include "nest/bird.h"
 #include "nest/protocol.h"
@@ -48,12 +49,12 @@ struct snmp_bond {
 
 struct snmp_config {
   struct proto_config cf;
-  ip_addr local_ip;
-  ip_addr remote_ip;
+  ip4_addr local_ip;
+  ip4_addr remote_ip;
   u16 local_port;
   u16 remote_port;
 
-  ip_addr bgp_local_id;          /* BGP4-MIB related fields */
+  ip4_addr bgp_local_id;         /* BGP4-MIB related fields */
   u32 bgp_local_as;
 
   btime timeout;
@@ -94,17 +95,28 @@ struct snmp_proto {
   struct object_lock *lock;
   pool *pool;                    /* a shortcut to the procotol mem. pool */
   linpool *lp;                   /* linpool for bgp_trie nodes */
+  slab *request_storage;                 /* manages storages storage for incomming requests */
 
-  ip_addr local_ip;
-  ip_addr remote_ip;
+  enum snmp_proto_state state;
+
+  ip4_addr local_ip;
+  ip4_addr remote_ip;
   u16 local_port;
   u16 remote_port;
 
-  ip_addr bgp_local_id;                  /* BGP4-MIB related fields */
+  ip4_addr bgp_local_id;                 /* BGP4-MIB related fields */
   u32 bgp_local_as;
 
   sock *sock;
-  void *last_header;             /* points to partial PDU header */
+
+  /* Partial packet processing */
+  uint header_offset;            /* offset of PDU header in TX-buffer during
+                                  * partial parsing */
+  uint last_index;               /* stores last index during partial parsing */
+  uint last_size;                /* number of bytes used inside TX-buffer */
+  uint last_pkt_id;              /* stores packetId to use for partial packet */
+
+
   btime timeout;                 /* timeout is part of MIB registration. It
                                    specifies how long should the master
                                    agent wait for request responses. */
@@ -123,10 +135,11 @@ struct snmp_proto {
   HASH(struct snmp_bgp_peer) bgp_hash;
   struct tbf rl_gen;
 
+  list pending_pdus;
+
   timer *ping_timer;
   btime startup_delay;
   timer *startup_timer;
-  u8 state;
 };
 
 //void snmp_tx(sock *sk);
index 64a9ce64725ae9970ca0a727354c2ad85510c8e2..cc78f85eabc1f060671d691f359fe2da8ae61d83 100644 (file)
 #include "snmp_utils.h"
 
 inline void
-snmp_pdu_context(struct snmp_pdu *pdu, sock *sk)
+snmp_pdu_context(const struct snmp_proto *p, struct snmp_pdu *pdu, sock *sk)
 {
-  pdu->buffer = sk->tpos;
-  pdu->size = sk->tbuf + sk->tbsize - sk->tpos;
   pdu->error = AGENTX_RES_NO_ERROR;
-  pdu->index = 0;
+  if (!snmp_is_partial(p))
+  {
+    pdu->buffer = sk->tpos;
+    pdu->size = sk->tbuf + sk->tbsize - sk->tpos;
+    pdu->index = 0;
+    return;
+  }
+
+  pdu->buffer = sk->tbuf + p->header_offset + p->last_size;
+  pdu->size = sk->tbuf + sk->tbsize - pdu->buffer;
+  pdu->index = p->last_index;
 }
 
 inline void
 snmp_session(const struct snmp_proto *p, struct agentx_header *h)
 {
-  h->session_id = p->session_id;
-  h->transaction_id = p->transaction_id;
-  h->packet_id = p->packet_id;
+  STORE_U32(h->session_id, p->session_id);
+  STORE_U32(h->transaction_id, p->transaction_id);
+  STORE_U32(h->packet_id, p->packet_id);
+  //log(L_INFO "storing packet id %u into the header %p", p->packet_id, h);
 }
 
 inline int
@@ -36,7 +45,8 @@ snmp_has_context(const struct agentx_header *h)
 inline byte *
 snmp_add_context(struct snmp_proto *p, struct agentx_header *h, uint contid)
 {
-  h->flags = h->flags | AGENTX_NON_DEFAULT_CONTEXT;
+  u8 flags = LOAD_U8(h->flags);
+  STORE_U8(h->flags, flags | AGENTX_NON_DEFAULT_CONTEXT);
   // TODO append the context after the header
   (void)p;
   (void)contid;
@@ -60,7 +70,8 @@ int
 snmp_is_oid_empty(const struct oid *oid)
 {
   if (oid != NULL)
-    return oid->n_subid == 0 && oid->prefix == 0 && oid->include == 0;
+    return LOAD_U8(oid->n_subid) == 0 && LOAD_U8(oid->prefix) == 0 &&
+       LOAD_U8(oid->include) == 0;
   else
     return 0;
 }
@@ -76,9 +87,10 @@ snmp_pkt_len(const byte *start, const byte *end)
   return (end - start) - AGENTX_HEADER_SIZE;
 }
 
-/**
- *
- * used for copying oid to in buffer oid @dest
+/*
+ * snmp_oid_copy - copy OID from one place to another
+ * @dest: destination to use
+ * @src: OID to be copied from
  */
 void
 snmp_oid_copy(struct oid *dest, const struct oid *src)
@@ -88,12 +100,14 @@ snmp_oid_copy(struct oid *dest, const struct oid *src)
   STORE_U8(dest->include, src->include ? 1 : 0);
   STORE_U8(dest->pad,    0);
 
-  for (int i = 0; i < src->n_subid; i++)
+  for (int i = 0; i < LOAD_U8(src->n_subid); i++)
     STORE_U32(dest->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)
@@ -113,6 +127,10 @@ 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()
+ */
 size_t
 snmp_str_size_from_len(uint len)
 {
@@ -152,79 +170,218 @@ snmp_oid_sizeof(uint n_subid)
   return sizeof(struct oid) + n_subid * sizeof(u32);
 }
 
-uint snmp_varbind_hdr_size_from_oid(struct oid *oid)
+/*
+ * snmp_varbind_hdr_size_from_oid - return in-buffer size of VarBind
+ * @oid: OID used as VarBind's name
+ *
+ * This function assume @oid to be not NULL.
+ */
+uint
+snmp_varbind_hdr_size_from_oid(const struct oid *oid)
+{
+  ASSUME(oid);
+  return snmp_oid_size(oid) + OFFSETOF(struct agentx_varbind, name);
+}
+
+/*
+ * snmp_set_varbind_type - set VarBind's type field
+ * @vb: Varbind inside TX-buffer
+ * @t: a valid type to be set
+ *
+ * This function assumes valid @t.
+ */
+inline void
+snmp_set_varbind_type(struct agentx_varbind *vb, enum agentx_type t)
 {
-  return snmp_oid_size(oid) + 4;
+  ASSUME(t != AGENTX_INVALID);
+  STORE_U16(vb->type, t);
+}
+
+/* Internal wrapper */
+static inline u16
+snmp_load_varbind_type(const struct agentx_varbind *vb)
+{
+  return LOAD_U16(vb->type);
+}
+
+/*
+ * snmp_get_varbind_type - loads a VarBind type
+ * @vb: VarBind pointer to TX-buffer
+ *
+ * This function assumes VarBind with valid type, always call snmp_test_varbind
+ * for in TX-buffer VarBinds!
+ */
+inline enum agentx_type
+snmp_get_varbind_type(const struct agentx_varbind *vb)
+{
+  ASSUME(snmp_test_varbind(vb));
+  return (enum agentx_type) snmp_load_varbind_type(vb);
+}
+
+static inline uint
+snmp_get_octet_size(const struct agentx_octet_str *str)
+{
+  return LOAD_U32(str->length);
 }
 
 /**
- * snmp_vb_size - measure size of varbind in bytes
- * @vb: variable binding to use
+ * snmp_varbind_header_size - measure size of VarBind without data in bytes
+ * @vb: VarBind to use
+ *
+ * Return size including whole OID as well as the VarBind header.
  */
 uint
-snmp_varbind_header_size(struct agentx_varbind *vb)
+snmp_varbind_header_size(const struct agentx_varbind *vb)
 {
   return snmp_varbind_hdr_size_from_oid(&vb->name);
 }
 
 uint
-snmp_varbind_size(struct agentx_varbind *vb)
+snmp_varbind_size_unsafe(const struct agentx_varbind *vb)
 {
-  uint hdr_size = snmp_varbind_header_size(vb);
-  int s = agentx_type_size(vb->type);
+  ASSUME(snmp_test_varbind(vb));
 
-  if (s >= 0)
-    return hdr_size + (uint) s;
+  enum agentx_type type = snmp_get_varbind_type(vb);
+  int value_size = agentx_type_size(type);
 
-  void *data = snmp_varbind_data(vb);
+  uint vb_header = snmp_varbind_header_size(vb);
 
-  if (vb->type == AGENTX_OBJECT_ID)
-    return hdr_size + snmp_oid_size((struct oid *) data);
+  if (value_size == 0)
+    return vb_header;
 
-  /*
-   * Load length of octet string
-   * (AGENTX_OCTET_STRING, AGENTX_IP_ADDRESS, AGENTX_OPAQUE)
-   */
-  return hdr_size + snmp_str_size_from_len(LOAD_PTR(data));
+  if (value_size > 0)
+    return vb_header + value_size;
+
+  switch (type)
+  {
+    case AGENTX_OBJECT_ID:;
+      struct oid *oid = snmp_varbind_data(vb);
+      return vb_header + snmp_oid_size(oid);
+
+    case AGENTX_OCTET_STRING:
+    case AGENTX_IP_ADDRESS:
+    case AGENTX_OPAQUE:;
+      struct agentx_octet_str *string = snmp_varbind_data(vb);
+      return vb_header + snmp_get_octet_size(string);
+
+    default:
+      /* Shouldn't happen */
+      return 0;
+  }
 }
 
-/* test if the varbind has valid type */
+/**
+ * snmp_varbind_size - get size of in-buffer VarBind
+ * @vb: VarBind 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
+snmp_varbind_size(const struct agentx_varbind *vb, uint limit)
+{
+  ASSUME(snmp_test_varbind(vb));
+
+  if (limit < sizeof(struct agentx_varbind))
+    return 0;
+
+  enum agentx_type type = agentx_type_size(snmp_get_varbind_type(vb));
+  int s = agentx_type_size(type);
+  uint vb_header = snmp_varbind_header_size(vb);
+
+  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;
+
+  switch (type)
+  {
+    case AGENTX_OBJECT_ID:;
+      struct oid *oid = snmp_varbind_data(vb);
+      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);
+      return vb_header + snmp_get_octet_size(os);
+
+    default:
+      /* This should not happen */
+      return 0;
+  }
+}
+
+/*
+ * snmp_test_varbind - test validity of VarBind's type
+ * @vb: VarBind to test
+ */
 int
 snmp_test_varbind(const struct agentx_varbind *vb)
 {
-  if (vb->type == AGENTX_INTEGER  ||
-      vb->type == AGENTX_OCTET_STRING  ||
-      vb->type == AGENTX_NULL  ||
-      vb->type == AGENTX_OBJECT_ID  ||
-      vb->type == AGENTX_IP_ADDRESS  ||
-      vb->type == AGENTX_COUNTER_32  ||
-      vb->type == AGENTX_GAUGE_32  ||
-      vb->type == AGENTX_TIME_TICKS  ||
-      vb->type == AGENTX_OPAQUE  ||
-      vb->type == AGENTX_COUNTER_64  ||
-      vb->type == AGENTX_NO_SUCH_OBJECT  ||
-      vb->type == AGENTX_NO_SUCH_INSTANCE  ||
-      vb->type == AGENTX_END_OF_MIB_VIEW)
+  ASSUME(vb);
+
+  u16 type = snmp_load_varbind_type(vb);
+  if (type == AGENTX_INTEGER  ||
+      type == AGENTX_OCTET_STRING  ||
+      type == AGENTX_NULL  ||
+      type == AGENTX_OBJECT_ID  ||
+      type == AGENTX_IP_ADDRESS  ||
+      type == AGENTX_COUNTER_32  ||
+      type == AGENTX_GAUGE_32  ||
+      type == AGENTX_TIME_TICKS  ||
+      type == AGENTX_OPAQUE  ||
+      type == AGENTX_COUNTER_64  ||
+      type == AGENTX_NO_SUCH_OBJECT  ||
+      type == AGENTX_NO_SUCH_INSTANCE  ||
+      type == AGENTX_END_OF_MIB_VIEW)
     return 1;
   else
     return 0;
 }
 
+/*
+ * snmp_create_varbind - create a null-typed VarBind in buffer
+ * @buf: buffer to use
+ */
+struct agentx_varbind *
+snmp_create_varbind_null(byte *buf)
+{
+  struct oid o = { 0 };
+  struct agentx_varbind *vb = snmp_create_varbind(buf, &o);
+  snmp_set_varbind_type(vb, AGENTX_NULL);
+  return vb;
+}
+
+/*
+ * snmp_create_varbind - initialize in-buffer non-typed VarBind
+ * @buf: pointer to first unused buffer byte
+ * @oid: OID to use as VarBind name
+ */
 struct agentx_varbind *
 snmp_create_varbind(byte *buf, struct oid *oid)
 {
-  struct agentx_varbind *vb = (void*) buf;
-  vb->pad = 0;
+  struct agentx_varbind *vb = (void *) buf;
+  STORE_U16(vb->pad, 0);
   snmp_oid_copy(&vb->name, oid);
   return vb;
 }
 
+#if 0
 byte *
 snmp_fix_varbind(struct agentx_varbind *vb, struct oid *new)
 {
   memcpy(&vb->name, new, snmp_oid_size(new));
   return (void *) vb + snmp_varbind_header_size(vb);
 }
+#endif
 
 /**
  * snmp_oid_ip4_index - check IPv4 address validity in oid
@@ -258,20 +415,27 @@ snmp_valid_ip4_index_unsafe(const struct oid *o, uint start)
   return 1;
 }
 
+/*
+ * snmp_put_nstr - copy c-string into buffer with limit
+ * @buf: destination buffer
+ * @str: string to use
+ * @len: number of characters to use from string
+ */
 byte *
 snmp_put_nstr(byte *buf, const char *str, uint len)
 {
   uint alen = BIRD_ALIGN(len, 4);
 
-  STORE_PTR(buf, len);
-  buf += 4;
-  memcpy(buf, str, len);
+  struct agentx_octet_str *octet = (void *) buf;
+  STORE_U32(octet->length, len);
+  memcpy(&octet->data, str, len);
+  buf += len + sizeof(octet->length);
 
   /* Insert zero padding in the gap at the end */
   for (uint i = 0; i < alen - len; i++)
-    buf[len + i] = '\0';
+    STORE_U8(buf[i], '\0');
 
-  return buf + alen;
+  return buf + (alen - len);
 }
 
 /**
@@ -296,6 +460,7 @@ snmp_put_ip4(byte *buf, ip4_addr addr)
   /* octet string has size 4 bytes */
   STORE_PTR(buf, 4);
 
+  /* Always use Network byte order */
   put_u32(buf+4, ip4_to_u32(addr));
 
   return buf + 8;
@@ -328,15 +493,11 @@ snmp_put_oid(byte *buf, struct oid *oid)
  *
  * Put @data into buffer @buf with 3B zeroed padding.
  */
-/* paste data at first byte in message
- *   with 3B of padding
- */
 byte *
 snmp_put_fbyte(byte *buf, u8 data)
 {
-  //log(L_INFO "paste_fbyte()");
-  put_u8(buf, data);
-  put_u24(++buf, 0);  // PADDING
+  STORE_U8(*buf++, data);
+  memset(buf, 0, 3); /* we fill the 24bit padding with zeros */
   return buf + 3;
 }
 
@@ -394,9 +555,6 @@ snmp_oid_dump(const struct oid *oid)
 int
 snmp_oid_compare(const struct oid *left, const struct oid *right)
 {
-  const u32 INTERNET_PREFIX[] = {1, 3, 6, 1};
-
-
   if (left->prefix == 0 && right->prefix == 0)
     goto test_ids;
 
@@ -406,9 +564,9 @@ snmp_oid_compare(const struct oid *left, const struct oid *right)
   if (left->prefix == 0)
   {
     for (int i = 0; i < 4; i++)
-      if (left->ids[i] < INTERNET_PREFIX[i])
+      if (left->ids[i] < snmp_internet[i])
        return -1;
-      else if (left->ids[i] > INTERNET_PREFIX[i])
+      else if (left->ids[i] > snmp_internet[i])
        return 1;
 
     for (int i = 0; i < MIN(left->n_subid - 4, right->n_subid); i++)
@@ -453,7 +611,9 @@ snmp_registration_create(struct snmp_proto *p, u8 mib_class)
   r->session_id = p->session_id;
   /* will be incremented by snmp_session() macro during packet assembly */
   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);
 
   r->mib_class = mib_class;
 
@@ -465,6 +625,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);
   return
     (r->mib_class == class) &&
     (r->session_id == h->session_id) &&
@@ -483,8 +644,11 @@ snmp_dump_packet(byte UNUSED *pkt, uint size)
 }
 
 /*
- * Returns length of agentx_type @type in bytes.
- * Variable length types result in -1.
+ * agentx_type_size - get in packet VarBind type size
+ * @type: VarBind type
+ *
+ * Returns length of agentx_type @type in bytes, Variable length types result in
+ * -1.
  */
 int
 agentx_type_size(enum agentx_type type)
@@ -518,10 +682,11 @@ snmp_varbind_type32(struct agentx_varbind *vb, uint size, enum agentx_type type,
   if (size < (uint) agentx_type_size(type))
     return NULL;
 
-  vb->type = type;
+  snmp_set_varbind_type(vb, type);
   u32 *data = snmp_varbind_data(vb);
-  *data = val;
-  return (byte *)(data + 1);
+  STORE_PTR(data, val); /* note that the data has u32 type */
+  data++;
+  return (byte *) data;
 }
 
 inline byte *
@@ -556,17 +721,18 @@ snmp_varbind_ip4(struct agentx_varbind *vb, uint size, ip4_addr addr)
   if (size < snmp_str_size_from_len(4))
     return NULL;
 
-  vb->type = AGENTX_IP_ADDRESS;
+  snmp_set_varbind_type(vb, AGENTX_IP_ADDRESS);
   return snmp_put_ip4(snmp_varbind_data(vb), addr);
 }
 
+// TODO doc string, we have already the varbind prepared
 inline byte *
 snmp_varbind_nstr(struct agentx_varbind *vb, uint size, const char *str, uint len)
 {
   if (size < snmp_str_size_from_len(len))
     return NULL;
 
-  vb->type = AGENTX_OCTET_STRING;
+  snmp_set_varbind_type(vb, AGENTX_OCTET_STRING);
   return snmp_put_nstr(snmp_varbind_data(vb), str, len);
 }
 
@@ -574,7 +740,7 @@ inline enum agentx_type
 snmp_search_res_to_type(enum snmp_search_res r)
 {
   ASSUME(r != SNMP_SEARCH_OK);
-  static enum agentx_type type_arr[] = {
+  enum agentx_type type_arr[] = {
     [SNMP_SEARCH_NO_OBJECT]   = AGENTX_NO_SUCH_OBJECT,
     [SNMP_SEARCH_NO_INSTANCE] = AGENTX_NO_SUCH_INSTANCE,
     [SNMP_SEARCH_END_OF_VIEW] = AGENTX_END_OF_MIB_VIEW,
@@ -583,3 +749,88 @@ snmp_search_res_to_type(enum snmp_search_res r)
   return type_arr[r];
 }
 
+inline int
+snmp_test_close_reason(byte value)
+{
+  if (value >= (byte) AGENTX_CLOSE_OTHER &&
+      value <= (byte) AGENTX_CLOSE_BY_MANAGER)
+    return 1;
+  else
+    return 0;
+}
+
+inline struct agentx_header *
+snmp_create_tx_header(struct snmp_proto *p, byte *tbuf)
+{
+  /* the response is created always in TX-buffer */
+  p->header_offset = tbuf - p->sock->tbuf;
+  ASSERT(p->header_offset < p->sock->tbsize);
+  return (struct agentx_header *) tbuf;
+}
+
+/*
+ * Partial header manipulation functions
+ */
+
+/*
+ * snmp_is_partial - check if we have a partially parted packet in TX-buffer
+ * @p: SNMP protocol instance
+ */
+inline int
+snmp_is_partial(const struct snmp_proto *p)
+{
+  return p->last_size > 0;
+}
+
+/*
+ * snmp_get_header - restore partial packet's header from TX-buffer
+ * @p: SNMP protocol instance
+ */
+inline struct agentx_header *
+snmp_get_header(const struct snmp_proto *p)
+{
+  /* Nonzero last size indicates existence of partial packet */
+  ASSERT(p->last_size && p->header_offset < p->sock->tbsize);
+  return (struct agentx_header *) (p->sock->tbuf + p->header_offset);
+}
+
+/*
+ * snmp_set_header - store partial packet's header into protocol
+ * @p: SNMP protocol instance
+ * @h: header of the currently parsed PDU
+ * @c: SNMP PDU context
+ *
+ * Store the needed values regarding later partial PDU processing.
+ */
+inline void
+snmp_set_header(struct snmp_proto *p, struct agentx_header *h, struct snmp_pdu *c)
+{
+  sock *sk = p->sock;
+  // TODO agentx_headier in last_size or not?
+  ASSERT(c->buffer - sk->tpos >= AGENTX_HEADER_SIZE);
+  p->last_size = c->buffer - sk->tpos + p->last_size;
+  p->header_offset = (((byte *) h) - sk->tbuf);
+  p->last_index = c->index;
+  log(L_INFO "using p->packet_id %u as a p->last_pkt_id %u", p->packet_id, p->last_pkt_id);
+  p->last_pkt_id = p->packet_id;
+  log(L_INFO "snmp_set_header() tbuf %p tpos %p buffer %p header %p header2 %p offset %u last_size %u last_index %u last_pkt_id %u",
+      sk->tbuf, sk->tpos, c->buffer,h,(byte*)h, p->header_offset, p->last_size,
+      p->last_index, p->last_pkt_id);
+}
+
+/*
+ * snmp_unset_header - clean partial packet's header
+ * @p: SNMP protocol instance
+ *
+ * Clean the partial packet processing fields of protocol when the packet is
+ * fully processed.
+ */
+inline void
+snmp_unset_header(struct snmp_proto *p)
+{
+  p->last_size = 0;
+  p->header_offset = 0;
+  p->last_index = 0;
+  p->last_pkt_id = 0;
+}
+
index fe371ee00e2a3ed0a23168a5410b9dcdb6d4dc68..52ba14787a7118cbb3915746c8e3b63ba104f92e 100644 (file)
@@ -11,13 +11,14 @@ int snmp_valid_ip4_index(const struct oid *o, uint start);
 int snmp_valid_ip4_index_unsafe(const struct oid *o, uint start);
 uint snmp_oid_size(const struct oid *o);
 size_t snmp_oid_sizeof(uint n_subid);
-uint snmp_varbind_hdr_size_from_oid(struct oid *oid);
-uint snmp_varbind_header_size(struct agentx_varbind *vb);
-uint snmp_varbind_size(struct agentx_varbind *vb);
+uint snmp_varbind_hdr_size_from_oid(const struct oid *oid);
+uint snmp_varbind_header_size(const struct agentx_varbind *vb);
+uint snmp_varbind_size(const struct agentx_varbind *vb, uint limit);
+uint snmp_varbind_size_unsafe(const struct agentx_varbind *vb);
 int snmp_test_varbind(const struct agentx_varbind *vb);
 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, sock *sk);
+void snmp_pdu_context(const struct snmp_proto *p, struct snmp_pdu *pdu, sock *sk);
 
 void snmp_oid_copy(struct oid *dest, const struct oid *src);
 
@@ -25,7 +26,8 @@ struct oid *snmp_oid_duplicate(pool *pool, const struct oid *oid);
 struct oid *snmp_oid_blank(struct snmp_proto *p);
 
 void *snmp_varbind_data(const struct agentx_varbind *vb);
-struct agentx_varbind *snmp_create_varbind(byte* buf, struct oid *oid);
+struct agentx_varbind *snmp_create_varbind(byte *buf, struct oid *oid);
+struct agentx_varbind *snmp_create_varbind_null(byte *buf);
 byte *snmp_fix_varbind(struct agentx_varbind *vb, struct oid *new);
 
 int snmp_oid_compare(const struct oid *first, const struct oid *second);
@@ -46,11 +48,15 @@ void snmp_oid_ip4_index(struct oid *o, uint start, ip4_addr addr);
 
 void snmp_oid_dump(const struct oid *oid);
 
+void snmp_set_varbind_type(struct agentx_varbind *vb, enum agentx_type t);
+enum agentx_type snmp_get_varbind_type(const struct agentx_varbind *vb);
+
 //struct oid *snmp_prefixize(struct snmp_proto *p, struct oid *o);
 
 struct snmp_registration *snmp_registration_create(struct snmp_proto *p, u8 mib_class);
 int snmp_registration_match(struct snmp_registration *r, struct agentx_header *h, u8 class);
 
+/* Functions filling buffer a typed value */
 byte *snmp_varbind_int(struct agentx_varbind *vb, uint size, u32 val);
 byte *snmp_varbind_counter32(struct agentx_varbind *vb, uint size, u32 val);
 byte *snmp_varbind_gauge32(struct agentx_varbind *vb, uint size, s64 val);
@@ -64,5 +70,12 @@ enum agentx_type snmp_search_res_to_type(enum snmp_search_res res);
 
 int agentx_type_size(enum agentx_type t);
 
+int snmp_test_close_reason(byte value);
+
+struct agentx_header *snmp_create_tx_header(struct snmp_proto *p, byte *rbuf);
+int snmp_is_partial(const struct snmp_proto *p);
+struct agentx_header *snmp_get_header(const struct snmp_proto *p);
+void snmp_set_header(struct snmp_proto *p, struct agentx_header *h, struct snmp_pdu *c);
+void snmp_unset_header(struct snmp_proto *p);
 
 #endif
index 804cfa1ea9288e75c185e768c0957ee595e918f1..5da01bafe0de577b95ae131ef740926da6b568db 100644 (file)
@@ -39,7 +39,7 @@
  * communication socket. This implicitly closes the established session. We
  * chose this approach because we cannot easily mark the boundary between packets.
  * When we are reseting the connection, we change the snmp_state to SNMP_RESET.
- * In SNMP_RESET state we skip all received bytes and wait for snmp_tx()
+ * In SNMP_RESET state we skip all received bytes and wait for snmp_tx_skip()
  * to be called. The socket's tx_hook is called when the TX-buffer is empty,
  * meaning our response (agentx-Response-PDU) was send.
  *
@@ -66,12 +66,20 @@ static uint parse_response(struct snmp_proto *p, byte *buf, uint size);
 static void do_response(struct snmp_proto *p, byte *buf, uint size);
 static uint parse_gets2_pdu(struct snmp_proto *p, byte *buf, uint size, uint *skip);
 static struct agentx_response *prepare_response(struct snmp_proto *p, struct snmp_pdu *c);
-static void response_err_ind(struct agentx_response *res, enum agentx_response_errs err, u16 ind);
-static uint update_packet_size(struct snmp_proto *p, const byte *start, byte *end);
+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 snmp_proto *p, struct agentx_header *start, byte *end);
 static struct oid *search_mib(struct snmp_proto *p, const struct oid *o_start, const struct oid *o_end, struct oid *o_curr, struct snmp_pdu *c, enum snmp_search_res *result);
 
 u32 snmp_internet[] = { SNMP_ISO, SNMP_ORG, SNMP_DOD, SNMP_INTERNET };
 
+static inline int
+snmp_is_active(struct snmp_proto *p)
+{
+  /* Note: states in which we have opened socket */
+  return p->state == SNMP_OPEN || p->state == SNMP_REGISTER ||
+      p->state == SNMP_CONN;
+}
+
 static inline void
 snmp_header(struct agentx_header *h, enum agentx_pdu_types type, u8 flags)
 {
@@ -179,20 +187,21 @@ snmp_error(struct snmp_proto *p)
  * @p: SNMP protocol instance
  * @error: PDU error fields value
  * @index: PDU error index field value
+ *
+ * This function assumes that the buffer has enough space to fill in the AgentX
+ * Response PDU. So it is the responsibility of the caller to provide that.
  */
 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, sk);
+  snmp_pdu_context(p, &c, sk);
 
-  // XXX: THIS SHOULDN'T HAPPEN! - fix it
-  if (c.size < sizeof(struct agentx_response))
-    snmp_manage_tbuf(p, &c);
+  ASSUME(c.size >= sizeof(struct agentx_response));
 
   struct agentx_response *res = prepare_response(p, &c);
-  response_err_ind(res, error, index);
+  response_err_ind(p, res, error, index);
   sk_send(sk, sizeof(struct agentx_response));
 }
 
@@ -201,7 +210,7 @@ snmp_simple_response(struct snmp_proto *p, enum agentx_response_errs error, u16
  * @p: SNMP protocol instance
  * @oid: PDU OID description field value
  *
- * Other fields are filled based on @p configuratin (timeout, subagent string
+ * Other fields are filled based on @p configuration (timeout, subagent string
  * description)
  */
 static void
@@ -211,14 +220,14 @@ open_pdu(struct snmp_proto *p, struct oid *oid)
   sock *sk = p->sock;
 
   struct snmp_pdu c;
-  snmp_pdu_context(&c, sk);
+  snmp_pdu_context(p, &c, sk);
 
 #define TIMEOUT_SIZE 4 /* 1B timeout, 3B zero padding */
   if (c.size < AGENTX_HEADER_SIZE + TIMEOUT_SIZE + snmp_oid_size(oid) +
       + snmp_str_size(cf->description))
     snmp_manage_tbuf(p, &c);
 
-  struct agentx_header *h = (struct agentx_header *) c.buffer;
+  struct agentx_header *h = snmp_create_tx_header(p, c.buffer);
   ADVANCE(c.buffer, c.size, AGENTX_HEADER_SIZE);
   snmp_blank_header(h, AGENTX_OPEN_PDU);
 
@@ -228,7 +237,7 @@ open_pdu(struct snmp_proto *p, struct oid *oid)
 
   c.size -= (4 + snmp_oid_size(oid) + snmp_str_size(cf->description));
 
-  if (p->timeout >= 1 TO_US && p->timeout <= 255 TO_US)
+  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);
@@ -241,7 +250,7 @@ open_pdu(struct snmp_proto *p, struct oid *oid)
   c.buffer = snmp_put_oid(c.buffer, oid);
   c.buffer = snmp_put_str(c.buffer, cf->description);
 
-  uint s = update_packet_size(p, sk->tpos, c.buffer);
+  uint s = update_packet_size(p, h, c.buffer);
   sk_send(sk, s);
 #undef TIMEOUT_SIZE
 }
@@ -260,10 +269,10 @@ snmp_notify_pdu(struct snmp_proto *p, struct oid *oid, void *data, uint size, in
   sock *sk = p->sock;
 
   struct snmp_pdu c;
-  snmp_pdu_context(&c, sk);
+  snmp_pdu_context(p, &c, sk);
 
 #define UPTIME_SIZE \
-  (6 * sizeof(u32)) /* sizeof( { u32 vb_type, u32 oid_hdr, u32 ids[4] } )*/
+  (6 * sizeof(u32)) /* sizeof( { 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] } ) */
 
@@ -276,37 +285,44 @@ snmp_notify_pdu(struct snmp_proto *p, struct oid *oid, void *data, uint size, in
   if (c.size < sz)
     snmp_manage_tbuf(p, &c);
 
-  struct agentx_header *h = (struct agentx_header *) c.buffer;
+  struct agentx_header *h = snmp_create_tx_header(p, c.buffer);
   ADVANCE(c.buffer, c.size, AGENTX_HEADER_SIZE);
   snmp_blank_header(h, AGENTX_NOTIFY_PDU);
   p->packet_id++;
+  log(L_INFO "incrementing packet_id to %u (notify)", p->packet_id);
   snmp_session(p, h);
 
   if (include_uptime)
   {
     /* sysUpTime.0 oid */
-    struct oid uptime = {
+    struct oid uptime_oid = {
       .n_subid = 4,
       .prefix = SNMP_MGMT,
       .include = 0,
       .pad = 0,
     };
+    /* {mgmt}.mib-2.system.sysUpTime.sysUpTimeInstance (0) */
     u32 uptime_ids[] = { 1, 1, 3, 0 };
 
-    struct agentx_varbind *vb = snmp_create_varbind(c.buffer, &uptime);
-    for (uint i = 0; i < uptime.n_subid; i++)
+    struct agentx_varbind *vb = snmp_create_varbind(c.buffer, &uptime_oid);
+    for (uint i = 0; i < uptime_oid.n_subid; i++)
       STORE_U32(vb->name.ids[i], uptime_ids[i]);
-    snmp_varbind_ticks(vb, c.size, (current_time() TO_S) / 100);
-    ADVANCE(c.buffer, c.size, snmp_varbind_size(vb));
+
+    /* TODO: use time from last reconfiguration instead? [config->load_time] */
+    btime uptime = current_time() - boot_time;
+    snmp_varbind_ticks(vb, c.size, (uptime TO_S) / 100);
+    ASSUME(snmp_test_varbind(vb));
+    ADVANCE(c.buffer, c.size, snmp_varbind_size_unsafe(vb));
   }
 
   /* snmpTrapOID.0 oid */
   struct oid trap0 = {
     .n_subid = 6,
-    .prefix = 6,
+    .prefix = 6, /* snmpV2 */
     .include = 0,
     .pad = 0,
   };
+  /* {snmpV2}.snmpModules.snmpAlarmNextIndex.snmpMIBObjects.snmpTrap.snmpTrapIOD.0 */
   u32 trap0_ids[] = { 3, 1, 1, 4, 1, 0 };
 
   struct agentx_varbind *trap_vb = snmp_create_varbind(c.buffer, &trap0);
@@ -314,67 +330,18 @@ snmp_notify_pdu(struct snmp_proto *p, struct oid *oid, void *data, uint size, in
     STORE_U32(trap_vb->name.ids[i], trap0_ids[i]);
   trap_vb->type = AGENTX_OBJECT_ID;
   snmp_put_oid(snmp_varbind_data(trap_vb), oid);
-  ADVANCE(c.buffer, c.size, snmp_varbind_size(trap_vb));
+  ADVANCE(c.buffer, c.size, snmp_varbind_size_unsafe(trap_vb));
 
   memcpy(c.buffer, data, size);
   ADVANCE(c.buffer, c.size, size);
 
-  uint s = update_packet_size(p, sk->tpos, c.buffer);
+  uint s = update_packet_size(p, h, c.buffer);
   sk_send(sk, s);
 
 #undef TRAP0_HEADER_SIZE
 #undef UPTIME_SIZE
 }
 
-#if 0
-/*
- * de_allocate_pdu - common functionality for allocation PDUs
- * @p: SNMP protocol instance
- * @oid: OID to allocate/deallocate
- * @type: allocate/deacollcate PDU type
- * @flags: type of allocation (NEW_INDEX, ANY_INDEX)
- *
- * This function is internal and shouldn't be used outside the SNMP module.
- */
-static void
-de_allocate_pdu(struct snmp_proto *p, struct oid *oid, enum agentx_pdu_types type, u8 flags)
-{
-  sock *sk = p->sock;
-  byte *buf, *pkt;
-  buf = pkt = sk->tbuf;
-  uint size = sk->tbsize;
-  struct snmp_pdu c;
-  snmp_pdu_context(&c, p->sock);
-
-
-  if (size > AGENTX_HEADER_SIZE + 0)  // TODO additional size
-  {
-    struct agentx_header *h;
-    SNMP_CREATE(pkt, struct agentx_header, h);
-    snmp_blank_header(h, type);
-    snmp_session(p, h);
-
-    struct agentx_varbind *vb = (struct agentx_varbind *) pkt;
-
-    // TODO
-    STORE_16(vb->type, AGENTX_OBJECT_ID);
-    STORE(vb->oid, 0);
-  }
-}
-
-void
-snmp_allocate(struct snmp_proto *p, struct oid *oid, u8 flags)
-{
-  /* TODO - call the de_allocate_pdu() */
-}
-
-void
-snmp_deallocate(struct snmp_proto *p, struct oid *oid, u8 flags)
-{
-  /* TODO - call the de_allocate_pdu() */
-}
-#endif
-
 /*
  * un_register_pdu - common functionality for registration PDUs
  * @p: SNMP protocol instance
@@ -404,7 +371,7 @@ un_register_pdu(struct snmp_proto *p, struct oid *oid, uint bound, uint index, e
   const struct snmp_config *cf = SKIP_BACK(struct snmp_config, cf, p->p.cf);
   sock *sk = p->sock;
   struct snmp_pdu c;
-  snmp_pdu_context(&c, sk);
+  snmp_pdu_context(p, &c, sk);
 
   /* conditional +4 for upper-bound (optinal field) */
   uint sz = AGENTX_HEADER_SIZE + snmp_oid_size(oid) + ((bound > 1) ? 4 : 0);
@@ -412,11 +379,13 @@ un_register_pdu(struct snmp_proto *p, struct oid *oid, uint bound, uint index, e
   if (c.size < sz)
     snmp_manage_tbuf(p, &c);
 
-  struct agentx_header *h = (struct agentx_header *) c.buffer;
+  struct agentx_header *h = snmp_create_tx_header(p, c.buffer);
   ADVANCE(c.buffer, c.size, AGENTX_HEADER_SIZE);
 
   snmp_header(h, type, is_instance ? AGENTX_FLAG_INSTANCE_REGISTRATION : 0);
   p->packet_id++;
+  log(L_INFO "incementing the packet_id to %u (%s)", p->packet_id, type ==
+    AGENTX_REGISTER_PDU ? "register" : "unregister");
   snmp_session(p, h);
 
   struct agentx_un_register_hdr *ur = (struct agentx_un_register_hdr *) c.buffer;
@@ -439,7 +408,7 @@ un_register_pdu(struct snmp_proto *p, struct oid *oid, uint bound, uint index, e
     ADVANCE(c.buffer, c.size, 4);
   }
 
-  uint s = update_packet_size(p, sk->tpos, c.buffer);
+  uint s = update_packet_size(p, h, c.buffer);
 
   sk_send(sk, s);
 }
@@ -458,6 +427,7 @@ un_register_pdu(struct snmp_proto *p, struct oid *oid, uint bound, uint index, e
 void
 snmp_register(struct snmp_proto *p, struct oid *oid, uint bound, uint index, u8 is_instance, uint contid)
 {
+  log(L_INFO "performing a registration");
   un_register_pdu(p, oid, bound, index, AGENTX_REGISTER_PDU, is_instance, contid);
 }
 
@@ -487,22 +457,23 @@ close_pdu(struct snmp_proto *p, enum agentx_close_reasons reason)
 {
   sock *sk = p->sock;
   struct snmp_pdu c;
-  snmp_pdu_context(&c, sk);
+  snmp_pdu_context(p, &c, sk);
 
 #define REASON_SIZE 4
   if (c.size < AGENTX_HEADER_SIZE + REASON_SIZE)
     snmp_manage_tbuf(p, &c);
 
-  struct agentx_header *h = (struct agentx_header *) c.buffer;
+  struct agentx_header *h = snmp_create_tx_header(p, c.buffer);
   ADVANCE(c.buffer, c.size, AGENTX_HEADER_SIZE);
   snmp_blank_header(h, AGENTX_CLOSE_PDU);
   p->packet_id++;
+  log(L_INFO "incrementing packet_id %u (close)", p->packet_id);
   snmp_session(p, h);
 
   snmp_put_fbyte(c.buffer, (u8) reason);
   ADVANCE(c.buffer, c.size, 4);
 
-  uint s = update_packet_size(p, sk->tpos, c.buffer);
+  uint s = update_packet_size(p, h, c.buffer);
   sk_send(sk, s);
 #undef REASON_SIZE
 }
@@ -520,25 +491,31 @@ parse_close_pdu(struct snmp_proto *p, byte * const pkt_start, uint size)
 {
   TRACE(D_PACKETS, "SNMP received agentx-Close-PDU");
   byte *pkt = pkt_start;
-  struct agentx_header *h = (void *) pkt;
-  ADVANCE(pkt, size, AGENTX_HEADER_SIZE);
-  uint pkt_size = LOAD_U32(h->payload);
 
-  // TODO load c.reason
-  if (pkt_size != 0)
+  if (size < sizeof(struct agentx_close_pdu))
   {
-    /* The packet is malform, we reset the connnection anyway */
-    TRACE(D_PACKETS, "SNMP last PDU was malformed (size)");
+    TRACE(D_PACKETS, "SNMP malformed agentx-Close-PDU, closing anyway");
     snmp_simple_response(p, AGENTX_RES_GEN_ERROR, 0);
+    snmp_set_state(p, SNMP_RESET);
+    return size;
   }
-  else
+
+  struct agentx_close_pdu *pdu = (void *) pkt;
+  ADVANCE(pkt, size, sizeof(struct agentx_close_pdu));
+
+  if (!snmp_test_close_reason(pdu->reason))
   {
-    snmp_simple_response(p, AGENTX_RES_NO_ERROR, 0);
-    //snmp_sock_disconnect(p, 1); // TODO XXX: should we try to reconnect (2nd arg) ??
+    TRACE(D_PACKETS, "SNMP invalid close reason");
+    snmp_simple_response(p, AGENTX_RES_GEN_ERROR, 0);
+    snmp_set_state(p, SNMP_RESET);
+    return sizeof(struct agentx_close_pdu);
   }
 
+  enum agentx_close_reasons reason = (enum agentx_close_reasons) pdu->reason;
+  TRACE(D_PACKETS, "SNMP close reason %u", reason);
+  snmp_simple_response(p, AGENTX_RES_NO_ERROR, 0);
   snmp_set_state(p, SNMP_RESET);
-  return AGENTX_HEADER_SIZE;
+  return sizeof(struct agentx_close_pdu);
 }
 
 
@@ -565,7 +542,6 @@ snmp_testset(struct snmp_proto *p, const struct agentx_varbind *vb, struct oid *
   (void)p;(void)vb;(void)oid;(void)pkt_size;
   return 0;
 #if 0
-  // TODO better logic
   if (!oid)
     return 0;
 
@@ -579,89 +555,6 @@ snmp_testset(struct snmp_proto *p, const struct agentx_varbind *vb, struct oid *
 #endif
 }
 
-
-#if 0
-static void UNUSED
-addagentcaps_pdu(struct snmp_proto *p, struct oid *cap, char *descr,
-                uint descr_len)
-{
-  ASSUME(descr != NULL && descr_len > 0);
-  sock *sk = p->sock;
-  //byte *buf = sk->tbuf;
-  //uint size = sk->tbsize;
-  // TO-DO rename to pkt and add pkt_start
-  byte *buf = sk->tpos;
-  uint size = sk->tbuf + sk->tbsize - sk->tpos;
-
-  if (size < AGENTX_HEADER_SIZE + snmp_oid_size(cap) + snmp_str_size_from_len(descr_len))
-  {
-    /* TO-DO need more mem */
-    return;
-  }
-
-  struct agentx_header *h;
-  SNMP_CREATE(buf, struct agentx_header, h);
-  snmp_blank_header(h, AGENTX_ADD_AGENT_CAPS_PDU);
-  snmp_session(p, h);
-  ADVANCE(buf, size, AGENTX_HEADER_SIZE);
-
-  uint in_pkt;
-  if (c && c->length)
-  {
-    SNMP_HAS_CONTEXT(h);
-    ADVANCE(buf, size, in_pkt);
-  }
-
-  // memcpy(buf, cap, snmp_oid_size(cap));
-  ADVANCE(buf, size, snmp_oid_size(cap));
-
-  in_pkt = snmp_put_nstr(buf, descr, descr_len) - buf;
-  ADVANCE(buf, size, in_pkt);
-
-  // make a note in the snmp_proto structure
-
-  //int ret = sk_send(sk, buf - sk->tbuf);
-  sk_send(sk, buf - sk->tpos);
-}
-
-static void UNUSED
-removeagentcaps_pdu(struct snmp_proto *p, struct oid *cap)
-{
-  sock *sk = p->sock;
-
-  //byte *buf = sk->tbuf;
-  //uint size = sk->tbsize;
-  // TO-DO rename to pkt and add pkt_start
-  byte *buf = sk->tpos;
-  uint size = sk->tbuf + sk->tbsize - sk->tpos;
-
-  if (size < AGENTX_HEADER_SIZE + snmp_oid_size(cap))
-  {
-    /* TO-DO need more mem */
-    return;
-  }
-
-  struct agentx_header *h;
-  SNMP_CREATE(buf, struct agentx_header, h);
-  snmp_session(p, h);
-  ADVANCE(buf, size, AGENTX_HEADER_SIZE);
-
-  uint in_pkt;
-  if (c && c->length)
-  {
-    SNMP_HAS_CONTEXT(h);
-    ADVANCE(buf, size, in_pkt);
-  }
-
-  memcpy(buf, cap, snmp_oid_size(cap));
-  ADVANCE(buf, size, snmp_oid_size(cap));
-
-  // update state in snmp_proto structure
-
-  sk_send(sk, buf - sk->tpos);
-}
-#endif
-
 /*
  * refresh_ids - Copy current ids from packet to protocol
  * @p: SNMP protocol instance
@@ -672,6 +565,7 @@ 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);
+  //log(L_INFO "loading packet_id %u from header %p", p->packet_id, h);
 }
 
 /*
@@ -699,7 +593,7 @@ parse_test_set_pdu(struct snmp_proto *p, byte * const pkt_start, uint size)
 
   sock *sk = p->sock;
   struct snmp_pdu c;
-  snmp_pdu_context(&c, sk);
+  snmp_pdu_context(p, &c, sk);
 
   if (c.size < AGENTX_HEADER_SIZE)
     snmp_manage_tbuf(p, &c);
@@ -722,7 +616,7 @@ parse_test_set_pdu(struct snmp_proto *p, byte * const pkt_start, uint size)
   while (size > 0 && all_possible)
   {
     vb = (void *) pkt;
-    sz = snmp_varbind_size(vb, 0);
+    sz = snmp_varbind_size(vb, size);
 
     if (sz > size)
     /* wait for more data to arive */
@@ -742,7 +636,7 @@ parse_test_set_pdu(struct snmp_proto *p, byte * const pkt_start, uint size)
       all_possible = 0;
       break;
     }
-    ADVANCE(pkt, size, snmp_varbind_size(vb, 0));
+    ADVANCE(pkt, size, snmp_varbind_size(vb, size));
 
     // TODO remove the mb_alloc() in prefixize()
     struct oid *work = snmp_prefixize(p, &vb->name);
@@ -752,24 +646,23 @@ parse_test_set_pdu(struct snmp_proto *p, byte * const pkt_start, uint size)
   }
   mb_free(tr);
 #endif
-  s = update_packet_size(p, sk->tpos, c.buffer);
+  s = update_packet_size(p, h, c.buffer);
 
   if (c.error != AGENTX_RES_NO_ERROR)
   {
-    TRACE(D_PACKETS, "last PDU was parsed with error %u", c.error);
-    response_err_ind(res, c.error, c.index + 1);
+    response_err_ind(p, res, c.error, c.index + 1);
     snmp_error(p);
   }
   else if (all_possible)
   {
-    response_err_ind(res, AGENTX_RES_NO_ERROR, 0);
+    response_err_ind(p, res, AGENTX_RES_NO_ERROR, 0);
   }
   else
   {
     TRACE(D_PACKETS, "SNMP SET action failed (not writable)");
-    /* This is an recoverable error, we do not need to reset the connection */
-    //response_err_ind(res, AGENTX_RES_RESOURCE_UNAVAILABLE, c.index + 1);
-    response_err_ind(res, AGENTX_RES_NOT_WRITABLE, c.index + 1);
+    /* This is a recoverable error, we do not need to reset the connection */
+    //response_err_ind(p, res, AGENTX_RES_RESOURCE_UNAVAILABLE, c.index + 1);
+    response_err_ind(p, res, AGENTX_RES_NOT_WRITABLE, c.index + 1);
   }
 
   sk_send(sk, s);
@@ -777,7 +670,7 @@ parse_test_set_pdu(struct snmp_proto *p, byte * const pkt_start, uint size)
 }
 
 /*
- * parse_set_pdu - common functionality for commit set and undo set PDUs
+ * 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
  * @size: number of bytes received from a socket
@@ -786,9 +679,8 @@ parse_test_set_pdu(struct snmp_proto *p, byte * const pkt_start, uint size)
  * Return number of bytes parsed from RX-buffer.
  */
 static uint
-parse_set_pdu(struct snmp_proto *p, byte * const pkt_start, uint size, enum agentx_response_errs err)
+parse_sets_pdu(struct snmp_proto *p, byte * const pkt_start, uint size, enum agentx_response_errs err)
 {
-  // TODO renema the function to pdus
   byte *pkt = pkt_start;
   struct agentx_header *h = (void *) pkt;
   ADVANCE(pkt, size, AGENTX_HEADER_SIZE);
@@ -804,7 +696,7 @@ parse_set_pdu(struct snmp_proto *p, byte * const pkt_start, uint size, enum agen
   }
 
   struct snmp_pdu c;
-  snmp_pdu_context(&c, p->sock);
+  snmp_pdu_context(p, &c, p->sock);
   if (c.size < sizeof(struct agentx_response))
     snmp_manage_tbuf(p, &c);
 
@@ -823,7 +715,7 @@ parse_set_pdu(struct snmp_proto *p, byte * const pkt_start, uint size, enum agen
   }
 
   TRACE(D_PACKETS, "SNMP received set PDU with error %u", c.error);
-  response_err_ind(r, c.error, 0);
+  response_err_ind(p, r, c.error, 0);
   sk_send(p->sock, AGENTX_HEADER_SIZE);
 
   /* Reset the connection on unrecoverable error */
@@ -847,7 +739,7 @@ parse_commit_set_pdu(struct snmp_proto *p, byte *pkt, uint size)
   // 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_set_pdu(p, pkt, size, AGENTX_RES_COMMIT_FAILED);
+  return parse_sets_pdu(p, pkt, size, AGENTX_RES_COMMIT_FAILED);
 }
 
 /*
@@ -864,7 +756,7 @@ parse_undo_set_pdu(struct snmp_proto *p, byte *pkt, uint size)
   // 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_set_pdu(p, pkt, size, AGENTX_RES_UNDO_FAILED);
+  return parse_sets_pdu(p, pkt, size, AGENTX_RES_UNDO_FAILED);
 }
 
 /*
@@ -899,6 +791,22 @@ parse_cleanup_set_pdu(struct snmp_proto *p, byte * const pkt_start, uint size)
   return pkt_size;
 }
 
+/*
+ * 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)
+  );
+}
+
 /**
  * parse_pkt - parse received AgentX packet
  * @p: SNMP protocol instance
@@ -911,12 +819,21 @@ parse_cleanup_set_pdu(struct snmp_proto *p, byte * const pkt_start, uint size)
 static uint
 parse_pkt(struct snmp_proto *p, byte *pkt, uint size, uint *skip)
 {
+  /* 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;
   uint pkt_size = LOAD_U32(h->payload);
 
+  if (pkt_size > SNMP_PKT_SIZE_MAX)
+    return simple_response(p, AGENTX_RES_GEN_ERR, 0);
+
   /* 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
@@ -924,9 +841,11 @@ parse_pkt(struct snmp_proto *p, byte *pkt, uint size, uint *skip)
   if (h->type == AGENTX_RESPONSE_PDU)
     return parse_response(p, pkt, size);
 
+  ASSERT(snmp_is_active(p));
   if (p->state != SNMP_CONN ||
       p->session_id != LOAD_U32(h->session_id))
   {
+    // TODO: resolve issues connected to partial processed packets
     struct agentx_header copy = {
       .session_id = p->session_id,
       .transaction_id = p->transaction_id,
@@ -939,23 +858,25 @@ parse_pkt(struct snmp_proto *p, byte *pkt, uint size, uint *skip)
     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);
 
-    // TODO: fix the parsed size for possibly malitious packets
-    // this could be broken when sender sends the packet in two parts
-    // -> size < pkt_size;
-    // maliciously large pkt_size -> breaks the size >= pkt_size
-    // maybe move this test down to parse_*() functions
-    return MIN(size, pkt_size + AGENTX_HEADER_SIZE);
+    /*
+     * After unexpected state, we simply reset the session
+     * only sending the agentx-Response-PDU.
+     */
+    snmp_set_state(p, SNMP_RESET);
+    return 0;
   }
 
+  ASSERT(snmp_is_active(p));
   if (h->flags & AGENTX_NON_DEFAULT_CONTEXT)
   {
-    // TODO: shouldn't we return a parseError or genError for
-    // packets that mustn't have a non-default context ??
+    // TODO: add non-default context support
     TRACE(D_PACKETS, "SNMP received PDU with unexpected byte order");
     snmp_simple_response(p, AGENTX_RES_UNSUPPORTED_CONTEXT, 0);
-    // TODO: fix the parsed size (as above)
-    return MIN(size, pkt_size + AGENTX_HEADER_SIZE);
+    /* We always accept the packet length as correct, up to set limit */
+    // TODO limit
+    return pkt_size + AGENTX_HEADER_SIZE;
   }
 
   refresh_ids(p, h);
@@ -982,10 +903,10 @@ parse_pkt(struct snmp_proto *p, byte *pkt, uint size, uint *skip)
       return parse_cleanup_set_pdu(p, pkt, size);
 
     default:
+      /* We reset the connection for malformed packet (Unknown packet type) */
       TRACE(D_PACKETS, "SNMP received unknown packet with type %u", h->type);
-      snmp_set_state(p, SNMP_DOWN);
-      //  TODO reset connection here
-      return AGENTX_HEADER_SIZE;
+      snmp_set_state(p, SNMP_RESET);
+      return 0;
   }
 }
 
@@ -1001,21 +922,22 @@ parse_pkt(struct snmp_proto *p, byte *pkt, uint size, uint *skip)
 static uint
 parse_response(struct snmp_proto *p, byte *res, uint size)
 {
+  log(L_WARN "parse response");
   if (size < sizeof(struct agentx_response))
     return 0;
 
   struct agentx_response *r = (void *) res;
-  struct agentx_header *h = &r->h;
+  struct agentx_header *h = (void *) r;
 
   // todo reject not compiled byte order
   uint pkt_size = LOAD_U32(h->payload);
   if (size < pkt_size + AGENTX_HEADER_SIZE)
     return 0;
 
-  TRACE(D_PACKETS, "SNMP received agentx-Response-PDU with error %u", r->error);
   switch (r->error)
   {
     case AGENTX_RES_NO_ERROR:
+      TRACE(D_PACKETS, "SNMP received agetnx-Response-PDU");
       do_response(p, res, size);
       break;
 
@@ -1024,6 +946,7 @@ parse_response(struct snmp_proto *p, byte *res, uint size)
     case AGENTX_RES_REQUEST_DENIED:
     case AGENTX_RES_UNKNOWN_REGISTER:
       // TODO: more direct path to mib-specifiec code
+      TRACE(D_PACKETS, "SNMP received agentx-Response-PDU with error %u", r->error);
       snmp_register_ack(p, r, size);
       break;
 
@@ -1070,7 +993,7 @@ static void
 do_response(struct snmp_proto *p, byte *buf, uint size)
 {
   struct agentx_response *r = (void *) buf;
-  struct agentx_header *h = &r->h;
+  struct agentx_header *h = (void *) r;
 
   /* TODO make it asynchronous for better speed */
   switch (p->state)
@@ -1161,9 +1084,15 @@ snmp_get_next2(struct snmp_proto *p, struct oid *o_start, struct oid *o_end,
     case SNMP_SEARCH_END_OF_VIEW:;
       uint sz = snmp_varbind_hdr_size_from_oid(o_start);
 
-      if (c->size < sz)
+      if (c->size < sz && c->size >= sizeof(struct agentx_varbind))
+      {
+       struct agentx_varbind *vb_null = snmp_create_varbind_null(c->buffer);
+       ADVANCE(c->buffer, c->size, snmp_varbind_size_unsafe(vb_null));
+       c->error = AGENTX_RES_GEN_ERROR;
+       return 0;
+      }
+      else if (c->size < sz)
       {
-       /* TODO create NULL varbind */
        c->error = AGENTX_RES_GEN_ERROR;
        return 0;
       }
@@ -1199,10 +1128,7 @@ snmp_get_next2(struct snmp_proto *p, struct oid *o_start, struct oid *o_end,
   }
 
   if (c->size < snmp_varbind_hdr_size_from_oid(o_start))
-  {
-    // TODO FIXME this is a bit tricky as we need to renew all TX buffer pointers
     snmp_manage_tbuf(p, c);
-  }
 
   vb = snmp_create_varbind(c->buffer, o_start);
   vb->type = AGENTX_END_OF_MIB_VIEW;
@@ -1286,28 +1212,44 @@ snmp_get_bulk2(struct snmp_proto *p, struct oid *o_start, struct oid *o_end,
  * Return number of bytes in TX-buffer (including header size).
  */
 static inline uint
-update_packet_size(struct snmp_proto *p, const byte *start, byte *end)
+update_packet_size(struct snmp_proto *p, struct agentx_header *start, byte *end)
 {
-  struct agentx_header *h = (void *) p->sock->tpos;
-  size_t s = snmp_pkt_len(start, end);
-  STORE_U32(h->payload, s);
+  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.
+ * 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 agentx_response *res, enum agentx_response_errs err, u16 ind)
+response_err_ind(struct snmp_proto *p, struct agentx_response *res, enum agentx_response_errs err, u16 ind)
 {
   STORE_U32(res->error, (u16) err);
-  if (err != AGENTX_RES_NO_ERROR && err != AGENTX_RES_PARSE_ERROR)
+  // TODO deal with auto-incrementing of snmp_pdu context c.ind
+  if (err != AGENTX_RES_NO_ERROR && err != AGENTX_RES_GEN_ERROR)
+  {
+    TRACE(D_PACKETS, "Last PDU resulted in error %u", err);
     STORE_U32(res->index, ind);
+    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,
+      sizeof(struct agentx_response) - AGENTX_HEADER_SIZE);
+  }
+  else if (err == AGENTX_RES_GEN_ERROR)
+  {
+    TRACE(D_PACKETS, "Last PDU resulted in error %u", 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,
+      sizeof(struct agentx_response) - AGENTX_HEADER_SIZE);
+  }
   else
     STORE_U32(res->index, 0);
 }
@@ -1337,8 +1279,7 @@ parse_gets2_pdu(struct snmp_proto *p, byte * const pkt_start, uint size, uint *s
 
   sock *sk = p->sock;
   struct snmp_pdu c;
-  snmp_pdu_context(&c, sk);
-  // TODO better handling of endianness
+  snmp_pdu_context(p, &c, sk);
 
   /*
    * Get-Bulk processing stops if all the varbind have type END_OF_MIB_VIEW
@@ -1355,7 +1296,7 @@ parse_gets2_pdu(struct snmp_proto *p, byte * const pkt_start, uint size, uint *s
     {
       c.error = AGENTX_RES_PARSE_ERROR;
       c.index = 0;
-      ret = MIN(size, pkt_size + AGENTX_HEADER_SIZE);
+      ret = pkt_size + AGENTX_HEADER_SIZE;
       goto error;
     }
 
@@ -1373,12 +1314,6 @@ parse_gets2_pdu(struct snmp_proto *p, byte * const pkt_start, uint size, uint *s
     };
   }
 
-  if (c.size < sizeof(struct agentx_response))
-  {
-    snmp_manage_tbuf(p, &c);
-    // TODO renew pkt, pkt_start pointers
-  }
-
   struct agentx_response *response_header = prepare_response(p, &c);
 
   while (c.error == AGENTX_RES_NO_ERROR && size > 0 && pkt_size > 0)
@@ -1403,7 +1338,7 @@ parse_gets2_pdu(struct snmp_proto *p, byte * const pkt_start, uint size, uint *s
      */
     if (sz > size && c.index > 0)
     {
-      goto partial;  /* send already processed part */
+      goto partial;
     }
     else if (sz > size)
     {
@@ -1446,6 +1381,9 @@ parse_gets2_pdu(struct snmp_proto *p, byte * const pkt_start, uint size, uint *s
     o_start = snmp_prefixize(p, o_start_b);
     o_end = snmp_prefixize(p, o_end_b);
 
+    ASSERT(o_start);
+    ASSERT(o_end);
+
     if (!snmp_is_oid_empty(o_end) && snmp_oid_compare(o_start, o_end) > 0)
     {
       c.error = AGENTX_RES_GEN_ERROR;
@@ -1505,21 +1443,23 @@ parse_gets2_pdu(struct snmp_proto *p, byte * const pkt_start, uint size, uint *s
 
   /* send the constructed packet */
   struct agentx_response *res;
-  if (p->last_header)
+  if (snmp_is_partial(p))
   {
-    res = p->last_header;
-    p->last_header = NULL;
-    p->last_size = 0;
+    snmp_log("snmp_is_partial() <pkt> packet_id %u last_pkt_id %u [proto packet_id] %u", LOAD_U32(res->h.packet_id), p->last_pkt_id, p->packet_id);
+    res = SKIP_BACK(struct agentx_response, h, snmp_get_header(p));
+    ASSERT(LOAD_U32(res->h.packet_id) == p->last_pkt_id);
+    STORE_U32(res->h.packet_id, p->last_pkt_id);
+    snmp_unset_header(p);
   }
   else
     res = response_header;
 
   /* We update the error, index pair on the beginning of the packet. */
-  response_err_ind(res, c.error, c.index + 1);
-  uint s = update_packet_size(p, (byte *) res, c.buffer);
+  response_err_ind(p, res, c.error, c.index + 1);
+  uint s = update_packet_size(p, &res->h, c.buffer);
 
   /* We send the message in TX-buffer. */
-  p->last_header = NULL;
+  snmp_unset_header(p);
   sk_send(sk, s);
   // TODO think through the error state
 
@@ -1529,31 +1469,28 @@ parse_gets2_pdu(struct snmp_proto *p, byte * const pkt_start, uint size, uint *s
 
 partial:
   /* need to tweak RX buffer packet size */
-  STORE_U32(h->payload, pkt_size);
+  //STORE_U32(h->payload, pkt_size);
   *skip = AGENTX_HEADER_SIZE;
-  p->last_header = h;
-  p->last_size = c.buffer - sk->tpos;
+  snmp_set_header(p, &response_header->h, &c);
 
   /* number of bytes parsed from RX-buffer */
   ret = pkt - pkt_start;
+  /* update the packet length such that the used bytes are not included */
+  STORE_U32(h->payload, LOAD_U32(h->payload) - ret + AGENTX_HEADER_SIZE);
   goto free;
 
 wait:
   p->packet_id--; /* we did not use the packetID */
+  log(L_INFO "decrementing back the packet_id to %u", p->packet_id);
   ret = 0;
   goto free;
 
 error:
+  TRACE(D_PACKETS, "SNMP error %u while parsing gets PDU", c.error);
   if (c.index > UINT16_MAX)
-  {
-    TRACE(D_PACKETS, "SNMP error %u while parsing gets PDU");
     snmp_simple_response(p, AGENTX_RES_GEN_ERROR, UINT16_MAX);
-  }
   else
-  {
-    TRACE(D_PACKETS, "SNMP error %u while parsing gets PDU", c.error);
     snmp_simple_response(p, c.error, c.index);
-  }
 
 free:
   mb_free(o_start);
@@ -1570,12 +1507,12 @@ free:
 void
 snmp_start_subagent(struct snmp_proto *p)
 {
+  ASSUME(p->state == SNMP_OPEN);
+
   /* blank oid means unsupported */
   struct oid *blank = snmp_oid_blank(p);
   open_pdu(p, blank);
 
-  p->state = SNMP_OPEN;
-
   mb_free(blank);
 }
 
@@ -1600,30 +1537,37 @@ snmp_stop_subagent(struct snmp_proto *p)
 int
 snmp_rx(sock *sk, uint size)
 {
+  log(L_INFO "snmp_rx with size %u", size);
   struct snmp_proto *p = sk->data;
   byte *pkt_start = sk->rbuf;
   byte *end = pkt_start + size;
 
+  byte *last_pkt = pkt_start;
+
   /*
    * In some cases we want to save the header for future parsing, skip is number
    * of bytes that should not be overriden by memmove()
    */
   uint skip = 0;
 
-  while (end >= pkt_start + AGENTX_HEADER_SIZE && skip == 0)
+  while (snmp_is_active(p) && end >= pkt_start + AGENTX_HEADER_SIZE && skip == 0)
   {
     uint parsed_len = parse_pkt(p, pkt_start, size, &skip);
 
     if (parsed_len == 0)
       break;
 
+    last_pkt = pkt_start;
     pkt_start += parsed_len;
     size -= parsed_len;
   }
 
-  /* Incomplete packets */
+  if (!snmp_is_active(p))
+    return 1;
+
   if (skip != 0 || pkt_start != end)
   {
+    memmove(sk->rbuf, last_pkt, skip); /* maybe no op */
     memmove(sk->rbuf + skip, pkt_start, size);
     sk->rpos = sk->rbuf + size + skip;
     return 0;
@@ -1632,6 +1576,28 @@ snmp_rx(sock *sk, uint size)
   return 1;
 }
 
+/*
+ * snmp_tx - handle TX-buffer
+ * @sk: communication socket owned by SNMP protocol instance
+ *
+ * The snmp_tx hook is used only to delay the processing in cases we don't have
+ * enough space in TX-buffer. Therefore we simply call the snmp_rx hook.
+ */
+void
+snmp_tx(sock *sk)
+{
+  log(L_INFO "snmp_tx()");
+  /* We still not have enough space */
+  if (!space_for_response(sk))
+    return;
+
+  /* There is nothing to process, no bytes in RX-buffer */
+  if (sk_tx_buffer_empty(sk))
+    return;
+
+  snmp_rx(sk, sk->tpos - sk->tbuf);
+}
+
 
 /*
  * snmp_ping - send an agentx-Ping-PDU
@@ -1640,30 +1606,40 @@ snmp_rx(sock *sk, uint size)
 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, sk);
+  snmp_pdu_context(p, &c, sk);
 
   if (c.size < AGENTX_HEADER_SIZE)
     return;
 
-  int unused = (sk->tbsize - (sk->tpos - sk->tbuf)) - (p->last_size + AGENTX_HEADER_SIZE);
-  if (p->last_header && unused >= 0)
-  {
-    
-  }
-  else if (p->last_header)
+  int unused = sk->tbuf + sk->tbsize - c.buffer;
+  if (snmp_is_partial(p) && unused >= AGENTX_HEADER_SIZE)
   {
+    /* We use the start of unsent TX-buffer to fit in the agentx-Ping-PDU */
+    memmove(sk->tpos + AGENTX_HEADER_SIZE, sk->tpos, p->last_size);
+    p->header_offset += AGENTX_HEADER_SIZE;
   }
+  else if (snmp_is_partial(p))
+    /* Not enough space inside the buffer */
+    return;
 
-  struct agentx_header *h = (struct agentx_header *) c.buffer;
+  /* we do not use the snmp_create_tx_header() because of side effects */
+  struct agentx_header *h = (void *) sk->tpos;
   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_session(p, h);
+  
+  if (snmp_is_partial(p)) // TODO remove me
+    snmp_log("snmp_ping() <pkt> packet_id %u last_pkt_id %u [proto packet_id] ))%u", LOAD_U32(h->packet), p->last_pkt_id, p->packet_id);
 
-  /* sending only header -> pkt - buf */
-  uint s = update_packet_size(p, sk->tpos, c.buffer);
+  /* sending only header */
+  uint s = update_packet_size(p, h, (byte *) h + AGENTX_HEADER_SIZE);
 
   sk_send(sk, s);
 }
@@ -1777,7 +1753,7 @@ search_mib(struct snmp_proto *p, const struct oid *o_start, const struct oid *o_
 struct oid *
 snmp_prefixize(struct snmp_proto *proto, const struct oid *oid)
 {
-  ASSERT(oid != NULL);
+  ASSUME(oid != NULL);
 
   if (snmp_is_oid_empty(oid))
   {
@@ -1870,13 +1846,11 @@ snmp_mib_fill2(struct snmp_proto *p, struct oid *oid, struct snmp_pdu *c)
 void
 snmp_manage_tbuf(struct snmp_proto UNUSED *p, struct snmp_pdu *c)
 {
-  /*
   sock *sk = p->sock;
 
+  log(L_INFO "snmp_manage_tbuf()");
   sk_set_tbsize(sk, sk->tbsize + 2048);
-  // XXX buffer snmp_pdu pointer
   c->size += 2048;
-  */
 }
 
 /*
@@ -1889,11 +1863,11 @@ snmp_manage_tbuf(struct snmp_proto UNUSED *p, struct snmp_pdu *c)
 static struct agentx_response *
 prepare_response(struct snmp_proto *p, struct snmp_pdu *c)
 {
-  if (p->last_header)
-    return p->last_header;
+  if (snmp_is_partial(p))
+    return (struct agentx_response *) snmp_get_header(p);
 
   struct agentx_response *r = (void *) c->buffer;
-  struct agentx_header *h = &r->h;
+  struct agentx_header *h = snmp_create_tx_header(p, (byte *) r);
 
   snmp_blank_header(h, AGENTX_RESPONSE_PDU);
   snmp_session(p, h);
index 9a263a28bacc430adba1734a334ed6f8b44e2d05..c91206ea7c2dff2e3b73fd1cb322ea78c87ab631 100644 (file)
@@ -36,8 +36,6 @@ enum SNMP_CLASSES {
   SNMP_CLASS_END,
 };
 
-#define BGP4_VERSIONS ((char[]) { 0x10 })
-
 enum agentx_type {
   AGENTX_INTEGER           =   2,
   AGENTX_OCTET_STRING      =   4,
@@ -52,6 +50,8 @@ enum agentx_type {
   AGENTX_NO_SUCH_OBJECT            = 128,
   AGENTX_NO_SUCH_INSTANCE   = 129,
   AGENTX_END_OF_MIB_VIEW    = 130,
+
+  AGENTX_INVALID           =  -1,
 } PACKED;
 
 enum snmp_search_res {
@@ -158,7 +158,8 @@ struct agentx_header {
   u32 payload;         /* payload_length of the packet without header */
 };
 
-#define AGENTX_HEADER_SIZE sizeof(struct agentx_header)
+#define AGENTX_HEADER_SIZE 20
+STATIC_ASSERT(AGENTX_HEADER_SIZE == sizeof(struct agentx_header));
 
 struct oid {
   u8 n_subid;
@@ -176,10 +177,15 @@ struct agentx_varbind {
   /* AgentX variable binding data optionaly here */
 };
 
-/* this does not work */
 struct agentx_search_range {
-  struct oid start;
-  struct oid end;
+  struct oid *start;
+  struct oid *end;
+};
+
+/* AgentX Octet String */
+struct agentx_octet_str {
+  u32 length;
+  byte data[0];
 };
 
 struct agentx_getbulk {
@@ -194,9 +200,13 @@ struct agentx_response {
   u16 index;
 };
 
+STATIC_ASSERT(4 + 2 + 2 + AGENTX_HEADER_SIZE == sizeof(struct agentx_response));
+
 struct agentx_close_pdu {
   struct agentx_header h;
   u8 reason;
+  u8 pad1;
+  u16 pad2;
 };
 
 struct agentx_un_register_hdr {
@@ -279,7 +289,7 @@ enum agentx_response_errs {
   AGENTX_RES_PROCESSING_ERR        = 268,      /* processingError */
 } PACKED;
 
-/* SNMP PDU buffer info */
+/* SNMP PDU TX-buffer info */
 struct snmp_pdu {
   byte *buffer;                            /* pointer to buffer */
   uint size;                       /* unused space in buffer */
@@ -287,6 +297,15 @@ struct snmp_pdu {
   u32 index;                       /* index on which the error was found */
 };
 
+struct snmp_packet_info {
+  node n;
+  u8 type; // enum type
+  u32 session_id;
+  u32 transaction_id;
+  u32 packet_id;
+  void *data;
+};
+
 #if 0
 struct agentx_alloc_context {
   u8 is_instance; /* flag INSTANCE_REGISTRATION */