From a737f7a4d8000539f9aa3dda2f04cab6d1318959 Mon Sep 17 00:00:00 2001
From: Vojtech Vilimek <vojtech.vilimek@nic.cz>
Date: Wed, 25 Oct 2023 16:57:46 +0200
Subject: [PATCH] SNMP: Improvements in registration handling

---
 proto/snmp/bgp_mib.c    | 27 ++++++++++++++-----
 proto/snmp/bgp_mib.h    |  2 ++
 proto/snmp/snmp.c       |  8 +++---
 proto/snmp/snmp_utils.c |  2 ++
 proto/snmp/subagent.c   | 57 ++++++++++++++++++++++-------------------
 5 files changed, 60 insertions(+), 36 deletions(-)

diff --git a/proto/snmp/bgp_mib.c b/proto/snmp/bgp_mib.c
index f3e9b347..7695ce87 100644
--- a/proto/snmp/bgp_mib.c
+++ b/proto/snmp/bgp_mib.c
@@ -195,6 +195,23 @@ snmp_bgp_state(const struct oid *oid)
   return state;
 }
 
+void
+snmp_bgp_reg_ok(struct snmp_proto *p, struct agentx_response *r, struct oid *oid)
+{
+  const struct oid *in_buf = ((void *) r) + sizeof(r);
+  int byte_ord = r->h.flags & AGENTX_NETWORK_BYTE_ORDER;
+  struct oid *dup = snmp_prefixize(p, in_buf, byte_ord);
+
+  ASSUME(snmp_bgp_state(oid) == snmp_bgp_state(dup));
+  mb_free(dup);
+}
+
+void
+snmp_bgp_reg_failed(struct snmp_proto *p, struct agentx_response UNUSED *r, struct oid UNUSED *oid)
+{
+  snmp_stop_subagent(p);
+}
+
 static void
 snmp_bgp_notify_common(struct snmp_proto *p, uint type, ip4_addr ip4, char last_error[], uint state_val)
 {
@@ -315,15 +332,13 @@ snmp_bgp_register(struct snmp_proto *p)
     /* Register the whole BGP4-MIB::bgp root tree node */
     struct snmp_register *registering = snmp_register_create(p, SNMP_BGP4_MIB);
 
-    struct oid *oid = mb_alloc(p->pool, snmp_oid_sizeof(2));
-    STORE_U8(oid->n_subid, 2);
+    struct oid *oid = mb_alloc(p->pool,
+      snmp_oid_sizeof(ARRAY_SIZE(bgp_mib_prefix)));
+    STORE_U8(oid->n_subid, ARRAY_SIZE(bgp_mib_prefix));
     STORE_U8(oid->prefix, SNMP_MGMT);
 
-    memcpy(oid->ids, bgp_mib_prefix, 2 * sizeof(u32));
-
+    memcpy(oid->ids, bgp_mib_prefix, sizeof(bgp_mib_prefix));
     registering->oid = oid;
-    add_tail(&p->register_queue, &registering->n);
-    p->register_to_ack++;
 
     /* snmp_register(struct snmp_proto *p, struct oid *oid, uint index, uint len, u8 is_instance, uint contid) */
     snmp_register(p, oid, 1, 0, SNMP_REGISTER_TREE, SNMP_DEFAULT_CONTEXT);
diff --git a/proto/snmp/bgp_mib.h b/proto/snmp/bgp_mib.h
index b07d3c92..7e6d35dd 100644
--- a/proto/snmp/bgp_mib.h
+++ b/proto/snmp/bgp_mib.h
@@ -39,6 +39,8 @@ enum BGP4_MIB_PEER_TABLE {
 struct oid;
 
 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);
 
 u8 snmp_bgp_get_valid(u8 state);
 u8 snmp_bgp_getnext_valid(u8 state);
diff --git a/proto/snmp/snmp.c b/proto/snmp/snmp.c
index 94118ce0..1938d565 100644
--- a/proto/snmp/snmp.c
+++ b/proto/snmp/snmp.c
@@ -225,9 +225,14 @@ snmp_start_locked(struct object_lock *lock)
 {
   struct snmp_proto *p = lock->data;
 
+  snmp_log("changing state to LOCKED");
   p->state = SNMP_LOCKED;
   sock *s = p->sock;
 
+  p->to_send = 0;
+  p->errs = 0;
+
+
   if (!p->bgp_trie)
     p->bgp_trie = f_new_trie(p->lp, 0);  // TODO user-data attachment size
 
@@ -348,9 +353,6 @@ snmp_start(struct proto *P)
   struct snmp_proto *p = (void *) P;
   struct snmp_config *cf = (struct snmp_config *) P->cf;
 
-  p->to_send = 0;
-  p->errs = 0;
-
   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, 0, 0);
 
diff --git a/proto/snmp/snmp_utils.c b/proto/snmp/snmp_utils.c
index 516cc92d..6fb4d3d4 100644
--- a/proto/snmp/snmp_utils.c
+++ b/proto/snmp/snmp_utils.c
@@ -416,6 +416,8 @@ snmp_register_create(struct snmp_proto *p, u8 mib_class)
 
   r->mib_class = mib_class;
 
+  add_tail(&p->register_queue, &r->n);
+
   return r;
 }
 
