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; }