With the traphandler code beaten into submission I was able to keep the
traphandler process removal diff more manageable. This diff does a
couple things.
- "listen on" now accepts any combination of read, write and notify.
  This combination removes the traphandler dependency on the generic
  listen on statement, which allows an admin to run snmpd in
  traphandler-only mode. This will break existing traphandler setups,
  but considering the benefit of not having the two functionalities
  mixed is worth it to me and is fully intentional.
  The names are choosen based on viewType names from RFC 3415 (VACM)
- The traphandler process is gone, resulting in a single dispatcher
  responsible for the initial parsing of the packets, which allows for
  better msg checking in the future. The current traphandler process
  does nothing more then what snmpe already does, but a lot crappier:
  the pledge was also too wide.
- We now check incoming trap packets against "trap community". The
  current code does no community verification.
- trap parsing errors are now shown in a similar fashion to the read
  and write requests.
- traphandler now support tcp!!!
- Minus 65 LoC

With this change regress requires the following diff:
Index: snmpd.sh
===================================================================
RCS file: /cvs/src/regress/usr.sbin/snmpd/snmpd.sh,v
retrieving revision 1.12
diff -u -p -r1.12 snmpd.sh
--- snmpd.sh    8 Aug 2020 11:06:40 -0000       1.12
+++ snmpd.sh    5 Jan 2021 20:46:19 -0000
@@ -65,6 +65,7 @@ cat > ${OBJDIR}/snmpd.conf <<EOF
 # This is config template (1) for snmpd regression testing
 # Restrict daemon to listen on localhost only
 listen on 127.0.0.1
+listen on 127.0.0.1 notify
 listen on ::1
 
 # Specify a number of trap receivers

OK?

martijn@

Index: parse.y
===================================================================
RCS file: /cvs/src/usr.sbin/snmpd/parse.y,v
retrieving revision 1.62
diff -u -p -r1.62 parse.y
--- parse.y     30 Oct 2020 07:43:48 -0000      1.62
+++ parse.y     5 Jan 2021 21:22:12 -0000
@@ -94,11 +94,10 @@ char                *symget(const char *);
 struct snmpd                   *conf = NULL;
 static int                      errors = 0;
 static struct usmuser          *user = NULL;
-static char                    *snmpd_port = SNMPD_PORT;
 
 int             host(const char *, const char *, int,
                    struct sockaddr_storage *, int);
