ping
On Tue, 2021-01-05 at 22:24 +0100, Martijn van Duren wrote:
> 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;
> }
>