summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorblambert <blambert@openbsd.org>2014-04-25 06:57:11 +0000
committerblambert <blambert@openbsd.org>2014-04-25 06:57:11 +0000
commit70201ef177bc5bc25de6c00ea68edb50056c6db2 (patch)
treef2082adab85b788847a9f38a36eaaccd07345870
parentbe a bit clearer about what this page is; ok millert schwarze (diff)
downloadwireguard-openbsd-70201ef177bc5bc25de6c00ea68edb50056c6db2.tar.xz
wireguard-openbsd-70201ef177bc5bc25de6c00ea68edb50056c6db2.zip
Support running user-defined actions on receipt of snmp traps.
testing sthen@ much man page guidance jmc@ ok reyk@
-rw-r--r--usr.sbin/snmpd/Makefile4
-rw-r--r--usr.sbin/snmpd/ber.c12
-rw-r--r--usr.sbin/snmpd/parse.y82
-rw-r--r--usr.sbin/snmpd/snmpd.c6
-rw-r--r--usr.sbin/snmpd/snmpd.conf.521
-rw-r--r--usr.sbin/snmpd/snmpd.h29
-rw-r--r--usr.sbin/snmpd/traphandler.c470
7 files changed, 609 insertions, 15 deletions
diff --git a/usr.sbin/snmpd/Makefile b/usr.sbin/snmpd/Makefile
index 753ab01ae1e..eec5451f50e 100644
--- a/usr.sbin/snmpd/Makefile
+++ b/usr.sbin/snmpd/Makefile
@@ -1,10 +1,10 @@
-# $OpenBSD: Makefile,v 1.12 2014/04/14 12:55:10 blambert Exp $
+# $OpenBSD: Makefile,v 1.13 2014/04/25 06:57:11 blambert Exp $
PROG= snmpd
MAN= snmpd.8 snmpd.conf.5
SRCS= parse.y ber.c log.c control.c snmpe.c \
mps.c trap.c mib.c smi.c kroute.c snmpd.c timer.c \
- pf.c proc.c usm.c agentx.c
+ pf.c proc.c usm.c agentx.c traphandler.c
LDADD= -levent -lutil -lkvm -lcrypto
DPADD= ${LIBEVENT} ${LIBUTIL}
diff --git a/usr.sbin/snmpd/ber.c b/usr.sbin/snmpd/ber.c
index 9ae498292ef..53e801a6514 100644
--- a/usr.sbin/snmpd/ber.c
+++ b/usr.sbin/snmpd/ber.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ber.c,v 1.26 2014/04/14 12:55:10 blambert Exp $ */
+/* $OpenBSD: ber.c,v 1.27 2014/04/25 06:57:11 blambert Exp $ */
/*
* Copyright (c) 2007, 2012 Reyk Floeter <reyk@openbsd.org>
@@ -614,7 +614,7 @@ ber_scanf_elements(struct ber_element *ber, char *fmt, ...)
va_list ap;
int *d, level = -1;
unsigned long *t;
- long long *i;
+ long long *i, l;
void **ptr;
size_t *len, ret = 0, n = strlen(fmt);
char **s;
@@ -640,6 +640,13 @@ ber_scanf_elements(struct ber_element *ber, char *fmt, ...)
goto fail;
ret++;
break;
+ case 'd':
+ d = va_arg(ap, int *);
+ if (ber_get_integer(ber, &l) == -1)
+ goto fail;
+ *d = l;
+ ret++;
+ break;
case 'e':
e = va_arg(ap, struct ber_element **);
*e = ber;
@@ -651,7 +658,6 @@ ber_scanf_elements(struct ber_element *ber, char *fmt, ...)
goto fail;
ret++;
break;
- case 'd':
case 'i':
i = va_arg(ap, long long *);
if (ber_get_integer(ber, i) == -1)
diff --git a/usr.sbin/snmpd/parse.y b/usr.sbin/snmpd/parse.y
index fe5d7fe39fd..c2048c254ff 100644
--- a/usr.sbin/snmpd/parse.y
+++ b/usr.sbin/snmpd/parse.y
@@ -1,4 +1,4 @@
-/* $OpenBSD: parse.y,v 1.31 2014/04/24 08:51:08 blambert Exp $ */
+/* $OpenBSD: parse.y,v 1.32 2014/04/25 06:57:11 blambert Exp $ */
/*
* Copyright (c) 2007, 2008, 2012 Reyk Floeter <reyk@openbsd.org>
@@ -125,13 +125,13 @@ typedef struct {
%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
-%token SOCKET RESTRICTED AGENTX
+%token SOCKET RESTRICTED AGENTX HANDLE DEFAULT
%token <v.string> STRING
%token <v.number> NUMBER
%type <v.string> hostcmn
%type <v.number> optwrite yesno seclevel socktype
-%type <v.data> objtype
-%type <v.oid> oid hostoid
+%type <v.data> objtype cmd
+%type <v.oid> oid hostoid trapoid
%type <v.auth> auth
%type <v.enc> enc
@@ -243,6 +243,19 @@ main : LISTEN ON STRING {
} host {
hlist = NULL;
}
+ | TRAP HANDLE hostcmn trapoid cmd {
+ struct trapcmd *cmd = $5.data;
+
+ cmd->cmd_oid = $4;
+
+ if (trapcmd_add(cmd) != 0) {
+ free($4);
+ free(cmd);
+ yyerror("duplicate oid");
+ YYERROR;
+ }
+ conf->sc_traphandler = 1;
+ }
| RTFILTER yesno {
if ($2 == 1)
conf->sc_rtfilter = ROUTE_FILTER(RTM_NEWADDR) |
@@ -396,6 +409,19 @@ oid : STRING {
}
;
+trapoid : oid { $$ = $1; }
+ | DEFAULT {
+ struct ber_oid *sysoid;
+ if ((sysoid =
+ calloc(1, sizeof(*sysoid))) == NULL) {
+ yyerror("calloc");
+ YYERROR;
+ }
+ ber_string2oid("1.3", sysoid);
+ $$ = sysoid;
+ }
+ ;
+
hostoid : /* empty */ { $$ = NULL; }
| OBJECTID oid { $$ = $2; }
;
@@ -488,6 +514,52 @@ socktype : RESTRICTED { $$ = SOCK_TYPE_RESTRICTED; }
| /* nothing */ { $$ = 0; }
;
+cmd : STRING {
+ struct trapcmd *cmd;
+ size_t span, limit;
+ char *pos, **args, **args2;
+ int nargs = 32; /* XXX */
+
+ if ((cmd = calloc(1, sizeof(*cmd))) == NULL ||
+ (args = calloc(nargs, sizeof(char *))) == NULL) {
+ free(cmd);
+ free($1);
+ YYERROR;
+ }
+
+ pos = $1;
+ limit = strlen($1);
+
+ while ((span = strcspn(pos, " \t")) != 0 &&
+ pos < $1 + limit) {
+ pos[span] = '\0';
+ args[cmd->cmd_argc] = strdup(pos);
+ if (args[cmd->cmd_argc] == NULL) {
+ trapcmd_free(cmd);
+ free(args);
+ free($1);
+ YYERROR;
+ }
+ cmd->cmd_argc++;
+ if (cmd->cmd_argc >= nargs - 1) {
+ nargs *= 2;
+ args2 = calloc(nargs, sizeof(char *));
+ if (args2 == NULL) {
+ trapcmd_free(cmd);
+ free(args);
+ free($1);
+ YYERROR;
+ }
+ args = args2;
+ }
+ pos += span + 1;
+ }
+ free($1);
+ cmd->cmd_argv = args;
+ $$.data = cmd;
+ }
+ ;
+
%%
struct keywords {
@@ -527,11 +599,13 @@ lookup(char *s)
{ "authkey", AUTHKEY },
{ "community", COMMUNITY },
{ "contact", CONTACT },
+ { "default", DEFAULT },
{ "description", DESCR },
{ "disabled", DISABLED},
{ "enc", ENC },
{ "enckey", ENCKEY },
{ "filter-routes", RTFILTER },
+ { "handle", HANDLE },
{ "include", INCLUDE },
{ "integer", INTEGER },
{ "listen", LISTEN },
diff --git a/usr.sbin/snmpd/snmpd.c b/usr.sbin/snmpd/snmpd.c
index e36820f322e..dc80527a540 100644
--- a/usr.sbin/snmpd/snmpd.c
+++ b/usr.sbin/snmpd/snmpd.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: snmpd.c,v 1.20 2014/04/21 19:47:27 reyk Exp $ */
+/* $OpenBSD: snmpd.c,v 1.21 2014/04/25 06:57:11 blambert Exp $ */
/*
* Copyright (c) 2007, 2008, 2012 Reyk Floeter <reyk@openbsd.org>
@@ -51,7 +51,9 @@ int check_child(pid_t, const char *);
struct snmpd *snmpd_env;
static struct privsep_proc procs[] = {
- { "snmpe", PROC_SNMPE, snmpd_dispatch_snmpe, snmpe, snmpe_shutdown }
+ { "snmpe", PROC_SNMPE, snmpd_dispatch_snmpe, snmpe, snmpe_shutdown },
+ { "traphandler", PROC_ALERT, snmpd_dispatch_traphandler, traphandler,
+ traphandler_shutdown }
};
void
diff --git a/usr.sbin/snmpd/snmpd.conf.5 b/usr.sbin/snmpd/snmpd.conf.5
index e03e000cf3a..2fbb7693a37 100644
--- a/usr.sbin/snmpd/snmpd.conf.5
+++ b/usr.sbin/snmpd/snmpd.conf.5
@@ -1,4 +1,4 @@
-.\" $OpenBSD: snmpd.conf.5,v 1.30 2014/04/14 15:34:48 jmc Exp $
+.\" $OpenBSD: snmpd.conf.5,v 1.31 2014/04/25 06:57:11 blambert Exp $
.\"
.\" Copyright (c) 2007, 2008, 2012 Reyk Floeter <reyk@openbsd.org>
.\"
@@ -14,7 +14,7 @@
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
-.Dd $Mdocdate: April 14 2014 $
+.Dd $Mdocdate: April 25 2014 $
.Dt SNMPD.CONF 5
.Os
.Sh NAME
@@ -195,6 +195,23 @@ configured trap community.
The default community is specified by the global
.Ic trap community
option.
+.It Ic trap handle Ar oid Qq Ar command
+Execute
+.Ic command
+upon receipt of an SNMP trap that begins with a prefix of
+.Ic oid .
+Alternately, the string
+.Qq Ic default
+may be used, in which case the prefix used is
+.Ic 1.3 .
+The invoked
+.Ar command
+will receive the following information about the trap on standard input,
+one per line, in this order:
+the resolved hostname of the host sending the trap,
+the IP address of the host sending the trap,
+and any variable bindings contained in the trap
+(the OID followed by the value, separated by a single space).
.El
.Sh USER CONFIGURATION
Users for the SNMP User-based Security Model (USM, RFC 3414) must be
diff --git a/usr.sbin/snmpd/snmpd.h b/usr.sbin/snmpd/snmpd.h
index 1355faf71e7..7383fea488a 100644
--- a/usr.sbin/snmpd/snmpd.h
+++ b/usr.sbin/snmpd/snmpd.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: snmpd.h,v 1.51 2014/04/21 19:47:27 reyk Exp $ */
+/* $OpenBSD: snmpd.h,v 1.52 2014/04/25 06:57:11 blambert Exp $ */
/*
* Copyright (c) 2007, 2008, 2012 Reyk Floeter <reyk@openbsd.org>
@@ -75,7 +75,8 @@ enum imsg_type {
IMSG_CTL_END,
IMSG_CTL_NOTIFY,
IMSG_CTL_VERBOSE,
- IMSG_CTL_RELOAD
+ IMSG_CTL_RELOAD,
+ IMSG_ALERT
};
struct imsgev {
@@ -111,6 +112,7 @@ TAILQ_HEAD(control_socks, control_sock);
enum privsep_procid {
PROC_PARENT, /* Parent process and application interface */
PROC_SNMPE, /* SNMP engine */
+ PROC_ALERT, /* SNMP trap receiver */
PROC_MAX
};
@@ -515,10 +517,24 @@ struct snmpd {
int sc_min_seclevel;
int sc_readonly;
+ int sc_traphandler;
struct privsep sc_ps;
};
+struct trapcmd {
+ struct ber_oid *cmd_oid;
+ /* sideways return for intermediate lookups */
+ struct trapcmd *cmd_maybe;
+
+ int cmd_argc;
+ char **cmd_argv;
+
+ RB_ENTRY(trapcmd) cmd_entry;
+};
+RB_HEAD(trapcmd_tree, trapcmd);
+extern struct trapcmd_tree trapcmd_tree;
+
/* control.c */
int control_init(struct privsep *, struct control_sock *);
int control_listen(struct control_sock *);
@@ -682,4 +698,13 @@ struct imsgbuf *
struct imsgev *
proc_iev(struct privsep *, enum privsep_procid, int);
+/* traphandler.c */
+pid_t traphandler(struct privsep *, struct privsep_proc *);
+void traphandler_shutdown(void);
+int snmpd_dispatch_traphandler(int, struct privsep_proc *, struct imsg *);
+void trapcmd_free(struct trapcmd *);
+int trapcmd_add(struct trapcmd *);
+struct trapcmd *
+ trapcmd_lookup(struct ber_oid *);
+
#endif /* _SNMPD_H */
diff --git a/usr.sbin/snmpd/traphandler.c b/usr.sbin/snmpd/traphandler.c
new file mode 100644
index 00000000000..08be75451e8
--- /dev/null
+++ b/usr.sbin/snmpd/traphandler.c
@@ -0,0 +1,470 @@
+/* $OpenBSD: traphandler.c,v 1.1 2014/04/25 06:57:11 blambert Exp $ */
+/*
+ * Copyright (c) 2014 Bret Stephen Lambert <blambert@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/queue.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <sys/wait.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <event.h>
+#include <fcntl.h>
+#include <imsg.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <pwd.h>
+
+#include "ber.h"
+#include "snmpd.h"
+#include "mib.h"
+
+int trapsock;
+struct event trapev;
+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(char *, size_t, struct ber_element **,
+ struct ber_element **, u_int *, struct ber_oid *);
+void traphandler_v1translate(struct ber_oid *, u_int, u_int);
+
+int trapcmd_cmp(struct trapcmd *, struct trapcmd *);
+void trapcmd_exec(struct trapcmd *, struct sockaddr *,
+ struct ber_element *, char *, u_int);
+
+char *traphandler_hostname(struct sockaddr *, int);
+
+RB_PROTOTYPE(trapcmd_tree, trapcmd, cmd_entry, trapcmd_cmp)
+RB_GENERATE(trapcmd_tree, trapcmd, cmd_entry, trapcmd_cmp)
+
+struct trapcmd_tree trapcmd_tree = RB_INITIALIZER(&trapcmd_tree);
+
+static struct privsep_proc procs[] = {
+ { "parent", PROC_PARENT, traphandler_dispatch_parent }
+};
+
+pid_t
+traphandler(struct privsep *ps, struct privsep_proc *p)
+{
+ struct snmpd *env = ps->ps_env;
+
+ if (env->sc_traphandler &&
+ (trapsock = traphandler_bind(&env->sc_address)) == -1)
+ fatal("could not create trap listener socket");
+
+ return (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;
+
+ if (!env->sc_traphandler)
+ return;
+
+ /* listen for SNMP trap messages */
+ event_set(&trapev, trapsock, EV_READ|EV_PERSIST, traphandler_recvmsg,
+ ps);
+ event_add(&trapev, NULL);
+}
+
+int
+traphandler_bind(struct address *addr)
+{
+ int s;
+
+ if ((s = snmpd_socket_af(&addr->ss, htons(SNMPD_TRAPPORT))) == -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;
+
+ return (s);
+ bad:
+ close (s);
+ return (-1);
+}
+
+void
+traphandler_shutdown(void)
+{
+ event_del(&trapev);
+ close(trapsock);
+}
+
+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 privsep *ps = arg;
+ char buf[8196];
+ struct iovec iov[2];
+ struct sockaddr_storage ss;
+ socklen_t slen;
+ ssize_t n;
+ struct ber_element *req, *iter;
+ struct ber_oid trapoid;
+ u_int uptime;
+
+ slen = sizeof(ss);
+ if ((n = recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *)&ss,
+ &slen)) == -1)
+ return;
+
+ if (traphandler_parse(buf, n, &req, &iter, &uptime, &trapoid) == -1)
+ goto done;
+
+ iov[0].iov_base = &ss;
+ iov[0].iov_len = ss.ss_len;
+ iov[1].iov_base = buf;
+ iov[1].iov_len = n;
+
+ /* Forward it to the parent process */
+ if (proc_composev_imsg(ps, PROC_PARENT, -1, IMSG_ALERT,
+ -1, iov, 2) == -1)
+ goto done;
+
+ done:
+ if (req != NULL)
+ ber_free_elements(req);
+ return;
+}
+
+/*
+ * Validate received message
+ */
+int
+traphandler_parse(char *buf, size_t n, struct ber_element **req,
+ struct ber_element **vbinds, u_int *uptime, struct ber_oid *trapoid)
+{
+ struct ber ber;
+ struct ber_element *elm;
+ u_int vers, gtype, etype;
+
+ bzero(&ber, sizeof(ber));
+ ber.fd = -1;
+ ber_set_application(&ber, smi_application);
+ ber_set_readbuf(&ber, buf, n);
+
+ if ((*req = ber_read_elements(&ber, NULL)) == NULL)
+ goto done;
+
+ if (ber_scanf_elements(*req, "{dSe", &vers, &elm) == -1)
+ goto done;
+
+ switch (vers) {
+ case SNMP_V1:
+ if (ber_scanf_elements(elm, "{oSddd",
+ trapoid, &gtype, &etype, uptime) == -1)
+ goto done;
+ traphandler_v1translate(trapoid, gtype, etype);
+ break;
+
+ case SNMP_V2:
+ if (ber_scanf_elements(elm, "{SSSS{e}}", &elm) == -1 ||
+ ber_scanf_elements(elm, "{SdS}{So}e",
+ uptime, trapoid, vbinds) == -1)
+ goto done;
+ break;
+
+ default:
+ log_warnx("unsupported SNMP trap version '%d'", vers);
+ goto done;
+ }
+
+ ber_free(&ber);
+ return (0);
+
+ done:
+ ber_free(&ber);
+ if (*req)
+ ber_free_elements(*req);
+ *req = NULL;
+ return (-1);
+}
+
+void
+traphandler_v1translate(struct ber_oid *oid, u_int gtype, u_int etype)
+{
+ /* append 'specific trap' number to 'enterprise specific' traps */
+ if (gtype >= 6) {
+ oid->bo_id[oid->bo_n] = 0;
+ oid->bo_id[oid->bo_n + 1] = etype;
+ oid->bo_n += 2;
+ }
+}
+
+int
+traphandler_priv_recvmsg(struct privsep_proc *p, struct imsg *imsg)
+{
+ ssize_t n;
+ pid_t pid;
+
+ if ((n = IMSG_DATA_SIZE(imsg)) <= 0)
+ return (-1); /* XXX */
+
+ switch ((pid = fork())) {
+ case 0:
+ traphandler_fork_handler(p, imsg);
+ /* NOTREACHED */
+ case -1:
+ log_warn("%s: couldn't fork traphandler", __func__);
+ return (0);
+ default:
+ log_debug("forked process %i to handle trap", pid);
+ return (0);
+ }
+ /* NOTREACHED */
+}
+
+int
+traphandler_fork_handler(struct privsep_proc *p, struct imsg *imsg)
+{
+ char oidbuf[SNMP_MAX_OID_STRLEN];
+ struct sockaddr *sa;
+ char *buf;
+ ssize_t n;
+ struct ber_element *req, *iter;
+ struct trapcmd *cmd;
+ struct ber_oid trapoid;
+ u_int uptime;
+ struct passwd *pw;
+ extern int debug;
+
+ pw = p->p_ps->ps_pw;
+
+ if (setgroups(1, &pw->pw_gid) ||
+ setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
+ setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
+ fatal("traphandler_fork_handler: cannot drop privileges");
+
+ closefrom(STDERR_FILENO + 1);
+ log_init(debug);
+
+ n = IMSG_DATA_SIZE(imsg);
+
+ sa = imsg->data;
+ n -= sa->sa_len;
+ buf = (char *)imsg->data + sa->sa_len;
+
+ if (traphandler_parse(buf, n, &req, &iter, &uptime, &trapoid) == -1)
+ fatalx("couldn't parse SNMP trap message");
+
+ smi_oid2string(&trapoid, oidbuf, sizeof(oidbuf), 0);
+ if ((cmd = trapcmd_lookup(&trapoid)) != NULL)
+ trapcmd_exec(cmd, sa, iter, oidbuf, uptime);
+
+ if (req != NULL)
+ ber_free_elements(req);
+
+ exit(0);
+}
+
+void
+trapcmd_exec(struct trapcmd *cmd, struct sockaddr *sa,
+ struct ber_element *iter, char *trapoid, u_int uptime)
+{
+ char oidbuf[SNMP_MAX_OID_STRLEN];
+ struct ber_oid oid;
+ struct ber_element *elm;
+ int n, s[2], status = 0;
+ char *value, *host;
+ pid_t child = -1;
+
+ if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, s) == -1) {
+ log_warn("could not create pipe for OID '%s'",
+ smi_oid2string(cmd->cmd_oid, oidbuf, sizeof(oidbuf), 0));
+ return;
+ }
+
+ switch (child = fork()) {
+ case 0:
+ dup2(s[1], STDIN_FILENO);
+
+ close(s[0]);
+ close(s[1]);
+
+ closefrom(STDERR_FILENO + 1);
+
+ /* path to command is in argv[0], args follow */
+ execve(cmd->cmd_argv[0], cmd->cmd_argv, NULL);
+
+ /* this shouldn't happen */
+ log_warn("could not exec trap command for OID '%s'",
+ smi_oid2string(cmd->cmd_oid, oidbuf, sizeof(oidbuf), 0));
+ _exit(1);
+ /* NOTREACHED */
+
+ case -1:
+ log_warn("could not fork trap command for OID '%s'",
+ smi_oid2string(cmd->cmd_oid, oidbuf, sizeof(oidbuf), 0));
+ close(s[0]);
+ close(s[1]);
+ return;
+ }
+
+ close(s[1]);
+
+ host = traphandler_hostname(sa, 0);
+ if (dprintf(s[0], "%s\n", host) == -1)
+ goto out;
+
+ host = traphandler_hostname(sa, 1);
+ if (dprintf(s[0], "%s\n", host) == -1)
+ goto out;
+
+ if (dprintf(s[0],
+ "iso.org.dod.internet.mgmt.mib-2.system.sysUpTime.0 %u\n",
+ uptime) == -1)
+ goto out;
+
+ if (dprintf(s[0],
+ "iso.org.dod.internet.snmpV2.snmpModules.snmpMIB.snmpMIBObjects."
+ "snmpTrap.snmpTrapOID.0 %s\n", trapoid) == -1)
+ goto out;
+
+ for (; iter != NULL; iter = iter->be_next) {
+ if (ber_scanf_elements(iter, "{oe}", &oid, &elm) == -1)
+ goto out;
+ if ((value = smi_print_element(elm)) == NULL)
+ goto out;
+ smi_oid2string(&oid, oidbuf, sizeof(oidbuf), 0);
+ n = dprintf(s[0], "%s %s\n", oidbuf, value);
+ free(value);
+ if (n == -1)
+ goto out;
+ }
+ out:
+ close(s[0]);
+ waitpid(child, &status, 0);
+
+ if (WIFSIGNALED(status)) {
+ log_warnx("child %i exited due to receipt of signal %i",
+ child, WTERMSIG(status));
+ } else if (WEXITSTATUS(status) != 0) {
+ log_warnx("child %i exited with status %i",
+ child, WEXITSTATUS(status));
+ } else {
+ log_debug("child %i finished", child);
+ }
+ close(s[1]);
+
+ return;
+}
+
+char *
+traphandler_hostname(struct sockaddr *sa, int numeric)
+{
+ static char buf[NI_MAXHOST];
+ int flag = 0;
+
+ if (numeric)
+ flag = NI_NUMERICHOST;
+
+ bzero(buf, sizeof(buf));
+ if (getnameinfo(sa, sa->sa_len, buf, sizeof(buf), NULL, 0, flag) != 0)
+ return ("Unknown");
+
+ return (buf);
+}
+
+struct trapcmd *
+trapcmd_lookup(struct ber_oid *oid)
+{
+ struct trapcmd key, *res;
+
+ bzero(&key, sizeof(key));
+ key.cmd_oid = oid;
+
+ if ((res = RB_FIND(trapcmd_tree, &trapcmd_tree, &key)) == NULL)
+ res = key.cmd_maybe;
+ return (res);
+}
+
+int
+trapcmd_cmp(struct trapcmd *cmd1, struct trapcmd *cmd2)
+{
+ int ret;
+
+ ret = ber_oid_cmp(cmd2->cmd_oid, cmd1->cmd_oid);
+ switch (ret) {
+ case 2:
+ /* cmd1 is a child of cmd2 */
+ cmd1->cmd_maybe = cmd2;
+ return (1);
+ default:
+ return (ret);
+ }
+ /* NOTREACHED */
+}
+
+int
+trapcmd_add(struct trapcmd *cmd)
+{
+ return (RB_INSERT(trapcmd_tree, &trapcmd_tree, cmd) != NULL);
+}
+
+void
+trapcmd_free(struct trapcmd *cmd)
+{
+ RB_REMOVE(trapcmd_tree, &trapcmd_tree, cmd);
+ free(cmd->cmd_argv);
+ free(cmd->cmd_oid);
+ free(cmd);
+}