Hello,

I have just patched my snmpd from -current ; everything else is 7.0-stable. I'm not sure what happens but I use the same snmpd.conf and connects to snmpd from another machine using

# snmpwalk -v 3 -a SHA -A "changeme" -l authPriv -u telegraf \
-x AES -X "changeme" server

But using the patched snmpd, I get the following error:
mib_2 = No Such Object available on this agent at this OID. Using the 7.0 version, it works perfectly.

I can send full snmpd logs if you think it's usefull.

Regards,
Joel C.

On 1/3/22 13:57, Martijn van Duren wrote:
On Sun, 2021-11-21 at 14:58 +0100, Martijn van Duren wrote:
On Sun, 2021-11-14 at 14:35 +0000, Stuart Henderson wrote:
On 2021/11/14 11:49, Martijn van Duren wrote:
sthen@ found an issue when using this diff with netsnmp tools.

The problem was that I put the requestID in the msgID, resulting
in a mismatch upon receiving the reply. The reason that snmp(1)
works is because msgID and requestID are the same.
Diff below fixes things.

This version works for me, and the runtime increase with librenms
fetches and polls (which use a mixture of get/bulkwalk) is acceptable
(10% or so).

Anyone else put this through a test? I want to move forward with this.

martijn@

2 month ping.
So far I only have gotten test results from sthen@.
Should I just put this in or is someone planning to actually look into
the code?

martijn@

Index: Makefile
===================================================================
RCS file: /cvs/src/usr.sbin/snmpd/Makefile,v
retrieving revision 1.17
diff -u -p -r1.17 Makefile
--- Makefile    30 Jun 2020 17:11:49 -0000      1.17
+++ Makefile    1 Dec 2021 06:45:57 -0000
@@ -2,7 +2,7 @@
PROG= snmpd
  MAN=          snmpd.8 snmpd.conf.5
-SRCS=          parse.y log.c snmpe.c \
+SRCS=          parse.y log.c snmpe.c application.c application_legacy.c \
                    mps.c trap.c mib.c smi.c kroute.c snmpd.c timer.c \
                    pf.c proc.c usm.c traphandler.c util.c