diff --git a/proto/snmp/subagent.c b/proto/snmp/subagent.c
index 28283add..bace2933 100644
--- a/proto/snmp/subagent.c
+++ b/proto/snmp/subagent.c
@@ -77,14 +77,29 @@ static const char * const snmp_pkt_type[] UNUSED = {
   [AGENTX_RESPONSE_PDU]		  =  "Response-PDU",
 };
 
+
+static void
+snmp_register_ok(struct snmp_proto *p, struct agentx_response *res, struct oid *oid, u8 UNUSED class)
+{
+  // TODO switch based on oid type
+  snmp_bgp_reg_ok(p, res, oid);
+}
+
+static void
+snmp_register_failed(struct snmp_proto *p, struct agentx_response *res, struct oid *oid, u8 UNUSED class)
+{
+  // TODO switch based on oid type
+  snmp_bgp_reg_failed(p, res, oid);
+}
+
 void
-snmp_register_ack(struct snmp_proto *p, struct agentx_header *h, u8 class)
+snmp_register_ack(struct snmp_proto *p, struct agentx_response *res, u8 class)
 {
   struct snmp_register *reg;
   WALK_LIST(reg, p->register_queue)
   {
     // TODO add support for more mib trees (other than BGP)
-    if (snmp_register_same(reg, h, class))
+    if (snmp_register_same(reg, &res->h, class))
     {
       struct snmp_registered_oid *ro = \
 	 mb_alloc(p->p.pool, sizeof(struct snmp_registered_oid));
@@ -98,6 +113,11 @@ snmp_register_ack(struct snmp_proto *p, struct agentx_header *h, u8 class)
       p->register_to_ack--;
 
       add_tail(&p->bgp_registered, &ro->n);
+
+      if (res->error == AGENTX_RES_NO_ERROR)
+	snmp_register_ok(p, res, ro->oid, class);
+      else
+	snmp_register_failed(p, res, ro->oid, class);
       return;
     }
   }
@@ -373,7 +393,7 @@ parse_close_pdu(struct snmp_proto *p, byte * const pkt_start, uint size)
     static int snmp_testset(struct snmp_proto *p, const struct agentx_varbind *vb, uint pkt_size);
  */
 /* return 1 if the value could be set */
-static int
+static int UNUSED
 snmp_testset(struct snmp_proto *p, const struct agentx_varbind *vb, struct oid *oid, uint pkt_size)
 {
   /* Hard-coded no support for writing */
@@ -496,6 +516,9 @@ parse_test_set_pdu(struct snmp_proto *p, byte * const pkt_start, uint size)
   ADVANCE(pkt, size, AGENTX_HEADER_SIZE);
   uint pkt_size = LOAD_U32(h->payload, h->flags & AGENTX_NETWORK_BYTE_ORDER);
 
+  if (pkt_size < size)
+    return 0;
+
   sock *sk = p->sock;
   struct snmp_pdu c = SNMP_PDU_CONTEXT(sk);
   c.byte_ord = 0; /* use little-endian */
@@ -747,24 +770,6 @@ parse_pkt(struct snmp_proto *p, byte *pkt, uint size, uint *skip)
   }
 }
 
-static void
-snmp_register_ok(struct snmp_proto *p, struct agentx_response *r, uint size, u8 type)
-{
-  (void)p;(void)r;(void)size;(void)type;
-}
-
-static void
-snmp_register_failed(struct snmp_proto *p, struct agentx_response *r, uint size, u8 type)
-{
-  (void)p;(void)r;(void)size;(void)type;
-}
-
-static void
-unsupported_context(struct snmp_proto *p, struct agentx_response *r, uint size)
-{
-  (void)p;(void)r;(void)size;
-  // TODO unsupported_context
-}
 
 static uint
 parse_response(struct snmp_proto *p, byte *res, uint size)
@@ -814,17 +819,15 @@ parse_response(struct snmp_proto *p, byte *res, uint size)
     /* Registration errors */
     case AGENTX_RES_DUPLICATE_REGISTER:
     case AGENTX_RES_REQUEST_DENIED:
-      snmp_register_failed(p, r, size, h->type);
-      break;
-
-    case AGENTX_RES_UNSUPPORTED_CONTEXT:
-      unsupported_context(p, r, size);
+      // TODO: more direct path to mib-specifiec code
+      snmp_register_ack(p, r, size);
       break;
 
     /* We are trying to unregister a MIB, the unknownRegistration has same
      * effect as success */
     case AGENTX_RES_UNKNOWN_REGISTER:
     case AGENTX_RES_UNKNOWN_AGENT_CAPS:
+    case AGENTX_RES_UNSUPPORTED_CONTEXT:
     case AGENTX_RES_PARSE_ERROR:
     case AGENTX_RES_PROCESSING_ERR:
     default:
@@ -878,7 +881,7 @@ do_response(struct snmp_proto *p, byte *buf, uint size)
 
       const struct oid *oid = (void *) pkt;
 
-      snmp_register_ack(p, h, snmp_get_mib_class(oid));
+      snmp_register_ack(p, r, snmp_get_mib_class(oid));
 
       if (p->register_to_ack == 0)
       {