0
0
mirror of https://gitlab.nic.cz/labs/bird.git synced 2024-12-22 09:41:54 +00:00

SNMP: Better handling of errors

This commit is contained in:
Vojtech Vilimek 2023-10-25 12:56:23 +02:00
parent 151fa4b6b2
commit aef20fe1a7
3 changed files with 79 additions and 22 deletions

View File

@ -54,6 +54,15 @@
* +-----------------+ * +-----------------+
* *
* *
* +-----------------+
* | SNMP_RESET | waiting to transmit response to malformed packet
* +-----------------+
* |
* | response was send, reseting the session (with socket)
* |
* \--> SNMP_LOCKED
*
*
* Erroneous transitions: * Erroneous transitions:
* SNMP is UP in states SNMP_CONN and also in SNMP_REGISTER because the * SNMP is UP in states SNMP_CONN and also in SNMP_REGISTER because the
* session is establised and the GetNext request should be responsed * session is establised and the GetNext request should be responsed
@ -196,15 +205,15 @@ snmp_reconnect(timer *tm)
snmp_connected(p->sock); snmp_connected(p->sock);
} }
static void /* this function is internal and shouldn't be used outside the snmp module */
snmp_sock_err(sock *sk, int UNUSED err) 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); tm_stop(p->ping_timer);
if (!reconnect)
return snmp_down(p);
proto_notify_state(&p->p, PS_START); proto_notify_state(&p->p, PS_START);
rfree(p->sock); rfree(p->sock);
p->sock = NULL; p->sock = NULL;
@ -212,10 +221,20 @@ snmp_sock_err(sock *sk, int UNUSED err)
snmp_log("changing state to LOCKED"); snmp_log("changing state to LOCKED");
p->state = SNMP_LOCKED; p->state = SNMP_LOCKED;
/* We try to reconnect after a short delay */
p->startup_timer->hook = snmp_startup_timeout; 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 static void
snmp_start_locked(struct object_lock *lock) snmp_start_locked(struct object_lock *lock)

View File

@ -37,6 +37,7 @@ enum snmp_proto_state {
SNMP_CONN, SNMP_CONN,
SNMP_STOP, SNMP_STOP,
SNMP_DOWN, SNMP_DOWN,
SNMP_RESET,
}; };
/* hash table macros */ /* hash table macros */
@ -169,6 +170,7 @@ void snmp_startup(struct snmp_proto *p);
void snmp_connected(sock *sk); void snmp_connected(sock *sk);
void snmp_startup_timeout(timer *tm); void snmp_startup_timeout(timer *tm);
void snmp_reconnect(timer *tm); void snmp_reconnect(timer *tm);
void snmp_sock_disconnect(struct snmp_proto *p, int reconnect);
#endif #endif

View 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 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 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 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 }; 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", [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 static void
snmp_simple_response(struct snmp_proto *p, enum agentx_response_err error, u16 index) 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 #undef REASON_SIZE
} }
static uint UNUSED static uint
parse_close_pdu(struct snmp_proto UNUSED *p, byte UNUSED *req, uint UNUSED size) parse_close_pdu(struct snmp_proto *p, byte * const pkt_start, uint size)
{ {
#if 0 byte *pkt = pkt_start;
//byte *pkt = req; struct agentx_header *h = (void *) pkt;
//sock *sk = p->sock; 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; /* The agentx-Close-PDU must not have non-default context */
ADVANCE(req, size, AGENTX_HEADER_SIZE);
p->state = SNMP_ERR; snmp_simple_response(p, AGENTX_RES_NO_ERROR, 0);
snmp_sock_disconnect(p, 1); // TODO: should we try to reconnect (2nd arg) ??
/* or snmp_cleanup(); // ??! */ return AGENTX_HEADER_SIZE;
proto_notify_state(&p->p, PS_DOWN);
#endif
return 0;
} }
/* MUCH better signature would be /* MUCH better signature would be
@ -541,7 +559,10 @@ error:
s = update_packet_size(p, sk->tpos, c.buffer); s = update_packet_size(p, sk->tpos, c.buffer);
if (c.error != AGENTX_RES_NO_ERROR) if (c.error != AGENTX_RES_NO_ERROR)
{
response_err_ind(res, c.error, c.index + 1); response_err_ind(res, c.error, c.index + 1);
snmp_error(p);
}
else if (all_possible) else if (all_possible)
response_err_ind(res, AGENTX_RES_NO_ERROR, 0); response_err_ind(res, AGENTX_RES_NO_ERROR, 0);
else else
@ -604,6 +625,11 @@ parse_set_pdu(struct snmp_proto *p, byte * const pkt_start, uint size, uint err)
error:; error:;
response_err_ind(r, c.error, 0); response_err_ind(r, c.error, 0);
sk_send(p->sock, AGENTX_HEADER_SIZE); 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; return pkt - pkt_start;
} }
@ -1374,6 +1400,16 @@ snmp_rx(sock *sk, uint size)
return 1; 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 */ /* Ping-PDU */
void void
snmp_ping(struct snmp_proto *p) snmp_ping(struct snmp_proto *p)