Index: application.c
===================================================================
RCS file: application.c
diff -N application.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ application.c       1 Dec 2021 06:45:57 -0000
@@ -0,0 +1,1451 @@
+/*     $OpenBSD$       */
+
+/*
+ * Copyright (c) 2021 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/queue.h>
+#include <sys/tree.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <event.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "application.h"
+#include "log.h"
+#include "smi.h"
+#include "snmp.h"
+#include "snmpe.h"
+#include "mib.h"
+
+TAILQ_HEAD(, appl_context) contexts = TAILQ_HEAD_INITIALIZER(contexts);
+
+struct appl_context {
+       char ac_name[APPL_CONTEXTNAME_MAX + 1];
+
+       RB_HEAD(appl_regions, appl_region) ac_regions;
+
+       TAILQ_ENTRY(appl_context) ac_entries;
+};
+
+struct appl_region {
+       struct ber_oid ar_oid;
+       uint8_t ar_priority;
+       int32_t ar_timeout;
+       int ar_instance;
+       int ar_subtree; /* Claim entire subtree */
+       struct appl_backend *ar_backend;
+       struct appl_region *ar_next; /* Sorted by priority */
+
+       RB_ENTRY(appl_region) ar_entry;
+};
+
+struct appl_request_upstream {
+       struct appl_context *aru_ctx;
+       struct snmp_message *aru_statereference;
+       int32_t aru_requestid; /* upstream requestid */
+       int32_t aru_transactionid; /* RFC 2741 section 6.1 */
+       int16_t aru_nonrepeaters;
+       int16_t aru_maxrepetitions;
+       struct appl_varbind_internal *aru_vblist;
+       size_t aru_varbindlen;
+       enum appl_error aru_error;
+       int16_t aru_index;
+       int aru_locked; /* Prevent recursion through appl_request_send */
+
+       enum snmp_version aru_pduversion;
+       struct ber_element *aru_pdu; /* Original requested pdu */
+};
+
+struct appl_request_downstream {
+       struct appl_request_upstream *ard_request;
+       struct appl_backend *ard_backend;
+       enum snmp_pdutype ard_requesttype;
+       int16_t ard_nonrepeaters;
+       int16_t ard_maxrepetitions;
+       int32_t ard_requestid;
+       uint8_t ard_retries;
+
+       struct appl_varbind_internal *ard_vblist;
+       struct event ard_timer;
+
+       RB_ENTRY(appl_request_downstream) ard_entry;
+};
+
+enum appl_varbind_state {
+       APPL_VBSTATE_MUSTFILL,
+       APPL_VBSTATE_NEW,
+       APPL_VBSTATE_PENDING,
+       APPL_VBSTATE_DONE
+};
+
+struct appl_varbind_internal {
+       enum appl_varbind_state avi_state;
+       struct appl_varbind avi_varbind;
+       struct appl_region *avi_region;
+       int16_t avi_index;
+       struct appl_request_upstream *avi_request_upstream;
+       struct appl_request_downstream *avi_request_downstream;
+       struct appl_varbind_internal *avi_next;
+       struct appl_varbind_internal *avi_sub;
+};
+
+/* SNMP-TARGET-MIB (RFC 3413) */
+struct snmp_target_mib {
+       uint32_t                snmp_unavailablecontexts;
+       uint32_t                snmp_unknowncontexts;
+} snmp_target_mib;
+
+enum appl_error appl_region(struct appl_context *, uint32_t, uint8_t,
+    struct ber_oid *, int, int, struct appl_backend *);
+void appl_region_free(struct appl_context *, struct appl_region *);
+struct appl_region *appl_region_find(struct appl_context *,
+    const struct ber_oid *);
+struct appl_region *appl_region_next(struct appl_context *,
+    struct ber_oid *, struct appl_region *);
+void appl_request_upstream_free(struct appl_request_upstream *);
+void appl_request_downstream_free(struct appl_request_downstream *);
+void appl_request_upstream_resolve(struct appl_request_upstream *);
+void appl_request_downstream_send(struct appl_request_downstream *);
+void appl_request_downstream_timeout(int, short, void *);
+void appl_request_upstream_reply(struct appl_request_upstream *);
+int appl_varbind_valid(struct appl_varbind *, struct appl_varbind *, int, int,
+    const char **);
+int appl_varbind_backend(struct appl_varbind_internal *);
+void appl_varbind_error(struct appl_varbind_internal *, enum appl_error);
+void appl_report(struct snmp_message *, int32_t, struct ber_oid *,
+    struct ber_element *);
+void appl_pdu_log(struct appl_backend *, enum snmp_pdutype, int32_t, uint16_t,
+    uint16_t, struct appl_varbind *);
+void ober_oid_nextsibling(struct ber_oid *);
+
+int appl_region_cmp(struct appl_region *, struct appl_region *);
+int appl_request_cmp(struct appl_request_downstream *,
+    struct appl_request_downstream *);
+
+RB_PROTOTYPE_STATIC(appl_regions, appl_region, ar_entry, appl_region_cmp);
+RB_PROTOTYPE_STATIC(appl_requests, appl_request_downstream, ard_entry,
+    appl_request_cmp);
+
+#define APPL_CONTEXT_NAME(ctx) (ctx->ac_name[0] == '\0' ? NULL : ctx->ac_name)
+
+void
+appl_init(void)
+{
+       appl_legacy_init();
+}
+
+void
+appl_shutdown(void)
+{
+       struct appl_context *ctx, *tctx;
+
+       appl_legacy_shutdown();
+
+       TAILQ_FOREACH_SAFE(ctx, &contexts, ac_entries, tctx) {
+               assert(RB_EMPTY(&(ctx->ac_regions)));
+               TAILQ_REMOVE(&contexts, ctx, ac_entries);
+               free(ctx);
+       }
+}
+
+static struct appl_context *
+appl_context(const char *name, int create)
+{
+       struct appl_context *ctx;
+
+       if (name == NULL)
+               name = "";
+
+       if (strlen(name) > APPL_CONTEXTNAME_MAX) {
+               errno = EINVAL;
+               return NULL;
+       }
+
+       TAILQ_FOREACH(ctx, &contexts, ac_entries) {
+               if (strcmp(name, ctx->ac_name) == 0)
+                       return ctx;
+       }
+
+       /* Always allow the default namespace */
+       if (!create && name[0] != '\0') {
+               errno = ENOENT;
+               return NULL;
+       }
+
+       if ((ctx = malloc(sizeof(*ctx))) == NULL)
+               return NULL;
+
+       strlcpy(ctx->ac_name, name, sizeof(ctx->ac_name));
+       RB_INIT(&(ctx->ac_regions));
+
+       TAILQ_INSERT_TAIL(&contexts, ctx, ac_entries);
+       return ctx;
+}
+
+enum appl_error
+appl_region(struct appl_context *ctx, uint32_t timeout, uint8_t priority,
+    struct ber_oid *oid, int instance, int subtree,
+    struct appl_backend *backend)
+{
+       struct appl_region *region = NULL, *nregion, search;
+       char oidbuf[1024], regionbuf[1024], subidbuf[11];
+       size_t i;
+
+       /*
+        * Don't allow overlap when subtree flag is set.
+        * This allows us to keep control of certain regions like system.
+        */
+       region = appl_region_find(ctx, oid);
+       if (region != NULL && region->ar_subtree)
+               goto overlap;
+
+       search.ar_oid = *oid;
+       region = RB_NFIND(appl_regions, &(ctx->ac_regions), &search);
+       if (region != NULL && region->ar_subtree && (
+           appl_region_cmp(&search, region) == 0 ||
+           appl_region_cmp(&search, region) == -2))
+               goto overlap;
+
+       /* Don't use smi_oid2string, because appl_register can't use it */
+       for (i = 0; i < oid->bo_n; i++) {
+               if (i != 0)
+                       strlcat(oidbuf, ".", sizeof(oidbuf));
+               snprintf(subidbuf, sizeof(subidbuf), "%"PRIu32,
+                   oid->bo_id[i]);
+               strlcat(oidbuf, subidbuf, sizeof(oidbuf));
+       }
+       if ((nregion = malloc(sizeof(*nregion))) == NULL) {
+               log_warn("%s: Can't register %s: Processing error",
+                   backend->ab_name, oidbuf);
+               return APPL_ERROR_PROCESSINGERROR;
+       }
+       nregion->ar_oid = *oid;
+       nregion->ar_priority = priority;
+       nregion->ar_timeout = timeout;
+       nregion->ar_instance = instance;
+       nregion->ar_subtree = subtree;
+       nregion->ar_backend = backend;
+       nregion->ar_next = NULL;
+
+       region = RB_INSERT(appl_regions, &(ctx->ac_regions), nregion);
+       if (region == NULL)
+               return APPL_ERROR_NOERROR;
+
+       if (region->ar_priority == priority)
+               goto duplicate;
+       if (region->ar_priority > priority) {
+               RB_REMOVE(appl_regions, &(ctx->ac_regions), region);
+               RB_INSERT(appl_regions, &(ctx->ac_regions), nregion);
+               nregion->ar_next = region;
+               return APPL_ERROR_NOERROR;
+       }
+
+       while (region->ar_next != NULL &&
+           region->ar_next->ar_priority < priority)
+               region = region->ar_next;
+       if (region->ar_next != NULL && region->ar_next->ar_priority == priority)
+               goto duplicate;
+       nregion->ar_next = region->ar_next;
+       region->ar_next = nregion;
+
+       return APPL_ERROR_NOERROR;
+ duplicate:
+       free(nregion);
+       log_info("%s: %s priority %"PRId8": Duplicate registration",
+           backend->ab_name, oidbuf, priority);
+       return APPL_ERROR_DUPLICATEREGISTRATION;
+ overlap:
+       for (i = 0; i < region->ar_oid.bo_n; i++) {
+               if (i != 0)
+                       strlcat(regionbuf, ".", sizeof(regionbuf));
+               snprintf(subidbuf, sizeof(subidbuf), "%"PRIu32,
+                   region->ar_oid.bo_id[i]);
+               strlcat(regionbuf, subidbuf, sizeof(regionbuf));
+       }
+       log_info("%s: %s overlaps with %s: Request denied",
+           backend->ab_name, oidbuf, regionbuf);
+       return APPL_ERROR_REQUESTDENIED;
+}
+
+/* Name from RFC 2741 section 6.2.3 */
+enum appl_error
+appl_register(const char *ctxname, uint32_t timeout, uint8_t priority,
+    struct ber_oid *oid, int instance, int subtree, uint8_t range_subid,
+    uint32_t upper_bound, struct appl_backend *backend)
+{
+       struct appl_context *ctx;
+       struct appl_region *region, search;
+       char oidbuf[1024], subidbuf[11];
+       enum appl_error error;
+       size_t i;
+       uint32_t lower_bound;
+
+       oidbuf[0] = '\0';
+       /* smi_oid2string can't do ranges */
+       for (i = 0; i < oid->bo_n; i++) {
+               snprintf(subidbuf, sizeof(subidbuf), "%"PRIu32, oid->bo_id[i]);
+               if (i != 0)
+                       strlcat(oidbuf, ".", sizeof(oidbuf));
+               if (range_subid == i + 1) {
+                       strlcat(oidbuf, "[", sizeof(oidbuf));
+                       strlcat(oidbuf, subidbuf, sizeof(oidbuf));
+                       strlcat(oidbuf, "-", sizeof(oidbuf));
+                       snprintf(subidbuf, sizeof(subidbuf), "%"PRIu32,
+                           upper_bound);
+                       strlcat(oidbuf, subidbuf, sizeof(oidbuf));
+                       strlcat(oidbuf, "]", sizeof(oidbuf));
+               } else
+                       strlcat(oidbuf, subidbuf, sizeof(oidbuf));
+       }
+
+       if (ctxname == NULL)
+               ctxname = "";
+       log_info("%s: Registering %s%s context(%s) priority(%"PRIu8") "
+           "timeout(%"PRIu32".%02us)", backend->ab_name, oidbuf,
+            instance ? "(instance)" : "", ctxname, priority,
+            timeout/100, timeout % 100);
+
+       if ((ctx = appl_context(ctxname, 0)) == NULL) {
+               if (errno == ENOMEM) {
+                       log_warn("%s: Can't register %s: Processing error",
+                           backend->ab_name, oidbuf);
+                       return APPL_ERROR_PROCESSINGERROR;
+               }
+               log_info("%s: Can't register %s: Unsupported context \"%s\"",
+                   backend->ab_name, oidbuf, ctxname);
+               return APPL_ERROR_UNSUPPORTEDCONTEXT;
+       }
+       /* Default timeouts should be handled by backend */
+       if (timeout == 0)
+               fatalx("%s: Timeout can't be 0", __func__);
+       if (priority == 0) {
+               log_warnx("%s: Can't register %s: priority can't be 0",
+                   backend->ab_name, oidbuf);
+               return APPL_ERROR_PARSEERROR;
+       }
+       if (range_subid > oid->bo_n) {
+               log_warnx("%s: Can't regiser %s: range_subid too large",
+                   backend->ab_name, oidbuf);
+               return APPL_ERROR_PARSEERROR;
+       }
+       if (range_subid != 0 && oid->bo_id[range_subid] >= upper_bound) {
+               log_warnx("%s: Can't register %s: upper bound smaller or equal "
+                   "to range_subid", backend->ab_name, oidbuf);
+               return APPL_ERROR_PARSEERROR;
+       }
+       if (range_subid != 0)
+               lower_bound = oid->bo_id[range_subid];
+
+       if (range_subid == 0)
+               return appl_region(ctx, timeout, priority, oid, instance,
+                   subtree, backend);
+
+       do {
+               if ((error = appl_region(ctx, timeout, priority, oid, instance,
+                   subtree, backend)) != APPL_ERROR_NOERROR)
+                       goto fail;
+       } while (oid->bo_id[range_subid] != upper_bound);
+       if ((error = appl_region(ctx, timeout, priority, oid, instance, subtree,
+           backend)) != APPL_ERROR_NOERROR)
+               goto fail;
+
+       return APPL_ERROR_NOERROR;
+ fail:
+       search.ar_oid = *oid;
+       if (search.ar_oid.bo_id[range_subid] == lower_bound)
+               return error;
+       
+       for (search.ar_oid.bo_id[range_subid]--;
+           search.ar_oid.bo_id[range_subid] != lower_bound;
+           search.ar_oid.bo_id[range_subid]--) {
+               region = RB_FIND(appl_regions, &(ctx->ac_regions), &search);
+               while (region->ar_priority != priority)
+                       region = region->ar_next;
+               appl_region_free(ctx, region);
+       }
+       region = RB_FIND(appl_regions, &(ctx->ac_regions), &search);
+       while (region->ar_priority != priority)
+               region = region->ar_next;
+       appl_region_free(ctx, region);
+       return error;
+}
+
+/* Name from RFC 2741 section 6.2.4 */
+enum appl_error
+appl_unregister(const char *ctxname, uint8_t priority, struct ber_oid *oid,
+    uint8_t range_subid, uint32_t upper_bound, struct appl_backend *backend)
+{
+       struct appl_region *region, search;
+       struct appl_context *ctx;
+       char oidbuf[1024], subidbuf[11];
+       size_t i;
+
+       oidbuf[0] = '\0';
+       for (i = 0; i < oid->bo_n; i++) {
+               snprintf(subidbuf, sizeof(subidbuf), "%"PRIu32, oid->bo_id[i]);
+               if (i != 0)
+                       strlcat(oidbuf, ".", sizeof(oidbuf));
+               if (range_subid == i + 1) {
+                       strlcat(oidbuf, "[", sizeof(oidbuf));
+                       strlcat(oidbuf, subidbuf, sizeof(oidbuf));
+                       strlcat(oidbuf, "-", sizeof(oidbuf));
+                       snprintf(subidbuf, sizeof(subidbuf), "%"PRIu32,
+                           upper_bound);
+                       strlcat(oidbuf, subidbuf, sizeof(oidbuf));
+                       strlcat(oidbuf, "]", sizeof(oidbuf));
+               } else
+                       strlcat(oidbuf, subidbuf, sizeof(oidbuf));
+       }
+
+       if (ctxname == NULL)
+               ctxname = "";
+       log_info("%s: Unregistering %s context(%s) priority(%"PRIu8")",
+            backend->ab_name, oidbuf,ctxname, priority);
+
+       if ((ctx = appl_context(ctxname, 0)) == NULL) {
+               if (errno == ENOMEM) {
+                       log_warn("%s: Can't unregister %s: Processing error",
+                           backend->ab_name, oidbuf);
+                       return APPL_ERROR_PROCESSINGERROR;
+               }
+               log_info("%s: Can't unregister %s: Unsupported context \"%s\"",
+                   backend->ab_name, oidbuf, ctxname);
+               return APPL_ERROR_UNSUPPORTEDCONTEXT;
+       }
+
+       if (priority == 0) {
+               log_warnx("%s: Can't unregister %s: priority can't be 0",
+                   backend->ab_name, oidbuf);
+               return APPL_ERROR_PARSEERROR;
+       }
+
+       if (range_subid > oid->bo_n) {
+               log_warnx("%s: Can't unregiser %s: range_subid too large",
+                   backend->ab_name, oidbuf);
+               return APPL_ERROR_PARSEERROR;
+       }
+       if (range_subid != 0 && oid->bo_id[range_subid] >= upper_bound) {
+               log_warnx("%s: Can't unregister %s: upper bound smaller or "
+                   "equal to range_subid", backend->ab_name, oidbuf);
+               return APPL_ERROR_PARSEERROR;
+       }
+
+       search.ar_oid = *oid;
+       while (range_subid != 0 &&
+           search.ar_oid.bo_id[range_subid] != upper_bound) {
+               region = RB_FIND(appl_regions, &(ctx->ac_regions), &search);
+               while (region != NULL && region->ar_priority < priority)
+                       region = region->ar_next;
+               if (region == NULL || region->ar_priority != priority) {
+                       log_warnx("%s: Can't unregister %s: region not found",
+                           backend->ab_name, oidbuf);
+                       return APPL_ERROR_UNKNOWNREGISTRATION;
+               }
+               if (region->ar_backend != backend) {
+                       log_warnx("%s: Can't unregister %s: region not owned "
+                           "by backend", backend->ab_name, oidbuf);
+                       return APPL_ERROR_UNKNOWNREGISTRATION;
+               }
+       }
+       region = RB_FIND(appl_regions, &(ctx->ac_regions), &search);
+       while (region != NULL && region->ar_priority < priority)
+               region = region->ar_next;
+       if (region == NULL || region->ar_priority != priority) {
+               log_warnx("%s: Can't unregister %s: region not found",
+                   backend->ab_name, oidbuf);
+               return APPL_ERROR_UNKNOWNREGISTRATION;
+       }
+       if (region->ar_backend != backend) {
+               log_warnx("%s: Can't unregister %s: region not owned "
+                   "by backend", backend->ab_name, oidbuf);
+               return APPL_ERROR_UNKNOWNREGISTRATION;
+       }
+
+       search.ar_oid = *oid;
+       while (range_subid != 0 &&
+           search.ar_oid.bo_id[range_subid] != upper_bound) {
+               region = RB_FIND(appl_regions, &(ctx->ac_regions), &search);
+               while (region != NULL && region->ar_priority != priority)
+                       region = region->ar_next;
+               appl_region_free(ctx, region);
+       }
+       region = RB_FIND(appl_regions, &(ctx->ac_regions), &search);
+       while (region != NULL && region->ar_priority != priority)
+               region = region->ar_next;
+       appl_region_free(ctx, region);
+
+       return APPL_ERROR_NOERROR;
+}
+
+void
+appl_region_free(struct appl_context *ctx, struct appl_region *region)
+{
+       struct appl_region *pregion;
+
+       pregion = RB_FIND(appl_regions, &(ctx->ac_regions), region);
+
+       if (pregion == region) {
+               RB_REMOVE(appl_regions, &(ctx->ac_regions), region);
+               if (region->ar_next != NULL)
+                       RB_INSERT(appl_regions, &(ctx->ac_regions),
+                           region->ar_next);
+       } else {
+               while (pregion->ar_next != region)
+                       pregion = pregion->ar_next;
+               pregion->ar_next = region->ar_next;
+       }
+
+       free(region);
+}
+
+/* backend is owned by the sub-application, just release application.c stuff */
+void
+appl_close(struct appl_backend *backend)
+{
+       struct appl_context *ctx;
+       struct appl_region *region, *tregion, *nregion;
+       struct appl_request_downstream *request, *trequest;
+
+       TAILQ_FOREACH(ctx, &contexts, ac_entries) {
+               RB_FOREACH_SAFE(region, appl_regions,
+                   &(ctx->ac_regions), tregion) {
+                       while (region != NULL) {
+                               nregion = region->ar_next;
+                               if (region->ar_backend == backend)
+                                       appl_region_free(ctx, region);
+                               region = nregion;
+                       }
+               }
+       }
+
+       RB_FOREACH_SAFE(request, appl_requests,
+           &(backend->ab_requests), trequest)
+       appl_request_downstream_free(request);
+}
+
+struct appl_region *
+appl_region_find(struct appl_context *ctx,
+    const struct ber_oid *oid)
+{
+       struct appl_region *region, search;
+
+       search.ar_oid = *oid;
+       while (search.ar_oid.bo_n > 0) {
+               region = RB_FIND(appl_regions, &(ctx->ac_regions), &search);
+               if (region != NULL)
+                       return region;
+               search.ar_oid.bo_n--;
+       }
+       return NULL;
+}
+
+struct appl_region *
+appl_region_next(struct appl_context *ctx, struct ber_oid *oid,
+    struct appl_region *cregion)
+{
+       struct appl_region search, *nregion, *pregion;
+       int cmp;
+
+       search.ar_oid = *oid;
+       nregion = RB_NFIND(appl_regions, &(ctx->ac_regions), &search);
+
+       if (cregion == nregion)
+               nregion = RB_NEXT(appl_regions, &(ctx->ac_regions), nregion);
+       /* Past last element in tree, we might still have a parent */
+       if (nregion == NULL) {
+               search.ar_oid = cregion->ar_oid;
+               search.ar_oid.bo_n--;
+               return appl_region_find(ctx, &(search.ar_oid));
+       }
+       cmp = appl_region_cmp(cregion, nregion);
+       if (cmp >= 0)
+               fatalx("%s: wrong OID order", __func__);
+       /* Direct descendant */
+       if (cmp == -2)
+               return nregion;
+
+       /* cmp == -1 */
+       search.ar_oid = cregion->ar_oid;
+       /* Find direct next sibling */
+       ober_oid_nextsibling(&(search.ar_oid));
+       if (ober_oid_cmp(&(nregion->ar_oid), &(search.ar_oid)) == 0)
+               return nregion;
+       /* Sibling gaps go to parent, or end end at border */
+       search.ar_oid = cregion->ar_oid;
+       search.ar_oid.bo_n--;
+       pregion = appl_region_find(ctx, &(search.ar_oid));
+
+       return pregion != NULL ? pregion : nregion;
+}
+
+/* Name from RFC 3413 section 3.2 */
+void
+appl_processpdu(struct snmp_message *statereference, const char *ctxname,
+    enum snmp_version pduversion, struct ber_element *pdu)
+{
+       struct appl_context *ctx;
+       struct appl_request_upstream *ureq;
+       struct ber_oid oid;
+       struct ber_element *value, *varbind, *varbindlist;
+       long long nonrepeaters, maxrepetitions;
+       static uint32_t transactionid;
+       int32_t requestid;
+       size_t i, varbindlen = 0, repeaterlen;
+
+       /* pdu must be ASN.1 validated in snmpe.c */
+       (void) ober_scanf_elements(pdu, "{iiie", &requestid, &nonrepeaters,
+           &maxrepetitions, &varbindlist);
+
+       /* RFC 3413, section 3.2, processPDU, item 5, final bullet */
+       if ((ctx = appl_context(ctxname, 0)) == NULL) {
+               oid = BER_OID(MIB_snmpUnknownContexts, 0);
+               snmp_target_mib.snmp_unknowncontexts++;
+               if ((value = ober_add_integer(NULL,
+                   snmp_target_mib.snmp_unknowncontexts)) == NULL)
+                       fatal("ober_add_integer");
+               appl_report(statereference, requestid, &oid, value);
+               return;
+       }
+
+       if ((ureq = malloc(sizeof(*ureq))) == NULL)
+               fatal("malloc");
+
+       ureq->aru_ctx = ctx;
+       ureq->aru_statereference = statereference;
+       ureq->aru_transactionid = transactionid++;
+       ureq->aru_requestid = requestid;
+       ureq->aru_error = APPL_ERROR_NOERROR;
+       ureq->aru_index = 0;
+       ureq->aru_nonrepeaters = nonrepeaters;
+       ureq->aru_maxrepetitions = maxrepetitions;
+       ureq->aru_varbindlen = 0;
+       ureq->aru_locked = 0;
+       ureq->aru_pduversion = pduversion;
+       ureq->aru_pdu = pdu;
+
+       varbind = varbindlist->be_sub;
+       for (; varbind != NULL; varbind = varbind->be_next)
+               varbindlen++;
+
+       repeaterlen = varbindlen - nonrepeaters;
+       if (pdu->be_type == SNMP_C_GETBULKREQ)
+               ureq->aru_varbindlen = nonrepeaters +
+                   (repeaterlen * maxrepetitions);
+       else
+               ureq->aru_varbindlen = varbindlen;
+       if ((ureq->aru_vblist = calloc(ureq->aru_varbindlen,
+           sizeof(*ureq->aru_vblist))) == NULL)
+               fatal("malloc");
+
+       varbind = varbindlist->be_sub;
+       /* Use aru_varbindlen in case maxrepetitions == 0 */
+       for (i = 0; i < ureq->aru_varbindlen; i++) {
+               ureq->aru_vblist[i].avi_request_upstream = ureq;
+               ureq->aru_vblist[i].avi_index = i + 1;
+               ureq->aru_vblist[i].avi_state = APPL_VBSTATE_NEW;
+               /* This can only happen with bulkreq */
+               if (varbind == NULL) {
+                       ureq->aru_vblist[i - repeaterlen].avi_sub =
+                           &(ureq->aru_vblist[i]);
+                       ureq->aru_vblist[i].avi_state = APPL_VBSTATE_MUSTFILL;
+                       continue;
+               }
+               ober_get_oid(varbind->be_sub,
+                   &(ureq->aru_vblist[i].avi_varbind.av_oid));
+               if (i + 1 < ureq->aru_varbindlen) {
+                       ureq->aru_vblist[i].avi_next =
+                           &(ureq->aru_vblist[i + 1]);
+                       ureq->aru_vblist[i].avi_varbind.av_next =
+                           &(ureq->aru_vblist[i + 1].avi_varbind);
+               } else {
+                       ureq->aru_vblist[i].avi_next = NULL;
+                       ureq->aru_vblist[i].avi_varbind.av_next = NULL;
+               }
+               varbind = varbind->be_next;
+       }
+
+       appl_pdu_log(NULL, pdu->be_type, requestid, nonrepeaters,
+           maxrepetitions, &(ureq->aru_vblist[0].avi_varbind));
+
+       appl_request_upstream_resolve(ureq);
+}
+
+void
+appl_request_upstream_free(struct appl_request_upstream *ureq)
+{
+       size_t i;
+       struct appl_varbind_internal *vb;
+
+       if (ureq == NULL)
+               return;
+
+       for (i = 0; i < ureq->aru_varbindlen && ureq->aru_vblist != NULL; i++) {
+               vb = &(ureq->aru_vblist[i]);
+               ober_free_elements(vb->avi_varbind.av_value);
+               appl_request_downstream_free(vb->avi_request_downstream);
+       }
+       free(ureq->aru_vblist);
+
+       assert(ureq->aru_statereference == NULL);
+
+       free(ureq);
+}
+
+void
+appl_request_downstream_free(struct appl_request_downstream *dreq)
+{
+       struct appl_varbind_internal *vb;
+
+       if (dreq == NULL)
+               return;
+
+       RB_REMOVE(appl_requests, &(dreq->ard_backend->ab_requests), dreq);
+       evtimer_del(&(dreq->ard_timer));
+
+       for (vb = dreq->ard_vblist; vb != NULL; vb = vb->avi_next)
+               vb->avi_request_downstream = NULL;
+
+       free(dreq);
+}
+
+void
+appl_request_upstream_resolve(struct appl_request_upstream *ureq)
+{
+       struct appl_varbind_internal *vb, *lvb, *tvb;
+       struct appl_request_downstream *dreq;
+       struct appl_region *region, *lregion;
+       struct timeval tv;
+       int done;
+       size_t i;
+       int32_t maxrepetitions;
+       int32_t timeout;
+
+       if (ureq->aru_locked)
+               return;
+       ureq->aru_locked = 1;
+
+       if (ureq->aru_pdu->be_type == SNMP_C_SETREQ) {
+               ureq->aru_error = APPL_ERROR_NOTWRITABLE;
+               ureq->aru_index = 1;
+               appl_request_upstream_reply(ureq);
+               return;
+       }
+
+ next:
+       dreq = NULL;
+       lvb = NULL;
+       done = 1;
+       timeout = 0;
+
+       if (ureq->aru_error != APPL_ERROR_NOERROR) {
+               appl_request_upstream_reply(ureq);
+               return;
+       }
+       for (i = 0; i < ureq->aru_varbindlen; i++) {
+               vb = &(ureq->aru_vblist[i]);
+
+               switch (vb->avi_state) {
+               case APPL_VBSTATE_MUSTFILL:
+               case APPL_VBSTATE_PENDING:
+                       done = 0;
+                       continue;
+               case APPL_VBSTATE_DONE:
+                       continue;
+               case APPL_VBSTATE_NEW:
+                       break;
+               }
+               if (appl_varbind_backend(vb) == -1)
+                       fatal("appl_varbind_backend");
+               if (vb->avi_state != APPL_VBSTATE_DONE)
+                       done = 0;
+       }
+
+       for (i = 0; i < ureq->aru_varbindlen; i++) {
+               vb = &(ureq->aru_vblist[i]);
+
+               if (vb->avi_state != APPL_VBSTATE_NEW)
+                       continue;
+
+               vb = &(ureq->aru_vblist[i]);
+               region = vb->avi_region;
+               lregion = lvb != NULL ? lvb->avi_region : NULL;
+               if (lvb != NULL && region->ar_backend != lregion->ar_backend)
+                       continue;
+
+               vb->avi_varbind.av_next = NULL;
+               vb->avi_next = NULL;
+               tvb = vb;
+               for (maxrepetitions = 0; tvb != NULL; tvb = tvb->avi_sub)
+                       maxrepetitions++;
+               if (dreq == NULL) {
+                       if ((dreq = malloc(sizeof(*dreq))) == NULL)
+                               fatal("malloc");
+
+                       dreq->ard_request = ureq;
+                       dreq->ard_vblist = vb;
+                       dreq->ard_backend = vb->avi_region->ar_backend;
+                       dreq->ard_retries = dreq->ard_backend->ab_retries;
+                       dreq->ard_requesttype = ureq->aru_pdu->be_type;
+                       /*
+                        * We don't yet fully handle bulkrequest responses.
+                        * It's completely valid to map onto getrequest.
+                        * maxrepetitions calculated in preparation of support.
+                        */
+                       if (dreq->ard_requesttype == SNMP_C_GETBULKREQ &&
+                           dreq->ard_backend->ab_fn->ab_getbulk == NULL)
+                               dreq->ard_requesttype = SNMP_C_GETNEXTREQ;
+                       /*
+                        * If first varbind is nonrepeater, set maxrepetitions
+                        * to 0, so that the next varbind with
+                        * maxrepetitions > 1 determines length.
+                        */
+                       dreq->ard_maxrepetitions = maxrepetitions == 1 ?
+                           0 : maxrepetitions;
+                       dreq->ard_nonrepeaters = maxrepetitions == 1 ? 1 : 0;
+                       do {
+                               dreq->ard_requestid = arc4random();
+                       } while (RB_INSERT(appl_requests,
+                           &(dreq->ard_backend->ab_requests), dreq) != NULL);
+                       lvb = vb;
+               /* avi_sub isn't set on !bulkrequest, so we always enter here */
+               } else if (maxrepetitions == 1) {
+                       dreq->ard_nonrepeaters++;
+                       vb->avi_varbind.av_next =
+                           &(dreq->ard_vblist->avi_varbind);
+                       vb->avi_next = dreq->ard_vblist;
+                       dreq->ard_vblist = vb;
+               } else {
+                       lvb->avi_varbind.av_next = &(vb->avi_varbind);
+                       lvb->avi_next = vb;
+                       /* RFC 2741 section 7.2.1.3:
+                        * The value of g.max_repetitions in the GetBulk-PDU may
+                        * be less than (but not greater than) the value in the
+                        * original request PDU.
+                        */
+                       if (dreq->ard_maxrepetitions > maxrepetitions ||
+                           dreq->ard_maxrepetitions == 0)
+                               dreq->ard_maxrepetitions = maxrepetitions;
+                       lvb = vb;
+               }
+               vb->avi_request_downstream = dreq;
+               vb->avi_state = APPL_VBSTATE_PENDING;
+               if (region->ar_timeout > timeout)
+                       timeout = region->ar_timeout;
+       }
+
+       if (dreq == NULL) {
+               ureq->aru_locked = 0;
+               if (done)
+                       appl_request_upstream_reply(ureq);
+               return;
+       }
+
+       tv.tv_sec = timeout / 100;
+       tv.tv_usec = (timeout % 100) * 10000;
+       evtimer_set(&(dreq->ard_timer), appl_request_downstream_timeout, dreq);
+       evtimer_add(&(dreq->ard_timer), &tv);
+
+       appl_request_downstream_send(dreq);
+       goto next;
+}
+
+void
+appl_request_downstream_send(struct appl_request_downstream *dreq)
+{
+
+       appl_pdu_log(dreq->ard_backend, dreq->ard_requesttype,
+           dreq->ard_requestid, 0, 0, &(dreq->ard_vblist->avi_varbind));
+
+       if (dreq->ard_requesttype == SNMP_C_GETREQ) {
+               dreq->ard_backend->ab_fn->ab_get(dreq->ard_backend,
+                   dreq->ard_request->aru_transactionid,
+                   dreq->ard_requestid,
+                   APPL_CONTEXT_NAME(dreq->ard_request->aru_ctx),
+                   &(dreq->ard_vblist->avi_varbind));
+       } else if (dreq->ard_requesttype == SNMP_C_GETNEXTREQ) {
+               dreq->ard_backend->ab_fn->ab_getnext(dreq->ard_backend,
+                   dreq->ard_request->aru_transactionid,
+                   dreq->ard_requestid,
+                   APPL_CONTEXT_NAME(dreq->ard_request->aru_ctx),
+                   &(dreq->ard_vblist->avi_varbind));
+       }
+}
+
+void
+appl_request_downstream_timeout(__unused int fd, __unused short event,
+    void *cookie)
+{
+       struct appl_request_downstream *dreq = cookie;
+
+       log_info("%s: %"PRIu32" timed out%s",
+           dreq->ard_backend->ab_name, dreq->ard_requestid,
+           dreq->ard_retries > 0 ? ": retrying" : "");
+       if (dreq->ard_retries > 0) {
+               dreq->ard_retries--;
+               appl_request_downstream_send(dreq);
+       } else
+               appl_response(dreq->ard_backend, dreq->ard_requestid,
+                   APPL_ERROR_GENERR, 1, &(dreq->ard_vblist->avi_varbind));
+}
+
+void
+appl_request_upstream_reply(struct appl_request_upstream *ureq)
+{
+       struct ber_element *varbindlist = NULL, *varbind = NULL, *value;
+       struct appl_varbind_internal *vb;
+       size_t i, repvarbinds, varbindlen;
+       ssize_t match = -1;
+
+       /* RFC 3416 section 4.2.3: Strip excessive EOMV */
+       varbindlen = ureq->aru_varbindlen;
+       if (ureq->aru_pdu->be_type == SNMP_C_GETBULKREQ &&
+           ureq->aru_error == APPL_ERROR_NOERROR) {
+               repvarbinds = (ureq->aru_varbindlen - ureq->aru_nonrepeaters) /
+                   ureq->aru_maxrepetitions;
+               for (i = ureq->aru_nonrepeaters;
+                   i < ureq->aru_varbindlen - repvarbinds; i++) {
+                       value = ureq->aru_vblist[i].avi_varbind.av_value;
+                       if ((i - ureq->aru_nonrepeaters) % repvarbinds == 0 &&
+                           value->be_class == BER_CLASS_CONTEXT &&
+                           value->be_type == APPL_EXC_ENDOFMIBVIEW) {
+                               if (match != -1)
+                                       break;
+                               match = i;
+                       }
+                       if (value->be_class != BER_CLASS_CONTEXT ||
+                           value->be_type != APPL_EXC_ENDOFMIBVIEW)
+                               match = -1;
+               }
+               if (match != -1)
+                       varbindlen = match + repvarbinds;
+       }
+
+       i = 0;
+       for (; i < varbindlen && ureq->aru_error == APPL_ERROR_NOERROR; i++) {
+               vb = &(ureq->aru_vblist[i]);
+               vb->avi_varbind.av_next =
+                   &(ureq->aru_vblist[i + 1].avi_varbind);
+               /* RFC 3584 section 4.2.2.2 */
+               if (ureq->aru_pduversion == SNMP_V1 &&
+                   vb->avi_varbind.av_value->be_class == BER_CLASS_CONTEXT)
+                       appl_varbind_error(vb, APPL_ERROR_NOSUCHNAME);
+       }
+       /* RFC 3584 section 4.4 */
+       if (ureq->aru_pduversion == SNMP_V1) {
+               switch (ureq->aru_error) {
+               case APPL_ERROR_WRONGVALUE:
+               case APPL_ERROR_WRONGENCODING:
+               case APPL_ERROR_WRONGTYPE:
+               case APPL_ERROR_WRONGLENGTH:
+               case APPL_ERROR_INCONSISTENTVALUE:
+                       ureq->aru_error = APPL_ERROR_BADVALUE;
+                       break;
+               case APPL_ERROR_NOACCESS:
+               case APPL_ERROR_NOTWRITABLE:
+               case APPL_ERROR_NOCREATION:
+               case APPL_ERROR_INCONSISTENTNAME:
+               case APPL_ERROR_AUTHORIZATIONERROR:
+                       ureq->aru_error = APPL_ERROR_NOSUCHNAME;
+                       break;
+               case APPL_ERROR_RESOURCEUNAVAILABLE:
+               case APPL_ERROR_COMMITFAILED:
+               case APPL_ERROR_UNDOFAILED:
+                       ureq->aru_error = APPL_ERROR_GENERR;
+                       break;
+               default:
+                       break;
+               }
+       }
+
+       ureq->aru_vblist[i - 1].avi_varbind.av_next = NULL;
+       /* XXX On error, print the original string, not the half filled goop */
+       appl_pdu_log(NULL, SNMP_C_RESPONSE, ureq->aru_requestid,
+           ureq->aru_error, ureq->aru_index,
+           &(ureq->aru_vblist[0].avi_varbind));
+
+       if (ureq->aru_error != APPL_ERROR_NOERROR) {
+               ober_scanf_elements(ureq->aru_pdu, "{SSSe", &varbindlist);
+               varbindlist = ober_unlink_elements(varbindlist);
+       } else {
+               for (i = 0; i < varbindlen; i++) {
+                       varbind = ober_printf_elements(varbind, "{Oe}",
+                           &(ureq->aru_vblist[i].avi_varbind.av_oid),
+                           ureq->aru_vblist[i].avi_varbind.av_value);
+                       ureq->aru_vblist[i].avi_varbind.av_value = NULL;
+                       if (varbind == NULL)
+                               fatal("ober_printf_elements");
+                       if (varbindlist == NULL)
+                               varbindlist = varbind;
+               }
+       }
+
+       snmpe_send(ureq->aru_statereference, SNMP_C_RESPONSE,
+           ureq->aru_requestid, ureq->aru_error, ureq->aru_index, varbindlist);
+       ureq->aru_statereference = NULL;
+       appl_request_upstream_free(ureq);
+}
+
+/* Name from RFC 2741 section 6.2.16 */
+void
+appl_response(struct appl_backend *backend, int32_t requestid,
+    enum appl_error error, int16_t index, struct appl_varbind *vblist)
+{
+       struct appl_request_downstream *dreq, search;
+       struct appl_request_upstream *ureq = NULL;
+       struct appl_region *nregion;
+       const char *errstr;
+       char oidbuf[1024];
+       enum snmp_pdutype pdutype;
+       struct appl_varbind *vb;
+       struct appl_varbind_internal *origvb = NULL;
+       int invalid = 0;
+       int next = 0;
+       int32_t i;
+
+       appl_pdu_log(backend, SNMP_C_RESPONSE, requestid, error, index, vblist);
+
+       search.ard_requestid = requestid;
+       dreq = RB_FIND(appl_requests, &(backend->ab_requests), &search);
+       if (dreq == NULL) {
+               log_debug("%s: %"PRIu32" not outstanding",
+                   backend->ab_name, requestid);
+               /* Continue to verify validity */
+       } else {
+               ureq = dreq->ard_request;
+               pdutype = ureq->aru_pdu->be_type;
+               next = pdutype == SNMP_C_GETNEXTREQ ||
+                   pdutype == SNMP_C_GETBULKREQ;
+               origvb = dreq->ard_vblist;
+       }
+
+       vb = vblist;
+       for (i = 1; vb != NULL; vb = vb->av_next, i++) {
+               if (!appl_varbind_valid(vb, &(origvb->avi_varbind), next,
+                   error != APPL_ERROR_NOERROR, &errstr)) {
+                       smi_oid2string(&(vb->av_oid), oidbuf,
+                           sizeof(oidbuf), 0);
+                       log_warnx("%s: %"PRIu32" %s: %s",
+                           backend->ab_name, requestid, oidbuf, errstr);
+                       invalid = 1;
+               }
+               /* Tranfer av_value */
+               if (origvb != NULL) {
+                       if (error != APPL_ERROR_NOERROR && i == index)
+                               appl_varbind_error(origvb, error);
+                       origvb->avi_state = APPL_VBSTATE_DONE;
+                       origvb->avi_varbind.av_oid = vb->av_oid;
+                       if (vb->av_value->be_class == BER_CLASS_CONTEXT &&
+                           vb->av_value->be_type == APPL_EXC_ENDOFMIBVIEW) {
+                               nregion = appl_region_next(ureq->aru_ctx,
+                                   &(vb->av_oid), origvb->avi_region);
+                               if (nregion != NULL) {
+                                       ober_free_elements(vb->av_value);
+                                       origvb->avi_varbind.av_oid =
+                                           nregion->ar_oid;
+                                       origvb->avi_varbind.av_include = 1;
+                                       vb->av_value = NULL;
+                                       origvb->avi_state = APPL_VBSTATE_NEW;
+                               }
+                       }
+                       /* RFC 3584 section 4.2.2.1 */
+                       if (ureq->aru_pduversion == SNMP_V1 &&
+                           vb->av_value != NULL &&
+                           vb->av_value->be_class == BER_CLASS_APPLICATION &&
+                           vb->av_value->be_type == SNMP_COUNTER64) {
+                               if (ureq->aru_pdu->be_type == SNMP_C_GETREQ) {
+                                       appl_varbind_error(origvb,
+                                           APPL_ERROR_NOSUCHNAME);
+                               } else {
+                                       ober_free_elements(vb->av_value);
+                                       vb->av_value = NULL;
+                                       origvb->avi_varbind.av_oid = vb->av_oid;
+                                       origvb->avi_varbind.av_include = 0;
+                                       origvb->avi_state = APPL_VBSTATE_NEW;
+                               }
+                       }
+                       origvb->avi_varbind.av_value = vb->av_value;
+                       if (origvb->avi_varbind.av_next == NULL &&
+                           vb->av_next != NULL) {
+                               log_warnx("%s: Request %"PRIu32" returned more "
+                                   "varbinds then requested",
+                                   backend->ab_name, requestid);
+                               invalid = 1;
+                       }
+                       if (origvb->avi_sub != NULL &&
+                           origvb->avi_state == APPL_VBSTATE_DONE) {
+                               origvb->avi_sub->avi_varbind.av_oid =
+                                   origvb->avi_varbind.av_oid;
+                               origvb->avi_sub->avi_state = APPL_VBSTATE_NEW;
+                       }
+                       origvb = origvb->avi_next;
+               } else {
+                       ober_free_elements(vb->av_value);
+                       vb->av_value = NULL;
+               }
+       }
+       if (error != APPL_ERROR_NOERROR && (index <= 0 || index >= i)) {
+               log_warnx("Invalid error index");
+               invalid = 1;
+       }
+       if (error == APPL_ERROR_NOERROR && index != 0) {
+               log_warnx("error index with no error");
+               invalid = 1;
+       }
+       if (vb == NULL && origvb != NULL) {
+               log_warnx("%s: Request %"PRIu32" returned less varbinds then "
+                   "requested", backend->ab_name, requestid);
+               invalid = 1;
+       }
+
+       if (dreq != NULL) {
+               if (invalid)
+                       appl_varbind_error(dreq->ard_vblist, APPL_ERROR_GENERR);
+               appl_request_downstream_free(dreq);
+       }
+
+       if (invalid && backend->ab_fn->ab_close != NULL) {
+               log_warnx("%s: Closing: Too many parse errors",
+                   backend->ab_name);
+               backend->ab_fn->ab_close(backend, APPL_CLOSE_REASONPARSEERROR);
+       }
+
+       if (ureq != NULL)
+               appl_request_upstream_resolve(ureq);
+}
+
+int
+appl_varbind_valid(struct appl_varbind *varbind, struct appl_varbind *request,
+    int next, int null, const char **errstr)
+{
+       int cmp;
+       int eomv = 0;
+
+       if (varbind->av_value == NULL) {
+               *errstr = "missing value";
+               return 0;
+       }
+       if (varbind->av_value->be_class == BER_CLASS_UNIVERSAL) {
+               switch (varbind->av_value->be_type) {
+               case BER_TYPE_NULL:
+                       if (null)
+                               break;
+                       *errstr = "unexpected null value";
+                       return 0;
+               case BER_TYPE_INTEGER:
+               case BER_TYPE_OCTETSTRING:
+               case BER_TYPE_OBJECT:
+                       if (!null)
+                               break;
+                       /* FALLTHROUGH */
+               default:
+                       *errstr = "invalid value";
+                       return 0;
+               }
+       } else if (varbind->av_value->be_class == BER_CLASS_APPLICATION) {
+               switch (varbind->av_value->be_type) {
+               case SNMP_T_IPADDR:
+               case SNMP_T_COUNTER32:
+               case SNMP_T_GAUGE32:
+               case SNMP_T_TIMETICKS:
+               case SNMP_T_OPAQUE:
+               case SNMP_T_COUNTER64:
+                       if (!null)
+                               break;
+                       /* FALLTHROUGH */
+               default:
+                       *errstr = "expecting null value";
+                       return 0;
+               }
+       } else if (varbind->av_value->be_class == BER_CLASS_CONTEXT) {
+               switch (varbind->av_value->be_type) {
+               case APPL_EXC_NOSUCHOBJECT:
+                       if (next && request != NULL) {
+                               *errstr = "Unexpected noSuchObject";
+                               return 0;
+                       }
+                       /* FALLTHROUGH */
+               case APPL_EXC_NOSUCHINSTANCE:
+                       if (null) {
+                               *errstr = "expecting null value";
+                               return 0;
+                       }
+                       if (next && request != NULL) {
+                               *errstr = "Unexpected noSuchInstance";
+                               return 0;
+                       }
+                       break;
+               case APPL_EXC_ENDOFMIBVIEW:
+                       if (null) {
+                               *errstr = "expecting null value";
+                               return 0;
+                       }
+                       if (!next && request != NULL) {
+                               *errstr = "Unexpected endOfMibView";
+                               return 0;
+                       }
+                       eomv = 1;
+                       break;
+               default:
+                       *errstr = "invalid exception";
+                       return 0;
+               }
+       } else {
+               *errstr = "invalid value";
+               return 0;
+       }
+
+       if (request == NULL)
+               return 1;
+
+       cmp = ober_oid_cmp(&(request->av_oid), &(varbind->av_oid));
+       if (next && !eomv) {
+               if (request->av_include) {
+                       if (cmp > 0) {
+                               *errstr = "oid not incrementing";
+                               return 0;
+                       }
+               } else {
+                       if (cmp >= 0) {
+                               *errstr = "oid not incrementing";
+                               return 0;
+                       }
+               }
+       } else {
+               if (cmp != 0) {
+                       *errstr = "oids not equal";
+                       return 0;
+               }
+       }
+       return 1;
+}
+
+int
+appl_varbind_backend(struct appl_varbind_internal *ivb)
+{
+       struct appl_request_upstream *ureq = ivb->avi_request_upstream;
+       struct appl_region search, *region, *pregion;
+       struct appl_varbind *vb = &(ivb->avi_varbind);
+       int next, cmp;
+
+       next = ureq->aru_pdu->be_type == SNMP_C_GETNEXTREQ ||
+           ureq->aru_pdu->be_type == SNMP_C_GETBULKREQ;
+
+       region = appl_region_find(ureq->aru_ctx, &(vb->av_oid));
+       if (region == NULL) {
+               if (!next) {
+                       vb->av_value = appl_exception(APPL_EXC_NOSUCHOBJECT);
+                       ivb->avi_state = APPL_VBSTATE_DONE;
+                       if (vb->av_value == NULL)
+                               return -1;
+                       return 0;
+               }
+               search.ar_oid = vb->av_oid;
+               region = RB_NFIND(appl_regions,
+                   &(ureq->aru_ctx->ac_regions), &search);
+               if (region == NULL)
+                       goto eomv;
+               vb->av_oid = region->ar_oid;
+               vb->av_include = 1;
+       }
+       cmp = ober_oid_cmp(&(region->ar_oid), &(vb->av_oid));
+       if (cmp == -2) {
+               if (region->ar_instance) {
+                       if (!next) {
+                               vb->av_value =
+                                   appl_exception(APPL_EXC_NOSUCHINSTANCE);
+                               ivb->avi_state = APPL_VBSTATE_DONE;
+                               if (vb->av_value == NULL)
+                                       return -1;
+                               return 0;
+                       }
+                       if ((region = appl_region_next(ureq->aru_ctx,
+                           &(vb->av_oid), region)) == NULL)
+                               goto eomv;
+                       vb->av_oid = region->ar_oid;
+                       vb->av_include = 1;
+               }
+       } else if (cmp == 0) {
+               if (region->ar_instance && next && !vb->av_include) {
+                       if ((region = appl_region_next(ureq->aru_ctx,
+                           &(vb->av_oid), region)) == NULL)
+                               goto eomv;
+                       vb->av_oid = region->ar_oid;
+                       vb->av_include = 1;
+               }
+       }
+       ivb->avi_region = region;
+       if (next) {
+               do {
+                       pregion = region;
+                       region = appl_region_next(ureq->aru_ctx,
+                           &(region->ar_oid), pregion);
+               } while (region != NULL &&
+                   region->ar_backend == pregion->ar_backend);
+               if (region == NULL) {
+                       vb->av_oid_end = pregion->ar_oid;
+                       if (pregion->ar_instance &&
+                           vb->av_oid_end.bo_n < BER_MAX_OID_LEN)
+                               vb->av_oid_end.bo_id[vb->av_oid_end.bo_n++] = 0;
+                       else
+                               ober_oid_nextsibling(&(vb->av_oid_end));
+               } else {
+                       if (ober_oid_cmp(&(region->ar_oid),
+                           &(ivb->avi_region->ar_oid)) == 2)
+                               vb->av_oid_end = region->ar_oid;
+                       else {
+                               vb->av_oid_end = ivb->avi_region->ar_oid;
+                               ober_oid_nextsibling(&(vb->av_oid_end));
+                       }
+               }
+       }
+       return 0;
+
+ eomv:
+       do {
+               ivb->avi_varbind.av_value =
+                   appl_exception(APPL_EXC_ENDOFMIBVIEW);
+               ivb->avi_state = APPL_VBSTATE_DONE;
+               if (ivb->avi_varbind.av_value == NULL)
+                       return -1;
+               if (ivb->avi_sub != NULL)
+                       ivb->avi_sub->avi_varbind.av_oid =
+                           ivb->avi_varbind.av_oid;
+               ivb = ivb->avi_sub;
+       } while (ivb != NULL);
+
+       return 0;
+}
+
+void
+appl_varbind_error(struct appl_varbind_internal *avi, enum appl_error error)
+{
+       struct appl_request_upstream *ureq = avi->avi_request_upstream;
+
+       if (ureq->aru_error == APPL_ERROR_GENERR)
+               return;
+       if (ureq->aru_error != APPL_ERROR_NOERROR && error != APPL_ERROR_GENERR)
+               return;
+       ureq->aru_error = error;
+       ureq->aru_index = avi->avi_index;
+}
+
+void
+appl_report(struct snmp_message *msg, int32_t requestid, struct ber_oid *oid,
+    struct ber_element *value)
+{
+       struct ber_element *varbind;
+
+       varbind = ober_printf_elements(NULL, "{Oe}", oid, value);
+       if (varbind == NULL) {
+               log_warn("%"PRId32": ober_printf_elements", requestid);
+               ober_free_elements(value);
+               snmp_msgfree(msg);
+               return;
+       }
+
+       snmpe_send(msg, SNMP_C_REPORT, requestid, 0, 0, varbind);
+}
+
+struct ber_element *
+appl_exception(enum appl_exception type)
+{
+       struct ber_element *value;
+
+       if ((value = ober_add_null(NULL)) == NULL) {
+               log_warn("malloc");
+               return NULL;
+       }
+       ober_set_header(value, BER_CLASS_CONTEXT, type);
+
+       return value;
+}
+
+void
+appl_pdu_log(struct appl_backend *backend, enum snmp_pdutype pdutype,
+    int32_t requestid, uint16_t error, uint16_t index,
+    struct appl_varbind *vblist)
+{
+       struct appl_varbind *vb;
+       char buf[1024], oidbuf[1024], *str;
+       int next;
+
+       if (log_getverbose() < 2)
+               return;
+
+       next = (pdutype == SNMP_C_GETNEXTREQ || pdutype == SNMP_C_GETBULKREQ);
+
+       buf[0] = '\0';
+       for (vb = vblist; vb != NULL; vb = vb->av_next) {
+               strlcat(buf, "{", sizeof(buf));
+               strlcat(buf, smi_oid2string(&(vb->av_oid), oidbuf,
+                   sizeof(oidbuf), 0), sizeof(buf));
+               if (next) {
+                       if (vb->av_include)
+                               strlcat(buf, "(incl)", sizeof(buf));
+                       if (vb->av_oid_end.bo_n > 0) {
+                               strlcat(buf, "-", sizeof(buf));
+                               strlcat(buf, smi_oid2string(&(vb->av_oid_end),
+                                   oidbuf, sizeof(oidbuf), 0), sizeof(buf));
+                       }
+               }
+               strlcat(buf, ":", sizeof(buf));
+               if (vb->av_value != NULL) {
+                       str = smi_print_element(vb->av_value);
+                       strlcat(buf, str == NULL ? "???" : str, sizeof(buf));
+                       free(str);
+               } else
+                       strlcat(buf, "null", sizeof(buf));
+               strlcat(buf, "}", sizeof(buf));
+       }
+       log_debug("%s%s%s{%"PRId32", %"PRIu16", %"PRIu16", {%s}}",
+           backend != NULL ? backend->ab_name : "",
+           backend != NULL ? ": " : "",
+           snmpe_pdutype2string(pdutype), requestid, error, index, buf);
+}
+
+void
+ober_oid_nextsibling(struct ber_oid *oid)
+{
+       while (oid->bo_n > 0) {
+               oid->bo_id[oid->bo_n - 1]++;
+               /* Overflow check */
+               if (oid->bo_id[oid->bo_n - 1] != 0)
+                       return;
+               oid->bo_n--;
+       }
+}
+
+int
+appl_region_cmp(struct appl_region *r1, struct appl_region *r2)
+{
+       return ober_oid_cmp(&(r1->ar_oid), &(r2->ar_oid));
+}
+
+int
+appl_request_cmp(struct appl_request_downstream *r1,
+    struct appl_request_downstream *r2)
+{
+       return r1->ard_requestid < r2->ard_requestid ? -1 :
+           r1->ard_requestid > r2->ard_requestid;
+}
+
+RB_GENERATE_STATIC(appl_regions, appl_region, ar_entry, appl_region_cmp);
+RB_GENERATE_STATIC(appl_requests, appl_request_downstream, ard_entry,
+    appl_request_cmp);
Index: application.h
===================================================================
RCS file: application.h
diff -N application.h
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ application.h       1 Dec 2021 06:45:57 -0000
@@ -0,0 +1,138 @@
+/*     $OpenBSD$       */
+
+/*
+ * Copyright (c) 2021 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 <ber.h>
+
+#include <stdint.h>
+
+#define APPL_OIDMAX 128 /* RFC 2578 Section 3.5 */
+#define APPL_CONTEXTNAME_MAX 32 /* RFC 3415 vacmContextName */
+
+/* Combination of RFC 3416 error-status and RFC 2741 res.error */
+enum appl_error {
+       APPL_ERROR_NOERROR              = 0,
+       APPL_ERROR_TOOBIG               = 1,
+       APPL_ERROR_NOSUCHNAME           = 2,
+       APPL_ERROR_BADVALUE             = 3,
+       APPL_ERROR_READONLY             = 4,
+       APPL_ERROR_GENERR               = 5,
+       APPL_ERROR_NOACCESS             = 6,
+       APPL_ERROR_WRONGTYPE            = 7,
+       APPL_ERROR_WRONGLENGTH          = 8,
+       APPL_ERROR_WRONGENCODING        = 9,
+       APPL_ERROR_WRONGVALUE           = 10,
+       APPL_ERROR_NOCREATION           = 11,
+       APPL_ERROR_INCONSISTENTVALUE    = 12,
+       APPL_ERROR_RESOURCEUNAVAILABLE  = 13,
+       APPL_ERROR_COMMITFAILED         = 14,
+       APPL_ERROR_UNDOFAILED           = 15,
+       APPL_ERROR_AUTHORIZATIONERROR   = 16,
+       APPL_ERROR_NOTWRITABLE          = 17,
+       APPL_ERROR_INCONSISTENTNAME     = 18,
+       APPL_ERROR_OPENfAILED           = 256,
+       APPL_ERROR_NOTOPEN              = 257,
+       APPL_ERROR_INDEXWRONGTYPE       = 258,
+       APPL_ERROR_INDEXALREADYALLOCATED= 259,
+       APPL_ERROR_INDEXNONEAVAILABLE   = 260,
+       APPL_ERROR_INDEXNOTALLOCATED    = 261,
+       APPL_ERROR_UNSUPPORTEDCONTEXT   = 262,
+       APPL_ERROR_DUPLICATEREGISTRATION= 263,
+       APPL_ERROR_UNKNOWNREGISTRATION  = 264,
+       APPL_ERROR_UNKNOWNAGENTCAPS     = 265,
+       APPL_ERROR_PARSEERROR           = 266,
+       APPL_ERROR_REQUESTDENIED        = 267,
+       APPL_ERROR_PROCESSINGERROR      = 268
+};
+
+enum appl_exception {
+       APPL_EXC_NOSUCHOBJECT           = 0,
+       APPL_EXC_NOSUCHINSTANCE         = 1,
+       APPL_EXC_ENDOFMIBVIEW           = 2
+};
+
+enum appl_close_reason {
+       APPL_CLOSE_REASONOTHER          = 1,
+       APPL_CLOSE_REASONPARSEERROR     = 2,
+       APPL_CLOSE_REASONPROTOCOLERROR  = 3,
+       APPL_CLOSE_REASONTIMEOUTS       = 4,
+       APPL_CLOSE_REASONSHUTDOWN       = 5,
+       APPL_CLOSE_REASONBYMANAGER      = 6
+};
+
+struct appl_varbind {
+       int8_t av_include; /* RFC 2741 section 5.1 */
+       struct ber_oid av_oid;
+       struct ber_oid av_oid_end;
+       struct ber_element *av_value;
+
+       struct appl_varbind *av_next;
+};
+
+struct snmp_message;
+enum snmp_version;
+struct appl_backend;
+
+struct appl_backend_functions {
+       void (*ab_close)(struct appl_backend *, enum appl_close_reason);
+       void (*ab_get)(struct appl_backend *, int32_t, int32_t, const char *,
+           struct appl_varbind *);
+       void (*ab_getnext)(struct appl_backend *, int32_t, int32_t, const char 
*,
+           struct appl_varbind *);
+       /*
+        * RFC 3416 section 3: non-repeaters/max-repetitions = 0..max-bindings
+        * max-bindings = (2^31)-1
+        * RFC 2741 section 6.2.7: non-repeaters/max-repetitions = 2 bytes
+        * Go for the lowest common denominator.
+        */
+       void (*ab_getbulk)(struct appl_backend *, int32_t, int32_t, int16_t,
+           int16_t, const char *, struct appl_varbind *);
+};
+
+struct appl_backend {
+       char *ab_name;
+       void *ab_cookie;
+       uint8_t ab_retries;
+       struct appl_backend_functions *ab_fn;
+       /*
+        * Only store downstream requests: they reference upstream and when
+        * downstream requests are done the upstream request is finalized.
+        */
+       RB_HEAD(appl_requests, appl_request_downstream) ab_requests;
+};
+
+void appl_init(void);
+void appl_shutdown(void);
+enum appl_error appl_register(const char *, uint32_t, uint8_t, struct ber_oid 
*,
+    int, int, uint8_t, uint32_t, struct appl_backend *);
+enum appl_error appl_unregister(const char *, uint8_t, struct ber_oid *,
+    uint8_t, uint32_t, struct appl_backend *);
+void appl_close(struct appl_backend *);
+void appl_processpdu(struct snmp_message *, const char *,
+    enum snmp_version , struct ber_element *);
+void appl_response(struct appl_backend *, int32_t, enum appl_error, int16_t,
+    struct appl_varbind *);
+struct ber_element *appl_exception(enum appl_exception);
+
+/* Dispatcher */
+void    snmp_msgfree(struct snmp_message *);
+
+/* application_legacy.c */
+void    appl_legacy_init(void);
+void    appl_legacy_shutdown(void);
Index: application_legacy.c
===================================================================
RCS file: application_legacy.c
diff -N application_legacy.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ application_legacy.c        1 Dec 2021 06:45:57 -0000
@@ -0,0 +1,173 @@
+/*     $OpenBSD$       */
+
+/*
+ * Copyright (c) 2021 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 <event.h>
+#include <stdlib.h>
+
+#include "application.h"
+
+#include "snmpd.h"
+
+struct appl_varbind *appl_legacy_response(size_t);
+void appl_legacy_get(struct appl_backend *, int32_t, int32_t, const char *,
+    struct appl_varbind *);
+void appl_legacy_getnext(struct appl_backend *, int32_t, int32_t, const char *,
+    struct appl_varbind *);
+
+struct appl_backend_functions appl_legacy_functions = {
+       .ab_get = appl_legacy_get,
+       .ab_getnext = appl_legacy_getnext,
+       .ab_getbulk = NULL, /* Legacy getbulk implementation is broken */
+};
+
+struct appl_backend appl_legacy = {
+       .ab_name = "legacy backend",
+       .ab_cookie = NULL,
+       .ab_retries = 0,
+       .ab_fn = &appl_legacy_functions
+};
+
+static struct ber_element *root;
+static struct appl_varbind *response = NULL;
+static size_t responsesz = 0;
+
+void
+appl_legacy_init(void)
+{
+       struct oid *object = NULL;
+       struct ber_oid oid;
+
+       while ((object = smi_foreach(object, OID_RD)) != NULL) {
+               oid = object->o_id;
+               if (!(object->o_flags & OID_TABLE))
+                       oid.bo_id[oid.bo_n++] = 0;
+               appl_register(NULL, 150, 1, &oid,
+                   !(object->o_flags & OID_TABLE), 1, 0, 0, &appl_legacy);
+       }
+
+       if ((root = ober_add_sequence(NULL)) == NULL)
+               fatal("%s: Failed to init root", __func__);
+}
+
+void
+appl_legacy_shutdown(void)
+{
+       appl_close(&appl_legacy);
+
+       ober_free_elements(root);
+       free(response);
+}
+
+struct appl_varbind *
+appl_legacy_response(size_t nvarbind)
+{
+       struct appl_varbind *tmp;
+       size_t i;
+
+       if (responsesz < nvarbind) {
+               if ((tmp = recallocarray(response, responsesz, nvarbind,
+                   sizeof(*response))) == NULL) {
+                       log_warn(NULL);
+                       return NULL;
+               }
+               responsesz = nvarbind;
+               response = tmp;
+       }
+       for (i = 0; i < nvarbind; i++)
+               response[i].av_next = i + 1 == nvarbind ?
+                   NULL : &(response[i + 1]);
+       return response;
+}
+
+void
+appl_legacy_get(struct appl_backend *backend, __unused int32_t transactionid,
+    int32_t requestid, __unused const char *ctx, struct appl_varbind *vblist)
+{
+       size_t i;
+       struct ber_element *elm;
+       struct appl_varbind *vb, *rvb, *rvblist;
+
+       for (i = 0, vb = vblist; vb != NULL; vb = vb->av_next)
+               i++;
+       if ((rvblist = appl_legacy_response(i)) == NULL) {
+               appl_response(backend, requestid, APPL_ERROR_GENERR, 1, vb);
+               return;
+       }
+       rvb = rvblist;
+       for (vb = vblist; vb != NULL; vb = vb->av_next, rvb = rvb->av_next) {
+               (void)mps_getreq(NULL, root, &(vb->av_oid), 1);
+               elm = ober_unlink_elements(root);
+               ober_get_oid(elm, &(rvb->av_oid));
+               rvb->av_value = ober_unlink_elements(elm);
+               ober_free_elements(elm);
+       }
+
+       appl_response(backend, requestid, APPL_ERROR_NOERROR, 0, rvblist);
+}
+
+void
+appl_legacy_getnext(struct appl_backend *backend,
+    __unused int32_t transactionid, int32_t requestid, __unused const char 
*ctx,
+    struct appl_varbind *vblist)
+{
+       size_t i;
+       struct ber_element *elm;
+       struct appl_varbind *vb, *rvb, *rvblist;
+       struct snmp_message msg;
+       int ret;
+
+       for (i = 0, vb = vblist; vb != NULL; vb = vb->av_next)
+               i++;
+       if ((rvblist = appl_legacy_response(i)) == NULL) {
+               appl_response(backend, requestid, APPL_ERROR_GENERR, 1, vb);
+               return;
+       }
+       rvb = rvblist;
+       i = 1;
+       for (vb = vblist; vb != NULL; vb = vb->av_next, rvb = rvb->av_next) {
+               ret = -1;
+               if (vb->av_include) {
+                       ret = mps_getreq(NULL, root, &(vb->av_oid), 0);
+                       if (ret == -1)
+                               ober_free_elements(ober_unlink_elements(root));
+               }
+               rvb->av_oid = vb->av_oid;
+               if (ret == -1) {
+                       msg.sm_version = 1;
+                       (void)mps_getnextreq(&msg, root, &(rvb->av_oid));
+               }
+               elm = ober_unlink_elements(root);
+               ober_get_oid(elm, &(rvb->av_oid));
+               if (ober_oid_cmp(&(rvb->av_oid), &(vb->av_oid_end)) > 0) {
+                       rvb->av_oid = vb->av_oid;
+                       rvb->av_value = appl_exception(APPL_EXC_ENDOFMIBVIEW);
+                       if (rvb->av_value == NULL) {
+                               log_warn("Failed to create endOfMibView");
+                               rvb->av_next = NULL;
+                               appl_response(backend, requestid,
+                                   APPL_ERROR_GENERR, i, rvblist);
+                               return;
+                       }
+               } else
+                       rvb->av_value = ober_unlink_elements(elm);
+               ober_free_elements(elm);
+               i++;
+       }
+
+       appl_response(backend, requestid, APPL_ERROR_NOERROR, 0, rvblist);
+}
Index: log.h
===================================================================
RCS file: log.h
diff -N log.h
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ log.h       1 Dec 2021 06:45:57 -0000
@@ -0,0 +1,40 @@
+/*     $OpenBSD: log.c,v 1.16 2017/03/21 12:06:56 bluhm Exp $  */
+
+/*
+ * Copyright (c) 2003, 2004 Henning Brauer <henn...@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 <stdarg.h>
+
+void   log_init(int, int);
+void   log_procinit(const char *);
+void   log_setverbose(int);
+int    log_getverbose(void);
+void   log_warn(const char *, ...)
+           __attribute__((__format__ (printf, 1, 2)));
+void   log_warnx(const char *, ...)
+           __attribute__((__format__ (printf, 1, 2)));
+void   log_info(const char *, ...)
+           __attribute__((__format__ (printf, 1, 2)));
+void   log_debug(const char *, ...)
+           __attribute__((__format__ (printf, 1, 2)));
+void   logit(int, const char *, ...)
+           __attribute__((__format__ (printf, 2, 3)));
+void   vlog(int, const char *, va_list)
+           __attribute__((__format__ (printf, 2, 0)));
+__dead void fatal(const char *, ...)
+           __attribute__((__format__ (printf, 1, 2)));
+__dead void fatalx(const char *, ...)
+           __attribute__((__format__ (printf, 1, 2)));
Index: mib.h
===================================================================
RCS file: /cvs/src/usr.sbin/snmpd/mib.h,v
retrieving revision 1.40
diff -u -p -r1.40 mib.h
--- mib.h       20 Jun 2018 09:20:51 -0000      1.40
+++ mib.h       1 Dec 2021 06:45:58 -0000
@@ -132,6 +132,33 @@
  #define MIB_usmStatsWrongDigests      MIB_usmStats, OIDVAL_usmErrDigest
  #define MIB_usmStatsDecryptionErrors  MIB_usmStats, OIDVAL_usmErrDecrypt
+/* SNMP-TARGET-MIB */
+#define MIB_snmpTargetMIB              MIB_snmpModules, 12
+#define MIB_snmpTargetObjects          MIB_snmpTargetMIB, 1
+#define MIB_snmpTargetSpinLock         MIB_snmpTargetObjects, 1
+#define MIB_snmpTargetAddrTable                MIB_snmpTargetObjects, 2
+#define MIB_snmpTargetAddrEntry                MIB_snmpTargetAddrTable, 1
+#define MIB_snmpTargetAddrName         MIB_snmpTargetAddrEntry, 1
+#define MIB_snmpTargetAddrTDomain      MIB_snmpTargetAddrEntry, 2
+#define MIB_snmpTargetAddrTAddress     MIB_snmpTargetAddrEntry, 3
+#define MIB_snmpTargetAddrTimeout      MIB_snmpTargetAddrEntry, 4
+#define MIB_snmpTargetAddrRetryCount   MIB_snmpTargetAddrEntry, 5
+#define MIB_snmpTargetAddrTagList      MIB_snmpTargetAddrEntry, 6
+#define MIB_snmpTargetAddrParams       MIB_snmpTargetAddrEntry, 7
+#define MIB_snmpTargetAddrStorageType  MIB_snmpTargetAddrEntry, 8
+#define MIB_snmpTargetAddrRowStatus    MIB_snmpTargetAddrEntry, 9
+#define MIB_snmpTargetParamsTable      MIB_snmpTargetObjects, 3
+#define MIB_snmpTargetParamsEntry      MIB_snmpTargetParamsTable, 1
+#define MIB_snmpTargetParamsName       MIB_snmpTargetParamsEntry, 1
+#define MIB_snmpTargetParamsMPModel    MIB_snmpTargetParamsEntry, 2
+#define MIB_snmpTargetParamsSecurityModel      MIB_snmpTargetParamsEntry, 3
+#define MIB_snmpTargetParamsSecurityName       MIB_snmpTargetParamsEntry, 4
+#define MIB_snmpTargetParamsSecurityLevel      MIB_snmpTargetParamsEntry, 5
+#define MIB_snmpTargetParamsStorageType        MIB_snmpTargetParamsEntry, 6
+#define MIB_snmpTargetParamsRowStatus  MIB_snmpTargetParamsEntry, 7
+#define MIB_snmpUnavailableContexts    MIB_snmpTargetObjects, 4
+#define MIB_snmpUnknownContexts                MIB_snmpTargetObjects, 5
+
  /* HOST-RESOURCES-MIB */
  #define MIB_host                      MIB_mib_2, 25
  #define MIB_hrSystem                  MIB_host, 1