-int             listen_add(struct sockaddr_storage *, int);
+int             listen_add(struct sockaddr_storage *, int, int);
 
 typedef struct {
        union {
@@ -121,7 +120,7 @@ typedef struct {
 %}
 
 %token INCLUDE
-%token  LISTEN ON
+%token  LISTEN ON READ WRITE NOTIFY
 %token SYSTEM CONTACT DESCR LOCATION NAME OBJECTID SERVICES RTFILTER
 %token READONLY READWRITE OCTETSTRING INTEGER COMMUNITY TRAP RECEIVER
 %token SECLEVEL NONE AUTH ENC USER AUTHKEY ENCKEY ERROR DISABLED
@@ -130,7 +129,7 @@ typedef struct {
 %token  <v.number>     NUMBER
 %type  <v.string>      hostcmn
 %type  <v.string>      srcaddr port
-%type  <v.number>      optwrite yesno seclevel
+%type  <v.number>      optwrite yesno seclevel listenopt listenopts
 %type  <v.data>        objtype cmd
 %type  <v.oid>         oid hostoid trapoid
 %type  <v.auth>        auth
@@ -281,54 +280,74 @@ listenproto       : UDP listen_udp
                | TCP listen_tcp
                | listen_udp
 
-listen_udp     : STRING port                   {
+listenopts     : /* empty */ { $$ = 0; }
+               | listenopts listenopt { $$ |= $2; }
+               ;
+
+listenopt      : READ { $$ = ADDRESS_FLAG_READ; }
+               | WRITE { $$ = ADDRESS_FLAG_WRITE; }
+               | NOTIFY { $$ = ADDRESS_FLAG_NOTIFY; }
+               ;
+
+listen_udp     : STRING port listenopts        {
                        struct sockaddr_storage ss[16];
                        int nhosts, i;
+                       char *port = $2;
+
+                       if (port == NULL) {
+                               if ($3 == ADDRESS_FLAG_NOTIFY)
+                                       port = SNMPTRAP_PORT;
+                               else
+                                       port = SNMP_PORT;
+                       }
 
-                       nhosts = host($1, $2, SOCK_DGRAM, ss, nitems(ss));
+                       nhosts = host($1, port, SOCK_DGRAM, ss, nitems(ss));
                        if (nhosts < 1) {
                                yyerror("invalid address: %s", $1);
                                free($1);
-                               if ($2 != snmpd_port)
-                                       free($2);
+                               free($2);
                                YYERROR;
                        }
                        if (nhosts > (int)nitems(ss))
                                log_warn("%s:%s resolves to more than %zu 
hosts",
-                                   $1, $2, nitems(ss));
+                                   $1, port, nitems(ss));
 
                        free($1);
-                       if ($2 != snmpd_port)
-                               free($2);
+                       free($2);
                        for (i = 0; i < nhosts; i++) {
-                               if (listen_add(&(ss[i]), SOCK_DGRAM) == -1) {
+                               if (listen_add(&(ss[i]), SOCK_DGRAM, $3) == -1) 
{
                                        yyerror("calloc");
                                        YYERROR;
                                }
                        }
                }
 
-listen_tcp     : STRING port                   {
+listen_tcp     : STRING port listenopts        {
                        struct sockaddr_storage ss[16];
                        int nhosts, i;
+                       char *port = $2;
 
-                       nhosts = host($1, $2, SOCK_STREAM, ss, nitems(ss));
+                       if (port == NULL) {
+                               if ($3 == ADDRESS_FLAG_NOTIFY)
+                                       port = SNMPTRAP_PORT;
+                               else
+                                       port = SNMP_PORT;
+                       }
+                       nhosts = host($1, port, SOCK_STREAM, ss, nitems(ss));
                        if (nhosts < 1) {
                                yyerror("invalid address: %s", $1);
                                free($1);
-                               if ($2 != snmpd_port)
-                                       free($2);
+                               free($2);
                                YYERROR;
                        }
                        if (nhosts > (int)nitems(ss))
                                log_warn("%s:%s resolves to more than %zu 
hosts",
-                                   $1, $2, nitems(ss));
+                                   $1, port, nitems(ss));
 
                        free($1);
-                       if ($2 != snmpd_port)
-                               free($2);
+                       free($2);
                        for (i = 0; i < nhosts; i++) {
-                               if (listen_add(&(ss[i]), SOCK_STREAM) == -1) {
+                               if (listen_add(&(ss[i]), SOCK_STREAM, $3) == 
-1) {
                                        yyerror("calloc");
                                        YYERROR;
                                }
@@ -336,7 +355,7 @@ listen_tcp  : STRING port                   {
                }
 
 port           : /* empty */                   {
-                       $$ = snmpd_port;
+                       $$ = NULL;
                }
                | PORT STRING                   {
                        $$ = $2;
@@ -497,7 +516,7 @@ hostdef             : STRING hostoid hostcmn srcadd
                                YYERROR;
                        }
 
-                       if (host($1, SNMPD_TRAPPORT, SOCK_DGRAM, &ss, 1) <= 0) {
+                       if (host($1, SNMPTRAP_PORT, SOCK_DGRAM, &ss, 1) <= 0) {
                                yyerror("invalid host: %s", $1);
                                free($1);
                                free($2);
@@ -704,9 +723,11 @@ lookup(char *s)
                { "location",                   LOCATION },
                { "name",                       NAME },
                { "none",                       NONE },
+               { "notify",                     NOTIFY },
                { "oid",                        OBJECTID },
                { "on",                         ON },
                { "port",                       PORT },
+               { "read",                       READ },
                { "read-only",                  READONLY },
                { "read-write",                 READWRITE },
                { "receiver",                   RECEIVER },
@@ -718,7 +739,8 @@ lookup(char *s)
                { "tcp",                        TCP },
                { "trap",                       TRAP },
                { "udp",                        UDP },
-               { "user",                       USER }
+               { "user",                       USER },
+               { "write",                      WRITE }
        };
        const struct keywords   *p;
 
@@ -1110,27 +1132,29 @@ parse_config(const char *filename, u_int
 
        /* Setup default listen addresses */
        if (TAILQ_EMPTY(&conf->sc_addresses)) {
-               if (host("0.0.0.0", SNMPD_PORT, SOCK_DGRAM, &ss, 1) != 1)
+               if (host("0.0.0.0", SNMP_PORT, SOCK_DGRAM, &ss, 1) != 1)
                        fatal("Unexpected resolving of 0.0.0.0");
-               if (listen_add(&ss, SOCK_DGRAM) == -1)
+               if (listen_add(&ss, SOCK_DGRAM, 0) == -1)
                        fatal("calloc");
-               if (host("::", SNMPD_PORT, SOCK_DGRAM, &ss, 1) != 1)
+               if (host("::", SNMP_PORT, SOCK_DGRAM, &ss, 1) != 1)
                        fatal("Unexpected resolving of ::");
-               if (listen_add(&ss, SOCK_DGRAM) == -1)
+               if (listen_add(&ss, SOCK_DGRAM, 0) == -1)
                        fatal("calloc");
        }
-       if (conf->sc_traphandler) {
-               found = 0;
-               TAILQ_FOREACH(h, &conf->sc_addresses, entry) {
-                       if (h->type == SOCK_DGRAM)
-                               found = 1;
-               }
-               if (!found) {
-                       log_warnx("trap handler needs at least one "
-                           "udp listen address");
-                       free(conf);
-                       return (NULL);
-               }
+       found = 0;
+       TAILQ_FOREACH(h, &conf->sc_addresses, entry) {
+               if (h->flags & ADDRESS_FLAG_NOTIFY)
+                       found = 1;
+       }
+       if (conf->sc_traphandler && !found) {
+               log_warnx("trap handler needs at least one notify listener");
+               free(conf);
+               return (NULL);
+       }
+       if (!conf->sc_traphandler && found) {
+               log_warnx("notify listener needs at least one trap handler");
+               free(conf);
+               return (NULL);
        }
 
        /* Free macros and check which have not been used. */
@@ -1258,7 +1282,7 @@ host(const char *s, const char *port, in
 }
 
 int
-listen_add(struct sockaddr_storage *ss, int type)
+listen_add(struct sockaddr_storage *ss, int type, int flags)
 {
        struct sockaddr_in *sin;
        struct sockaddr_in6 *sin6;
@@ -1275,6 +1299,12 @@ listen_add(struct sockaddr_storage *ss, 
                h->port = ntohs(sin6->sin6_port);
        }
        h->type = type;
+       if ((h->flags = flags) == 0) {
+               if (h->port == 162)
+                       h->flags = ADDRESS_FLAG_NOTIFY;
+               else
+                       h->flags = ADDRESS_FLAG_READ | ADDRESS_FLAG_WRITE;
+       }
        TAILQ_INSERT_TAIL(&(conf->sc_addresses), h, entry);
 
        return 0;
Index: snmpd.c
===================================================================
RCS file: /cvs/src/usr.sbin/snmpd/snmpd.c,v
retrieving revision 1.42
diff -u -p -r1.42 snmpd.c
--- snmpd.c     6 Sep 2020 15:51:28 -0000       1.42
+++ snmpd.c     5 Jan 2021 21:22:12 -0000
@@ -52,8 +52,6 @@ struct snmpd  *snmpd_env;
 
 static struct privsep_proc procs[] = {
        { "snmpe", PROC_SNMPE, snmpd_dispatch_snmpe, snmpe, snmpe_shutdown },
-       { "traphandler", PROC_TRAP, snmpd_dispatch_traphandler, traphandler,
-           traphandler_shutdown }
 };
 
 void
@@ -300,6 +298,8 @@ int
 snmpd_dispatch_snmpe(int fd, struct privsep_proc *p, struct imsg *imsg)
 {
        switch (imsg->hdr.type) {
+       case IMSG_TRAP_EXEC:
+               return (traphandler_priv_recvmsg(p, imsg));
        case IMSG_CTL_RELOAD:
                /* XXX notyet */
        default:
Index: snmpd.conf.5
===================================================================
RCS file: /cvs/src/usr.sbin/snmpd/snmpd.conf.5,v
retrieving revision 1.45
diff -u -p -r1.45 snmpd.conf.5
--- snmpd.conf.5        24 Oct 2020 14:52:11 -0000      1.45
+++ snmpd.conf.5        5 Jan 2021 21:22:12 -0000
@@ -95,13 +95,37 @@ Routing table information will not be av
 reduced during bulk updates.
 The default is
 .Ic no .
-.It Ic listen on Oo Ic tcp | udp Oc Ar address Op Ic port Ar port
+.It Ic listen on Oo Ic tcp | udp Oc Ar address Oo Ic port Ar port Oc Op Ic 
read | Ic write | Ic notify
 Specify the local address
 .Xr snmpd 8
 should listen on for incoming SNMP messages.
+The
+.Ic read
+flag specifies if the listen statement accepts get, getnext and bulkget
+requests.
+The
+.Ic write
+flag specifies if the listen statement accepts set requests
+and
+.Ic notify
+flags specifes if the listen statements accepts trapv1 and trapv2 requests.
 Multiple
 .Ic listen on
-statements are supported, the default is UDP.
+statements are supported.
+The default protocol is
+.Ic udp.
+The default
+.Ar port
+is 161, unless the only listen flags contains
+.Ic notify
+which sets it to 162.
+If no flags are specified it defaults to
+.Dq Ic read Ic write ,
+or
+.Ic notify
+when
+.Ar port
+is 162.
 .It Ic read-only community Ar string
 Specify the name of the read-only community.
 The default value is
Index: snmpd.h
===================================================================
RCS file: /cvs/src/usr.sbin/snmpd/snmpd.h,v
retrieving revision 1.90
diff -u -p -r1.90 snmpd.h
--- snmpd.h     6 Sep 2020 15:51:28 -0000       1.90
+++ snmpd.h     5 Jan 2021 21:22:12 -0000
@@ -48,8 +48,8 @@
 #define CONF_FILE              "/etc/snmpd.conf"
 #define SNMPD_SOCKET           "/var/run/snmpd.sock"
 #define SNMPD_USER             "_snmpd"
-#define SNMPD_PORT             "161"
-#define SNMPD_TRAPPORT         "162"
+#define SNMP_PORT              "161"
+#define SNMPTRAP_PORT          "162"
 
 #define SNMPD_MAXSTRLEN                484
 #define SNMPD_MAXCOMMUNITYLEN  SNMPD_MAXSTRLEN
@@ -88,7 +88,7 @@ enum imsg_type {
        IMSG_CTL_VERBOSE,
        IMSG_CTL_RELOAD,
        IMSG_CTL_PROCFD,
-       IMSG_ALERT
+       IMSG_TRAP_EXEC
 };
 
 struct imsgev {
@@ -110,7 +110,6 @@ struct imsgev {
 enum privsep_procid {
        PROC_PARENT,    /* Parent process and application interface */
        PROC_SNMPE,     /* SNMP engine */
-       PROC_TRAP,      /* SNMP trap receiver */
        PROC_MAX
 };
 
@@ -384,8 +383,11 @@ struct snmp_message {
        struct sockaddr_storage  sm_ss;
        socklen_t                sm_slen;
        int                      sm_sock_tcp;
+       int                      sm_aflags;
+       int                      sm_type;
        struct event             sm_sockev;
        char                     sm_host[HOST_NAME_MAX+1];
+       in_port_t                sm_port;
 
        struct sockaddr_storage  sm_local_ss;
        socklen_t                sm_local_slen;
@@ -482,6 +484,7 @@ struct address {
        struct sockaddr_storage  ss;
        in_port_t                port;
        int                      type;
+       int                      flags;
        int                      fd;
        struct event             ev;
        struct event             evt;
@@ -490,6 +493,10 @@ struct address {
 };
 TAILQ_HEAD(addresslist, address);
 
+#define ADDRESS_FLAG_READ 0x1
+#define ADDRESS_FLAG_WRITE 0x2
+#define ADDRESS_FLAG_NOTIFY 0x4
+
 struct trap_address {
        struct sockaddr_storage  ss;
        struct sockaddr_storage  ss_local;
@@ -761,9 +768,8 @@ struct imsgev *
 int     proc_flush_imsg(struct privsep *, enum privsep_procid, int);
 
 /* traphandler.c */
-void    traphandler(struct privsep *, struct privsep_proc *);
-void    traphandler_shutdown(void);
-int     snmpd_dispatch_traphandler(int, struct privsep_proc *, struct imsg *);
+int     traphandler_parse(struct snmp_message *);
+int     traphandler_priv_recvmsg(struct privsep_proc *, struct imsg *);
 void    trapcmd_free(struct trapcmd *);
 int     trapcmd_add(struct trapcmd *);
 struct trapcmd *
Index: snmpe.c
===================================================================
RCS file: /cvs/src/usr.sbin/snmpd/snmpe.c,v
retrieving revision 1.67
diff -u -p -r1.67 snmpe.c
--- snmpe.c     6 Sep 2020 17:29:35 -0000       1.67
+++ snmpe.c     5 Jan 2021 21:22:12 -0000
@@ -108,7 +108,7 @@ snmpe_init(struct privsep *ps, struct pr
                        evtimer_set(&h->evt, snmpe_acceptcb, h);
                } else {
                        event_set(&h->ev, h->fd, EV_READ|EV_PERSIST,
-                           snmpe_recvmsg, env);
+                           snmpe_recvmsg, h);
                }
                event_add(&h->ev, NULL);
        }
@@ -271,6 +271,7 @@ snmpe_parse(struct snmp_message *msg)
        if (class != BER_CLASS_CONTEXT)
                goto parsefail;
 
+       msg->sm_type = type;
        switch (type) {
        case SNMP_C_GETBULKREQ:
                if (msg->sm_version == SNMP_V1) {
@@ -288,6 +289,10 @@ snmpe_parse(struct snmp_message *msg)
        case SNMP_C_GETNEXTREQ:
                if (type == SNMP_C_GETNEXTREQ)
                        stats->snmp_ingetnexts++;
+               if (!(msg->sm_aflags & ADDRESS_FLAG_READ)) {
+                       msg->sm_errstr = "read requests disabled";
+                       goto fail;
+               }
                if (msg->sm_version != SNMP_V3 &&
                    strcmp(env->sc_rdcommunity, msg->sm_community) != 0 &&
                    (env->sc_readonly ||
@@ -301,6 +306,10 @@ snmpe_parse(struct snmp_message *msg)
 
        case SNMP_C_SETREQ:
                stats->snmp_insetrequests++;
+               if (!(msg->sm_aflags & ADDRESS_FLAG_WRITE)) {
+                       msg->sm_errstr = "write requests disabled";
+                       goto fail;
+               }
                if (msg->sm_version != SNMP_V3 &&
                    (env->sc_readonly ||
                    strcmp(env->sc_rwcommunity, msg->sm_community) != 0)) {
@@ -320,16 +329,40 @@ snmpe_parse(struct snmp_message *msg)
                goto parsefail;
 
        case SNMP_C_TRAP:
+               if (msg->sm_version != SNMP_V1) {
+                       msg->sm_errstr = "trapv1 request on !SNMPv1 message";
+                       goto parsefail;
+               }
        case SNMP_C_TRAPV2:
-               if (msg->sm_version != SNMP_V3 &&
-                   strcmp(env->sc_trcommunity, msg->sm_community) != 0) {
+               if (type == SNMP_C_TRAPV2 &&
+                   !(msg->sm_version == SNMP_V2 ||
+                   msg->sm_version != SNMP_V3)) {
+                       msg->sm_errstr = "trapv2 request on !SNMPv2C or "
+                           "!SNMPv3 message";
+                       goto parsefail;
+               }
+               if (!(msg->sm_aflags & ADDRESS_FLAG_NOTIFY)) {
+                       msg->sm_errstr = "notify requests disabled";
+                       goto fail;
+               }
+               if (msg->sm_version == SNMP_V3) {
+                       msg->sm_errstr = "SNMPv3 doesn't support traps yet";
+                       goto fail;
+               }
+               if (strcmp(env->sc_trcommunity, msg->sm_community) != 0) {
                        stats->snmp_inbadcommunitynames++;
                        msg->sm_errstr = "wrong trap community";
                        goto fail;
                }
                stats->snmp_intraps++;
-               msg->sm_errstr = "received trap";
-               goto fail;
+               /*
+                * This should probably go into parsevarbinds, but that's for a
+                * next refactor
+                */
+               if (traphandler_parse(msg) == -1)
+                       goto fail;
+               /* Shortcircuit */
+               return 0;
 
        default:
                msg->sm_errstr = "invalid context";
@@ -356,15 +389,15 @@ snmpe_parse(struct snmp_message *msg)
 
        print_host(ss, msg->sm_host, sizeof(msg->sm_host));
        if (msg->sm_version == SNMP_V3)
-               log_debug("%s: %s: SNMPv3 context %d, flags %#x, "
+               log_debug("%s: %s:%hd: SNMPv3 context %d, flags %#x, "
                    "secmodel %lld, user '%s', ctx-engine %s, ctx-name '%s', "
-                   "request %lld", __func__, msg->sm_host, msg->sm_context,
-                   msg->sm_flags, msg->sm_secmodel, msg->sm_username,
-                   tohexstr(msg->sm_ctxengineid, msg->sm_ctxengineid_len),
-                   msg->sm_ctxname, msg->sm_request);
+                   "request %lld", __func__, msg->sm_host, msg->sm_port,
+                   msg->sm_context, msg->sm_flags, msg->sm_secmodel,
+                   msg->sm_username, tohexstr(msg->sm_ctxengineid,
+                   msg->sm_ctxengineid_len), msg->sm_ctxname, msg->sm_request);
        else
-               log_debug("%s: %s: SNMPv%d '%s' context %d request %lld",
-                   __func__, msg->sm_host, msg->sm_version + 1,
+               log_debug("%s: %s:%hd: SNMPv%d '%s' context %d request %lld",
+                   __func__, msg->sm_host, msg->sm_port, msg->sm_version + 1,
                    msg->sm_community, msg->sm_context, msg->sm_request);
 
        return (0);
@@ -373,7 +406,8 @@ snmpe_parse(struct snmp_message *msg)
        stats->snmp_inasnparseerrs++;
  fail:
        print_host(ss, msg->sm_host, sizeof(msg->sm_host));
-       log_debug("%s: %s: %s", __func__, msg->sm_host, msg->sm_errstr);
+       log_debug("%s: %s:%hd: %s", __func__, msg->sm_host, msg->sm_port,
+           msg->sm_errstr);
        return (-1);
 }
 
@@ -403,8 +437,8 @@ snmpe_parsevarbinds(struct snmp_message 
                if (o.bo_n < BER_MIN_OID_LEN || o.bo_n > BER_MAX_OID_LEN)
                        goto varfail;
 
-               log_debug("%s: %s: oid %s", __func__, msg->sm_host,
-                   smi_oid2string(&o, buf, sizeof(buf), 0));
+               log_debug("%s: %s:%hd: oid %s", __func__, msg->sm_host,
+                   msg->sm_port, smi_oid2string(&o, buf, sizeof(buf), 0));
 
                /*
                 * XXX intotalreqvars should only be incremented after all are
@@ -479,8 +513,8 @@ snmpe_parsevarbinds(struct snmp_message 
 
        return 0;
  varfail:
-       log_debug("%s: %s: %s, error index %d", __func__,
-           msg->sm_host, msg->sm_errstr, i);
+       log_debug("%s: %s:%hd: %s, error index %d", __func__,
+           msg->sm_host, msg->sm_port, msg->sm_errstr, i);
        if (msg->sm_error == 0)
                msg->sm_error = SNMP_ERROR_GENERR;
        msg->sm_errorindex = i;
@@ -515,6 +549,10 @@ snmpe_acceptcb(int fd, short type, void 
        if ((msg = calloc(1, sizeof(*msg))) == NULL)
                goto fail;
 
+       memcpy(&(msg->sm_ss), &ss, len);
+       msg->sm_slen = len;
+       msg->sm_aflags = h->flags;
+       msg->sm_port = h->port;
        snmpe_prepare_read(msg, afd);
        return;
 fail:
@@ -635,6 +673,10 @@ snmpe_writecb(int fd, short type, void *
 
        if ((nmsg = calloc(1, sizeof(*nmsg))) == NULL)
                goto fail;
+       memcpy(&(nmsg->sm_ss), &(msg->sm_ss), msg->sm_slen);
+       nmsg->sm_slen = msg->sm_slen;
+       nmsg->sm_aflags = msg->sm_aflags;
+       nmsg->sm_port = msg->sm_port;
 
        /*
         * Reuse the connection.
@@ -662,16 +704,18 @@ snmpe_writecb(int fd, short type, void *
 void
 snmpe_recvmsg(int fd, short sig, void *arg)
 {
-       struct snmpd            *env = arg;
-       struct snmp_stats       *stats = &env->sc_stats;
+       struct address          *h = arg;
+       struct snmp_stats       *stats = &snmpd_env->sc_stats;
        ssize_t                  len;
        struct snmp_message     *msg;
 
        if ((msg = calloc(1, sizeof(*msg))) == NULL)
                return;
 
+       msg->sm_aflags = h->flags;
        msg->sm_sock = fd;
        msg->sm_slen = sizeof(msg->sm_ss);
+       msg->sm_port = h->port;
        if ((len = recvfromto(fd, msg->sm_data, sizeof(msg->sm_data), 0,
            (struct sockaddr *)&msg->sm_ss, &msg->sm_slen,
            (struct sockaddr *)&msg->sm_local_ss, &msg->sm_local_slen)) < 1) {
@@ -715,6 +759,11 @@ snmpe_recvmsg(int fd, short sig, void *a
 void
 snmpe_dispatchmsg(struct snmp_message *msg)
 {
+       if (msg->sm_type == SNMP_C_TRAP ||
+           msg->sm_type == SNMP_C_TRAPV2) {
+               snmp_msgfree(msg);
+               return;
+       }
        /* dispatched to subagent */
        /* XXX Do proper error handling */
        (void) snmpe_parsevarbinds(msg);
Index: traphandler.c
===================================================================
RCS file: /cvs/src/usr.sbin/snmpd/traphandler.c,v
retrieving revision 1.19
diff -u -p -r1.19 traphandler.c
--- traphandler.c       5 Jan 2021 18:12:15 -0000       1.19
+++ traphandler.c       5 Jan 2021 21:22:12 -0000
@@ -43,17 +43,10 @@
 #include "snmpd.h"
 #include "mib.h"
 
-char    trap_path[PATH_MAX];
-
-void    traphandler_init(struct privsep *, struct privsep_proc *, void *arg);
-int     traphandler_dispatch_parent(int, struct privsep_proc *, struct imsg *);
-int     traphandler_bind(struct address *);
-void    traphandler_recvmsg(int, short, void *);
 int     traphandler_priv_recvmsg(struct privsep_proc *, struct imsg *);
 int     traphandler_fork_handler(struct privsep_proc *, struct imsg *);
-int     traphandler_parse(struct ber_element *, char *, struct sockaddr *);
 struct ber_element *
-        traphandler_v1translate(struct ber_element *, char *, int);
+        traphandler_v1translate(struct snmp_message *, int);
 int     trapcmd_cmp(struct trapcmd *, struct trapcmd *);
 void    trapcmd_exec(struct trapcmd *, struct sockaddr *,
            struct ber_element *);
@@ -65,172 +58,14 @@ RB_GENERATE(trapcmd_tree, trapcmd, cmd_e
 
 struct trapcmd_tree trapcmd_tree = RB_INITIALIZER(&trapcmd_tree);
 
-static struct privsep_proc procs[] = {
-       { "parent",     PROC_PARENT,    traphandler_dispatch_parent }
-};
-
-void
-traphandler(struct privsep *ps, struct privsep_proc *p)
-{
-       struct snmpd            *env = ps->ps_env;
-       struct address          *h;
-
-       if (env->sc_traphandler) {
-               TAILQ_FOREACH(h, &env->sc_addresses, entry) {
-                       if (h->type != SOCK_DGRAM)
-                               continue;
-                       if ((h->fd = traphandler_bind(h)) == -1)
-                               fatal("could not create trap listener socket");
-               }
-       }
-
-       proc_run(ps, p, procs, nitems(procs), traphandler_init, NULL);
-}
-
-void
-traphandler_init(struct privsep *ps, struct privsep_proc *p, void *arg)
-{
-       struct snmpd            *env = ps->ps_env;
-       struct address          *h;
-
-       if (pledge("stdio id proc recvfd exec", NULL) == -1)
-               fatal("pledge");
-
-       if (!env->sc_traphandler)
-               return;
-
-       /* listen for SNMP trap messages */
-       TAILQ_FOREACH(h, &env->sc_addresses, entry) {
-               event_set(&h->ev, h->fd, EV_READ|EV_PERSIST,
-                   traphandler_recvmsg, NULL);
-               event_add(&h->ev, NULL);
-       }
-}
-
-int
-traphandler_bind(struct address *addr)
-{
-       int                      s;
-       char                     buf[512];
-       struct sockaddr_in      *sin;
-       struct sockaddr_in6     *sin6;
-
-       if (addr->ss.ss_family == AF_INET) {
-               sin = (struct sockaddr_in *)&(addr->ss);
-               sin->sin_port = htons(162);
-       } else {
-               sin6 = (struct sockaddr_in6 *)&(addr->ss);
-               sin6->sin6_port = htons(162);
-       }
-       if ((s = snmpd_socket_af(&addr->ss, SOCK_DGRAM)) == -1)
-               return (-1);
-
-       if (fcntl(s, F_SETFL, O_NONBLOCK) == -1)
-               goto bad;
-
-       if (bind(s, (struct sockaddr *)&addr->ss, addr->ss.ss_len) == -1)
-               goto bad;
-
-       if (print_host(&addr->ss, buf, sizeof(buf)) == NULL)
-               goto bad;
-
-       log_info("traphandler: listening on %s:%s", buf, SNMPD_TRAPPORT);
-
-       return (s);
- bad:
-       close (s);
-       return (-1);
-}
-
-void
-traphandler_shutdown(void)
-{
-       struct address          *h;
-
-       TAILQ_FOREACH(h, &snmpd_env->sc_addresses, entry) {
-               event_del(&h->ev);
-               close(h->fd);
-       }
-}
-
-int
-traphandler_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg)
-{
-       switch (imsg->hdr.type) {
-       default:
-               break;
-       }
-
-       return (-1);
-}
-
-int
-snmpd_dispatch_traphandler(int fd, struct privsep_proc *p, struct imsg *imsg)
-{
-       switch (imsg->hdr.type) {
-       case IMSG_ALERT:
-               return (traphandler_priv_recvmsg(p, imsg));
-       default:
-               break;
-       }
-
-       return (-1);
-}
-
-void
-traphandler_recvmsg(int fd, short events, void *arg)
-{
-       struct ber               ber = {0};
-       struct ber_element      *msg = NULL, *pdu;
-       char                     buf[8196];
-       struct sockaddr_storage  ss;
-       socklen_t                slen;
-       ssize_t                  n;
-       int                      vers;
-       char                    *community;
-
-       slen = sizeof(ss);
-       if ((n = recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *)&ss,
-           &slen)) == -1)
-               return;
-
-       ober_set_application(&ber, smi_application);
-       ober_set_readbuf(&ber, buf, n);
-
-       if ((msg = ober_read_elements(&ber, NULL)) == NULL)
-               goto parsefail;
-
-       if (ober_scanf_elements(msg, "{dse", &vers, &community, &pdu) == -1)
-               goto parsefail;
-
-       switch (vers) {
-       case SNMP_V1:
-               if (pdu->be_type != SNMP_C_TRAP)
-                       goto parsefail;
-               break;
-       case SNMP_V2:
-               if (pdu->be_type != SNMP_C_TRAPV2)
-                       goto parsefail;
-               break;
-       default:
-               goto parsefail;
-       }
-
-       (void)traphandler_parse(pdu, community, (struct sockaddr *)&ss);
-
-parsefail:
-       ober_free(&ber);
-       if (msg != NULL)
-               ober_free_elements(msg);
-}
-
 /*
  * Validate received message
  */
 int
-traphandler_parse(struct ber_element *pdu, char *community, struct sockaddr 
*sa)
+traphandler_parse(struct snmp_message *msg)
 {
        struct privsep          *ps = &snmpd_env->sc_ps;
+       struct snmp_stats       *stats = &snmpd_env->sc_stats;
        struct ber               ber = {0};
        struct ber_element      *vblist = NULL, *elm, *elm2;
        struct ber_oid           o1, o2, snmpTrapOIDOID;
@@ -241,22 +76,24 @@ traphandler_parse(struct ber_element *pd
        ssize_t                  buflen;
        int                      ret = -1;
 
-       switch (pdu->be_type) {
+       switch (msg->sm_pdu->be_type) {
        case SNMP_C_TRAP:
-               vblist = traphandler_v1translate(pdu, community, 0);
-               if (vblist == NULL)
+               if ((vblist = traphandler_v1translate(msg, 0)) == NULL)
                        goto done;
                break;
        case SNMP_C_TRAPV2:
-               if (ober_scanf_elements(pdu, "{SSe}", &elm) == -1)
+               if (ober_scanf_elements(msg->sm_pdu, "{SSe}", &elm) == -1) {
+                       stats->snmp_inasnparseerrs++;
                        goto done;
-               if (elm->be_type != BER_TYPE_INTEGER)
+               }
+               if (elm->be_type != BER_TYPE_INTEGER) {
+                       stats->snmp_inasnparseerrs++;
                        goto done;
+               }
                vblist = ober_unlink_elements(elm);
                break;
        default:
-               log_warnx("unsupported SNMP trap version '%d'", pdu->be_type);
-               goto done;
+               fatalx("%s called without proper context", __func__);
        }
 
        (void)ober_string2oid("1.3.6.1.2.1.1.3.0", &sysUpTimeOID);
@@ -265,29 +102,36 @@ traphandler_parse(struct ber_element *pd
            &snmpTrapOID) == -1 ||
            ober_oid_cmp(&o1, &sysUpTimeOID) != 0 ||
            ober_oid_cmp(&o2, &snmpTrapOIDOID) != 0) {
+               stats->snmp_inasnparseerrs++;
                goto done;
        }
        (void)ober_scanf_elements(vblist, "{Se", &elm);
        for (elm = elm->be_next; elm != NULL; elm = elm->be_next) {
                if (ober_scanf_elements(elm, "{oe}", &o1, &elm2) == -1 ||
-                   elm2->be_next != NULL)
+                   elm2->be_next != NULL) {
+                       stats->snmp_inasnparseerrs++;
                        goto done;
+               }
        }
 
        ober_set_application(&ber, smi_application);
 
        if ((buflen = ober_write_elements(&ber, vblist)) == -1 ||
-           ober_get_writebuf(&ber, &buf) == -1)
+           ober_get_writebuf(&ber, &buf) == -1) {
+               msg->sm_errstr = "failed to handle trap";
                goto done;
+       }
 
-       iov[0].iov_base = sa;
-       iov[0].iov_len = sa->sa_len;
+       iov[0].iov_base = &(msg->sm_ss);
+       iov[0].iov_len = msg->sm_slen;
        iov[1].iov_base = buf;
        iov[1].iov_len = buflen;
 
        /* Forward it to the parent process */
-       if (proc_composev(ps, PROC_PARENT, IMSG_ALERT, iov, 2) == -1)
+       if (proc_composev(ps, PROC_PARENT, IMSG_TRAP_EXEC, iov, 2) == -1) {
+               msg->sm_errstr = "failed to handle trap";
                goto done;
+       }
 
        ret = 0;
 done:
@@ -298,8 +142,9 @@ done:
 }
 
 struct ber_element *
-traphandler_v1translate(struct ber_element *pdu, char *community, int proxy)
+traphandler_v1translate(struct snmp_message *msg, int proxy)
 {
+       struct snmp_stats       *stats = &snmpd_env->sc_stats;
        struct ber_oid trapoid, enterprise, oid, snmpTrapAddressOid;
        struct ber_oid snmpTrapCommunityOid, snmpTrapEnterpriseOid;
        struct ber_element *elm, *last, *vblist, *vb0 = NULL;
@@ -308,12 +153,12 @@ traphandler_v1translate(struct ber_eleme
        int generic_trap, specific_trap, time_stamp;
        int hasaddress = 0, hascommunity = 0, hasenterprise = 0;
 
-       if (ober_scanf_elements(pdu, "{oxddde", &enterprise, &agent_addr,
-           &agent_addrlen, &generic_trap, &specific_trap, &time_stamp,
-           &vblist) == -1 ||
+       if (ober_scanf_elements(msg->sm_pdu, "{oxddde", &enterprise,
+           &agent_addr, &agent_addrlen, &generic_trap, &specific_trap,
+           &time_stamp, &vblist) == -1 ||
            agent_addrlen != 4 ||
            vblist->be_type != BER_TYPE_SEQUENCE) {
-               errno = EINVAL;
+               stats->snmp_inasnparseerrs++;
                return NULL;
        }
        switch (generic_trap) {
@@ -337,15 +182,16 @@ traphandler_v1translate(struct ber_eleme
                break;
        case 6:
                trapoid = enterprise;
+               /* Officially this should be 128, but BER_MAX_OID_LEN is 64 */
                if (trapoid.bo_n + 2 > BER_MAX_OID_LEN) {
-                       errno = EINVAL;
+                       stats->snmp_inasnparseerrs++;
                        return NULL;
                }
                trapoid.bo_id[trapoid.bo_n++] = 0;
                trapoid.bo_id[trapoid.bo_n++] = specific_trap;
                break;
        default:
-               errno = EINVAL;
+               stats->snmp_inasnparseerrs++;
                return NULL;
        }
 
@@ -354,12 +200,14 @@ traphandler_v1translate(struct ber_eleme
                vb0 = ober_unlink_elements(vblist);
 
        if ((vblist = ober_add_sequence(NULL)) == NULL) {
+               msg->sm_errstr = strerror(errno);
                if (vb0 != NULL)
                        ober_free_elements(vb0);
                return NULL;
        }
        if (ober_printf_elements(vblist, "{od}{oO}e", "1.3.6.1.2.1.1.3.0",
            time_stamp, "1.3.6.1.6.3.1.1.4.1.0", &trapoid, vb0) == NULL) {
+               msg->sm_errstr = strerror(errno);
                if (vb0 != 0)
                        ober_free_elements(vb0);
                ober_free_elements(vblist);
@@ -375,6 +223,7 @@ traphandler_v1translate(struct ber_eleme
                    &snmpTrapEnterpriseOid);
                for (elm = vblist->be_sub; elm != NULL; elm = elm->be_next) {
                        if (ober_get_oid(elm->be_sub, &oid) == -1) {
+                               msg->sm_errstr = "failed to read oid";
                                ober_free_elements(vblist);
                                return NULL;
                        }
@@ -391,8 +240,9 @@ traphandler_v1translate(struct ber_eleme
                        if (ober_printf_elements(last, "{Oxt}{Os}{OO}",
                            &snmpTrapAddressOid, agent_addr, 4,
                            BER_CLASS_APPLICATION, SNMP_T_IPADDR,
-                           &snmpTrapCommunityOid, community,
+                           &snmpTrapCommunityOid, msg->sm_community,
                            &snmpTrapEnterpriseOid, &enterprise) == NULL) {
+                               msg->sm_errstr = strerror(errno);
                                ober_free_elements(vblist);
                                return NULL;
                        }


Reply via email to