In my quest to move away from the old code of mps.c and mib.c, here's the next step: Introduce a new application_internal.c. This is basically a very simplified version of the libagentx interface. We register a region at a certain point with application.c, and use an internal database of objects that can be walked like leaves that application.c doesn't know about.
Apart from the code being a little easier to read than mps.c (imho), this new interface has a few advantages over application_legacy.c: - no objects are registered inside application.c as instance. Since application.c needs to account for overlapping regions it's quite a bit slower in finding the correct regions. appl_internal just deals with objects, which means we can just walk the leaves. This also makes the startup logs a bit quieter. - Since we can now register an entire region we can block reserved regions from being claimed by other backends. - We can now better distinguish between noSuchObject and noSuchInstance for GetRequests. Since our only internal table atm is sysORTable, which only contains invalid values I left out struct appl_internal_object's getnext() call, but it should be "easy" enough to add later (I have some code here that needs a bit of an extra shine before sending out). getnext() is going to be called for non-scalars, where struct appl_internal_object can't know the index. This diff just adds the plumbing. Migrating the remaining mib.c code will be done in 3 followup diffs, after which mib.c can be removed. mps.c does need to stick around for a little longer, because of the oid keyword in snmpd.conf. OK? martijn@ diff --git a/Makefile b/Makefile index 261e89b..3522a38 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ PROG= snmpd MAN= snmpd.8 snmpd.conf.5 SRCS= parse.y log.c snmpe.c application.c application_legacy.c \ - application_blocklist.c \ + application_blocklist.c application_internal.c \ application_agentx.c ax.c \ mps.c trap.c mib.c smi.c snmpd.c \ proc.c usm.c traphandler.c util.c diff --git a/application.c b/application.c index c36f059..2359943 100644 --- a/application.c +++ b/application.c @@ -159,6 +159,7 @@ void appl_init(void) { appl_blocklist_init(); + appl_internal_init(); appl_legacy_init(); appl_agentx_init(); } @@ -169,6 +170,7 @@ appl_shutdown(void) struct appl_context *ctx, *tctx; appl_blocklist_shutdown(); + appl_internal_shutdown(); appl_legacy_shutdown(); appl_agentx_shutdown(); diff --git a/application.h b/application.h index 39e7b5f..b9c95c0 100644 --- a/application.h +++ b/application.h @@ -147,3 +147,7 @@ void appl_agentx_backend(int); /* application_blocklist.c */ void appl_blocklist_init(void); void appl_blocklist_shutdown(void); + +/* application_internal.c */ +void appl_internal_init(void); +void appl_internal_shutdown(void); diff --git a/application_internal.c b/application_internal.c new file mode 100644 index 0000000..ff9611e --- /dev/null +++ b/application_internal.c @@ -0,0 +1,271 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2023 Martijn van Duren <mart...@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/tree.h> + +#include <event.h> +#include <stdlib.h> + +#include "application.h" +#include "log.h" +#include "mib.h" +#include "smi.h" +#include "snmpd.h" + +struct appl_internal_object { + struct ber_oid oid; + struct ber_element * (*get)(struct ber_oid *); + /* No getnext means the object is scalar */ + struct ber_element * (*getnext)(int8_t, struct ber_oid *); + + RB_ENTRY(appl_internal_object) entry; +}; + +void appl_internal_region(struct ber_oid *); +void appl_internal_object(struct ber_oid *, + struct ber_element *(*)(struct ber_oid *), + struct ber_element *(*)(int8_t, struct ber_oid *)); +void appl_internal_get(struct appl_backend *, int32_t, int32_t, const char *, + struct appl_varbind *); +void appl_internal_getnext(struct appl_backend *, int32_t, int32_t, + const char *, struct appl_varbind *); +struct appl_internal_object *appl_internal_object_parent(struct ber_oid *); +int appl_internal_object_cmp(struct appl_internal_object *, + struct appl_internal_object *); + +struct appl_backend_functions appl_internal_functions = { + .ab_get = appl_internal_get, + .ab_getnext = appl_internal_getnext, + .ab_getbulk = NULL, /* getbulk is too complex */ +}; + +struct appl_backend appl_internal = { + .ab_name = "internal", + .ab_cookie = NULL, + .ab_retries = 0, + .ab_range = 1, + .ab_fn = &appl_internal_functions +}; + +static RB_HEAD(appl_internal_objects, appl_internal_object) + appl_internal_objects = RB_INITIALIZER(&appl_internal_objects); +RB_PROTOTYPE_STATIC(appl_internal_objects, appl_internal_object, entry, + appl_internal_object_cmp); + +void +appl_internal_init(void) +{ +} + +void +appl_internal_shutdown(void) +{ + struct appl_internal_object *object; + + while ((object = RB_ROOT(&appl_internal_objects)) != NULL) { + RB_REMOVE(appl_internal_objects, &appl_internal_objects, + object); + free(object); + } + + appl_close(&appl_internal); +} + +void +appl_internal_region(struct ber_oid *oid) +{ + enum appl_error error; + char oidbuf[1024]; + + error = appl_register(NULL, 150, 1, oid, 0, 1, 0, 0, &appl_internal); + /* + * Ignore requestDenied, duplicateRegistration, and unsupportedContext + */ + if (error == APPL_ERROR_PROCESSINGERROR || + error == APPL_ERROR_PARSEERROR) { + smi_oid2string(oid, oidbuf, sizeof(oidbuf), 0); + fatalx("internal: Failed to register %s", oidbuf); + } +} + +void +appl_internal_object(struct ber_oid *oid, + struct ber_element *(*get)(struct ber_oid *), + struct ber_element *(*getnext)(int8_t, struct ber_oid *)) +{ + struct appl_internal_object *obj; + + if ((obj = calloc(1, sizeof(*obj))) == NULL) + fatal(NULL); + obj->oid = *oid; + obj->get = get; + obj->getnext = getnext; + + RB_INSERT(appl_internal_objects, &appl_internal_objects, obj); +} + +void +appl_internal_get(struct appl_backend *backend, __unused int32_t transactionid, + int32_t requestid, __unused const char *ctx, struct appl_varbind *vblist) +{ + struct ber_oid oid; + struct appl_internal_object *object; + struct appl_varbind *vb, *resp; + size_t i; + int r; + + for (i = 0, vb = vblist; vb != NULL; vb = vb->av_next, i++) + continue; + + if ((resp = calloc(i, sizeof(*resp))) == NULL) { + log_warn("%s", backend->ab_name); + appl_response(backend, requestid, APPL_ERROR_GENERR, 1, vblist); + return; + } + + for (i = 0, vb = vblist; vb != NULL; vb = vb->av_next, i++) { + resp[i].av_oid = vb->av_oid; + if ((object = appl_internal_object_parent(&vb->av_oid)) == NULL) + resp[i].av_value = + appl_exception(APPL_EXC_NOSUCHOBJECT); + else { + oid = object->oid; + /* Add 0 element for scalar */ + if (object->getnext == NULL) + oid.bo_id[oid.bo_n++] = 0; + r = ober_oid_cmp(&vb->av_oid, &oid); + if ((r == 0 && object->getnext == NULL) || + (r == 2 && object->getnext != NULL)) + resp[i].av_value = object->get(&resp[i].av_oid); + else + resp[i].av_value = + appl_exception(APPL_EXC_NOSUCHINSTANCE); + } + if (resp[i].av_value == NULL) { + log_warnx("%s: Failed to get value", backend->ab_name); + goto fail; + } + resp[i].av_next = &resp[i + 1]; + } + resp[i - 1].av_next = NULL; + + appl_response(backend, requestid, APPL_ERROR_NOERROR, 0, resp); + return; + + fail: + for (vb = resp; vb != NULL; vb = vb->av_next) + ober_free_elements(vb->av_value); + free(resp); + appl_response(backend, requestid, APPL_ERROR_GENERR, i + 1, vblist); +} + +void +appl_internal_getnext(struct appl_backend *backend, + __unused int32_t transactionid, int32_t requestid, __unused const char *ctx, + struct appl_varbind *vblist) +{ + struct ber_oid oid; + struct appl_internal_object *object, search; + struct appl_varbind *vb, *resp; + size_t i; + int r; + int8_t include; + + for (i = 0, vb = vblist; vb != NULL; vb = vb->av_next, i++) + continue; + + if ((resp = calloc(i, sizeof(*resp))) == NULL) { + log_warn("%s", backend->ab_name); + appl_response(backend, requestid, APPL_ERROR_GENERR, 1, vblist); + return; + } + + for (i = 0, vb = vblist; vb != NULL; vb = vb->av_next, i++) { + resp[i].av_oid = vb->av_oid; + object = appl_internal_object_parent(&vb->av_oid); + if (object == NULL) { + search.oid = vb->av_oid; + object = RB_NFIND(appl_internal_objects, + &appl_internal_objects, &search); + } + + include = vb->av_include; + for (; object != NULL; object = RB_NEXT(appl_internal_objects, + &appl_internal_objects, object), include = 1) { + if (object->getnext == NULL) { + oid = object->oid; + oid.bo_id[oid.bo_n++] = 0; + r = ober_oid_cmp(&resp[i].av_oid, &oid); + if (r > 0 || (r == 0 && !include)) + continue; + resp[i].av_oid = oid; + resp[i].av_value = object->get(&oid); + break; + } + /* non-scalar */ + fatalx("%s: not implemented", backend->ab_name); + } + if (ober_oid_cmp(&resp[i].av_oid, &vb->av_oid_end) >= 0 || + object == NULL) { + resp[i].av_oid = vb->av_oid; + ober_free_elements(resp[i].av_value); + resp[i].av_value = + appl_exception(APPL_EXC_ENDOFMIBVIEW); + } + if (resp[i].av_value == NULL) { + log_warnx("%s: Failed to get value", backend->ab_name); + goto fail; + } + resp[i].av_next = &resp[i + 1]; + } + resp[i - 1].av_next = NULL; + + appl_response(backend, requestid, APPL_ERROR_NOERROR, 0, resp); + return; + + fail: + for (vb = resp; vb != NULL; vb = vb->av_next) + ober_free_elements(vb->av_value); + free(resp); + appl_response(backend, requestid, APPL_ERROR_GENERR, i + 1, vblist); +} + +struct appl_internal_object * +appl_internal_object_parent(struct ber_oid *oid) +{ + struct appl_internal_object *object, search; + + search.oid = *oid; + do { + if ((object = RB_FIND(appl_internal_objects, + &appl_internal_objects, &search)) != NULL) + return object; + } while (--search.oid.bo_n > 0); + + return NULL; +} + +int +appl_internal_object_cmp(struct appl_internal_object *o1, + struct appl_internal_object *o2) +{ + return ober_oid_cmp(&o1->oid, &o2->oid); +} + +RB_GENERATE_STATIC(appl_internal_objects, appl_internal_object, entry, + appl_internal_object_cmp); diff --git a/snmpd.h b/snmpd.h index 7abeeea..8313a79 100644 --- a/snmpd.h +++ b/snmpd.h @@ -215,7 +215,8 @@ struct oid { (((_oid)->o_flags & OID_IFSET) && \ ((_oid)->o_data == NULL) && ((_oid)->o_val == 0)) -#define OID(...) { { __VA_ARGS__ } } +#define OID(...) (struct ber_oid){ { __VA_ARGS__ }, \ + (sizeof((uint32_t []) { __VA_ARGS__ }) / sizeof(uint32_t)) } #define MIBDECL(...) { { MIB_##__VA_ARGS__ } }, #__VA_ARGS__ #define MIB(...) { { MIB_##__VA_ARGS__ } }, NULL #define MIBEND { { 0 } }, NULL