]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
SNMP: Bare support for agentx-TestSet-PDU
authorVojtech Vilimek <vojtech.vilimek@nic.cz>
Thu, 19 Oct 2023 14:27:04 +0000 (16:27 +0200)
committerVojtech Vilimek <vojtech.vilimek@nic.cz>
Thu, 19 Oct 2023 14:27:04 +0000 (16:27 +0200)
proto/snmp/bgp_mib.c
proto/snmp/bgp_mib.h
proto/snmp/subagent.c
proto/snmp/subagent.h

index 59fb7524d6624bca6dc309dda0b3cba9a6746d18..64782cdc36bfd6b3490da02033652a432228c394 100644 (file)
@@ -1302,3 +1302,12 @@ snmp_bgp_fill(struct snmp_proto *p, struct agentx_varbind *vb,
   ADVANCE(c->buffer, c->size, snmp_varbind_header_size(vb));
 }
 
+#if 0
+int
+snmp_bgp_testset(struct snmp_proto *p, const struct agentx_varbind *vb, void *tr, struct oid *oid, uint pkt_size)
+{
+  // TODO: check the type of varbind vb and it's value correctness, don't overflow the pkt_size
+  return 0;
+}
+#endif
+
index 3d6f07eef4527e25cb5181c95a5233fee6a4a87e..b07d3c92e647f41134ea9fd36935dfe185bebd3a 100644 (file)
@@ -46,6 +46,7 @@ u8 snmp_bgp_getnext_valid(u8 state);
 struct oid *snmp_bgp_search(struct snmp_proto *p, struct oid *o_start, struct oid *o_end, uint contid);
 enum snmp_search_res snmp_bgp_search2(struct snmp_proto *p, struct oid **searched, const struct oid *o_end, uint contid);
 void snmp_bgp_fill(struct snmp_proto *p, struct agentx_varbind *vb, struct snmp_pdu *c);
+//int snmp_bgp_testset(struct snmp_proto *p, const struct agentx_varbind *vb, void* tr, struct oid *oid, uint pkt_type);
 
 void snmp_bgp_notify_established(struct snmp_proto *p, struct bgp_proto *bgp);
 void snmp_bgp_notify_backward_trans(struct snmp_proto *p, struct bgp_proto *bgp);
index 1ace2c439f458a16527999fd305d8282a4daa55b..47232b356d87f633345ea13fd80eb66ddd643596 100644 (file)
@@ -328,6 +328,59 @@ close_pdu(struct snmp_proto *p, u8 reason)
   sk_send(sk, s);
 }
 
+static uint UNUSED
+parse_close_pdu(struct snmp_proto UNUSED *p, byte UNUSED *req, uint UNUSED size)
+{
+  /*
+  snmp_log("parse_close_pdu()");
+
+  // byte *pkt = req;
+  // sock *sk = p->sock;
+
+  if (size < sizeof(struct agentx_header))
+  {
+    snmp_log("p_close early return");
+    return 0;
+  }
+
+  // struct agentx_header *h = (void *) req;
+  ADVANCE(req, size, AGENTX_HEADER_SIZE);
+  //snmp_log("after header %p", req);
+
+  p->state = SNMP_ERR;
+
+  // or snmp_cleanup(); // ??!
+  proto_notify_state(&p->p, PS_DOWN);
+  */
+  return 0;
+}
+
+/* MUCH better signature would be
+    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
+snmp_testset(struct snmp_proto *p, const struct agentx_varbind *vb, struct oid *oid, uint pkt_size)
+{
+  /* Hard-coded no support for writing */
+  (void)p;(void)vb;(void)oid;(void)pkt_size;
+  return 0;
+#if 0
+  // TODO better logic
+  if (!oid)
+    return 0;
+
+  switch (oid->ids[1])
+  {
+    case SNMP_BGP4_MIB:
+      return snmp_bgp_testset(p, vb, oid, pkt_size);
+    default:
+      return 0;
+  }
+#endif
+}
+
+
 #if 0
 static void UNUSED
 addagentcaps_pdu(struct snmp_proto *p, struct oid *cap, char *descr,
@@ -420,6 +473,194 @@ refresh_ids(struct snmp_proto *p, struct agentx_header *h)
   p->packet_id = LOAD_U32(h->packet_id, byte_ord);
 }
 
