]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
SNMP: Better handling of errors
authorVojtech Vilimek <vojtech.vilimek@nic.cz>
Wed, 25 Oct 2023 10:56:23 +0000 (12:56 +0200)
committerVojtech Vilimek <vojtech.vilimek@nic.cz>
Wed, 25 Oct 2023 10:56:23 +0000 (12:56 +0200)
proto/snmp/snmp.c
proto/snmp/snmp.h
proto/snmp/subagent.c

index 0b123f045090fec3d437bc1ae91b6b3edba9e073..ef0412014d6b23fbaceeea82fee40e657671b188 100644 (file)
  *    +-----------------+
  *
  *
+ *    +-----------------+
+ *    | SNMP_RESET      |     waiting to transmit response to malformed packet
+ *    +-----------------+
+ *      |
+ *       |    response was send, reseting the session (with socket)
+ *       |
+ *      \--> SNMP_LOCKED
+ *
+ *
  *  Erroneous transitions:
  *    SNMP is UP in states SNMP_CONN and also in SNMP_REGISTER because the
  *    session is establised and the GetNext request should be responsed
@@ -196,15 +205,15 @@ snmp_reconnect(timer *tm)
   snmp_connected(p->sock);
 }
 
-static void
-snmp_sock_err(sock *sk, int UNUSED err)
+/* this function is internal and shouldn't be used outside the snmp module */
+void
+snmp_sock_disconnect(struct snmp_proto *p, int reconnect)
 {
-  snmp_log("socket error '%s' (errno: %d)", strerror(err), err);
-  struct snmp_proto *p = sk->data;
-  p->errs++;
-
   tm_stop(p->ping_timer);
 
+  if (!reconnect)
+    return snmp_down(p);
+
   proto_notify_state(&p->p, PS_START);
   rfree(p->sock);
   p->sock = NULL;
@@ -212,10 +221,20 @@ snmp_sock_err(sock *sk, int UNUSED err)
   snmp_log("changing state to LOCKED");
   p->state = SNMP_LOCKED;
 
+  /* We try to reconnect after a short delay */
   p->startup_timer->hook = snmp_startup_timeout;
-  tm_start(p->startup_timer,  4 S); // TODO make me configurable
+  tm_start(p->startup_timer, 4 S);  // TODO make me configurable
 }
 
+static void
+snmp_sock_err(sock *sk, int UNUSED err)
+{
+  snmp_log("socket error '%s' (errno: %d)", strerror(err), err);
+  struct snmp_proto *p = sk->data;
+  p->errs++;
+
+  snmp_sock_disconnect(p, 1);
+}
 
 static void
 snmp_start_locked(struct object_lock *lock)
index 180a20309ab2a5bb38ec86a419323a2d27544289..ebdd9b5faac3c8ea6aa94e1592a98019678fa42a 100644 (file)
@@ -37,6 +37,7 @@ enum snmp_proto_state {
   SNMP_CONN,
   SNMP_STOP,
   SNMP_DOWN,
+  SNMP_RESET,
 };
 
 /* hash table macros */
@@ -169,6 +170,7 @@ void snmp_startup(struct snmp_proto *p);
 void snmp_connected(sock *sk);
 void snmp_startup_timeout(timer *tm);
 void snmp_reconnect(timer *tm);
+void snmp_sock_disconnect(struct snmp_proto *p, int reconnect);
 
 
 #endif