Index: smi.h
===================================================================
RCS file: smi.h
diff -N smi.h
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ smi.h       1 Dec 2021 06:45:58 -0000
@@ -0,0 +1,27 @@
+/*     $OpenBSD: smi.c,v 1.28 2021/01/04 07:59:54 martijn Exp $        */
+
+/*
+ * Copyright (c) 2021 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 <ber.h>
+#include <stdint.h>
+
+#define BER_OID(...) (struct ber_oid) { { __VA_ARGS__ }, \
+    (sizeof((u_int32_t []) { __VA_ARGS__ }) / sizeof(u_int32_t)) }
+
+char *smi_oid2string(struct ber_oid *, char *, size_t, size_t);
+u_long smi_getticks(void);
+char *smi_print_element(struct ber_element *);
Index: snmpd.h
===================================================================
RCS file: /cvs/src/usr.sbin/snmpd/snmpd.h,v
retrieving revision 1.100
diff -u -p -r1.100 snmpd.h
--- snmpd.h     2 Sep 2021 05:41:02 -0000       1.100
+++ snmpd.h     1 Dec 2021 06:45:58 -0000
@@ -36,6 +36,8 @@
  #include <imsg.h>
#include "snmp.h"
+#include "smi.h"
+#include "log.h"
#ifndef nitems
  #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
@@ -617,28 +619,6 @@ extern struct snmpd *snmpd_env;
  struct snmpd  *parse_config(const char *, u_int);
  int            cmdline_symset(char *);
-/* log.c */
-void   log_init(int, int);
-void   log_procinit(const char *);
-void   log_setverbose(int);
-int    log_getverbose(void);
-void   log_warn(const char *, ...)
-           __attribute__((__format__ (printf, 1, 2)));
-void   log_warnx(const char *, ...)
-           __attribute__((__format__ (printf, 1, 2)));
-void   log_info(const char *, ...)
-           __attribute__((__format__ (printf, 1, 2)));
-void   log_debug(const char *, ...)
-           __attribute__((__format__ (printf, 1, 2)));
-void   logit(int, const char *, ...)
-           __attribute__((__format__ (printf, 2, 3)));
-void   vlog(int, const char *, va_list)
-           __attribute__((__format__ (printf, 2, 0)));
-__dead void fatal(const char *, ...)
-           __attribute__((__format__ (printf, 1, 2)));
-__dead void fatalx(const char *, ...)
-           __attribute__((__format__ (printf, 1, 2)));
-
  /* kroute.c */
  void           kr_init(void);
  void           kr_shutdown(void);