+static uint
+parse_test_set_pdu(struct snmp_proto *p, byte *pkt, uint size)
+{
+  //snmp_log("parse_test_set");
+  const byte *pkt_start = pkt;   /* start of packet in RX-buffer */
+  uint ind = 0; /* index of the error */
+  uint s; /* final packat size */
+  struct agentx_response *res; /* pointer to reponse in TX-buffer */
+
+  struct agentx_header *h = (void *) pkt;
+  ADVANCE(pkt, size, AGENTX_HEADER_SIZE);
+  uint pkt_size = LOAD_U32(h->payload, h->flags & AGENTX_NETWORK_BYTE_ORDER);
+
+  sock *sk = p->sock;
+  struct snmp_pdu c = SNMP_PDU_CONTEXT(sk);
+  byte *buf = c.buffer;        /* start of packet in TX-buffer */
+  c.byte_ord = 0; /* use little-endian */
+
+  if (c.size < AGENTX_HEADER_SIZE)
+  {
+    snmp_manage_tbuf(p, &c);
+    // TODO renew all pointers
+  }
+
+  res = prepare_response(p, &c);
+
+  uint clen;
+  const char *context;
+  SNMP_LOAD_CONTEXT(h, pkt, context, clen);
+
+  if (size < clen)
+    return 0;
+
+  if (pkt_size < clen)
+  {
+    c.error = AGENTX_RES_PARSE_ERROR;
+    goto error;
+  }
+
+  ADVANCE(pkt, pkt_size, clen);
+  size -= clen;
+
+  //snmp_log("test_set: parsed header and context");
+  /* 0 if there is piece, that we cannot set */
+  int all_possible = 0;
+  /* the all_possible is currently hard-coded with no support for writing to mib
+   * variables, when implementing the mentioned support, change the initializer
+   * to 1
+   */
+
+#if 0
+  // TODO think about future value setting data structure
+  //struct agentx_transaction *tr = mb_alloc(...);
+  void *tr = mb_alloc(p->pool, 16);
+
+  struct agentx_varbind *vb;
+  uint sz;
+  while (size > 0 && all_possible)
+  {
+    vb = (void *) pkt;
+    sz = snmp_varbind_size(vb, 0);
+
+    if (sz > size)
+    /* wait for more data to arive */
+      return 0;
+
+    if (sz > pkt_size)
+    {
+      c.error = AGENTX_RES_PARSE_ERROR;
+      goto error;
+    }
+
+    /* Unknown VarBind type check */
+    if (!snmp_test_varbind(vb))
+    {
+      c.error = AGENTX_RES_PARSE_ERROR;
+      goto error;
+    }
+    ADVANCE(pkt, size, snmp_varbind_size(vb, 0));
+
+    // TODO remove the mb_alloc() in prefixize()
+    struct oid *work = snmp_prefixize(p, &vb->name, c.byte_ord);
+    (void)work;
+    all_possible = snmp_testset(p, vb, tr, work, pkt_size);
+    mb_free(work);
+  }
+  //snmp_log("test_set parsed all varbinds");
+  mb_free(tr);
+#endif
+
+error:
+  s = update_packet_size(p, buf, c.buffer);
+
+  if (c.error != AGENTX_RES_NO_ERROR)
+    response_err_ind(res, c.error, ind + 1);
+  else if (all_possible)
+    response_err_ind(res, AGENTX_RES_NO_ERROR, 0);
+  else
+    //response_err_ind(res, AGENTX_RES_RESOURCE_UNAVAILABLE, ind + 1);
+    response_err_ind(res, AGENTX_RES_NOT_WRITABLE, ind + 1);
+
+  //snmp_log("test_set sending response");
+  sk_send(sk, s);
+  return pkt - pkt_start;
+}
+
+static uint
+parse_set_pdu(struct snmp_proto *p, byte *pkt, uint size, uint err)
+{
+  const byte *pkt_start = pkt;
+
+  if (size < AGENTX_HEADER_SIZE)
+    return 0;
+
+  struct agentx_header *h = (void *) pkt;
+  ADVANCE(pkt, size, AGENTX_HEADER_SIZE);
+  uint pkt_size = LOAD_U32(h->payload, h->flags & AGENTX_NETWORK_BYTE_ORDER);
+
+  struct snmp_pdu c = SNMP_PDU_CONTEXT(p->sock);
+  if (c.size < sizeof(struct agentx_response))
+  {
+    snmp_manage_tbuf(p, &c);
+    // TODO renew all pointers
+  }
+
+  struct agentx_response *r = prepare_response(p, &c);
+
+  uint clen;
+  const char *context;
+  SNMP_LOAD_CONTEXT(h, pkt, context, clen);
+
+  if (size < snmp_str_size_from_len(clen))
+    return 0;
+
+  if (size < pkt_size)
+  {
+    c.error = AGENTX_RES_PARSE_ERROR;
+    goto error;
+  }
+
+  ADVANCE(pkt, size, snmp_str_size_from_len(clen));
+  // TODO: work with context
+
+  // TODO: free resource allocated by parse_test_set_pdu()
+  // TODO: do something meaningful
+  //mb_free(tr);
+  c.error = err;
+
+error:;
+  response_err_ind(r, c.error, 0);
+  sk_send(p->sock, AGENTX_HEADER_SIZE);
+  return pkt - pkt_start;
+}
+
+/* agentx-CommitSet-PDU */
+static uint
+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);
+  return parse_set_pdu(p, pkt, size, AGENTX_RES_COMMIT_FAILED);
+}
+
+/* agentx-UndoSet-PDU */
+static uint
+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);
+  return parse_set_pdu(p, pkt, size, AGENTX_RES_UNDO_FAILED);
+}
+
+/* agentx-CleanupSet-PDU */
+static uint
+parse_cleanup_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);
+
+  if (size < AGENTX_HEADER_SIZE)
+    return 0;
+
+  struct agentx_header *h = (void *) pkt;
+  return LOAD_U32(h->payload, h->flags & AGENTX_NETWORK_BYTE_ORDER);
+
+  /* No agentx-Response-PDU is sent in response to agentx-CleanupSet-PDU */
+}
+
 /**
  * parse_pkt - parse recieved response packet
  * @p:
@@ -437,7 +678,6 @@ parse_pkt(struct snmp_proto *p, byte *pkt, uint size, uint *skip)
   if (size < AGENTX_HEADER_SIZE)
     return 0;
 
-  uint parsed_len = 0;
   struct agentx_header *h = (void *) pkt;
 
   //snmp_log("parse_pkt got type %s (%d)", snmp_pkt_type[h->type], h->type);
@@ -447,33 +687,58 @@ parse_pkt(struct snmp_proto *p, byte *pkt, uint size, uint *skip)
   {
     case AGENTX_RESPONSE_PDU:
       //snmp_log("parse_pkt returning parse_response");
-      parsed_len = parse_response(p, pkt, size);
-      break;
+      return parse_response(p, pkt, size);
 
     case AGENTX_GET_PDU:
     case AGENTX_GET_NEXT_PDU:
     case AGENTX_GET_BULK_PDU:
       refresh_ids(p, h);
-      parsed_len = parse_gets2_pdu(p, pkt, size, skip);
-      break;
-
-    /* during testing the connection should stay opened (we die if we screw up
-     * and get CLOSE_PDU in response)
+      return parse_gets2_pdu(p, pkt, size, skip);
 
     case AGENTX_CLOSE_PDU:
       refresh_ids(p, h);
-      parsed_len = parse_close_pdu(p, pkt, size);
-      break;
-    */
+      return parse_close_pdu(p, pkt, size);
+
+    case AGENTX_TEST_SET_PDU:
+      refresh_ids(p, h);
+      return parse_test_set_pdu(p, pkt, size);
+
+    case AGENTX_COMMIT_SET_PDU:
+      refresh_ids(p, h);
+      return parse_commit_set_pdu(p, pkt, size);
+
+    case AGENTX_UNDO_SET_PDU:
+      refresh_ids(p, h);
+      return parse_undo_set_pdu(p, pkt, size);
+
+    case AGENTX_CLEANUP_SET_PDU:
+      refresh_ids(p, h);
+      return parse_cleanup_set_pdu(p, pkt, size);
 
     default:
       /* drop the packet with unknown type silently */
       //snmp_log("unknown packet type %u", h->type);
       return 0;
   }
