summaryrefslogtreecommitdiff
path: root/usr.sbin
diff options
context:
space:
mode:
authorReyk Floeter <reyk@cvs.openbsd.org>2007-12-05 09:22:45 +0000
committerReyk Floeter <reyk@cvs.openbsd.org>2007-12-05 09:22:45 +0000
commit174402352855c05a4a262f9afbbd6de1dd3ed82c (patch)
tree66e421d1b6a3d063f3d25c178aef3a600d370fc1 /usr.sbin
parent67c2cb68245a11d91e5fe144633f5f970f775656 (diff)
Start working on snmpd(8) and snmpctl(8), a lightweight SNMP implementation
for OpenBSD. SNMP is a necessary evil. This is work in progress, don't expect too much from it yet. ok deraadt@
Diffstat (limited to 'usr.sbin')
-rw-r--r--usr.sbin/snmpctl/Makefile16
-rw-r--r--usr.sbin/snmpctl/parser.c155
-rw-r--r--usr.sbin/snmpctl/parser.h30
-rw-r--r--usr.sbin/snmpctl/snmpctl.856
-rw-r--r--usr.sbin/snmpctl/snmpctl.c191
-rw-r--r--usr.sbin/snmpd/Makefile17
-rw-r--r--usr.sbin/snmpd/README121
-rw-r--r--usr.sbin/snmpd/ber.3233
-rw-r--r--usr.sbin/snmpd/ber.c1163
-rw-r--r--usr.sbin/snmpd/ber.h123
-rw-r--r--usr.sbin/snmpd/buffer.c244
-rw-r--r--usr.sbin/snmpd/control.c270
-rw-r--r--usr.sbin/snmpd/imsg.c233
-rw-r--r--usr.sbin/snmpd/kroute.c1093
-rw-r--r--usr.sbin/snmpd/log.c182
-rw-r--r--usr.sbin/snmpd/mib.c836
-rw-r--r--usr.sbin/snmpd/mib.h209
-rw-r--r--usr.sbin/snmpd/mps.c447
-rw-r--r--usr.sbin/snmpd/parse.y936
-rw-r--r--usr.sbin/snmpd/snmp.h86
-rw-r--r--usr.sbin/snmpd/snmpd.893
-rw-r--r--usr.sbin/snmpd/snmpd.c295
-rw-r--r--usr.sbin/snmpd/snmpd.conf9
-rw-r--r--usr.sbin/snmpd/snmpd.conf.583
-rw-r--r--usr.sbin/snmpd/snmpd.h407
-rw-r--r--usr.sbin/snmpd/snmpe.c803
26 files changed, 8331 insertions, 0 deletions
diff --git a/usr.sbin/snmpctl/Makefile b/usr.sbin/snmpctl/Makefile
new file mode 100644
index 00000000000..dedb04dd6e7
--- /dev/null
+++ b/usr.sbin/snmpctl/Makefile
@@ -0,0 +1,16 @@
+# $Id: Makefile,v 1.1 2007/12/05 09:22:44 reyk Exp $
+
+.PATH: ${.CURDIR}/../snmpd
+
+PROG= snmpctl
+SRCS= buffer.c imsg.c log.c snmpctl.c parser.c
+
+MAN= snmpctl.8
+
+CFLAGS+= -Wall -I${.CURDIR} -I${.CURDIR}/../snmpd
+CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes
+CFLAGS+= -Wmissing-declarations
+CFLAGS+= -Wshadow -Wpointer-arith -Wcast-qual
+CFLAGS+= -Wsign-compare -Wbounded
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/snmpctl/parser.c b/usr.sbin/snmpctl/parser.c
new file mode 100644
index 00000000000..1f9c0140c6e
--- /dev/null
+++ b/usr.sbin/snmpctl/parser.c
@@ -0,0 +1,155 @@
+/* $Id: parser.c,v 1.1 2007/12/05 09:22:44 reyk Exp $ */
+
+/*
+ * Copyright (c) 2004 Esben Norby <norby@openbsd.org>
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@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/types.h>
+#include <sys/socket.h>
+#include <sys/queue.h>
+#include <sys/tree.h>
+
+#include <netinet/in.h>
+#include <net/if.h>
+#include <arpa/inet.h>
+
+#include <err.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <event.h>
+
+#include "snmpd.h"
+#include "parser.h"
+
+enum token_type {
+ NOTOKEN,
+ ENDTOKEN,
+ KEYWORD
+};
+
+struct token {
+ enum token_type type;
+ const char *keyword;
+ int value;
+ const struct token *next;
+};
+
+static const struct token t_main[];
+static const struct token t_show[];
+
+static const struct token t_main[] = {
+ {KEYWORD, "monitor", MONITOR, NULL},
+ {ENDTOKEN, "", NONE, NULL}
+};
+
+static struct parse_result res;
+
+struct parse_result *
+parse(int argc, char *argv[])
+{
+ const struct token *table = t_main;
+ const struct token *match;
+
+ bzero(&res, sizeof(res));
+
+ while (argc >= 0) {
+ if ((match = match_token(argv[0], table)) == NULL) {
+ fprintf(stderr, "valid commands/args:\n");
+ show_valid_args(table);
+ return (NULL);
+ }
+
+ argc--;
+ argv++;
+
+ if (match->type == NOTOKEN || match->next == NULL)
+ break;
+
+ table = match->next;
+ }
+
+ if (argc > 0) {
+ fprintf(stderr, "superfluous argument: %s\n", argv[0]);
+ return (NULL);
+ }
+
+ return (&res);
+}
+
+const struct token *
+match_token(const char *word, const struct token table[])
+{
+ u_int i, match;
+ const struct token *t = NULL;
+
+ match = 0;
+
+ for (i = 0; table[i].type != ENDTOKEN; i++) {
+ switch (table[i].type) {
+ case NOTOKEN:
+ if (word == NULL || strlen(word) == 0) {
+ match++;
+ t = &table[i];
+ }
+ break;
+ case KEYWORD:
+ if (word != NULL && strncmp(word, table[i].keyword,
+ strlen(word)) == 0) {
+ match++;
+ t = &table[i];
+ if (t->value)
+ res.action = t->value;
+ }
+ break;
+ case ENDTOKEN:
+ break;
+ }
+ }
+
+ if (match != 1) {
+ if (word == NULL)
+ fprintf(stderr, "missing argument:\n");
+ else if (match > 1)
+ fprintf(stderr, "ambiguous argument: %s\n", word);
+ else if (match < 1)
+ fprintf(stderr, "unknown argument: %s\n", word);
+ return (NULL);
+ }
+
+ return (t);
+}
+
+void
+show_valid_args(const struct token table[])
+{
+ int i;
+
+ for (i = 0; table[i].type != ENDTOKEN; i++) {
+ switch (table[i].type) {
+ case NOTOKEN:
+ fprintf(stderr, " <cr>\n");
+ break;
+ case KEYWORD:
+ fprintf(stderr, " %s\n", table[i].keyword);
+ break;
+ case ENDTOKEN:
+ break;
+ }
+ }
+}
diff --git a/usr.sbin/snmpctl/parser.h b/usr.sbin/snmpctl/parser.h
new file mode 100644
index 00000000000..41202685b9d
--- /dev/null
+++ b/usr.sbin/snmpctl/parser.h
@@ -0,0 +1,30 @@
+/* $OpenBSD: parser.h,v 1.1 2007/12/05 09:22:44 reyk Exp $ */
+
+/*
+ * Copyright (c) 2007 Reyk Floeter <reyk@vantronix.net>
+ *
+ * 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.
+ */
+
+enum actions {
+ NONE,
+ MONITOR
+};
+
+struct parse_result {
+ enum actions action;
+};
+
+struct parse_result *parse(int, char *[]);
+const struct token *match_token(const char *, const struct token []);
+void show_valid_args(const struct token []);
diff --git a/usr.sbin/snmpctl/snmpctl.8 b/usr.sbin/snmpctl/snmpctl.8
new file mode 100644
index 00000000000..c83747ffe1c
--- /dev/null
+++ b/usr.sbin/snmpctl/snmpctl.8
@@ -0,0 +1,56 @@
+.\" $OpenBSD: snmpctl.8,v 1.1 2007/12/05 09:22:44 reyk Exp $
+.\"
+.\" Copyright (c) 2006 Reyk Floeter <reyk@vantronix.net>
+.\"
+.\" 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.
+.\"
+.Dd $Mdocdate: December 5 2007 $
+.Dt SNMPCTL 8
+.Os
+.Sh NAME
+.Nm snmpctl
+.Nd control the SNMP daemon
+.Sh SYNOPSIS
+.Nm
+.Ar command
+.Op Ar arguments ...
+.Sh DESCRIPTION
+The
+.Nm
+program controls the
+.Xr snmpd 8
+daemon.
+.Pp
+The following commands are available:
+.Bl -tag -width Ds
+.It Cm something
+Do something
+.El
+.Sh FILES
+.Bl -tag -width "/var/run/snmpd.sockXX" -compact
+.It /var/run/snmpd.sock
+Unix-domain socket used for communication with
+.Xr snmpd 8 .
+.El
+.Sh SEE ALSO
+.Xr snmpd 8
+.Sh HISTORY
+The
+.Nm
+program first appeared in
+.Ox 4.3 .
+.Sh AUTHORS
+The
+.Nm
+program was written by
+.An Reyk Floeter Aq reyk@vantronix.net .
diff --git a/usr.sbin/snmpctl/snmpctl.c b/usr.sbin/snmpctl/snmpctl.c
new file mode 100644
index 00000000000..c625645223f
--- /dev/null
+++ b/usr.sbin/snmpctl/snmpctl.c
@@ -0,0 +1,191 @@
+/* $Id: snmpctl.c,v 1.1 2007/12/05 09:22:44 reyk Exp $ */
+
+/*
+ * Copyright (c) 2007 Reyk Floeter <reyk@vantronix.net>
+ * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
+ * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
+ * Copyright (c) 2003 Henning Brauer <henning@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/types.h>
+#include <sys/socket.h>
+#include <sys/queue.h>
+#include <sys/un.h>
+#include <sys/tree.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
+
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <event.h>
+
+#include "snmpd.h"
+#include "parser.h"
+
+__dead void usage(void);
+
+struct imsgname {
+ int type;
+ char *name;
+ void (*func)(struct imsg *);
+};
+
+struct imsgname *monitor_lookup(u_int8_t);
+void monitor_host_status(struct imsg *);
+void monitor_id(struct imsg *);
+int monitor(struct imsg *);
+
+struct imsgname imsgs[] = {
+ { 0, NULL, NULL }
+};
+struct imsgname imsgunknown = {
+ -1, "<unknown>", NULL
+};
+
+struct imsgbuf *ibuf;
+
+__dead void
+usage(void)
+{
+ extern char *__progname;
+
+ fprintf(stderr, "usage: %s <command> [arg [...]]\n", __progname);
+ exit(1);
+}
+
+/* dummy function so that hoststatectl does not need libevent */
+void
+imsg_event_add(struct imsgbuf *i)
+{
+ /* nothing */
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct sockaddr_un sun;
+ struct parse_result *res;
+ struct imsg imsg;
+ int ctl_sock;
+ int done = 0;
+ int n;
+
+ /* parse options */
+ if ((res = parse(argc - 1, argv + 1)) == NULL)
+ exit(1);
+
+ /* connect to snmpd control socket */
+ if ((ctl_sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
+ err(1, "socket");
+
+ bzero(&sun, sizeof(sun));
+ sun.sun_family = AF_UNIX;
+ strlcpy(sun.sun_path, SNMPD_SOCKET, sizeof(sun.sun_path));
+ reconnect:
+ if (connect(ctl_sock, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
+ /* Keep retrying if running in monitor mode */
+ if (res->action == MONITOR &&
+ (errno == ENOENT || errno == ECONNREFUSED)) {
+ usleep(100);
+ goto reconnect;
+ }
+ err(1, "connect: %s", SNMPD_SOCKET);
+ }
+
+ if ((ibuf = malloc(sizeof(struct imsgbuf))) == NULL)
+ err(1, NULL);
+ imsg_init(ibuf, ctl_sock, NULL);
+ done = 0;
+
+ /* process user request */
+ switch (res->action) {
+ case NONE:
+ usage();
+ /* not reached */
+ case MONITOR:
+ imsg_compose(ibuf, IMSG_CTL_NOTIFY, 0, 0, -1, NULL, 0);
+ break;
+ }
+
+ while (ibuf->w.queued)
+ if (msgbuf_write(&ibuf->w) < 0)
+ err(1, "write error");
+
+ while (!done) {
+ if ((n = imsg_read(ibuf)) == -1)
+ errx(1, "imsg_read error");
+ if (n == 0)
+ errx(1, "pipe closed");
+
+ while (!done) {
+ if ((n = imsg_get(ibuf, &imsg)) == -1)
+ errx(1, "imsg_get error");
+ if (n == 0)
+ break;
+ switch (res->action) {
+ case MONITOR:
+ done = monitor(&imsg);
+ break;
+ case NONE:
+ break;
+ }
+ imsg_free(&imsg);
+ }
+ }
+ close(ctl_sock);
+ free(ibuf);
+
+ return (0);
+}
+
+struct imsgname *
+monitor_lookup(u_int8_t type)
+{
+ int i;
+
+ for (i = 0; imsgs[i].name != NULL; i++)
+ if (imsgs[i].type == type)
+ return (&imsgs[i]);
+ return (&imsgunknown);
+}
+
+int
+monitor(struct imsg *imsg)
+{
+ time_t now;
+ int done = 0;
+ struct imsgname *imn;
+
+ now = time(NULL);
+
+ imn = monitor_lookup(imsg->hdr.type);
+ printf("%s: imsg type %u len %u peerid %u pid %d\n", imn->name,
+ imsg->hdr.type, imsg->hdr.len, imsg->hdr.peerid, imsg->hdr.pid);
+ printf("\ttimestamp: %u, %s", now, ctime(&now));
+ if (imn->type == -1)
+ done = 1;
+ if (imn->func != NULL)
+ (*imn->func)(imsg);
+
+ return (done);
+}
diff --git a/usr.sbin/snmpd/Makefile b/usr.sbin/snmpd/Makefile
new file mode 100644
index 00000000000..2ac483ded9b
--- /dev/null
+++ b/usr.sbin/snmpd/Makefile
@@ -0,0 +1,17 @@
+# $OpenBSD: Makefile,v 1.1 2007/12/05 09:22:44 reyk Exp $
+
+PROG= snmpd
+MAN= snmpd.8 snmpd.conf.5 ber.3
+SRCS= parse.y ber.c log.c control.c buffer.c imsg.c snmpe.c \
+ mps.c mib.c kroute.c snmpd.c
+
+LDADD= -levent
+DPADD= ${LIBEVENT}
+CFLAGS+= -Wall -I${.CURDIR}
+CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes
+CFLAGS+= -Wmissing-declarations
+CFLAGS+= -Wshadow -Wpointer-arith -Wcast-qual
+CFLAGS+= -Wsign-compare -Wbounded
+CLEANFILES+= y.tab.h
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/snmpd/README b/usr.sbin/snmpd/README
new file mode 100644
index 00000000000..8025c02be2b
--- /dev/null
+++ b/usr.sbin/snmpd/README
@@ -0,0 +1,121 @@
+# $OpenBSD: README,v 1.1 2007/12/05 09:22:44 reyk Exp $
+
+SNMP daemon
+-----------
+
+(Scary Network Management Protocol Daemon)
+
+ This is the very experimental implementation of a simple SNMP daemon.
+ Even if SNMP is not our favorite protocol, it is a very important
+ key requirement in many corporate networks; sync to reality. And
+ we don't want to rely on horrid SNMP implementations in this case.
+
+DESIGN
+
+ The main amazingly new thing is that this daemon will use the imsg
+ privsep approach using several different processes ("engines") to
+ separate privileges when handling the requests.
+
+ It uses a new ASN.1/BER implementation to parse and create the SNMP
+ messages. It is a simple implementation which does what we need.
+
+ I may change the layout a to look a bit more like RFC 2271. But
+ the proposed design in RFC 2271 is a bad joke; it uses too many
+ subsystems with the requirement to communicate with ASN.1/BER
+ messages.
+
+ The following picture is based on section 3.1 of RFC 2271 describing
+ the proposed design of SNMPv3 entities and the current mapping in
+ snmpd:
+
+ +-------------------------------------------------------------------+
+ | SNMP entity (snmpd(8)) |
+ | snmpd(8) |
+ | +-------------------------------------------------------------+ |
+ | | SNMP engine (identified by snmpEngineID) | |
+ | | snmpe.c | |
+ | | +------------+ +------------+ +-----------+-+-----------+ | |
+ | | | | | | | |\ | | |
+ | | | Dispatcher | | Message | | Security | | Access | | |
+ | | | | | Processing | | Subsystem | | Control | | |
+ | | | IMSG, | | Subsystem | | | | Subsystem | | |
+ | | | event(3) | | mps.c | | TODO \| | | |
+ | | +------------+ +------------+ +-----------+-+-----------+ | |
+ | | || || | | |
+ | +--------||-----------||---|----------------------------------+ |
+ | || || | |
+ | || || | --> application |
+ | || || | (unpriv, functions calls) |
+ | || || | |
+ | || || ------> application |
+ | || || | (priv, IMSGs to snmpd.c) |
+ | || || | |
+ . ||--------------------> external application .
+ . || || | (IMSGs via /var/run/snmpd.sock) .
+ . || || | .
+ . +--------||-----------||---|----------------------------------+ .
+ | | Application(s) | |
+ | | snmpd.c, snmpctl(8), possible: ospfd, bgpd, relayd, ... | |
+ | | +-------------+ +--------------+ +--------------+ | |
+ | | | Command | | Notification | | Proxy | | |
+ | | | Generator | | Receiver | | Forwarder | | |
+ | | +-------------+ +--------------+ +--------------+ | |
+ | | | |
+ | | +-------------+ +--------------+ +--------------+ | |
+ | | | Command | | Notification | | Other | | |
+ | | | Responder | | Originator | | | | |
+ | | +-------------+ +--------------+ +--------------+ | |
+ | | | |
+ | +-------------------------------------------------------------+ |
+ | |
+ +-------------------------------------------------------------------+
+
+FUZZ
+
+ There is at least one SNMP fuzzer. Of course, it will not crash snmpd.
+ http://www.hackingciscoexposed.com/tools/snmp-fuzzer-0.1.1.tar.gz
+
+TODO
+
+ snmpd:
+ - Implement the advanced SNMPv2 features like INFORM
+ - Implement USM (User-based Security Model)
+ - Implement some MIBs (SNMPv2/3 standard MIBs)
+ - MIB-2 (mostly done)
+ - IF-MIB (mostly done)
+ - IP-MIB
+ - TCP-MIB
+ - UDP-MIB
+ - BRIDGE-MIB
+ - OPENBSD-MIB (for sensors, etc.)
+ - ...
+ - Review review review
+ - Separate mps and snmpe?
+ - add trap receiver/logger
+ - manpages
+
+ - There is a SNMP over SSH draft, interesting reading:
+ http://tools.ietf.org/html/draft-ietf-isms-secshell-09
+ http://tools.ietf.org/wg/isms/
+
+ snmpctl:
+ - Add commands to query the SNMP mibtree via imsg
+ - Add commands to query/set remote SNMP daemons (instead snmpwalk etc.)
+ - Internal status, lalala, ...
+ - manpage
+
+NOT TO DO
+
+ - Do not implement all the zillions of MIBs from ucdavis/net-snmp
+ - Do not provide a complex API for plugins, modules, and pimp-my-SNMP
+ - ...
+
+THANKS
+
+ To .vantronix for supporting this work - it is time to get a
+ nice(r) SNMP implementation for OpenBSD.
+
+ Thanks to claudio@ and mbalmer@ for starting the implementation
+ of ASN.1/BER encoding.
+
+reyk
diff --git a/usr.sbin/snmpd/ber.3 b/usr.sbin/snmpd/ber.3
new file mode 100644
index 00000000000..3f1e66f15c3
--- /dev/null
+++ b/usr.sbin/snmpd/ber.3
@@ -0,0 +1,233 @@
+.\" $OpenBSD: ber.3,v 1.1 2007/12/05 09:22:44 reyk Exp $
+.\"
+.\" Copyright (c) 2007 Reyk Floeter <reyk@vantronix.net>
+.\"
+.\" 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.
+.\"
+.Dd $Mdocdate: December 5 2007 $
+.Dt BER 3
+.Os
+.Sh NAME
+.Nm ber_get_element ,
+.Nm ber_set_header ,
+.Nm ber_link_elements ,
+.Nm ber_unlink_elements ,
+.Nm ber_replace_elements ,
+.Nm ber_add_sequence ,
+.Nm ber_add_set ,
+.Nm ber_add_integer ,
+.Nm ber_get_integer ,
+.Nm ber_add_boolean ,
+.Nm ber_get_boolean ,
+.Nm ber_add_string ,
+.Nm ber_get_string ,
+.Nm ber_add_nstring ,
+.Nm ber_add_bitstring ,
+.Nm ber_get_nstring ,
+.Nm ber_get_bitstring ,
+.Nm ber_add_null ,
+.Nm ber_get_null ,
+.Nm ber_add_eoc ,
+.Nm ber_get_eoc ,
+.Nm ber_add_oid ,
+.Nm ber_add_oidstring ,
+.Nm ber_get_oid ,
+.Nm ber_oid2ber ,
+.Nm ber_string2oid ,
+.Nm ber_printf_elements ,
+.Nm ber_scanf_elements ,
+.Nm ber_get_writebuf ,
+.Nm ber_write_elements ,
+.Nm ber_set_readbuf ,
+.Nm ber_read_elements ,
+.Nm ber_free_elements ,
+.Nm ber_calc_len ,
+.Nm ber_set_application ,
+.Nd Parse ASN.1 with Basic Encoding Rules.
+.Sh SYNOPSIS
+.Fd #include <ber.h>
+.Ft "struct ber_element *"
+.Fn "ber_get_element" "unsigned long encoding"
+.Ft "void"
+.Fn "ber_set_header" "struct ber_element *elm" "int class" "unsigned long type"
+.Ft "void"
+.Fn "ber_link_elements" "struct ber_element *prev" "struct ber_element *elm"
+.Ft "struct ber_element *"
+.Fn "ber_unlink_elements" "struct ber_element *prev"
+.Ft "void"
+.Fn "ber_replace_elements" "struct ber_element *prev" "struct ber_element *elm"
+.Ft "struct ber_element *"
+.Fn "ber_add_sequence" "struct ber_element *prev"
+.Ft "struct ber_element *"
+.Fn "ber_add_set" "struct ber_element *prev"
+.Ft "struct ber_element *"
+.Fn "ber_add_integer" "struct ber_element *prev" "long long val"
+.Ft "int"
+.Fn "ber_get_integer" "struct ber_element *root" "long long *val"
+.Ft "struct ber_element *"
+.Fn "ber_add_boolean" "struct ber_element *prev" "int bool"
+.Ft "int"
+.Fn "ber_get_boolean" "struct ber_element *root" "int *bool"
+.Ft "struct ber_element *"
+.Fn "ber_add_string" "struct ber_element *prev" "char *string"
+.Ft "struct ber_element *"
+.Fn "ber_add_nstring" "struct ber_element *prev" "char *string" "size_t size"
+.Ft "int"
+.Fn "ber_get_string" "struct ber_element *root" "char **charbuf"
+.Ft "int"
+.Fn "ber_get_nstring" "struct ber_element *root" "void **buf" "size_t *size"
+.Ft "struct ber_element *"
+.Fn "ber_add_bitstring" "struct ber_element *prev" "void *buf" "size_t size"
+.Ft "int"
+.Fn "ber_get_bitstring" "struct ber_element *root" "void **buf" "size_t *size"
+.Ft "struct ber_element *"
+.Fn "ber_add_null" "struct ber_element *prev"
+.Ft "int"
+.Fn "ber_get_null" "struct ber_element *root"
+.Ft "struct ber_element *"
+.Fn "ber_add_eoc" "struct ber_element *prev"
+.Ft "int"
+.Fn "ber_get_eoc" "struct ber_element *root"
+.Ft "struct ber_element *"
+.Fn "ber_add_oid" "struct ber_element *prev" "struct ber_oid *oid"
+.Ft "struct ber_element *"
+.Fn "ber_add_oidstring" "struct ber_element *prev" "const char *string"
+.Ft "int"
+.Fn "ber_get_oid" "struct ber_element *root" "struct ber_oid *oid"
+.Ft "size_t"
+.Fn "ber_oid2ber" "struct ber_oid *oid" "u_int8_t *buf" "size_t size"
+.Ft "int"
+.Fn "ber_string2oid" "const char *string" "struct ber_oid *oid"
+.Ft "struct ber_element *"
+.Fn "ber_printf_elements" "struct ber_element *prev" "char *format" "..."
+.Ft "int"
+.Fn "ber_scanf_elements" "struct ber_element *root" "char *format" "..."
+.Ft "ssize_t"
+.Fn "ber_get_writebuf" "struct ber *ber" "void **buf"
+.Ft "int"
+.Fn "ber_write_elements" "struct ber *ber" "struct ber_element *root"
+.Ft "void"
+.Fn "ber_set_readbuf" "struct ber *ber" "void *buf" "size_t len"
+.Ft "struct"
+.Fn "ber_element *ber_read_elements" "struct ber *ber" "struct ber_element *root"
+.Ft "void"
+.Fn "ber_free_elements" "struct ber_element *root"
+.Ft "size_t"
+.Fn "ber_calc_len" "struct ber_element *root"
+.Ft "void"
+.Fn "ber_set_application" "struct ber *ber" "unsigned long (*cb)(struct ber_element *)"
+.Sh DESCRIPTION
+The
+.Nm ber
+API provides a mechanism to read and write ASN.1 streams and buffers
+using the
+.Ic Basic Encoding Rules .
+.Sh BER ELEMENTS
+.Fn ber_get_element ,
+.Fn ber_set_header ,
+.Fn ber_link_elements ,
+.Fn ber_unlink_elements ,
+.Fn ber_replace_elements ,
+.Fn ber_calc_len
+.Pp
+
+.Sh BER TYPES
+.Fn ber_add_sequence ,
+.Fn ber_add_set ,
+.Fn ber_add_integer ,
+.Fn ber_get_integer ,
+.Fn ber_add_boolean ,
+.Fn ber_get_boolean ,
+.Fn ber_add_string ,
+.Fn ber_get_string ,
+.Fn ber_add_nstring ,
+.Fn ber_add_bitstring ,
+.Fn ber_get_nstring ,
+.Fn ber_get_bitstring ,
+.Fn ber_add_null ,
+.Fn ber_get_null ,
+.Fn ber_add_eoc ,
+.Fn ber_get_eoc
+
+.Sh OBJECT IDS
+Object Identifiers are commonly used in ASN.1-bases protocols.
+These functions provide an interface to parse OIDs.
+For internal representation of OIDs, the following structure
+.Ft struct ber_oid
+is beeing used:
+.Bd -literal
+#define BER_MIN_OID_LEN 2
+#define BER_MAX_OID_LEN 128
+
+struct ber_oid {
+ u_int32_t bo_id[BER_MAX_OID_LEN + 1];
+ size_t bo_n;
+};
+.Ed
+.Pp
+.Fn ber_add_oid ,
+.Fn ber_add_oidstring ,
+.Fn ber_get_oid ,
+.Fn ber_oid2ber ,
+.Fn ber_string2oid
+.Pp
+
+.Sh FORMAT STRINGS
+.Fn ber_printf_elements ,
+.Fn ber_scanf_elements
+.Pp
+
+.Sh I/O OPERATIONS
+.Fn ber_get_writebuf ,
+.Fn ber_write_elements ,
+.Fn ber_set_readbuf ,
+.Fn ber_read_elements ,
+.Fn ber_free_elements ,
+.Fn ber_set_application
+.Pp
+
+.Sh RETURN VALUES
+Upon successful completion
+.Fn ber_get_integer ,
+.Fn ber_get_boolean ,
+.Fn ber_get_string ,
+.Fn ber_get_nstring ,
+.Fn ber_get_bitstring ,
+.Fn ber_get_null ,
+.Fn ber_get_eoc ,
+.Fn ber_get_oid ,
+.Fn ber_string2oid ,
+.Fn ber_scanf_elements ,
+and
+.Fn ber_write_elements
+return 0.
+Otherwise, \-1 is returned and the global variable errno is
+set to indicate the error.
+.Sh SEE ALSO
+.Xr socket 2
+.Sh HISTORY
+The
+.Nm ber
+manpage first appeared in
+.Ox 4.3 .
+.Sh AUTHORS
+The
+.Nm ber
+library was written by
+.An Claudio Jeker Aq claudio@openbsd.org ,
+.An Marc Balmer Aq marc@openbsd.org
+and
+.An Reyk Floeter Aq reyk@openbsd.org .
+.Sh BUGS
+The code is buggy and incomplete.
+This manpage is a stub.
diff --git a/usr.sbin/snmpd/ber.c b/usr.sbin/snmpd/ber.c
new file mode 100644
index 00000000000..09429075c84
--- /dev/null
+++ b/usr.sbin/snmpd/ber.c
@@ -0,0 +1,1163 @@
+/* $OpenBSD: ber.c,v 1.1 2007/12/05 09:22:44 reyk Exp $ */
+
+/*
+ * Copyright (c) 2007 Reyk Floeter <reyk@vantronix.net>
+ * Copyright (c) 2006, 2007 Claudio Jeker <claudio@openbsd.org>
+ * Copyright (c) 2006, 2007 Marc Balmer <mbalmer@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/types.h>
+#include <sys/param.h>
+
+#include <errno.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <err.h> /* XXX for debug output */
+#include <stdio.h> /* XXX for debug output */
+#include <strings.h>
+#include <unistd.h>
+#include <stdarg.h>
+
+#include "ber.h"
+
+
+#define BER_TYPE_CONSTRUCTED 0x20 /* otherwise primitive */
+#define BER_TYPE_SINGLE_MAX 30
+#define BER_TAG_MASK 0x1f
+#define BER_TAG_MORE 0x80 /* more subsequent octets */
+#define BER_TAG_TYPE_MASK 0x7f
+#define BER_CLASS_SHIFT 6
+
+static int ber_dump_element(struct ber *ber, struct ber_element *root);
+static void ber_dump_header(struct ber *ber, struct ber_element *root);
+static void ber_putc(struct ber *ber, u_char c);
+static void ber_write(struct ber *ber, void *buf, size_t len);
+static ssize_t get_id(struct ber *b, unsigned long *tag, int *class,
+ int *cstruct);
+static ssize_t get_len(struct ber *b, ssize_t *len);
+static ssize_t ber_read_element(struct ber *ber, struct ber_element *elm);
+static ssize_t ber_readbuf(struct ber *b, void *buf, size_t nbytes);
+static ssize_t ber_getc(struct ber *b, u_char *c);
+static ssize_t ber_read(struct ber *ber, void *buf, size_t len);
+
+#ifdef DEBUG
+#define DPRINTF(x...) printf(x)
+#else
+#define DPRINTF(x...) do { } while (0)
+#endif
+
+struct ber_element *
+ber_get_element(unsigned long encoding)
+{
+ struct ber_element *elm;
+
+ if ((elm = calloc(1, sizeof(*elm))) == NULL)
+ return NULL;
+
+ elm->be_encoding = encoding;
+ ber_set_header(elm, BER_CLASS_UNIVERSAL, BER_TYPE_DEFAULT);
+
+ return elm;
+}
+
+void
+ber_set_header(struct ber_element *elm, int class, unsigned long type)
+{
+ elm->be_class = class & BER_CLASS_MASK;
+ if (type == BER_TYPE_DEFAULT)
+ type = elm->be_encoding;
+ elm->be_type = type;
+}
+
+void
+ber_link_elements(struct ber_element *prev, struct ber_element *elm)
+{
+ if (prev != NULL) {
+ if ((prev->be_encoding == BER_TYPE_SEQUENCE ||
+ prev->be_encoding == BER_TYPE_SET) &&
+ prev->be_sub == NULL)
+ prev->be_sub = elm;
+ else
+ prev->be_next = elm;
+ }
+}
+
+struct ber_element *
+ber_unlink_elements(struct ber_element *prev)
+{
+ struct ber_element *elm;
+
+ if ((prev->be_encoding == BER_TYPE_SEQUENCE ||
+ prev->be_encoding == BER_TYPE_SET) &&
+ prev->be_sub == NULL) {
+ elm = prev->be_sub;
+ prev->be_sub = NULL;
+ } else {
+ elm = prev->be_next;
+ prev->be_next = NULL;
+ }
+
+ return (elm);
+}
+
+void
+ber_replace_elements(struct ber_element *prev, struct ber_element *new)
+{
+ struct ber_element *ber, *next;
+
+ ber = ber_unlink_elements(prev);
+ next = ber_unlink_elements(ber);
+ ber_link_elements(new, next);
+ ber_link_elements(prev, new);
+
+ /* cleanup old element */
+ ber_free_elements(ber);
+}
+
+struct ber_element *
+ber_add_sequence(struct ber_element *prev)
+{
+ struct ber_element *elm;
+
+ if ((elm = ber_get_element(BER_TYPE_SEQUENCE)) == NULL)
+ return NULL;
+
+ ber_link_elements(prev, elm);
+
+ return elm;
+}
+
+struct ber_element *
+ber_add_set(struct ber_element *prev)
+{
+ struct ber_element *elm;
+
+ if ((elm = ber_get_element(BER_TYPE_SET)) == NULL)
+ return NULL;
+
+ ber_link_elements(prev, elm);
+
+ return elm;
+}
+
+struct ber_element *
+ber_add_integer(struct ber_element *prev, long long val)
+{
+ struct ber_element *elm;
+ u_int i, len = 0;
+ u_char cur, last = 0;
+
+ if ((elm = ber_get_element(BER_TYPE_INTEGER)) == NULL)
+ return NULL;
+
+ elm->be_numeric = val;
+
+ for (i = 0; i < sizeof(long long); i++) {
+ cur = val & 0xff;
+ if (cur != 0 && cur != 0xff)
+ len = i;
+ if ((cur == 0 && last & 0x80) ||
+ (cur == 0xff && (last & 0x80) == 0))
+ len = i;
+ val >>= 8;
+ last = cur;
+ }
+ elm->be_len = len + 1;
+
+ ber_link_elements(prev, elm);
+
+ return elm;
+}
+
+int
+ber_get_integer(struct ber_element *elm, long long *n)
+{
+ if (elm->be_encoding != BER_TYPE_INTEGER)
+ return -1;
+
+ *n = elm->be_numeric;
+ return 0;
+}
+
+struct ber_element *
+ber_add_boolean(struct ber_element *prev, int bool)
+{
+ struct ber_element *elm;
+
+ if ((elm = ber_get_element(BER_TYPE_BOOLEAN)) == NULL)
+ return NULL;
+
+ elm->be_numeric = bool ? 0xff : 0;
+ elm->be_len = 1;
+
+ ber_link_elements(prev, elm);
+
+ return elm;
+}
+
+int
+ber_get_boolean(struct ber_element *elm, int *b)
+{
+ if (elm->be_encoding != BER_TYPE_BOOLEAN)
+ return -1;
+
+ *b = !(elm->be_numeric == 0);
+ return 0;
+}
+
+struct ber_element *
+ber_add_string(struct ber_element *prev, char *string)
+{
+ struct ber_element *elm;
+
+ if ((elm = ber_get_element(BER_TYPE_OCTETSTRING)) == NULL)
+ return NULL;
+
+ elm->be_val = string;
+ elm->be_len = strlen(string); /* terminating '\0' not included */
+
+ ber_link_elements(prev, elm);
+
+ return elm;
+}
+
+struct ber_element *
+ber_add_nstring(struct ber_element *prev, char *string, size_t len)
+{
+ struct ber_element *elm;
+
+ if ((elm = ber_get_element(BER_TYPE_OCTETSTRING)) == NULL)
+ return NULL;
+
+ elm->be_val = string;
+ elm->be_len = len;
+
+ ber_link_elements(prev, elm);
+
+ return elm;
+}
+
+int
+ber_get_string(struct ber_element *elm, char **s)
+{
+ if (elm->be_encoding != BER_TYPE_OCTETSTRING)
+ return -1;
+
+ *s = elm->be_val;
+ return 0;
+}
+
+int
+ber_get_nstring(struct ber_element *elm, void **p, size_t *len)
+{
+ if (elm->be_encoding != BER_TYPE_OCTETSTRING)
+ return -1;
+
+ *p = elm->be_val;
+ *len = elm->be_len;
+ return 0;
+}
+
+struct ber_element *
+ber_add_bitstring(struct ber_element *prev, void *v, size_t len)
+{
+ struct ber_element *elm;
+
+ if ((elm = ber_get_element(BER_TYPE_BITSTRING)) == NULL)
+ return NULL;
+
+ elm->be_val = v;
+ elm->be_len = len;
+
+ ber_link_elements(prev, elm);
+
+ return elm;
+}
+
+int
+ber_get_bitstring(struct ber_element *elm, void **v, size_t *len)
+{
+ if (elm->be_encoding != BER_TYPE_BITSTRING)
+ return -1;
+
+ *v = elm->be_val;
+ *len = elm->be_len;
+ return 0;
+}
+
+struct ber_element *
+ber_add_null(struct ber_element *prev)
+{
+ struct ber_element *elm;
+
+ if ((elm = ber_get_element(BER_TYPE_NULL)) == NULL)
+ return NULL;
+
+ ber_link_elements(prev, elm);
+
+ return elm;
+}
+
+int
+ber_get_null(struct ber_element *elm)
+{
+ if (elm->be_encoding != BER_TYPE_NULL)
+ return -1;
+
+ return 0;
+}
+
+struct ber_element *
+ber_add_eoc(struct ber_element *prev)
+{
+ struct ber_element *elm;
+
+ if ((elm = ber_get_element(BER_TYPE_EOC)) == NULL)
+ return NULL;
+
+ ber_link_elements(prev, elm);
+
+ return elm;
+}
+
+int
+ber_get_eoc(struct ber_element *elm)
+{
+ if (elm->be_encoding != BER_TYPE_EOC)
+ return -1;
+
+ return 0;
+}
+
+size_t
+ber_oid2ber(struct ber_oid *o, u_int8_t *buf, size_t len)
+{
+ u_int32_t v;
+ u_int i, j = 0, k;
+
+ if (o->bo_n < BER_MIN_OID_LEN || o->bo_n > BER_MAX_OID_LEN ||
+ o->bo_id[0] > 2 || o->bo_id[1] > 40)
+ return (0);
+
+ v = (o->bo_id[0] * 40) + o->bo_id[1];
+ for (i = 2, j = 0; i <= o->bo_n; v = o->bo_id[i], i++) {
+ for (k = 28; k >= 7; k -= 7) {
+ if (v > (u_int)(1 << k)) {
+ if (len)
+ buf[j] = v >> k | BER_TAG_MORE;
+ j++;
+ }
+ }
+ if (len)
+ buf[j] = v & BER_TAG_TYPE_MASK;
+ j++;
+ }
+
+ return (j);
+}
+
+int
+ber_string2oid(const char *oidstr, struct ber_oid *o)
+{
+ size_t len;
+ char *sp, *p, str[BUFSIZ];
+ const char *errstr;
+
+ if (strlcpy(str, oidstr, sizeof(str)) >= sizeof(str))
+ return (-1);
+ len = strlen(str);
+ bzero(o, sizeof(*o));
+
+ /* Parse OID strings in the common forms n.n.n, n_n_n_n, or n-n-n */
+ for (p = sp = str; p != NULL; sp = p) {
+ if ((p = strpbrk(p, "._-")) != NULL)
+ *p++ = '\0';
+ o->bo_id[o->bo_n++] = strtonum(sp, 0, UINT_MAX, &errstr);
+ if (errstr || o->bo_n > BER_MAX_OID_LEN)
+ return (-1);
+ }
+
+ return (0);
+}
+
+struct ber_element *
+ber_add_oid(struct ber_element *prev, struct ber_oid *o)
+{
+ struct ber_element *elm;
+ u_int8_t *buf;
+ size_t len;
+
+ if ((elm = ber_get_element(BER_TYPE_OBJECT)) == NULL)
+ return (NULL);
+
+ if ((len = ber_oid2ber(o, NULL, 0)) == 0)
+ goto fail;
+
+ if ((buf = calloc(1, len)) == NULL)
+ goto fail;
+
+ elm->be_val = buf;
+ elm->be_len = len;
+ elm->be_free = 1;
+
+ if (ber_oid2ber(o, buf, len) != len)
+ goto fail;
+
+ ber_link_elements(prev, elm);
+
+ return (elm);
+
+ fail:
+ ber_free_elements(elm);
+ return (NULL);
+}
+
+struct ber_element *
+ber_add_oidstring(struct ber_element *prev, const char *oidstr)
+{
+ struct ber_oid o;
+
+ if (ber_string2oid(oidstr, &o) == -1)
+ return (NULL);
+
+ return (ber_add_oid(prev, &o));
+}
+
+int
+ber_get_oid(struct ber_element *elm, struct ber_oid *o)
+{
+ u_int8_t *buf;
+ size_t len, i = 0, j = 0;
+
+ if (elm->be_encoding != BER_TYPE_OBJECT)
+ return (-1);
+
+ buf = elm->be_val;
+ len = elm->be_len;
+
+ if (!buf[i])
+ return (-1);
+
+ bzero(o, sizeof(*o));
+ o->bo_id[j++] = buf[i] / 40;
+ o->bo_id[j++] = buf[i++] % 40;
+ for (; i < len && j < BER_MAX_OID_LEN; i++) {
+ o->bo_id[j] = (o->bo_id[j] << 7) + (buf[i] & ~0x80);
+ if (buf[i] & 0x80)
+ continue;
+ j++;
+ }
+ o->bo_n = j;
+
+ return (0);
+}
+
+struct ber_element *
+ber_printf_elements(struct ber_element *ber, char *fmt, ...)
+{
+ va_list ap;
+ int d, class;
+ size_t len;
+ unsigned long type;
+ long long i;
+ char *s;
+ void *p;
+ struct ber_oid *o;
+ struct ber_element *sub = ber, *e;
+
+ va_start(ap, fmt);
+ while (*fmt) {
+ switch (*fmt++) {
+ case 'B':
+ p = va_arg(ap, void *);
+ len = va_arg(ap, size_t);
+ ber = ber_add_bitstring(ber, p, len);
+ break;
+ case 'b':
+ d = va_arg(ap, int);
+ ber = ber_add_boolean(ber, d);
+ break;
+ case 'e':
+ e = va_arg(ap, struct ber_element *);
+ ber_link_elements(ber, e);
+ break;
+ case 'i':
+ i = va_arg(ap, long long);
+ ber = ber_add_integer(ber, i);
+ break;
+ case 'O':
+ o = va_arg(ap, struct ber_oid *);
+ ber = ber_add_oid(ber, o);
+ break;
+ case 'o':
+ s = va_arg(ap, char *);
+ ber = ber_add_oidstring(ber, s);
+ break;
+ case 's':
+ s = va_arg(ap, char *);
+ ber = ber_add_string(ber, s);
+ break;
+ case 't':
+ class = va_arg(ap, int);
+ type = va_arg(ap, unsigned long);
+ ber_set_header(ber, class, type);
+ break;
+ case 'x':
+ s = va_arg(ap, char *);
+ len = va_arg(ap, size_t);
+ ber = ber_add_nstring(ber, s, len);
+ break;
+ case '0':
+ ber = ber_add_null(ber);
+ break;
+ case '{':
+ ber = sub = ber_add_sequence(ber);
+ break;
+ case '(':
+ ber = sub = ber_add_set(ber);
+ break;
+ case '}':
+ case ')':
+ ber = sub;
+ break;
+ case '.':
+ ber = ber_add_eoc(ber);
+ break;
+ default:
+ break;
+ }
+ }
+ va_end(ap);
+
+ return (ber);
+}
+
+int
+ber_scanf_elements(struct ber_element *ber, char *fmt, ...)
+{
+#define _MAX_SEQ 128
+ va_list ap;
+ int *d, level = -1;
+ unsigned long *t;
+ long long *i;
+ void **ptr;
+ size_t *len, ret = 0, n = strlen(fmt);
+ char **s;
+ struct ber_oid *o;
+ struct ber_element *parent[_MAX_SEQ], **e;
+
+ bzero(parent, sizeof(struct ber_element *) * _MAX_SEQ);
+
+ va_start(ap, fmt);
+ while (*fmt) {
+ switch (*fmt++) {
+ case 'B':
+ ptr = va_arg(ap, void **);
+ len = va_arg(ap, size_t *);
+ if (ber_get_bitstring(ber, ptr, len) == -1)
+ goto fail;
+ ret++;
+ break;
+ case 'b':
+ d = va_arg(ap, int *);
+ if (ber_get_boolean(ber, d) == -1)
+ goto fail;
+ ret++;
+ break;
+ case 'e':
+ e = va_arg(ap, struct ber_element **);
+ *e = ber;
+ ret++;
+ continue;
+ case 'i':
+ i = va_arg(ap, long long *);
+ if (ber_get_integer(ber, i) == -1)
+ goto fail;
+ ret++;
+ break;
+ case 'o':
+ o = va_arg(ap, struct ber_oid *);
+ if (ber_get_oid(ber, o) == -1)
+ goto fail;
+ ret++;
+ break;
+ case 's':
+ s = va_arg(ap, char **);
+ if (ber_get_string(ber, s) == -1)
+ goto fail;
+ ret++;
+ break;
+ case 't':
+ d = va_arg(ap, int *);
+ t = va_arg(ap, unsigned long *);
+ *d = ber->be_class;
+ *t = ber->be_type;
+ ret++;
+ continue;
+ case 'x':
+ ptr = va_arg(ap, void **);
+ len = va_arg(ap, size_t *);
+ if (ber_get_nstring(ber, ptr, len) == -1)
+ goto fail;
+ ret++;
+ break;
+ case '0':
+ if (ber->be_encoding != BER_TYPE_NULL)
+ goto fail;
+ ret++;
+ break;
+ case '.':
+ if (ber->be_encoding != BER_TYPE_EOC)
+ goto fail;
+ ret++;
+ break;
+ case '{':
+ case '(':
+ if (ber->be_sub == NULL || level >= _MAX_SEQ)
+ goto fail;
+ parent[++level] = ber;
+ ber = ber->be_sub;
+ ret++;
+ continue;
+ case '}':
+ case ')':
+ if (parent[level] == NULL)
+ goto fail;
+ ber = parent[level--];
+ ret++;
+ continue;
+ default:
+ goto fail;
+ }
+
+ if (ber->be_next == NULL)
+ continue;
+ ber = ber->be_next;
+ }
+ va_end(ap);
+ return (ret == n ? 0 : -1);
+
+ fail:
+ va_end(ap);
+ return (-1);
+
+}
+
+/*
+ * write ber elements to the socket
+ *
+ * params:
+ * ber holds the socket
+ * root fully populated element tree
+ *
+ * returns:
+ * 0 on success
+ *
+ * returns:
+ * 0 on success
+ * -1 on failure and sets errno
+ */
+int
+ber_write_elements(struct ber *ber, struct ber_element *root)
+{
+ size_t len;
+
+ /* calculate length because only the definite form is required */
+ len = ber_calc_len(root);
+ DPRINTF("write ber element of %zd bytes length\n", len);
+
+ if (ber->br_wbuf != NULL && ber->br_wbuf + len > ber->br_wend) {
+ free(ber->br_wbuf);
+ ber->br_wbuf = NULL;
+ }
+ if (ber->br_wbuf == NULL) {
+ if ((ber->br_wbuf = malloc(len)) == NULL)
+ return -1;
+ ber->br_wend = ber->br_wbuf + len;
+ }
+
+ /* reset write pointer */
+ ber->br_wptr = ber->br_wbuf;
+
+ if (ber_dump_element(ber, root) == -1)
+ return -1;
+
+ /* XXX this should be moved to a different function */
+ if (ber->fd != -1)
+ return write(ber->fd, ber->br_wbuf, len);
+
+ return (len);
+}
+
+/*
+ * read ber elements from the socket
+ *
+ * params:
+ * ber holds the socket and lot more
+ * root if NULL, build up an element tree from what we receive on
+ * the wire. If not null, use the specified encoding for the
+ * elements received.
+ *
+ * returns:
+ * !=NULL, elements read and store in the ber_element tree
+ * NULL, type mismatch or read error
+ */
+struct ber_element *
+ber_read_elements(struct ber *ber, struct ber_element *root)
+{
+ if (root == NULL) {
+ if ((root = ber_get_element(0)) == NULL)
+ return NULL;
+ }
+
+ DPRINTF("read ber elements, root %p\n", root);
+
+ if (ber_read_element(ber, root) == -1)
+ return NULL;
+
+ return root;
+}
+
+void
+ber_free_elements(struct ber_element *root)
+{
+ if (root->be_sub && (root->be_encoding == BER_TYPE_SEQUENCE ||
+ root->be_encoding == BER_TYPE_SET))
+ ber_free_elements(root->be_sub);
+ if (root->be_next)
+ ber_free_elements(root->be_next);
+ if (root->be_free && (root->be_encoding == BER_TYPE_OCTETSTRING ||
+ root->be_encoding == BER_TYPE_BITSTRING ||
+ root->be_encoding == BER_TYPE_OBJECT))
+ free(root->be_val);
+ free(root);
+}
+
+/*
+ * internal functions
+ */
+
+size_t
+ber_calc_len(struct ber_element *root)
+{
+ unsigned long t;
+ size_t s;
+ size_t size = 2; /* minimum 1 byte head and 1 byte size */
+
+ /* calculate the real lenght of a sequence or set */
+ if (root->be_sub && (root->be_encoding == BER_TYPE_SEQUENCE ||
+ root->be_encoding == BER_TYPE_SET))
+ root->be_len = ber_calc_len(root->be_sub);
+
+ /* fix header length for extended types */
+ if (root->be_type > BER_TYPE_SINGLE_MAX)
+ for (t = root->be_type; t > 0; t >>= 7)
+ size++;
+ if (root->be_len >= BER_TAG_MORE)
+ for (s = root->be_len; s > 0; s >>= 8)
+ size++;
+
+ /* calculate the length of the following elements */
+ if (root->be_next)
+ size += ber_calc_len(root->be_next);
+
+ /* This is an empty element, do not use a minimal size */
+ if (root->be_type == BER_TYPE_EOC && root->be_len == 0)
+ return (0);
+
+ return (root->be_len + size);
+}
+
+static int
+ber_dump_element(struct ber *ber, struct ber_element *root)
+{
+ unsigned long long l;
+ int i, pos;
+ uint8_t u;
+
+ ber_dump_header(ber, root);
+
+ switch (root->be_encoding) {
+ case BER_TYPE_BOOLEAN:
+ case BER_TYPE_INTEGER:
+ case BER_TYPE_ENUMERATED:
+ pos = (root->be_numeric > 0);
+ l = (unsigned long long)root->be_numeric;
+ for (i = root->be_len; i > 0; i--) {
+ u = (l >> ((i - 1) * 8)) & 0xff;
+ ber_putc(ber, u);
+ }
+ break;
+ case BER_TYPE_BITSTRING:
+ return -1;
+ case BER_TYPE_OCTETSTRING:
+ case BER_TYPE_OBJECT:
+ ber_write(ber, root->be_val, root->be_len);
+ break;
+ case BER_TYPE_NULL: /* no payload */
+ case BER_TYPE_EOC:
+ break;
+ case BER_TYPE_SEQUENCE:
+ case BER_TYPE_SET:
+ if (root->be_sub && ber_dump_element(ber, root->be_sub) == -1)
+ return -1;
+ break;
+ }
+
+ if (root->be_next == NULL)
+ return 0;
+ return ber_dump_element(ber, root->be_next);
+}
+
+static void
+ber_dump_header(struct ber *ber, struct ber_element *root)
+{
+ u_char id = 0, t, buf[8];
+ unsigned long type;
+ size_t size;
+
+ /* class universal, type encoding depending on type value */
+ /* length encoding */
+ if (root->be_type <= BER_TYPE_SINGLE_MAX) {
+ id = root->be_type | (root->be_class << BER_CLASS_SHIFT);
+ if (root->be_encoding == BER_TYPE_SEQUENCE ||
+ root->be_encoding == BER_TYPE_SET)
+ id |= BER_TYPE_CONSTRUCTED;
+
+ ber_putc(ber, id);
+ } else {
+ id = BER_TAG_MASK | (root->be_class << BER_CLASS_SHIFT);
+ if (root->be_encoding == BER_TYPE_SEQUENCE ||
+ root->be_encoding == BER_TYPE_SET)
+ id |= BER_TYPE_CONSTRUCTED;
+
+ ber_putc(ber, id);
+
+ for (t = 0, type = root->be_type; type > 0; type >>= 7)
+ buf[t++] = type & ~BER_TAG_MORE;
+
+ while (t-- > 0) {
+ if (t > 0)
+ buf[t] |= BER_TAG_MORE;
+ ber_putc(ber, buf[t]);
+ }
+ }
+
+ if (root->be_len < BER_TAG_MORE) {
+ /* short form */
+ ber_putc(ber, root->be_len);
+ } else {
+ for (t = 0, size = root->be_len; size > 0; size >>= 8)
+ buf[t++] = size & 0xff;
+
+ ber_putc(ber, t | BER_TAG_MORE);
+
+ while (t > 0)
+ ber_putc(ber, buf[--t]);
+ }
+}
+
+static void
+ber_putc(struct ber *ber, u_char c)
+{
+ if (ber->br_wptr + 1 <= ber->br_wend)
+ *ber->br_wptr = c;
+ ber->br_wptr++;
+}
+
+static void
+ber_write(struct ber *ber, void *buf, size_t len)
+{
+ if (ber->br_wptr + len <= ber->br_wend)
+ bcopy(buf, ber->br_wptr, len);
+ ber->br_wptr += len;
+}
+
+/*
+ * extract a BER encoded tag. There are two types, a short and long form.
+ */
+static ssize_t
+get_id(struct ber *b, unsigned long *tag, int *class, int *cstruct)
+{
+ u_char u;
+ size_t i = 0;
+ unsigned long t = 0;
+
+ if (ber_getc(b, &u) == -1)
+ return -1;
+
+ *class = (u >> BER_CLASS_SHIFT) & BER_CLASS_MASK;
+ *cstruct = (u & BER_TYPE_CONSTRUCTED) == BER_TYPE_CONSTRUCTED;
+
+ if ((u & BER_TAG_MASK) != BER_TAG_MASK) {
+ *tag = u & BER_TAG_MASK;
+ return 1;
+ }
+
+ do {
+ if (ber_getc(b, &u) == -1)
+ return -1;
+ t = (t << 7) | (u & ~BER_TAG_MORE);
+ i++;
+ } while (u & BER_TAG_MORE);
+
+ if (i > sizeof(unsigned long)) {
+ errno = ERANGE;
+ return -1;
+ }
+
+ *tag = t;
+ return i + 1;
+}
+
+/*
+ * extract lenght of a ber object -- if length is unknown a length of -1 is
+ * returned.
+ */
+static ssize_t
+get_len(struct ber *b, ssize_t *len)
+{
+ u_char u, n;
+ ssize_t s, r;
+
+ if (ber_getc(b, &u) == -1)
+ return -1;
+ if ((u & BER_TAG_MORE) == 0) {
+ /* short form */
+ *len = u;
+ return 1;
+ }
+
+ n = u & ~BER_TAG_MORE;
+ if (sizeof(ssize_t) < n) {
+ errno = ERANGE;
+ return -1;
+ }
+ r = n + 1;
+
+ for (s = 0; n > 0; n--) {
+ if (ber_getc(b, &u) == -1)
+ return -1;
+ s = (s << 8) | u;
+ }
+
+ if (s < 0) {
+ /* overflow */
+ errno = ERANGE;
+ return -1;
+ }
+
+ if (s == 0)
+ s = -1;
+
+ *len = s;
+ return r;
+}
+
+static ssize_t
+ber_read_element(struct ber *ber, struct ber_element *elm)
+{
+ long long val = 0;
+ struct ber_element *next;
+ unsigned long type;
+ int i, class, cstruct;
+ ssize_t len, r, totlen = 0;
+ u_char c;
+
+ if ((r = get_id(ber, &type, &class, &cstruct)) == -1)
+ return -1;
+ DPRINTF("ber read got class %d type %lu, %s\n",
+ class, type, cstruct ? "constructive" : "primitive");
+ totlen += r;
+ if ((r = get_len(ber, &len)) == -1)
+ return -1;
+ DPRINTF("ber read element size %zd\n", len);
+ totlen += r + len;
+
+ elm->be_type = type;
+ elm->be_len = len;
+ elm->be_class = class;
+
+ if (elm->be_encoding == 0) {
+ /* try to figure out the encoding via class, type and cstruct */
+ if (cstruct)
+ elm->be_encoding = BER_TYPE_SEQUENCE;
+ else if (class == BER_CLASS_UNIVERSAL)
+ elm->be_encoding = type;
+ else if (ber->br_application != NULL) {
+ /*
+ * Ask the application to map the encoding to a
+ * universal type. For example, a SMI IpAddress
+ * type is defined as 4 byte OCTET STRING.
+ */
+ elm->be_encoding = (*ber->br_application)(elm);
+ } else
+ /* last resort option */
+ elm->be_encoding = BER_TYPE_NULL;
+ }
+
+ switch (elm->be_encoding) {
+ case BER_TYPE_EOC: /* End-Of-Content */
+ break;
+ case BER_TYPE_BOOLEAN:
+ case BER_TYPE_INTEGER:
+ case BER_TYPE_ENUMERATED:
+ if (len > (u_int)sizeof(long long))
+ return -1;
+ for (i = 0; i < len; i++) {
+ if (ber_getc(ber, &c) != 1)
+ return -1;
+ val <<= 8;
+ val |= c;
+ }
+
+ /* sign extend if MSB is set */
+ if (val >> ((i - 1) * 8) & 0x80)
+ val |= ULLONG_MAX << (i * 8);
+ elm->be_numeric = val;
+ break;
+ case BER_TYPE_BITSTRING:
+ elm->be_val = malloc(len);
+ if (elm->be_val == NULL)
+ return -1;
+ elm->be_free = 1;
+ elm->be_len = len;
+ ber_read(ber, elm->be_val, len);
+ break;
+ case BER_TYPE_OCTETSTRING:
+ case BER_TYPE_OBJECT:
+ elm->be_val = malloc(len + 1);
+ if (elm->be_val == NULL)
+ return -1;
+ elm->be_free = 1;
+ elm->be_len = len;
+ ber_read(ber, elm->be_val, len);
+ ((u_char *)elm->be_val)[len] = '\0';
+ break;
+ case BER_TYPE_NULL: /* no payload */
+ if (len != 0)
+ return -1;
+ break;
+ case BER_TYPE_SEQUENCE:
+ case BER_TYPE_SET:
+ if (elm->be_sub == NULL) {
+ if ((elm->be_sub = ber_get_element(0)) == NULL)
+ return -1;
+ }
+ next = elm->be_sub;
+ do {
+ r = ber_read_element(ber, next);
+ if (r == -1)
+ return -1;
+ if (next->be_next == NULL) {
+ if ((next->be_next = ber_get_element(0)) ==
+ NULL)
+ return -1;
+ }
+ next = next->be_next;
+ len -= r;
+ } while (len > 0);
+ break;
+ }
+ return totlen;
+}
+
+static ssize_t
+ber_readbuf(struct ber *b, void *buf, size_t nbytes)
+{
+ size_t sz;
+ size_t len;
+
+ if (b->br_rbuf == NULL)
+ return -1;
+
+ sz = b->br_rend - b->br_rptr;
+ len = MIN(nbytes, sz);
+
+ bcopy(b->br_rptr, buf, len);
+ b->br_rptr += len;
+
+ return (len);
+}
+
+void
+ber_set_readbuf(struct ber *b, void *buf, size_t len)
+{
+ b->br_rbuf = b->br_rptr = buf;
+ b->br_rend = (u_int8_t *)buf + len;
+}
+
+ssize_t
+ber_get_writebuf(struct ber *b, void **buf)
+{
+ if (b->br_wbuf == NULL)
+ return -1;
+ *buf = b->br_wbuf;
+ return (b->br_wend - b->br_wbuf);
+}
+
+void
+ber_set_application(struct ber *b, unsigned long (*cb)(struct ber_element *))
+{
+ b->br_application = cb;
+}
+
+static ssize_t
+ber_getc(struct ber *b, u_char *c)
+{
+ ssize_t r;
+ /*
+ * XXX calling read here is wrong in many ways. The most obivous one
+ * being that we will block till data arrives.
+ * But for now it is _good enough_ *gulp*
+ */
+ if (b->fd == -1)
+ r = ber_readbuf(b, c, 1);
+ else
+ r = read(b->fd, c, 1);
+ if (r == -1)
+ warn("ber_getc");
+ return r;
+}
+
+static ssize_t
+ber_read(struct ber *ber, void *buf, unsigned long len)
+{
+ u_char *b = buf;
+ ssize_t r, remain = len;
+
+ /*
+ * XXX calling read here is wrong in many ways. The most obivous one
+ * being that we will block till data arrives.
+ * But for now it is _good enough_ *gulp*
+ */
+
+ while (remain > 0) {
+ if (ber->fd == -1)
+ r = ber_readbuf(ber, b, remain);
+ else
+ r = read(ber->fd, b, remain);
+ if (r == -1) {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ return -1;
+ }
+ if (r == 0)
+ return (b - (u_char *)buf);
+ b += r;
+ remain -= r;
+ }
+ return (b - (u_char *)buf);
+}
diff --git a/usr.sbin/snmpd/ber.h b/usr.sbin/snmpd/ber.h
new file mode 100644
index 00000000000..726515720e5
--- /dev/null
+++ b/usr.sbin/snmpd/ber.h
@@ -0,0 +1,123 @@
+/* $OpenBSD: ber.h,v 1.1 2007/12/05 09:22:44 reyk Exp $ */
+
+/*
+ * Copyright (c) 2007 Reyk Floeter <reyk@vantronix.net>
+ * Copyright (c) 2006, 2007 Claudio Jeker <claudio@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.
+ */
+
+struct ber_element {
+ struct ber_element *be_next;
+ unsigned long be_type;
+ unsigned long be_encoding;
+ size_t be_len;
+ int be_free;
+ u_int8_t be_class;
+ union {
+ struct ber_element *bv_sub;
+ void *bv_val;
+ long long bv_numeric;
+ } be_union;
+#define be_sub be_union.bv_sub
+#define be_val be_union.bv_val
+#define be_numeric be_union.bv_numeric
+};
+
+struct ber {
+ int fd;
+ u_char *br_wbuf;
+ u_char *br_wptr;
+ u_char *br_wend;
+ u_char *br_rbuf;
+ u_char *br_rptr;
+ u_char *br_rend;
+
+ unsigned long (*br_application)(struct ber_element *);
+};
+
+/* well-known ber_element types */
+#define BER_TYPE_DEFAULT ((unsigned long)-1)
+#define BER_TYPE_EOC 0
+#define BER_TYPE_BOOLEAN 1
+#define BER_TYPE_INTEGER 2
+#define BER_TYPE_BITSTRING 3
+#define BER_TYPE_OCTETSTRING 4
+#define BER_TYPE_NULL 5
+#define BER_TYPE_OBJECT 6
+#define BER_TYPE_ENUMERATED 10
+#define BER_TYPE_SEQUENCE 16
+#define BER_TYPE_SET 17
+
+/* ber classes */
+#define BER_CLASS_UNIVERSAL 0x0
+#define BER_CLASS_UNIV BER_CLASS_UNIVERSAL
+#define BER_CLASS_APPLICATION 0x1
+#define BER_CLASS_APP BER_CLASS_APPLICATION
+#define BER_CLASS_CONTEXT 0x2
+#define BER_CLASS_PRIVATE 0x3
+#define BER_CLASS_MASK 0x3
+
+/* common definitions */
+#define BER_MIN_OID_LEN 2 /* OBJECT */
+#define BER_MAX_OID_LEN 128 /* OBJECT */
+
+struct ber_oid {
+ u_int32_t bo_id[BER_MAX_OID_LEN + 1];
+ size_t bo_n;
+};
+
+__BEGIN_DECLS
+struct ber_element *ber_get_element(unsigned long);
+void ber_set_header(struct ber_element *, int,
+ unsigned long);
+void ber_link_elements(struct ber_element *,
+ struct ber_element *);
+struct ber_element *ber_unlink_elements(struct ber_element *);
+void ber_replace_elements(struct ber_element *,
+ struct ber_element *);
+struct ber_element *ber_add_sequence(struct ber_element *);
+struct ber_element *ber_add_set(struct ber_element *);
+struct ber_element *ber_add_integer(struct ber_element *, long long);
+int ber_get_integer(struct ber_element *, long long *);
+struct ber_element *ber_add_boolean(struct ber_element *, int);
+int ber_get_boolean(struct ber_element *, int *);
+struct ber_element *ber_add_string(struct ber_element *, char *);
+struct ber_element *ber_add_nstring(struct ber_element *, char *, size_t);
+int ber_get_string(struct ber_element *, char **);
+int ber_get_nstring(struct ber_element *, void **,
+ size_t *);
+struct ber_element *ber_add_bitstring(struct ber_element *, void *,
+ size_t);
+int ber_get_bitstring(struct ber_element *, void **,
+ size_t *);
+struct ber_element *ber_add_null(struct ber_element *);
+int ber_get_null(struct ber_element *);
+struct ber_element *ber_add_eoc(struct ber_element *);
+int ber_get_eoc(struct ber_element *);
+struct ber_element *ber_add_oid(struct ber_element *, struct ber_oid *);
+struct ber_element *ber_add_oidstring(struct ber_element *, const char *);
+int ber_get_oid(struct ber_element *, struct ber_oid *);
+size_t ber_oid2ber(struct ber_oid *, u_int8_t *, size_t);
+int ber_string2oid(const char *, struct ber_oid *);
+struct ber_element *ber_printf_elements(struct ber_element *, char *, ...);
+int ber_scanf_elements(struct ber_element *, char *, ...);
+ssize_t ber_get_writebuf(struct ber *, void **);
+int ber_write_elements(struct ber *, struct ber_element *);
+void ber_set_readbuf(struct ber *, void *, size_t);
+struct ber_element *ber_read_elements(struct ber *, struct ber_element *);
+void ber_free_elements(struct ber_element *);
+size_t ber_calc_len(struct ber_element *);
+void ber_set_application(struct ber *,
+ unsigned long (*)(struct ber_element *));
+__END_DECLS
diff --git a/usr.sbin/snmpd/buffer.c b/usr.sbin/snmpd/buffer.c
new file mode 100644
index 00000000000..c0fc184df46
--- /dev/null
+++ b/usr.sbin/snmpd/buffer.c
@@ -0,0 +1,244 @@
+/* $OpenBSD: buffer.c,v 1.1 2007/12/05 09:22:44 reyk Exp $ */
+
+/*
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@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/types.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <sys/param.h>
+#include <sys/tree.h>
+
+#include <net/if.h>
+
+#include <errno.h>
+#include <event.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "snmpd.h"
+
+int buf_realloc(struct buf *, size_t);
+void buf_enqueue(struct msgbuf *, struct buf *);
+void buf_dequeue(struct msgbuf *, struct buf *);
+
+struct buf *
+buf_open(size_t len)
+{
+ struct buf *buf;
+
+ if ((buf = calloc(1, sizeof(struct buf))) == NULL)
+ return (NULL);
+ if ((buf->buf = malloc(len)) == NULL) {
+ free(buf);
+ return (NULL);
+ }
+ buf->size = buf->max = len;
+ buf->fd = -1;
+
+ return (buf);
+}
+
+struct buf *
+buf_dynamic(size_t len, size_t max)
+{
+ struct buf *buf;
+
+ if (max < len)
+ return (NULL);
+
+ if ((buf = buf_open(len)) == NULL)
+ return (NULL);
+
+ if (max > 0)
+ buf->max = max;
+
+ return (buf);
+}
+
+int
+buf_realloc(struct buf *buf, size_t len)
+{
+ u_char *b;
+
+ /* on static buffers max is eq size and so the following fails */
+ if (buf->wpos + len > buf->max) {
+ errno = ENOMEM;
+ return (-1);
+ }
+
+ b = realloc(buf->buf, buf->wpos + len);
+ if (b == NULL)
+ return (-1);
+ buf->buf = b;
+ buf->size = buf->wpos + len;
+
+ return (0);
+}
+
+int
+buf_add(struct buf *buf, void *data, size_t len)
+{
+ if (buf->wpos + len > buf->size)
+ if (buf_realloc(buf, len) == -1)
+ return (-1);
+
+ memcpy(buf->buf + buf->wpos, data, len);
+ buf->wpos += len;
+ return (0);
+}
+
+void *
+buf_reserve(struct buf *buf, size_t len)
+{
+ void *b;
+
+ if (buf->wpos + len > buf->size)
+ if (buf_realloc(buf, len) == -1)
+ return (NULL);
+
+ b = buf->buf + buf->wpos;
+ buf->wpos += len;
+ return (b);
+}
+
+int
+buf_close(struct msgbuf *msgbuf, struct buf *buf)
+{
+ buf_enqueue(msgbuf, buf);
+ return (1);
+}
+
+void
+buf_free(struct buf *buf)
+{
+ free(buf->buf);
+ free(buf);
+}
+
+void
+msgbuf_init(struct msgbuf *msgbuf)
+{
+ msgbuf->queued = 0;
+ msgbuf->fd = -1;
+ TAILQ_INIT(&msgbuf->bufs);
+}
+
+void
+msgbuf_clear(struct msgbuf *msgbuf)
+{
+ struct buf *buf;
+
+ while ((buf = TAILQ_FIRST(&msgbuf->bufs)) != NULL)
+ buf_dequeue(msgbuf, buf);
+}
+
+int
+msgbuf_write(struct msgbuf *msgbuf)
+{
+ struct iovec iov[IOV_MAX];
+ struct buf *buf, *next;
+ int i = 0;
+ ssize_t n;
+ struct msghdr msg;
+ struct cmsghdr *cmsg;
+ char cmsgbuf[CMSG_SPACE(sizeof(int))];
+
+ bzero(&iov, sizeof(iov));
+ bzero(&msg, sizeof(msg));
+ TAILQ_FOREACH(buf, &msgbuf->bufs, entry) {
+ if (i >= IOV_MAX)
+ break;
+ iov[i].iov_base = buf->buf + buf->rpos;
+ iov[i].iov_len = buf->size - buf->rpos;
+ i++;
+ if (buf->fd != -1)
+ break;
+ }
+
+ msg.msg_iov = iov;
+ msg.msg_iovlen = i;
+
+ if (buf != NULL && buf->fd != -1) {
+ msg.msg_control = (caddr_t)cmsgbuf;
+ msg.msg_controllen = CMSG_LEN(sizeof(int));
+ cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ *(int *)CMSG_DATA(cmsg) = buf->fd;
+ }
+
+ if ((n = sendmsg(msgbuf->fd, &msg, 0)) == -1) {
+ if (errno == EAGAIN || errno == ENOBUFS ||
+ errno == EINTR) /* try later */
+ return (0);
+ else
+ return (-1);
+ }
+
+ if (n == 0) { /* connection closed */
+ errno = 0;
+ return (-2);
+ }
+
+ if (buf != NULL && buf->fd != -1) {
+ msg.msg_control = (caddr_t)cmsgbuf;
+ msg.msg_controllen = CMSG_LEN(sizeof(int));
+ cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ *(int *)CMSG_DATA(cmsg) = buf->fd;
+ }
+
+ for (buf = TAILQ_FIRST(&msgbuf->bufs); buf != NULL && n > 0;
+ buf = next) {
+ next = TAILQ_NEXT(buf, entry);
+ if (buf->rpos + n >= buf->size) {
+ n -= buf->size - buf->rpos;
+ buf_dequeue(msgbuf, buf);
+ } else {
+ buf->rpos += n;
+ n = 0;
+ }
+ }
+
+ return (0);
+}
+
+void
+buf_enqueue(struct msgbuf *msgbuf, struct buf *buf)
+{
+ TAILQ_INSERT_TAIL(&msgbuf->bufs, buf, entry);
+ msgbuf->queued++;
+}
+
+void
+buf_dequeue(struct msgbuf *msgbuf, struct buf *buf)
+{
+ TAILQ_REMOVE(&msgbuf->bufs, buf, entry);
+
+ if (buf->fd != -1)
+ close(buf->fd);
+
+ msgbuf->queued--;
+ buf_free(buf);
+}
diff --git a/usr.sbin/snmpd/control.c b/usr.sbin/snmpd/control.c
new file mode 100644
index 00000000000..24023e798c5
--- /dev/null
+++ b/usr.sbin/snmpd/control.c
@@ -0,0 +1,270 @@
+/* $OpenBSD: control.c,v 1.1 2007/12/05 09:22:44 reyk Exp $ */
+
+/*
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@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/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/tree.h>
+
+#include <net/if.h>
+
+#include <errno.h>
+#include <event.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+
+#include "snmpd.h"
+
+#define CONTROL_BACKLOG 5
+
+struct ctl_connlist ctl_conns;
+
+struct ctl_conn *control_connbyfd(int);
+void control_close(int);
+
+struct imsgbuf *ibuf_parent = NULL;
+
+int
+control_init(void)
+{
+ struct sockaddr_un sun;
+ int fd;
+ mode_t old_umask;
+
+ if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
+ log_warn("control_init: socket");
+ return (-1);
+ }
+
+ sun.sun_family = AF_UNIX;
+ if (strlcpy(sun.sun_path, SNMPD_SOCKET,
+ sizeof(sun.sun_path)) >= sizeof(sun.sun_path)) {
+ log_warn("control_init: %s name too long", SNMPD_SOCKET);
+ close(fd);
+ return (-1);
+ }
+
+ if (unlink(SNMPD_SOCKET) == -1)
+ if (errno != ENOENT) {
+ log_warn("control_init: unlink %s", SNMPD_SOCKET);
+ close(fd);
+ return (-1);
+ }
+
+ old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH);
+ if (bind(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
+ log_warn("control_init: bind: %s", SNMPD_SOCKET);
+ close(fd);
+ (void)umask(old_umask);
+ return (-1);
+ }
+ (void)umask(old_umask);
+
+ if (chmod(SNMPD_SOCKET, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP) == -1) {
+ log_warn("control_init: chmod");
+ close(fd);
+ (void)unlink(SNMPD_SOCKET);
+ return (-1);
+ }
+
+ session_socket_blockmode(fd, BM_NONBLOCK);
+ control_state.fd = fd;
+
+ return (0);
+}
+
+int
+control_listen(struct snmpd *env, struct imsgbuf *parent)
+{
+ ibuf_parent = parent;
+
+ if (listen(control_state.fd, CONTROL_BACKLOG) == -1) {
+ log_warn("control_listen: listen");
+ return (-1);
+ }
+
+ event_set(&control_state.ev, control_state.fd, EV_READ | EV_PERSIST,
+ control_accept, env);
+ event_add(&control_state.ev, NULL);
+
+ return (0);
+}
+
+void
+control_cleanup(void)
+{
+ (void)unlink(SNMPD_SOCKET);
+}
+
+/* ARGSUSED */
+void
+control_accept(int listenfd, short event, void *arg)
+{
+ int connfd;
+ socklen_t len;
+ struct sockaddr_un sun;
+ struct ctl_conn *c;
+ struct snmpd *env = arg;
+
+ len = sizeof(sun);
+ if ((connfd = accept(listenfd,
+ (struct sockaddr *)&sun, &len)) == -1) {
+ if (errno != EWOULDBLOCK && errno != EINTR)
+ log_warn("control_accept");
+ return;
+ }
+
+ session_socket_blockmode(connfd, BM_NONBLOCK);
+
+ if ((c = malloc(sizeof(struct ctl_conn))) == NULL) {
+ log_warn("control_accept");
+ return;
+ }
+
+ imsg_init(&c->ibuf, connfd, control_dispatch_imsg);
+ c->ibuf.events = EV_READ;
+ event_set(&c->ibuf.ev, c->ibuf.fd, c->ibuf.events,
+ c->ibuf.handler, env);
+ event_add(&c->ibuf.ev, NULL);
+
+ TAILQ_INSERT_TAIL(&ctl_conns, c, entry);
+}
+
+struct ctl_conn *
+control_connbyfd(int fd)
+{
+ struct ctl_conn *c;
+
+ for (c = TAILQ_FIRST(&ctl_conns); c != NULL && c->ibuf.fd != fd;
+ c = TAILQ_NEXT(c, entry))
+ ; /* nothing */
+
+ return (c);
+}
+
+void
+control_close(int fd)
+{
+ struct ctl_conn *c;
+
+ if ((c = control_connbyfd(fd)) == NULL)
+ log_warn("control_close: fd %d: not found", fd);
+
+ msgbuf_clear(&c->ibuf.w);
+ TAILQ_REMOVE(&ctl_conns, c, entry);
+
+ event_del(&c->ibuf.ev);
+ close(c->ibuf.fd);
+ free(c);
+}
+
+/* ARGSUSED */
+void
+control_dispatch_imsg(int fd, short event, void *arg)
+{
+ struct ctl_conn *c;
+ struct imsg imsg;
+ int n;
+
+ if ((c = control_connbyfd(fd)) == NULL) {
+ log_warn("control_dispatch_imsg: fd %d: not found", fd);
+ return;
+ }
+
+ switch (event) {
+ case EV_READ:
+ if ((n = imsg_read(&c->ibuf)) == -1 || n == 0) {
+ control_close(fd);
+ return;
+ }
+ break;
+ case EV_WRITE:
+ if (msgbuf_write(&c->ibuf.w) < 0) {
+ control_close(fd);
+ return;
+ }
+ imsg_event_add(&c->ibuf);
+ return;
+ default:
+ fatalx("unknown event");
+ }
+
+ for (;;) {
+ if ((n = imsg_get(&c->ibuf, &imsg)) == -1) {
+ control_close(fd);
+ return;
+ }
+
+ if (n == 0)
+ break;
+
+ switch (imsg.hdr.type) {
+ case IMSG_CTL_NOTIFY:
+ if (c->flags & CTL_CONN_NOTIFY) {
+ log_debug("control_dispatch_imsg: "
+ "client requested notify more than once");
+ imsg_compose(&c->ibuf, IMSG_CTL_FAIL, 0, 0, -1,
+ NULL, 0);
+ break;
+ }
+ c->flags |= CTL_CONN_NOTIFY;
+ break;
+ default:
+ log_debug("control_dispatch_imsg: "
+ "error handling imsg %d", imsg.hdr.type);
+ break;
+ }
+ imsg_free(&imsg);
+ }
+
+ imsg_event_add(&c->ibuf);
+}
+
+void
+control_imsg_forward(struct imsg *imsg)
+{
+ struct ctl_conn *c;
+
+ TAILQ_FOREACH(c, &ctl_conns, entry)
+ if (c->flags & CTL_CONN_NOTIFY)
+ imsg_compose(&c->ibuf, imsg->hdr.type, 0, imsg->hdr.pid,
+ -1, imsg->data, imsg->hdr.len - IMSG_HEADER_SIZE);
+}
+
+void
+session_socket_blockmode(int fd, enum blockmodes bm)
+{
+ int flags;
+
+ if ((flags = fcntl(fd, F_GETFL, 0)) == -1)
+ fatal("fcntl F_GETFL");
+
+ if (bm == BM_NONBLOCK)
+ flags |= O_NONBLOCK;
+ else
+ flags &= ~O_NONBLOCK;
+
+ if ((flags = fcntl(fd, F_SETFL, flags)) == -1)
+ fatal("fcntl F_SETFL");
+}
diff --git a/usr.sbin/snmpd/imsg.c b/usr.sbin/snmpd/imsg.c
new file mode 100644
index 00000000000..83bb7262568
--- /dev/null
+++ b/usr.sbin/snmpd/imsg.c
@@ -0,0 +1,233 @@
+/* $OpenBSD: imsg.c,v 1.1 2007/12/05 09:22:44 reyk Exp $ */
+
+/*
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@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/types.h>
+#include <sys/socket.h>
+#include <sys/queue.h>
+#include <sys/uio.h>
+#include <sys/param.h>
+#include <sys/tree.h>
+
+#include <net/if.h>
+
+#include <errno.h>
+#include <event.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "snmpd.h"
+
+void
+imsg_init(struct imsgbuf *ibuf, int fd, void (*handler)(int, short, void *))
+{
+ msgbuf_init(&ibuf->w);
+ bzero(&ibuf->r, sizeof(ibuf->r));
+ ibuf->fd = fd;
+ ibuf->w.fd = fd;
+ ibuf->pid = getpid();
+ ibuf->handler = handler;
+ TAILQ_INIT(&ibuf->fds);
+}
+
+ssize_t
+imsg_read(struct imsgbuf *ibuf)
+{
+ struct msghdr msg;
+ struct cmsghdr *cmsg;
+ char cmsgbuf[CMSG_SPACE(sizeof(int) * 16)];
+ struct iovec iov;
+ ssize_t n;
+ int fd;
+ struct imsg_fd *ifd;
+
+ bzero(&msg, sizeof(msg));
+
+ iov.iov_base = ibuf->r.buf + ibuf->r.wpos;
+ iov.iov_len = sizeof(ibuf->r.buf) - ibuf->r.wpos;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = cmsgbuf;
+ msg.msg_controllen = sizeof(cmsgbuf);
+
+ if ((n = recvmsg(ibuf->fd, &msg, 0)) == -1) {
+ if (errno != EINTR && errno != EAGAIN) {
+ log_warn("imsg_read: pipe read error");
+ return (-1);
+ }
+ return (-2);
+ }
+
+ ibuf->r.wpos += n;
+
+ for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
+ cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+ if (cmsg->cmsg_level == SOL_SOCKET &&
+ cmsg->cmsg_type == SCM_RIGHTS) {
+ fd = (*(int *)CMSG_DATA(cmsg));
+ if ((ifd = calloc(1, sizeof(struct imsg_fd))) == NULL)
+ fatal("imsg_read calloc");
+ ifd->fd = fd;
+ TAILQ_INSERT_TAIL(&ibuf->fds, ifd, entry);
+ } else
+ log_warn("imsg_read: got unexpected ctl data level %d "
+ "type %d", cmsg->cmsg_level, cmsg->cmsg_type);
+ }
+
+ return (n);
+}
+
+ssize_t
+imsg_get(struct imsgbuf *ibuf, struct imsg *imsg)
+{
+ size_t av, left, datalen;
+
+ av = ibuf->r.wpos;
+
+ if (IMSG_HEADER_SIZE > av)
+ return (0);
+
+ memcpy(&imsg->hdr, ibuf->r.buf, sizeof(imsg->hdr));
+ if (imsg->hdr.len < IMSG_HEADER_SIZE ||
+ imsg->hdr.len > MAX_IMSGSIZE) {
+ log_warnx("imsg_get: imsg hdr len %u out of bounds, type=%u",
+ imsg->hdr.len, imsg->hdr.type);
+ return (-1);
+ }
+ if (imsg->hdr.len > av)
+ return (0);
+ datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
+ ibuf->r.rptr = ibuf->r.buf + IMSG_HEADER_SIZE;
+ if ((imsg->data = malloc(datalen)) == NULL) {
+ log_warn("imsg_get");
+ return (-1);
+ }
+ memcpy(imsg->data, ibuf->r.rptr, datalen);
+
+ if (imsg->hdr.len < av) {
+ left = av - imsg->hdr.len;
+ memmove(&ibuf->r.buf, ibuf->r.buf + imsg->hdr.len, left);
+ ibuf->r.wpos = left;
+ } else
+ ibuf->r.wpos = 0;
+
+ return (datalen + IMSG_HEADER_SIZE);
+}
+
+int
+imsg_compose(struct imsgbuf *ibuf, enum imsg_type type, u_int32_t peerid,
+ pid_t pid, int fd, void *data, u_int16_t datalen)
+{
+ struct buf *wbuf;
+ int n;
+
+ if ((wbuf = imsg_create(ibuf, type, peerid, pid, datalen)) == NULL)
+ return (-1);
+
+ if (imsg_add(wbuf, data, datalen) == -1)
+ return (-1);
+
+ wbuf->fd = fd;
+
+ if ((n = imsg_close(ibuf, wbuf)) < 0)
+ return (-1);
+
+ return (n);
+}
+
+/* ARGSUSED */
+struct buf *
+imsg_create(struct imsgbuf *ibuf, enum imsg_type type, u_int32_t peerid,
+ pid_t pid, u_int16_t datalen)
+{
+ struct buf *wbuf;
+ struct imsg_hdr hdr;
+
+ datalen += IMSG_HEADER_SIZE;
+ if (datalen > MAX_IMSGSIZE) {
+ log_warnx("imsg_create: len %u > MAX_IMSGSIZE; "
+ "type %u peerid %lu", datalen + IMSG_HEADER_SIZE,
+ type, peerid);
+ return (NULL);
+ }
+
+ hdr.type = type;
+ hdr.peerid = peerid;
+ if ((hdr.pid = pid) == 0)
+ hdr.pid = ibuf->pid;
+ if ((wbuf = buf_dynamic(datalen, MAX_IMSGSIZE)) == NULL) {
+ log_warn("imsg_create: buf_open");
+ return (NULL);
+ }
+ if (imsg_add(wbuf, &hdr, sizeof(hdr)) == -1)
+ return (NULL);
+
+ return (wbuf);
+}
+
+int
+imsg_add(struct buf *msg, void *data, u_int16_t datalen)
+{
+ if (datalen)
+ if (buf_add(msg, data, datalen) == -1) {
+ log_warnx("imsg_add: buf_add error");
+ buf_free(msg);
+ return (-1);
+ }
+ return (datalen);
+}
+
+int
+imsg_close(struct imsgbuf *ibuf, struct buf *msg)
+{
+ int n;
+ struct imsg_hdr *hdr;
+
+ hdr = (struct imsg_hdr *)msg->buf;
+ hdr->len = (u_int16_t)msg->wpos;
+ if ((n = buf_close(&ibuf->w, msg)) < 0) {
+ log_warnx("imsg_close: buf_close error");
+ buf_free(msg);
+ return (-1);
+ }
+ imsg_event_add(ibuf);
+
+ return (n);
+}
+
+void
+imsg_free(struct imsg *imsg)
+{
+ free(imsg->data);
+}
+
+int
+imsg_get_fd(struct imsgbuf *ibuf)
+{
+ int fd;
+ struct imsg_fd *ifd;
+
+ if ((ifd = TAILQ_FIRST(&ibuf->fds)) == NULL)
+ return (-1);
+
+ fd = ifd->fd;
+ TAILQ_REMOVE(&ibuf->fds, ifd, entry);
+ free(ifd);
+
+ return (fd);
+}
diff --git a/usr.sbin/snmpd/kroute.c b/usr.sbin/snmpd/kroute.c
new file mode 100644
index 00000000000..40d9709398a
--- /dev/null
+++ b/usr.sbin/snmpd/kroute.c
@@ -0,0 +1,1093 @@
+/* $OpenBSD: kroute.c,v 1.1 2007/12/05 09:22:44 reyk Exp $ */
+
+/*
+ * Copyright (c) 2007 Reyk Floeter <reyk@vantronix.net>
+ * Copyright (c) 2004 Esben Norby <norby@openbsd.org>
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@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/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <sys/tree.h>
+#include <sys/uio.h>
+#include <sys/ioctl.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_types.h>
+#include <net/route.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#include <arpa/inet.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <event.h>
+
+#include "snmpd.h"
+
+struct {
+ u_int32_t ks_rtseq;
+ pid_t ks_pid;
+ int ks_fd;
+ int ks_ifd;
+ struct event ks_ev;
+ u_short ks_nkif;
+ u_long ks_iflastchange;
+} kr_state;
+
+struct kroute_node {
+ RB_ENTRY(kroute_node) entry;
+ struct kroute r;
+ struct kroute_node *next;
+};
+
+struct kif_node {
+ RB_ENTRY(kif_node) entry;
+ TAILQ_HEAD(, kif_addr) addrs;
+ struct kif k;
+};
+
+int kroute_compare(struct kroute_node *, struct kroute_node *);
+struct kroute_node *kroute_find(in_addr_t, u_int8_t);
+struct kroute_node *kroute_match(in_addr_t);
+struct kroute_node *kroute_matchgw(struct kroute_node *, struct in_addr);
+int kroute_insert(struct kroute_node *);
+int kroute_remove(struct kroute_node *);
+void kroute_clear(void);
+
+int kif_init(void);
+int kif_compare(struct kif_node *, struct kif_node *);
+struct kif_node *kif_find(u_short);
+struct kif_node *kif_insert(u_short);
+int kif_remove(struct kif_node *);
+void kif_clear(void);
+struct kif *kif_update(u_short, int, struct if_data *,
+ struct sockaddr_dl *);
+int kif_validate(u_short);
+
+u_int16_t rtlabel_name2id(const char *);
+const char *rtlabel_id2name(u_int16_t);
+void rtlabel_unref(u_int16_t);
+
+int protect_lo(void);
+u_int8_t prefixlen_classful(in_addr_t);
+u_int8_t mask2prefixlen(in_addr_t);
+in_addr_t prefixlen2mask(u_int8_t);
+void get_rtaddrs(int, struct sockaddr *, struct sockaddr **);
+void if_change(u_short, int, struct if_data *);
+void if_newaddr(u_short, struct sockaddr_in *, struct sockaddr_in *,
+ struct sockaddr_in *);
+void if_announce(void *);
+
+int send_rtmsg(int, int, struct kroute *);
+void dispatch_rtmsg(int, short, void *);
+int fetchifs(u_short);
+int fetchtable(void);
+
+RB_HEAD(kroute_tree, kroute_node) krt;
+RB_PROTOTYPE(kroute_tree, kroute_node, entry, kroute_compare)
+RB_GENERATE(kroute_tree, kroute_node, entry, kroute_compare)
+
+RB_HEAD(kif_tree, kif_node) kit;
+RB_PROTOTYPE(kif_tree, kif_node, entry, kif_compare)
+RB_GENERATE(kif_tree, kif_node, entry, kif_compare)
+
+int
+kif_init(void)
+{
+ RB_INIT(&kit);
+
+ if (fetchifs(0) == -1)
+ return (-1);
+
+ return (0);
+}
+
+int
+kr_init(void)
+{
+ int opt = 0, rcvbuf, default_rcvbuf;
+ socklen_t optlen;
+
+ if (kif_init() == -1)
+ return (-1);
+
+ if ((kr_state.ks_ifd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
+ log_warn("kr_init: ioctl socket");
+ return (-1);
+ }
+
+ if ((kr_state.ks_fd = socket(AF_ROUTE, SOCK_RAW, 0)) == -1) {
+ log_warn("kr_init: route socket");
+ return (-1);
+ }
+
+ /* not interested in my own messages */
+ if (setsockopt(kr_state.ks_fd, SOL_SOCKET, SO_USELOOPBACK,
+ &opt, sizeof(opt)) == -1)
+ log_warn("kr_init: setsockopt"); /* not fatal */
+
+ /* grow receive buffer, don't wanna miss messages */
+ optlen = sizeof(default_rcvbuf);
+ if (getsockopt(kr_state.ks_fd, SOL_SOCKET, SO_RCVBUF,
+ &default_rcvbuf, &optlen) == -1)
+ log_warn("kr_init getsockopt SOL_SOCKET SO_RCVBUF");
+ else
+ for (rcvbuf = MAX_RTSOCK_BUF;
+ rcvbuf > default_rcvbuf &&
+ setsockopt(kr_state.ks_fd, SOL_SOCKET, SO_RCVBUF,
+ &rcvbuf, sizeof(rcvbuf)) == -1 && errno == ENOBUFS;
+ rcvbuf /= 2)
+ ; /* nothing */
+
+ kr_state.ks_pid = getpid();
+ kr_state.ks_rtseq = 1;
+
+ RB_INIT(&krt);
+
+ if (fetchtable() == -1)
+ return (-1);
+
+ if (protect_lo() == -1)
+ return (-1);
+
+ event_set(&kr_state.ks_ev, kr_state.ks_fd, EV_READ | EV_PERSIST,
+ dispatch_rtmsg, NULL);
+ event_add(&kr_state.ks_ev, NULL);
+
+ return (0);
+}
+
+void
+kr_shutdown(void)
+{
+ kroute_clear();
+ kif_clear();
+}
+
+u_int
+kr_ifnumber(void)
+{
+ return (kr_state.ks_nkif);
+}
+
+u_long
+kr_iflastchange(void)
+{
+ return (kr_state.ks_iflastchange);
+}
+
+int
+kr_updateif(u_int if_index)
+{
+ struct kif_node *kn;
+
+ if ((kn = kif_find(if_index)) != NULL)
+ kif_remove(kn);
+
+ /* Do not update the interface address list */
+ return (fetchifs(if_index));
+}
+
+/* rb-tree compare */
+int
+kroute_compare(struct kroute_node *a, struct kroute_node *b)
+{
+ if (ntohl(a->r.prefix.s_addr) < ntohl(b->r.prefix.s_addr))
+ return (-1);
+ if (ntohl(a->r.prefix.s_addr) > ntohl(b->r.prefix.s_addr))
+ return (1);
+ if (a->r.prefixlen < b->r.prefixlen)
+ return (-1);
+ if (a->r.prefixlen > b->r.prefixlen)
+ return (1);
+ return (0);
+}
+
+int
+kif_compare(struct kif_node *a, struct kif_node *b)
+{
+ return (a->k.if_index - b->k.if_index);
+}
+
+/* tree management */
+struct kroute_node *
+kroute_find(in_addr_t prefix, u_int8_t prefixlen)
+{
+ struct kroute_node s;
+
+ s.r.prefix.s_addr = prefix;
+ s.r.prefixlen = prefixlen;
+
+ return (RB_FIND(kroute_tree, &krt, &s));
+}
+
+struct kroute_node *
+kroute_matchgw(struct kroute_node *kr, struct in_addr nh)
+{
+ in_addr_t nexthop;
+
+ nexthop = nh.s_addr;
+
+ while (kr) {
+ if (kr->r.nexthop.s_addr == nexthop)
+ return (kr);
+ kr = kr->next;
+ }
+
+ return (NULL);
+}
+
+
+int
+kroute_insert(struct kroute_node *kr)
+{
+ struct kroute_node *krm;
+
+ if ((krm = RB_INSERT(kroute_tree, &krt, kr)) != NULL) {
+ /*
+ * Multipath route, add at end of list and clone the
+ * ospfd inserted flag.
+ */
+ kr->r.flags |= krm->r.flags & F_OSPFD_INSERTED;
+ while (krm->next != NULL)
+ krm = krm->next;
+ krm->next = kr;
+ kr->next = NULL; /* to be sure */
+ } else
+ krm = kr;
+
+ if (!(kr->r.flags & F_KERNEL)) {
+ /* don't validate or redistribute ospf route */
+ kr->r.flags &= ~F_DOWN;
+ return (0);
+ }
+
+ if (kif_validate(kr->r.if_index))
+ kr->r.flags &= ~F_DOWN;
+ else
+ kr->r.flags |= F_DOWN;
+
+ return (0);
+}
+
+int
+kroute_remove(struct kroute_node *kr)
+{
+ struct kroute_node *krm;
+
+ if ((krm = RB_FIND(kroute_tree, &krt, kr)) == NULL) {
+ log_warnx("kroute_remove failed to find %s/%u",
+ inet_ntoa(kr->r.prefix), kr->r.prefixlen);
+ return (-1);
+ }
+
+ if (krm == kr) {
+ /* head element */
+ if (RB_REMOVE(kroute_tree, &krt, kr) == NULL) {
+ log_warnx("kroute_remove failed for %s/%u",
+ inet_ntoa(kr->r.prefix), kr->r.prefixlen);
+ return (-1);
+ }
+ if (kr->next != NULL) {
+ if (RB_INSERT(kroute_tree, &krt, kr->next) != NULL) {
+ log_warnx("kroute_remove failed to add %s/%u",
+ inet_ntoa(kr->r.prefix), kr->r.prefixlen);
+ return (-1);
+ }
+ }
+ } else {
+ /* somewhere in the list */
+ while (krm->next != kr && krm->next != NULL)
+ krm = krm->next;
+ if (krm->next == NULL) {
+ log_warnx("kroute_remove multipath list corrupted "
+ "for %s/%u", inet_ntoa(kr->r.prefix),
+ kr->r.prefixlen);
+ return (-1);
+ }
+ krm->next = kr->next;
+ }
+
+ rtlabel_unref(kr->r.rtlabel);
+
+ free(kr);
+ return (0);
+}
+
+void
+kroute_clear(void)
+{
+ struct kroute_node *kr;
+
+ while ((kr = RB_MIN(kroute_tree, &krt)) != NULL)
+ kroute_remove(kr);
+}
+
+struct kif_node *
+kif_find(u_short if_index)
+{
+ struct kif_node s;
+
+ bzero(&s, sizeof(s));
+ s.k.if_index = if_index;
+
+ return (RB_FIND(kif_tree, &kit, &s));
+}
+
+struct kif *
+kr_getif(u_short if_index)
+{
+ struct kif_node *kn;
+
+ if (if_index == 0)
+ kn = RB_ROOT(&kit);
+ else
+ kn = kif_find(if_index);
+ if (kn == NULL)
+ return (NULL);
+
+ return (&kn->k);
+}
+
+struct kif *
+kr_getnextif(u_short if_index)
+{
+ struct kif_node *kn;
+
+ if (if_index == 0) {
+ kn = RB_ROOT(&kit);
+ return (&kn->k);
+ }
+
+ if ((kn = kif_find(if_index)) == NULL)
+ return (NULL);
+ kn = RB_NEXT(kif_tree, &kit, kn);
+ if (kn == NULL)
+ return (NULL);
+
+ return (&kn->k);
+}
+
+struct kif_node *
+kif_insert(u_short if_index)
+{
+ struct kif_node *kif;
+
+ if ((kif = calloc(1, sizeof(struct kif_node))) == NULL)
+ return (NULL);
+
+ kif->k.if_index = if_index;
+ TAILQ_INIT(&kif->addrs);
+
+ if (RB_INSERT(kif_tree, &kit, kif) != NULL)
+ fatalx("kif_insert: RB_INSERT");
+
+ kr_state.ks_nkif++;
+ kr_state.ks_iflastchange = mps_getticks();
+
+ return (kif);
+}
+
+int
+kif_remove(struct kif_node *kif)
+{
+ struct kif_addr *ka;
+
+ if (RB_REMOVE(kif_tree, &kit, kif) == NULL) {
+ log_warnx("RB_REMOVE(kif_tree, &kit, kif)");
+ return (-1);
+ }
+
+ while ((ka = TAILQ_FIRST(&kif->addrs)) != NULL) {
+ TAILQ_REMOVE(&kif->addrs, ka, entry);
+ free(ka);
+ }
+ free(kif);
+
+ kr_state.ks_nkif--;
+ kr_state.ks_iflastchange = mps_getticks();
+
+ return (0);
+}
+
+void
+kif_clear(void)
+{
+ struct kif_node *kif;
+
+ while ((kif = RB_MIN(kif_tree, &kit)) != NULL)
+ kif_remove(kif);
+ kr_state.ks_nkif = 0;
+ kr_state.ks_iflastchange = mps_getticks();
+}
+
+struct kif *
+kif_update(u_short if_index, int flags, struct if_data *ifd,
+ struct sockaddr_dl *sdl)
+{
+ struct kif_node *kif;
+ struct ether_addr *ea;
+ struct ifreq ifr;
+
+ if ((kif = kif_find(if_index)) == NULL)
+ if ((kif = kif_insert(if_index)) == NULL)
+ return (NULL);
+
+ kif->k.if_flags = flags;
+ bcopy(ifd, &kif->k.if_data, sizeof(struct if_data));
+ kif->k.if_ticks = mps_getticks();
+
+ if (sdl && sdl->sdl_family == AF_LINK) {
+ if (sdl->sdl_nlen >= sizeof(kif->k.if_name))
+ memcpy(kif->k.if_name, sdl->sdl_data,
+ sizeof(kif->k.if_name) - 1);
+ else if (sdl->sdl_nlen > 0)
+ memcpy(kif->k.if_name, sdl->sdl_data,
+ sdl->sdl_nlen);
+ /* string already terminated via calloc() */
+
+ if ((ea = (struct ether_addr *)LLADDR(sdl)) != NULL)
+ bcopy(&ea->ether_addr_octet, kif->k.if_lladdr,
+ ETHER_ADDR_LEN);
+ }
+
+ bzero(&ifr, sizeof(ifr));
+ strlcpy(ifr.ifr_name, kif->k.if_name, sizeof(ifr.ifr_name));
+ ifr.ifr_data = (caddr_t)&kif->k.if_descr;
+ if (ioctl(kr_state.ks_ifd, SIOCGIFDESCR, &ifr) == -1)
+ bzero(&kif->k.if_descr, sizeof(kif->k.if_descr));
+
+ return (&kif->k);
+}
+
+int
+kif_validate(u_short if_index)
+{
+ struct kif_node *kif;
+
+ if ((kif = kif_find(if_index)) == NULL) {
+ log_warnx("interface with index %u not found", if_index);
+ return (1);
+ }
+
+ return (kif->k.if_nhreachable);
+}
+
+struct kroute_node *
+kroute_match(in_addr_t key)
+{
+ int i;
+ struct kroute_node *kr;
+
+ /* we will never match the default route */
+ for (i = 32; i > 0; i--)
+ if ((kr = kroute_find(key & prefixlen2mask(i), i)) != NULL)
+ return (kr);
+
+ /* if we don't have a match yet, try to find a default route */
+ if ((kr = kroute_find(0, 0)) != NULL)
+ return (kr);
+
+ return (NULL);
+}
+
+/* misc */
+int
+protect_lo(void)
+{
+ struct kroute_node *kr;
+
+ /* special protection for 127/8 */
+ if ((kr = calloc(1, sizeof(struct kroute_node))) == NULL) {
+ log_warn("protect_lo");
+ return (-1);
+ }
+ kr->r.prefix.s_addr = htonl(INADDR_LOOPBACK);
+ kr->r.prefixlen = 8;
+ kr->r.flags = F_KERNEL|F_CONNECTED;
+
+ if (RB_INSERT(kroute_tree, &krt, kr) != NULL)
+ free(kr); /* kernel route already there, no problem */
+
+ return (0);
+}
+
+u_int8_t
+prefixlen_classful(in_addr_t ina)
+{
+ /* it hurt to write this. */
+
+ if (ina >= 0xf0000000U) /* class E */
+ return (32);
+ else if (ina >= 0xe0000000U) /* class D */
+ return (4);
+ else if (ina >= 0xc0000000U) /* class C */
+ return (24);
+ else if (ina >= 0x80000000U) /* class B */
+ return (16);
+ else /* class A */
+ return (8);
+}
+
+u_int8_t
+mask2prefixlen(in_addr_t ina)
+{
+ if (ina == 0)
+ return (0);
+ else
+ return (33 - ffs(ntohl(ina)));
+}
+
+in_addr_t
+prefixlen2mask(u_int8_t prefixlen)
+{
+ if (prefixlen == 0)
+ return (0);
+
+ return (htonl(0xffffffff << (32 - prefixlen)));
+}
+
+#define ROUNDUP(a, size) \
+ (((a) & ((size) - 1)) ? (1 + ((a) | ((size) - 1))) : (a))
+
+void
+get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info)
+{
+ int i;
+
+ for (i = 0; i < RTAX_MAX; i++) {
+ if (addrs & (1 << i)) {
+ rti_info[i] = sa;
+ sa = (struct sockaddr *)((char *)(sa) +
+ ROUNDUP(sa->sa_len, sizeof(long)));
+ } else
+ rti_info[i] = NULL;
+
+ }
+}
+
+void
+if_change(u_short if_index, int flags, struct if_data *ifd)
+{
+ struct kroute_node *kr, *tkr;
+ struct kif *kif;
+ u_int8_t reachable;
+
+ if ((kif = kif_update(if_index, flags, ifd, NULL)) == NULL) {
+ log_warn("if_change: kif_update(%u)", if_index);
+ return;
+ }
+
+ reachable = (kif->if_flags & IFF_UP) &&
+ (LINK_STATE_IS_UP(kif->if_link_state) ||
+ (kif->if_link_state == LINK_STATE_UNKNOWN &&
+ kif->if_type != IFT_CARP));
+
+ if (reachable == kif->if_nhreachable)
+ return; /* nothing changed wrt nexthop validity */
+
+ kif->if_nhreachable = reachable;
+
+#ifdef notyet
+ /* notify ospfe about interface link state */
+ main_imsg_compose_ospfe(IMSG_IFINFO, 0, kif, sizeof(struct kif));
+#endif
+
+ /* update redistribute list */
+ RB_FOREACH(kr, kroute_tree, &krt) {
+ for (tkr = kr; tkr != NULL; tkr = tkr->next) {
+ if (tkr->r.if_index == if_index) {
+ if (reachable)
+ tkr->r.flags &= ~F_DOWN;
+ else
+ tkr->r.flags |= F_DOWN;
+ }
+ }
+ }
+}
+
+void
+if_newaddr(u_short if_index, struct sockaddr_in *ifa, struct sockaddr_in *mask,
+ struct sockaddr_in *brd)
+{
+ struct kif_node *kif;
+ struct kif_addr *ka;
+
+ if (ifa == NULL || ifa->sin_family != AF_INET)
+ return;
+ if ((kif = kif_find(if_index)) == NULL) {
+ log_warnx("if_newaddr: corresponding if %i not found", if_index);
+ return;
+ }
+ if ((ka = calloc(1, sizeof(struct kif_addr))) == NULL)
+ fatal("if_newaddr");
+ ka->addr = ifa->sin_addr;
+ if (mask)
+ ka->mask = mask->sin_addr;
+ else
+ ka->mask.s_addr = INADDR_NONE;
+ if (brd)
+ ka->dstbrd = brd->sin_addr;
+ else
+ ka->dstbrd.s_addr = INADDR_NONE;
+
+ TAILQ_INSERT_TAIL(&kif->addrs, ka, entry);
+}
+
+void
+if_announce(void *msg)
+{
+ struct if_announcemsghdr *ifan;
+ struct kif_node *kif;
+
+ ifan = msg;
+
+ switch (ifan->ifan_what) {
+ case IFAN_ARRIVAL:
+ kif = kif_insert(ifan->ifan_index);
+ strlcpy(kif->k.if_name, ifan->ifan_name,
+ sizeof(kif->k.if_name));
+ break;
+ case IFAN_DEPARTURE:
+ kif = kif_find(ifan->ifan_index);
+ kif_remove(kif);
+ break;
+ }
+}
+
+int
+fetchtable(void)
+{
+ size_t len;
+ int mib[7];
+ char *buf, *next, *lim;
+ struct rt_msghdr *rtm;
+ struct sockaddr *sa, *rti_info[RTAX_MAX];
+ struct sockaddr_in *sa_in;
+ struct sockaddr_rtlabel *label;
+ struct kroute_node *kr;
+
+ mib[0] = CTL_NET;
+ mib[1] = AF_ROUTE;
+ mib[2] = 0;
+ mib[3] = AF_INET;
+ mib[4] = NET_RT_DUMP;
+ mib[5] = 0;
+ mib[6] = 0; /* rtableid */
+
+ if (sysctl(mib, 7, NULL, &len, NULL, 0) == -1) {
+ log_warn("sysctl");
+ return (-1);
+ }
+ if ((buf = malloc(len)) == NULL) {
+ log_warn("fetchtable");
+ return (-1);
+ }
+ if (sysctl(mib, 7, buf, &len, NULL, 0) == -1) {
+ log_warn("sysctl");
+ free(buf);
+ return (-1);
+ }
+
+ lim = buf + len;
+ for (next = buf; next < lim; next += rtm->rtm_msglen) {
+ rtm = (struct rt_msghdr *)next;
+ sa = (struct sockaddr *)(rtm + 1);
+ get_rtaddrs(rtm->rtm_addrs, sa, rti_info);
+
+ if ((sa = rti_info[RTAX_DST]) == NULL)
+ continue;
+
+ if (rtm->rtm_flags & RTF_LLINFO) /* arp cache */
+ continue;
+
+ if ((kr = calloc(1, sizeof(struct kroute_node))) == NULL) {
+ log_warn("fetchtable");
+ free(buf);
+ return (-1);
+ }
+
+ kr->r.flags = F_KERNEL;
+
+ switch (sa->sa_family) {
+ case AF_INET:
+ kr->r.prefix.s_addr =
+ ((struct sockaddr_in *)sa)->sin_addr.s_addr;
+ sa_in = (struct sockaddr_in *)rti_info[RTAX_NETMASK];
+ if (rtm->rtm_flags & RTF_STATIC)
+ kr->r.flags |= F_STATIC;
+ if (rtm->rtm_flags & RTF_DYNAMIC)
+ kr->r.flags |= F_DYNAMIC;
+ if (rtm->rtm_flags & RTF_PROTO1)
+ kr->r.flags |= F_BGPD_INSERTED;
+ if (sa_in != NULL) {
+ if (sa_in->sin_len == 0)
+ break;
+ kr->r.prefixlen =
+ mask2prefixlen(sa_in->sin_addr.s_addr);
+ } else if (rtm->rtm_flags & RTF_HOST)
+ kr->r.prefixlen = 32;
+ else
+ kr->r.prefixlen =
+ prefixlen_classful(kr->r.prefix.s_addr);
+ break;
+ default:
+ free(kr);
+ continue;
+ }
+
+ kr->r.if_index = rtm->rtm_index;
+ if ((sa = rti_info[RTAX_GATEWAY]) != NULL)
+ switch (sa->sa_family) {
+ case AF_INET:
+ kr->r.nexthop.s_addr =
+ ((struct sockaddr_in *)sa)->sin_addr.s_addr;
+ break;
+ case AF_LINK:
+ kr->r.flags |= F_CONNECTED;
+ break;
+ }
+
+ if ((label = (struct sockaddr_rtlabel *)
+ rti_info[RTAX_LABEL]) != NULL) {
+ kr->r.rtlabel =
+ rtlabel_name2id(label->sr_label);
+ }
+ kroute_insert(kr);
+ }
+
+ free(buf);
+ return (0);
+}
+
+int
+fetchifs(u_short if_index)
+{
+ size_t len;
+ int mib[6];
+ char *buf, *next, *lim;
+ struct rt_msghdr *rtm;
+ struct if_msghdr ifm;
+ struct ifa_msghdr *ifam;
+ struct kif *kif = NULL;
+ struct sockaddr *sa, *rti_info[RTAX_MAX];
+
+ mib[0] = CTL_NET;
+ mib[1] = AF_ROUTE;
+ mib[2] = 0;
+ mib[3] = 0; /* wildcard address family */
+ mib[4] = NET_RT_IFLIST;
+ mib[5] = if_index;
+
+ if (sysctl(mib, 6, NULL, &len, NULL, 0) == -1) {
+ log_warn("sysctl");
+ return (-1);
+ }
+ if ((buf = malloc(len)) == NULL) {
+ log_warn("fetchif");
+ return (-1);
+ }
+ if (sysctl(mib, 6, buf, &len, NULL, 0) == -1) {
+ log_warn("sysctl");
+ free(buf);
+ return (-1);
+ }
+
+ lim = buf + len;
+ for (next = buf; next < lim; next += rtm->rtm_msglen) {
+ rtm = (struct rt_msghdr *)next;
+ if (rtm->rtm_version != RTM_VERSION)
+ continue;
+ switch (rtm->rtm_type) {
+ case RTM_IFINFO:
+ bcopy(rtm, &ifm, sizeof ifm);
+ sa = (struct sockaddr *)(next + sizeof(ifm));
+ get_rtaddrs(ifm.ifm_addrs, sa, rti_info);
+
+ if ((kif = kif_update(ifm.ifm_index,
+ ifm.ifm_flags, &ifm.ifm_data,
+ (struct sockaddr_dl *)rti_info[RTAX_IFP])) == NULL)
+ fatal("fetchifs");
+
+ kif->if_nhreachable = (kif->if_flags & IFF_UP) &&
+ (LINK_STATE_IS_UP(ifm.ifm_data.ifi_link_state) ||
+ (ifm.ifm_data.ifi_link_state ==
+ LINK_STATE_UNKNOWN &&
+ ifm.ifm_data.ifi_type != IFT_CARP));
+ break;
+ case RTM_NEWADDR:
+ ifam = (struct ifa_msghdr *)rtm;
+ if ((ifam->ifam_addrs & (RTA_NETMASK | RTA_IFA |
+ RTA_BRD)) == 0)
+ break;
+ sa = (struct sockaddr *)(ifam + 1);
+ get_rtaddrs(ifam->ifam_addrs, sa, rti_info);
+
+ if_newaddr(ifam->ifam_index,
+ (struct sockaddr_in *)rti_info[RTAX_IFA],
+ (struct sockaddr_in *)rti_info[RTAX_NETMASK],
+ (struct sockaddr_in *)rti_info[RTAX_BRD]);
+ break;
+ }
+ }
+ free(buf);
+ return (0);
+}
+
+/* ARGSUSED */
+void
+dispatch_rtmsg(int fd, short event, void *arg)
+{
+ char buf[RT_BUF_SIZE];
+ ssize_t n;
+ char *next, *lim;
+ struct rt_msghdr *rtm;
+ struct if_msghdr ifm;
+ struct ifa_msghdr *ifam;
+ struct sockaddr *sa, *rti_info[RTAX_MAX];
+ struct sockaddr_in *sa_in;
+ struct sockaddr_rtlabel *label;
+ struct kroute_node *kr, *okr;
+ struct in_addr prefix, nexthop;
+ u_int8_t prefixlen;
+ int flags, mpath;
+ u_short if_index = 0;
+
+ if ((n = read(fd, &buf, sizeof(buf))) == -1) {
+ log_warn("dispatch_rtmsg: read error");
+ return;
+ }
+
+ if (n == 0) {
+ log_warnx("routing socket closed");
+ return;
+ }
+
+ lim = buf + n;
+ for (next = buf; next < lim; next += rtm->rtm_msglen) {
+ rtm = (struct rt_msghdr *)next;
+
+ prefix.s_addr = 0;
+ prefixlen = 0;
+ flags = F_KERNEL;
+ nexthop.s_addr = 0;
+ mpath = 0;
+
+ if (rtm->rtm_type == RTM_ADD || rtm->rtm_type == RTM_CHANGE ||
+ rtm->rtm_type == RTM_DELETE) {
+ sa = (struct sockaddr *)(rtm + 1);
+ get_rtaddrs(rtm->rtm_addrs, sa, rti_info);
+
+ if (rtm->rtm_tableid != 0)
+ continue;
+
+ if (rtm->rtm_pid == kr_state.ks_pid) /* caused by us */
+ continue;
+
+ if (rtm->rtm_errno) /* failed attempts... */
+ continue;
+
+ if (rtm->rtm_flags & RTF_LLINFO) /* arp cache */
+ continue;
+
+#ifdef RTF_MPATH
+ if (rtm->rtm_flags & RTF_MPATH)
+ mpath = 1;
+#endif
+ switch (sa->sa_family) {
+ case AF_INET:
+ prefix.s_addr =
+ ((struct sockaddr_in *)sa)->sin_addr.s_addr;
+ sa_in = (struct sockaddr_in *)
+ rti_info[RTAX_NETMASK];
+ if (sa_in != NULL) {
+ if (sa_in->sin_len != 0)
+ prefixlen = mask2prefixlen(
+ sa_in->sin_addr.s_addr);
+ } else if (rtm->rtm_flags & RTF_HOST)
+ prefixlen = 32;
+ else
+ prefixlen =
+ prefixlen_classful(prefix.s_addr);
+ if (rtm->rtm_flags & RTF_STATIC)
+ flags |= F_STATIC;
+ if (rtm->rtm_flags & RTF_DYNAMIC)
+ flags |= F_DYNAMIC;
+ if (rtm->rtm_flags & RTF_PROTO1)
+ flags |= F_BGPD_INSERTED;
+ break;
+ default:
+ continue;
+ }
+
+ if_index = rtm->rtm_index;
+ if ((sa = rti_info[RTAX_GATEWAY]) != NULL) {
+ switch (sa->sa_family) {
+ case AF_INET:
+ nexthop.s_addr = ((struct
+ sockaddr_in *)sa)->sin_addr.s_addr;
+ break;
+ case AF_LINK:
+ flags |= F_CONNECTED;
+ break;
+ }
+ }
+ }
+
+ switch (rtm->rtm_type) {
+ case RTM_ADD:
+ case RTM_CHANGE:
+ if (nexthop.s_addr == 0 && !(flags & F_CONNECTED)) {
+ log_warnx("dispatch_rtmsg no nexthop for %s/%u",
+ inet_ntoa(prefix), prefixlen);
+ continue;
+ }
+
+ if ((okr = kroute_find(prefix.s_addr, prefixlen)) !=
+ NULL) {
+ /* just add new multipath routes */
+ if (mpath && rtm->rtm_type == RTM_ADD)
+ goto add;
+ /* get the correct route */
+ kr = okr;
+ if (mpath && (kr = kroute_matchgw(okr,
+ nexthop)) == NULL) {
+ log_warnx("dispatch_rtmsg mpath route"
+ " not found");
+ /* add routes we missed out earlier */
+ goto add;
+ }
+
+ kr->r.nexthop.s_addr = nexthop.s_addr;
+ kr->r.flags = flags;
+ kr->r.if_index = if_index;
+ kr->r.ticks = mps_getticks();
+
+ rtlabel_unref(kr->r.rtlabel);
+ kr->r.rtlabel = 0;
+ if ((label = (struct sockaddr_rtlabel *)
+ rti_info[RTAX_LABEL]) != NULL) {
+ kr->r.rtlabel =
+ rtlabel_name2id(label->sr_label);
+ }
+
+ if (kif_validate(kr->r.if_index))
+ kr->r.flags &= ~F_DOWN;
+ else
+ kr->r.flags |= F_DOWN;
+ } else {
+add:
+ if ((kr = calloc(1,
+ sizeof(struct kroute_node))) == NULL) {
+ log_warn("dispatch_rtmsg");
+ return;
+ }
+ kr->r.prefix.s_addr = prefix.s_addr;
+ kr->r.prefixlen = prefixlen;
+ kr->r.nexthop.s_addr = nexthop.s_addr;
+ kr->r.flags = flags;
+ kr->r.if_index = if_index;
+ kr->r.ticks = mps_getticks();
+
+ if ((label = (struct sockaddr_rtlabel *)
+ rti_info[RTAX_LABEL]) != NULL) {
+ kr->r.rtlabel =
+ rtlabel_name2id(label->sr_label);
+ }
+
+ kroute_insert(kr);
+ }
+ break;
+ case RTM_DELETE:
+ if ((kr = kroute_find(prefix.s_addr, prefixlen)) ==
+ NULL)
+ continue;
+ if (!(kr->r.flags & F_KERNEL))
+ continue;
+ /* get the correct route */
+ okr = kr;
+ if (mpath &&
+ (kr = kroute_matchgw(kr, nexthop)) == NULL) {
+ log_warnx("dispatch_rtmsg mpath route"
+ " not found");
+ return;
+ }
+#ifdef notyet
+ /*
+ * last route is getting removed request the
+ * ospf route from the RDE to insert instead
+ */
+ if (okr == kr && kr->next == NULL &&
+ kr->r.flags & F_OSPFD_INSERTED)
+ main_imsg_compose_rde(IMSG_KROUTE_GET, 0,
+ &kr->r, sizeof(struct kroute));
+#endif
+ if (kroute_remove(kr) == -1)
+ return;
+ break;
+ case RTM_IFINFO:
+ memcpy(&ifm, next, sizeof(ifm));
+ if_change(ifm.ifm_index, ifm.ifm_flags,
+ &ifm.ifm_data);
+ break;
+ case RTM_NEWADDR:
+ ifam = (struct ifa_msghdr *)rtm;
+ if ((ifam->ifam_addrs & (RTA_NETMASK | RTA_IFA |
+ RTA_BRD)) == 0)
+ break;
+ sa = (struct sockaddr *)(ifam + 1);
+ get_rtaddrs(ifam->ifam_addrs, sa, rti_info);
+
+ if_newaddr(ifam->ifam_index,
+ (struct sockaddr_in *)rti_info[RTAX_IFA],
+ (struct sockaddr_in *)rti_info[RTAX_NETMASK],
+ (struct sockaddr_in *)rti_info[RTAX_BRD]);
+ break;
+ case RTM_IFANNOUNCE:
+ if_announce(next);
+ break;
+ default:
+ /* ignore for now */
+ break;
+ }
+ }
+}
+
+u_int16_t
+rtlabel_name2id(const char *name)
+{
+ return (0);
+}
+
+const char
+*rtlabel_id2name(u_int16_t id)
+{
+ return ("");
+}
+
+void
+rtlabel_unref(u_int16_t id)
+{
+ /* not used */
+}
diff --git a/usr.sbin/snmpd/log.c b/usr.sbin/snmpd/log.c
new file mode 100644
index 00000000000..f1d773c35f0
--- /dev/null
+++ b/usr.sbin/snmpd/log.c
@@ -0,0 +1,182 @@
+/* $OpenBSD: log.c,v 1.1 2007/12/05 09:22:44 reyk Exp $ */
+
+/*
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@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 MIND, 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/types.h>
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/tree.h>
+
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <net/if.h>
+
+#include <arpa/inet.h>
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <event.h>
+
+#include <openssl/ssl.h>
+
+#include "snmpd.h"
+
+int debug;
+
+void vlog(int, const char *, va_list);
+void logit(int, const char *, ...);
+
+void
+log_init(int n_debug)
+{
+ extern char *__progname;
+
+ debug = n_debug;
+
+ if (!debug)
+ openlog(__progname, LOG_PID | LOG_NDELAY, LOG_DAEMON);
+
+ tzset();
+}
+
+void
+logit(int pri, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vlog(pri, fmt, ap);
+ va_end(ap);
+}
+
+void
+vlog(int pri, const char *fmt, va_list ap)
+{
+ char *nfmt;
+
+ if (debug) {
+ /* best effort in out of mem situations */
+ if (asprintf(&nfmt, "%s\n", fmt) == -1) {
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+ } else {
+ vfprintf(stderr, nfmt, ap);
+ free(nfmt);
+ }
+ fflush(stderr);
+ } else
+ vsyslog(pri, fmt, ap);
+}
+
+
+void
+log_warn(const char *emsg, ...)
+{
+ char *nfmt;
+ va_list ap;
+
+ /* best effort to even work in out of memory situations */
+ if (emsg == NULL)
+ logit(LOG_CRIT, "%s", strerror(errno));
+ else {
+ va_start(ap, emsg);
+
+ if (asprintf(&nfmt, "%s: %s", emsg, strerror(errno)) == -1) {
+ /* we tried it... */
+ vlog(LOG_CRIT, emsg, ap);
+ logit(LOG_CRIT, "%s", strerror(errno));
+ } else {
+ vlog(LOG_CRIT, nfmt, ap);
+ free(nfmt);
+ }
+ va_end(ap);
+ }
+}
+
+void
+log_warnx(const char *emsg, ...)
+{
+ va_list ap;
+
+ va_start(ap, emsg);
+ vlog(LOG_CRIT, emsg, ap);
+ va_end(ap);
+}
+
+void
+log_info(const char *emsg, ...)
+{
+ va_list ap;
+
+ va_start(ap, emsg);
+ vlog(LOG_INFO, emsg, ap);
+ va_end(ap);
+}
+
+void
+log_debug(const char *emsg, ...)
+{
+ va_list ap;
+
+ if (debug) {
+ va_start(ap, emsg);
+ vlog(LOG_DEBUG, emsg, ap);
+ va_end(ap);
+ }
+}
+
+void
+fatal(const char *emsg)
+{
+ if (emsg == NULL)
+ logit(LOG_CRIT, "fatal: %s", strerror(errno));
+ else
+ if (errno)
+ logit(LOG_CRIT, "fatal: %s: %s",
+ emsg, strerror(errno));
+ else
+ logit(LOG_CRIT, "fatal: %s", emsg);
+
+ exit(1);
+}
+
+void
+fatalx(const char *emsg)
+{
+ errno = 0;
+ fatal(emsg);
+}
+
+const char *
+log_host(struct sockaddr_storage *ss, char *buf, size_t len)
+{
+ int af = ss->ss_family;
+ void *ptr;
+
+ bzero(buf, len);
+ if (af == AF_INET)
+ ptr = &((struct sockaddr_in *)ss)->sin_addr;
+ else
+ ptr = &((struct sockaddr_in6 *)ss)->sin6_addr;
+ return (inet_ntop(af, ptr, buf, len));
+}
diff --git a/usr.sbin/snmpd/mib.c b/usr.sbin/snmpd/mib.c
new file mode 100644
index 00000000000..630c1c6e797
--- /dev/null
+++ b/usr.sbin/snmpd/mib.c
@@ -0,0 +1,836 @@
+/* $OpenBSD: mib.c,v 1.1 2007/12/05 09:22:44 reyk Exp $ */
+
+/*
+ * Copyright (c) 2007 Reyk Floeter <reyk@vantronix.net>
+ *
+ * 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/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/tree.h>
+#include <sys/utsname.h>
+#include <sys/sysctl.h>
+
+#include <net/if.h>
+#include <net/if_types.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <event.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+#include <pwd.h>
+
+#include "snmpd.h"
+#include "mib.h"
+
+extern struct snmpd *env;
+
+/*
+ * Defined in SNMPv2-MIB.txt (RFC 3418)
+ */
+
+int mib_getsys(struct oid *, struct ber_oid *, struct ber_element **);
+int mib_getsnmp(struct oid *, struct ber_oid *, struct ber_element **);
+int mib_sysor(struct oid *, struct ber_oid *, struct ber_element **);
+int mib_setsnmp(struct oid *, struct ber_oid *, struct ber_element **);
+
+/* base MIB tree */
+static struct oid base_mib[] = {
+ { MIB(ISO), "iso" },
+ { MIB(ORG), "org" },
+ { MIB(DOD), "dod" },
+ { MIB(INTERNET), "internet" },
+ { MIB(DIRECTORY), "directory" },
+ { MIB(MGMT), "mgmt" },
+ { MIB(MIB_2), "mib-2", OID_MIB },
+ { MIB(SYSTEM), "system" },
+ { MIB(SYSDESCR), "sysDescr", OID_RD, mib_getsys },
+ { MIB(SYSOID), "sysOID", OID_RD, mib_getsys },
+ { MIB(SYSUPTIME), "sysUpTime", OID_RD, mib_getsys },
+ { MIB(SYSCONTACT), "sysContact", OID_RW, mib_getsys, mps_setstr },
+ { MIB(SYSNAME), "sysName", OID_RW, mib_getsys, mps_setstr },
+ { MIB(SYSLOCATION), "sysLocation", OID_RW, mib_getsys, mps_setstr },
+ { MIB(SYSSERVICES), "sysServices", OID_RS, mib_getsys },
+ { MIB(SYSORLASTCHANGE), "sysORLastChange", OID_RD, mps_getts },
+ { MIB(SYSORTABLE), "sysORTable" },
+ { MIB(SYSORENTRY), "sysOREntry" },
+ { MIB(SYSORINDEX), "sysORIndex", OID_TRD, mib_sysor },
+ { MIB(SYSORID), "sysORID", OID_TRD, mib_sysor },
+ { MIB(SYSORDESCR), "sysORDescr", OID_TRD, mib_sysor },
+ { MIB(SYSORUPTIME), "sysORUptime", OID_TRD, mib_sysor },
+ { MIB(TRANSMISSION), "transmission" },
+ { MIB(SNMP), "snmp", OID_MIB },
+ { MIB(SNMPINPKTS), "snmpInPkts", OID_RD, mib_getsnmp },
+ { MIB(SNMPOUTPKTS), "snmpOutPkts", OID_RD, mib_getsnmp },
+ { MIB(SNMPINBADVERSIONS), "snmpInBadVersions", OID_RD, mib_getsnmp },
+ { MIB(SNMPINBADCOMNNAMES), "snmpInBadCommunityNames", OID_RD, mib_getsnmp },
+ { MIB(SNMPINBADCOMNUSES), "snmpInBadCommunityUses", OID_RD, mib_getsnmp },
+ { MIB(SNMPINASNPARSEERRS), "snmpInASNParseErrs", OID_RD, mib_getsnmp },
+ { MIB(SNMPINTOOBIGS), "snmpInTooBigs", OID_RD, mib_getsnmp },
+ { MIB(SNMPINNOSUCHNAMES), "snmpInNoSuchNames", OID_RD, mib_getsnmp },
+ { MIB(SNMPINBADVALUES), "snmpInBadValues", OID_RD, mib_getsnmp },
+ { MIB(SNMPINREADONLYS), "snmpInReadOnlys", OID_RD, mib_getsnmp },
+ { MIB(SNMPINGENERRS), "snmpInGenErrs", OID_RD, mib_getsnmp },
+ { MIB(SNMPINTOTALREQVARS), "snmpInTotalReqVars", OID_RD, mib_getsnmp },
+ { MIB(SNMPINTOTALSETVARS), "snmpInTotalSetVars", OID_RD, mib_getsnmp },
+ { MIB(SNMPINGETREQUESTS), "snmpInGetRequests", OID_RD, mib_getsnmp },
+ { MIB(SNMPINGETNEXTS), "snmpInGetNexts", OID_RD, mib_getsnmp },
+ { MIB(SNMPINSETREQUESTS), "snmpInSetRequests", OID_RD, mib_getsnmp },
+ { MIB(SNMPINGETRESPONSES), "snmpInGetResponses", OID_RD, mib_getsnmp },
+ { MIB(SNMPINTRAPS), "snmpInTraps", OID_RD, mib_getsnmp },
+ { MIB(SNMPOUTTOOBIGS), "snmpOutTooBigs", OID_RD, mib_getsnmp },
+ { MIB(SNMPOUTNOSUCHNAMES), "snmpOutNoSuchNames", OID_RD, mib_getsnmp },
+ { MIB(SNMPOUTBADVALUES), "snmpOutBadValues", OID_RD, mib_getsnmp },
+ { MIB(SNMPOUTGENERRS), "snmpOutGenErrs", OID_RD, mib_getsnmp },
+ { MIB(SNMPOUTGETREQUESTS), "snmpOutGetRequests", OID_RD, mib_getsnmp },
+ { MIB(SNMPOUTGETNEXTS), "snmpOutGetNexts", OID_RD, mib_getsnmp },
+ { MIB(SNMPOUTSETREQUESTS), "snmpOutSetRequests", OID_RD, mib_getsnmp },
+ { MIB(SNMPOUTGETRESPONSES), "snmpOutGetResponses", OID_RD, mib_getsnmp },
+ { MIB(SNMPOUTTRAPS), "snmpOutTraps", OID_RD, mib_getsnmp },
+ { MIB(SNMPENAUTHTRAPS), "snmpEnableAuthenTraps", OID_RW, mib_getsnmp, mib_setsnmp },
+ { MIB(SNMPSILENTDROPS), "snmpSilentDrops", OID_RD, mib_getsnmp },
+ { MIB(SNMPPROXYDROPS), "snmpProxyDrops", OID_RD, mib_getsnmp },
+ { MIB(EXPERIMENTAL), "experimental" },
+ { MIB(PRIVATE), "private" },
+ { MIB(ENTERPRISES), "enterprises" },
+ { MIB(SECURITY), "security" },
+ { MIB(SNMPV2), "snmpV2" },
+ { MIB(SNMPDOMAINS), "snmpDomains" },
+ { MIB(SNMPPROXIES), "snmpProxies" },
+ { MIB(SNMPMODULES), "snmpModules" },
+ { MIB(SNMPMIB), "snmpMIB" },
+ { MIB(SNMPMIBOBJECTS), "snmpMIBObjects" },
+ { MIB(SNMPTRAP), "snmpTrap" },
+ { MIB(SNMPTRAPOID), "snmpTrapOID" },
+ { MIB(SNMPTRAPENTERPRISE), "snmpTrapEnterprise" },
+ { MIB(SNMPTRAPS), "snmpTraps" },
+ { MIB(COLDSTART), "coldStart" },
+ { MIB(WARMSTART), "warmStart" },
+ { MIB(LINKDOWN), "linkDown" },
+ { MIB(LINKUP), "linkUp" },
+ { MIB(AUTHFAILURE), "authenticationFailure" },
+ { MIB(EGPNEIGHBORLOSS), "egpNeighborLoss" }
+};
+
+int
+mib_getsys(struct oid *oid, struct ber_oid *o, struct ber_element **elm)
+{
+ struct ber_oid sysoid = OID(MIB_SYSOID_DEFAULT);
+ char *s = oid->o_data;
+ struct ber_oid *so = oid->o_data;
+ struct utsname u;
+ long long ticks;
+
+ if (uname(&u) == -1)
+ return (-1);
+
+ switch (oid->o_oid[OIDIDX_SYSTEM]) {
+ case 1:
+ if (s == NULL) {
+ if (asprintf(&s, "%s %s %s %s %s",
+ u.sysname, u.nodename, u.release,
+ u.version, u.machine) == -1)
+ return (-1);
+ oid->o_data = s;
+ oid->o_val = strlen(s);
+ }
+ *elm = ber_add_string(*elm, s);
+ break;
+ case 2:
+ if (so == NULL)
+ so = &sysoid;
+ mps_oidlen(so);
+ *elm = ber_add_oid(*elm, so);
+ break;
+ case 3:
+ ticks = mps_getticks();
+ *elm = ber_add_integer(*elm, ticks);
+ ber_set_header(*elm, BER_CLASS_APPLICATION, SNMP_T_TIMETICKS);
+ break;
+ case 4:
+ if (s == NULL) {
+ if (asprintf(&s, "root@%s", u.nodename) == -1)
+ return (-1);
+ oid->o_data = s;
+ oid->o_val = strlen(s);
+ }
+ *elm = ber_add_string(*elm, s);
+ break;
+ case 5:
+ if (s == NULL) {
+ if ((s = strdup(u.nodename)) == NULL)
+ return (-1);
+ oid->o_data = s;
+ oid->o_val = strlen(s);
+ }
+ *elm = ber_add_string(*elm, s);
+ break;
+ case 6:
+ if (s == NULL)
+ s = "";
+ *elm = ber_add_string(*elm, s);
+ break;
+ case 7:
+ *elm = ber_add_integer(*elm, oid->o_val);
+ break;
+ default:
+ return (-1);
+ }
+ return (0);
+}
+
+int
+mib_sysor(struct oid *oid, struct ber_oid *o, struct ber_element **elm)
+{
+ struct ber_element *ber = *elm;
+ u_int32_t idx = 1, nmib = 0;
+ struct oid *next, *miboid;
+ char buf[SNMPD_MAXSTRLEN], *ptr;
+
+ /* Count MIB root OIDs in the tree */
+ for (next = NULL;
+ (next = mps_foreach(next, OID_MIB)) != NULL; nmib++);
+
+ /* Get and verify the current row index */
+ idx = o->bo_id[OIDIDX_ORENTRY];
+ if (idx > nmib)
+ return (1);
+
+ /* Find the MIB root element for this Id */
+ for (next = miboid = NULL, nmib = 1;
+ (next = mps_foreach(next, OID_MIB)) != NULL; nmib++) {
+ if (nmib == idx)
+ miboid = next;
+ }
+ if (miboid == NULL)
+ return (-1);
+
+ /* Tables need to prepend the OID on their own */
+ ber = ber_add_oid(ber, o);
+
+ switch (o->bo_id[OIDIDX_OR]) {
+ case 1:
+ ber = ber_add_integer(ber, idx);
+ break;
+ case 2:
+ ber = ber_add_oid(ber, &miboid->o_id);
+ break;
+ case 3:
+ /*
+ * This should be a description of the MIB.
+ * But we use the symbolic OID string for now, it may
+ * help to display names of internal OIDs.
+ */
+ mps_oidstring(&miboid->o_id, buf, sizeof(buf));
+ if ((ptr = strdup(buf)) == NULL) {
+ ber = ber_add_string(ber, miboid->o_name);
+ } else {
+ ber = ber_add_string(ber, ptr);
+ ber->be_free = 1;
+ }
+ break;
+ case 4:
+ /*
+ * We do not support dynamic loading of MIB at runtime,
+ * the sysORUpTime value of 0 will indicate "loaded at
+ * startup".
+ */
+ ber = ber_add_integer(ber, 0);
+ ber_set_header(ber,
+ BER_CLASS_APPLICATION, SNMP_T_TIMETICKS);
+ break;
+ default:
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+mib_getsnmp(struct oid *oid, struct ber_oid *o, struct ber_element **elm)
+{
+ struct snmp_stats *stats = &env->sc_stats;
+ long long i;
+ struct statsmap {
+ u_int8_t m_id;
+ u_int32_t *m_ptr;
+ } mapping[] = {
+ { 1, &stats->snmp_inpkts },
+ { 2, &stats->snmp_outpkts },
+ { 3, &stats->snmp_inbadversions },
+ { 4, &stats->snmp_inbadcommunitynames },
+ { 5, &stats->snmp_inbadcommunityuses },
+ { 6, &stats->snmp_inasnparseerrs },
+ { 8, &stats->snmp_intoobigs },
+ { 9, &stats->snmp_innosuchnames },
+ { 10, &stats->snmp_inbadvalues },
+ { 11, &stats->snmp_inreadonlys },
+ { 12, &stats->snmp_ingenerrs },
+ { 13, &stats->snmp_intotalreqvars },
+ { 14, &stats->snmp_intotalsetvars },
+ { 15, &stats->snmp_ingetrequests },
+ { 16, &stats->snmp_ingetnexts },
+ { 17, &stats->snmp_insetrequests },
+ { 18, &stats->snmp_ingetresponses },
+ { 19, &stats->snmp_intraps },
+ { 20, &stats->snmp_outtoobigs },
+ { 21, &stats->snmp_outnosuchnames },
+ { 22, &stats->snmp_outbadvalues },
+ { 24, &stats->snmp_outgenerrs },
+ { 25, &stats->snmp_outgetrequests },
+ { 26, &stats->snmp_outgetnexts },
+ { 27, &stats->snmp_outsetrequests },
+ { 28, &stats->snmp_outgetresponses },
+ { 29, &stats->snmp_outtraps },
+ { 31, &stats->snmp_silentdrops },
+ { 32, &stats->snmp_proxydrops }
+ };
+
+ switch (oid->o_oid[OIDIDX_SNMP]) {
+ case 30:
+ i = stats->snmp_enableauthentraps == 1 ? 1 : 2;
+ *elm = ber_add_integer(*elm, i);
+ break;
+ default:
+ for (i = 0;
+ (u_int)i < (sizeof(mapping) / sizeof(mapping[0])); i++) {
+ if (oid->o_oid[OIDIDX_SNMP] == mapping[i].m_id) {
+ *elm = ber_add_integer(*elm, *mapping[i].m_ptr);
+ ber_set_header(*elm,
+ BER_CLASS_APPLICATION, SNMP_T_COUNTER32);
+ return (0);
+ }
+ }
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+mib_setsnmp(struct oid *oid, struct ber_oid *o, struct ber_element **elm)
+{
+ struct snmp_stats *stats = &env->sc_stats;
+ long long i;
+
+ if (ber_get_integer(*elm, &i) == -1)
+ return (-1);
+
+ stats->snmp_enableauthentraps = i == 1 ? 1 : 0;
+
+ return (0);
+}
+
+/*
+ * Defined in IF-MIB.txt (RFCs 1229, 1573, 2233, 2863)
+ */
+
+int mib_ifnumber(struct oid *, struct ber_oid *, struct ber_element **);
+struct kif
+ *mib_ifget(u_int);
+int mib_iftable(struct oid *, struct ber_oid *, struct ber_element **);
+int mib_ifxtable(struct oid *, struct ber_oid *, struct ber_element **);
+int mib_ifstacklast(struct oid *, struct ber_oid *, struct ber_element **);
+int mib_ifrcvtable(struct oid *, struct ber_oid *, struct ber_element **);
+
+static u_int8_t ether_zeroaddr[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+static struct ber_oid zerodotzero = { { 0, 0 }, 2 };
+
+static struct oid if_mib[] = {
+ { MIB(IFMIB), "ifMIB", OID_MIB },
+ { MIB(IFMIBOBJECTS), "ifMIBObjects" },
+ { MIB(IFXTABLE), "ifXTable" },
+ { MIB(IFXENTRY), "ifXEntry" },
+ { MIB(IFNAME), "ifName", OID_TRD, mib_ifxtable },
+ { MIB(IFINMASTPKTS), "ifInMulticastPkts", OID_TRD, mib_ifxtable },
+ { MIB(IFINBASTPKTS), "ifInBroadcastPkts", OID_TRD, mib_ifxtable },
+ { MIB(IFOUTMASTPKTS), "ifOutMulticastPkts", OID_TRD, mib_ifxtable },
+ { MIB(IFOUTBASTPKTS), "ifOurBroadcastPkts", OID_TRD, mib_ifxtable },
+ { MIB(IFHCINOCTETS), "ifHCInOctets", OID_TRD, mib_ifxtable },
+ { MIB(IFHCINUCASTPKTS), "ifHCInUcastPkts", OID_TRD, mib_ifxtable },
+ { MIB(IFHCINMCASTPKTS), "ifHCInMulticastPkts", OID_TRD, mib_ifxtable },
+ { MIB(IFHCINBCASTPKTS), "ifHCInBroadcastPkts", OID_TRD, mib_ifxtable },
+ { MIB(IFHCOUTOCTETS), "ifHCOutOctets", OID_TRD, mib_ifxtable },
+ { MIB(IFHCOUTUCASTPKTS), "ifHCOutUcastPkts", OID_TRD, mib_ifxtable },
+ { MIB(IFHCOUTMCASTPKTS), "ifHCOutMulticastPkts", OID_TRD, mib_ifxtable },
+ { MIB(IFHCOUTBCASTPKTS), "ifHCOutBroadcastPkts", OID_TRD, mib_ifxtable },
+ { MIB(IFLINKUPDORNTRAPENABLE), "ifLinkUpDownTrapEnable", OID_TRD, mib_ifxtable },
+ { MIB(IFHIGHSPEED), "ifHighSpeed", OID_TRD, mib_ifxtable },
+ { MIB(IFPROMISCMODE), "ifPromiscuousMode", OID_TRD, mib_ifxtable },
+ { MIB(IFCONNECTORPRESENT), "ifConnectorPresent", OID_TRD, mib_ifxtable },
+ { MIB(IFALIAS), "ifAlias", OID_TRD, mib_ifxtable },
+ { MIB(IFCNTDISCONTINUITYTIME), "ifCounterDiscontinuityTime", OID_TRD, mib_ifxtable },
+ { MIB(IFSTACKTABLE), "ifStackTable" },
+ { MIB(IFSTACKENTRY), "ifStackEntry" },
+ { MIB(IFRCVTABLE), "ifRcvAddressTable" },
+ { MIB(IFRCVENTRY), "ifRcvAddressEntry" },
+ { MIB(IFRCVSTATUS), "ifRcvAddressStatus", OID_TRD, mib_ifrcvtable },
+ { MIB(IFRCVTYPE), "ifRcvAddressType", OID_TRD, mib_ifrcvtable },
+ { MIB(IFSTACKLASTCHANGE), "ifStackLastChange", OID_RD, mib_ifstacklast },
+ { MIB(INTERFACES), "interfaces" },
+ { MIB(IFNUMBER), "ifNumber", OID_RD, mib_ifnumber },
+ { MIB(IFTABLE), "ifTable" },
+ { MIB(IFENTRY), "ifEntry" },
+ { MIB(IFINDEX), "ifIndex", OID_TRD, mib_iftable },
+ { MIB(IFDESCR), "ifDescr", OID_TRD, mib_iftable },
+ { MIB(IFTYPE), "ifDescr", OID_TRD, mib_iftable },
+ { MIB(IFMTU), "ifMtu", OID_TRD, mib_iftable },
+ { MIB(IFSPEED), "ifSpeed", OID_TRD, mib_iftable },
+ { MIB(IFPHYSADDR), "ifPhysAddress", OID_TRD, mib_iftable },
+ { MIB(IFADMINSTATUS), "ifAdminStatus", OID_TRD, mib_iftable },
+ { MIB(IFOPERSTATUS), "ifOperStatus", OID_TRD, mib_iftable },
+ { MIB(IFLASTCHANGE), "ifLastChange", OID_TRD, mib_iftable },
+ { MIB(IFINOCTETS), "ifInOctets", OID_TRD, mib_iftable },
+ { MIB(IFINUCASTPKTS), "ifInUcastPkts", OID_TRD, mib_iftable },
+ { MIB(IFINNUCASTPKTS), "ifInNUcastPkts", OID_TRD, mib_iftable },
+ { MIB(IFINDISCARDS), "ifInDiscards", OID_TRD, mib_iftable },
+ { MIB(IFINERRORS), "ifInErrors", OID_TRD, mib_iftable },
+ { MIB(IFINUNKNOWNERRORS), "ifInUnknownErrors", OID_TRD, mib_iftable },
+ { MIB(IFOUTOCTETS), "ifOutOctets", OID_TRD, mib_iftable },
+ { MIB(IFOUTUCASTPKTS), "ifOutUcastPkts", OID_TRD, mib_iftable },
+ { MIB(IFOUTNUCASTPKTS), "ifOutNUcastPkts", OID_TRD, mib_iftable },
+ { MIB(IFOUTDISCARDS), "ifOutDiscards", OID_TRD, mib_iftable },
+ { MIB(IFOUTERRORS), "ifOutErrors", OID_TRD, mib_iftable },
+ { MIB(IFOUTQLEN), "ifOutQLen", OID_TRD, mib_iftable },
+ { MIB(IFSPECIFIC), "ifSpecific", OID_TRD, mib_iftable }
+};
+
+int
+mib_ifnumber(struct oid *oid, struct ber_oid *o, struct ber_element **elm)
+{
+ *elm = ber_add_integer(*elm, kr_ifnumber());
+ return (0);
+}
+
+struct kif *
+mib_ifget(u_int idx)
+{
+ struct kif *kif;
+
+ if ((kif = kr_getif(idx)) == NULL) {
+ /*
+ * It may happen that a interface with a specific index
+ * does not exist or has been removed. Jump to the next
+ * available interface index.
+ */
+ for (kif = kr_getif(0); kif != NULL;
+ kif = kr_getnextif(kif->if_index))
+ if (kif->if_index > idx)
+ break;
+ if (kif == NULL)
+ return (NULL);
+ }
+
+ /* Update interface information */
+ kr_updateif(kif->if_index);
+ if ((kif = kr_getif(kif->if_index)) == NULL) {
+ log_debug("mib_ifxtable: interface disappeared?");
+ return (NULL);
+ }
+
+ return (kif);
+}
+
+int
+mib_iftable(struct oid *oid, struct ber_oid *o, struct ber_element **elm)
+{
+ struct ber_element *ber = *elm;
+ u_int32_t idx = 0;
+ struct kif *kif;
+ long long i;
+ size_t len;
+ int ifq;
+ int mib[5];
+
+ /* Get and verify the current row index */
+ idx = o->bo_id[OIDIDX_IFENTRY];
+ if ((kif = mib_ifget(idx)) == NULL)
+ return (1);
+
+ /* Tables need to prepend the OID on their own */
+ o->bo_id[OIDIDX_IFENTRY] = kif->if_index;
+ ber = ber_add_oid(ber, o);
+
+ switch (o->bo_id[OIDIDX_IF]) {
+ case 1:
+ ber = ber_add_integer(ber, kif->if_index);
+ break;
+ case 2:
+ /*
+ * The ifDescr should contain a vendor, product, etc.
+ * but we just use the interface name (like ifName).
+ * The interface name includes the driver name on OpenBSD.
+ */
+ ber = ber_add_string(ber, kif->if_name);
+ break;
+ case 3:
+ if (kif->if_type >= 0xf0) {
+ /*
+ * It does not make sense to announce the private
+ * interface types for CARP, ENC, PFSYNC, etc.
+ */
+ ber = ber_add_integer(ber, IFT_OTHER);
+ } else
+ ber = ber_add_integer(ber, kif->if_type);
+ break;
+ case 4:
+ ber = ber_add_integer(ber, kif->if_mtu);
+ break;
+ case 5:
+ ber = ber_add_integer(ber, kif->if_baudrate);
+ ber_set_header(ber, BER_CLASS_APPLICATION, SNMP_T_GAUGE32);
+ break;
+ case 6:
+ if (bcmp(kif->if_lladdr, ether_zeroaddr,
+ sizeof(kif->if_lladdr)) == 0) {
+ ber = ber_add_string(ber, "");
+ } else {
+ ber = ber_add_nstring(ber, kif->if_lladdr,
+ sizeof(kif->if_lladdr));
+ }
+ break;
+ case 7:
+ /* ifAdminStatus up(1), down(2), testing(3) */
+ i = (kif->if_flags & IFF_UP) ? 1 : 2;
+ ber = ber_add_integer(ber, i);
+ break;
+ case 8:
+ /* ifOperStatus */
+ if ((kif->if_flags & IFF_UP) == 0) {
+ i = 2; /* down(2) */
+ } else if (LINK_STATE_IS_UP(kif->if_link_state)) {
+ i = 1; /* up(1) */
+ } else if (kif->if_link_state == LINK_STATE_DOWN) {
+ i = 7; /* lowerLayerDown(7) or dormant(5)? */
+ } else
+ i = 4; /* unknown(4) */
+ ber = ber_add_integer(ber, i);
+ break;
+ case 9:
+ ber = ber_add_integer(ber, kif->if_ticks);
+ ber_set_header(ber, BER_CLASS_APPLICATION, SNMP_T_TIMETICKS);
+ break;
+ case 10:
+ ber = ber_add_integer(ber, (u_int32_t)kif->if_ibytes);
+ ber_set_header(ber, BER_CLASS_APPLICATION, SNMP_T_COUNTER32);
+ break;
+ case 11:
+ ber = ber_add_integer(ber, (u_int32_t)kif->if_ipackets);
+ ber_set_header(ber, BER_CLASS_APPLICATION, SNMP_T_COUNTER32);
+ break;
+ case 12:
+ ber = ber_add_integer(ber, (u_int32_t)kif->if_imcasts);
+ ber_set_header(ber, BER_CLASS_APPLICATION, SNMP_T_COUNTER32);
+ break;
+ case 13:
+ ber = ber_add_integer(ber, (u_int32_t)kif->if_iqdrops);
+ ber_set_header(ber, BER_CLASS_APPLICATION, SNMP_T_COUNTER32);
+ break;
+ case 14:
+ ber = ber_add_integer(ber, (u_int32_t)kif->if_ierrors);
+ ber_set_header(ber, BER_CLASS_APPLICATION, SNMP_T_COUNTER32);
+ break;
+ case 15:
+ ber = ber_add_integer(ber, 0); /* unknown errors? */
+ ber_set_header(ber, BER_CLASS_APPLICATION, SNMP_T_COUNTER32);
+ break;
+ case 16:
+ ber = ber_add_integer(ber, (u_int32_t)kif->if_obytes);
+ ber_set_header(ber, BER_CLASS_APPLICATION, SNMP_T_COUNTER32);
+ break;
+ case 17:
+ ber = ber_add_integer(ber, (u_int32_t)kif->if_opackets);
+ ber_set_header(ber, BER_CLASS_APPLICATION, SNMP_T_COUNTER32);
+ break;
+ case 18:
+ ber = ber_add_integer(ber, (u_int32_t)kif->if_omcasts);
+ ber_set_header(ber, BER_CLASS_APPLICATION, SNMP_T_COUNTER32);
+ break;
+ case 19:
+ mib[0] = CTL_NET;
+ mib[1] = AF_INET;
+ mib[2] = IPPROTO_IP;
+ mib[3] = IPCTL_IFQUEUE;
+ mib[4] = IFQCTL_DROPS;
+ len = sizeof(ifq);
+ if (sysctl(mib, 5, &ifq, &len, 0, 0) == -1) {
+ log_info("mib_iftable: %s: invalid ifq: %s",
+ kif->if_name, strerror(errno));
+ return (-1);
+ }
+ ber = ber_add_integer(ber, kif->if_noproto + ifq);
+ ber_set_header(ber, BER_CLASS_APPLICATION, SNMP_T_COUNTER32);
+ break;
+ case 20:
+ ber = ber_add_integer(ber, (u_int32_t)kif->if_oerrors);
+ ber_set_header(ber, BER_CLASS_APPLICATION, SNMP_T_COUNTER32);
+ break;
+ case 21:
+ mib[0] = CTL_NET;
+ mib[1] = AF_INET;
+ mib[2] = IPPROTO_IP;
+ mib[3] = IPCTL_IFQUEUE;
+ mib[4] = IFQCTL_LEN;
+ len = sizeof(ifq);
+ if (sysctl(mib, 5, &ifq, &len, 0, 0) == -1) {
+ log_info("mib_iftable: %s: invalid ifq: %s",
+ kif->if_name, strerror(errno));
+ return (-1);
+ }
+ ber = ber_add_integer(ber, ifq);
+ ber_set_header(ber, BER_CLASS_APPLICATION, SNMP_T_GAUGE32);
+ break;
+ case 22:
+ ber = ber_add_oid(ber, &zerodotzero);
+ break;
+ default:
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+mib_ifxtable(struct oid *oid, struct ber_oid *o, struct ber_element **elm)
+{
+ struct ber_element *ber = *elm;
+ u_int32_t idx = 0;
+ struct kif *kif;
+ int i = 0;
+
+ /* Get and verify the current row index */
+ idx = o->bo_id[OIDIDX_IFXENTRY];
+ if ((kif = mib_ifget(idx)) == NULL)
+ return (1);
+
+ /* Tables need to prepend the OID on their own */
+ o->bo_id[OIDIDX_IFXENTRY] = kif->if_index;
+ ber = ber_add_oid(ber, o);
+
+ switch (o->bo_id[OIDIDX_IFX]) {
+ case 1:
+ ber = ber_add_string(ber, kif->if_name);
+ break;
+ case 2:
+ ber = ber_add_integer(ber, (u_int32_t)kif->if_imcasts);
+ ber_set_header(ber, BER_CLASS_APPLICATION, SNMP_T_COUNTER32);
+ break;
+ case 3:
+ ber = ber_add_integer(ber, 0);
+ ber_set_header(ber, BER_CLASS_APPLICATION, SNMP_T_COUNTER32);
+ break;
+ case 4:
+ ber = ber_add_integer(ber, (u_int32_t)kif->if_omcasts);
+ ber_set_header(ber, BER_CLASS_APPLICATION, SNMP_T_COUNTER32);
+ break;
+ case 5:
+ ber = ber_add_integer(ber, 0);
+ ber_set_header(ber, BER_CLASS_APPLICATION, SNMP_T_COUNTER32);
+ break;
+ case 6:
+ ber = ber_add_integer(ber, (u_int64_t)kif->if_ibytes);
+ ber_set_header(ber, BER_CLASS_APPLICATION, SNMP_T_COUNTER64);
+ break;
+ case 7:
+ ber = ber_add_integer(ber, (u_int64_t)kif->if_ipackets);
+ ber_set_header(ber, BER_CLASS_APPLICATION, SNMP_T_COUNTER64);
+ break;
+ case 8:
+ ber = ber_add_integer(ber, (u_int64_t)kif->if_imcasts);
+ ber_set_header(ber, BER_CLASS_APPLICATION, SNMP_T_COUNTER64);
+ break;
+ case 9:
+ ber = ber_add_integer(ber, 0);
+ ber_set_header(ber, BER_CLASS_APPLICATION, SNMP_T_COUNTER64);
+ break;
+ case 10:
+ ber = ber_add_integer(ber, (u_int64_t)kif->if_obytes);
+ ber_set_header(ber, BER_CLASS_APPLICATION, SNMP_T_COUNTER64);
+ break;
+ case 11:
+ ber = ber_add_integer(ber, (u_int64_t)kif->if_opackets);
+ ber_set_header(ber, BER_CLASS_APPLICATION, SNMP_T_COUNTER64);
+ break;
+ case 12:
+ ber = ber_add_integer(ber, (u_int64_t)kif->if_omcasts);
+ ber_set_header(ber, BER_CLASS_APPLICATION, SNMP_T_COUNTER64);
+ break;
+ case 13:
+ ber = ber_add_integer(ber, 0);
+ ber_set_header(ber, BER_CLASS_APPLICATION, SNMP_T_COUNTER64);
+ break;
+ case 14:
+ ber = ber_add_integer(ber, 0); /* enabled(1), disabled(2) */
+ break;
+ case 15:
+ i = kif->if_baudrate >= 1000000 ?
+ kif->if_baudrate / 1000000 : 0;
+ ber = ber_add_integer(ber, i);
+ ber_set_header(ber, BER_CLASS_APPLICATION, SNMP_T_GAUGE32);
+ break;
+ case 16:
+ /* ifPromiscuousMode: true(1), false(2) */
+ i = kif->if_flags & IFF_PROMISC ? 1 : 2;
+ ber = ber_add_integer(ber, i);
+ break;
+ case 17:
+ /* ifConnectorPresent: false(2), true(1) */
+ i = kif->if_type == IFT_ETHER ? 1 : 2;
+ ber = ber_add_integer(ber, i);
+ break;
+ case 18:
+ ber = ber_add_string(ber, kif->if_descr);
+ break;
+ case 19:
+ ber = ber_add_integer(ber, 0);
+ ber_set_header(ber, BER_CLASS_APPLICATION, SNMP_T_TIMETICKS);
+ break;
+ default:
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+mib_ifstacklast(struct oid *oid, struct ber_oid *o, struct ber_element **elm)
+{
+ struct ber_element *ber = *elm;
+ ber = ber_add_integer(ber, kr_iflastchange());
+ ber_set_header(ber, BER_CLASS_APPLICATION, SNMP_T_TIMETICKS);
+ return (0);
+}
+
+int
+mib_ifrcvtable(struct oid *oid, struct ber_oid *o, struct ber_element **elm)
+{
+ struct ber_element *ber = *elm;
+ u_int32_t idx = 0;
+ struct kif *kif;
+ u_int i = 0;
+
+ /* Get and verify the current row index */
+ idx = o->bo_id[OIDIDX_IFRCVENTRY];
+ if ((kif = mib_ifget(idx)) == NULL)
+ return (1);
+
+ /*
+ * The lladdr of the interface will be encoded in the returned OID
+ * ifRcvAddressX.ifindex.6.x.x.x.x.x.x = val
+ * Thanks to the virtual cloner interfaces, it is an easy 1:1
+ * mapping in OpenBSD; only one lladdr (MAC) address per interface.
+ */
+
+ /* first set the base OID and caluculate the length */
+ idx = 0;
+ o->bo_id[OIDIDX_IFRCVENTRY + idx++] = kif->if_index;
+ o->bo_id[OIDIDX_IFRCVENTRY + idx] = 0;
+ mps_oidlen(o);
+
+ /* extend the OID with the lladdr length and octets */
+ o->bo_id[OIDIDX_IFRCVENTRY + idx++] = sizeof(kif->if_lladdr);
+ o->bo_n++;
+ for (i = 0; i < sizeof(kif->if_lladdr); i++, o->bo_n++)
+ o->bo_id[OIDIDX_IFRCVENTRY + idx++] = kif->if_lladdr[i];
+
+ /* write OID */
+ ber = ber_add_oid(ber, o);
+
+ switch (o->bo_id[OIDIDX_IFRCV]) {
+ case 2:
+ /* ifRcvAddressStatus: RowStatus active(1), notInService(2) */
+ i = kif->if_flags & IFF_UP ? 1 : 2;
+ ber = ber_add_integer(ber, i);
+ break;
+ case 3:
+ /* ifRcvAddressType: other(1), volatile(2), nonVolatile(3) */
+ ber = ber_add_integer(ber, 1);
+ break;
+ default:
+ return (-1);
+ }
+
+ return (0);
+}
+
+/*
+ * PRIVATE ENTERPRISE NUMBERS from
+ * http://www.iana.org/assignments/enterprise-numbers
+ *
+ * This is not the complete list of private enterprise numbers, it only
+ * includes some well-known companies and especially network companies
+ * that are very common in the datacenters around the world. It would
+ * be an overkill to include ~30.000 entries for all the organizations
+ * from the official list.
+ */
+static struct oid enterprise_mib[] = {
+ { MIB(IBM), "ibm" },
+ { MIB(CMU), "cmu" },
+ { MIB(UNIX), "unix" },
+ { MIB(CISCO), "ciscoSystems" },
+ { MIB(HP), "hp" },
+ { MIB(MIT), "mit" },
+ { MIB(NORTEL), "nortelNetworks" },
+ { MIB(SUN), "sun" },
+ { MIB(3COM), "3com" },
+ { MIB(SYNOPTICS), "synOptics" },
+ { MIB(ENTERASYS), "enterasys" },
+ { MIB(SGI), "sgi" },
+ { MIB(APPLE), "apple" },
+ { MIB(ATT), "att" },
+ { MIB(NOKIA), "nokia" },
+ { MIB(CERN), "cern" },
+ { MIB(FSC), "fsc" },
+ { MIB(COMPAQ), "compaq" },
+ { MIB(DELL), "dell" },
+ { MIB(ALTEON), "alteon" },
+ { MIB(EXTREME), "extremeNetworks" },
+ { MIB(FOUNDRY), "foundryNetworks" },
+ { MIB(HUAWAI), "huawaiTechnology" },
+ { MIB(UCDAVIS), "ucDavis" },
+ { MIB(CHECKPOINT), "checkPoint" },
+ { MIB(JUNIPER), "juniper" },
+ { MIB(FORCE10), "force10Networks" },
+ { MIB(ALCATELLUCENT), "alcatelLucent" },
+ { MIB(SNOM), "snom" },
+ { MIB(GOOGLE), "google" },
+ { MIB(F5), "f5Networks" },
+ { MIB(SFLOW), "sFlow" },
+ { MIB(MSYS), "microSystems" },
+ { MIB(VANTRONIX), "vantronix" },
+ { MIB(OPENBSD), "openBSD" }
+};
+
+void
+mib_init(void)
+{
+ /* SNMPv2-MIB */
+ mps_mibtree(base_mib, sizeof(base_mib) / sizeof(base_mib[0]));
+
+ /* IF-MIB */
+ mps_mibtree(if_mib, sizeof(if_mib) / sizeof(if_mib[0]));
+
+ /* some http://www.iana.org/assignments/enterprise-numbers */
+ mps_mibtree(enterprise_mib,
+ sizeof(enterprise_mib) / sizeof(enterprise_mib[0]));
+}
diff --git a/usr.sbin/snmpd/mib.h b/usr.sbin/snmpd/mib.h
new file mode 100644
index 00000000000..5dd8ee6b32f
--- /dev/null
+++ b/usr.sbin/snmpd/mib.h
@@ -0,0 +1,209 @@
+/* $OpenBSD: mib.h,v 1.1 2007/12/05 09:22:44 reyk Exp $ */
+
+/*
+ * Copyright (c) 2007 Reyk Floeter <reyk@vantronix.net>
+ *
+ * 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.
+ */
+
+#ifndef _SNMPD_MIB_H
+#define _SNMPD_MIB_H
+
+/* From the SNMPv2-SMI MIB */
+#define MIB_ISO 1
+#define MIB_ORG MIB_ISO, 3
+#define MIB_DOD MIB_ORG, 6
+#define MIB_INTERNET MIB_DOD, 1
+#define MIB_DIRECTORY MIB_INTERNET, 1
+#define MIB_MGMT MIB_INTERNET, 2
+#define MIB_MIB_2 MIB_MGMT, 1
+#define MIB_SYSTEM MIB_MIB_2, 1
+#define OIDIDX_SYSTEM 7
+#define MIB_SYSDESCR MIB_SYSTEM, 1
+#define MIB_SYSOID MIB_SYSTEM, 2
+#define MIB_SYSUPTIME MIB_SYSTEM, 3
+#define MIB_SYSCONTACT MIB_SYSTEM, 4
+#define MIB_SYSNAME MIB_SYSTEM, 5
+#define MIB_SYSLOCATION MIB_SYSTEM, 6
+#define MIB_SYSSERVICES MIB_SYSTEM, 7
+#define MIB_SYSORLASTCHANGE MIB_SYSTEM, 8
+#define MIB_SYSORTABLE MIB_SYSTEM, 9
+#define MIB_SYSORENTRY MIB_SYSORTABLE, 1
+#define OIDIDX_OR 9
+#define OIDIDX_ORENTRY 10
+#define MIB_SYSORINDEX MIB_SYSORENTRY, 1
+#define MIB_SYSORID MIB_SYSORENTRY, 2
+#define MIB_SYSORDESCR MIB_SYSORENTRY, 3
+#define MIB_SYSORUPTIME MIB_SYSORENTRY, 4
+#define MIB_TRANSMISSION MIB_MIB_2, 10
+#define MIB_SNMP MIB_MIB_2, 11
+#define OIDIDX_SNMP 7
+#define MIB_SNMPINPKTS MIB_SNMP, 1
+#define MIB_SNMPOUTPKTS MIB_SNMP, 2
+#define MIB_SNMPINBADVERSIONS MIB_SNMP, 3
+#define MIB_SNMPINBADCOMNNAMES MIB_SNMP, 4
+#define MIB_SNMPINBADCOMNUSES MIB_SNMP, 5
+#define MIB_SNMPINASNPARSEERRS MIB_SNMP, 6
+#define MIB_SNMPINTOOBIGS MIB_SNMP, 8
+#define MIB_SNMPINNOSUCHNAMES MIB_SNMP, 9
+#define MIB_SNMPINBADVALUES MIB_SNMP, 10
+#define MIB_SNMPINREADONLYS MIB_SNMP, 11
+#define MIB_SNMPINGENERRS MIB_SNMP, 12
+#define MIB_SNMPINTOTALREQVARS MIB_SNMP, 13
+#define MIB_SNMPINTOTALSETVARS MIB_SNMP, 14
+#define MIB_SNMPINGETREQUESTS MIB_SNMP, 15
+#define MIB_SNMPINGETNEXTS MIB_SNMP, 16
+#define MIB_SNMPINSETREQUESTS MIB_SNMP, 17
+#define MIB_SNMPINGETRESPONSES MIB_SNMP, 18
+#define MIB_SNMPINTRAPS MIB_SNMP, 19
+#define MIB_SNMPOUTTOOBIGS MIB_SNMP, 20
+#define MIB_SNMPOUTNOSUCHNAMES MIB_SNMP, 21
+#define MIB_SNMPOUTBADVALUES MIB_SNMP, 22
+#define MIB_SNMPOUTGENERRS MIB_SNMP, 24
+#define MIB_SNMPOUTGETREQUESTS MIB_SNMP, 25
+#define MIB_SNMPOUTGETNEXTS MIB_SNMP, 26
+#define MIB_SNMPOUTSETREQUESTS MIB_SNMP, 27
+#define MIB_SNMPOUTGETRESPONSES MIB_SNMP, 28
+#define MIB_SNMPOUTTRAPS MIB_SNMP, 29
+#define MIB_SNMPENAUTHTRAPS MIB_SNMP, 30
+#define MIB_SNMPSILENTDROPS MIB_SNMP, 31
+#define MIB_SNMPPROXYDROPS MIB_SNMP, 32
+#define MIB_EXPERIMENTAL MIB_INTERNET, 3
+#define MIB_PRIVATE MIB_INTERNET, 4
+#define MIB_ENTERPRISES MIB_PRIVATE, 1
+#define MIB_SECURITY MIB_INTERNET, 5
+#define MIB_SNMPV2 MIB_INTERNET, 6
+#define MIB_SNMPDOMAINS MIB_SNMPV2, 1
+#define MIB_SNMPPROXIES MIB_SNMPV2, 2
+#define MIB_SNMPMODULES MIB_SNMPV2, 3
+#define MIB_SNMPMIB MIB_SNMPMODULES, 1
+#define MIB_SNMPMIBOBJECTS MIB_SNMPMIB, 1
+#define MIB_SNMPTRAP MIB_SNMPMIBOBJECTS, 4
+#define MIB_SNMPTRAPOID MIB_SNMPTRAP, 1
+#define MIB_SNMPTRAPENTERPRISE MIB_SNMPTRAP, 3
+#define MIB_SNMPTRAPS MIB_SNMPMIBOBJECTS, 5
+#define MIB_COLDSTART MIB_SNMPTRAPS, 1
+#define MIB_WARMSTART MIB_SNMPTRAPS, 2
+#define MIB_LINKDOWN MIB_SNMPTRAPS, 3
+#define MIB_LINKUP MIB_SNMPTRAPS, 4
+#define MIB_AUTHFAILURE MIB_SNMPTRAPS, 5
+#define MIB_EGPNEIGHBORLOSS MIB_SNMPTRAPS, 6
+
+/* IF-MIB */
+#define MIB_IFMIB MIB_MIB_2, 31
+#define MIB_IFMIBOBJECTS MIB_IFMIB, 1
+#define MIB_IFXTABLE MIB_IFMIBOBJECTS, 1
+#define MIB_IFXENTRY MIB_IFXTABLE, 1
+#define OIDIDX_IFX 10
+#define OIDIDX_IFXENTRY 11
+#define MIB_IFNAME MIB_IFXENTRY, 1
+#define MIB_IFINMASTPKTS MIB_IFXENTRY, 2
+#define MIB_IFINBASTPKTS MIB_IFXENTRY, 3
+#define MIB_IFOUTMASTPKTS MIB_IFXENTRY, 4
+#define MIB_IFOUTBASTPKTS MIB_IFXENTRY, 5
+#define MIB_IFHCINOCTETS MIB_IFXENTRY, 6
+#define MIB_IFHCINUCASTPKTS MIB_IFXENTRY, 7
+#define MIB_IFHCINMCASTPKTS MIB_IFXENTRY, 8
+#define MIB_IFHCINBCASTPKTS MIB_IFXENTRY, 9
+#define MIB_IFHCOUTOCTETS MIB_IFXENTRY, 10
+#define MIB_IFHCOUTUCASTPKTS MIB_IFXENTRY, 11
+#define MIB_IFHCOUTMCASTPKTS MIB_IFXENTRY, 12
+#define MIB_IFHCOUTBCASTPKTS MIB_IFXENTRY, 13
+#define MIB_IFLINKUPDORNTRAPENABLE MIB_IFXENTRY, 14
+#define MIB_IFHIGHSPEED MIB_IFXENTRY, 15
+#define MIB_IFPROMISCMODE MIB_IFXENTRY, 16
+#define MIB_IFCONNECTORPRESENT MIB_IFXENTRY, 17
+#define MIB_IFALIAS MIB_IFXENTRY, 18
+#define MIB_IFCNTDISCONTINUITYTIME MIB_IFXENTRY, 19
+#define MIB_IFSTACKTABLE MIB_IFMIBOBJECTS, 2
+#define MIB_IFSTACKENTRY MIB_IFSTACKTABLE, 1
+#define OIDIDX_IFSTACK 10
+#define OIDIDX_IFSTACKENTRY 11
+#define MIB_IFSTACKSTATUS MIB_IFSTACKENTRY, 3
+#define MIB_IFRCVTABLE MIB_IFMIBOBJECTS, 4
+#define MIB_IFRCVENTRY MIB_IFRCVTABLE, 1
+#define OIDIDX_IFRCV 10
+#define OIDIDX_IFRCVENTRY 11
+#define MIB_IFRCVSTATUS MIB_IFRCVENTRY, 2
+#define MIB_IFRCVTYPE MIB_IFRCVENTRY, 3
+#define MIB_IFSTACKLASTCHANGE MIB_IFMIBOBJECTS, 6
+#define MIB_INTERFACES MIB_MIB_2, 2
+#define MIB_IFNUMBER MIB_INTERFACES, 1
+#define MIB_IFTABLE MIB_INTERFACES, 2
+#define MIB_IFENTRY MIB_IFTABLE, 1
+#define OIDIDX_IF 9
+#define OIDIDX_IFENTRY 10
+#define MIB_IFINDEX MIB_IFENTRY, 1
+#define MIB_IFDESCR MIB_IFENTRY, 2
+#define MIB_IFTYPE MIB_IFENTRY, 3
+#define MIB_IFMTU MIB_IFENTRY, 4
+#define MIB_IFSPEED MIB_IFENTRY, 5
+#define MIB_IFPHYSADDR MIB_IFENTRY, 6
+#define MIB_IFADMINSTATUS MIB_IFENTRY, 7
+#define MIB_IFOPERSTATUS MIB_IFENTRY, 8
+#define MIB_IFLASTCHANGE MIB_IFENTRY, 9
+#define MIB_IFINOCTETS MIB_IFENTRY, 10
+#define MIB_IFINUCASTPKTS MIB_IFENTRY, 11
+#define MIB_IFINNUCASTPKTS MIB_IFENTRY, 12
+#define MIB_IFINDISCARDS MIB_IFENTRY, 13
+#define MIB_IFINERRORS MIB_IFENTRY, 14
+#define MIB_IFINUNKNOWNERRORS MIB_IFENTRY, 15
+#define MIB_IFOUTOCTETS MIB_IFENTRY, 16
+#define MIB_IFOUTUCASTPKTS MIB_IFENTRY, 17
+#define MIB_IFOUTNUCASTPKTS MIB_IFENTRY, 18
+#define MIB_IFOUTDISCARDS MIB_IFENTRY, 19
+#define MIB_IFOUTERRORS MIB_IFENTRY, 20
+#define MIB_IFOUTQLEN MIB_IFENTRY, 21
+#define MIB_IFSPECIFIC MIB_IFENTRY, 22
+
+/* Some enterprise-sepcific OIDs */
+#define MIB_IBM MIB_ENTERPRISES, 2
+#define MIB_CMU MIB_ENTERPRISES, 3
+#define MIB_UNIX MIB_ENTERPRISES, 4
+#define MIB_CISCO MIB_ENTERPRISES, 9
+#define MIB_HP MIB_ENTERPRISES, 11
+#define MIB_MIT MIB_ENTERPRISES, 20
+#define MIB_NORTEL MIB_ENTERPRISES, 35
+#define MIB_SUN MIB_ENTERPRISES, 42
+#define MIB_3COM MIB_ENTERPRISES, 43
+#define MIB_SYNOPTICS MIB_ENTERPRISES, 45
+#define MIB_ENTERASYS MIB_ENTERPRISES, 52
+#define MIB_SGI MIB_ENTERPRISES, 59
+#define MIB_APPLE MIB_ENTERPRISES, 63
+#define MIB_ATT MIB_ENTERPRISES, 74
+#define MIB_NOKIA MIB_ENTERPRISES, 94
+#define MIB_CERN MIB_ENTERPRISES, 96
+#define MIB_FSC MIB_ENTERPRISES, 231
+#define MIB_COMPAQ MIB_ENTERPRISES, 232
+#define MIB_DELL MIB_ENTERPRISES, 674
+#define MIB_ALTEON MIB_ENTERPRISES, 1872
+#define MIB_EXTREME MIB_ENTERPRISES, 1916
+#define MIB_FOUNDRY MIB_ENTERPRISES, 1991
+#define MIB_HUAWAI MIB_ENTERPRISES, 2011
+#define MIB_UCDAVIS MIB_ENTERPRISES, 2021
+#define MIB_CHECKPOINT MIB_ENTERPRISES, 2620
+#define MIB_JUNIPER MIB_ENTERPRISES, 2636
+#define MIB_FORCE10 MIB_ENTERPRISES, 6027
+#define MIB_ALCATELLUCENT MIB_ENTERPRISES, 7483
+#define MIB_SNOM MIB_ENTERPRISES, 7526
+#define MIB_GOOGLE MIB_ENTERPRISES, 11129
+#define MIB_F5 MIB_ENTERPRISES, 12276
+#define MIB_SFLOW MIB_ENTERPRISES, 14706
+#define MIB_MSYS MIB_ENTERPRISES, 18623
+#define MIB_VANTRONIX MIB_ENTERPRISES, 26766
+#define MIB_OPENBSD MIB_VANTRONIX, 42
+#define MIB_SYSOID_DEFAULT MIB_OPENBSD, 2, 1
+
+void mib_init(void);
+
+#endif /* _SNMPD_MIB_H */
diff --git a/usr.sbin/snmpd/mps.c b/usr.sbin/snmpd/mps.c
new file mode 100644
index 00000000000..8a7c90ef887
--- /dev/null
+++ b/usr.sbin/snmpd/mps.c
@@ -0,0 +1,447 @@
+/* $OpenBSD: mps.c,v 1.1 2007/12/05 09:22:44 reyk Exp $ */
+
+/*
+ * Copyright (c) 2007 Reyk Floeter <reyk@vantronix.net>
+ *
+ * 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/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/tree.h>
+#include <sys/sysctl.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_arp.h>
+#include <net/if_media.h>
+#include <net/route.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#include <arpa/inet.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <event.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+#include <pwd.h>
+
+#include "snmpd.h"
+#include "mib.h"
+
+RB_HEAD(oidtree, oid);
+RB_PROTOTYPE(oidtree, oid, o_element, mps_oid_cmp);
+
+extern struct snmpd *env;
+struct oidtree mps_oidtree;
+
+struct ber_oid *
+ mps_table(struct oid *, struct ber_oid *, struct ber_oid *);
+
+u_long
+mps_getticks(void)
+{
+ struct timeval now, run;
+ u_long ticks;
+
+ gettimeofday(&now, NULL);
+ if (timercmp(&now, &env->sc_starttime, <=))
+ return (0);
+ timersub(&now, &env->sc_starttime, &run);
+ ticks = run.tv_sec * 100;
+ if (run.tv_usec)
+ ticks += run.tv_usec / 10000;
+
+ return (ticks);
+}
+
+int
+mps_getstr(struct oid *oid, struct ber_oid *o, struct ber_element **elm)
+{
+ char *s = oid->o_data;
+
+ if (s == NULL)
+ return (-1);
+ *elm = ber_add_string(*elm, s);
+ return (0);
+}
+
+int
+mps_setstr(struct oid *oid, struct ber_oid *o, struct ber_element **elm)
+{
+ struct ber_element *ber = *elm;
+ char *s;
+
+ if ((oid->o_flags & OID_WR) == 0)
+ return (-1);
+
+ if (ber->be_class != BER_CLASS_UNIVERSAL ||
+ ber->be_type != BER_TYPE_OCTETSTRING)
+ return (-1);
+ if (ber_get_string(ber, &s) == -1)
+ return (-1);
+ if ((oid->o_data = (void *)strdup(s)) == NULL)
+ return (-1);
+ oid->o_val = strlen((char *)oid->o_data);
+
+ return (0);
+}
+
+int
+mps_getint(struct oid *oid, struct ber_oid *o, struct ber_element **elm)
+{
+ *elm = ber_add_integer(*elm, oid->o_val);
+ return (0);
+}
+
+int
+mps_setint(struct oid *oid, struct ber_oid *o, struct ber_element **elm)
+{
+ long long i;
+
+ if (ber_get_integer(*elm, &i) == -1)
+ return (-1);
+ oid->o_val = i;
+
+ return (0);
+}
+
+int
+mps_getts(struct oid *oid, struct ber_oid *o, struct ber_element **elm)
+{
+ *elm = ber_add_integer(*elm, oid->o_val);
+ ber_set_header(*elm, BER_CLASS_APPLICATION, SNMP_T_TIMETICKS);
+ return (0);
+}
+
+void
+mps_oidlen(struct ber_oid *o)
+{
+ size_t i;
+ for (i = 0; i < BER_MAX_OID_LEN && o->bo_id[i] != 0; i++);
+ o->bo_n = i;
+}
+
+char *
+mps_oidstring(struct ber_oid *o, char *buf, size_t len)
+{
+ char str[256];
+ struct oid *value, key;
+ size_t i, lookup = 1;
+
+ bzero(buf, len);
+ bcopy(o, &key.o_id, sizeof(struct ber_oid));
+ key.o_flags |= OID_TABLE; /* do not match wildcards */
+
+ if (env->sc_flags & SNMPD_F_NONAMES)
+ lookup = 0;
+
+ for (i = 0; i < o->bo_n; i++) {
+ key.o_oidlen = i + 1;
+ if (lookup &&
+ (value = RB_FIND(oidtree, &mps_oidtree, &key)) != NULL)
+ snprintf(str, sizeof(str), "%s", value->o_name);
+ else
+ snprintf(str, sizeof(str), "%d", key.o_oid[i]);
+ strlcat(buf, str, len);
+ if (i < (o->bo_n - 1))
+ strlcat(buf, ".", len);
+ }
+
+ return (buf);
+}
+
+struct ber_element *
+mps_getreq(struct ber_element *root, struct ber_oid *o)
+{
+ struct ber_element *elm = root;
+ struct oid key, *value;
+
+ mps_oidlen(o);
+ if (o->bo_n > BER_MAX_OID_LEN)
+ return (NULL);
+ bcopy(o, &key.o_id, sizeof(struct ber_oid));
+ value = RB_FIND(oidtree, &mps_oidtree, &key);
+ if (value == NULL)
+ return (NULL);
+ if (OID_NOTSET(value))
+ return (NULL);
+ if ((value->o_flags & OID_TABLE) == 0)
+ elm = ber_add_oid(elm, &value->o_id);
+ if (value->o_get == NULL)
+ elm = ber_add_null(elm);
+ else
+ if (value->o_get(value, o, &elm) != 0)
+ return (NULL);
+
+ return (elm);
+}
+
+int
+mps_setreq(struct ber_element *ber, struct ber_oid *o)
+{
+ struct oid key, *value;
+
+ mps_oidlen(o);
+ if (o->bo_n > BER_MAX_OID_LEN)
+ return (-1);
+ bcopy(o, &key.o_id, sizeof(struct ber_oid));
+ value = RB_FIND(oidtree, &mps_oidtree, &key);
+ if (value == NULL)
+ return (-1);
+ if ((value->o_flags & OID_WR) == 0 ||
+ value->o_set == NULL)
+ return (-1);
+ return (value->o_set(value, o, &ber));
+}
+
+struct ber_element *
+mps_getnextreq(struct ber_element *root, struct ber_oid *o)
+{
+ struct oid *next = NULL;
+ struct ber_element *ber = root;
+ struct oid key, *value;
+ int ret;
+ struct ber_oid no;
+
+ mps_oidlen(o);
+ if (o->bo_n > BER_MAX_OID_LEN)
+ return (NULL);
+ bcopy(o, &key.o_id, sizeof(struct ber_oid));
+ value = RB_FIND(oidtree, &mps_oidtree, &key);
+ if (value == NULL)
+ return (NULL);
+ if (value->o_flags & OID_TABLE) {
+ /* Get the next table row for this column */
+ if (mps_table(value, o, &no) == NULL)
+ return (NULL);
+ bcopy(&no, o, sizeof(*o));
+ ret = value->o_get(value, o, &ber);
+ switch (ret) {
+ case 0:
+ return (ber);
+ case -1:
+ return (NULL);
+ case 1: /* end-of-rows */
+ break;
+ }
+ }
+ for (next = value; next != NULL;) {
+ next = RB_NEXT(oidtree, &mps_oidtree, next);
+ if (next == NULL)
+ break;
+ if (!OID_NOTSET(next) && next->o_get != NULL)
+ break;
+ }
+ if (next == NULL || next->o_get == NULL)
+ return (NULL);
+
+ if (next->o_flags & OID_TABLE) {
+ /* Get the next table row for this column */
+ if (mps_table(next, o, &no) == NULL)
+ return (NULL);
+ bcopy(&no, o, sizeof(*o));
+ if ((ret = next->o_get(next, o, &ber)) != 0)
+ return (NULL);
+ } else {
+ bcopy(&next->o_id, o, sizeof(*o));
+ ber = ber_add_oid(ber, &next->o_id);
+ if ((ret = next->o_get(next, o, &ber)) != 0)
+ return (NULL);
+ }
+
+ return (ber);
+}
+
+int
+mps_set(struct ber_oid *o, void *p, long long len)
+{
+ struct oid key, *value;
+
+ mps_oidlen(o);
+ if (o->bo_n > BER_MAX_OID_LEN)
+ return (-1);
+ bcopy(o, &key.o_id, sizeof(struct ber_oid));
+ value = RB_FIND(oidtree, &mps_oidtree, &key);
+ if (value == NULL)
+ return (-1);
+ if (value->o_data != NULL)
+ free(value->o_data);
+ value->o_data = p;
+ value->o_val = len;
+
+ return (0);
+}
+
+struct ber_oid *
+mps_table(struct oid *oid, struct ber_oid *o, struct ber_oid *no)
+{
+ u_int32_t col, idx = 1;
+ size_t id, subid;
+ struct oid a, b;
+
+ /*
+ * This function is beeing used to iterate through elements
+ * in a SMI "table". It is called by the mps_getnext() handler.
+ * For example, if the input is sysORIndex, it will return
+ * sysORIndex.1. If the inpurt is sysORIndex.2, it will return
+ * sysORIndex.3 etc.. The MIB code has to verify the index,
+ * see mib_sysor() as an example.
+ */
+
+ id = oid->o_oidlen - 1;
+ subid = oid->o_oidlen;
+ bcopy(&oid->o_id, no, sizeof(*no));
+
+ if (o->bo_n >= oid->o_oidlen) {
+ /*
+ * Compare the requested and the matched OID to see
+ * if we have to iterate to the next element.
+ */
+ bzero(&a, sizeof(a));
+ bcopy(o, &a.o_id, sizeof(struct ber_oid));
+ bzero(&b, sizeof(b));
+ bcopy(&oid->o_id, &b.o_id, sizeof(struct ber_oid));
+ b.o_oidlen--;
+ b.o_flags |= OID_TABLE;
+ if (mps_oid_cmp(&a, &b) == 0) {
+ col = oid->o_oid[id];
+ if (col > o->bo_id[id])
+ idx = 1;
+ else
+ idx = o->bo_id[subid] + 1;
+ o->bo_id[subid] = idx;
+ o->bo_id[id] = col;
+ bcopy(o, no, sizeof(*no));
+ }
+ }
+
+ /* The root element ends with a 0, iterate to the first element */
+ if (!no->bo_id[subid])
+ no->bo_id[subid]++;
+
+ mps_oidlen(no);
+
+ return (no);
+}
+
+void
+mps_delete(struct oid *oid)
+{
+ struct oid key, *value;
+
+ bcopy(&oid->o_id, &key.o_id, sizeof(struct ber_oid));
+ if ((value = RB_FIND(oidtree, &mps_oidtree, &key)) != NULL &&
+ value == oid)
+ RB_REMOVE(oidtree, &mps_oidtree, value);
+
+ if (oid->o_data != NULL)
+ free(oid->o_data);
+ if (oid->o_flags & OID_DYNAMIC) {
+ free(oid->o_name);
+ free(oid);
+ }
+}
+
+void
+mps_insert(struct oid *oid)
+{
+ struct oid key, *value;
+
+ if ((oid->o_flags & OID_TABLE) && oid->o_get == NULL)
+ fatalx("mps_insert: invalid MIB table");
+
+ bcopy(&oid->o_id, &key.o_id, sizeof(struct ber_oid));
+ value = RB_FIND(oidtree, &mps_oidtree, &key);
+ if (value != NULL)
+ mps_delete(value);
+
+ RB_INSERT(oidtree, &mps_oidtree, oid);
+}
+
+void
+mps_mibtree(struct oid *oids, size_t n)
+{
+ struct oid *oid;
+ size_t i;
+
+ for (i = 0; i < n; i++) {
+ oid = &oids[i];
+ mps_oidlen(&oid->o_id);
+ if ((oid->o_flags & OID_TABLE) && oid->o_get == NULL)
+ fatalx("mps_mibtree: invalid MIB table");
+ RB_INSERT(oidtree, &mps_oidtree, oid);
+ }
+}
+
+int
+mps_init(void)
+{
+ RB_INIT(&mps_oidtree);
+ mib_init();
+ return (0);
+}
+
+struct oid *
+mps_foreach(struct oid *oid, u_int flags)
+{
+ /*
+ * Traverse the tree of MIBs with the option to check
+ * for specific OID flags.
+ */
+ if (oid == NULL) {
+ oid = RB_MIN(oidtree, &mps_oidtree);
+ if (oid == NULL)
+ return (NULL);
+ if (flags == 0 || (oid->o_flags & flags))
+ return (oid);
+ }
+ for (;;) {
+ oid = RB_NEXT(oidtree, &mps_oidtree, oid);
+ if (oid == NULL)
+ break;
+ if (flags == 0 || (oid->o_flags & flags))
+ return (oid);
+ }
+
+ return (oid);
+}
+
+long
+mps_oid_cmp(struct oid *a, struct oid *b)
+{
+ size_t i;
+
+ for (i = 0; i < MIN(a->o_oidlen, b->o_oidlen); i++)
+ if (a->o_oid[i] != b->o_oid[i])
+ return (a->o_oid[i] - b->o_oid[i]);
+
+ /*
+ * Return success if the matched object is a table
+ * (it will match any sub-elements)
+ */
+ if ((b->o_flags & OID_TABLE) &&
+ (a->o_flags & OID_TABLE) == 0)
+ return (0);
+
+ return (a->o_oidlen - b->o_oidlen);
+}
+
+RB_GENERATE(oidtree, oid, o_element, mps_oid_cmp);
diff --git a/usr.sbin/snmpd/parse.y b/usr.sbin/snmpd/parse.y
new file mode 100644
index 00000000000..d7ac346d6bd
--- /dev/null
+++ b/usr.sbin/snmpd/parse.y
@@ -0,0 +1,936 @@
+/* $OpenBSD: parse.y,v 1.1 2007/12/05 09:22:44 reyk Exp $ */
+
+/*
+ * Copyright (c) 2007 Reyk Floeter <reyk@vantronix.net>
+ * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
+ * Copyright (c) 2004 Ryan McBride <mcbride@openbsd.org>
+ * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org>
+ * Copyright (c) 2001 Markus Friedl. All rights reserved.
+ * Copyright (c) 2001 Daniel Hartmeier. All rights reserved.
+ * Copyright (c) 2001 Theo de Raadt. All rights reserved.
+ *
+ * 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/types.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/queue.h>
+#include <sys/tree.h>
+
+#include <netinet/in.h>
+#include <net/if.h>
+
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+
+#include <ctype.h>
+#include <unistd.h>
+#include <err.h>
+#include <errno.h>
+#include <event.h>
+#include <limits.h>
+#include <stdint.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+
+#include "snmpd.h"
+#include "mib.h"
+
+TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files);
+static struct file {
+ TAILQ_ENTRY(file) entry;
+ FILE *stream;
+ char *name;
+ int lineno;
+ int errors;
+} *file;
+struct file *pushfile(const char *, int);
+int popfile(void);
+int check_file_secrecy(int, const char *);
+int yyparse(void);
+int yylex(void);
+int yyerror(const char *, ...);
+int kw_cmp(const void *, const void *);
+int lookup(char *);
+int lgetc(int);
+int lungetc(int);
+int findeol(void);
+
+TAILQ_HEAD(symhead, sym) symhead = TAILQ_HEAD_INITIALIZER(symhead);
+struct sym {
+ TAILQ_ENTRY(sym) entry;
+ int used;
+ int persist;
+ char *nam;
+ char *val;
+};
+int symset(const char *, const char *, int);
+char *symget(const char *);
+
+struct snmpd *conf = NULL;
+static int errors = 0;
+
+struct address *host_v4(const char *);
+struct address *host_v6(const char *);
+int host_dns(const char *, struct addresslist *,
+ int, in_port_t, const char *);
+int host(const char *, struct addresslist *,
+ int, in_port_t, const char *);
+
+typedef struct {
+ union {
+ int64_t number;
+ char *string;
+ struct host *host;
+ struct timeval tv;
+ struct ber_oid *oid;
+ struct {
+ int type;
+ void *data;
+ long long value;
+ } data;
+ } v;
+ int lineno;
+} YYSTYPE;
+
+%}
+
+%token INCLUDE
+%token LISTEN ON
+%token SYSTEM CONTACT DESCR LOCATION NAME OBJECTID SERVICES
+%token READONLY READWRITE OCTETSTRING INTEGER COMMUNITY TRAP
+%token ERROR
+%token <v.string> STRING
+%token <v.number> NUMBER
+%type <v.number> optwrite
+%type <v.data> objtype
+%type <v.oid> oid
+
+%%
+
+grammar : /* empty */
+ | grammar include '\n'
+ | grammar '\n'
+ | grammar varset '\n'
+ | grammar main '\n'
+ | grammar system '\n'
+ | grammar mib '\n'
+ | grammar error '\n' { file->errors++; }
+ ;
+
+include : INCLUDE STRING {
+ struct file *nfile;
+
+ if ((nfile = pushfile($2, 0)) == NULL) {
+ yyerror("failed to include file %s", $2);
+ free($2);
+ YYERROR;
+ }
+ free($2);
+
+ file = nfile;
+ lungetc('\n');
+ }
+
+varset : STRING '=' STRING {
+ if (symset($1, $3, 0) == -1)
+ fatal("cannot store variable");
+ free($1);
+ free($3);
+ }
+ ;
+
+main : LISTEN ON STRING {
+ struct addresslist al;
+ struct address *h;
+
+ TAILQ_INIT(&al);
+ if (host($3, &al, 1, SNMPD_PORT, NULL) <= 0) {
+ yyerror("invalid ip address: %s", $3);
+ free($3);
+ YYERROR;
+ }
+ free($3);
+ h = TAILQ_FIRST(&al);
+ bcopy(&h->ss, &conf->sc_address.ss, sizeof(*h));
+ conf->sc_address.port = h->port;
+ }
+ | READONLY COMMUNITY STRING {
+ if (strlcpy(conf->sc_rdcommunity, $3,
+ sizeof(conf->sc_rdcommunity)) >=
+ sizeof(conf->sc_rdcommunity)) {
+ yyerror("r/o community name too long");
+ free($3);
+ YYERROR;
+ }
+ free($3);
+ }
+ | READWRITE COMMUNITY STRING {
+ if (strlcpy(conf->sc_rwcommunity, $3,
+ sizeof(conf->sc_rwcommunity)) >=
+ sizeof(conf->sc_rwcommunity)) {
+ yyerror("r/w community name too long");
+ free($3);
+ YYERROR;
+ }
+ free($3);
+ }
+ | TRAP COMMUNITY STRING {
+ if (strlcpy(conf->sc_trcommunity, $3,
+ sizeof(conf->sc_trcommunity)) >=
+ sizeof(conf->sc_trcommunity)) {
+ yyerror("r/w community name too long");
+ free($3);
+ YYERROR;
+ }
+ free($3);
+ }
+ ;
+
+system : SYSTEM sysmib
+ ;
+
+sysmib : CONTACT STRING {
+ struct ber_oid o = OID(MIB_SYSCONTACT);
+ mps_set(&o, $2, strlen($2));
+ }
+ | DESCR STRING {
+ struct ber_oid o = OID(MIB_SYSDESCR);
+ mps_set(&o, $2, strlen($2));
+ }
+ | LOCATION STRING {
+ struct ber_oid o = OID(MIB_SYSLOCATION);
+ mps_set(&o, $2, strlen($2));
+ }
+ | NAME STRING {
+ struct ber_oid o = OID(MIB_SYSNAME);
+ mps_set(&o, $2, strlen($2));
+ }
+ | OBJECTID oid {
+ struct ber_oid o = OID(MIB_SYSOID);
+ mps_set(&o, $2, sizeof(struct ber_oid));
+ }
+ | SERVICES NUMBER {
+ struct ber_oid o = OID(MIB_SYSSERVICES);
+ mps_set(&o, NULL, $2);
+ }
+ ;
+
+mib : OBJECTID oid NAME STRING optwrite objtype {
+ struct oid *oid;
+ if ((oid = (struct oid *)
+ calloc(1, sizeof(*oid))) == NULL) {
+ yyerror("calloc");
+ free($2);
+ free($6.data);
+ YYERROR;
+ }
+
+ mps_oidlen($2);
+ bcopy($2, &oid->o_id, sizeof(struct ber_oid));
+ free($2);
+ oid->o_name = $4;
+ oid->o_data = $6.data;
+ oid->o_val = $6.value;
+ switch ($6.type) {
+ case 1:
+ oid->o_get = mps_getint;
+ oid->o_set = mps_setint;
+ break;
+ case 2:
+ oid->o_get = mps_getstr;
+ oid->o_set = mps_setstr;
+ break;
+ }
+ oid->o_flags = OID_RD|OID_DYNAMIC;
+ if ($5)
+ oid->o_flags |= OID_WR;
+
+ mps_insert(oid);
+ }
+ ;
+
+objtype : INTEGER NUMBER {
+ $$.type = 1;
+ $$.data = NULL;
+ $$.value = $2;
+ }
+ | OCTETSTRING STRING {
+ $$.type = 2;
+ $$.data = $2;
+ $$.value = strlen($2);
+ }
+ ;
+
+optwrite : READONLY { $$ = 0; }
+ | READWRITE { $$ = 1; }
+ ;
+
+oid : STRING {
+ struct ber_oid *sysoid;
+ if ((sysoid =
+ calloc(1, sizeof(*sysoid))) == NULL) {
+ yyerror("calloc");
+ free($1);
+ YYERROR;
+ }
+ if (ber_string2oid($1, sysoid) == -1) {
+ yyerror("invalid OID: %s", $1);
+ free(sysoid);
+ free($1);
+ YYERROR;
+ }
+ free($1);
+ $$ = sysoid;
+ }
+ ;
+
+comma : ','
+ | /* empty */
+ ;
+
+optnl : '\n' optnl
+ |
+ ;
+
+nl : '\n' optnl
+ ;
+
+%%
+
+struct keywords {
+ const char *k_name;
+ int k_val;
+};
+
+int
+yyerror(const char *fmt, ...)
+{
+ va_list ap;
+
+ file->errors++;
+ va_start(ap, fmt);
+ fprintf(stderr, "%s:%d: ", file->name, yylval.lineno);
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+ va_end(ap);
+ return (0);
+}
+
+int
+kw_cmp(const void *k, const void *e)
+{
+ return (strcmp(k, ((const struct keywords *)e)->k_name));
+}
+
+int
+lookup(char *s)
+{
+ /* this has to be sorted always */
+ static const struct keywords keywords[] = {
+ { "community", COMMUNITY },
+ { "contact", CONTACT },
+ { "description", DESCR },
+ { "include", INCLUDE },
+ { "integer", INTEGER },
+ { "listen", LISTEN },
+ { "location", LOCATION },
+ { "name", NAME },
+ { "oid", OBJECTID },
+ { "on", ON },
+ { "read-only", READONLY },
+ { "read-write", READWRITE },
+ { "services", SERVICES },
+ { "string", OCTETSTRING },
+ { "system", SYSTEM },
+ { "trap", TRAP }
+ };
+ const struct keywords *p;
+
+ p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
+ sizeof(keywords[0]), kw_cmp);
+
+ if (p)
+ return (p->k_val);
+ else
+ return (STRING);
+}
+
+#define MAXPUSHBACK 128
+
+char *parsebuf;
+int parseindex;
+char pushback_buffer[MAXPUSHBACK];
+int pushback_index = 0;
+
+int
+lgetc(int quotec)
+{
+ int c, next;
+
+ if (parsebuf) {
+ /* Read character from the parsebuffer instead of input. */
+ if (parseindex >= 0) {
+ c = parsebuf[parseindex++];
+ if (c != '\0')
+ return (c);
+ parsebuf = NULL;
+ } else
+ parseindex++;
+ }
+
+ if (pushback_index)
+ return (pushback_buffer[--pushback_index]);
+
+ if (quotec) {
+ if ((c = getc(file->stream)) == EOF) {
+ yyerror("reached end of file while parsing quoted string");
+ if (popfile() == EOF)
+ return (EOF);
+ return (quotec);
+ }
+ return (c);
+ }
+
+ while ((c = getc(file->stream)) == '\\') {
+ next = getc(file->stream);
+ if (next != '\n') {
+ c = next;
+ break;
+ }
+ yylval.lineno = file->lineno;
+ file->lineno++;
+ }
+ if (c == '\t' || c == ' ') {
+ /* Compress blanks to a single space. */
+ do {
+ c = getc(file->stream);
+ } while (c == '\t' || c == ' ');
+ ungetc(c, file->stream);
+ c = ' ';
+ }
+
+ while (c == EOF) {
+ if (popfile() == EOF)
+ return (EOF);
+ c = getc(file->stream);
+ }
+ return (c);
+}
+
+int
+lungetc(int c)
+{
+ if (c == EOF)
+ return (EOF);
+ if (parsebuf) {
+ parseindex--;
+ if (parseindex >= 0)
+ return (c);
+ }
+ if (pushback_index < MAXPUSHBACK-1)
+ return (pushback_buffer[pushback_index++] = c);
+ else
+ return (EOF);
+}
+
+int
+findeol(void)
+{
+ int c;
+
+ parsebuf = NULL;
+ pushback_index = 0;
+
+ /* skip to either EOF or the first real EOL */
+ while (1) {
+ c = lgetc(0);
+ if (c == '\n') {
+ file->lineno++;
+ break;
+ }
+ if (c == EOF)
+ break;
+ }
+ return (ERROR);
+}
+
+int
+yylex(void)
+{
+ char buf[8096];
+ char *p, *val;
+ int quotec, next, c;
+ int token;
+
+top:
+ p = buf;
+ while ((c = lgetc(0)) == ' ')
+ ; /* nothing */
+
+ yylval.lineno = file->lineno;
+ if (c == '#')
+ while ((c = lgetc(0)) != '\n' && c != EOF)
+ ; /* nothing */
+ if (c == '$' && parsebuf == NULL) {
+ while (1) {
+ if ((c = lgetc(0)) == EOF)
+ return (0);
+
+ if (p + 1 >= buf + sizeof(buf) - 1) {
+ yyerror("string too long");
+ return (findeol());
+ }
+ if (isalnum(c) || c == '_') {
+ *p++ = (char)c;
+ continue;
+ }
+ *p = '\0';
+ lungetc(c);
+ break;
+ }
+ val = symget(buf);
+ if (val == NULL) {
+ yyerror("macro '%s' not defined", buf);
+ return (findeol());
+ }
+ parsebuf = val;
+ parseindex = 0;
+ goto top;
+ }
+
+ switch (c) {
+ case '\'':
+ case '"':
+ quotec = c;
+ while (1) {
+ if ((c = lgetc(quotec)) == EOF)
+ return (0);
+ if (c == '\n') {
+ file->lineno++;
+ continue;
+ } else if (c == '\\') {
+ if ((next = lgetc(quotec)) == EOF)
+ return (0);
+ if (next == quotec || c == ' ' || c == '\t')
+ c = next;
+ else if (next == '\n')
+ continue;
+ else
+ lungetc(next);
+ } else if (c == quotec) {
+ *p = '\0';
+ break;
+ }
+ if (p + 1 >= buf + sizeof(buf) - 1) {
+ yyerror("string too long");
+ return (findeol());
+ }
+ *p++ = (char)c;
+ }
+ yylval.v.string = strdup(buf);
+ if (yylval.v.string == NULL)
+ err(1, "yylex: strdup");
+ return (STRING);
+ }
+
+#define allowed_to_end_number(x) \
+ (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=')
+
+ if (c == '-' || isdigit(c)) {
+ do {
+ *p++ = c;
+ if ((unsigned)(p-buf) >= sizeof(buf)) {
+ yyerror("string too long");
+ return (findeol());
+ }
+ } while ((c = lgetc(0)) != EOF && isdigit(c));
+ lungetc(c);
+ if (p == buf + 1 && buf[0] == '-')
+ goto nodigits;
+ if (c == EOF || allowed_to_end_number(c)) {
+ const char *errstr = NULL;
+
+ *p = '\0';
+ yylval.v.number = strtonum(buf, LLONG_MIN,
+ LLONG_MAX, &errstr);
+ if (errstr) {
+ yyerror("\"%s\" invalid number: %s",
+ buf, errstr);
+ return (findeol());
+ }
+ return (NUMBER);
+ } else {
+nodigits:
+ while (p > buf + 1)
+ lungetc(*--p);
+ c = *--p;
+ if (c == '-')
+ return (c);
+ }
+ }
+
+#define allowed_in_string(x) \
+ (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
+ x != '{' && x != '}' && \
+ x != '!' && x != '=' && x != '#' && \
+ x != ','))
+
+ if (isalnum(c) || c == ':' || c == '_') {
+ do {
+ *p++ = c;
+ if ((unsigned)(p-buf) >= sizeof(buf)) {
+ yyerror("string too long");
+ return (findeol());
+ }
+ } while ((c = lgetc(0)) != EOF && (allowed_in_string(c)));
+ lungetc(c);
+ *p = '\0';
+ if ((token = lookup(buf)) == STRING)
+ if ((yylval.v.string = strdup(buf)) == NULL)
+ err(1, "yylex: strdup");
+ return (token);
+ }
+ if (c == '\n') {
+ yylval.lineno = file->lineno;
+ file->lineno++;
+ }
+ if (c == EOF)
+ return (0);
+ return (c);
+}
+
+int
+check_file_secrecy(int fd, const char *fname)
+{
+ struct stat st;
+
+ if (fstat(fd, &st)) {
+ log_warn("cannot stat %s", fname);
+ return (-1);
+ }
+ if (st.st_uid != 0 && st.st_uid != getuid()) {
+ log_warnx("%s: owner not root or current user", fname);
+ return (-1);
+ }
+ if (st.st_mode & (S_IRWXG | S_IRWXO)) {
+ log_warnx("%s: group/world readable/writeable", fname);
+ return (-1);
+ }
+ return (0);
+}
+
+struct file *
+pushfile(const char *name, int secret)
+{
+ struct file *nfile;
+
+ if ((nfile = calloc(1, sizeof(struct file))) == NULL ||
+ (nfile->name = strdup(name)) == NULL) {
+ log_warn("malloc");
+ return (NULL);
+ }
+ if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
+ log_warn("%s", nfile->name);
+ free(nfile->name);
+ free(nfile);
+ return (NULL);
+ } else if (secret &&
+ check_file_secrecy(fileno(nfile->stream), nfile->name)) {
+ fclose(nfile->stream);
+ free(nfile->name);
+ free(nfile);
+ return (NULL);
+ }
+ nfile->lineno = 1;
+ TAILQ_INSERT_TAIL(&files, nfile, entry);
+ return (nfile);
+}
+
+int
+popfile(void)
+{
+ struct file *prev;
+
+ if ((prev = TAILQ_PREV(file, files, entry)) != NULL) {
+ prev->errors += file->errors;
+ TAILQ_REMOVE(&files, file, entry);
+ fclose(file->stream);
+ free(file->name);
+ free(file);
+ file = prev;
+ return (0);
+ }
+ return (EOF);
+}
+
+struct snmpd *
+parse_config(const char *filename, u_int flags)
+{
+ struct sym *sym, *next;
+
+ if ((conf = calloc(1, sizeof(*conf))) == NULL) {
+ log_warn("cannot allocate memory");
+ return (NULL);
+ }
+
+ conf->sc_flags = flags;
+ conf->sc_confpath = filename;
+ conf->sc_address.ss.ss_family = AF_INET;
+ conf->sc_address.port = htons(SNMPD_PORT);
+ strlcpy(conf->sc_rdcommunity, "public", SNMPD_MAXCOMMUNITYLEN);
+ strlcpy(conf->sc_rwcommunity, "private", SNMPD_MAXCOMMUNITYLEN);
+ strlcpy(conf->sc_trcommunity, "public", SNMPD_MAXCOMMUNITYLEN);
+
+ if ((file = pushfile(filename, 0)) == NULL) {
+ free(conf);
+ return (NULL);
+ }
+ setservent(1);
+
+ yyparse();
+ errors = file->errors;
+ popfile();
+
+ endservent();
+
+ /* Free macros and check which have not been used. */
+ for (sym = TAILQ_FIRST(&symhead); sym != NULL; sym = next) {
+ next = TAILQ_NEXT(sym, entry);
+ if ((conf->sc_flags & SNMPD_F_VERBOSE) && !sym->used)
+ fprintf(stderr, "warning: macro '%s' not "
+ "used\n", sym->nam);
+ if (!sym->persist) {
+ free(sym->nam);
+ free(sym->val);
+ TAILQ_REMOVE(&symhead, sym, entry);
+ free(sym);
+ }
+ }
+
+ if (errors) {
+ free(conf);
+ return (NULL);
+ }
+
+ return (conf);
+}
+
+int
+symset(const char *nam, const char *val, int persist)
+{
+ struct sym *sym;
+
+ for (sym = TAILQ_FIRST(&symhead); sym && strcmp(nam, sym->nam);
+ sym = TAILQ_NEXT(sym, entry))
+ ; /* nothing */
+
+ if (sym != NULL) {
+ if (sym->persist == 1)
+ return (0);
+ else {
+ free(sym->nam);
+ free(sym->val);
+ TAILQ_REMOVE(&symhead, sym, entry);
+ free(sym);
+ }
+ }
+ if ((sym = calloc(1, sizeof(*sym))) == NULL)
+ return (-1);
+
+ sym->nam = strdup(nam);
+ if (sym->nam == NULL) {
+ free(sym);
+ return (-1);
+ }
+ sym->val = strdup(val);
+ if (sym->val == NULL) {
+ free(sym->nam);
+ free(sym);
+ return (-1);
+ }
+ sym->used = 0;
+ sym->persist = persist;
+ TAILQ_INSERT_TAIL(&symhead, sym, entry);
+ return (0);
+}
+
+int
+cmdline_symset(char *s)
+{
+ char *sym, *val;
+ int ret;
+ size_t len;
+
+ if ((val = strrchr(s, '=')) == NULL)
+ return (-1);
+
+ len = strlen(s) - strlen(val) + 1;
+ if ((sym = malloc(len)) == NULL)
+ errx(1, "cmdline_symset: malloc");
+
+ (void)strlcpy(sym, s, len);
+
+ ret = symset(sym, val + 1, 1);
+ free(sym);
+
+ return (ret);
+}
+
+char *
+symget(const char *nam)
+{
+ struct sym *sym;
+
+ TAILQ_FOREACH(sym, &symhead, entry)
+ if (strcmp(nam, sym->nam) == 0) {
+ sym->used = 1;
+ return (sym->val);
+ }
+ return (NULL);
+}
+
+struct address *
+host_v4(const char *s)
+{
+ struct in_addr ina;
+ struct sockaddr_in *sain;
+ struct address *h;
+
+ bzero(&ina, sizeof(ina));
+ if (inet_pton(AF_INET, s, &ina) != 1)
+ return (NULL);
+
+ if ((h = calloc(1, sizeof(*h))) == NULL)
+ fatal(NULL);
+ sain = (struct sockaddr_in *)&h->ss;
+ sain->sin_len = sizeof(struct sockaddr_in);
+ sain->sin_family = AF_INET;
+ sain->sin_addr.s_addr = ina.s_addr;
+
+ return (h);
+}
+
+struct address *
+host_v6(const char *s)
+{
+ struct in6_addr ina6;
+ struct sockaddr_in6 *sin6;
+ struct address *h;
+
+ bzero(&ina6, sizeof(ina6));
+ if (inet_pton(AF_INET6, s, &ina6) != 1)
+ return (NULL);
+
+ if ((h = calloc(1, sizeof(*h))) == NULL)
+ fatal(NULL);
+ sin6 = (struct sockaddr_in6 *)&h->ss;
+ sin6->sin6_len = sizeof(struct sockaddr_in6);
+ sin6->sin6_family = AF_INET6;
+ memcpy(&sin6->sin6_addr, &ina6, sizeof(ina6));
+
+ return (h);
+}
+
+int
+host_dns(const char *s, struct addresslist *al, int max,
+ in_port_t port, const char *ifname)
+{
+ struct addrinfo hints, *res0, *res;
+ int error, cnt = 0;
+ struct sockaddr_in *sain;
+ struct sockaddr_in6 *sin6;
+ struct address *h;
+
+ bzero(&hints, sizeof(hints));
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_DGRAM; /* DUMMY */
+ error = getaddrinfo(s, NULL, &hints, &res0);
+ if (error == EAI_AGAIN || error == EAI_NODATA || error == EAI_NONAME)
+ return (0);
+ if (error) {
+ log_warnx("host_dns: could not parse \"%s\": %s", s,
+ gai_strerror(error));
+ return (-1);
+ }
+
+ for (res = res0; res && cnt < max; res = res->ai_next) {
+ if (res->ai_family != AF_INET &&
+ res->ai_family != AF_INET6)
+ continue;
+ if ((h = calloc(1, sizeof(*h))) == NULL)
+ fatal(NULL);
+
+ h->port = port;
+ if (ifname != NULL) {
+ if (strlcpy(h->ifname, ifname, sizeof(h->ifname)) >=
+ sizeof(h->ifname))
+ log_warnx("host_dns: interface name truncated");
+ return (-1);
+ }
+ h->ss.ss_family = res->ai_family;
+ if (res->ai_family == AF_INET) {
+ sain = (struct sockaddr_in *)&h->ss;
+ sain->sin_len = sizeof(struct sockaddr_in);
+ sain->sin_addr.s_addr = ((struct sockaddr_in *)
+ res->ai_addr)->sin_addr.s_addr;
+ } else {
+ sin6 = (struct sockaddr_in6 *)&h->ss;
+ sin6->sin6_len = sizeof(struct sockaddr_in6);
+ memcpy(&sin6->sin6_addr, &((struct sockaddr_in6 *)
+ res->ai_addr)->sin6_addr, sizeof(struct in6_addr));
+ }
+
+ TAILQ_INSERT_HEAD(al, h, entry);
+ cnt++;
+ }
+ if (cnt == max && res) {
+ log_warnx("host_dns: %s resolves to more than %d hosts",
+ s, max);
+ }
+ freeaddrinfo(res0);
+ return (cnt);
+}
+
+int
+host(const char *s, struct addresslist *al, int max,
+ in_port_t port, const char *ifname)
+{
+ struct address *h;
+
+ h = host_v4(s);
+
+ /* IPv6 address? */
+ if (h == NULL)
+ h = host_v6(s);
+
+ if (h != NULL) {
+ h->port = port;
+ if (ifname != NULL) {
+ if (strlcpy(h->ifname, ifname, sizeof(h->ifname)) >=
+ sizeof(h->ifname)) {
+ log_warnx("host: interface name truncated");
+ return (-1);
+ }
+ }
+
+ TAILQ_INSERT_HEAD(al, h, entry);
+ return (1);
+ }
+
+ return (host_dns(s, al, max, port, ifname));
+}
diff --git a/usr.sbin/snmpd/snmp.h b/usr.sbin/snmpd/snmp.h
new file mode 100644
index 00000000000..5bb21f7e199
--- /dev/null
+++ b/usr.sbin/snmpd/snmp.h
@@ -0,0 +1,86 @@
+/* $OpenBSD: snmp.h,v 1.1 2007/12/05 09:22:44 reyk Exp $ */
+
+/*
+ * Copyright (c) 2007 Reyk Floeter <reyk@vantronix.net>
+ *
+ * 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.
+ */
+
+#ifndef SNMP_HEADER
+#define SNMP_HEADER
+
+enum snmp_version {
+ SNMP_V1 = 0,
+ SNMP_V2 = 1,
+ SNMP_V3 = 2
+};
+
+enum snmp_context {
+ SNMP_T_GETREQ = 0,
+ SNMP_T_GETNEXTREQ = 1,
+ SNMP_T_GETRESP = 2,
+ SNMP_T_SETREQ = 3,
+ SNMP_T_TRAP = 4,
+
+ /* SNMPv2 */
+ SNMP_T_GETBULKREQ = 5,
+ SNMP_T_INFORMREQ = 6,
+ SNMP_T_TRAPV2 = 7,
+ SNMP_T_REPORT = 8
+};
+
+enum snmp_application {
+ SNMP_T_IPADDR = 0,
+ SNMP_T_COUNTER32 = 1,
+ SNMP_T_GAUGE32 = 2,
+ SNMP_T_UNSIGNED32 = 2,
+ SNMP_T_TIMETICKS = 3,
+ SNMP_T_OPAQUE = 4,
+ SNMP_T_COUNTER64 = 6,
+};
+
+enum snmp_generic_trap {
+ SNMP_TRAP_COLDSTART = 0,
+ SNMP_TRAP_WARMSTART = 1,
+ SNMP_TRAP_LINKDOWN = 2,
+ SNMP_TRAP_LINKUP = 3,
+ SNMP_TRAP_AUTHFAILURE = 4,
+ SNMP_TRAP_EGPNEIGHLOSS = 5,
+ SNMP_TRAP_ENTERPRISE = 6
+};
+
+enum snmp_error {
+ SNMP_ERROR_NONE = 0,
+ SNMP_ERROR_TOOBIG = 1,
+ SNMP_ERROR_NOSUCHNAME = 2,
+ SNMP_ERROR_BADVALUE = 3,
+ SNMP_ERROR_READONLY = 4,
+ SNMP_ERROR_GENERR = 5,
+
+ /* SNMPv2 */
+ SNMP_ERROR_NOACCESS = 6,
+ SNMP_ERROR_WRONGTYPE = 7,
+ SNMP_ERROR_WRONGLENGTH = 8,
+ SNMP_ERROR_WRONGENC = 9,
+ SNMP_ERROR_WRONGVALUE = 10,
+ SNMP_ERROR_NOCREATION = 11,
+ SNMP_ERROR_INCONVALUE = 12,
+ SNMP_ERROR_RESUNAVAIL = 13, /* EGAIN */
+ SNMP_ERROR_COMMITFAILED = 14,
+ SNMP_ERROR_UNDOFAILED = 15,
+ SNMP_ERROR_AUTHERROR = 16,
+ SNMP_ERROR_NOTWRITABLE = 17,
+ SNMP_ERROR_INCONNAME = 18
+};
+
+#endif /* SNMP_HEADER */
diff --git a/usr.sbin/snmpd/snmpd.8 b/usr.sbin/snmpd/snmpd.8
new file mode 100644
index 00000000000..c0e5e022146
--- /dev/null
+++ b/usr.sbin/snmpd/snmpd.8
@@ -0,0 +1,93 @@
+.\" $OpenBSD: snmpd.8,v 1.1 2007/12/05 09:22:44 reyk Exp $
+.\"
+.\" Copyright (c) 2007 Reyk Floeter <reyk@vantronix.net>
+.\"
+.\" 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.
+.\"
+.Dd $Mdocdate: December 5 2007 $
+.Dt SNMPD 8
+.Os
+.Sh NAME
+.Nm snmpd
+.Nd Simple Network Management Protocol Daemon
+.Sh SYNOPSIS
+.Nm snmpd
+.Op Fl dv
+.Oo Xo
+.Fl D Ar macro Ns = Ns Ar value Oc
+.Xc
+.Op Fl f Ar file
+.Sh DESCRIPTION
+.Nm
+is a daemon which implement the SNMP protocol.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl D Ar macro Ns = Ns Ar value
+Define
+.Ar macro
+to be set to
+.Ar value
+on the command line.
+Overrides the definition of
+.Ar macro
+in the configuration file.
+.It Fl d
+Do not daemonize and log to
+.Em stderr .
+.It Fl f Ar file
+Use
+.Ar file
+as the configuration file, instead of the default
+.Pa /etc/snmpd.conf .
+.It Fl v
+Produce more verbose output.
+.El
+.Sh FILES
+.Bl -tag -width "/etc/snmpd.confXXX" -compact
+.It Pa /etc/snmpd.conf
+default
+.Nm
+configuration file
+.El
+.Sh SEE ALSO
+.Xr snmpd.conf 5 ,
+.Rs
+.%R RFC 1157
+.%T A Simple Network Management Protocol (SNMP)
+.%D May 1990
+.Re
+.Rs
+.%R http://www.ibr.cs.tu-bs.de/projects/snmpv3/
+.%T SNMP Version 3 (SNMPv3)
+.%D March 2002
+.Re
+.Rs
+.%R RFC 3410
+.%T Introduction and Applicability Statements for Internet Standard Management Framework
+.%D December 2002
+.Re
+.Sh HISTORY
+The
+.Nm
+file format first appeared in
+.Ox 4.3 .
+.Sh AUTHORS
+The
+.Nm
+program was written by
+.An Reyk Floeter Aq reyk@vantronix.net .
+.Sh CAVEATS
+The
+.Nm
+does not fully work yet.
diff --git a/usr.sbin/snmpd/snmpd.c b/usr.sbin/snmpd/snmpd.c
new file mode 100644
index 00000000000..292c9d9ae42
--- /dev/null
+++ b/usr.sbin/snmpd/snmpd.c
@@ -0,0 +1,295 @@
+/* $OpenBSD: snmpd.c,v 1.1 2007/12/05 09:22:44 reyk Exp $ */
+
+/*
+ * Copyright (c) 2007 Reyk Floeter <reyk@vantronix.net>
+ *
+ * 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/types.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/param.h>
+#include <sys/wait.h>
+#include <sys/tree.h>
+
+#include <net/if.h>
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <err.h>
+#include <errno.h>
+#include <event.h>
+#include <signal.h>
+#include <unistd.h>
+#include <pwd.h>
+
+#include "snmpd.h"
+
+__dead void usage(void);
+
+void snmpd_sig_handler(int, short, void *);
+void snmpd_shutdown(struct snmpd *);
+void snmpd_dispatch_snmpe(int, short, void *);
+int check_child(pid_t, const char *);
+
+struct snmpd *snmpd_env;
+
+int pipe_parent2snmpe[2];
+struct imsgbuf *ibuf_snmpe;
+pid_t snmpe_pid = 0;
+
+void
+snmpd_sig_handler(int sig, short event, void *arg)
+{
+ struct snmpd *env = arg;
+ int die = 0;
+
+ switch (sig) {
+ case SIGTERM:
+ case SIGINT:
+ die = 1;
+ /* FALLTHROUGH */
+ case SIGCHLD:
+ if (check_child(snmpe_pid, "snmp engine")) {
+ snmpe_pid = 0;
+ die = 1;
+ }
+ if (die)
+ snmpd_shutdown(env);
+ break;
+ case SIGHUP:
+ /* reconfigure */
+ break;
+ default:
+ fatalx("unexpected signal");
+ }
+}
+
+/* __dead is for lint */
+__dead void
+usage(void)
+{
+ extern char *__progname;
+
+ fprintf(stderr, "%s [-dnNv] [-D macro=value] [-f file]\n", __progname);
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int c;
+ struct snmpd *env;
+ struct event ev_sigint;
+ struct event ev_sigterm;
+ struct event ev_sigchld;
+ struct event ev_sighup;
+ int debug = 0;
+ u_int flags = 0;
+ int noaction = 0;
+ const char *conffile = CONF_FILE;
+
+ mps_init();
+
+ log_init(1); /* log to stderr until daemonized */
+
+ while ((c = getopt(argc, argv, "dD:nNf:v")) != -1) {
+ switch (c) {
+ case 'd':
+ debug = 1;
+ break;
+ case 'D':
+ if (cmdline_symset(optarg) < 0)
+ log_warnx("could not parse macro definition %s",
+ optarg);
+ break;
+ case 'n':
+ noaction++;
+ break;
+ case 'N':
+ flags |= SNMPD_F_NONAMES;
+ break;
+ case 'f':
+ conffile = optarg;
+ break;
+ case 'v':
+ flags |= SNMPD_F_VERBOSE;
+ break;
+ default:
+ usage();
+ }
+ }
+
+ if ((env = parse_config(conffile, flags)) == NULL)
+ exit(1);
+ snmpd_env = env;
+
+ if (noaction) {
+ fprintf(stderr, "configuration ok\n");
+ exit(0);
+ }
+
+ if (geteuid())
+ errx(1, "need root privileges");
+
+ if (getpwnam(SNMPD_USER) == NULL)
+ errx(1, "unknown user %s", SNMPD_USER);
+
+ log_init(debug);
+
+ if (!debug) {
+ if (daemon(1, 0) == -1)
+ err(1, "failed to daemonize");
+ }
+
+ gettimeofday(&env->sc_starttime, NULL);
+
+ log_info("startup");
+
+ if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC,
+ pipe_parent2snmpe) == -1)
+ fatal("socketpair");
+
+ session_socket_blockmode(pipe_parent2snmpe[0], BM_NONBLOCK);
+ session_socket_blockmode(pipe_parent2snmpe[1], BM_NONBLOCK);
+
+ snmpe_pid = snmpe(env, pipe_parent2snmpe);
+ setproctitle("parent");
+
+ event_init();
+
+ signal_set(&ev_sigint, SIGINT, snmpd_sig_handler, env);
+ signal_set(&ev_sigterm, SIGTERM, snmpd_sig_handler, env);
+ signal_set(&ev_sigchld, SIGCHLD, snmpd_sig_handler, env);
+ signal_set(&ev_sighup, SIGHUP, snmpd_sig_handler, env);
+ signal_add(&ev_sigint, NULL);
+ signal_add(&ev_sigterm, NULL);
+ signal_add(&ev_sigchld, NULL);
+ signal_add(&ev_sighup, NULL);
+ signal(SIGPIPE, SIG_IGN);
+
+ close(pipe_parent2snmpe[1]);
+
+ if ((ibuf_snmpe = calloc(1, sizeof(struct imsgbuf))) == NULL)
+ fatal(NULL);
+
+ imsg_init(ibuf_snmpe, pipe_parent2snmpe[0], snmpd_dispatch_snmpe);
+
+ ibuf_snmpe->events = EV_READ;
+ event_set(&ibuf_snmpe->ev, ibuf_snmpe->fd, ibuf_snmpe->events,
+ ibuf_snmpe->handler, ibuf_snmpe);
+ event_add(&ibuf_snmpe->ev, NULL);
+
+ event_dispatch();
+
+ return (0);
+}
+
+void
+snmpd_shutdown(struct snmpd *env)
+{
+ pid_t pid;
+
+ if (snmpe_pid)
+ kill(snmpe_pid, SIGTERM);
+
+ do {
+ if ((pid = wait(NULL)) == -1 &&
+ errno != EINTR && errno != ECHILD)
+ fatal("wait");
+ } while (pid != -1 || (pid == -1 && errno == EINTR));
+
+ control_cleanup();
+ log_info("terminating");
+ exit(0);
+}
+
+int
+check_child(pid_t pid, const char *pname)
+{
+ int status;
+
+ if (waitpid(pid, &status, WNOHANG) > 0) {
+ if (WIFEXITED(status)) {
+ log_warnx("check_child: lost child: %s exited", pname);
+ return (1);
+ }
+ if (WIFSIGNALED(status)) {
+ log_warnx("check_child: lost child: %s terminated; "
+ "signal %d", pname, WTERMSIG(status));
+ return (1);
+ }
+ }
+
+ return (0);
+}
+
+void
+imsg_event_add(struct imsgbuf *ibuf)
+{
+ ibuf->events = EV_READ;
+ if (ibuf->w.queued)
+ ibuf->events |= EV_WRITE;
+
+ event_del(&ibuf->ev);
+ event_set(&ibuf->ev, ibuf->fd, ibuf->events, ibuf->handler, ibuf);
+ event_add(&ibuf->ev, NULL);
+}
+
+void
+snmpd_dispatch_snmpe(int fd, short event, void * ptr)
+{
+ struct imsgbuf *ibuf;
+ struct imsg imsg;
+ ssize_t n;
+
+ ibuf = ptr;
+ switch (event) {
+ case EV_READ:
+ if ((n = imsg_read(ibuf)) == -1)
+ fatal("imsg_read error");
+ if (n == 0) {
+ /* this pipe is dead, so remove the event handler */
+ event_del(&ibuf->ev);
+ event_loopexit(NULL);
+ return;
+ }
+ break;
+ case EV_WRITE:
+ if (msgbuf_write(&ibuf->w) == -1)
+ fatal("msgbuf_write");
+ imsg_event_add(ibuf);
+ return;
+ default:
+ fatalx("unknown event");
+ }
+
+ for (;;) {
+ if ((n = imsg_get(ibuf, &imsg)) == -1)
+ fatal("snmpd_dispatch_relay: imsg_read error");
+ if (n == 0)
+ break;
+
+ switch (imsg.hdr.type) {
+ default:
+ log_debug("snmpd_dispatch_relay: unexpected imsg %d",
+ imsg.hdr.type);
+ break;
+ }
+ imsg_free(&imsg);
+ }
+ imsg_event_add(ibuf);
+}
diff --git a/usr.sbin/snmpd/snmpd.conf b/usr.sbin/snmpd/snmpd.conf
new file mode 100644
index 00000000000..5173b98dc2d
--- /dev/null
+++ b/usr.sbin/snmpd/snmpd.conf
@@ -0,0 +1,9 @@
+listen on 127.0.0.1
+
+system oid 1.3.6.1.4.1.26766.42.2.1.42
+system services 74
+
+oid 1.3.6.1.4.1.26766.42.3.1 name humppa read-only string "foobar"
+oid 1.3.6.1.4.1.26766.42.3.2 name jenka read-only string "jenka"
+oid 1.3.6.1.4.1.26766.42.3.3 name polka read-write string "polka"
+oid 1.3.6.1.4.1.26766.42.3.4 name walzer read-write integer 1
diff --git a/usr.sbin/snmpd/snmpd.conf.5 b/usr.sbin/snmpd/snmpd.conf.5
new file mode 100644
index 00000000000..5bb650dc022
--- /dev/null
+++ b/usr.sbin/snmpd/snmpd.conf.5
@@ -0,0 +1,83 @@
+.\" $OpenBSD: snmpd.conf.5,v 1.1 2007/12/05 09:22:44 reyk Exp $
+.\"
+.\" Copyright (c) 2007 Reyk Floeter <reyk@vantronix.net>
+.\"
+.\" 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.
+.\"
+.Dd $Mdocdate: December 5 2007 $
+.Dt SNMPD.CONF 5
+.Os
+.Sh NAME
+.Nm snmpd.conf
+.Nd configuration file for the SNMP daemon
+.Sh DESCRIPTION
+.Nm
+is the configuration file for the
+.Xr snmpd 8
+daemon.
+.Sh SECTIONS
+The
+.Nm
+file is divided into n main sections.
+.Bl -tag -width xxxx
+.It Sy Macros
+User-defined variables may be defined and used later, simplifying the
+configuration file.
+.It Sy Global Configuration
+Global runtime settings for
+.Xr snmpd 8 .
+.El
+.Pp
+Comments can be put anywhere in the file using a hash mark
+.Pq Sq # ,
+and extend to the end of the current line.
+.Pp
+Additional configuration files can be included with the
+.Ic include
+keyword, for example:
+.Bd -literal -offset indent
+include "/etc/snmpd.conf.local"
+.Ed
+.Sh MACROS
+Macros can be defined that will later be expanded in context.
+Macro names must start with a letter, and may contain letters, digits
+and underscores.
+Macro names may not be reserved words (for example,
+.Ic foo ,
+.Ic bar ,
+or
+.Ic baz ) .
+Macros are not expanded inside quotes.
+.Pp
+For example:
+.Bd -literal -offset indent
+var="blah"
+foo $var
+.Ed
+.Sh FILES
+.Bl -tag -width "/etc/snmpd.conf" -compact
+.It Pa /etc/snmpd.conf
+Default location of the configuration file.
+.El
+.Sh SEE ALSO
+.Xr snmpd 8
+.Sh HISTORY
+The
+.Nm
+file format first appeared in
+.Ox 4.3 .
+.Sh AUTHORS
+The
+.Xr snmpd 8
+program was written by
+.An Reyk Floeter Aq reyk@vantronix.net .
diff --git a/usr.sbin/snmpd/snmpd.h b/usr.sbin/snmpd/snmpd.h
new file mode 100644
index 00000000000..b8570b3ddaa
--- /dev/null
+++ b/usr.sbin/snmpd/snmpd.h
@@ -0,0 +1,407 @@
+/* $OpenBSD: snmpd.h,v 1.1 2007/12/05 09:22:44 reyk Exp $ */
+
+/*
+ * Copyright (c) 2007 Reyk Floeter <reyk@vantronix.net>
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@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.
+ */
+
+#ifndef _SNMPD_H
+#define _SNMPD_H
+
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#include <net/route.h>
+
+#include <ber.h>
+#include <snmp.h>
+
+/*
+ * common definitions for snmpd
+ */
+
+#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 SNMPD_MAXSTRLEN 484
+#define SNMPD_MAXCOMMUNITYLEN SNMPD_MAXSTRLEN
+#define SNMPD_MAXVARBINDLEN 1210
+
+#define SMALL_READ_BUF_SIZE 1024
+#define READ_BUF_SIZE 65535
+#define RT_BUF_SIZE 16384
+#define MAX_RTSOCK_BUF (128 * 1024)
+
+struct address {
+ struct sockaddr_storage ss;
+ in_port_t port;
+ char ifname[IFNAMSIZ];
+ TAILQ_ENTRY(address) entry;
+};
+TAILQ_HEAD(addresslist, address);
+
+/*
+ * imsg framework and privsep
+ */
+
+struct buf {
+ TAILQ_ENTRY(buf) entry;
+ u_char *buf;
+ size_t size;
+ size_t max;
+ size_t wpos;
+ size_t rpos;
+ int fd;
+};
+
+struct msgbuf {
+ TAILQ_HEAD(, buf) bufs;
+ u_int32_t queued;
+ int fd;
+};
+
+#define IMSG_HEADER_SIZE sizeof(struct imsg_hdr)
+#define MAX_IMSGSIZE 8192
+
+struct buf_read {
+ u_char buf[READ_BUF_SIZE];
+ u_char *rptr;
+ size_t wpos;
+};
+
+struct imsg_fd {
+ TAILQ_ENTRY(imsg_fd) entry;
+ int fd;
+};
+
+struct imsgbuf {
+ TAILQ_HEAD(, imsg_fd) fds;
+ struct buf_read r;
+ struct msgbuf w;
+ struct event ev;
+ void (*handler)(int, short, void *);
+ int fd;
+ pid_t pid;
+ short events;
+};
+
+enum imsg_type {
+ IMSG_NONE,
+ IMSG_CTL_OK, /* answer to snmpctl requests */
+ IMSG_CTL_FAIL,
+ IMSG_CTL_END,
+ IMSG_CTL_NOTIFY
+};
+
+struct imsg_hdr {
+ u_int16_t type;
+ u_int16_t len;
+ u_int32_t peerid;
+ pid_t pid;
+};
+
+struct imsg {
+ struct imsg_hdr hdr;
+ void *data;
+};
+
+enum {
+ PROC_PARENT, /* Parent process and application interface */
+ PROC_SNMPE /* SNMP engine */
+} snmpd_process;
+
+/* initially control.h */
+struct {
+ struct event ev;
+ int fd;
+} control_state;
+
+enum blockmodes {
+ BM_NORMAL,
+ BM_NONBLOCK
+};
+
+struct ctl_conn {
+ TAILQ_ENTRY(ctl_conn) entry;
+ u_int8_t flags;
+#define CTL_CONN_NOTIFY 0x01
+ struct imsgbuf ibuf;
+
+};
+TAILQ_HEAD(ctl_connlist, ctl_conn);
+extern struct ctl_connlist ctl_conns;
+
+/*
+ * kroute
+ */
+
+struct kroute {
+ struct in_addr prefix;
+ struct in_addr nexthop;
+ u_int16_t flags;
+ u_int16_t rtlabel;
+ u_short if_index;
+ u_int8_t prefixlen;
+ u_long ticks;
+};
+
+struct kif_addr {
+ TAILQ_ENTRY(kif_addr) entry;
+ struct in_addr addr;
+ struct in_addr mask;
+ struct in_addr dstbrd;
+};
+
+struct kif {
+ char if_name[IF_NAMESIZE];
+ char if_descr[IFDESCRSIZE];
+ u_int8_t if_lladdr[ETHER_ADDR_LEN];
+ int if_flags;
+ u_short if_index;
+ u_int8_t if_nhreachable; /* for nexthop verification */
+ u_long if_ticks;
+ struct if_data if_data;
+};
+
+#define F_OSPFD_INSERTED 0x0001
+#define F_KERNEL 0x0002
+#define F_BGPD_INSERTED 0x0004
+#define F_CONNECTED 0x0008
+#define F_DOWN 0x0010
+#define F_STATIC 0x0020
+#define F_DYNAMIC 0x0040
+#define F_REDISTRIBUTED 0x0100
+
+/*
+ * Message Processing Subsystem (mps)
+ */
+
+struct oid {
+ struct ber_oid o_id;
+#define o_oid o_id.bo_id
+#define o_oidlen o_id.bo_n
+
+ char *o_name;
+
+ u_int o_flags;
+
+ int (*o_get)(struct oid *, struct ber_oid *,
+ struct ber_element **);
+ int (*o_set)(struct oid *, struct ber_oid *,
+ struct ber_element **);
+
+ void *o_data;
+ long long o_val;
+
+ RB_ENTRY(oid) o_element;
+};
+
+#define OID_ROOT 0x00
+#define OID_RD 0x01
+#define OID_WR 0x02
+#define OID_IFSET 0x04 /* only if user-specified value */
+#define OID_DYNAMIC 0x08 /* free allocated data */
+#define OID_TABLE 0x10 /* dynamic sub-elements */
+#define OID_MIB 0x20 /* root-OID of a supported MIB */
+
+#define OID_RS (OID_RD|OID_IFSET)
+#define OID_WS (OID_WR|OID_IFSET)
+#define OID_RW (OID_RD|OID_WR)
+#define OID_RWS (OID_RW|OID_IFSET)
+
+#define OID_TRD (OID_RD|OID_TABLE)
+#define OID_TWR (OID_WR|OID_TABLE)
+#define OID_TRS (OID_RD|OID_IFSET|OID_TABLE)
+#define OID_TWS (OID_WR|OID_IFSET|OID_TABLE)
+#define OID_TRW (OID_RD|OID_WR|OID_TABLE)
+#define OID_TRWS (OID_RW|OID_IFSET|OID_TABLE)
+
+#define OID_NOTSET(_oid) \
+ (((_oid)->o_flags & OID_IFSET) && \
+ ((_oid)->o_data == NULL) && ((_oid)->o_val == 0))
+
+#define OID(_mib...) { { _mib } }
+#define MIB(_mib...) { { MIB_##_mib } }
+
+/*
+ * daemon structures
+ */
+
+struct snmp_message {
+ u_int8_t sm_version;
+ char sm_community[SNMPD_MAXCOMMUNITYLEN];
+ u_int sm_context;
+
+ struct ber_element *sm_header;
+ struct ber_element *sm_headerend;
+
+ long long sm_request;
+
+ long long sm_error;
+#define sm_nonrepeaters sm_error
+ long long sm_errorindex;
+#define sm_maxrepetitions sm_errorindex
+
+ struct ber_element *sm_pdu;
+ struct ber_element *sm_pduend;
+
+ struct ber_element *sm_varbind;
+ struct ber_element *sm_varbindresp;
+};
+
+/* Defined in SNMPv2-MIB.txt (RFC 3418) */
+struct snmp_stats {
+ u_int32_t snmp_inpkts;
+ u_int32_t snmp_outpkts;
+ u_int32_t snmp_inbadversions;
+ u_int32_t snmp_inbadcommunitynames;
+ u_int32_t snmp_inbadcommunityuses;
+ u_int32_t snmp_inasnparseerrs;
+ u_int32_t snmp_intoobigs;
+ u_int32_t snmp_innosuchnames;
+ u_int32_t snmp_inbadvalues;
+ u_int32_t snmp_inreadonlys;
+ u_int32_t snmp_ingenerrs;
+ u_int32_t snmp_intotalreqvars;
+ u_int32_t snmp_intotalsetvars;
+ u_int32_t snmp_ingetrequests;
+ u_int32_t snmp_ingetnexts;
+ u_int32_t snmp_insetrequests;
+ u_int32_t snmp_ingetresponses;
+ u_int32_t snmp_intraps;
+ u_int32_t snmp_outtoobigs;
+ u_int32_t snmp_outnosuchnames;
+ u_int32_t snmp_outbadvalues;
+ u_int32_t snmp_outgenerrs;
+ u_int32_t snmp_outgetrequests;
+ u_int32_t snmp_outgetnexts;
+ u_int32_t snmp_outsetrequests;
+ u_int32_t snmp_outgetresponses;
+ u_int32_t snmp_outtraps;
+ int snmp_enableauthentraps;
+ u_int32_t snmp_silentdrops;
+ u_int32_t snmp_proxydrops;
+};
+
+struct snmpd {
+ u_int8_t sc_flags;
+#define SNMPD_F_VERBOSE 0x01
+#define SNMPD_F_NONAMES 0x02
+
+ const char *sc_confpath;
+ struct address sc_address;
+ int sc_sock;
+ struct event sc_ev;
+ struct timeval sc_starttime;
+
+ char sc_rdcommunity[SNMPD_MAXCOMMUNITYLEN];
+ char sc_rwcommunity[SNMPD_MAXCOMMUNITYLEN];
+ char sc_trcommunity[SNMPD_MAXCOMMUNITYLEN];
+
+ struct snmp_stats sc_stats;
+};
+
+/* control.c */
+int control_init(void);
+int control_listen(struct snmpd *, struct imsgbuf *);
+void control_accept(int, short, void *);
+void control_dispatch_imsg(int, short, void *);
+void control_imsg_forward(struct imsg *);
+void control_cleanup(void);
+
+void session_socket_blockmode(int, enum blockmodes);
+
+/* parse.y */
+struct snmpd *parse_config(const char *, u_int);
+int cmdline_symset(char *);
+
+/* log.c */
+void log_init(int);
+void log_warn(const char *, ...);
+void log_warnx(const char *, ...);
+void log_info(const char *, ...);
+void log_debug(const char *, ...);
+__dead void fatal(const char *);
+__dead void fatalx(const char *);
+const char *log_host(struct sockaddr_storage *, char *, size_t);
+
+/* buffer.c */
+struct buf *buf_open(size_t);
+struct buf *buf_dynamic(size_t, size_t);
+int buf_add(struct buf *, void *, size_t);
+void *buf_reserve(struct buf *, size_t);
+int buf_close(struct msgbuf *, struct buf *);
+void buf_free(struct buf *);
+void msgbuf_init(struct msgbuf *);
+void msgbuf_clear(struct msgbuf *);
+int msgbuf_write(struct msgbuf *);
+
+/* imsg.c */
+void imsg_init(struct imsgbuf *, int, void (*)(int, short, void *));
+ssize_t imsg_read(struct imsgbuf *);
+ssize_t imsg_get(struct imsgbuf *, struct imsg *);
+int imsg_compose(struct imsgbuf *, enum imsg_type, u_int32_t,
+ pid_t, int, void *, u_int16_t);
+struct buf *imsg_create(struct imsgbuf *, enum imsg_type, u_int32_t,
+ pid_t, u_int16_t);
+int imsg_add(struct buf *, void *, u_int16_t);
+int imsg_close(struct imsgbuf *, struct buf *);
+void imsg_free(struct imsg *);
+void imsg_event_add(struct imsgbuf *); /* provided externally */
+int imsg_get_fd(struct imsgbuf *);
+
+/* kroute.c */
+int kr_init(void);
+void kr_shutdown(void);
+
+int kr_updateif(u_int);
+u_int kr_ifnumber(void);
+u_long kr_iflastchange(void);
+struct kif *kr_getif(u_short);
+struct kif *kr_getnextif(u_short);
+
+/* snmpe.c */
+pid_t snmpe(struct snmpd *, int [2]);
+
+/* mps.c */
+int mps_init(void);
+u_long mps_getticks(void);
+long mps_oid_cmp(struct oid *, struct oid *);
+void mps_mibtree(struct oid *, size_t);
+struct oid *mps_foreach(struct oid *, u_int);
+void mps_oidlen(struct ber_oid *);
+char *mps_oidstring(struct ber_oid *, char *, size_t);
+struct ber_element *
+ mps_getreq(struct ber_element *, struct ber_oid *);
+struct ber_element *
+ mps_getnextreq(struct ber_element *, struct ber_oid *);
+int mps_setreq(struct ber_element *, struct ber_oid *);
+int mps_set(struct ber_oid *, void *, long long);
+void mps_delete(struct oid *);
+void mps_insert(struct oid *);
+int mps_getstr(struct oid *, struct ber_oid *,
+ struct ber_element **);
+int mps_setstr(struct oid *, struct ber_oid *,
+ struct ber_element **);
+int mps_getint(struct oid *, struct ber_oid *,
+ struct ber_element **);
+int mps_setint(struct oid *, struct ber_oid *,
+ struct ber_element **);
+int mps_getts(struct oid *, struct ber_oid *,
+ struct ber_element **);
+
+#endif /* _SNMPD_H */
diff --git a/usr.sbin/snmpd/snmpe.c b/usr.sbin/snmpd/snmpe.c
new file mode 100644
index 00000000000..4d9cad4062c
--- /dev/null
+++ b/usr.sbin/snmpd/snmpe.c
@@ -0,0 +1,803 @@
+/* $OpenBSD: snmpe.c,v 1.1 2007/12/05 09:22:44 reyk Exp $ */
+
+/*
+ * Copyright (c) 2007 Reyk Floeter <reyk@vantronix.net>
+ *
+ * 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/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/tree.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <event.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+#include <pwd.h>
+
+#include "snmpd.h"
+
+int snmpe_parse(struct sockaddr_storage *,
+ struct ber_element *, struct snmp_message *);
+unsigned long
+ snmpe_application(struct ber_element *);
+void snmpe_sig_handler(int sig, short, void *);
+void snmpe_shutdown(void);
+void snmpe_dispatch_parent(int, short, void *);
+int snmpe_socket_af(struct sockaddr_storage *, in_port_t);
+int snmpe_bind(struct address *);
+void snmpe_recvmsg(int fd, short, void *);
+
+struct snmpd *env = NULL;
+
+struct imsgbuf *ibuf_parent;
+
+void
+snmpe_sig_handler(int sig, short event, void *arg)
+{
+ switch (sig) {
+ case SIGINT:
+ case SIGTERM:
+ snmpe_shutdown();
+ default:
+ fatalx("snmpe_sig_handler: unexpected signal");
+ }
+}
+
+pid_t
+snmpe(struct snmpd *x_env, int pipe_parent2snmpe[2])
+{
+ pid_t pid;
+ struct passwd *pw;
+ struct event ev_sigint;
+ struct event ev_sigterm;
+#ifdef DEBUG
+ struct oid *oid;
+#endif
+
+ switch (pid = fork()) {
+ case -1:
+ fatal("snmpe: cannot fork");
+ case 0:
+ break;
+ default:
+ return (pid);
+ }
+
+ env = x_env;
+
+ if (control_init() == -1)
+ fatalx("snmpe: control socket setup failed");
+
+ if ((env->sc_sock = snmpe_bind(&env->sc_address)) == -1)
+ fatalx("snmpe: failed to bind SNMP UDP socket");
+
+ if ((pw = getpwnam(SNMPD_USER)) == NULL)
+ fatal("snmpe: getpwnam");
+
+#ifndef DEBUG
+ if (chroot(pw->pw_dir) == -1)
+ fatal("snmpe: chroot");
+ if (chdir("/") == -1)
+ fatal("snmpe: chdir(\"/\")");
+#else
+#warning disabling privilege revocation and chroot in DEBUG mode
+#endif
+
+ setproctitle("snmp engine");
+ snmpd_process = PROC_SNMPE;
+
+#ifndef DEBUG
+ 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("snmpe: cannot drop privileges");
+#endif
+
+#ifdef DEBUG
+ for (oid = NULL; (oid = mps_foreach(oid, 0)) != NULL;) {
+ char buf[BUFSIZ];
+ mps_oidstring(&oid->o_id, buf, sizeof(buf));
+ log_debug("oid %s", buf);
+ }
+#endif
+
+ event_init();
+
+ signal_set(&ev_sigint, SIGINT, snmpe_sig_handler, NULL);
+ signal_set(&ev_sigterm, SIGTERM, snmpe_sig_handler, NULL);
+ signal_add(&ev_sigint, NULL);
+ signal_add(&ev_sigterm, NULL);
+ signal(SIGPIPE, SIG_IGN);
+ signal(SIGHUP, SIG_IGN);
+
+ close(pipe_parent2snmpe[0]);
+
+ if ((ibuf_parent = calloc(1, sizeof(struct imsgbuf))) == NULL)
+ fatal("snmpe");
+
+ imsg_init(ibuf_parent, pipe_parent2snmpe[1], snmpe_dispatch_parent);
+
+ ibuf_parent->events = EV_READ;
+ event_set(&ibuf_parent->ev, ibuf_parent->fd, ibuf_parent->events,
+ ibuf_parent->handler, ibuf_parent);
+ event_add(&ibuf_parent->ev, NULL);
+
+ TAILQ_INIT(&ctl_conns);
+
+ if (control_listen(env, ibuf_parent) == -1)
+ fatalx("snmpe: control socket listen failed");
+
+ event_set(&env->sc_ev, env->sc_sock, EV_READ|EV_PERSIST,
+ snmpe_recvmsg, env);
+ event_add(&env->sc_ev, NULL);
+
+ kr_init();
+
+ event_dispatch();
+
+ snmpe_shutdown();
+ kr_shutdown();
+
+ return (0);
+}
+
+void
+snmpe_shutdown(void)
+{
+ log_info("snmp engine exiting");
+ _exit(0);
+}
+
+void
+snmpe_dispatch_parent(int fd, short event, void * ptr)
+{
+ struct imsgbuf *ibuf;
+ struct imsg imsg;
+ ssize_t n;
+
+ ibuf = ptr;
+ switch (event) {
+ case EV_READ:
+ if ((n = imsg_read(ibuf)) == -1)
+ fatal("imsg_read error");
+ if (n == 0) {
+ /* this pipe is dead, so remove the event handler */
+ event_del(&ibuf->ev);
+ event_loopexit(NULL);
+ return;
+ }
+ break;
+ case EV_WRITE:
+ if (msgbuf_write(&ibuf->w) == -1)
+ fatal("msgbuf_write");
+ imsg_event_add(ibuf);
+ return;
+ default:
+ fatalx("snmpe_dispatch_parent: unknown event");
+ }
+
+ for (;;) {
+ if ((n = imsg_get(ibuf, &imsg)) == -1)
+ fatal("snmpe_dispatch_parent: imsg_read error");
+ if (n == 0)
+ break;
+
+ switch (imsg.hdr.type) {
+ default:
+ log_debug("snmpe_dispatch_parent: unexpected imsg %d",
+ imsg.hdr.type);
+ break;
+ }
+ imsg_free(&imsg);
+ }
+ imsg_event_add(ibuf);
+}
+
+int
+snmpe_socket_af(struct sockaddr_storage *ss, in_port_t port)
+{
+ switch (ss->ss_family) {
+ case AF_INET:
+ ((struct sockaddr_in *)ss)->sin_port = port;
+ ((struct sockaddr_in *)ss)->sin_len =
+ sizeof(struct sockaddr_in);
+ break;
+ case AF_INET6:
+ ((struct sockaddr_in6 *)ss)->sin6_port = port;
+ ((struct sockaddr_in6 *)ss)->sin6_len =
+ sizeof(struct sockaddr_in6);
+ break;
+ default:
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+snmpe_bind(struct address *addr)
+{
+ char buf[512];
+ int s = -1;
+
+ if (snmpe_socket_af(&addr->ss, htons(addr->port)) == -1)
+ goto bad;
+
+ if ((s = socket(addr->ss.ss_family, SOCK_DGRAM, IPPROTO_UDP)) == -1)
+ goto bad;
+
+ /*
+ * Socket options
+ */
+ 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 (log_host(&addr->ss, buf, sizeof(buf)) == NULL)
+ goto bad;
+
+ log_info("snmpe_bind: binding to address %s:%d", buf, addr->port);
+
+ return (s);
+
+ bad:
+ if (s != -1)
+ close(s);
+ return (-1);
+}
+
+#ifdef DEBUG
+static void
+snmpe_debug_elements(struct ber_element *root)
+{
+ static int indent = 0;
+ long long v;
+ int d;
+ char *buf;
+ size_t len;
+ u_int i;
+ int constructed;
+ struct ber_oid o;
+ char str[BUFSIZ];
+
+ /* calculate lengths */
+ ber_calc_len(root);
+
+ switch (root->be_encoding) {
+ case BER_TYPE_SEQUENCE:
+ case BER_TYPE_SET:
+ constructed = root->be_encoding;
+ break;
+ default:
+ constructed = 0;
+ break;
+ }
+
+ fprintf(stderr, "%*slen %lu ", indent, "", root->be_len);
+ switch (root->be_class) {
+ case BER_CLASS_UNIVERSAL:
+ fprintf(stderr, "class: universal(%u) type: ", root->be_class);
+ switch (root->be_type) {
+ case BER_TYPE_EOC:
+ fprintf(stderr, "end-of-content");
+ break;
+ case BER_TYPE_BOOLEAN:
+ fprintf(stderr, "boolean");
+ break;
+ case BER_TYPE_INTEGER:
+ fprintf(stderr, "integer");
+ break;
+ case BER_TYPE_BITSTRING:
+ fprintf(stderr, "bit-string");
+ break;
+ case BER_TYPE_OCTETSTRING:
+ fprintf(stderr, "octet-string");
+ break;
+ case BER_TYPE_NULL:
+ fprintf(stderr, "null");
+ break;
+ case BER_TYPE_OBJECT:
+ fprintf(stderr, "object");
+ break;
+ case BER_TYPE_ENUMERATED:
+ fprintf(stderr, "enumerated");
+ break;
+ case BER_TYPE_SEQUENCE:
+ fprintf(stderr, "sequence");
+ break;
+ case BER_TYPE_SET:
+ fprintf(stderr, "set");
+ break;
+ }
+ break;
+ case BER_CLASS_APPLICATION:
+ fprintf(stderr, "class: application(%u) type: ",
+ root->be_class);
+ switch (root->be_type) {
+ case SNMP_T_IPADDR:
+ fprintf(stderr, "ipaddr");
+ break;
+ case SNMP_T_COUNTER32:
+ fprintf(stderr, "counter32");
+ break;
+ case SNMP_T_GAUGE32:
+ fprintf(stderr, "gauge32");
+ break;
+ case SNMP_T_TIMETICKS:
+ fprintf(stderr, "timeticks");
+ break;
+ case SNMP_T_OPAQUE:
+ fprintf(stderr, "opaque");
+ break;
+ case SNMP_T_COUNTER64:
+ fprintf(stderr, "counter64");
+ break;
+ }
+ break;
+ case BER_CLASS_CONTEXT:
+ fprintf(stderr, "class: context(%u) type: ",
+ root->be_class);
+ switch (root->be_type) {
+ case SNMP_T_GETREQ:
+ fprintf(stderr, "getreq");
+ break;
+ case SNMP_T_GETNEXTREQ:
+ fprintf(stderr, "nextreq");
+ break;
+ case SNMP_T_GETRESP:
+ fprintf(stderr, "getresp");
+ break;
+ case SNMP_T_SETREQ:
+ fprintf(stderr, "setreq");
+ break;
+ case SNMP_T_TRAP:
+ fprintf(stderr, "trap");
+ break;
+ case SNMP_T_GETBULKREQ:
+ fprintf(stderr, "getbulkreq");
+ break;
+ case SNMP_T_INFORMREQ:
+ fprintf(stderr, "informreq");
+ break;
+ case SNMP_T_TRAPV2:
+ fprintf(stderr, "trapv2");
+ break;
+ case SNMP_T_REPORT:
+ fprintf(stderr, "report");
+ break;
+ }
+ break;
+ case BER_CLASS_PRIVATE:
+ fprintf(stderr, "class: private(%u) type: ", root->be_class);
+ break;
+ default:
+ fprintf(stderr, "class: <INVALID>(%u) type: ", root->be_class);
+ break;
+ }
+ fprintf(stderr, "(%lu) encoding %lu ",
+ root->be_type, root->be_encoding);
+
+ if (constructed)
+ root->be_encoding = constructed;
+
+ switch (root->be_encoding) {
+ case BER_TYPE_BOOLEAN:
+ if (ber_get_boolean(root, &d) == -1) {
+ fprintf(stderr, "<INVALID>\n");
+ break;
+ }
+ fprintf(stderr, "%s(%d)\n", d ? "true" : "false", d);
+ break;
+ case BER_TYPE_INTEGER:
+ case BER_TYPE_ENUMERATED:
+ if (ber_get_integer(root, &v) == -1) {
+ fprintf(stderr, "<INVALID>\n");
+ break;
+ }
+ fprintf(stderr, "value %lld\n", v);
+ break;
+ case BER_TYPE_BITSTRING:
+ if (ber_get_bitstring(root, (void *)&buf, &len) == -1) {
+ fprintf(stderr, "<INVALID>\n");
+ break;
+ }
+ fprintf(stderr, "hexdump ");
+ for (i = 0; i < len; i++)
+ fprintf(stderr, "%02x", buf[i]);
+ fprintf(stderr, "\n");
+ break;
+ case BER_TYPE_OBJECT:
+ if (ber_get_oid(root, &o) == -1) {
+ fprintf(stderr, "<INVALID>\n");
+ break;
+ }
+ fprintf(stderr, "oid %s",
+ mps_oidstring(&o, str, sizeof(str)));
+ fprintf(stderr, "\n");
+ break;
+ case BER_TYPE_OCTETSTRING:
+ if (ber_get_string(root, &buf) == -1) {
+ fprintf(stderr, "<INVALID>\n");
+ break;
+ }
+ if (root->be_class == BER_CLASS_APPLICATION &&
+ root->be_type == SNMP_T_IPADDR) {
+ fprintf(stderr, "addr %s\n",
+ inet_ntoa(*(struct in_addr *)buf));
+ } else
+ fprintf(stderr, "string \"%s\"\n", buf);
+ break;
+ case BER_TYPE_NULL: /* no payload */
+ case BER_TYPE_EOC:
+ case BER_TYPE_SEQUENCE:
+ case BER_TYPE_SET:
+ default:
+ fprintf(stderr, "\n");
+ break;
+ }
+
+ if (constructed && root->be_sub) {
+ indent += 2;
+ snmpe_debug_elements(root->be_sub);
+ indent -= 2;
+ }
+ if (root->be_next)
+ snmpe_debug_elements(root->be_next);
+}
+#endif
+
+unsigned long
+snmpe_application(struct ber_element *elm)
+{
+ if (elm->be_class != BER_CLASS_APPLICATION)
+ return (BER_TYPE_OCTETSTRING);
+
+ switch (elm->be_type) {
+ case SNMP_T_IPADDR:
+ return (BER_TYPE_OCTETSTRING);
+ case SNMP_T_COUNTER32:
+ case SNMP_T_GAUGE32:
+ case SNMP_T_TIMETICKS:
+ case SNMP_T_OPAQUE:
+ case SNMP_T_COUNTER64:
+ return (BER_TYPE_INTEGER);
+ default:
+ break;
+ }
+ return (BER_TYPE_OCTETSTRING);
+}
+
+int
+snmpe_parse(struct sockaddr_storage *ss,
+ struct ber_element *root, struct snmp_message *msg)
+{
+ struct snmp_stats *stats = &env->sc_stats;
+ struct ber_element *a, *b, *c, *d, *e, *f, *next, *last;
+ const char *errstr = "invalid message";
+ long long ver;
+ unsigned long type, req, errval, erridx;
+ int class, state, i = 0, j = 0;
+ char *comn, buf[BUFSIZ], host[MAXHOSTNAMELEN];
+ struct ber_oid o;
+ size_t len;
+
+ bzero(msg, sizeof(*msg));
+
+ if (ber_scanf_elements(root, "e{ieset{e",
+ &msg->sm_header, &ver, &msg->sm_headerend, &comn,
+ &msg->sm_pdu, &class, &type, &a) != 0)
+ goto fail;
+
+ /* SNMP version and community */
+ switch (ver) {
+ case SNMP_V1:
+ case SNMP_V2:
+ msg->sm_version = ver;
+ break;
+ case SNMP_V3:
+ default:
+ stats->snmp_inbadversions++;
+ log_debug("bad snmp version");
+ return (-1);
+ }
+
+ /* SNMP PDU context */
+ if (class != BER_CLASS_CONTEXT)
+ goto fail;
+ switch (type) {
+ case SNMP_T_GETBULKREQ:
+ if (msg->sm_version == SNMP_V1) {
+ stats->snmp_inbadversions++;
+ log_debug("invalid request for protocol version 1");
+ return (-1);
+ }
+ /* FALLTHROUGH */
+ case SNMP_T_GETREQ:
+ stats->snmp_ingetrequests++;
+ /* FALLTHROUGH */
+ case SNMP_T_GETNEXTREQ:
+ if (type == SNMP_T_GETNEXTREQ)
+ stats->snmp_ingetnexts++;
+ if (strcmp(env->sc_rdcommunity, comn) != 0 &&
+ strcmp(env->sc_rwcommunity, comn) != 0) {
+ stats->snmp_inbadcommunitynames++;
+ log_debug("wrong read community");
+ return (-1);
+ }
+ msg->sm_context = type;
+ break;
+ case SNMP_T_SETREQ:
+ stats->snmp_insetrequests++;
+ if (strcmp(env->sc_rwcommunity, comn) != 0) {
+ if (strcmp(env->sc_rdcommunity, comn) != 0)
+ stats->snmp_inbadcommunitynames++;
+ else
+ stats->snmp_inbadcommunityuses++;
+ log_debug("wrong write community");
+ return (-1);
+ }
+ msg->sm_context = type;
+ break;
+ case SNMP_T_GETRESP:
+ errstr = "response without request";
+ stats->snmp_ingetresponses++;
+ goto fail;
+ case SNMP_T_TRAP:
+ case SNMP_T_TRAPV2:
+ if (strcmp(env->sc_trcommunity, comn) != 0) {
+ stats->snmp_inbadcommunitynames++;
+ log_debug("wrong trap community");
+ return (-1);
+ }
+ errstr = "received trap";
+ stats->snmp_intraps++;
+ goto fail;
+ default:
+ errstr = "invalid context";
+ goto fail;
+ }
+
+ if (strlcpy(msg->sm_community, comn, sizeof(msg->sm_community)) >=
+ sizeof(msg->sm_community)) {
+ stats->snmp_inbadcommunitynames++;
+ log_debug("community name too long");
+ return (-1);
+ }
+
+ /* SNMP PDU */
+ if (ber_scanf_elements(a, "iiie{et",
+ &req, &errval, &erridx, &msg->sm_pduend,
+ &msg->sm_varbind, &class, &type) != 0) {
+ stats->snmp_silentdrops++;
+ log_debug("invalid PDU");
+ return (-1);
+ }
+ if (class != BER_CLASS_UNIVERSAL && type != BER_TYPE_SEQUENCE) {
+ stats->snmp_silentdrops++;
+ log_debug("invalid varbind");
+ return (-1);
+ }
+
+ msg->sm_request = req;
+ msg->sm_error = errval;
+ msg->sm_errorindex = erridx;
+
+ log_debug("snmpe_parse: %s: SNMPv%d '%s' context %d request %d",
+ log_host(ss, host, sizeof(host)),
+ msg->sm_version + 1, msg->sm_community, msg->sm_context,
+ msg->sm_request);
+
+ errstr = "invalid varbind element";
+ for (i = 1, a = msg->sm_varbind, last = NULL;
+ a != NULL; a = a->be_next, i++) {
+ next = a->be_next;
+
+ if (a->be_class != BER_CLASS_UNIVERSAL &&
+ a->be_type != BER_TYPE_SEQUENCE)
+ goto varfail;
+ if ((b = a->be_sub) == NULL)
+ continue;
+ for (state = 0; state < 2 && b != NULL; b = b->be_next) {
+ switch (state++) {
+ case 0:
+ if (ber_get_oid(b, &o) != 0)
+ goto varfail;
+ if (o.bo_n < BER_MIN_OID_LEN ||
+ o.bo_n > BER_MAX_OID_LEN)
+ goto varfail;
+ log_debug("snmpe_parse: %s: oid %s", host,
+ mps_oidstring(&o, buf, sizeof(buf)));
+ break;
+ case 1:
+ c = d = NULL;
+ switch (msg->sm_context) {
+ case SNMP_T_GETNEXTREQ:
+ c = ber_add_sequence(NULL);
+ if ((d = mps_getnextreq(c, &o)) != NULL)
+ break;
+ ber_free_elements(c);
+ c = NULL;
+ msg->sm_error = SNMP_ERROR_NOSUCHNAME;
+ msg->sm_errorindex = i;
+ break; /* ignore error */
+ case SNMP_T_GETREQ:
+ c = ber_add_sequence(NULL);
+ if ((d = mps_getreq(c, &o)) != NULL)
+ break;
+ msg->sm_error = SNMP_ERROR_NOSUCHNAME;
+ ber_free_elements(c);
+ goto varfail;
+ case SNMP_T_SETREQ:
+ if (mps_setreq(b, &o) == 0)
+ break;
+ msg->sm_error = SNMP_ERROR_READONLY;
+ goto varfail;
+ case SNMP_T_GETBULKREQ:
+ j = msg->sm_maxrepetitions;
+ msg->sm_errorindex = 0;
+ msg->sm_error = SNMP_ERROR_NOSUCHNAME;
+ for (d = NULL, len = 0; j > 0; j--) {
+ e = ber_add_sequence(NULL);
+ if (c == NULL)
+ c = e;
+ f = mps_getnextreq(e, &o);
+ if (f == NULL) {
+ ber_free_elements(e);
+ if (d == NULL)
+ goto varfail;
+ break;
+ }
+ len += ber_calc_len(e);
+ if (len > SNMPD_MAXVARBINDLEN) {
+ ber_free_elements(e);
+ break;
+ }
+ if (d != NULL)
+ ber_link_elements(d, e);
+ d = e;
+ }
+ msg->sm_error = 0;
+ break;
+ default:
+ goto varfail;
+ }
+ if (c == NULL)
+ break;
+ if (last == NULL)
+ msg->sm_varbindresp = c;
+ else
+ ber_replace_elements(last, c);
+ a = c;
+ break;
+ }
+ }
+ if (state < 2) {
+ log_debug("snmpe_parse: state %d", state);
+ goto varfail;
+ }
+ last = a;
+ }
+
+ return (0);
+ varfail:
+ log_debug("snmpe_parse: %s: %s, error index %d", host, errstr, i);
+ if (msg->sm_error == 0)
+ msg->sm_error = SNMP_ERROR_GENERR;
+ msg->sm_errorindex = i;
+ return (0);
+ fail:
+ stats->snmp_inasnparseerrs++;
+ log_debug("snmpe_parse: %s: %s", host, errstr);
+ return (-1);
+}
+
+void
+snmpe_recvmsg(int fd, short sig, void *arg)
+{
+ struct snmp_stats *stats = &env->sc_stats;
+ struct sockaddr_storage ss;
+ u_int8_t buf[READ_BUF_SIZE], *ptr;
+ socklen_t slen;
+ ssize_t len;
+ struct ber ber;
+ struct ber_element *req = NULL, *resp = NULL;
+ struct snmp_message msg;
+
+ slen = sizeof(ss);
+ if ((len = recvfrom(fd, buf, sizeof(buf), 0,
+ (struct sockaddr *)&ss, &slen)) < 1)
+ return;
+
+ stats->snmp_inpkts++;
+
+ ber.fd = -1;
+ ber_set_application(&ber, snmpe_application);
+
+ ber_set_readbuf(&ber, buf, len);
+ req = ber_read_elements(&ber, NULL);
+
+ if (req == NULL) {
+ stats->snmp_inasnparseerrs++;
+ goto done;
+ }
+
+#ifdef DEBUG
+ snmpe_debug_elements(req);
+#endif
+
+ if (snmpe_parse(&ss, req, &msg) == -1)
+ goto done;
+
+ if (msg.sm_varbindresp == NULL)
+ msg.sm_varbindresp = ber_unlink_elements(msg.sm_pduend);
+
+ switch (msg.sm_error) {
+ case SNMP_ERROR_TOOBIG:
+ stats->snmp_intoobigs++;
+ break;
+ case SNMP_ERROR_NOSUCHNAME:
+ stats->snmp_innosuchnames++;
+ break;
+ case SNMP_ERROR_BADVALUE:
+ stats->snmp_inbadvalues++;
+ break;
+ case SNMP_ERROR_READONLY:
+ stats->snmp_inreadonlys++;
+ break;
+ case SNMP_ERROR_GENERR:
+ default:
+ stats->snmp_ingenerrs++;
+ break;
+ }
+
+ /* Create new SNMP packet */
+ resp = ber_add_sequence(NULL);
+ ber_printf_elements(resp, "is{tiii{e}}.",
+ msg.sm_version, msg.sm_community,
+ BER_CLASS_CONTEXT, SNMP_T_GETRESP,
+ msg.sm_request, msg.sm_error, msg.sm_errorindex,
+ msg.sm_varbindresp);
+
+#ifdef DEBUG
+ snmpe_debug_elements(resp);
+#endif
+
+ ber_write_elements(&ber, resp);
+ if ((len = ber_get_writebuf(&ber, (void *)&ptr)) == -1)
+ goto done;
+
+ len = sendto(fd, ptr, len, 0, (struct sockaddr *)&ss, slen);
+ if (len != -1)
+ stats->snmp_outpkts++;
+
+ done:
+ if (req != NULL)
+ ber_free_elements(req);
+ if (resp != NULL)
+ ber_free_elements(resp);
+}