From e15f38e082df00ea7fcb7246b712fa0aacbb8481 Mon Sep 17 00:00:00 2001 From: Vojtech Vilimek Date: Thu, 19 Oct 2023 16:27:04 +0200 Subject: [PATCH] SNMP: Bare support for agentx-TestSet-PDU --- proto/snmp/bgp_mib.c | 9 ++ proto/snmp/bgp_mib.h | 1 + proto/snmp/subagent.c | 348 +++++++++++++++++++++++++++++++++++++++--- proto/snmp/subagent.h | 4 +- 4 files changed, 342 insertions(+), 20 deletions(-) diff --git a/proto/snmp/bgp_mib.c b/proto/snmp/bgp_mib.c index 59fb7524d..64782cdc3 100644 --- a/proto/snmp/bgp_mib.c +++ b/proto/snmp/bgp_mib.c @@ -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 + diff --git a/proto/snmp/bgp_mib.h b/proto/snmp/bgp_mib.h index 3d6f07eef..b07d3c92e 100644 --- a/proto/snmp/bgp_mib.h +++ b/proto/snmp/bgp_mib.h @@ -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); diff --git a/proto/snmp/subagent.c b/proto/snmp/subagent.c index 1ace2c439..47232b356 100644 --- a/proto/snmp/subagent.c +++ b/proto/snmp/subagent.c @@ -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; } diff --git a/proto/snmp/subagent.h b/proto/snmp/subagent.h index 937b2de99..c57542b01 100644 --- a/proto/snmp/subagent.h +++ b/proto/snmp/subagent.h @@ -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, -- 2.47.2