+}
 
-  //snmp_log("parse_pkt returning parsed length");
-  return parsed_len;
+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
@@ -506,13 +771,58 @@ parse_response(struct snmp_proto *p, byte *res, uint size)
 */
   //snmp_log("  pkt size %u", h->payload);
 
-  //snmp_log("  pkt size %u", h->payload);
+  switch (r->error)
+  {
+    case AGENTX_RES_NO_ERROR:
+      do_response(p, res, size);
+      break;
 
-  if (r->error == AGENTX_RES_NO_ERROR)
-    do_response(p, res, size);
-  else
-    /* erronous packet should be dropped quietly */
-    {}//snmp_log("an error occured '%s'", snmp_errs[get_u16(&r->error) - SNMP_ERR_SHIFT]);
+    case AGENTX_RES_NOT_OPEN:
+      if (p->state == SNMP_LOCKED || p->state == SNMP_INIT)
+       snmp_startup(p);
+      else
+       snmp_connected(p->sock);
+      break;
+
+    case AGENTX_RES_OPEN_FAILED:
+      if (p->state == SNMP_LOCKED || p->state == SNMP_INIT)
+      {
+       ASSUME(p->startup_timer);
+       p->startup_timer->hook = snmp_startup_timeout;
+       // TODO: better timeout
+       tm_set(p->startup_timer, current_time() + p->timeout S);
+      }
+      else
+      {
+       ASSUME(p->startup_timer);
+       p->startup_timer->hook = snmp_reconnect;
+       // TODO: better timeout
+       tm_set(p->startup_timer, current_time() + p->timeout S);
+      }
+      break;
+
+    /* 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);
+      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_PARSE_ERROR:
+    case AGENTX_RES_PROCESSING_ERR:
+    default:
+      /* erronous packet should be dropped quietly */
+      // TODO correct error?
+      snmp_log("recieved response with error '%s'", snmp_errs[get_u16(&r->error) - SNMP_ERR_SHIFT]);
+      break;
+  }
 
   return pkt_size + AGENTX_HEADER_SIZE;
 }
index 937b2de99b2834cab25b3d1d127476c66c9a07e0..c57542b0197aa3c9ac0427f4ae3dea3ea92404ab 100644 (file)
@@ -289,7 +289,9 @@ enum agentx_response_err {
   AGENTX_RES_NO_CREATION           =  11,
   AGENTX_RES_INCONSISTENT_VALUE            =  12,
   AGENTX_RES_RESOURCE_UNAVAILABLE   =  13,
-  AGENTX_RES_NOT_WRITEABLE         =  17,
+  AGENTX_RES_COMMIT_FAILED         =  14,
+  AGENTX_RES_UNDO_FAILED           =  15,
+  AGENTX_RES_NOT_WRITABLE          =  17,
   AGENTX_RES_INCONSISTENT_NAME     =  18,
   /* end of TEST_SET_PDU resonse errs (master -> subagent) */
   AGENTX_RES_OPEN_FAILED           = 256,