@@ -719,7 +699,6 @@ int                  pfta_get_first(struct pfr_astats
/* smi.c */
  int            smi_init(void);
-u_long          smi_getticks(void);
  void           smi_mibtree(struct oid *);
  struct oid    *smi_find(struct oid *);
  struct oid    *smi_nfind(struct oid *);
@@ -728,7 +707,6 @@ struct oid  *smi_next(struct oid *);
  struct oid    *smi_foreach(struct oid *, u_int);
  void           smi_oidlen(struct ber_oid *);
  void           smi_scalar_oidlen(struct ber_oid *);
-char           *smi_oid2string(struct ber_oid *, char *, size_t, size_t);
  int            smi_string2oid(const char *, struct ber_oid *);
  void           smi_delete(struct oid *);
  int            smi_insert(struct oid *);
@@ -736,7 +714,6 @@ int          smi_oid_cmp(struct oid *, struct o
  int            smi_key_cmp(struct oid *, struct oid *);
  unsigned int   smi_application(struct ber_element *);
  void           smi_debug_elements(struct ber_element *);
-char           *smi_print_element(struct ber_element *);
/* timer.c */
  void           timer_init(void);
Index: snmpe.c
===================================================================
RCS file: /cvs/src/usr.sbin/snmpd/snmpe.c,v
retrieving revision 1.78
diff -u -p -r1.78 snmpe.c
--- snmpe.c     21 Oct 2021 14:33:13 -0000      1.78
+++ snmpe.c     1 Dec 2021 06:45:58 -0000
@@ -37,11 +37,12 @@
  #include <unistd.h>
  #include <pwd.h>
+#include "application.h"
  #include "snmpd.h"
+#include "snmpe.h"
  #include "mib.h"
void snmpe_init(struct privsep *, struct privsep_proc *, void *);
-const char *snmpe_pdutype2string(enum snmp_pdutype);
  int    snmpe_parse(struct snmp_message *);
  void   snmpe_tryparse(int, struct snmp_message *);
  int    snmpe_parsevarbinds(struct snmp_message *);
@@ -53,7 +54,6 @@ void   snmpe_writecb(int fd, short, void
  void   snmpe_acceptcb(int fd, short, void *);
  void   snmpe_prepare_read(struct snmp_message *, int);
  int    snmpe_encode(struct snmp_message *);
-void    snmp_msgfree(struct snmp_message *);
struct imsgev *iev_parent;
  static const struct timeval   snmpe_tcp_timeout = { 10, 0 }; /* 10s */
@@ -99,6 +99,7 @@ snmpe_init(struct privsep *ps, struct pr
        kr_init();
        timer_init();
        usm_generate_keys();
+       appl_init();
/* listen for incoming SNMP UDP/TCP messages */
        TAILQ_FOREACH(h, &env->sc_addresses, entry) {
@@ -137,6 +138,7 @@ snmpe_shutdown(void)
                close(h->fd);
        }
        kr_shutdown();
+       appl_shutdown();
  }
int
@@ -427,6 +429,11 @@ badversion:
                goto fail;
        }
+ for (a = msg->sm_varbind; a != NULL; a = a->be_next) {
+               if (ober_scanf_elements(a, "{oS$}", NULL) == -1)
+                       goto parsefail;
+       }
+
        msg->sm_request = req;
        msg->sm_error = errval;
        msg->sm_errorindex = erridx;
@@ -467,6 +474,13 @@ snmpe_parsevarbinds(struct snmp_message
        struct ber_oid           o;
        int                      i;
+ appl_processpdu(msg, msg->sm_ctxname, msg->sm_version, msg->sm_pdu);
+       return 0;
+       /*
+        * Leave code here for now so it's easier to switch back in case of
+        * issues.
+        */
+
        msg->sm_errstr = "invalid varbind element";
varbind = msg->sm_varbind;
@@ -811,7 +825,26 @@ snmpe_dispatchmsg(struct snmp_message *m
        /* XXX Do proper error handling */
        (void) snmpe_parsevarbinds(msg);
+ return;
+       /*
+        * Leave code here for now so it's easier to switch back in case of
+        * issues.
+        */
        /* respond directly */
+       msg->sm_pdutype = SNMP_C_RESPONSE;
+       snmpe_response(msg);
+}
+
+void
+snmpe_send(struct snmp_message *msg, enum snmp_pdutype type, int32_t requestid,
+    int32_t error, uint32_t index, struct ber_element *varbindlist)
+{
+       msg->sm_request = requestid;
+       msg->sm_pdutype = type;
+       msg->sm_error = error;
+       msg->sm_errorindex = index;
+       msg->sm_varbindresp = varbindlist;
+
        msg->sm_pdutype = SNMP_C_RESPONSE;
        snmpe_response(msg);
  }
Index: snmpe.h
===================================================================
RCS file: snmpe.h
diff -N snmpe.h
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ snmpe.h     1 Dec 2021 06:45:58 -0000
@@ -0,0 +1,25 @@
+/*     $OpenBSD$       */
+
+/*
+ * Copyright (c) 2021 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 "snmp.h"
+
+struct snmp_message;
+
+void snmpe_send(struct snmp_message *, enum snmp_pdutype, int32_t, int32_t,
+    uint32_t, struct ber_element *);
+const char *snmpe_pdutype2string(enum snmp_pdutype);



Reply via email to