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

Reply via email to