index f65b904719d997e35cba2a46090e86aabb69e1f0..3404c568eb078bdf613f0d09af4598f1f346680c 100644 (file)
@@ -35,6 +35,7 @@ static struct agentx_response *prepare_response(struct snmp_proto *p, struct snm
 static void response_err_ind(struct agentx_response *res, uint err, uint ind);
 static uint update_packet_size(struct snmp_proto *p, const byte *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);
+static void snmp_tx(sock *sk);
 
 u32 snmp_internet[] = { SNMP_ISO, SNMP_ORG, SNMP_DOD, SNMP_INTERNET };
 
@@ -76,6 +77,22 @@ static const char * const snmp_pkt_type[] UNUSED = {
   [AGENTX_RESPONSE_PDU]                  =  "Response-PDU",
 };
 
+static int
+snmp_rx_skip(sock UNUSED *sk, uint UNUSED size)
+{
+  return 1;
+}
+
+static inline void
+snmp_error(struct snmp_proto *p)
+{
+  snmp_log("changing state to RESET");
+  p->state = SNMP_RESET;
+
+  p->sock->rx_hook = snmp_rx_skip;
+  p->sock->tx_hook = snmp_tx;
+}
+
 static void
 snmp_simple_response(struct snmp_proto *p, enum agentx_response_err error, u16 index)
 {
@@ -313,27 +330,28 @@ close_pdu(struct snmp_proto *p, u8 reason)
 #undef REASON_SIZE
 }
 
-static uint UNUSED
-parse_close_pdu(struct snmp_proto UNUSED *p, byte UNUSED *req, uint UNUSED size)
+static uint
+parse_close_pdu(struct snmp_proto *p, byte * const pkt_start, uint size)
 {
-#if 0
-  //byte *pkt = req;
-  //sock *sk = p->sock;
+  byte *pkt = pkt_start;
+  struct agentx_header *h = (void *) pkt;
+  ADVANCE(pkt, size, AGENTX_HEADER_SIZE);
+  int byte_ord = h->flags & AGENTX_NETWORK_BYTE_ORDER;
+  uint pkt_size = LOAD_U32(h->payload, byte_ord);
 
-  if (size < sizeof(struct agentx_header))
+  if (pkt_size != 0)
   {
-    return 0;
+    snmp_simple_response(p, AGENTX_RES_GEN_ERROR, 0);
+    // TODO: best solution for possibly malicious pkt_size
+    //return AGENTX_HEADER_SIZE + MIN(size, pkt_size);
+    return AGENTX_HEADER_SIZE;
   }
 
-  //struct agentx_header *h = (void *) req;
-  ADVANCE(req, size, AGENTX_HEADER_SIZE);
-
-  p->state = SNMP_ERR;
+  /* The agentx-Close-PDU must not have non-default context */
 
-  /* or snmp_cleanup(); // ??! */
-  proto_notify_state(&p->p, PS_DOWN);
-#endif
-  return 0;
+  snmp_simple_response(p, AGENTX_RES_NO_ERROR, 0);
+  snmp_sock_disconnect(p, 1); // TODO: should we try to reconnect (2nd arg) ??
+  return AGENTX_HEADER_SIZE;
 }
 
 /* MUCH better signature would be
@@ -541,7 +559,10 @@ error:
   s = update_packet_size(p, sk->tpos, c.buffer);
 
   if (c.error != AGENTX_RES_NO_ERROR)
+  {
     response_err_ind(res, c.error, c.index + 1);
+    snmp_error(p);
+  }
   else if (all_possible)
     response_err_ind(res, AGENTX_RES_NO_ERROR, 0);
   else
@@ -604,6 +625,11 @@ parse_set_pdu(struct snmp_proto *p, byte * const pkt_start, uint size, uint err)
 error:;
   response_err_ind(r, c.error, 0);
   sk_send(p->sock, AGENTX_HEADER_SIZE);
+
+  /* Reset the connection on unrecoverable error */
+  if (c.error != AGENTX_RES_NO_ERROR && c.error != err)
+    snmp_error(p);
+
   return pkt - pkt_start;
 }
 
@@ -1374,6 +1400,16 @@ snmp_rx(sock *sk, uint size)
   return 1;
 }
 
+/* snmp_tx - used to reset the connection when the
+ *   agentx-Response-PDU was sent
+ */
+static void
+snmp_tx(sock *sk)
+{
+  struct snmp_proto *p = sk->data;
+  snmp_sock_disconnect(p, 1);
+}
+
 /* Ping-PDU */
 void
 snmp_ping(struct snmp_proto *p)