summaryrefslogtreecommitdiffstats
path: root/contrib
diff options
context:
space:
mode:
authorharti <harti@FreeBSD.org>2003-11-10 08:53:38 +0000
committerharti <harti@FreeBSD.org>2003-11-10 08:53:38 +0000
commitd65ce423f44192b586dab407fe6ffa6a2ce388fe (patch)
treefd16e5b3593bc1dc75e34b28be054bae74571dfa /contrib
parent5a27373bba4ecea4611b6e20d0847df6a50a2344 (diff)
parentea9d8683bc24e904e026c05abd5768f41c3b551e (diff)
downloadFreeBSD-src-d65ce423f44192b586dab407fe6ffa6a2ce388fe.zip
FreeBSD-src-d65ce423f44192b586dab407fe6ffa6a2ce388fe.tar.gz
This commit was generated by cvs2svn to compensate for changes in r122394,
which included commits to RCS files with non-trunk default branches.
Diffstat (limited to 'contrib')
-rw-r--r--contrib/bsnmp/NEWS43
-rw-r--r--contrib/bsnmp/README65
-rw-r--r--contrib/bsnmp/TODO5
-rw-r--r--contrib/bsnmp/VERSION1
-rw-r--r--contrib/bsnmp/gensnmptree/gensnmptree.1190
-rw-r--r--contrib/bsnmp/gensnmptree/gensnmptree.c770
-rw-r--r--contrib/bsnmp/lib/asn1.3484
-rw-r--r--contrib/bsnmp/lib/asn1.c994
-rw-r--r--contrib/bsnmp/lib/asn1.h186
-rw-r--r--contrib/bsnmp/lib/bsnmpagent.3417
-rw-r--r--contrib/bsnmp/lib/bsnmpclient.3590
-rw-r--r--contrib/bsnmp/lib/bsnmplib.3306
-rw-r--r--contrib/bsnmp/lib/snmp.c1027
-rw-r--r--contrib/bsnmp/lib/snmp.h176
-rw-r--r--contrib/bsnmp/lib/snmpagent.c1031
-rw-r--r--contrib/bsnmp/lib/snmpagent.h159
-rw-r--r--contrib/bsnmp/lib/snmpclient.c1658
-rw-r--r--contrib/bsnmp/lib/snmpclient.h182
-rw-r--r--contrib/bsnmp/lib/snmppriv.h47
-rw-r--r--contrib/bsnmp/snmp_mibII/mibII.c1564
-rw-r--r--contrib/bsnmp/snmp_mibII/mibII.h238
-rw-r--r--contrib/bsnmp/snmp_mibII/mibII_ifmib.c80
-rw-r--r--contrib/bsnmp/snmp_mibII/mibII_ifstack.c109
-rw-r--r--contrib/bsnmp/snmp_mibII/mibII_interfaces.c524
-rw-r--r--contrib/bsnmp/snmp_mibII/mibII_ip.c497
-rw-r--r--contrib/bsnmp/snmp_mibII/mibII_ipaddr.c351
-rw-r--r--contrib/bsnmp/snmp_mibII/mibII_nettomedia.c154
-rw-r--r--contrib/bsnmp/snmp_mibII/mibII_rcvaddr.c138
-rw-r--r--contrib/bsnmp/snmp_mibII/mibII_route.c299
-rw-r--r--contrib/bsnmp/snmp_mibII/mibII_tcp.c361
-rw-r--r--contrib/bsnmp/snmp_mibII/mibII_tree.def248
-rw-r--r--contrib/bsnmp/snmp_mibII/mibII_udp.c257
-rw-r--r--contrib/bsnmp/snmp_mibII/snmp_mibII.3348
-rw-r--r--contrib/bsnmp/snmp_mibII/snmp_mibII.h167
-rw-r--r--contrib/bsnmp/snmpd/.gdbinit2
-rw-r--r--contrib/bsnmp/snmpd/BEGEMOT-MIB.txt63
-rw-r--r--contrib/bsnmp/snmpd/BEGEMOT-SNMPD.txt482
-rw-r--r--contrib/bsnmp/snmpd/FOKUS-MIB.txt61
-rw-r--r--contrib/bsnmp/snmpd/action.c1145
-rw-r--r--contrib/bsnmp/snmpd/bsnmpd.1256
-rw-r--r--contrib/bsnmp/snmpd/config.c1361
-rw-r--r--contrib/bsnmp/snmpd/export.c378
-rw-r--r--contrib/bsnmp/snmpd/main.c2038
-rw-r--r--contrib/bsnmp/snmpd/snmpd.config92
-rw-r--r--contrib/bsnmp/snmpd/snmpd.h277
-rwxr-xr-xcontrib/bsnmp/snmpd/snmpd.sh89
-rw-r--r--contrib/bsnmp/snmpd/snmpmod.3861
-rw-r--r--contrib/bsnmp/snmpd/snmpmod.h363
-rw-r--r--contrib/bsnmp/snmpd/trap.c475
-rw-r--r--contrib/bsnmp/snmpd/tree.def183
50 files changed, 21792 insertions, 0 deletions
diff --git a/contrib/bsnmp/NEWS b/contrib/bsnmp/NEWS
new file mode 100644
index 0000000..3d0ca12
--- /dev/null
+++ b/contrib/bsnmp/NEWS
@@ -0,0 +1,43 @@
+08-Nov-2003
+ WARNS=6 fixed.
+
+28-Jan-2003
+ WARNS=5 fixes.
+
+09-Jan-2003
+ snmpd: remove local socket in case of an error to fully initialize it.
+ Use chmod instead of fchmod. The latter seems not really to change
+ the mode of the socket.
+
+ lib: at program exit remove the local socket in the library.
+
+11-Dec-2002
+ Implement listening on unix domain sockets. The client must bind
+ its socket, or the server cannot send back its response. These
+ sockets are considered to be more secure, because it is much
+ harder for an intruder to listen on them.
+
+ This requires changes in snmpmod.h and snmpclient.h.
+
+04-Dec-2002
+ Sparc fixes.
+
+15-Aug-2002
+ Use inttypes.h instead of limits.h to get integer limits. This
+ seems to be the Posix way.
+
+ First drafts of an snmpd, gensnmptree, asn1, bsnmplib,
+ bsnmpclient, bsnmpagent, snmpmod, snmp_mibII, snmp_netgraph man pages.
+
+ snmpd/main.c: reorder getopt options according to style(9). Implement
+ a -h option to print a short help.
+
+25-Jun-2002
+ Makefiles rewritten to not use bsnmpmod.mk. The BSD makefiles are
+ really hard to use, because a) they are not documented and b) they
+ change much too often.
+
+ Make the patch a context diff instead of a unified one.
+
+28-Feb-2002
+ Library code for SNMP clients.
diff --git a/contrib/bsnmp/README b/contrib/bsnmp/README
new file mode 100644
index 0000000..85ab2ed
--- /dev/null
+++ b/contrib/bsnmp/README
@@ -0,0 +1,65 @@
+Mon Nov 10 09:50:22 CET 2003
+
+This is a mini-SNMP daemon. The basic daemon implements the system group
+and a number of private extensions to manage the UDP transport mapping,
+communities, trap destinations and loadable modules. In this form it can
+be used to provide remote access to arbitrary data that can be described in
+the form as required by the SMI. The daemon speaks both SNMPv1 and SNMPv2c.
+
+One basic loadable module is provided together with the daemon:
+
+- snmp_mibII provides the information groups for ip, tcp, and udp.
+
+Installation
+------------
+
+You need to apply the patch in the patches directory to your system sources.
+This adds a sysctl to retrieve multicast address information from the kernel.
+
+As usual by doing:
+
+ make obj ; make depend ; make ; make install
+
+This does not install a configuration file. The standard location for the
+configuration is /etc/snmpd.config, but can be overwritten on the command
+line. An example configuration file is provided.
+
+Running
+-------
+
+ snmpd [-m name[=value]] [-p pid-file] [-c config-file] [-d] [-l prefix]
+ [-D debug-flags] [-I path]
+
+ -m defines a configuration macro. If no value is given it
+ is set to the empty string.
+
+ -p specify the file where to store the PID. Default is
+ /var/run/{prefix}.pid.
+
+ -c specify the configuration file. Default is /etc/{prefix}.config.
+
+ -d don't go into daemon mode.
+
+ -l specify the prefix. This is used for the default config and
+ pid file names and for the syslog. Default is "snmpd".
+
+ -D specify debug flags:
+
+ d dump all PDUs.
+
+ e debug event library.
+
+ -I specify the include path for system configuration files.
+ Default is /etc:/usr/etc:/usr/local/etc.
+
+The directory snmpd contains a snmpd.sh script, which can be copied to
+/usr/local/etc/rc.d to automatically start and stop the daemon. snmpd.config
+is an example config script.
+
+Bug reports:
+-----------
+
+Please report bugs to harti@freebsd.org.
+
+Happy hacking,
+harti
diff --git a/contrib/bsnmp/TODO b/contrib/bsnmp/TODO
new file mode 100644
index 0000000..5b30544
--- /dev/null
+++ b/contrib/bsnmp/TODO
@@ -0,0 +1,5 @@
+snmpd_mibII:
+ - handle HC counters by periodically polling the kernel counters.
+
+snmpd_netgraph:
+ - make some tables writeable
diff --git a/contrib/bsnmp/VERSION b/contrib/bsnmp/VERSION
new file mode 100644
index 0000000..c068b24
--- /dev/null
+++ b/contrib/bsnmp/VERSION
@@ -0,0 +1 @@
+1.4
diff --git a/contrib/bsnmp/gensnmptree/gensnmptree.1 b/contrib/bsnmp/gensnmptree/gensnmptree.1
new file mode 100644
index 0000000..2970355
--- /dev/null
+++ b/contrib/bsnmp/gensnmptree/gensnmptree.1
@@ -0,0 +1,190 @@
+.\"
+.\" Copyright (c) 2001-2003
+.\" Fraunhofer Institute for Open Communication Systems (FhG Fokus).
+.\" All rights reserved.
+.\"
+.\" Redistribution of this software and documentation and use in source and
+.\" binary forms, with or without modification, are permitted provided that
+.\" the following conditions are met:
+.\"
+.\" 1. Redistributions of source code or documentation must retain the above
+.\" copyright notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the Institute nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY FRAUNHOFER FOKUS
+.\" AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+.\" INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+.\" FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+.\" FRAUNHOFER FOKUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+.\" LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+.\" OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+.\" LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+.\" NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+.\" EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $Begemot: bsnmp/gensnmptree/gensnmptree.1,v 1.1 2002/08/15 13:27:44 hbb Exp $
+.\"
+.\" Author: Harti Brandt <harti@freebsd.org>
+.\"
+.Dd October 7, 2003
+.Dt gensnmptree 1
+.Os
+.Sh NAME
+.Nm gensnmptree
+.Nd "generate C and header files from a MIB description file"
+.Sh SYNOPSIS
+.Nm
+.Op Fl hel
+.Op Fl p Ar prefix
+.Op Ar name Ar ...
+.Sh DESCRIPTION
+The
+.Nm
+utility is used to either generate C language tables and header files from
+a MIB description or to numeric OIDs from MIB descriptions. The first form
+is used only for maintaining the
+.Xr snmpd 1
+daemon or for module writers.
+The second form may be used by SNMP client program writers.
+.Pp
+If the
+.Fl e
+option is not used
+.Nm
+reads a MIB description from its standard input and creates two files: a
+C-file
+.Ar prefix Ns tree.c
+containing a table used by
+.Xr snmpd 1
+during PDU processing
+and a header file
+.Ar prefix Ns tree.h
+containing appropriate declarations of the callback functions used in this table
+and the table itself.
+.Pp
+If the
+.Fl e
+option is specified
+.Nm
+expects MIB variable names (only the last component) on its command line.
+It reads a MIB specification from standard input and for each MIB variable
+name emits two C preprocessor defines on its standard output. One define
+.Va OID_ Ns Ar name
+can be used as an array initialized to initialize a
+.Va struct asn_oid .
+The other define
+.Va OIDLEN_ Ns Ar name
+contains the length of the OID.
+.Pp
+The options are as follows:
+.Bl -tag -width ".Fl d Ar argument"
+.It Fl h
+Print a short help page.
+.It Fl e
+Enter extract mode.
+.It Fl l
+Generate local preprocessor includes. This is used for bootstrapping
+.Xr snmpd 1 .
+.It Fl p Ar prefix
+Prefix the file names and the table name with
+.Ar prefix .
+.El
+.Sh MIBS
+The syntax of the MIB description file can formally be specified as follows:
+.Bd -unfilled -offset indent
+tree := head elements ')'
+
+entry := head ':' index STRING elements ')'
+
+leaf := head TYPE STRING ACCESS ')'
+
+column := head TYPE ACCESS ')'
+
+head := '(' INT STRING
+
+elements := EMPTY | elements element
+
+element := tree | leaf
+
+index := TYPE | index TYPE
+.Ed
+.Pp
+.Ar TYPE
+specifies a SNMP data type and may be one of
+.Bl -bullet -offset indent -compact
+.It
+NULL
+.It
+INTEGER
+.It
+INTEGER32 (same as INTEGER)
+.It
+UNSIGNED32 (same as GAUGE)
+.It
+OCTETSTRING
+.It
+IPADDRESS
+.It
+OID
+.It
+TIMETICKS
+.It
+COUNTER
+.It
+GAUGE
+.It
+COUNTER64
+.El
+.Pp
+.Ar ACCESS
+specifies the accessibility of the MIB variable (which operation can be
+performed) and is one of
+.Bl -bullet -offset indent -compact
+.It
+GET
+.It
+SET
+.El
+.Pp
+.Ar INT
+is a decimal integer and
+.Ar STRING
+is any string starting with a letter or underscore and consisting of
+letters, digits and underscores, that is not one of the keywords.
+.Sh EXAMPLES
+The following MIB description describes the system group:
+.Bd -literal -offset indent
+(1 internet
+ (2 mgmt
+ (1 mibII
+ (1 system
+ (1 sysDescr OCTETSTRING op_system_group GET)
+ (2 sysObjectId OID op_system_group GET)
+ (3 sysUpTime TIMETICKS op_system_group GET)
+ (4 sysContact OCTETSTRING op_system_group GET SET)
+ (5 sysName OCTETSTRING op_system_group GET SET)
+ (6 sysLocation OCTETSTRING op_system_group GET SET)
+ (7 sysServices INTEGER op_system_group GET)
+ (8 sysORLastChange TIMETICKS op_system_group GET)
+ (9 sysORTable
+ (1 sysOREntry : INTEGER op_or_table
+ (1 sysORIndex INTEGER)
+ (2 sysORID OID GET)
+ (3 sysORDescr OCTETSTRING GET)
+ (4 sysORUpTime TIMETICKS GET)
+ ))
+ )
+ )
+ )
+)
+.Ed
+.Sh SEE ALSO
+.Xr snmpd 1
+.Sh AUTHORS
+.An Hartmut Brandt Aq harti@freebsd.org
diff --git a/contrib/bsnmp/gensnmptree/gensnmptree.c b/contrib/bsnmp/gensnmptree/gensnmptree.c
new file mode 100644
index 0000000..5c400e3
--- /dev/null
+++ b/contrib/bsnmp/gensnmptree/gensnmptree.c
@@ -0,0 +1,770 @@
+/*
+ * Copyright (c) 2001-2003
+ * Fraunhofer Institute for Open Communication Systems (FhG Fokus).
+ * All rights reserved.
+ *
+ * Author: Harti Brandt <harti@freebsd.org>
+ *
+ * Redistribution of this software and documentation and use in source and
+ * binary forms, with or without modification, are permitted provided that
+ * the following conditions are met:
+ *
+ * 1. Redistributions of source code or documentation must retain the above
+ * copyright notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY FRAUNHOFER FOKUS
+ * AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * FRAUNHOFER FOKUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Begemot: bsnmp/gensnmptree/gensnmptree.c,v 1.34 2003/01/28 13:44:34 hbb Exp $
+ *
+ * Generate OID table from table description.
+ *
+ * Syntax is:
+ * ---------
+ * tree := head elements ')'
+ *
+ * entry := head ':' index STRING elements ')'
+ *
+ * leaf := head TYPE STRING ACCESS ')'
+ *
+ * column := head TYPE ACCESS ')'
+ *
+ * head := '(' INT STRING
+ *
+ * elements := EMPTY | elements element
+ *
+ * element := tree | leaf
+ *
+ * index := TYPE | index TYPE
+ *
+ */
+#include <sys/types.h>
+#include <sys/param.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <err.h>
+#include <sys/queue.h>
+#include "asn1.h"
+#include "snmp.h"
+#include "snmpagent.h"
+
+/*
+ * Constant prefix for all OIDs
+ */
+static const asn_subid_t prefix[] = { 1, 3, 6 };
+#define PREFIX_LEN (sizeof(prefix) / sizeof(prefix[0]))
+
+u_int tree_size;
+static const char *file_prefix = "";
+static FILE *fp;
+
+/* if true generate local include paths */
+static int localincs = 0;
+
+static const char usgtxt[] = "\
+Generate SNMP tables. Copyright (c) 2001-2002 Fraunhofer Institute for\n\
+Open Communication Systems (FhG Fokus). All rights reserved.\n\
+usage: gensnmptree [-hel] [-p prefix] [name]...\n\
+options:\n\
+ -h print this info\n\
+ -e extrace the named oids\n\
+ -l generate local include directives\n\
+ -p prefix prepend prefix to file and variable names\n\
+";
+
+/*
+ * A node in the OID tree
+ */
+enum ntype {
+ NODE_LEAF = 1,
+ NODE_TREE,
+ NODE_ENTRY,
+ NODE_COLUMN
+};
+
+enum {
+ FL_GET = 0x01,
+ FL_SET = 0x02,
+};
+
+struct node;
+TAILQ_HEAD(node_list, node);
+
+struct node {
+ enum ntype type;
+ asn_subid_t id; /* last element of OID */
+ char *name; /* name of node */
+ TAILQ_ENTRY(node) link;
+ u_int lno; /* starting line number */
+ u_int flags; /* allowed operations */
+
+ union {
+ struct tree {
+ struct node_list subs;
+ } tree;
+
+ struct entry {
+ u_int32_t index; /* index for table entry */
+ char *func; /* function for tables */
+ struct node_list subs;
+ } entry;
+
+ struct leaf {
+ enum snmp_syntax syntax; /* syntax for this leaf */
+ char *func; /* function name */
+ } leaf;
+
+ struct column {
+ enum snmp_syntax syntax; /* syntax for this column */
+ } column;
+ } u;
+};
+
+struct func {
+ const char *name;
+ LIST_ENTRY(func) link;
+};
+
+static LIST_HEAD(, func) funcs = LIST_HEAD_INITIALIZER(funcs);
+
+/************************************************************
+ *
+ * Allocate memory and panic just in the case...
+ */
+static void *
+xalloc(size_t size)
+{
+ void *ptr;
+
+ if ((ptr = malloc(size)) == NULL)
+ err(1, "allocing %u bytes", size);
+
+ return (ptr);
+}
+
+/************************************************************
+ *
+ * Parsing input
+ */
+enum tok {
+ TOK_EOF = 0200, /* end-of-file seen */
+ TOK_NUM, /* number */
+ TOK_STR, /* string */
+ TOK_ACCESS, /* access operator */
+ TOK_TYPE, /* type operator */
+};
+
+static const struct {
+ const char *str;
+ enum tok tok;
+ u_int val;
+} keywords[] = {
+ { "GET", TOK_ACCESS, FL_GET },
+ { "SET", TOK_ACCESS, FL_SET },
+ { "NULL", TOK_TYPE, SNMP_SYNTAX_NULL },
+ { "INTEGER", TOK_TYPE, SNMP_SYNTAX_INTEGER },
+ { "INTEGER32", TOK_TYPE, SNMP_SYNTAX_INTEGER },
+ { "UNSIGNED32", TOK_TYPE, SNMP_SYNTAX_GAUGE },
+ { "OCTETSTRING", TOK_TYPE, SNMP_SYNTAX_OCTETSTRING },
+ { "IPADDRESS", TOK_TYPE, SNMP_SYNTAX_IPADDRESS },
+ { "OID", TOK_TYPE, SNMP_SYNTAX_OID },
+ { "TIMETICKS", TOK_TYPE, SNMP_SYNTAX_TIMETICKS },
+ { "COUNTER", TOK_TYPE, SNMP_SYNTAX_COUNTER },
+ { "GAUGE", TOK_TYPE, SNMP_SYNTAX_GAUGE },
+ { "COUNTER64", TOK_TYPE, SNMP_SYNTAX_COUNTER64 },
+ { NULL, 0, 0 }
+};
+
+/* arbitrary upper limit on node names and function names */
+#define MAXSTR 1000
+char str[MAXSTR];
+u_long val; /* integer values */
+u_int lno = 1; /* current line number */
+
+static void report(const char *, ...) __dead2 __printflike(1, 2);
+static void report_node(const struct node *, const char *, ...)
+ __dead2 __printflike(2, 3);
+
+/*
+ * Report an error and exit.
+ */
+static void
+report(const char *fmt, ...)
+{
+ va_list ap;
+ int c;
+
+ va_start(ap, fmt);
+ fprintf(stderr, "line %u: ", lno);
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+ fprintf(stderr, "context: \"");
+ while ((c = getchar()) != EOF && c != '\n')
+ fprintf(stderr, "%c", c);
+ fprintf(stderr, "\n");
+ va_end(ap);
+ exit(1);
+}
+static void
+report_node(const struct node *np, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ fprintf(stderr, "line %u, node %s: ", np->lno, np->name);
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+ va_end(ap);
+ exit(1);
+}
+
+/*
+ * Return a fresh copy of the string constituting the current token.
+ */
+static char *
+savetok(void)
+{
+ return (strcpy(xalloc(strlen(str)+1), str));
+}
+
+/*
+ * Get the next token from input.
+ */
+static int
+gettoken(void)
+{
+ int c;
+
+ again:
+ /*
+ * Skip any whitespace before the next token
+ */
+ while ((c = getchar()) != EOF) {
+ if (c == '\n')
+ lno++;
+ if (!isspace(c))
+ break;
+ }
+ if (c == EOF)
+ return (TOK_EOF);
+ if (!isascii(c))
+ report("unexpected character %#2x", (u_int)c);
+
+ /*
+ * Skip comments
+ */
+ if (c == '#') {
+ while ((c = getchar()) != EOF) {
+ if (c == '\n') {
+ lno++;
+ goto again;
+ }
+ }
+ report("unexpected EOF in comment");
+ }
+
+ /*
+ * Single character tokens
+ */
+ if (c == ')' || c == '(' || c == ':')
+ return (c);
+
+ /*
+ * Sort out numbers
+ */
+ if (isdigit(c)) {
+ ungetc(c, stdin);
+ scanf("%lu", &val);
+ return (TOK_NUM);
+ }
+
+ /*
+ * So that has to be a string.
+ */
+ if (isalpha(c) || c == '_') {
+ size_t n = 0;
+ str[n++] = c;
+ while ((c = getchar()) != EOF) {
+ if (!isalnum(c) && c != '_') {
+ ungetc(c, stdin);
+ break;
+ }
+ if (n == sizeof(str) - 1) {
+ str[n++] = '\0';
+ report("string too long '%s...'", str);
+ }
+ str[n++] = c;
+ }
+ str[n++] = '\0';
+
+ /*
+ * Keywords
+ */
+ for (c = 0; keywords[c].str != NULL; c++)
+ if (strcmp(keywords[c].str, str) == 0) {
+ val = keywords[c].val;
+ return (keywords[c].tok);
+ }
+
+ return (TOK_STR);
+ }
+ if (isprint(c))
+ errx(1, "%u: unexpected character '%c'", lno, c);
+ else
+ errx(1, "%u: unexpected character 0x%02x", lno, (u_int)c);
+}
+
+/*
+ * Parse the next node (complete with all subnodes)
+ */
+static struct node *
+parse(enum tok tok)
+{
+ struct node *node;
+ struct node *sub;
+ u_int index_count;
+
+ node = xalloc(sizeof(struct node));
+ node->lno = lno;
+
+ if (tok != '(')
+ report("'(' expected at begin of node");
+ if (gettoken() != TOK_NUM)
+ report("node id expected after opening '('");
+ if (val > ASN_MAXID)
+ report("subid too large '%lu'", val);
+ node->id = (asn_subid_t)val;
+ if (gettoken() != TOK_STR)
+ report("node name expected after '(' ID");
+ node->name = savetok();
+
+ if ((tok = gettoken()) == TOK_TYPE) {
+ /* LEAF or COLUM */
+ u_int syntax = val;
+
+ if ((tok = gettoken()) == TOK_STR) {
+ /* LEAF */
+ node->type = NODE_LEAF;
+ node->u.leaf.func = savetok();
+ node->u.leaf.syntax = syntax;
+ tok = gettoken();
+ } else {
+ /* COLUMN */
+ node->type = NODE_COLUMN;
+ node->u.column.syntax = syntax;
+ }
+
+ while (tok != ')') {
+ if (tok != TOK_ACCESS)
+ report("access keyword or ')' expected");
+ node->flags |= (u_int)val;
+ tok = gettoken();
+ }
+
+ } else if (tok == ':') {
+ /* ENTRY */
+ node->type = NODE_ENTRY;
+ TAILQ_INIT(&node->u.entry.subs);
+
+ index_count = 0;
+ node->u.entry.index = 0;
+ while ((tok = gettoken()) == TOK_TYPE) {
+ if (index_count++ == SNMP_INDEXES_MAX)
+ report("too many table indexes");
+ node->u.entry.index |=
+ val << (SNMP_INDEX_SHIFT * index_count);
+ }
+ node->u.entry.index |= index_count;
+ if (index_count == 0)
+ report("need at least one index");
+
+ if (tok != TOK_STR)
+ report("function name expected");
+
+ node->u.entry.func = savetok();
+
+ tok = gettoken();
+
+ while (tok != ')') {
+ sub = parse(tok);
+ TAILQ_INSERT_TAIL(&node->u.entry.subs, sub, link);
+ tok = gettoken();
+ }
+
+ } else {
+ /* subtree */
+ node->type = NODE_TREE;
+ TAILQ_INIT(&node->u.tree.subs);
+
+ while (tok != ')') {
+ sub = parse(tok);
+ TAILQ_INSERT_TAIL(&node->u.tree.subs, sub, link);
+ tok = gettoken();
+ }
+ }
+ return (node);
+}
+
+/*
+ * Generate the C-code table part for one node.
+ */
+static void
+gen_node(struct node *np, struct asn_oid *oid, u_int idx, const char *func)
+{
+ u_int n;
+ struct node *sub;
+ u_int syntax;
+
+ if (oid->len == ASN_MAXOIDLEN)
+ report_node(np, "OID too long");
+ oid->subs[oid->len++] = np->id;
+
+ if (np->type == NODE_TREE) {
+ TAILQ_FOREACH(sub, &np->u.tree.subs, link)
+ gen_node(sub, oid, 0, NULL);
+ oid->len--;
+ return;
+ }
+ if (np->type == NODE_ENTRY) {
+ TAILQ_FOREACH(sub, &np->u.entry.subs, link)
+ gen_node(sub, oid, np->u.entry.index, np->u.entry.func);
+ oid->len--;
+ return;
+ }
+
+ /* leaf or column */
+ if ((np->flags & (FL_GET|FL_SET)) == 0) {
+ oid->len--;
+ return;
+ }
+
+ fprintf(fp, " {{ %u, {", oid->len);
+ for (n = 0; n < oid->len; n++)
+ fprintf(fp, " %u,", oid->subs[n]);
+ fprintf(fp, " }}, \"%s\", ", np->name);
+
+ if (np->type == NODE_COLUMN) {
+ syntax = np->u.column.syntax;
+ fprintf(fp, "SNMP_NODE_COLUMN, ");
+ } else {
+ syntax = np->u.leaf.syntax;
+ fprintf(fp, "SNMP_NODE_LEAF, ");
+ }
+
+ switch (syntax) {
+
+ case SNMP_SYNTAX_NULL:
+ fprintf(fp, "SNMP_SYNTAX_NULL, ");
+ break;
+
+ case SNMP_SYNTAX_INTEGER:
+ fprintf(fp, "SNMP_SYNTAX_INTEGER, ");
+ break;
+
+ case SNMP_SYNTAX_OCTETSTRING:
+ fprintf(fp, "SNMP_SYNTAX_OCTETSTRING, ");
+ break;
+
+ case SNMP_SYNTAX_IPADDRESS:
+ fprintf(fp, "SNMP_SYNTAX_IPADDRESS, ");
+ break;
+
+ case SNMP_SYNTAX_OID:
+ fprintf(fp, "SNMP_SYNTAX_OID, ");
+ break;
+
+ case SNMP_SYNTAX_TIMETICKS:
+ fprintf(fp, "SNMP_SYNTAX_TIMETICKS, ");
+ break;
+
+ case SNMP_SYNTAX_COUNTER:
+ fprintf(fp, "SNMP_SYNTAX_COUNTER, ");
+ break;
+
+ case SNMP_SYNTAX_GAUGE:
+ fprintf(fp, "SNMP_SYNTAX_GAUGE, ");
+ break;
+
+ case SNMP_SYNTAX_COUNTER64:
+ fprintf(fp, "SNMP_SYNTAX_COUNTER64, ");
+ break;
+
+ case SNMP_SYNTAX_NOSUCHOBJECT:
+ case SNMP_SYNTAX_NOSUCHINSTANCE:
+ case SNMP_SYNTAX_ENDOFMIBVIEW:
+ abort();
+ }
+
+ if (np->type == NODE_COLUMN)
+ fprintf(fp, "%s, ", func);
+ else
+ fprintf(fp, "%s, ", np->u.leaf.func);
+
+ fprintf(fp, "0");
+ if (np->flags & FL_SET)
+ fprintf(fp, "|SNMP_NODE_CANSET");
+ fprintf(fp, ", %#x, NULL },\n", idx);
+ oid->len--;
+ return;
+}
+
+/*
+ * Generate the header file with the function declarations.
+ */
+static void
+gen_header(struct node *np, u_int oidlen, const char *func)
+{
+ char f[MAXSTR + 4];
+ struct node *sub;
+ struct func *ptr;
+
+ oidlen++;
+ if (np->type == NODE_TREE) {
+ TAILQ_FOREACH(sub, &np->u.tree.subs, link)
+ gen_header(sub, oidlen, NULL);
+ return;
+ }
+ if (np->type == NODE_ENTRY) {
+ TAILQ_FOREACH(sub, &np->u.entry.subs, link)
+ gen_header(sub, oidlen, np->u.entry.func);
+ return;
+ }
+
+ if((np->flags & (FL_GET|FL_SET)) == 0)
+ return;
+
+ if (np->type == NODE_COLUMN)
+ sprintf(f, "%s", func);
+ else
+ sprintf(f, "%s", np->u.leaf.func);
+
+ LIST_FOREACH(ptr, &funcs, link)
+ if (strcmp(ptr->name, f) == 0)
+ break;
+
+ if (ptr == NULL) {
+ ptr = xalloc(sizeof(*ptr));
+ ptr->name = strcpy(xalloc(strlen(f)+1), f);
+ LIST_INSERT_HEAD(&funcs, ptr, link);
+
+ fprintf(fp, "int %s(struct snmp_context *, "
+ "struct snmp_value *, u_int, u_int, "
+ "enum snmp_op);\n", f);
+ }
+
+ fprintf(fp, "# define LEAF_%s %u\n", np->name, np->id);
+}
+
+/*
+ * Generate the OID table.
+ */
+static void
+gen_table(struct node *node)
+{
+ struct asn_oid oid;
+
+ fprintf(fp, "#include <sys/types.h>\n");
+ fprintf(fp, "#include <stdio.h>\n");
+ if (localincs) {
+ fprintf(fp, "#include \"asn1.h\"\n");
+ fprintf(fp, "#include \"snmp.h\"\n");
+ fprintf(fp, "#include \"snmpagent.h\"\n");
+ } else {
+ fprintf(fp, "#include <bsnmp/asn1.h>\n");
+ fprintf(fp, "#include <bsnmp/snmp.h>\n");
+ fprintf(fp, "#include <bsnmp/snmpagent.h>\n");
+ }
+ fprintf(fp, "#include \"%stree.h\"\n", file_prefix);
+ fprintf(fp, "\n");
+
+ fprintf(fp, "const struct snmp_node %sctree[] = {\n", file_prefix);
+
+ oid.len = PREFIX_LEN;
+ memcpy(oid.subs, prefix, sizeof(prefix));
+ gen_node(node, &oid, 0, NULL);
+
+ fprintf(fp, "};\n\n");
+}
+
+static int
+extract(const struct node *np, struct asn_oid *oid, const char *obj)
+{
+ struct node *sub;
+ u_long n;
+
+ if (oid->len == ASN_MAXOIDLEN)
+ report_node(np, "OID too long");
+ oid->subs[oid->len++] = np->id;
+
+ if (strcmp(obj, np->name) == 0) {
+ fprintf(fp, "#define OID_%s\t%u\n", np->name, np->id);
+ fprintf(fp, "#define OIDLEN_%s\t%u\n", np->name, oid->len);
+ fprintf(fp, "#define OIDX_%s\t{ %u, {", np->name, oid->len);
+ for (n = 0; n < oid->len; n++)
+ fprintf(fp, " %u,", oid->subs[n]);
+ fprintf(fp, " } }\n");
+ return (0);
+ }
+
+ if (np->type == NODE_TREE) {
+ TAILQ_FOREACH(sub, &np->u.tree.subs, link)
+ if (!extract(sub, oid, obj))
+ return (0);
+ } else if (np->type == NODE_ENTRY) {
+ TAILQ_FOREACH(sub, &np->u.entry.subs, link)
+ if (!extract(sub, oid, obj))
+ return (0);
+ }
+ oid->len--;
+ return (1);
+}
+
+static int
+gen_extract(const struct node *root, const char *object)
+{
+ struct asn_oid oid;
+
+ oid.len = PREFIX_LEN;
+ memcpy(oid.subs, prefix, sizeof(prefix));
+ return (extract(root, &oid, object));
+}
+
+
+static void
+check_sub_order(const struct node *np, const struct node_list *subs)
+{
+ int first;
+ const struct node *sub;
+ asn_subid_t maxid = 0;
+
+ /* ensure, that subids are ordered */
+ first = 1;
+ TAILQ_FOREACH(sub, subs, link) {
+ if (!first && sub->id <= maxid)
+ report_node(np, "subids not ordered at %s", sub->name);
+ maxid = sub->id;
+ first = 0;
+ }
+}
+
+/*
+ * Do some sanity checks on the tree definition and do some computations.
+ */
+static void
+check_tree(struct node *np)
+{
+ struct node *sub;
+
+ if (np->type == NODE_LEAF || np->type == NODE_COLUMN) {
+ if ((np->flags & (FL_GET|FL_SET)) != 0)
+ tree_size++;
+ return;
+ }
+
+ if (np->type == NODE_ENTRY) {
+ check_sub_order(np, &np->u.entry.subs);
+
+ /* ensure all subnodes are columns */
+ TAILQ_FOREACH(sub, &np->u.entry.subs, link) {
+ if (sub->type != NODE_COLUMN)
+ report_node(np, "entry subnode '%s' is not "
+ "a column", sub->name);
+ check_tree(sub);
+ }
+ } else {
+ check_sub_order(np, &np->u.tree.subs);
+
+ TAILQ_FOREACH(sub, &np->u.tree.subs, link)
+ check_tree(sub);
+ }
+}
+
+int
+main(int argc, char *argv[])
+{
+ int do_extract = 0;
+ int opt;
+ struct node *root;
+ char fname[MAXPATHLEN + 1];
+
+ while ((opt = getopt(argc, argv, "help:")) != EOF)
+ switch (opt) {
+
+ case 'h':
+ fprintf(stderr, "%s", usgtxt);
+ exit(0);
+
+ case 'e':
+ do_extract = 1;
+ break;
+
+ case 'l':
+ localincs = 1;
+ break;
+
+ case 'p':
+ file_prefix = optarg;
+ if (strlen(file_prefix) + strlen("tree.c") >
+ MAXPATHLEN)
+ errx(1, "prefix too long");
+ break;
+ }
+
+ if (!do_extract && argc != optind)
+ errx(1, "no arguments allowed");
+ if (do_extract && argc == optind)
+ errx(1, "no objects specified");
+
+ root = parse(gettoken());
+ if (gettoken() != TOK_EOF)
+ report("junk after closing ')'");
+
+ check_tree(root);
+
+ if (do_extract) {
+ fp = stdout;
+ while (optind < argc) {
+ if (gen_extract(root, argv[optind]))
+ errx(1, "object not found: %s", argv[optind]);
+ optind++;
+ }
+
+ return (0);
+ }
+ sprintf(fname, "%stree.h", file_prefix);
+ if ((fp = fopen(fname, "w")) == NULL)
+ err(1, "%s: ", fname);
+ gen_header(root, PREFIX_LEN, NULL);
+
+ fprintf(fp, "#define %sCTREE_SIZE %u\n", file_prefix, tree_size);
+ fprintf(fp, "extern const struct snmp_node %sctree[];\n", file_prefix);
+
+ fclose(fp);
+
+ sprintf(fname, "%stree.c", file_prefix);
+ if ((fp = fopen(fname, "w")) == NULL)
+ err(1, "%s: ", fname);
+ gen_table(root);
+ fclose(fp);
+
+ return (0);
+}
diff --git a/contrib/bsnmp/lib/asn1.3 b/contrib/bsnmp/lib/asn1.3
new file mode 100644
index 0000000..3751e50
--- /dev/null
+++ b/contrib/bsnmp/lib/asn1.3
@@ -0,0 +1,484 @@
+.\"
+.\" Copyright (c) 2001-2003
+.\" Fraunhofer Institute for Open Communication Systems (FhG Fokus).
+.\" All rights reserved.
+.\"
+.\" Author: Harti Brandt <harti@freebsd.org>
+.\"
+.\" Redistribution of this software and documentation and use in source and
+.\" binary forms, with or without modification, are permitted provided that
+.\" the following conditions are met:
+.\"
+.\" 1. Redistributions of source code or documentation must retain the above
+.\" copyright notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the Institute nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY FRAUNHOFER FOKUS
+.\" AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+.\" INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+.\" FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+.\" FRAUNHOFER FOKUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+.\" LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+.\" OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+.\" LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+.\" NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+.\" EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $Begemot: bsnmp/lib/asn1.3,v 1.2 2002/08/16 10:02:53 hbb Exp $
+.\"
+.Dd August 15, 2002
+.Dt asn1 3
+.Os
+.Sh NAME
+.Nm asn_get_header ,
+.Nm asn_put_header ,
+.Nm asn_put_temp_header ,
+.Nm asn_commit_header ,
+.Nm asn_get_integer_raw ,
+.Nm asn_get_integer ,
+.Nm asn_put_integer ,
+.Nm asn_get_octetstring_raw ,
+.Nm asn_get_octetstring ,
+.Nm asn_put_octetstring ,
+.Nm asn_get_null_raw ,
+.Nm asn_get_null ,
+.Nm asn_put_null ,
+.Nm asn_put_exception ,
+.Nm asn_get_objid_raw ,
+.Nm asn_get_objid ,
+.Nm asn_put_objid ,
+.Nm asn_get_sequence ,
+.Nm asn_get_ipaddress_raw ,
+.Nm asn_get_ipaddress ,
+.Nm asn_put_ipaddress ,
+.Nm asn_get_uint32_raw ,
+.Nm asn_put_uint32 ,
+.Nm asn_get_counter64_raw ,
+.Nm asn_put_counter64 ,
+.Nm asn_get_timeticks ,
+.Nm asn_put_timeticks ,
+.Nm asn_skip ,
+.Nm asn_slice_oid ,
+.Nm asn_append_oid ,
+.Nm asn_compare_oid ,
+.Nm asn_is_suboid ,
+.Nm asn_oid2str_r ,
+.Nm asn_oid2str
+.Nd "ASN.1 library for SNMP"
+.Sh LIBRARY
+Begemot SNMP library
+.Pq libbsnmp, -lbsnmp
+.Sh SYNOPSIS
+.In bsnmp/asn1.h
+.Ft enum asn_err
+.Fn asn_get_header "struct asn_buf *buf" "u_char *type" "asn_len_t *lenp"
+.Ft enum asn_err
+.Fn asn_put_header "struct asn_buf *buf" "u_char type" "asn_len_t len"
+.Ft enum asn_err
+.Fn asn_put_temp_header "struct asn_buf *buf" "u_char type" "u_char **ptr"
+.Ft enum asn_err
+.Fn asn_commit_header "struct asn_buf *buf" "u_char *ptr"
+.Ft enum asn_err
+.Fn asn_get_integer_raw "struct asn_buf *buf" "asn_len_t len" "int32_t *res"
+.Ft enum asn_err
+.Fn asn_get_integer "struct asn_buf *buf" "int32_t *res"
+.Ft enum asn_err
+.Fn asn_put_integer "struct asn_buf *buf" "int32_t arg"
+.Ft enum asn_err
+.Fn asn_get_octetstring_raw "struct asn_buf *buf" "asn_len_t len" "u_char *out" "u_int *outsize"
+.Ft enum asn_err
+.Fn asn_get_octetstring "struct asn_buf *buf" "u_char *out" "u_int *outsize"
+.Ft enum asn_err
+.Fn asn_put_octetstring "struct asn_buf *buf" "const u_char *str" "u_int strsize"
+.Ft enum asn_err
+.Fn asn_get_null_raw "struct asn_buf *buf" "asn_len_t len"
+.Ft enum asn_err
+.Fn asn_get_null "struct asn_buf *buf"
+.Ft enum asn_err
+.Fn asn_put_null "struct asn_buf *buf"
+.Ft enum asn_err
+.Fn asn_put_exception "struct asn_buf *buf" "u_int type"
+.Ft enum asn_err
+.Fn asn_get_objid_raw "struct asn_buf *buf" "asn_len_t len" "struct asn_oid *oid"
+.Ft enum asn_err
+.Fn asn_get_objid "struct asn_buf *buf" "struct asn_oid *oid"
+.Ft enum asn_err
+.Fn asn_put_objid "struct asn_buf *buf" "const struct asn_oid *oid"
+.Ft enum asn_err
+.Fn asn_get_sequence "struct asn_buf *buf" "asn_len_t *lenp"
+.Ft enum asn_err
+.Fn asn_get_ipaddress_raw "struct asn_buf *buf" "asn_len_t len" "u_char *ipa"
+.Ft enum asn_err
+.Fn asn_get_ipaddress "struct asn_buf *buf" "u_char *ipa"
+.Ft enum asn_err
+.Fn asn_put_ipaddress "struct asn_buf *buf" "const u_char *ipa"
+.Ft enum asn_err
+.Fn asn_get_uint32_raw "struct asn_buf *buf" "asn_len_t len" "u_int32_t *res"
+.Ft enum asn_err
+.Fn asn_put_uint32 "struct asn_buf *buf" "u_char type" "u_int32_t val"
+.Ft enum asn_err
+.Fn asn_get_counter64_raw "struct asn_buf *buf" "asn_len_t len" "u_int64_t *res"
+.Ft enum asn_err
+.Fn asn_put_counter64 "struct asn_buf *buf" "u_int64_t val"
+.Ft enum asn_err
+.Fn asn_get_timeticks "struct asn_buf *buf" "u_int32_t *valp"
+.Ft enum asn_err
+.Fn asn_put_timeticks "struct asn_buf *buf" "u_int32_t val"
+.Ft enum asn_err
+.Fn asn_skip "struct asn_buf *buf" "asn_len_t len"
+.Ft void
+.Fn asn_slice_oid "struct asn_oid *dest" "const struct asn_oid *src" "u_int from" "u_int to"
+.Ft void
+.Fn asn_append_oid "struct asn_oid *to" "const struct asn_oid *from"
+.Ft int
+.Fn asn_compare_oid "const struct asn_oid *oid1" "const struct asn_oid *oid2"
+.Ft int
+.Fn asn_is_suboid "const struct asn_oid *oid1" "const struct asn_oid *oid2"
+.Ft char *
+.Fn asn_oid2str_r "const struct asn_oid *oid" "char *buf"
+.Ft char *
+.Fn asn_oid2str "const struct asn_oid *oid"
+.Sh DESCRIPTION
+The ASN.1 library contains routines to handle ASN.1 encoding for SNMP.
+It supports only the restricted form of ASN.1 as required by SNMP. There
+are two basic structures used throughout the library:
+.Bd -literal -offset indent
+/* these restrictions are in the SMI */
+#define ASN_MAXID 0xffffffff
+#define ASN_MAXOIDLEN 128
+
+/* type of subidentifiers */
+typedef u_int32_t asn_subid_t;
+
+struct asn_oid {
+ u_int len;
+ asn_subid_t subs[ASN_MAXOIDLEN];
+};
+.Ed
+.Pp
+This structure represents an OID with the restrictions defined in the SNMP
+SMI.
+.Fa len
+holds the current length of the OID and
+.Fa subs
+holds the elements of the OID.
+.Bd -literal -offset indent
+struct asn_buf {
+ union {
+ u_char *ptr;
+ const u_char *cptr;
+ } asn_u;
+ size_t asn_len;
+};
+#define asn_cptr asn_u.cptr
+#define asn_ptr asn_u.ptr
+.Ed
+.Pp
+This structure is used to encode and decode ASN.1. It describes the output
+buffer for encoding routines and the input buffer for decoding routines.
+For encoding
+.Fa asn_len
+holds the number of remaining free octets in the buffer. The first free byte
+is pointed to by
+.Fa asn_ptr .
+For decoding
+.Fa asn_len
+holds the number of remaining bytes to decode. The next byte to decode is pointed
+to by
+.Fa asn_cptr .
+.Pp
+Most of the functions return an error code
+.Fa "enum asn_error" :
+.Bd -literal -offset indent
+enum asn_err {
+ /* conversion was ok */
+ ASN_ERR_OK = 0,
+ /* conversion failed and stopped */
+ ASN_ERR_FAILED = 1 | 0x1000,
+ /* length field bad, value skipped */
+ ASN_ERR_BADLEN = 2,
+ /* out of buffer, stopped */
+ ASN_ERR_EOBUF = 3 | 0x1000,
+ /* length ok, but value is out of range */
+ ASN_ERR_RANGE = 4,
+ /* not the expected tag, stopped */
+ ASN_ERR_TAG = 5 | 0x1000,
+};
+#define ASN_ERR_STOPPED(E) (((E) & 0x1000) != 0)
+.Ed
+.Pp
+If
+.Fn ASN_ERR_STOPPED
+returns true, the error was fatal and processing has stopped at the point
+of error.
+.Pp
+The function
+.Fn asn_get_header
+reads the next header from the input octet stream. It returns the tag
+in the variable pointed to by
+.Fa type
+(note that only single byte tags are supported) and the decoded length field
+in the value pointed to by
+.Fa lenp
+(this is restricted to a unsigned 32-bit value). All errors in this function
+are fatal and stop processing.
+.Pp
+The function
+.Fn asn_put_header
+writes an ASN.1 header.
+.Fa type
+is the tag to write and is restricted to one byte tags (i.e. tags
+lesser or equal than 0x30).
+.Fa len
+is the length of the value and is restricted to 16-bit.
+.Pp
+The functions
+.Fn asn_put_temp_header
+and
+.Fn asn_commit_header
+are used to write a header when the length of the value is not known in
+advance, for example, for sequences.
+.Fn asn_put_temp_header
+writes a header with the given tag
+.Fa type
+and space for the maximum supported length field and sets the pointer pointed
+to by
+.Fa ptr
+to the begin of this length field. This pointer must then be fed into
+.Fn asn_commit_header
+directly after writing the value to the buffer. The function will compute the
+length, insert it into the right place and shift the value if the resulting
+length field is shorter than the estimated one.
+.Pp
+The function
+.Fn asn_get_integer_raw
+is used to decode a signed integer value (32-bit). It assumes, that the
+header of the integer has been decoded already.
+.Fa len
+is the length obtained from the ASN.1 header and the integer will be returned
+in the value pointed to by
+.Fa res .
+.Pp
+The function
+.Fn asn_get_integer
+decodes a complete 32-bit signed integer including the header. If the
+tag is wrong
+.Li ASN_ERR_TAG
+is returned.
+The function
+.Fn asn_put_integer
+encodes a 32-bit signed integer.
+.Pp
+The function
+.Fn asn_get_octetstring_raw
+decodes the value field of an ASN.1 octet string. The length obtained from the
+header must be fed into the
+.Fa len
+argument and
+.Fa out
+must point to a buffer to receive the octet string. On entry to the function
+.Fa outsize
+must point to the size of the buffer. On exit
+.Fa outsize
+will point to the number of octets decoded (if no error occurs this will be
+equal to
+.Fa len ).
+The function
+.Fn asn_get_octetstring
+decodes an octetstring including the header.
+.Fa out
+must point to a buffer to receive the string,
+.Fa outsize
+must point to the size of the buffer. On exit of the function
+.Fa outsize
+will point to the number of octets decoded.
+The function
+.Fn asn_put_octetstring
+encodes an octetstring (including the header).
+.Fa str
+points to the string to encode and
+.Fa strsize
+is the length of the string (the string may contain embedded
+.Li NUL Ns s).
+.Pp
+The function
+.Fn asn_get_null_raw
+decodes a null value.
+.Fa len
+is the length obtained from the header and must be 0.
+The function
+.Fn asn_get_null
+decodes a null including the header and the function
+.Fn asn_put_null
+encodes a null.
+.Pp
+The function
+.Fn asn_put_exception
+is used to encode an SNMPv2 exception. The exception type is
+.Fa type .
+.Pp
+The function
+.Fn asn_get_objid_raw
+is used to decode an OID value.
+.Fa len
+must be the value length obtained from the header and
+.Fa oid
+will receive the decoded OID.
+The function
+.Fn asn_get_objid
+decodes a complete OID (including the header) and the function
+.Fn asn_put_objid
+encodes a complete OID.
+.Pp
+The function
+.Fn asn_get_sequence
+decodes a sequence header.
+The length of the sequence value will be stored in the value pointed to by
+.Fa lenp .
+.Pp
+The function
+.Fn asn_get_ipaddress_raw
+decodes an IP address value.
+.Fa len
+is the length from the header and must be 4.
+.Fa ipa
+will receive the decoded IP address and must point to a buffer of at least
+four bytes.
+The function
+.Fn asn_get_ipaddress
+decodes a complete IP address (including the header) and
+.Fn asn_put_ipaddress
+encodes an IP address.
+.Pp
+The function
+.Fn asn_get_uint32_raw
+decodes an unsigned 32-bit integer value.
+.Fa len
+is the length from the header and
+.Fa res
+will get the decoded value.
+The function
+.Fn asn_put_uint32
+encodes an unsigned 32-bit integer value and inserts the tag given in
+.Fa type
+into the header.
+.Pp
+The function
+.Fn asn_get_counter64_raw
+decodes an unsigned 64-bit integer value.
+.Fa len
+must be the value length from the header. The resulting value is
+stored into the variable pointed to by
+.Fa res .
+The function
+.Fn asn_put_counter64
+encodes a complete unsigned 64-bit value.
+.Pp
+The function
+.Fn asn_get_timeticks
+decodes an ASN.1 object of type
+.Li TIMETICKS
+and the function
+.Fn asn_put_timeticks
+encodes such an object.
+.Pp
+The function
+.Fn asn_skip
+can be used to skip
+.Fa len
+bytes in the input buffer.
+.Pp
+The function
+.Fn asn_slice_oid
+splits a part out from an OID. It takes all the subids from the OID
+pointed to by
+.Fa src
+starting with the subid at position
+.Fa from
+(the first subid beeing subid 0) up to, but not including, subid
+.Fa to
+and generates a new OID in
+.Fa dest .
+If
+.Fa to
+is less or equal to
+.Fa from
+the resulting OID will have a length of zero.
+.Pp
+The function
+.Fn asn_append_oid
+appends the OID
+.Fa from
+to the OID
+.Fa to
+given that the resulting OID is not too long. If the maximum length is exceeded
+the result is undefined.
+.Pp
+The function
+.Fn asn_compare_oid
+compares two oids and returns the values
+.Li -1 ,
+.Li 0 or
+.Li +1
+when
+.Fa oid1
+is lesser than, equal, or larger than
+.Fa oid2
+resp.
+.Pp
+The function
+.Fn asn_is_suboid
+returns 1 if
+.Fa oid1
+is equal to the leading part of
+.Fa oid2 .
+It returns 0 otherwise.
+.Pp
+The function
+.Fn asn_oid2str_r
+makes a printable string from
+.Fa oid .
+The buffer pointed to by
+.Fa str
+must be large enough to hold the result. The constant
+.Li ASN_OIDSTRLEN
+is defined to be the length of the maximum string generated by this function
+(including the trailing NUL).
+The function
+.Fn asn_oid2str
+makes a printable string from
+.Fa oid
+into a private buffer that is overwritten by each call.
+.Sh DIAGNOSTICS
+When an error occures in any of the function the function pointed to
+by the global pointer
+.Bd -literal -offset indent
+extern void (*asn_error)(const struct asn_buf *, const char *, ...);
+.Ed
+.Pp
+is called with the current buffer (this may be
+.Li NULL )
+and a
+.Xr printf 3
+style format string.
+There is a default error handler in the library that prints a message
+starting with
+.Sq ASN.1:
+followed by the error message and an optional dump of the buffer.
+.Sh SEE ALSO
+.Xr snmpd 1 ,
+.Xr gensnmptree 1 ,
+.Xr bsnmplib 3
+.Xr bsnmpclient 3 ,
+.Xr bsnmpagent 3
+.Sh STANDARDS
+This implementation conforms to the applicable IETF RFCs and ITU-T
+recommendations.
+.Sh AUTHORS
+.An Hartmut Brandt Aq brandt@fokus.gmd.de
diff --git a/contrib/bsnmp/lib/asn1.c b/contrib/bsnmp/lib/asn1.c
new file mode 100644
index 0000000..533b9dd
--- /dev/null
+++ b/contrib/bsnmp/lib/asn1.c
@@ -0,0 +1,994 @@
+/*
+ * Copyright (c) 2001-2003
+ * Fraunhofer Institute for Open Communication Systems (FhG Fokus).
+ * All rights reserved.
+ *
+ * Author: Harti Brandt <harti@freebsd.org>
+ *
+ * Redistribution of this software and documentation and use in source and
+ * binary forms, with or without modification, are permitted provided that
+ * the following conditions are met:
+ *
+ * 1. Redistributions of source code or documentation must retain the above
+ * copyright notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY FRAUNHOFER FOKUS
+ * AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * FRAUNHOFER FOKUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Begemot: bsnmp/lib/asn1.c,v 1.24 2003/01/28 13:44:34 hbb Exp $
+ *
+ * ASN.1 for SNMP.
+ */
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <inttypes.h>
+#include <assert.h>
+#include "asn1.h"
+
+static void asn_error_func(const struct asn_buf *, const char *, ...);
+
+void (*asn_error)(const struct asn_buf *, const char *, ...) = asn_error_func;
+
+/*
+ * Read the next header. This reads the tag (note, that only single
+ * byte tags are supported for now) and the length field. The length field
+ * is restricted to a 32-bit value.
+ * All errors of this function stop the decoding.
+ */
+enum asn_err
+asn_get_header(struct asn_buf *b, u_char *type, asn_len_t *len)
+{
+ u_int length;
+
+ if (b->asn_len == 0) {
+ asn_error(b, "no identifier for header");
+ return (ASN_ERR_EOBUF);
+ }
+ *type = *b->asn_cptr;
+ if ((*type & ASN_TYPE_MASK) > 0x30) {
+ asn_error(b, "types > 0x30 not supported (%u)",
+ *type & ASN_TYPE_MASK);
+ return (ASN_ERR_FAILED);
+ }
+ b->asn_cptr++;
+ b->asn_len--;
+ if (b->asn_len == 0) {
+ asn_error(b, "no length field");
+ return (ASN_ERR_EOBUF);
+ }
+ if (*b->asn_cptr & 0x80) {
+ length = *b->asn_cptr++ & 0x7f;
+ b->asn_len--;
+ if (length == 0) {
+ asn_error(b, "indefinite length not supported");
+ return (ASN_ERR_FAILED);
+ }
+ if (length > ASN_MAXLENLEN) {
+ asn_error(b, "long length too long (%u)", length);
+ return (ASN_ERR_FAILED);
+ }
+ if (length > b->asn_len) {
+ asn_error(b, "long length truncated");
+ return (ASN_ERR_EOBUF);
+ }
+ *len = 0;
+ while (length--) {
+ *len = (*len << 8) | *b->asn_cptr++;
+ b->asn_len--;
+ }
+ } else {
+ *len = *b->asn_cptr++;
+ b->asn_len--;
+ }
+ return (ASN_ERR_OK);
+}
+
+/*
+ * Write a length field (restricted to values < 2^32-1) and return the
+ * number of bytes this field takes. If ptr is NULL, the length is computed
+ * but nothing is written. If the length would be too large return 0.
+ */
+static u_int
+asn_put_len(u_char *ptr, asn_len_t len)
+{
+ u_int lenlen, lenlen1;
+ asn_len_t tmp;
+
+ if (len > ASN_MAXLEN) {
+ asn_error(NULL, "encoding length too long: (%u)", len);
+ return (0);
+ }
+
+ if (len <= 127) {
+ if (ptr)
+ *ptr++ = (u_char)len;
+ return (1);
+ } else {
+ lenlen = 0;
+ /* compute number of bytes for value (is at least 1) */
+ for (tmp = len; tmp != 0; tmp >>= 8)
+ lenlen++;
+ if (ptr != NULL) {
+ *ptr++ = (u_char)lenlen | 0x80;
+ lenlen1 = lenlen;
+ while (lenlen1-- > 0) {
+ ptr[lenlen1] = len & 0xff;
+ len >>= 8;
+ }
+ }
+ return (lenlen + 1);
+ }
+}
+
+/*
+ * Write a header (tag and length fields).
+ * Tags are restricted to one byte tags (value <= 0x30) and the
+ * lenght field to 16-bit. All errors stop the encoding.
+ */
+enum asn_err
+asn_put_header(struct asn_buf *b, u_char type, asn_len_t len)
+{
+ u_int lenlen;
+
+ /* tag field */
+ if ((type & ASN_TYPE_MASK) > 0x30) {
+ asn_error(NULL, "types > 0x30 not supported (%u)",
+ type & ASN_TYPE_MASK);
+ return (ASN_ERR_FAILED);
+ }
+ if (b->asn_len == 0)
+ return (ASN_ERR_EOBUF);
+
+ *b->asn_ptr++ = type;
+ b->asn_len--;
+
+ /* length field */
+ if ((lenlen = asn_put_len(NULL, len)) == 0)
+ return (ASN_ERR_FAILED);
+ if (b->asn_len < lenlen)
+ return (ASN_ERR_EOBUF);
+
+ (void)asn_put_len(b->asn_ptr, len);
+ b->asn_ptr += lenlen;
+ b->asn_len -= lenlen;
+ return (ASN_ERR_OK);
+}
+
+
+/*
+ * This constructs a temporary sequence header with space for the maximum
+ * length field (three byte). Set the pointer that ptr points to to the
+ * start of the encoded header. This is used for a later call to
+ * asn_commit_header which will fix-up the length field and move the
+ * value if needed. All errors should stop the encoding.
+ */
+#define TEMP_LEN (1 + ASN_MAXLENLEN + 1)
+enum asn_err
+asn_put_temp_header(struct asn_buf *b, u_char type, u_char **ptr)
+{
+ int ret;
+
+ if (b->asn_len < TEMP_LEN)
+ return (ASN_ERR_EOBUF);
+ *ptr = b->asn_ptr;
+ if ((ret = asn_put_header(b, type, ASN_MAXLEN)) == ASN_ERR_OK)
+ assert(b->asn_ptr == *ptr + TEMP_LEN);
+ return (ret);
+}
+enum asn_err
+asn_commit_header(struct asn_buf *b, u_char *ptr)
+{
+ asn_len_t len;
+ u_int lenlen, shift;
+
+ /* compute length of encoded value without header */
+ len = b->asn_ptr - (ptr + TEMP_LEN);
+
+ /* insert length. may not fail. */
+ lenlen = asn_put_len(ptr + 1, len);
+ if (lenlen > TEMP_LEN - 1)
+ return (ASN_ERR_FAILED);
+
+ if (lenlen < TEMP_LEN - 1) {
+ /* shift value down */
+ shift = (TEMP_LEN - 1) - lenlen;
+ memmove(ptr + 1 + lenlen, ptr + TEMP_LEN, len);
+ b->asn_ptr -= shift;
+ b->asn_len += shift;
+ }
+ return (ASN_ERR_OK);
+}
+#undef TEMP_LEN
+
+/*
+ * BER integer. This may be used to get a signed 64 bit integer at maximum.
+ * The maximum length should be checked by the caller. This cannot overflow
+ * if the caller ensures that len is at maximum 8.
+ *
+ * <bytes>
+ */
+static enum asn_err
+asn_get_real_integer(struct asn_buf *b, asn_len_t len, int64_t *vp)
+{
+ u_int64_t val;
+ int neg = 0;
+ enum asn_err err;
+
+ if (b->asn_len < len) {
+ asn_error(b, "truncated integer");
+ return (ASN_ERR_EOBUF);
+ }
+ if (len == 0) {
+ asn_error(b, "zero-length integer");
+ *vp = 0;
+ return (ASN_ERR_BADLEN);
+ }
+ err = ASN_ERR_OK;
+ if (len > 8)
+ err = ASN_ERR_RANGE;
+ if (*b->asn_cptr & 0x80)
+ neg = 1;
+ val = 0;
+ while (len--) {
+ val <<= 8;
+ val |= neg ? (u_char)~*b->asn_cptr : *b->asn_cptr;
+ b->asn_len--;
+ b->asn_cptr++;
+ }
+ if (neg) {
+ *vp = -(int64_t)val - 1;
+ } else
+ *vp = (int64_t)val;
+ return (err);
+}
+
+/*
+ * Write a signed integer with the given type. The caller has to ensure
+ * that the actual value is ok for this type.
+ */
+static enum asn_err
+asn_put_real_integer(struct asn_buf *b, u_char type, int64_t ival)
+{
+ int i, neg = 0;
+# define OCTETS 8
+ u_char buf[OCTETS];
+ u_int64_t val;
+ enum asn_err ret;
+
+ if (ival < 0) {
+ /* this may fail if |INT64_MIN| > |INT64_MAX| and
+ * the value is between * INT64_MIN <= ival < -(INT64_MAX+1) */
+ val = (u_int64_t)-(ival + 1);
+ neg = 1;
+ } else
+ val = (u_int64_t)ival;
+
+ /* split the value into octets */
+ for (i = OCTETS - 1; i >= 0; i--) {
+ buf[i] = val & 0xff;
+ if (neg)
+ buf[i] = ~buf[i];
+ val >>= 8;
+ }
+ /* no leading 9 zeroes or ones */
+ for (i = 0; i < OCTETS - 1; i++)
+ if (!((buf[i] == 0xff && (buf[i + 1] & 0x80) != 0) ||
+ (buf[i] == 0x00 && (buf[i + 1] & 0x80) == 0)))
+ break;
+ if ((ret = asn_put_header(b, type, OCTETS - i)))
+ return (ret);
+ if (OCTETS - (u_int)i > b->asn_len)
+ return (ASN_ERR_EOBUF);
+
+ while (i < OCTETS) {
+ *b->asn_ptr++ = buf[i++];
+ b->asn_len--;
+ }
+ return (ASN_ERR_OK);
+# undef OCTETS
+}
+
+
+/*
+ * The same for unsigned 64-bitters. Here we have the problem, that overflow
+ * can happen, because the value maybe 9 bytes long. In this case the
+ * first byte must be 0.
+ */
+static enum asn_err
+asn_get_real_unsigned(struct asn_buf *b, asn_len_t len, u_int64_t *vp)
+{
+ enum asn_err err;
+
+ if (b->asn_len < len) {
+ asn_error(b, "truncated integer");
+ return (ASN_ERR_EOBUF);
+ }
+ if (len == 0) {
+ asn_error(b, "zero-length integer");
+ *vp = 0;
+ return (ASN_ERR_BADLEN);
+ }
+ err = ASN_ERR_OK;
+ *vp = 0;
+ if ((*b->asn_cptr & 0x80) || (len == 9 && *b->asn_cptr != 0)) {
+ /* negative integer or too larger */
+ *vp = 0xffffffffffffffffULL;
+ err = ASN_ERR_RANGE;
+ }
+
+ while (len--) {
+ *vp = (*vp << 8) | *b->asn_cptr++;
+ b->asn_len--;
+ }
+ return (err);
+}
+
+
+/*
+ * Values with the msb on need 9 octets.
+ */
+static int
+asn_put_real_unsigned(struct asn_buf *b, u_char type, u_int64_t val)
+{
+ int i;
+# define OCTETS 9
+ u_char buf[OCTETS];
+ enum asn_err ret;
+
+ /* split the value into octets */
+ for (i = OCTETS - 1; i >= 0; i--) {
+ buf[i] = val & 0xff;
+ val >>= 8;
+ }
+ /* no leading 9 zeroes */
+ for (i = 0; i < OCTETS - 1; i++)
+ if (!(buf[i] == 0x00 && (buf[i + 1] & 0x80) == 0))
+ break;
+ if ((ret = asn_put_header(b, type, OCTETS - i)))
+ return (ret);
+ if (OCTETS - (u_int)i > b->asn_len)
+ return (ASN_ERR_EOBUF);
+
+ while (i < OCTETS) {
+ *b->asn_ptr++ = buf[i++];
+ b->asn_len--;
+ }
+#undef OCTETS
+ return (ASN_ERR_OK);
+}
+
+/*
+ * The ASN.1 INTEGER type is restricted to 32-bit signed by the SMI.
+ */
+enum asn_err
+asn_get_integer_raw(struct asn_buf *b, asn_len_t len, int32_t *vp)
+{
+ int64_t val;
+ enum asn_err ret;
+
+ if ((ret = asn_get_real_integer(b, len, &val)) == ASN_ERR_OK) {
+ if (len > 4)
+ ret = ASN_ERR_BADLEN;
+ else if (val > INT32_MAX || val < INT32_MIN)
+ /* may not happen */
+ ret = ASN_ERR_RANGE;
+ *vp = (int32_t)val;
+ }
+ return (ret);
+}
+
+enum asn_err
+asn_get_integer(struct asn_buf *b, int32_t *vp)
+{
+ asn_len_t len;
+ u_char type;
+ enum asn_err err;
+
+ if ((err = asn_get_header(b, &type, &len)) != ASN_ERR_OK)
+ return (err);
+ if (type != ASN_TYPE_INTEGER) {
+ asn_error(b, "bad type for integer (%u)", type);
+ return (ASN_ERR_TAG);
+ }
+
+ return (asn_get_integer_raw(b, len, vp));
+}
+
+enum asn_err
+asn_put_integer(struct asn_buf *b, int32_t val)
+{
+ return (asn_put_real_integer(b, ASN_TYPE_INTEGER, val));
+}
+
+/*
+ * OCTETSTRING
+ *
+ * <0x04> <len> <data ...>
+ *
+ * Get an octetstring. noctets must point to the buffer size and on
+ * return will contain the size of the octetstring, regardless of the
+ * buffer size.
+ */
+enum asn_err
+asn_get_octetstring_raw(struct asn_buf *b, asn_len_t len, u_char *octets,
+ u_int *noctets)
+{
+ enum asn_err err = ASN_ERR_OK;
+
+ if (*noctets < len) {
+ asn_error(b, "octetstring truncated");
+ err = ASN_ERR_RANGE;
+ }
+ if (b->asn_len < len) {
+ asn_error(b, "truncatet octetstring");
+ return (ASN_ERR_EOBUF);
+ }
+ if (*noctets < len)
+ memcpy(octets, b->asn_cptr, *noctets);
+ else
+ memcpy(octets, b->asn_cptr, len);
+ *noctets = len;
+ b->asn_cptr += len;
+ b->asn_len -= len;
+ return (err);
+}
+
+enum asn_err
+asn_get_octetstring(struct asn_buf *b, u_char *octets, u_int *noctets)
+{
+ enum asn_err err;
+ u_char type;
+ asn_len_t len;
+
+ if ((err = asn_get_header(b, &type, &len)) != ASN_ERR_OK)
+ return (err);
+ if (type != ASN_TYPE_OCTETSTRING) {
+ asn_error(b, "bad type for octetstring (%u)", type);
+ return (ASN_ERR_TAG);
+ }
+ return (asn_get_octetstring_raw(b, len, octets, noctets));
+}
+
+enum asn_err
+asn_put_octetstring(struct asn_buf *b, const u_char *octets, u_int noctets)
+{
+ enum asn_err ret;
+
+ if ((ret = asn_put_header(b, ASN_TYPE_OCTETSTRING, noctets)) != ASN_ERR_OK)
+ return (ret);
+ if (b->asn_len < noctets)
+ return (ASN_ERR_EOBUF);
+
+ memcpy(b->asn_ptr, octets, noctets);
+ b->asn_ptr += noctets;
+ b->asn_len -= noctets;
+ return (ASN_ERR_OK);
+}
+
+/*
+ * NULL
+ *
+ * <0x05> <0x00>
+ */
+enum asn_err
+asn_get_null_raw(struct asn_buf *b, asn_len_t len)
+{
+ if (len != 0) {
+ if (b->asn_len < len) {
+ asn_error(b, "truncated NULL");
+ return (ASN_ERR_EOBUF);
+ }
+ asn_error(b, "bad length for NULL (%u)", len);
+ b->asn_len -= len;
+ b->asn_ptr += len;
+ return (ASN_ERR_BADLEN);
+ }
+ return (ASN_ERR_OK);
+}
+
+enum asn_err
+asn_get_null(struct asn_buf *b)
+{
+ u_char type;
+ asn_len_t len;
+ enum asn_err err;
+
+ if ((err = asn_get_header(b, &type, &len)) != ASN_ERR_OK)
+ return (err);
+ if (type != ASN_TYPE_NULL) {
+ asn_error(b, "bad type for NULL (%u)", type);
+ return (ASN_ERR_TAG);
+ }
+ return (asn_get_null_raw(b, len));
+}
+
+enum asn_err
+asn_put_null(struct asn_buf *b)
+{
+ return (asn_put_header(b, ASN_TYPE_NULL, 0));
+}
+
+enum asn_err
+asn_put_exception(struct asn_buf *b, u_int except)
+{
+ return (asn_put_header(b, ASN_CLASS_CONTEXT | except, 0));
+}
+
+/*
+ * OBJID
+ *
+ * <0x06> <len> <subid...>
+ */
+enum asn_err
+asn_get_objid_raw(struct asn_buf *b, asn_len_t len, struct asn_oid *oid)
+{
+ asn_subid_t subid;
+ enum asn_err err;
+
+ if (b->asn_len < len) {
+ asn_error(b, "truncated OBJID");
+ return (ASN_ERR_EOBUF);
+ }
+ oid->len = 0;
+ if (len == 0) {
+ asn_error(b, "short OBJID");
+ oid->subs[oid->len++] = 0;
+ oid->subs[oid->len++] = 0;
+ return (ASN_ERR_BADLEN);
+ }
+ err = ASN_ERR_OK;
+ while (len != 0) {
+ if (oid->len == ASN_MAXOIDLEN) {
+ asn_error(b, "OID too long (%u)", oid->len);
+ b->asn_cptr += len;
+ b->asn_len -= len;
+ return (ASN_ERR_BADLEN);
+ }
+ subid = 0;
+ do {
+ if (len == 0) {
+ asn_error(b, "unterminated subid");
+ return (ASN_ERR_EOBUF);
+ }
+ if (subid > (ASN_MAXID >> 7)) {
+ asn_error(b, "OBID subid too larger");
+ err = ASN_ERR_RANGE;
+ }
+ subid = (subid << 7) | (*b->asn_cptr & 0x7f);
+ len--;
+ b->asn_len--;
+ } while (*b->asn_cptr++ & 0x80);
+ if (oid->len == 0) {
+ if (subid < 80) {
+ oid->subs[oid->len++] = subid / 40;
+ oid->subs[oid->len++] = subid % 40;
+ } else {
+ oid->subs[oid->len++] = 2;
+ oid->subs[oid->len++] = subid - 80;
+ }
+ } else {
+ oid->subs[oid->len++] = subid;
+ }
+ }
+ return (err);
+
+}
+
+enum asn_err
+asn_get_objid(struct asn_buf *b, struct asn_oid *oid)
+{
+ u_char type;
+ asn_len_t len;
+ enum asn_err err;
+
+ if ((err = asn_get_header(b, &type, &len)) != ASN_ERR_OK)
+ return (err);
+ if (type != ASN_TYPE_OBJID) {
+ asn_error(b, "bad type for OBJID (%u)", type);
+ return (ASN_ERR_TAG);
+ }
+ return (asn_get_objid_raw(b, len, oid));
+}
+
+enum asn_err
+asn_put_objid(struct asn_buf *b, const struct asn_oid *oid)
+{
+ asn_subid_t first, sub;
+ enum asn_err err, err1;
+ u_int i, oidlen;
+ asn_len_t len;
+
+ err = ASN_ERR_OK;
+ if (oid->len == 0) {
+ /* illegal */
+ asn_error(NULL, "short oid");
+ err = ASN_ERR_RANGE;
+ first = 0;
+ oidlen = 2;
+ } else if (oid->len == 1) {
+ /* illegal */
+ asn_error(b, "short oid");
+ if (oid->subs[0] > 2)
+ asn_error(NULL, "oid[0] too large (%u)", oid->subs[0]);
+ err = ASN_ERR_RANGE;
+ first = oid->subs[0] * 40;
+ oidlen = 2;
+ } else {
+ if (oid->len > ASN_MAXOIDLEN) {
+ asn_error(NULL, "oid too long %u", oid->len);
+ err = ASN_ERR_RANGE;
+ }
+ if (oid->subs[0] > 2 ||
+ (oid->subs[0] < 2 && oid->subs[0] >= 40)) {
+ asn_error(NULL, "oid out of range (%u,%u)",
+ oid->subs[0], oid->subs[1]);
+ err = ASN_ERR_RANGE;
+ }
+ first = 40 * oid->subs[0] + oid->subs[1];
+ oidlen = oid->len;
+ }
+ len = 0;
+ for (i = 1; i < oidlen; i++) {
+ sub = (i == 1) ? first : oid->subs[i];
+ if (sub > ASN_MAXID) {
+ asn_error(NULL, "oid subid too large");
+ err = ASN_ERR_RANGE;
+ }
+ len += (sub <= 0x7f) ? 1
+ : (sub <= 0x3fff) ? 2
+ : (sub <= 0x1fffff) ? 3
+ : (sub <= 0xfffffff) ? 4
+ : 5;
+ }
+ if ((err1 = asn_put_header(b, ASN_TYPE_OBJID, len)) != ASN_ERR_OK)
+ return (err1);
+ if (b->asn_len < len)
+ return (ASN_ERR_EOBUF);
+
+ for (i = 1; i < oidlen; i++) {
+ sub = (i == 1) ? first : oid->subs[i];
+ if (sub <= 0x7f) {
+ *b->asn_ptr++ = sub;
+ b->asn_len--;
+ } else if (sub <= 0x3fff) {
+ *b->asn_ptr++ = (sub >> 7) | 0x80;
+ *b->asn_ptr++ = sub & 0x7f;
+ b->asn_len -= 2;
+ } else if (sub <= 0x1fffff) {
+ *b->asn_ptr++ = (sub >> 14) | 0x80;
+ *b->asn_ptr++ = ((sub >> 7) & 0x7f) | 0x80;
+ *b->asn_ptr++ = sub & 0x7f;
+ b->asn_len -= 3;
+ } else if (sub <= 0xfffffff) {
+ *b->asn_ptr++ = (sub >> 21) | 0x80;
+ *b->asn_ptr++ = ((sub >> 14) & 0x7f) | 0x80;
+ *b->asn_ptr++ = ((sub >> 7) & 0x7f) | 0x80;
+ *b->asn_ptr++ = sub & 0x7f;
+ b->asn_len -= 4;
+ } else {
+ *b->asn_ptr++ = (sub >> 28) | 0x80;
+ *b->asn_ptr++ = ((sub >> 21) & 0x7f) | 0x80;
+ *b->asn_ptr++ = ((sub >> 14) & 0x7f) | 0x80;
+ *b->asn_ptr++ = ((sub >> 7) & 0x7f) | 0x80;
+ *b->asn_ptr++ = sub & 0x7f;
+ b->asn_len -= 5;
+ }
+ }
+ return (err);
+}
+/*
+ * SEQUENCE header
+ *
+ * <0x10|0x20> <len> <data...>
+ */
+enum asn_err
+asn_get_sequence(struct asn_buf *b, asn_len_t *len)
+{
+ u_char type;
+ enum asn_err err;
+
+ if ((err = asn_get_header(b, &type, len)) != ASN_ERR_OK)
+ return (err);
+ if (type != (ASN_TYPE_SEQUENCE|ASN_TYPE_CONSTRUCTED)) {
+ asn_error(b, "bad sequence type %u", type);
+ return (ASN_ERR_TAG);
+ }
+ if (*len > b->asn_len) {
+ asn_error(b, "truncated sequence");
+ return (ASN_ERR_EOBUF);
+ }
+ return (ASN_ERR_OK);
+}
+
+
+/*
+ * Application types
+ *
+ * 0x40 4 MSB 2MSB 2LSB LSB
+ */
+enum asn_err
+asn_get_ipaddress_raw(struct asn_buf *b, asn_len_t len, u_char *addr)
+{
+ u_int i;
+
+ if (b->asn_len < len) {
+ asn_error(b, "truncated ip-address");
+ return (ASN_ERR_EOBUF);
+ }
+ if (len < 4) {
+ asn_error(b, "short length for ip-Address %u", len);
+ for (i = 0; i < len; i++)
+ *addr++ = *b->asn_cptr++;
+ while (i++ < len)
+ *addr++ = 0;
+ b->asn_len -= len;
+ return (ASN_ERR_BADLEN);
+ }
+ for (i = 0; i < 4; i++)
+ *addr++ = *b->asn_cptr++;
+ b->asn_cptr += len - 4;
+ b->asn_len -= len;
+ return (ASN_ERR_OK);
+}
+
+enum asn_err
+asn_get_ipaddress(struct asn_buf *b, u_char *addr)
+{
+ u_char type;
+ asn_len_t len;
+ enum asn_err err;
+
+ if ((err = asn_get_header(b, &type, &len)) != ASN_ERR_OK)
+ return (err);
+ if (type != (ASN_CLASS_APPLICATION|ASN_APP_IPADDRESS)) {
+ asn_error(b, "bad type for ip-address %u", type);
+ return (ASN_ERR_TAG);
+ }
+ return (asn_get_ipaddress_raw(b, len, addr));
+}
+
+enum asn_err
+asn_put_ipaddress(struct asn_buf *b, const u_char *addr)
+{
+ enum asn_err err;
+
+ if ((err = asn_put_header(b, ASN_CLASS_APPLICATION|ASN_APP_IPADDRESS,
+ 4)) != ASN_ERR_OK)
+ return (err);
+ if (b->asn_len < 4)
+ return (ASN_ERR_EOBUF);
+
+ memcpy(b->asn_ptr, addr, 4);
+ b->asn_ptr += 4;
+ b->asn_len -= 4;
+ return (ASN_ERR_OK);
+}
+
+
+/*
+ * UNSIGNED32
+ *
+ * 0x42|0x41 <len> ...
+ */
+enum asn_err
+asn_get_uint32_raw(struct asn_buf *b, asn_len_t len, u_int32_t *vp)
+{
+ u_int64_t v;
+ enum asn_err err;
+
+ if ((err = asn_get_real_unsigned(b, len, &v)) == ASN_ERR_OK) {
+ if (len > 5) {
+ asn_error(b, "uint32 too long %u", len);
+ err = ASN_ERR_BADLEN;
+ } else if (v > UINT32_MAX) {
+ asn_error(b, "uint32 too large %llu", v);
+ err = ASN_ERR_RANGE;
+ }
+ *vp = (u_int32_t)v;
+ }
+ return (err);
+}
+
+enum asn_err
+asn_put_uint32(struct asn_buf *b, u_char type, u_int32_t val)
+{
+ u_int64_t v = val;
+
+ return (asn_put_real_unsigned(b, ASN_CLASS_APPLICATION|type, v));
+}
+
+/*
+ * COUNTER64
+ * 0x46 <len> ...
+ */
+enum asn_err
+asn_get_counter64_raw(struct asn_buf *b, asn_len_t len, u_int64_t *vp)
+{
+ return (asn_get_real_unsigned(b, len, vp));
+}
+
+enum asn_err
+asn_put_counter64(struct asn_buf *b, u_int64_t val)
+{
+ return (asn_put_real_unsigned(b,
+ ASN_CLASS_APPLICATION | ASN_APP_COUNTER64, val));
+}
+
+/*
+ * TimeTicks
+ * 0x43 <len> ...
+ */
+enum asn_err
+asn_get_timeticks(struct asn_buf *b, u_int32_t *vp)
+{
+ asn_len_t len;
+ u_char type;
+ enum asn_err err;
+
+ if ((err = asn_get_header(b, &type, &len)) != ASN_ERR_OK)
+ return (err);
+ if (type != (ASN_CLASS_APPLICATION|ASN_APP_TIMETICKS)) {
+ asn_error(b, "bad type for timeticks %u", type);
+ return (ASN_ERR_TAG);
+ }
+ return (asn_get_uint32_raw(b, len, vp));
+}
+
+enum asn_err
+asn_put_timeticks(struct asn_buf *b, u_int32_t val)
+{
+ u_int64_t v = val;
+
+ return (asn_put_real_unsigned(b,
+ ASN_CLASS_APPLICATION | ASN_APP_TIMETICKS, v));
+}
+
+/*
+ * Construct a new OID by taking a range of sub ids of the original oid.
+ */
+void
+asn_slice_oid(struct asn_oid *dest, const struct asn_oid *src,
+ u_int from, u_int to)
+{
+ if (from >= to) {
+ dest->len = 0;
+ return;
+ }
+ dest->len = to - from;
+ memcpy(dest->subs, &src->subs[from], dest->len * sizeof(dest->subs[0]));
+}
+
+/*
+ * Append from to to
+ */
+void
+asn_append_oid(struct asn_oid *to, const struct asn_oid *from)
+{
+ memcpy(&to->subs[to->len], &from->subs[0],
+ from->len * sizeof(from->subs[0]));
+ to->len += from->len;
+}
+
+/*
+ * Skip a value
+ */
+enum asn_err
+asn_skip(struct asn_buf *b, asn_len_t len)
+{
+ if (b->asn_len < len)
+ return (ASN_ERR_EOBUF);
+ b->asn_cptr += len;
+ b->asn_len -= len;
+ return (ASN_ERR_OK);
+}
+
+/*
+ * Compare two OIDs.
+ *
+ * o1 < o2 : -1
+ * o1 > o2 : +1
+ * o1 = o2 : 0
+ */
+int
+asn_compare_oid(const struct asn_oid *o1, const struct asn_oid *o2)
+{
+ u_long i;
+
+ for (i = 0; i < o1->len && i < o2->len; i++) {
+ if (o1->subs[i] < o2->subs[i])
+ return (-1);
+ if (o1->subs[i] > o2->subs[i])
+ return (+1);
+ }
+ if (o1->len < o2->len)
+ return (-1);
+ if (o1->len > o2->len)
+ return (+1);
+ return (0);
+}
+
+/*
+ * Check whether an OID is a sub-string of another OID.
+ */
+int
+asn_is_suboid(const struct asn_oid *o1, const struct asn_oid *o2)
+{
+ u_long i;
+
+ for (i = 0; i < o1->len; i++)
+ if (i >= o2->len || o1->subs[i] != o2->subs[i])
+ return (0);
+ return (1);
+}
+
+/*
+ * Put a string representation of an oid into a user buffer. This buffer
+ * is assumed to be at least ASN_OIDSTRLEN characters long.
+ *
+ * sprintf is assumed not to fail here.
+ */
+char *
+asn_oid2str_r(const struct asn_oid *oid, char *buf)
+{
+ u_int len, i;
+ char *ptr;
+
+ if ((len = oid->len) > ASN_MAXOIDLEN)
+ len = ASN_MAXOIDLEN;
+ buf[0] = '\0';
+ for (i = 0, ptr = buf; i < len; i++) {
+ if (i > 0)
+ *ptr++ = '.';
+ ptr += sprintf(ptr, "%u", oid->subs[i]);
+ }
+ return (buf);
+}
+
+/*
+ * Make a string from an OID in a private buffer.
+ */
+char *
+asn_oid2str(const struct asn_oid *oid)
+{
+ static char str[ASN_OIDSTRLEN];
+
+ return (asn_oid2str_r(oid, str));
+}
+
+
+static void
+asn_error_func(const struct asn_buf *b, const char *err, ...)
+{
+ va_list ap;
+ u_long i;
+
+ fprintf(stderr, "ASN.1: ");
+ va_start(ap, err);
+ vfprintf(stderr, err, ap);
+ va_end(ap);
+
+ if (b != NULL) {
+ fprintf(stderr, " at");
+ for (i = 0; b->asn_len > i; i++)
+ fprintf(stderr, " %02x", b->asn_cptr[i]);
+ }
+ fprintf(stderr, "\n");
+}
diff --git a/contrib/bsnmp/lib/asn1.h b/contrib/bsnmp/lib/asn1.h
new file mode 100644
index 0000000..83a31e5
--- /dev/null
+++ b/contrib/bsnmp/lib/asn1.h
@@ -0,0 +1,186 @@
+/*
+ * Copyright (c) 2001-2003
+ * Fraunhofer Institute for Open Communication Systems (FhG Fokus).
+ * All rights reserved.
+ *
+ * Author: Harti Brandt <harti@freebsd.org>
+ *
+ * Redistribution of this software and documentation and use in source and
+ * binary forms, with or without modification, are permitted provided that
+ * the following conditions are met:
+ *
+ * 1. Redistributions of source code or documentation must retain the above
+ * copyright notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY FRAUNHOFER FOKUS
+ * AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * FRAUNHOFER FOKUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Begemot: bsnmp/lib/asn1.h,v 1.16 2002/02/11 10:19:57 hbb Exp $
+ *
+ * ASN.1 for SNMP
+ */
+#ifndef asn1_h_
+#define asn1_h_
+
+#include <sys/types.h>
+
+struct asn_buf {
+ union {
+ u_char *ptr;
+ const u_char *cptr;
+ } asn_u;
+ size_t asn_len;
+};
+#define asn_cptr asn_u.cptr
+#define asn_ptr asn_u.ptr
+
+/* these restrictions are in the SMI */
+#define ASN_MAXID 0xffffffff
+#define ASN_MAXOIDLEN 128
+
+/* the string needed for this (with trailing zero) */
+#define ASN_OIDSTRLEN (ASN_MAXOIDLEN * (10 + 1) - 1 + 1)
+
+/* type of subidentifiers */
+typedef u_int32_t asn_subid_t;
+
+struct asn_oid {
+ u_int len;
+ asn_subid_t subs[ASN_MAXOIDLEN];
+};
+
+enum asn_err {
+ /* conversion was ok */
+ ASN_ERR_OK = 0,
+ /* conversion failed and stopped */
+ ASN_ERR_FAILED = 1 | 0x1000,
+ /* length field bad, value skipped */
+ ASN_ERR_BADLEN = 2,
+ /* out of buffer, stopped */
+ ASN_ERR_EOBUF = 3 | 0x1000,
+ /* length ok, but value is out of range */
+ ASN_ERR_RANGE = 4,
+ /* not the expected tag, stopped */
+ ASN_ERR_TAG = 5 | 0x1000,
+};
+#define ASN_ERR_STOPPED(E) (((E) & 0x1000) != 0)
+
+/* type for the length field of encoded values. The length is restricted
+ * to 65535, but using u_int16_t would give conversion warnings on gcc */
+typedef u_int32_t asn_len_t; /* could be also u_int16_t */
+
+/* maximal length of a long length field without the length of the length */
+#define ASN_MAXLEN 65535
+#define ASN_MAXLENLEN 2 /* number of bytes in a length */
+
+/* maximum size of an octet string as per SMIv2 */
+#define ASN_MAXOCTETSTRING 65535
+
+extern void (*asn_error)(const struct asn_buf *, const char *, ...);
+
+enum asn_err asn_get_header(struct asn_buf *, u_char *, asn_len_t *);
+enum asn_err asn_put_header(struct asn_buf *, u_char, asn_len_t);
+
+enum asn_err asn_put_temp_header(struct asn_buf *, u_char, u_char **);
+enum asn_err asn_commit_header(struct asn_buf *, u_char *);
+
+enum asn_err asn_get_integer_raw(struct asn_buf *, asn_len_t, int32_t *);
+enum asn_err asn_get_integer(struct asn_buf *, int32_t *);
+enum asn_err asn_put_integer(struct asn_buf *, int32_t);
+
+enum asn_err asn_get_octetstring_raw(struct asn_buf *, asn_len_t, u_char *, u_int *);
+enum asn_err asn_get_octetstring(struct asn_buf *, u_char *, u_int *);
+enum asn_err asn_put_octetstring(struct asn_buf *, const u_char *, u_int);
+
+enum asn_err asn_get_null_raw(struct asn_buf *b, asn_len_t);
+enum asn_err asn_get_null(struct asn_buf *);
+enum asn_err asn_put_null(struct asn_buf *);
+
+enum asn_err asn_put_exception(struct asn_buf *, u_int);
+
+enum asn_err asn_get_objid_raw(struct asn_buf *, asn_len_t, struct asn_oid *);
+enum asn_err asn_get_objid(struct asn_buf *, struct asn_oid *);
+enum asn_err asn_put_objid(struct asn_buf *, const struct asn_oid *);
+
+enum asn_err asn_get_sequence(struct asn_buf *, asn_len_t *);
+
+enum asn_err asn_get_ipaddress_raw(struct asn_buf *, asn_len_t, u_char *);
+enum asn_err asn_get_ipaddress(struct asn_buf *, u_char *);
+enum asn_err asn_put_ipaddress(struct asn_buf *, const u_char *);
+
+enum asn_err asn_get_uint32_raw(struct asn_buf *, asn_len_t, u_int32_t *);
+enum asn_err asn_put_uint32(struct asn_buf *, u_char, u_int32_t);
+
+enum asn_err asn_get_counter64_raw(struct asn_buf *, asn_len_t, u_int64_t *);
+enum asn_err asn_put_counter64(struct asn_buf *, u_int64_t);
+
+enum asn_err asn_get_timeticks(struct asn_buf *, u_int32_t *);
+enum asn_err asn_put_timeticks(struct asn_buf *, u_int32_t);
+
+enum asn_err asn_skip(struct asn_buf *, asn_len_t);
+
+/*
+ * Utility functions for OIDs
+ */
+/* get a sub-OID from the middle of another OID */
+void asn_slice_oid(struct asn_oid *, const struct asn_oid *, u_int, u_int);
+
+/* append an OID to another one */
+void asn_append_oid(struct asn_oid *, const struct asn_oid *);
+
+/* compare two OIDs */
+int asn_compare_oid(const struct asn_oid *, const struct asn_oid *);
+
+/* check whether the first is a suboid of the second one */
+int asn_is_suboid(const struct asn_oid *, const struct asn_oid *);
+
+/* format an OID into a user buffer of size ASN_OIDSTRLEN */
+char *asn_oid2str_r(const struct asn_oid *, char *);
+
+/* format an OID into a private static buffer */
+char *asn_oid2str(const struct asn_oid *);
+
+enum {
+ ASN_TYPE_BOOLEAN = 0x01,
+ ASN_TYPE_INTEGER = 0x02,
+ ASN_TYPE_BITSTRING = 0x03,
+ ASN_TYPE_OCTETSTRING = 0x04,
+ ASN_TYPE_NULL = 0x05,
+ ASN_TYPE_OBJID = 0x06,
+ ASN_TYPE_SEQUENCE = 0x10,
+
+ ASN_TYPE_CONSTRUCTED = 0x20,
+ ASN_CLASS_UNIVERSAL = 0x00,
+ ASN_CLASS_APPLICATION = 0x40,
+ ASN_CLASS_CONTEXT = 0x80,
+ ASN_CLASS_PRIVATE = 0xc0,
+ ASN_TYPE_MASK = 0x1f,
+
+ ASN_APP_IPADDRESS = 0x00,
+ ASN_APP_COUNTER = 0x01,
+ ASN_APP_GAUGE = 0x02,
+ ASN_APP_TIMETICKS = 0x03,
+ ASN_APP_OPAQUE = 0x04, /* not implemented */
+ ASN_APP_COUNTER64 = 0x06,
+
+ ASN_EXCEPT_NOSUCHOBJECT = 0x00,
+ ASN_EXCEPT_NOSUCHINSTANCE = 0x01,
+ ASN_EXCEPT_ENDOFMIBVIEW = 0x02,
+};
+
+#endif
diff --git a/contrib/bsnmp/lib/bsnmpagent.3 b/contrib/bsnmp/lib/bsnmpagent.3
new file mode 100644
index 0000000..a9a8104
--- /dev/null
+++ b/contrib/bsnmp/lib/bsnmpagent.3
@@ -0,0 +1,417 @@
+.\"
+.\" Copyright (c) 2001-2003
+.\" Fraunhofer Institute for Open Communication Systems (FhG Fokus).
+.\" All rights reserved.
+.\"
+.\" Author: Harti Brandt <harti@freebsd.org>
+.\"
+.\" Redistribution of this software and documentation and use in source and
+.\" binary forms, with or without modification, are permitted provided that
+.\" the following conditions are met:
+.\"
+.\" 1. Redistributions of source code or documentation must retain the above
+.\" copyright notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the Institute nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY FRAUNHOFER FOKUS
+.\" AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+.\" INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+.\" FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+.\" FRAUNHOFER FOKUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+.\" LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+.\" OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+.\" LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+.\" NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+.\" EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $Begemot: bsnmp/lib/bsnmpagent.3,v 1.1 2002/08/16 12:22:58 hbb Exp $
+.\"
+.Dd August 16, 2002
+.Dt bsnmpagent 3
+.Os
+.Sh NAME
+.Nm snmp_depop_t ,
+.Nm snmp_set_finish_t ,
+.Nm snmp_op_t ,
+.Nm tree ,
+.Nm tree_size ,
+.Nm snmp_trace ,
+.Nm snmp_debug ,
+.Nm snmp_get ,
+.Nm snmp_getnext ,
+.Nm snmp_getbulk ,
+.Nm snmp_set ,
+.Nm snmp_make_errresp ,
+.Nm snmp_dep_lookup ,
+.Nm snmp_set_atfinish ,
+.Nm snmp_init_context ,
+.Nm snmp_dep_commit ,
+.Nm snmp_dep_rollback
+.Nd "SNMP agent library"
+.Sh LIBRARY
+Begemot SNMP library
+.Pq libbsnmp, -lbsnmp
+.Sh SYNOPSIS
+.In asn1.h
+.In snmp.h
+.In snmpagent.h
+.Ft typedef int
+.Fn (*snmp_depop_t) "struct snmp_context *ctx" "struct snmp_dependency *dep" "enum snmp_depop op"
+.Ft typedef void
+.Fn (*snmp_set_finish_t) "struct snmp_context *ctx" "int fail" "void *uarg"
+.Ft typedef int
+.Fn (*snmp_op_t) "struct snmp_context *ctx" "struct snmp_value *val" "u_int len" "u_int idx" "enum snmp_op op"
+.Vt extern struct snmp_node *tree ;
+.Vt extern u_int tree_size ;
+.Vt extern u_int snmp_trace ;
+.Vt extern void (*snmp_debug)(const char *fmt, ...) ;
+.Ft enum snmp_ret
+.Fn snmp_get "struct snmp_pdu *pdu" "struct asn_buf *resp_b" "struct snmp_pdu *resp" "void *data"
+.Ft enum snmp_ret
+.Fn snmp_getnext "struct snmp_pdu *pdu" "struct asn_buf *resp_b" "struct snmp_pdu *resp" "void *data"
+.Ft enum snmp_ret
+.Fn snmp_getbulk "struct snmp_pdu *pdu" "struct asn_buf *resp_b" "struct snmp_pdu *resp" "void *data"
+.Ft enum snmp_ret
+.Fn snmp_set "struct snmp_pdu *pdu" "struct asn_buf *resp_b" "struct snmp_pdu *resp" "void *data"
+.Ft enum snmp_ret
+.Fn snmp_make_errresp "const struct snmp_pdu *pdu" "struct asn_buf *req_b" "struct asn_buf *resp_b"
+.Ft struct snmp_dependency *
+.Fn snmp_dep_lookup "struct snmp_context *ctx" "const struct asn_oid *base" "const struct asn_oid *idx" "size_t alloc" "snmp_depop_t func"
+.Ft int
+.Fn snmp_set_atfinish "struct snmp_context *ctx" "snmp_set_finish_t func" "void *uarg"
+.Ft struct snmp_context *
+.Fn snmp_init_context "void"
+.Ft int
+.Fn snmp_dep_commit "struct snmp_context *ctx"
+.Ft int
+.Fn snmp_dep_rollback "struct snmp_context *ctx"
+.Sh DESCRIPTION
+The SNMP library contains routines to easily build SNMP agent applications
+that use SNMP versions 1 or 2. Note, however, that it may be even easier to
+build an
+.Xr snmpd 1
+loadable module, that handles the new MIB (see
+.Xr snmpmod 3 ).
+.Pp
+Most of the agent routines operate on a global array that the describes the
+complete MIB served by the agent. This array is held in the two variables:
+.Bd -literal -offset indent
+extern struct snmp_node *tree;
+extern u_int tree_size;
+.Ed
+.Pp
+The elements of the array are of type
+.Vt struct snmp_node :
+.Bd -literal -offset indent
+typedef int (*snmp_op_t)(struct snmp_context *, struct snmp_value *,
+ u_int, u_int, enum snmp_op);
+
+struct snmp_node {
+ struct asn_oid oid;
+ const char *name; /* name of the leaf */
+ enum snmp_node_type type; /* type of this node */
+ enum snmp_syntax syntax;
+ snmp_op_t op;
+ u_int flags;
+ u_int32_t index; /* index data */
+ void *data; /* application data */
+};
+.Ed
+.Pp
+The fields of this structure are described below.
+.Bl -tag -width "syntax"
+.It Va oid
+Base OID of the scalar or table column.
+.It Va name
+Name of this variable.
+.It Va type
+Type of this variable. One of:
+.Bd -literal -offset indent
+enum snmp_node_type {
+ SNMP_NODE_LEAF = 1,
+ SNMP_NODE_COLUMN
+};
+.Ed
+.It Va syntax
+The SNMP syntax of this variable.
+.It Va op
+The user supplied handler for this variable. The handler is called with
+the following arguments:
+.Bl -tag -width "ctx"
+.It Fa ctx
+A pointer to the context (see below).
+.Li NULL .
+.It Fa val
+The value to be set or retrieved. For GETNEXT and GETBULK operations the oid in
+this value is the current OID. The function (called in this case only for
+table rows) must find the lexically next existing OID within the same column and
+set the oid and value subfields accordingly. If the table column is exhausted the
+function must return
+.Li SNMP_ERR_NOSUCHNAME .
+For all other operations the oid in
+.Fa val
+is the oid to fetch or set.
+.It Fa len
+The length of the base oid without index.
+.It Fa idx
+For table columns this is the index expression from the node (see below).
+.It Fa op
+This is the operation to execute, one of:
+.Bd -literal -offset indent
+enum snmp_op {
+ SNMP_OP_GET = 1,
+ SNMP_OP_GETNEXT,
+ SNMP_OP_SET,
+ SNMP_OP_COMMIT,
+ SNMP_OP_ROLLBACK,
+};
+.Ed
+.El
+.Pp
+The user handler must return an appropiate SNMP v2 error code. If the original
+PDU was a version 1 PDU, the error code is mapped automatically.
+.It Va flags
+Currently only the flag
+.Li SNMP_NODE_CANSET is defined and set for nodes, that can be written or
+created.
+.It Va index
+This word describes the index for table columns. Each part of the index
+takes 4 bits starting at bit 4. Bits 0 to 3 hold the number of index parts.
+This arrangment allows for tables with up to seven indexes. Each bit group
+contains the syntax for the index part. There are a number of macros to
+help in parsing this field:
+.Bd -literal -offset indent
+#define SNMP_INDEXES_MAX 7
+#define SNMP_INDEX_SHIFT 4
+#define SNMP_INDEX_MASK 0xf
+#define SNMP_INDEX_COUNT(V) ((V) & SNMP_INDEX_MASK)
+#define SNMP_INDEX(V,I) \e
+ (((V) >> (((I) + 1) * SNMP_INDEX_SHIFT)) & \e
+ SNMP_INDEX_MASK)
+.Ed
+.It Va data
+This field may contain arbitrary data and is not used by the library.
+.El
+.Pp
+The easiest way to construct the node table is
+.Xr gensnmptree 1 .
+Note, that one must be careful when changing the tree while executing a SET
+operation. Consult the sources for
+.Xr snmpd 1 .
+.Pp
+The global variable
+.Va snmp_trace
+together with the function pointed to by
+.Va snmp_debug
+help in debugging the library and the agent.
+.Va snmp_trace is a bit mask with the following bits:
+.Bd -literal -offset indent
+enum {
+ SNMP_TRACE_GET,
+ SNMP_TRACE_GETNEXT,
+ SNMP_TRACE_SET,
+ SNMP_TRACE_DEPEND,
+ SNMP_TRACE_FIND,
+};
+.Ed
+.Pp
+Setting a bit to true causes the library to call
+.Fn snmp_debug
+in strategic places with a debug string. The library contains a default
+implementation for the debug function that prints a message to standard error.
+.Pp
+Many of the functions use a so called context:
+.Bd -literal -offset indent
+struct snmp_context {
+ u_int var_index;
+ struct snmp_scratch *scratch;
+ struct snmp_dependency *dep;
+ void *data; /* user data */
+};
+
+struct snmp_scratch {
+ void *ptr1;
+ void *ptr2;
+ u_int32_t int1;
+ u_int32_t int2;
+};
+.Ed
+.Pp
+The fields are used as follows:
+.Bl -tag -width ".It Va var_index"
+.It Va va_index
+For the node operation callback this is the
+index of the variable binding that should be returned if an error occures.
+Set by the library. In all other functions this is undefined.
+.It Va scratch
+For the node operation callback this is a pointer to a per variable binding
+scratch area that can be used to implement the commit and rollback. Set
+by the library.
+.It Va dep
+In the dependency callback function (see below) this is a pointer to the
+current dependency. Set by the library.
+.It Va data
+This is the
+.Fa data
+argument from the call to the library and is not used by the library.
+.El
+.Pp
+The next three functions execute different kinds of GET requests.
+The function
+.Fn snmp_get
+executes an SNMP GET operation, the function
+.Fn snmp_getnext
+executes an SNMP GETNEXT operation and the function
+.Fn snmp_getbulk
+executes an SNMP GETBULK operation.
+For all three functions the response PDU is constructed and encoded
+on the fly. If everything is ok, the response PDU is returned in
+.Fa resp
+and
+.Fa resp_b .
+The caller must call
+.Fn snmp_pdu_free
+to free the response PDU in this case. One of the following values may be
+returned:
+.Bl -tag -width ".It Li SNMP_RET_ERR"
+.It Li SNMP_RET_OK
+Operation successful, response PDU may be sent.
+.It Li SNMP_RET_IGN
+Operation failed, no response PDU constructed. Request is ignored.
+.It Li SNMP_RET_ERR
+Error in operation. The error code and index have been set in
+.Fa pdu .
+No response PDU has been constructed.
+The caller may construct an error response PDU via
+.Fn snmp_make_errresp .
+.El
+.Pp
+The function
+.Fn snmp_set
+executes an SNMP SET operation. The arguments are the same as for the previous
+three functions. The operation of this functions is, however, much more complex.
+.Pp
+The SET operation occures in several stages:
+.Bl -enum -offset indent
+.It
+For each binding search the corresponding nodes, check that the
+variable is writeable and the syntax is ok. The writeable check can be done
+only for scalars. For columns it must be done in the node's operation callback
+function.
+.It
+For each binding call the node's operation callback with function SNMP_OP_SET.
+The callback may create dependencies or finalizers (see below). For simple
+scalars the scratch area may be enough to handle commit and rollback, for
+interdependend table columns dependencies may be necessary.
+.It
+If the previous step fails at any point, the node's operation callback
+functions are called for all bindings for which SNMP_OP_SET was executed
+with SNMP_OP_ROLLBACK, in the opposite order. This allows all variables to
+undo the effect of the SET operation. After this all the dependencies
+are freed
+and the finalizers are executed with a fail flag of 1. Then the function
+returns to the caller with an appropriate error indication.
+.It
+If the SET step was successful for all bindings, the dependency callbacks
+are executed in the order in which the dependencies were created with an
+operation of SNMP_DEPOP_COMMIT. If any of the dependencies fails, all the
+committed dependencies are called again in the opposite order
+with SNMP_DEPOP_ROLLBACK. Than for all bindings from the last to the first
+the node's operation callback is called with SNMP_OP_ROLLBACK to undo
+the effect of SNMP_OP_SET. At the end the dependencies are freed
+and the finalizers are called with a fail flag
+of 1 and the function returns to the caller with an appropriate error indication.
+.It
+If the dependency commits were successful, for each binding the node's
+operation callback is called with SNMP_OP_COMMIT. Any error returned from
+the callbacks is ignored (an error message is generated via
+.Fn snmp_error ).
+.It
+Now the dependencies are freed and the finalizers are called
+with a fail flag of 0. Then the function returns
+.Li SNMP_ERR_OK .
+.El
+.Pp
+There are to mechanisms to help in complex SET operations: dependencies and
+finalizers. A dependency is used if several bindings depend on each other.
+A typical example is the creation of a conceptual row, which requires
+the setting of several columns to succeed. A dependency is identified by
+two OIDs. In the table case, the first oid is typically the table's base OID
+and the second one the index. Both of these can easily be generated from the
+variables OID with
+.Fn asn_slice_oid .
+The function
+.Fn snmp_dep_lookup
+tries to find a dependency based on these two OIDs and, if it cannot find one
+creates a new one. This means for the table example, that the function
+returns the same dependency for each of the columns of the same table row.
+This allows during the SNMP_OP_SET processing to collect all information
+about the row into the dependency. The arguments to
+.Fn snmp_dep_lookup
+are: the two OIDs to identify the dependency (they are copied into newly
+created dependencies), the size of the structure to allocate and
+the dependency callback.
+.Pp
+When all SNMP_OP_SET operations have succeeded the dependencies are executed.
+At this stage the dependency callback has all information about the given
+table row that was available in this SET PDU and can operate accordingly.
+.Pp
+If a SNMP_OP_SET operation fails, the dependency callbacks are never
+called. The nodes SNMP_OP_ROLLBACK operations have to ensure, that
+any dynamically allocated data is freed.
+.Pp
+Finalizers are a
+.Sq last change
+to do processing.
+They are called after everything has been done, just before returning to the
+user. They get a flag, that tells them, whether the return to the user is a good
+one or not. The typical use is to finally remove deleted table elements.
+Finalizers are created with
+.Fn snmp_set_atfinish
+which takes the callback function and a user data pointer as argument.
+.Pp
+The function
+.Fn snmp_make_errresp
+makes an error response if an operation has failed. It takes the original
+request PDU (it will look only on the error code and index fields), the
+buffer containing the original PDU and a buffer for the error PDU. It copies
+the bindings field from the original PDUs buffer directly to the response
+PDU and thus does not depend on the decodability of this field. It may return
+the same values as the operation functions.
+.Pp
+The next three functions allow some parts of the SET operation to be executed.
+This is only used in
+.Xr snmpd 1
+to implement the configuration as a single transaction.
+The function
+.Fn snmp_init_context
+creates and initializes a context.
+The function
+.Fn snmp_dep_commit
+executes SNMP_DEPOP_COMMIT for all dependencies in the context stopping at
+the first error.
+The function
+.Fn snmp_dep_rollback
+executes SNMP_DEPOP_ROLLBACK starting at the previous of the current
+dependency in the context.
+.Sh DIAGNOSTICS
+If an error occures in any of the function an error indication as described
+above is returned. Additionally the functions may call snmp_error on unexected
+errors.
+.Sh SEE ALSO
+.Xr snmpd 1 ,
+.Xr gensnmptree 1 ,
+.Xr bsnmplib 3
+.Xr bsnmpclient 3 ,
+.Xr snmpmod 3
+.Sh STANDARDS
+This implementation conforms to the applicable IETF RFCs and ITU-T
+recommendations.
+.Sh AUTHORS
+.An Hartmut Brandt Aq brandt@fokus.gmd.de
diff --git a/contrib/bsnmp/lib/bsnmpclient.3 b/contrib/bsnmp/lib/bsnmpclient.3
new file mode 100644
index 0000000..f4a7eac
--- /dev/null
+++ b/contrib/bsnmp/lib/bsnmpclient.3
@@ -0,0 +1,590 @@
+.\"
+.\" Copyright (c) 2001-2003
+.\" Fraunhofer Institute for Open Communication Systems (FhG Fokus).
+.\" All rights reserved.
+.\"
+.\" Author: Harti Brandt <harti@freebsd.org>
+.\"
+.\" Redistribution of this software and documentation and use in source and
+.\" binary forms, with or without modification, are permitted provided that
+.\" the following conditions are met:
+.\"
+.\" 1. Redistributions of source code or documentation must retain the above
+.\" copyright notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the Institute nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY FRAUNHOFER FOKUS
+.\" AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+.\" INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+.\" FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+.\" FRAUNHOFER FOKUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+.\" LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+.\" OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+.\" LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+.\" NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+.\" EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $Begemot: bsnmp/lib/bsnmpclient.3,v 1.3 2002/12/11 15:54:07 hbb Exp $
+.\"
+.Dd August 15, 2002
+.Dt bsnmpclient 3
+.Os
+.Sh NAME
+.Nm snmp_client ,
+.Nm snmp_send_cb_f ,
+.Nm snmp_timeout_cb_f ,
+.Nm snmp_timeout_start_f ,
+.Nm snmp_timeout_stop_f ,
+.Nm snmp_open ,
+.Nm snmp_close ,
+.Nm snmp_pdu_create ,
+.Nm snmp_add_binding ,
+.Nm snmp_pdu_check ,
+.Nm snmp_pdu_send ,
+.Nm snmp_oid_append ,
+.Nm snmp_receive ,
+.Nm snmp_table_cb_f ,
+.Nm snmp_table_fetch ,
+.Nm snmp_table_fetch_async ,
+.Nm snmp_dialog
+.Nd "SNMP client library"
+.Sh LIBRARY
+Begemot SNMP library
+.Pq libbsnmp, -lbsnmp
+.Sh SYNOPSIS
+.In asn1.h
+.In snmp.h
+.In snmpclient.h
+.Ft typedef void
+.Fn (*snmp_send_cb_f) "struct snmp_pdu *req" "struct snmp_pdu *resp" "void *uarg"
+.Ft typedef void
+.Fn (*snmp_timeout_cb_f) "void *uarg"
+.Ft typedef void *
+.Fn (*snmp_timeout_start_f) "struct timeval *timeout" "snmp_timeout_cb_f callback" "void *uarg"
+.Ft typedef void
+.Fn (*snmp_timeout_stop_f) "void *timeout_id"
+.Vt extern struct snmp_client snmp_client ;
+.Ft void
+.Fn snmp_client_init "struct snmp_client *client"
+.Ft int
+.Fn snmp_client_set_host "struct snmp_client *client" "const char *host"
+.Ft int
+.Fn snmp_client_set_port "struct snmp_client *client" "const char *port"
+.Ft int
+.Fn snmp_open "const char *host" "const char *port" "const char *read_community" "const char *write_community"
+.Ft void
+.Fn snmp_close "void"
+.Ft void
+.Fn snmp_pdu_create "struct snmp_pdu *pdu" "u_int op"
+.Ft int
+.Fn snmp_add_binding "struct snmp_pdu *pdu" "..."
+.Ft int
+.Fn snmp_pdu_check "const struct snmp_pdu *req" "const struct snmp_pdu *resp"
+.Ft int32_t
+.Fn snmp_pdu_send "struct snmp_pdu *pdu" "snmp_send_cb_f func" "void *uarg"
+.Ft int
+.Fn snmp_oid_append "struct asn_oid *oid" "const char *fmt" "..."
+.Ft int
+.Fn snmp_receive "int blocking"
+.Ft typedef void
+.Fn (*snmp_table_cb_f) "void *list" "void *arg" "int res"
+.Ft int
+.Fn snmp_table_fetch "const struct snmp_table *descr" "void *list"
+.Ft int
+.Fn snmp_table_fetch_async "const struct snmp_table *descr" "void *list" "snmp_table_cb_f callback" "void *uarg"
+.Ft int
+.Fn snmp_dialog "struct snmp_pdu *req" "struct snmp_pdu *resp"
+.Sh DESCRIPTION
+The SNMP library contains routines to easily build SNMP client applications
+that use SNMP versions 1 or 2. Most of the routines use a
+.Vt struct snmp_client :
+.Bd -literal -offset indent
+struct snmp_client {
+ enum snmp_version version;
+ int local; /* use local socket */
+
+ /* these two are read-only for the application */
+ char *cport; /* port number as string */
+ char *chost; /* host name or IP address as string */
+
+ char read_community[SNMP_COMMUNITY_MAXLEN + 1];
+ char write_community[SNMP_COMMUNITY_MAXLEN + 1];
+
+ struct timeval timeout;
+ u_int retries;
+
+ int dump_pdus;
+
+ size_t txbuflen;
+ size_t rxbuflen;
+
+ int fd;
+
+ int32_t next_reqid;
+ int32_t max_reqid;
+ int32_t min_reqid;
+
+ char error[SNMP_STRERROR_LEN];
+
+ snmp_timeout_start_f timeout_start;
+ snmp_timeout_stop_f timeout_stop;
+
+ /* private */
+ char local_path[sizeof(SNMP_LOCAL_PATH)];
+};
+.Ed
+.Pp
+The fields of this structure are described below.
+.Bl -tag -width "timeout_start"
+.It Va version
+This is the version of SNMP to use. See
+.Xr bsnmplib 3
+for applicable values. The default version is
+.Li SNMP_V2c .
+.It Va local
+If this is set to true, the library opens a
+.Ux
+domain socket rather than
+an UDP socket. It uses the
+.Va chost
+field as the path to the server's socket.
+.It Va cport
+The SNMP agent's UDP port number. This may be a symbolic port number (from
+.Pa /etc/services
+or a numeric port number. If this field is
+.Li NULL
+(the default) the standard SNMP port is used. This field should not be changed
+directly but rather by calling
+.Fn snmp_client_set_port .
+.It Va chost
+The SNMP agent's host name, IP address or
+.Ux
+domain socket path name.
+If this is
+.Li NULL
+(the default)
+.Li localhost
+is assumed. This field should not be changed directly but rather through
+calling
+.Fn snmp_client_set_host .
+.It Va read_community
+This is the community name to be used for all requests except SET requests.
+The default is
+.Sq public .
+.It Va write_community
+The community name to be used for SET requests. The default is
+.Sq private .
+.It Va timeout
+The maximum time to wait for responses to requests. If the time elapses, the
+request is resent up to
+.Va retries
+times. The default is 3 seconds.
+.It Va retries
+Number of times a request PDU is to be resent. If set to 0, the request is
+sent only once. The default is 3 retransmissions.
+.It Va dump_pdus
+If set to a non-zero value all received and sent PDUs are dumped via
+.Xr snmp_pdu_dump 3 .
+The default is not to dump PDUs.
+.It Va txbuflen
+The encoding buffer size to be allocated for transmitted PDUs. The default is
+10000 octets.
+.It Va rxbuflen
+The decoding buffer size to be allocated for received PDUs. This is the size
+of the maximum PDU that can be received. The default is 10000 octets.
+.It Va fd
+After calling
+.Fn snmp_open
+this is the file socket file descriptor used for sending and receiving PDUs.
+.It Va next_reqid
+The request id of the next PDU to send. Used internal by the library.
+.It Va max_reqid
+The maximum request id to use for outging PDUs. The default is
+.Li INT32_MAX .
+.It Va min_reqid
+The minimum request id to use for outgoing PDUs. Request ids are allocated
+linerily starting at
+.Va min_reqid
+up to
+.Va max_reqid .
+.It Va error
+If an error happens, this field is set to a printable string describing the
+error.
+.It Va timeout_start
+This field must point to a function setting up a one shot timeout. After the
+timeout has elapsed, the given callback function must be called with the
+user argument. The
+.Fn timeout_start
+function must return a
+.Vt void *
+identifying the timeout.
+.It Va timeout_stop
+This field must be set to a function that stops a running timeout. The function
+will be called with the return value of the corresponding
+.Fn timeout_start
+function.
+.It Va local_path
+If in local socket mode, the name of the clients socket. Not needed by the
+application.
+.El
+.Pp
+In the current implementation there is a global variable
+.Bd -unfilled -offset indent
+.Vt extern struct snmp_client snmp_client ;
+.Ed
+.Pp
+that is used by all the library functions. The first call into the library must
+be a call to
+.Fn snmp_client_init
+to initialize this global variable to the default values.
+After this call and before calling
+.Fn snmp_open
+the fields of the variable may be modified by the user.
+The modification of the
+.Va chost
+and
+.Va cport
+fields should be done only via the functions
+.Fn snmp_client_set_host
+and
+.Fn snmp_client_set_port .
+.Pp
+The function
+.Fn snmp_open
+creates a UDP or
+.Ux
+domain socket and connects it to the agent's IP address and port.
+If any of the arguments of the call is not
+.Li NULL
+the corresponding field in the global
+.Va snmp_client
+is set from the argument. Otherwise the values that are already in that variable
+are used.
+The function
+.Fn snmp_close
+closes the socket, stops all timeouts and frees all dynamically allocated
+resources.
+.Pp
+The next three functions are used to create request PDUs. The function
+.Fn snmp_pdu_create
+initializes a PDU of type
+.Va op .
+It does not allocate space for the PDU itself. This is the responsibility of
+the caller.
+.Fn snmp_add_binding
+adds bindings to the PDU and returns the (zero based) index of the first new
+binding. The arguments are pairs of pointer to the OIDs and syntax constants,
+terminated by a NULL. The call
+.Bd -literal -offset indent
+snmp_add_binding(&pdu,
+ &oid1, SNMP_SYNTAX_INTEGER,
+ &oid2, SNMP_SYNTAX_OCTETSTRING,
+ NULL);
+.Ed
+.Pp
+adds two new bindings to the PDU and returns the index of the first one.
+It is the responsibility of the caller to set the value part of the binding
+if neccesary. The functions returns -1 if the maximum number of bindings
+is exhausted.
+The function
+.Fn snmp_oid_append
+can be used to construct variable OIDs for requests. It takes a pointer
+to an
+.Vt struct asn_oid
+that is to be constructed, a format string, and a number of arguments
+the type of which depends on the format string. The format string is interpreted
+character by character in the following way:
+.Bl -tag -width ".It Li ( Va N Ns Li )"
+.It Li i
+This format expects an argument of type
+.Vt asn_subid_t
+and appends this as a single integer to the OID.
+.It Li a
+This format expects an argument of type
+.Vt struct in_addr
+and appends to four parts of the IP address to the OID.
+.It Li s
+This format expects an argument of type
+.Vt const char *
+and appends the length of the string (as computed by
+.Xr strlen 3 )
+and each of the characters in the string to the OID.
+.It Li ( Va N Ns Li )
+This format expects no argument.
+.Va N
+must be a decimal number and is stored into an internal variable
+.Va size .
+.It Li b
+This format expects an argument of type
+.Vt const char *
+and appends
+.Va size
+characters from the string to the OID. The string may contain
+.Li NUL
+characters.
+.It Li c
+This format expects two arguments: one of type
+.Vt size_t
+and one of type
+.Vt const u_char * .
+The first argument gives the number of bytes to append to the OID from the string
+pointed to by the second argument.
+.El
+.Pp
+The function
+.Fn snmp_pdu_check
+may be used to check a response PDU. A number of checks are performed
+(error code, equal number of bindings, syntaxes and values for SET PDUs).
+The function returns +1 if everything is ok, 0 if a NOSUCHNAME or similar
+error was detected, -1 if the response PDU had fatal errors
+and -2 if
+.Fa resp
+is
+.Li NULL
+(a timeout occured).
+.Pp
+The function
+.Fn snmp_pdu_send
+encodes and sends the given PDU. It records the PDU together with the callback
+and user pointers in an internal list and arranges for retransmission if no
+response is received. When a response is received or the retransmission count
+is exceeded the callback
+.Fa func
+is called with the orignal request PDU, the response PDU and the user argument
+.Fa uarg .
+If the retransmit count is exceeded,
+.Fa func
+is called with the original request PDU, the reponse pointer set to
+.Li NULL
+and the user argument
+.Fa uarg .
+The caller should not free the request PDU until the callback function is
+called. The callback function must free the request PDU and the response
+PDU (if not
+.Li NULL ).
+.Pp
+The function
+.Fn snmp_receive
+tries to receive a PDU. If the argument is zero, the function polls to see
+whether a packet is available, if the argument is non-zero, the function blocks
+until the next packet is received. The packet is delivered via the usual callback
+mechanism (non-response packets are silently dropped).
+The function returns 0, if a packet was received and successfully dispatched,
+-1 if an error occured or no packet was available (in polling mode).
+.Pp
+The next two functions are used to retrieve tables from SNMP agents. The use
+the following input structure, that describes the table:
+.Bd -literal -offset indent
+struct snmp_table {
+ struct asn_oid table;
+ struct asn_oid last_change;
+ u_int max_iter;
+ size_t entry_size;
+ u_int index_size;
+ u_int64_t req_mask;
+
+ struct snmp_table_entry {
+ asn_subid_t subid;
+ enum snmp_syntax syntax;
+ off_t offset;
+ } entries[];
+};
+.Ed
+.Pp
+The fields of this structure have the following meaning:
+.Bl -tag -width "last_change"
+.It Va table
+This is the base OID of the table.
+.It Va last_change
+Some tables have a scalar variable of type TIMETICKS attached to them,
+that holds the time when the table was last changed. This OID should be
+the OID of this variable (without the \&.0 index). When the table is retrieved
+with multiple GET requests, and the variable changes between two request,
+the table fetch is restarted.
+.It Va max_iter
+Maximum number of tries to fetch the table.
+.It Va entry_size
+The table fetching routines return a list of structure one for each table
+row. This variable is the size of one structure and used to
+.Xr malloc 3
+the structure.
+.It Va index_size
+This is the number of index columns in the table.
+.It Va req_mask
+This is a bit mask with a 1 for each table column that is required.
+Bit 0 corresponds to the first element (index 0) in the array
+.Va entries ,
+bit 1 to the second (index 1) and so on. SNMP tables may be sparse. For sparse
+columns the bit should not be set. If the bit for a given column is set and
+the column value cannot be retrieved for a given row, the table fetch is
+restarted assuming that the table is currently beeing modified by the agent.
+The bits for the index columns are ignored.
+.It Va entries
+This is a variable sized array of column descriptors. This array is terminated
+by an element with syntax
+.Li SNMP_SYNTAX_NULL .
+The first
+.Va index_size
+elements describe all the index columns of the table, the rest are normal
+columns. If for a the column at
+.Ql entries[N]
+the expression
+.Ql req_mask & (1 << N)
+yields true, the column is considered a required column.
+The fields of this the array elements have the following meaning:
+.Bl -tag -width "syntax"
+.It Va subid
+This is the OID subid of the column. This is ignored for index entries. Index
+entries are decoded according to the
+.Va syntax
+field.
+.It Va syntax
+This is the syntax of the column or index. A syntax of
+.Li SNMP_SYNTAX_NULL
+terminates the array.
+.It Va offset
+This is the starting offset of the value of the column in the return structures.
+This field can be set with the ISO-C
+.Fn offsetof
+macro.
+.El
+.El
+.Pp
+Both table fetching functions return TAILQ (see
+.Xr queue 3 )
+of structures--one for each table row. These structures must start with a
+.Fn TAILQ_ENTRY
+and a
+.Vt u_int64_t
+and are allocated via
+.Xr malloc 3 .
+The
+.Fa list
+argument of the table functions must point to a
+.Fn TAILQ_HEAD .
+The
+.Vt u_int64_t
+fields, usually called
+.Va found
+is used to indicate which of the columns have been found for the given
+row. It is encoded like the
+.Fa req_mask
+field.
+.Pp
+The function
+.Fn snmp_table_fetch
+synchronuosly fetches the given table. If everything is ok 0 is returned.
+Otherwise the function returns -1 and sets an appropriate error string.
+The function
+.Fn snmp_table_fetch_async
+fetches the tables asynchronuosly. If either the entire table is fetch, or
+an error occures the callback function
+.Fa callback
+is called with the callers arguments
+.Fa list
+and
+.Fa uarg
+and a parameter that is either 0 if the table was fetched, or
+-1 if there was an error. The function itself returns -1 if it could not
+initialize fetching of the table.
+.Pp
+The following table description is used to fetch the ATM interface table:
+.Bd -literal -offset indent
+/*
+ * ATM interface table
+ */
+struct atmif {
+ TAILQ_ENTRY(atmif) link;
+ u_int64_t found;
+ int32_t index;
+ u_char *ifname;
+ size_t ifnamelen;
+ u_int32_t node_id;
+ u_int32_t pcr;
+ int32_t media;
+ u_int32_t vpi_bits;
+ u_int32_t vci_bits;
+ u_int32_t max_vpcs;
+ u_int32_t max_vccs;
+ u_char *esi;
+ size_t esilen;
+ int32_t carrier;
+};
+TAILQ_HEAD(atmif_list, atmif);
+
+/* list of all ATM interfaces */
+struct atmif_list atmif_list;
+
+static const struct snmp_table atmif_table = {
+ OIDX_begemotAtmIfTable,
+ OIDX_begemotAtmIfTableLastChange, 2,
+ sizeof(struct atmif),
+ 1, 0x7ffULL,
+ {
+ { 0, SNMP_SYNTAX_INTEGER,
+ offsetof(struct atmif, index) },
+ { 1, SNMP_SYNTAX_OCTETSTRING,
+ offsetof(struct atmif, ifname) },
+ { 2, SNMP_SYNTAX_GAUGE,
+ offsetof(struct atmif, node_id) },
+ { 3, SNMP_SYNTAX_GAUGE,
+ offsetof(struct atmif, pcr) },
+ { 4, SNMP_SYNTAX_INTEGER,
+ offsetof(struct atmif, media) },
+ { 5, SNMP_SYNTAX_GAUGE,
+ offsetof(struct atmif, vpi_bits) },
+ { 6, SNMP_SYNTAX_GAUGE,
+ offsetof(struct atmif, vci_bits) },
+ { 7, SNMP_SYNTAX_GAUGE,
+ offsetof(struct atmif, max_vpcs) },
+ { 8, SNMP_SYNTAX_GAUGE,
+ offsetof(struct atmif, max_vccs) },
+ { 9, SNMP_SYNTAX_OCTETSTRING,
+ offsetof(struct atmif, esi) },
+ { 10, SNMP_SYNTAX_INTEGER,
+ offsetof(struct atmif, carrier) },
+ { 0, SNMP_SYNTAX_NULL, 0 }
+ }
+};
+
+\&...
+ if (snmp_table_fetch(&atmif_table, &atmif_list) != 0)
+ errx(1, "AtmIf table: %s", snmp_client.error);
+\&...
+.Ed
+.Pp
+The function
+.Fn snmp_dialog
+is used to execute a synchonuous dialog with the agent. The request PDU
+.Fa req
+is sent and the function blocks until the response PDU is received. Note,
+that asynchonuous receives are handled (i.e. callback functions of other send
+calls or table fetches may be called while in the function). The response
+PDU is returned in
+.Fa resp .
+If no reponse could be received after all timeouts and retries, the function
+returns -1. If a response was received 0 is returned.
+.Sh DIAGNOSTICS
+If an error occures in any of the function an error indication as described
+above is returned. Additionally the function sets a printable error string
+in the
+.Va error
+filed of
+.Va snmp_client .
+.Sh SEE ALSO
+.Xr snmpd 1 ,
+.Xr gensnmptree 1 ,
+.Xr bsnmplib 3
+.Xr bsnmpagent 3
+.Sh STANDARDS
+This implementation conforms to the applicable IETF RFCs and ITU-T
+recommendations.
+.Sh AUTHORS
+.An Hartmut Brandt Aq brandt@fokus.gmd.de
+.An Kendy Kutzner Aq kutzner@fokus.gmd.de
diff --git a/contrib/bsnmp/lib/bsnmplib.3 b/contrib/bsnmp/lib/bsnmplib.3
new file mode 100644
index 0000000..13f9651
--- /dev/null
+++ b/contrib/bsnmp/lib/bsnmplib.3
@@ -0,0 +1,306 @@
+.\"
+.\" Copyright (c) 2001-2003
+.\" Fraunhofer Institute for Open Communication Systems (FhG Fokus).
+.\" All rights reserved.
+.\"
+.\" Author: Harti Brandt <harti@freebsd.org>
+.\"
+.\" Redistribution of this software and documentation and use in source and
+.\" binary forms, with or without modification, are permitted provided that
+.\" the following conditions are met:
+.\"
+.\" 1. Redistributions of source code or documentation must retain the above
+.\" copyright notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the Institute nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY FRAUNHOFER FOKUS
+.\" AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+.\" INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+.\" FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+.\" FRAUNHOFER FOKUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+.\" LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+.\" OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+.\" LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+.\" NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+.\" EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $Begemot: bsnmp/lib/bsnmplib.3,v 1.2 2002/08/16 10:02:53 hbb Exp $
+.\"
+.Dd August 15, 2002
+.Dt bsnmplib 3
+.Os
+.Sh NAME
+.Nm snmp_value_free ,
+.Nm snmp_value_parse ,
+.Nm snmp_value_copy ,
+.Nm snmp_pdu_free ,
+.Nm snmp_code snmp_pdu_decode ,
+.Nm snmp_code snmp_pdu_encode ,
+.Nm snmp_pdu_dump ,
+.Nm TRUTH_MK ,
+.Nm TRUTH_GET ,
+.Nm TRUTH_OK
+.Nd "SNMP decoding and encoding library"
+.Sh LIBRARY
+Begemot SNMP library
+.Pq libbsnmp, -lbsnmp
+.Sh SYNOPSIS
+.In bsnmp/asn1.h
+.In bsnmp/snmp.h
+.Ft void
+.Fn snmp_value_free "struct snmp_value *value"
+.Ft int
+.Fn snmp_value_parse "const char *buf" "enum snmp_syntax" "union snmp_values *value"
+.Ft int
+.Fn snmp_value_copy "struct snmp_value *to" "const struct snmp_value *from"
+.Ft void
+.Fn snmp_pdu_free "struct snmp_pdu *value"
+.Ft enum snmp_code
+.Fn snmp_pdu_decode "struct asn_buf *buf" "struct snmp_pdu *pdu" "int32_t *ip"
+.Ft enum snmp_code
+.Fn snmp_pdu_encode "struct snmp_pdu *pdu" "struct asn_buf *buf"
+.Ft void
+.Fn snmp_pdu_dump "const struct snmp_pdu *pdu"
+.Ft int
+.Fn TRUTH_MK "F"
+.Ft int
+.Fn TRUTH_GET "T"
+.Ft int
+.Fn TRUTH_OK "T"
+.Sh DESCRIPTION
+The SNMP library contains routines to handle SNMP version 1 and 2 PDUs.
+There are two basic structures used throughout the library:
+.Bd -literal -offset indent
+struct snmp_value {
+ struct asn_oid var;
+ enum snmp_syntax syntax;
+ union snmp_values {
+ int32_t integer;/* also integer32 */
+ struct {
+ u_int len;
+ u_char *octets;
+ } octetstring;
+ struct asn_oid oid;
+ u_char ipaddress[4];
+ u_int32_t uint32; /* also gauge32, counter32,
+ unsigned32, timeticks */
+ u_int64_t counter64;
+ } v;
+};
+.Ed
+.Pp
+This structure represents one variable binding from an SNMP PDU. The
+field
+.Fa var
+is the ASN.1 of the variable that is bound.
+.Fa syntax
+contains either the syntax code of the value or an exception code for SNMPv2
+and may be one of:
+.Bd -literal -offset indent
+enum snmp_syntax {
+ SNMP_SYNTAX_NULL = 0,
+ SNMP_SYNTAX_INTEGER, /* == INTEGER32 */
+ SNMP_SYNTAX_OCTETSTRING,
+ SNMP_SYNTAX_OID,
+ SNMP_SYNTAX_IPADDRESS,
+ SNMP_SYNTAX_COUNTER,
+ SNMP_SYNTAX_GAUGE, /* == UNSIGNED32 */
+ SNMP_SYNTAX_TIMETICKS,
+
+ /* v2 additions */
+ SNMP_SYNTAX_COUNTER64,
+ /* exceptions */
+ SNMP_SYNTAX_NOSUCHOBJECT,
+ SNMP_SYNTAX_NOSUCHINSTANCE,
+ SNMP_SYNTAX_ENDOFMIBVIEW,
+};
+.Ed
+The field
+.Fa v
+holds the actual value depending on
+.Fa syntax .
+Note, that if
+.Fa syntax
+is
+.Li SNMP_SYNTAX_OCTETSTRING
+and
+.Fa v.octetstring.len
+is not zero,
+.Fa v.octetstring.octets
+points to a string allocated by
+.Xr malloc 3 .
+.Pp
+.Bd -literal -offset indent
+#define SNMP_COMMUNITY_MAXLEN 128
+#define SNMP_MAX_BINDINGS 100
+
+struct snmp_pdu {
+ char community[SNMP_COMMUNITY_MAXLEN + 1];
+ enum snmp_version version;
+ u_int type;
+
+ /* trap only */
+ struct asn_oid enterprise;
+ u_char agent_addr[4];
+ int32_t generic_trap;
+ int32_t specific_trap;
+ u_int32_t time_stamp;
+
+ /* others */
+ int32_t request_id;
+ int32_t error_status;
+ int32_t error_index;
+
+ /* fixes for encoding */
+ u_char *outer_ptr;
+ u_char *pdu_ptr;
+ u_char *vars_ptr;
+
+ struct snmp_value bindings[SNMP_MAX_BINDINGS];
+ u_int nbindings;
+};
+.Ed
+This structure contains a decoded SNMP PDU.
+.Fa version
+is one of
+.Bd -literal -offset indent
+enum snmp_version {
+ SNMP_Verr = 0,
+ SNMP_V1 = 1,
+ SNMP_V2c,
+};
+.Ed
+and
+.Fa type
+is the type of the PDU.
+.Pp
+The function
+.Fn snmp_value_free
+is used to free all the dynamic allocated contents of an SNMP value. It does
+not free the structure pointed to by
+.Fa value
+itself.
+.Pp
+The function
+.Fn snmp_value_parse
+parses the ASCII representation of an SNMP value into its binary form.
+This function is mainly used by the configuration file reader of
+.Xr snmpd 1 .
+.Pp
+The function
+.Fn snmp_value_copy
+makes a deep copy of the value pointed to by
+.Fa from
+to the structure pointed to by
+.Fa to .
+It assumes that
+.Fa to
+is uninitialized and will overwrite its previous contents. It does not itself
+allocate the structure pointed to by
+.Fa to .
+.Pp
+The function
+.Fn snmp_pdu_free
+frees all the dynamically allocated components of the PDU. It does not itself
+free the structure pointed to by
+.Fa pdu .
+.Pp
+The function
+.Fn snmp_pdu_decode
+decodes the PDU pointed to by
+.Fa buf
+and stores the result into
+.Fa pdu .
+If an error occurs in a variable binding the (1 based) index of this binding
+is stored in the variable pointed to by
+.Fa ip .
+.Pp
+The function
+.Fn snmp_pdu_encode
+encodes the PDU
+.Fa pdu
+into the an octetstring in buffer
+.Fa buf .
+.Pp
+The function
+.Fn snmp_pdu_dump
+dumps the PDU in a human readable form by calling
+.Fn snmp_printf .
+.Pp
+The function
+.Fn TRUTH_MK
+takes a C truth value (zero or non-zero) and makes an SNMP truth value (2 or 1).
+The function
+.Fn TRUTH_GET
+takes an SNMP truth value and makes a C truth value (0 or 1).
+The function
+.Fn TRUTH_OK
+checks, whether its argument is a legal SNMP truth value.
+.Sh DIAGNOSTICS
+When an error occures in any of the function the function pointed to
+by the global pointer
+.Bd -literal -offset indent
+extern void (*snmp_error)(const char *, ...);
+.Ed
+.Pp
+with a
+.Xr printf 3
+style format string.
+There is a default error handler in the library that prints a message
+starting with
+.Sq SNMP:
+followed by the error message to standard error.
+.Pp
+The function pointed to by
+.Bd -literal -offset indent
+extern void (*snmp_printf)(const char *, ...);
+.Ed
+.Pp
+is called by the
+.Fn snmp_pdu_dump
+function.
+The default handler is
+.Xr printf 3 .
+.Sh ERRORS
+.Fn snmp_pdu_decode
+will return one of the following return codes:
+.Bl -tag -width Er
+.It Bq Er SNMP_CODE_OK
+Success.
+.It Bq Er SNMP_CODE_FAILED
+The ASN.1 coding was wrong.
+.It Bq Er SNMP_CODE_BADLEN
+A variable binding value had a wrong length field.
+.It Bq Er SNMP_CODE_OORANGE
+A variable binding value was out of the allowed range.
+.It Bq Er SNMP_CODE_BADVERS
+The PDU is of an unsupported version.
+.It Bq Er SNMP_CODE_BADENQ
+There was an ASN.1 value with an unsupported tag.
+.El
+.Pp
+.Fn snmp_pdu_encode
+will return one of the following return codes:
+.Bl -tag -width Er
+.It Bq Er SNMP_CODE_OK
+Success.
+.It Bq Er SNMP_CODE_FAILED
+Encoding failed.
+.El
+.Sh SEE ALSO
+.Xr snmpd 1 ,
+.Xr gensnmptree 1 ,
+.Xr bsnmplib 3
+.Xr bsnmpclient 3 ,
+.Xr bsnmpagent 3
+.Sh STANDARDS
+This implementation conforms to the applicable IETF RFCs and ITU-T
+recommendations.
+.Sh AUTHORS
+.An Hartmut Brandt Aq brandt@fokus.gmd.de
diff --git a/contrib/bsnmp/lib/snmp.c b/contrib/bsnmp/lib/snmp.c
new file mode 100644
index 0000000..786bf8c
--- /dev/null
+++ b/contrib/bsnmp/lib/snmp.c
@@ -0,0 +1,1027 @@
+/*
+ * Copyright (c) 2001-2003
+ * Fraunhofer Institute for Open Communication Systems (FhG Fokus).
+ * All rights reserved.
+ *
+ * Author: Harti Brandt <harti@freebsd.org>
+ *
+ * Redistribution of this software and documentation and use in source and
+ * binary forms, with or without modification, are permitted provided that
+ * the following conditions are met:
+ *
+ * 1. Redistributions of source code or documentation must retain the above
+ * copyright notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY FRAUNHOFER FOKUS
+ * AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * FRAUNHOFER FOKUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Begemot: bsnmp/lib/snmp.c,v 1.34 2003/01/28 13:44:34 hbb Exp $
+ *
+ * SNMP
+ */
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <stdarg.h>
+#include <string.h>
+#include <ctype.h>
+#include <netdb.h>
+#include <errno.h>
+
+#include "asn1.h"
+#include "snmp.h"
+#include "snmppriv.h"
+
+static void snmp_error_func(const char *, ...);
+static void snmp_printf_func(const char *, ...);
+
+void (*snmp_error)(const char *, ...) = snmp_error_func;
+void (*snmp_printf)(const char *, ...) = snmp_printf_func;
+
+
+/*
+ * Get the next variable binding from the list.
+ * ASN errors on the sequence or the OID are always fatal.
+ */
+static enum asn_err
+get_var_binding(struct asn_buf *b, struct snmp_value *binding)
+{
+ u_char type;
+ asn_len_t len, trailer;
+ enum asn_err err;
+
+ if (asn_get_sequence(b, &len) != ASN_ERR_OK) {
+ snmp_error("cannot parse varbind header");
+ return (ASN_ERR_FAILED);
+ }
+
+ /* temporary truncate the length so that the parser does not
+ * eat up bytes behind the sequence in the case the encoding is
+ * wrong of inner elements. */
+ trailer = b->asn_len - len;
+ b->asn_len = len;
+
+ if (asn_get_objid(b, &binding->var) != ASN_ERR_OK) {
+ snmp_error("cannot parse binding objid");
+ return (ASN_ERR_FAILED);
+ }
+ if (asn_get_header(b, &type, &len) != ASN_ERR_OK) {
+ snmp_error("cannot parse binding value header");
+ return (ASN_ERR_FAILED);
+ }
+
+ switch (type) {
+
+ case ASN_TYPE_NULL:
+ binding->syntax = SNMP_SYNTAX_NULL;
+ err = asn_get_null_raw(b, len);
+ break;
+
+ case ASN_TYPE_INTEGER:
+ binding->syntax = SNMP_SYNTAX_INTEGER;
+ err = asn_get_integer_raw(b, len, &binding->v.integer);
+ break;
+
+ case ASN_TYPE_OCTETSTRING:
+ binding->syntax = SNMP_SYNTAX_OCTETSTRING;
+ binding->v.octetstring.octets = malloc(len);
+ if (binding->v.octetstring.octets == NULL) {
+ snmp_error("%s", strerror(errno));
+ return (ASN_ERR_FAILED);
+ }
+ binding->v.octetstring.len = len;
+ err = asn_get_octetstring_raw(b, len,
+ binding->v.octetstring.octets,
+ &binding->v.octetstring.len);
+ if (ASN_ERR_STOPPED(err)) {
+ free(binding->v.octetstring.octets);
+ binding->v.octetstring.octets = NULL;
+ }
+ break;
+
+ case ASN_TYPE_OBJID:
+ binding->syntax = SNMP_SYNTAX_OID;
+ err = asn_get_objid_raw(b, len, &binding->v.oid);
+ break;
+
+ case ASN_CLASS_APPLICATION|ASN_APP_IPADDRESS:
+ binding->syntax = SNMP_SYNTAX_IPADDRESS;
+ err = asn_get_ipaddress_raw(b, len, binding->v.ipaddress);
+ break;
+
+ case ASN_CLASS_APPLICATION|ASN_APP_TIMETICKS:
+ binding->syntax = SNMP_SYNTAX_TIMETICKS;
+ err = asn_get_uint32_raw(b, len, &binding->v.uint32);
+ break;
+
+ case ASN_CLASS_APPLICATION|ASN_APP_COUNTER:
+ binding->syntax = SNMP_SYNTAX_COUNTER;
+ err = asn_get_uint32_raw(b, len, &binding->v.uint32);
+ break;
+
+ case ASN_CLASS_APPLICATION|ASN_APP_GAUGE:
+ binding->syntax = SNMP_SYNTAX_GAUGE;
+ err = asn_get_uint32_raw(b, len, &binding->v.uint32);
+ break;
+
+ case ASN_CLASS_APPLICATION|ASN_APP_COUNTER64:
+ binding->syntax = SNMP_SYNTAX_COUNTER64;
+ err = asn_get_counter64_raw(b, len, &binding->v.counter64);
+ break;
+
+ case ASN_CLASS_CONTEXT | ASN_EXCEPT_NOSUCHOBJECT:
+ binding->syntax = SNMP_SYNTAX_NOSUCHOBJECT;
+ err = asn_get_null_raw(b, len);
+ break;
+
+ case ASN_CLASS_CONTEXT | ASN_EXCEPT_NOSUCHINSTANCE:
+ binding->syntax = SNMP_SYNTAX_NOSUCHINSTANCE;
+ err = asn_get_null_raw(b, len);
+ break;
+
+ case ASN_CLASS_CONTEXT | ASN_EXCEPT_ENDOFMIBVIEW:
+ binding->syntax = SNMP_SYNTAX_ENDOFMIBVIEW;
+ err = asn_get_null_raw(b, len);
+ break;
+
+ default:
+ if ((err = asn_skip(b, len)) == ASN_ERR_OK)
+ err = ASN_ERR_TAG;
+ snmp_error("bad binding value type 0x%x", type);
+ break;
+ }
+
+ if (ASN_ERR_STOPPED(err)) {
+ snmp_error("cannot parse binding value");
+ return (err);
+ }
+
+ if (b->asn_len != 0)
+ snmp_error("ignoring junk at end of binding");
+
+ b->asn_len = trailer;
+
+ return (err);
+}
+
+/*
+ * Parse the different PDUs contents. Any ASN error in the outer components
+ * are fatal. Only errors in variable values may be tolerated. If all
+ * components can be parsed it returns either ASN_ERR_OK or the first
+ * error that was found.
+ */
+enum asn_err
+snmp_parse_pdus_hdr(struct asn_buf *b, struct snmp_pdu *pdu, asn_len_t *lenp)
+{
+ if (pdu->type == SNMP_PDU_TRAP) {
+ if (asn_get_objid(b, &pdu->enterprise) != ASN_ERR_OK) {
+ snmp_error("cannot parse trap enterprise");
+ return (ASN_ERR_FAILED);
+ }
+ if (asn_get_ipaddress(b, pdu->agent_addr) != ASN_ERR_OK) {
+ snmp_error("cannot parse trap agent address");
+ return (ASN_ERR_FAILED);
+ }
+ if (asn_get_integer(b, &pdu->generic_trap) != ASN_ERR_OK) {
+ snmp_error("cannot parse 'generic-trap'");
+ return (ASN_ERR_FAILED);
+ }
+ if (asn_get_integer(b, &pdu->specific_trap) != ASN_ERR_OK) {
+ snmp_error("cannot parse 'specific-trap'");
+ return (ASN_ERR_FAILED);
+ }
+ if (asn_get_timeticks(b, &pdu->time_stamp) != ASN_ERR_OK) {
+ snmp_error("cannot parse trap 'time-stamp'");
+ return (ASN_ERR_FAILED);
+ }
+ } else {
+ if (asn_get_integer(b, &pdu->request_id) != ASN_ERR_OK) {
+ snmp_error("cannot parse 'request-id'");
+ return (ASN_ERR_FAILED);
+ }
+ if (asn_get_integer(b, &pdu->error_status) != ASN_ERR_OK) {
+ snmp_error("cannot parse 'error_status'");
+ return (ASN_ERR_FAILED);
+ }
+ if (asn_get_integer(b, &pdu->error_index) != ASN_ERR_OK) {
+ snmp_error("cannot parse 'error_index'");
+ return (ASN_ERR_FAILED);
+ }
+ }
+
+ if (asn_get_sequence(b, lenp) != ASN_ERR_OK) {
+ snmp_error("cannot get varlist header");
+ return (ASN_ERR_FAILED);
+ }
+
+ return (ASN_ERR_OK);
+}
+
+static enum asn_err
+parse_pdus(struct asn_buf *b, struct snmp_pdu *pdu, int32_t *ip)
+{
+ asn_len_t len, trailer;
+ struct snmp_value *v;
+ enum asn_err err, err1;
+
+ err = snmp_parse_pdus_hdr(b, pdu, &len);
+ if (ASN_ERR_STOPPED(err))
+ return (err);
+
+ trailer = b->asn_len - len;
+
+ v = pdu->bindings;
+ err = ASN_ERR_OK;
+ while (b->asn_len != 0) {
+ if (pdu->nbindings == SNMP_MAX_BINDINGS) {
+ snmp_error("too many bindings (> %u) in PDU",
+ SNMP_MAX_BINDINGS);
+ return (ASN_ERR_FAILED);
+ }
+ err1 = get_var_binding(b, v);
+ if (ASN_ERR_STOPPED(err1))
+ return (ASN_ERR_FAILED);
+ if (err1 != ASN_ERR_OK && err == ASN_ERR_OK) {
+ err = err1;
+ *ip = pdu->nbindings + 1;
+ }
+ pdu->nbindings++;
+ v++;
+ }
+
+ b->asn_len = trailer;
+
+ return (err);
+}
+
+/*
+ * Parse the outer SEQUENCE value. ASN_ERR_TAG means 'bad version'.
+ */
+enum asn_err
+snmp_parse_message_hdr(struct asn_buf *b, struct snmp_pdu *pdu, asn_len_t *lenp)
+{
+ int32_t version;
+ u_char type;
+ u_int comm_len;
+
+ if (asn_get_integer(b, &version) != ASN_ERR_OK) {
+ snmp_error("cannot decode version");
+ return (ASN_ERR_FAILED);
+ }
+
+ if (version == 0) {
+ pdu->version = SNMP_V1;
+ } else if (version == 1) {
+ pdu->version = SNMP_V2c;
+ } else {
+ pdu->version = SNMP_Verr;
+ snmp_error("unsupported SNMP version");
+ return (ASN_ERR_TAG);
+ }
+
+ comm_len = SNMP_COMMUNITY_MAXLEN;
+ if (asn_get_octetstring(b, (u_char *)pdu->community,
+ &comm_len) != ASN_ERR_OK) {
+ snmp_error("cannot decode community");
+ return (ASN_ERR_FAILED);
+ }
+ pdu->community[comm_len] = '\0';
+
+ if (asn_get_header(b, &type, lenp) != ASN_ERR_OK) {
+ snmp_error("cannot get pdu header");
+ return (ASN_ERR_FAILED);
+ }
+ if ((type & ~ASN_TYPE_MASK) !=
+ (ASN_TYPE_CONSTRUCTED | ASN_CLASS_CONTEXT)) {
+ snmp_error("bad pdu header tag");
+ return (ASN_ERR_FAILED);
+ }
+ pdu->type = type & ASN_TYPE_MASK;
+
+ switch (pdu->type) {
+
+ case SNMP_PDU_GET:
+ case SNMP_PDU_GETNEXT:
+ case SNMP_PDU_RESPONSE:
+ case SNMP_PDU_SET:
+ break;
+
+ case SNMP_PDU_TRAP:
+ if (pdu->version != SNMP_V1) {
+ snmp_error("bad pdu type %u", pdu->type);
+ return (ASN_ERR_FAILED);
+ }
+ break;
+
+ case SNMP_PDU_GETBULK:
+ case SNMP_PDU_INFORM:
+ case SNMP_PDU_TRAP2:
+ case SNMP_PDU_REPORT:
+ if (pdu->version == SNMP_V1) {
+ snmp_error("bad pdu type %u", pdu->type);
+ return (ASN_ERR_FAILED);
+ }
+ break;
+
+ default:
+ snmp_error("bad pdu type %u", pdu->type);
+ return (ASN_ERR_FAILED);
+ }
+
+
+ if (*lenp > b->asn_len) {
+ snmp_error("pdu length too long");
+ return (ASN_ERR_FAILED);
+ }
+
+ return (ASN_ERR_OK);
+}
+
+static enum asn_err
+parse_message(struct asn_buf *b, struct snmp_pdu *pdu, int32_t *ip)
+{
+ enum asn_err err;
+ asn_len_t len, trailer;
+
+ err = snmp_parse_message_hdr(b, pdu, &len);
+ if (ASN_ERR_STOPPED(err))
+ return (err);
+
+ trailer = b->asn_len - len;
+ b->asn_len = len;
+
+ err = parse_pdus(b, pdu, ip);
+ if (ASN_ERR_STOPPED(err))
+ return (ASN_ERR_FAILED);
+
+ if (b->asn_len != 0)
+ snmp_error("ignoring trailing junk after pdu");
+
+ b->asn_len = trailer;
+
+ return (err);
+}
+
+/*
+ * Decode the PDU except for the variable bindings itself.
+ * If decoding fails because of a bad binding, but the rest can be
+ * decoded, ip points to the index of the failed variable (errors
+ * OORANGE, BADLEN or BADVERS).
+ */
+enum snmp_code
+snmp_pdu_decode(struct asn_buf *b, struct snmp_pdu *pdu, int32_t *ip)
+{
+ asn_len_t len;
+
+ memset(pdu, 0, sizeof(*pdu));
+
+ if (asn_get_sequence(b, &len) != ASN_ERR_OK) {
+ snmp_error("cannot decode pdu header");
+ return (SNMP_CODE_FAILED);
+ }
+ if (b->asn_len < len) {
+ snmp_error("outer sequence value too short");
+ return (SNMP_CODE_FAILED);
+ }
+ if (b->asn_len != len) {
+ snmp_error("ignoring trailing junk in message");
+ b->asn_len = len;
+ }
+
+ switch (parse_message(b, pdu, ip)) {
+
+ case ASN_ERR_OK:
+ return (SNMP_CODE_OK);
+
+ case ASN_ERR_FAILED:
+ case ASN_ERR_EOBUF:
+ snmp_pdu_free(pdu);
+ return (SNMP_CODE_FAILED);
+
+ case ASN_ERR_BADLEN:
+ return (SNMP_CODE_BADLEN);
+
+ case ASN_ERR_RANGE:
+ return (SNMP_CODE_OORANGE);
+
+ case ASN_ERR_TAG:
+ if (pdu->version == SNMP_Verr)
+ return (SNMP_CODE_BADVERS);
+ else
+ return (SNMP_CODE_BADENC);
+ }
+
+ return (SNMP_CODE_OK);
+}
+
+/*
+ * Encode the SNMP PDU without the variable bindings field.
+ * We do this the rather uneffective way by
+ * moving things around and assuming that the length field will never
+ * use more than 2 bytes.
+ * We need a number of pointers to apply the fixes afterwards.
+ */
+enum snmp_code
+snmp_pdu_encode_header(struct asn_buf *b, struct snmp_pdu *pdu)
+{
+ enum asn_err err;
+
+ if (asn_put_temp_header(b, (ASN_TYPE_SEQUENCE|ASN_TYPE_CONSTRUCTED),
+ &pdu->outer_ptr) != ASN_ERR_OK)
+ return (SNMP_CODE_FAILED);
+
+ if (pdu->version == SNMP_V1)
+ err = asn_put_integer(b, 0);
+ else if (pdu->version == SNMP_V2c)
+ err = asn_put_integer(b, 1);
+ else
+ return (SNMP_CODE_BADVERS);
+ if (err != ASN_ERR_OK)
+ return (SNMP_CODE_FAILED);
+
+ if (asn_put_octetstring(b, (u_char *)pdu->community,
+ strlen(pdu->community)) != ASN_ERR_OK)
+ return (SNMP_CODE_FAILED);
+
+ if (asn_put_temp_header(b, (ASN_TYPE_CONSTRUCTED | ASN_CLASS_CONTEXT |
+ pdu->type), &pdu->pdu_ptr) != ASN_ERR_OK)
+ return (SNMP_CODE_FAILED);
+
+ if (pdu->type == SNMP_PDU_TRAP) {
+ if (pdu->version != SNMP_V1 ||
+ asn_put_objid(b, &pdu->enterprise) != ASN_ERR_OK ||
+ asn_put_ipaddress(b, pdu->agent_addr) != ASN_ERR_OK ||
+ asn_put_integer(b, pdu->generic_trap) != ASN_ERR_OK ||
+ asn_put_integer(b, pdu->specific_trap) != ASN_ERR_OK ||
+ asn_put_timeticks(b, pdu->time_stamp) != ASN_ERR_OK)
+ return (SNMP_CODE_FAILED);
+ } else {
+ if (pdu->version == SNMP_V1 && (pdu->type == SNMP_PDU_GETBULK ||
+ pdu->type == SNMP_PDU_INFORM ||
+ pdu->type == SNMP_PDU_TRAP2 ||
+ pdu->type == SNMP_PDU_REPORT))
+ return (SNMP_CODE_FAILED);
+
+ if (asn_put_integer(b, pdu->request_id) != ASN_ERR_OK ||
+ asn_put_integer(b, pdu->error_status) != ASN_ERR_OK ||
+ asn_put_integer(b, pdu->error_index) != ASN_ERR_OK)
+ return (SNMP_CODE_FAILED);
+ }
+
+ if (asn_put_temp_header(b, (ASN_TYPE_SEQUENCE|ASN_TYPE_CONSTRUCTED),
+ &pdu->vars_ptr) != ASN_ERR_OK)
+ return (SNMP_CODE_FAILED);
+
+ return (SNMP_CODE_OK);
+}
+
+enum snmp_code
+snmp_fix_encoding(struct asn_buf *b, const struct snmp_pdu *pdu)
+{
+ if (asn_commit_header(b, pdu->vars_ptr) != ASN_ERR_OK ||
+ asn_commit_header(b, pdu->pdu_ptr) != ASN_ERR_OK ||
+ asn_commit_header(b, pdu->outer_ptr) != ASN_ERR_OK)
+ return (SNMP_CODE_FAILED);
+ return (SNMP_CODE_OK);
+}
+
+/*
+ * Encode a binding. Caller must ensure, that the syntax is ok for that version.
+ * Be sure not to cobber b, when something fails.
+ */
+enum asn_err
+snmp_binding_encode(struct asn_buf *b, const struct snmp_value *binding)
+{
+ u_char *ptr;
+ enum asn_err err;
+ struct asn_buf save = *b;
+
+ if ((err = asn_put_temp_header(b, (ASN_TYPE_SEQUENCE |
+ ASN_TYPE_CONSTRUCTED), &ptr)) != ASN_ERR_OK) {
+ *b = save;
+ return (err);
+ }
+
+ if ((err = asn_put_objid(b, &binding->var)) != ASN_ERR_OK) {
+ *b = save;
+ return (err);
+ }
+
+ switch (binding->syntax) {
+
+ case SNMP_SYNTAX_NULL:
+ err = asn_put_null(b);
+ break;
+
+ case SNMP_SYNTAX_INTEGER:
+ err = asn_put_integer(b, binding->v.integer);
+ break;
+
+ case SNMP_SYNTAX_OCTETSTRING:
+ err = asn_put_octetstring(b, binding->v.octetstring.octets,
+ binding->v.octetstring.len);
+ break;
+
+ case SNMP_SYNTAX_OID:
+ err = asn_put_objid(b, &binding->v.oid);
+ break;
+
+ case SNMP_SYNTAX_IPADDRESS:
+ err = asn_put_ipaddress(b, binding->v.ipaddress);
+ break;
+
+ case SNMP_SYNTAX_TIMETICKS:
+ err = asn_put_uint32(b, ASN_APP_TIMETICKS, binding->v.uint32);
+ break;
+
+ case SNMP_SYNTAX_COUNTER:
+ err = asn_put_uint32(b, ASN_APP_COUNTER, binding->v.uint32);
+ break;
+
+ case SNMP_SYNTAX_GAUGE:
+ err = asn_put_uint32(b, ASN_APP_GAUGE, binding->v.uint32);
+ break;
+
+ case SNMP_SYNTAX_COUNTER64:
+ err = asn_put_counter64(b, binding->v.counter64);
+ break;
+
+ case SNMP_SYNTAX_NOSUCHOBJECT:
+ err = asn_put_exception(b, ASN_EXCEPT_NOSUCHOBJECT);
+ break;
+
+ case SNMP_SYNTAX_NOSUCHINSTANCE:
+ err = asn_put_exception(b, ASN_EXCEPT_NOSUCHINSTANCE);
+ break;
+
+ case SNMP_SYNTAX_ENDOFMIBVIEW:
+ err = asn_put_exception(b, ASN_EXCEPT_ENDOFMIBVIEW);
+ break;
+ }
+
+ if (err != ASN_ERR_OK) {
+ *b = save;
+ return (err);
+ }
+
+ err = asn_commit_header(b, ptr);
+ if (err != ASN_ERR_OK) {
+ *b = save;
+ return (err);
+ }
+
+ return (ASN_ERR_OK);
+}
+
+/*
+ * Encode an PDU.
+ */
+enum snmp_code
+snmp_pdu_encode(struct snmp_pdu *pdu, struct asn_buf *resp_b)
+{
+ u_int idx;
+ enum snmp_code err;
+
+ if ((err = snmp_pdu_encode_header(resp_b, pdu)) != SNMP_CODE_OK)
+ return (err);
+ for (idx = 0; idx < pdu->nbindings; idx++)
+ if ((err = snmp_binding_encode(resp_b, &pdu->bindings[idx]))
+ != ASN_ERR_OK)
+ return (SNMP_CODE_FAILED);
+
+ return (snmp_fix_encoding(resp_b, pdu));
+}
+
+static void
+dump_binding(const struct snmp_value *b)
+{
+ u_int i;
+ char buf[ASN_OIDSTRLEN];
+
+ snmp_printf("%s=", asn_oid2str_r(&b->var, buf));
+ switch (b->syntax) {
+
+ case SNMP_SYNTAX_NULL:
+ snmp_printf("NULL");
+ break;
+
+ case SNMP_SYNTAX_INTEGER:
+ snmp_printf("INTEGER %d", b->v.integer);
+ break;
+
+ case SNMP_SYNTAX_OCTETSTRING:
+ snmp_printf("OCTET STRING %lu:", b->v.octetstring.len);
+ for (i = 0; i < b->v.octetstring.len; i++)
+ snmp_printf(" %02x", b->v.octetstring.octets[i]);
+ break;
+
+ case SNMP_SYNTAX_OID:
+ snmp_printf("OID %s", asn_oid2str_r(&b->v.oid, buf));
+ break;
+
+ case SNMP_SYNTAX_IPADDRESS:
+ snmp_printf("IPADDRESS %u.%u.%u.%u", b->v.ipaddress[0],
+ b->v.ipaddress[1], b->v.ipaddress[2], b->v.ipaddress[3]);
+ break;
+
+ case SNMP_SYNTAX_COUNTER:
+ snmp_printf("COUNTER %u", b->v.uint32);
+ break;
+
+ case SNMP_SYNTAX_GAUGE:
+ snmp_printf("GAUGE %u", b->v.uint32);
+ break;
+
+ case SNMP_SYNTAX_TIMETICKS:
+ snmp_printf("TIMETICKS %u", b->v.uint32);
+ break;
+
+ case SNMP_SYNTAX_COUNTER64:
+ snmp_printf("COUNTER64 %lld", b->v.counter64);
+ break;
+
+ case SNMP_SYNTAX_NOSUCHOBJECT:
+ snmp_printf("NoSuchObject");
+ break;
+
+ case SNMP_SYNTAX_NOSUCHINSTANCE:
+ snmp_printf("NoSuchInstance");
+ break;
+
+ case SNMP_SYNTAX_ENDOFMIBVIEW:
+ snmp_printf("EndOfMibView");
+ break;
+
+ default:
+ snmp_printf("UNKNOWN SYNTAX %u", b->syntax);
+ break;
+ }
+}
+
+static __inline void
+dump_bindings(const struct snmp_pdu *pdu)
+{
+ u_int i;
+
+ for (i = 0; i < pdu->nbindings; i++) {
+ snmp_printf(" [%u]: ", i);
+ dump_binding(&pdu->bindings[i]);
+ snmp_printf("\n");
+ }
+}
+
+static __inline void
+dump_notrap(const struct snmp_pdu *pdu)
+{
+ snmp_printf(" request_id=%d", pdu->request_id);
+ snmp_printf(" error_status=%d", pdu->error_status);
+ snmp_printf(" error_index=%d\n", pdu->error_index);
+ dump_bindings(pdu);
+}
+
+void
+snmp_pdu_dump(const struct snmp_pdu *pdu)
+{
+ char buf[ASN_OIDSTRLEN];
+ const char *vers;
+ static const char *types[] = {
+ [SNMP_PDU_GET] = "GET",
+ [SNMP_PDU_GETNEXT] = "GETNEXT",
+ [SNMP_PDU_RESPONSE] = "RESPONSE",
+ [SNMP_PDU_SET] = "SET",
+ [SNMP_PDU_TRAP] = "TRAPv1",
+ [SNMP_PDU_GETBULK] = "GETBULK",
+ [SNMP_PDU_INFORM] = "INFORM",
+ [SNMP_PDU_TRAP2] = "TRAPv2",
+ [SNMP_PDU_REPORT] = "REPORT",
+ };
+
+ if (pdu->version == SNMP_V1)
+ vers = "SNMPv1";
+ else if (pdu->version == SNMP_V2c)
+ vers = "SNMPv2c";
+ else
+ vers = "v?";
+
+ switch (pdu->type) {
+ case SNMP_PDU_TRAP:
+ snmp_printf("%s %s '%s'", types[pdu->type], vers, pdu->community);
+ snmp_printf(" enterprise=%s", asn_oid2str_r(&pdu->enterprise, buf));
+ snmp_printf(" agent_addr=%u.%u.%u.%u", pdu->agent_addr[0],
+ pdu->agent_addr[1], pdu->agent_addr[2], pdu->agent_addr[3]);
+ snmp_printf(" generic_trap=%d", pdu->generic_trap);
+ snmp_printf(" specific_trap=%d", pdu->specific_trap);
+ snmp_printf(" time-stamp=%u\n", pdu->time_stamp);
+ dump_bindings(pdu);
+ break;
+
+ case SNMP_PDU_GET:
+ case SNMP_PDU_GETNEXT:
+ case SNMP_PDU_RESPONSE:
+ case SNMP_PDU_SET:
+ case SNMP_PDU_GETBULK:
+ case SNMP_PDU_INFORM:
+ case SNMP_PDU_TRAP2:
+ case SNMP_PDU_REPORT:
+ snmp_printf("%s %s '%s'", types[pdu->type], vers, pdu->community);
+ dump_notrap(pdu);
+ break;
+
+ default:
+ snmp_printf("bad pdu type %u\n", pdu->type);
+ break;
+ }
+}
+
+void
+snmp_value_free(struct snmp_value *value)
+{
+ if (value->syntax == SNMP_SYNTAX_OCTETSTRING)
+ free(value->v.octetstring.octets);
+ value->syntax = SNMP_SYNTAX_NULL;
+}
+
+int
+snmp_value_copy(struct snmp_value *to, const struct snmp_value *from)
+{
+ to->var = from->var;
+ to->syntax = from->syntax;
+
+ if (from->syntax == SNMP_SYNTAX_OCTETSTRING) {
+ if ((to->v.octetstring.len = from->v.octetstring.len) == 0)
+ to->v.octetstring.octets = NULL;
+ else {
+ to->v.octetstring.octets = malloc(to->v.octetstring.len);
+ if (to->v.octetstring.octets == NULL)
+ return (-1);
+ (void)memcpy(to->v.octetstring.octets,
+ from->v.octetstring.octets, to->v.octetstring.len);
+ }
+ } else
+ to->v = from->v;
+ return (0);
+}
+
+void
+snmp_pdu_free(struct snmp_pdu *pdu)
+{
+ u_int i;
+
+ for (i = 0; i < pdu->nbindings; i++)
+ snmp_value_free(&pdu->bindings[i]);
+}
+
+/*
+ * Parse an ASCII SNMP value into the binary form
+ */
+int
+snmp_value_parse(const char *str, enum snmp_syntax syntax, union snmp_values *v)
+{
+ char *end;
+
+ switch (syntax) {
+
+ case SNMP_SYNTAX_NULL:
+ case SNMP_SYNTAX_NOSUCHOBJECT:
+ case SNMP_SYNTAX_NOSUCHINSTANCE:
+ case SNMP_SYNTAX_ENDOFMIBVIEW:
+ if (*str != '\0')
+ return (-1);
+ return (0);
+
+ case SNMP_SYNTAX_INTEGER:
+ v->integer = strtoll(str, &end, 0);
+ if (*end != '\0')
+ return (-1);
+ return (0);
+
+ case SNMP_SYNTAX_OCTETSTRING:
+ {
+ u_long len; /* actual length of string */
+ u_long alloc; /* allocate length of string */
+ u_char *octs; /* actual octets */
+ u_long oct; /* actual octet */
+ u_char *nocts; /* to avoid memory leak */
+ u_char c; /* actual character */
+
+# define STUFFC(C) \
+ if (alloc == len) { \
+ alloc += 100; \
+ if ((nocts = realloc(octs, alloc)) == NULL) { \
+ free(octs); \
+ return (-1); \
+ } \
+ octs = nocts; \
+ } \
+ octs[len++] = (C);
+
+ len = alloc = 0;
+ octs = NULL;
+
+ if (*str == '"') {
+ str++;
+ while((c = *str++) != '\0') {
+ if (c == '"') {
+ if (*str != '\0') {
+ free(octs);
+ return (-1);
+ }
+ break;
+ }
+ if (c == '\\') {
+ switch (c = *str++) {
+
+ case '\\':
+ break;
+ case 'a':
+ c = '\a';
+ break;
+ case 'b':
+ c = '\b';
+ break;
+ case 'f':
+ c = '\f';
+ break;
+ case 'n':
+ c = '\n';
+ break;
+ case 'r':
+ c = '\r';
+ break;
+ case 't':
+ c = '\t';
+ break;
+ case 'v':
+ c = '\v';
+ break;
+ case 'x':
+ c = 0;
+ if (!isxdigit(*str))
+ break;
+ if (isdigit(*str))
+ c = *str++ - '0';
+ else if (isupper(*str))
+ c = *str++ - 'A' + 10;
+ else
+ c = *str++ - 'a' + 10;
+ if (!isxdigit(*str))
+ break;
+ if (isdigit(*str))
+ c += *str++ - '0';
+ else if (isupper(*str))
+ c += *str++ - 'A' + 10;
+ else
+ c += *str++ - 'a' + 10;
+ break;
+ case '0': case '1': case '2':
+ case '3': case '4': case '5':
+ case '6': case '7':
+ c = *str++ - '0';
+ if (*str < '0' || *str > '7')
+ break;
+ c = *str++ - '0';
+ if (*str < '0' || *str > '7')
+ break;
+ c = *str++ - '0';
+ break;
+ default:
+ break;
+ }
+ }
+ STUFFC(c);
+ }
+ } else {
+ while (*str != '\0') {
+ oct = strtoul(str, &end, 16);
+ str = end;
+ if (oct > 0xff) {
+ free(octs);
+ return (-1);
+ }
+ STUFFC(oct);
+ if (*str == ':')
+ str++;
+ else if(*str != '\0') {
+ free(octs);
+ return (-1);
+ }
+ }
+ }
+ v->octetstring.octets = octs;
+ v->octetstring.len = len;
+ return (0);
+# undef STUFFC
+ }
+
+ case SNMP_SYNTAX_OID:
+ {
+ u_long subid;
+
+ v->oid.len = 0;
+
+ for (;;) {
+ if (v->oid.len == ASN_MAXOIDLEN)
+ return (-1);
+ subid = strtoul(str, &end, 10);
+ str = end;
+ if (subid > ASN_MAXID)
+ return (-1);
+ v->oid.subs[v->oid.len++] = (asn_subid_t)subid;
+ if (*str == '\0')
+ break;
+ if (*str != '.')
+ return (-1);
+ str++;
+ }
+ return (0);
+ }
+
+ case SNMP_SYNTAX_IPADDRESS:
+ {
+ struct hostent *he;
+ u_long ip[4];
+ int n;
+
+ if (sscanf(str, "%lu.%lu.%lu.%lu%n", &ip[0], &ip[1], &ip[2],
+ &ip[3], &n) == 4 && (size_t)n == strlen(str) &&
+ ip[0] <= 0xff && ip[1] <= 0xff &&
+ ip[2] <= 0xff && ip[3] <= 0xff) {
+ v->ipaddress[0] = (u_char)ip[0];
+ v->ipaddress[1] = (u_char)ip[1];
+ v->ipaddress[2] = (u_char)ip[2];
+ v->ipaddress[3] = (u_char)ip[3];
+ return (0);
+ }
+
+ if ((he = gethostbyname(str)) == NULL)
+ return (-1);
+ if (he->h_addrtype != AF_INET)
+ return (-1);
+
+ v->ipaddress[0] = he->h_addr[0];
+ v->ipaddress[1] = he->h_addr[1];
+ v->ipaddress[2] = he->h_addr[2];
+ v->ipaddress[3] = he->h_addr[3];
+ return (0);
+ }
+
+ case SNMP_SYNTAX_COUNTER:
+ case SNMP_SYNTAX_GAUGE:
+ case SNMP_SYNTAX_TIMETICKS:
+ {
+ u_int64_t sub;
+
+ sub = strtoull(str, &end, 0);
+ if (*end != '\0' || sub > 0xffffffff)
+ return (-1);
+ v->uint32 = (u_int32_t)sub;
+ return (0);
+ }
+
+ case SNMP_SYNTAX_COUNTER64:
+ v->counter64 = strtoull(str, &end, 0);
+ if (*end != '\0')
+ return (-1);
+ return (0);
+ }
+ abort();
+}
+
+static void
+snmp_error_func(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ fprintf(stderr, "SNMP: ");
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+ va_end(ap);
+}
+
+static void
+snmp_printf_func(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+}
diff --git a/contrib/bsnmp/lib/snmp.h b/contrib/bsnmp/lib/snmp.h
new file mode 100644
index 0000000..30b1e2c
--- /dev/null
+++ b/contrib/bsnmp/lib/snmp.h
@@ -0,0 +1,176 @@
+/*
+ * Copyright (c) 2001-2003
+ * Fraunhofer Institute for Open Communication Systems (FhG Fokus).
+ * All rights reserved.
+ *
+ * Author: Harti Brandt <harti@freebsd.org>
+ *
+ * Redistribution of this software and documentation and use in source and
+ * binary forms, with or without modification, are permitted provided that
+ * the following conditions are met:
+ *
+ * 1. Redistributions of source code or documentation must retain the above
+ * copyright notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY FRAUNHOFER FOKUS
+ * AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * FRAUNHOFER FOKUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Begemot: bsnmp/lib/snmp.h,v 1.27 2002/03/08 14:24:58 hbb Exp $
+ *
+ * Header file for SNMP functions.
+ */
+#ifndef snmp_h_
+#define snmp_h_
+
+#include <sys/types.h>
+
+#define SNMP_COMMUNITY_MAXLEN 128
+#define SNMP_MAX_BINDINGS 100
+
+enum snmp_syntax {
+ SNMP_SYNTAX_NULL = 0,
+ SNMP_SYNTAX_INTEGER, /* == INTEGER32 */
+ SNMP_SYNTAX_OCTETSTRING,
+ SNMP_SYNTAX_OID,
+ SNMP_SYNTAX_IPADDRESS,
+ SNMP_SYNTAX_COUNTER,
+ SNMP_SYNTAX_GAUGE, /* == UNSIGNED32 */
+ SNMP_SYNTAX_TIMETICKS,
+
+ /* v2 additions */
+ SNMP_SYNTAX_COUNTER64,
+ SNMP_SYNTAX_NOSUCHOBJECT, /* exception */
+ SNMP_SYNTAX_NOSUCHINSTANCE, /* exception */
+ SNMP_SYNTAX_ENDOFMIBVIEW, /* exception */
+};
+
+struct snmp_value {
+ struct asn_oid var;
+ enum snmp_syntax syntax;
+ union snmp_values {
+ int32_t integer; /* also integer32 */
+ struct {
+ u_int len;
+ u_char *octets;
+ } octetstring;
+ struct asn_oid oid;
+ u_char ipaddress[4];
+ u_int32_t uint32; /* also gauge32, counter32,
+ unsigned32, timeticks */
+ u_int64_t counter64;
+ } v;
+};
+
+enum snmp_version {
+ SNMP_Verr = 0,
+ SNMP_V1 = 1,
+ SNMP_V2c,
+};
+
+struct snmp_pdu {
+ char community[SNMP_COMMUNITY_MAXLEN + 1];
+ enum snmp_version version;
+ u_int type;
+
+ /* trap only */
+ struct asn_oid enterprise;
+ u_char agent_addr[4];
+ int32_t generic_trap;
+ int32_t specific_trap;
+ u_int32_t time_stamp;
+
+ /* others */
+ int32_t request_id;
+ int32_t error_status;
+ int32_t error_index;
+
+ /* fixes for encoding */
+ u_char *outer_ptr;
+ u_char *pdu_ptr;
+ u_char *vars_ptr;
+
+ struct snmp_value bindings[SNMP_MAX_BINDINGS];
+ u_int nbindings;
+};
+#define snmp_v1_pdu snmp_pdu
+
+#define SNMP_PDU_GET 0
+#define SNMP_PDU_GETNEXT 1
+#define SNMP_PDU_RESPONSE 2
+#define SNMP_PDU_SET 3
+#define SNMP_PDU_TRAP 4 /* v1 */
+#define SNMP_PDU_GETBULK 5 /* v2 */
+#define SNMP_PDU_INFORM 6 /* v2 */
+#define SNMP_PDU_TRAP2 7 /* v2 */
+#define SNMP_PDU_REPORT 8 /* v2 */
+
+#define SNMP_ERR_NOERROR 0
+#define SNMP_ERR_TOOBIG 1
+#define SNMP_ERR_NOSUCHNAME 2 /* v1 */
+#define SNMP_ERR_BADVALUE 3 /* v1 */
+#define SNMP_ERR_READONLY 4 /* v1 */
+#define SNMP_ERR_GENERR 5
+#define SNMP_ERR_NO_ACCESS 6 /* v2 */
+#define SNMP_ERR_WRONG_TYPE 7 /* v2 */
+#define SNMP_ERR_WRONG_LENGTH 8 /* v2 */
+#define SNMP_ERR_WRONG_ENCODING 9 /* v2 */
+#define SNMP_ERR_WRONG_VALUE 10 /* v2 */
+#define SNMP_ERR_NO_CREATION 11 /* v2 */
+#define SNMP_ERR_INCONS_VALUE 12 /* v2 */
+#define SNMP_ERR_RES_UNAVAIL 13 /* v2 */
+#define SNMP_ERR_COMMIT_FAILED 14 /* v2 */
+#define SNMP_ERR_UNDO_FAILED 15 /* v2 */
+#define SNMP_ERR_AUTH_ERR 16 /* v2 */
+#define SNMP_ERR_NOT_WRITEABLE 17 /* v2 */
+#define SNMP_ERR_INCONS_NAME 18 /* v2 */
+
+#define SNMP_TRAP_COLDSTART 0
+#define SNMP_TRAP_WARMSTART 1
+#define SNMP_TRAP_LINKDOWN 2
+#define SNMP_TRAP_LINKUP 3
+#define SNMP_TRAP_AUTHENTICATION_FAILURE 4
+#define SNMP_TRAP_EGP_NEIGHBOR_LOSS 5
+#define SNMP_TRAP_ENTERPRISE 6
+
+enum snmp_code {
+ SNMP_CODE_OK = 0,
+ SNMP_CODE_FAILED,
+ SNMP_CODE_BADVERS,
+ SNMP_CODE_BADLEN,
+ SNMP_CODE_BADENC,
+ SNMP_CODE_OORANGE,
+};
+
+void snmp_value_free(struct snmp_value *);
+int snmp_value_parse(const char *, enum snmp_syntax, union snmp_values *);
+int snmp_value_copy(struct snmp_value *, const struct snmp_value *);
+
+void snmp_pdu_free(struct snmp_pdu *);
+enum snmp_code snmp_pdu_decode(struct asn_buf *b, struct snmp_pdu *pdu, int32_t *);
+enum snmp_code snmp_pdu_encode(struct snmp_pdu *pdu, struct asn_buf *resp_b);
+
+void snmp_pdu_dump(const struct snmp_pdu *pdu);
+
+extern void (*snmp_error)(const char *, ...);
+extern void (*snmp_printf)(const char *, ...);
+
+#define TRUTH_MK(F) ((F) ? 1 : 2)
+#define TRUTH_GET(T) (((T) == 1) ? 1 : 0)
+#define TRUTH_OK(T) ((T) == 1 || (T) == 2)
+
+#endif
diff --git a/contrib/bsnmp/lib/snmpagent.c b/contrib/bsnmp/lib/snmpagent.c
new file mode 100644
index 0000000..19ac661
--- /dev/null
+++ b/contrib/bsnmp/lib/snmpagent.c
@@ -0,0 +1,1031 @@
+/*
+ * Copyright (c) 2001-2003
+ * Fraunhofer Institute for Open Communication Systems (FhG Fokus).
+ * All rights reserved.
+ *
+ * Author: Harti Brandt <harti@freebsd.org>
+ *
+ * Redistribution of this software and documentation and use in source and
+ * binary forms, with or without modification, are permitted provided that
+ * the following conditions are met:
+ *
+ * 1. Redistributions of source code or documentation must retain the above
+ * copyright notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY FRAUNHOFER FOKUS
+ * AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * FRAUNHOFER FOKUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Begemot: bsnmp/lib/snmpagent.c,v 1.14 2003/01/30 11:23:00 hbb Exp $
+ *
+ * SNMP Agent functions
+ */
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <stdarg.h>
+#include <string.h>
+
+#include "asn1.h"
+#include "snmp.h"
+#include "snmppriv.h"
+#include "snmpagent.h"
+
+static void snmp_debug_func(const char *fmt, ...);
+
+void (*snmp_debug)(const char *fmt, ...) = snmp_debug_func;
+
+struct snmp_node *tree;
+u_int tree_size;
+
+/*
+ * Structure to hold dependencies during SET processing
+ * The last two members of this structure must be the
+ * dependency visible by the user and the user data.
+ */
+struct depend {
+ TAILQ_ENTRY(depend) link;
+ size_t len; /* size of data part */
+ snmp_depop_t func;
+ struct snmp_dependency dep;
+ u_char data[];
+};
+TAILQ_HEAD(depend_list, depend);
+
+/*
+ * Structure to hold atfinish functions during SET processing.
+ */
+struct finish {
+ STAILQ_ENTRY(finish) link;
+ snmp_set_finish_t func;
+ void *arg;
+};
+STAILQ_HEAD(finish_list, finish);
+
+/*
+ * Set context
+ */
+struct context {
+ struct snmp_context ctx;
+ struct depend_list dlist;
+ struct finish_list flist;
+ const struct snmp_node *node[SNMP_MAX_BINDINGS];
+ struct snmp_scratch scratch[SNMP_MAX_BINDINGS];
+ struct depend *depend;
+};
+
+#define TR(W) (snmp_trace & SNMP_TRACE_##W)
+u_int snmp_trace = 0;
+
+static char oidbuf[ASN_OIDSTRLEN];
+
+/*
+ * Allocate a context
+ */
+struct snmp_context *
+snmp_init_context(void)
+{
+ struct context *context;
+
+ if ((context = malloc(sizeof(*context))) == NULL)
+ return (NULL);
+
+ memset(context, 0, sizeof(*context));
+ TAILQ_INIT(&context->dlist);
+ STAILQ_INIT(&context->flist);
+
+ return (&context->ctx);
+}
+
+/*
+ * Find a variable for SET/GET and the first GETBULK pass.
+ * Return the node pointer. If the search fails, set the errp to
+ * the correct SNMPv2 GET exception code.
+ */
+static struct snmp_node *
+find_node(const struct snmp_value *value, enum snmp_syntax *errp)
+{
+ struct snmp_node *tp;
+
+ if (TR(FIND))
+ snmp_debug("find: searching %s",
+ asn_oid2str_r(&value->var, oidbuf));
+
+ /*
+ * If we have an exact match (the entry in the table is a
+ * sub-oid from the variable) we have found what we are for.
+ * If the table oid is higher than the variable, there is no match.
+ */
+ for (tp = tree; tp < tree + tree_size; tp++) {
+ if (asn_is_suboid(&tp->oid, &value->var))
+ goto found;
+ if (asn_compare_oid(&tp->oid, &value->var) >= 0)
+ break;
+ }
+
+ if (TR(FIND))
+ snmp_debug("find: no match");
+ *errp = SNMP_SYNTAX_NOSUCHOBJECT;
+ return (NULL);
+
+ found:
+ /* leafs must have a 0 instance identifier */
+ if (tp->type == SNMP_NODE_LEAF &&
+ (value->var.len != tp->oid.len + 1 ||
+ value->var.subs[tp->oid.len] != 0)) {
+ if (TR(FIND))
+ snmp_debug("find: bad leaf index");
+ *errp = SNMP_SYNTAX_NOSUCHINSTANCE;
+ return (NULL);
+ }
+ if (TR(FIND))
+ snmp_debug("find: found %s",
+ asn_oid2str_r(&value->var, oidbuf));
+ return (tp);
+}
+
+static struct snmp_node *
+find_subnode(const struct snmp_value *value)
+{
+ struct snmp_node *tp;
+
+ for (tp = tree; tp < tree + tree_size; tp++) {
+ if (asn_is_suboid(&value->var, &tp->oid))
+ return (tp);
+ }
+ return (NULL);
+}
+
+/*
+ * Execute a GET operation. The tree is rooted at the global 'root'.
+ * Build the response PDU on the fly. If the return code is SNMP_RET_ERR
+ * the pdu error status and index will be set.
+ */
+enum snmp_ret
+snmp_get(struct snmp_pdu *pdu, struct asn_buf *resp_b,
+ struct snmp_pdu *resp, void *data)
+{
+ int ret;
+ u_int i;
+ struct snmp_node *tp;
+ enum snmp_syntax except;
+ struct context context;
+ enum asn_err err;
+
+ memset(&context, 0, sizeof(context));
+ context.ctx.data = data;
+
+ memset(resp, 0, sizeof(*resp));
+ strcpy(resp->community, pdu->community);
+ resp->version = pdu->version;
+ resp->type = SNMP_PDU_RESPONSE;
+ resp->request_id = pdu->request_id;
+ resp->version = pdu->version;
+
+ if (snmp_pdu_encode_header(resp_b, resp) != SNMP_CODE_OK)
+ /* cannot even encode header - very bad */
+ return (SNMP_RET_IGN);
+
+ for (i = 0; i < pdu->nbindings; i++) {
+ resp->bindings[i].var = pdu->bindings[i].var;
+ if ((tp = find_node(&pdu->bindings[i], &except)) == NULL) {
+ if (pdu->version == SNMP_V1) {
+ if (TR(GET))
+ snmp_debug("get: nosuchname");
+ pdu->error_status = SNMP_ERR_NOSUCHNAME;
+ pdu->error_index = i + 1;
+ snmp_pdu_free(resp);
+ return (SNMP_RET_ERR);
+ }
+ if (TR(GET))
+ snmp_debug("get: exception %u", except);
+ resp->bindings[i].syntax = except;
+
+ } else {
+ /* call the action to fetch the value. */
+ resp->bindings[i].syntax = tp->syntax;
+ ret = (*tp->op)(&context.ctx, &resp->bindings[i],
+ tp->oid.len, tp->index, SNMP_OP_GET);
+ if (TR(GET))
+ snmp_debug("get: action returns %d", ret);
+
+ if (ret == SNMP_ERR_NOSUCHNAME) {
+ if (pdu->version == SNMP_V1) {
+ pdu->error_status = SNMP_ERR_NOSUCHNAME;
+ pdu->error_index = i + 1;
+ snmp_pdu_free(resp);
+ return (SNMP_RET_ERR);
+ }
+ if (TR(GET))
+ snmp_debug("get: exception noSuchInstance");
+ resp->bindings[i].syntax = SNMP_SYNTAX_NOSUCHINSTANCE;
+
+ } else if (ret != SNMP_ERR_NOERROR) {
+ pdu->error_status = SNMP_ERR_GENERR;
+ pdu->error_index = i + 1;
+ snmp_pdu_free(resp);
+ return (SNMP_RET_ERR);
+ }
+ }
+ resp->nbindings++;
+
+ err = snmp_binding_encode(resp_b, &resp->bindings[i]);
+
+ if (err == ASN_ERR_EOBUF) {
+ pdu->error_status = SNMP_ERR_TOOBIG;
+ pdu->error_index = 0;
+ snmp_pdu_free(resp);
+ return (SNMP_RET_ERR);
+ }
+ if (err != ASN_ERR_OK) {
+ if (TR(GET))
+ snmp_debug("get: binding encoding: %u", err);
+ pdu->error_status = SNMP_ERR_GENERR;
+ pdu->error_index = i + 1;
+ snmp_pdu_free(resp);
+ return (SNMP_RET_ERR);
+ }
+ }
+
+ return (snmp_fix_encoding(resp_b, resp));
+}
+
+static struct snmp_node *
+next_node(const struct snmp_value *value, int *pnext)
+{
+ struct snmp_node *tp;
+
+ if (TR(FIND))
+ snmp_debug("next: searching %s",
+ asn_oid2str_r(&value->var, oidbuf));
+
+ *pnext = 0;
+ for (tp = tree; tp < tree + tree_size; tp++) {
+ if (asn_is_suboid(&tp->oid, &value->var)) {
+ /* the tree OID is a sub-oid of the requested OID. */
+ if (tp->type == SNMP_NODE_LEAF) {
+ if (tp->oid.len == value->var.len) {
+ /* request for scalar type */
+ if (TR(FIND))
+ snmp_debug("next: found scalar %s",
+ asn_oid2str_r(&tp->oid, oidbuf));
+ return (tp);
+ }
+ /* try next */
+ } else {
+ if (TR(FIND))
+ snmp_debug("next: found column %s",
+ asn_oid2str_r(&tp->oid, oidbuf));
+ return (tp);
+ }
+ } else if (asn_is_suboid(&value->var, &tp->oid) ||
+ asn_compare_oid(&tp->oid, &value->var) >= 0) {
+ if (TR(FIND))
+ snmp_debug("next: found %s",
+ asn_oid2str_r(&tp->oid, oidbuf));
+ *pnext = 1;
+ return (tp);
+ }
+ }
+
+ if (TR(FIND))
+ snmp_debug("next: failed");
+
+ return (NULL);
+}
+
+static enum snmp_ret
+do_getnext(struct context *context, const struct snmp_value *inb,
+ struct snmp_value *outb, struct snmp_pdu *pdu)
+{
+ const struct snmp_node *tp;
+ int ret, next;
+
+ if ((tp = next_node(inb, &next)) == NULL)
+ goto eofMib;
+
+ /* retain old variable if we are doing a GETNEXT on an exact
+ * matched leaf only */
+ if (tp->type == SNMP_NODE_LEAF || next)
+ outb->var = tp->oid;
+ else
+ outb->var = inb->var;
+
+ for (;;) {
+ outb->syntax = tp->syntax;
+ if (tp->type == SNMP_NODE_LEAF) {
+ /* make a GET operation */
+ outb->var.subs[outb->var.len++] = 0;
+ ret = (*tp->op)(&context->ctx, outb, tp->oid.len,
+ tp->index, SNMP_OP_GET);
+ } else {
+ /* make a GETNEXT */
+ ret = (*tp->op)(&context->ctx, outb, tp->oid.len,
+ tp->index, SNMP_OP_GETNEXT);
+ }
+ if (ret != SNMP_ERR_NOSUCHNAME) {
+ /* got something */
+ if (ret != SNMP_ERR_NOERROR && TR(GETNEXT))
+ snmp_debug("getnext: %s returns %u",
+ asn_oid2str(&outb->var), ret);
+ break;
+ }
+
+ /* object has no data - try next */
+ if (++tp == tree + tree_size)
+ break;
+ outb->var = tp->oid;
+ }
+
+ if (ret == SNMP_ERR_NOSUCHNAME) {
+ eofMib:
+ outb->var = inb->var;
+ if (pdu->version == SNMP_V1) {
+ pdu->error_status = SNMP_ERR_NOSUCHNAME;
+ return (SNMP_RET_ERR);
+ }
+ outb->syntax = SNMP_SYNTAX_ENDOFMIBVIEW;
+
+ } else if (ret != SNMP_ERR_NOERROR) {
+ pdu->error_status = SNMP_ERR_GENERR;
+ return (SNMP_RET_ERR);
+ }
+ return (SNMP_RET_OK);
+}
+
+
+/*
+ * Execute a GETNEXT operation. The tree is rooted at the global 'root'.
+ * Build the response PDU on the fly. The return is:
+ */
+enum snmp_ret
+snmp_getnext(struct snmp_pdu *pdu, struct asn_buf *resp_b,
+ struct snmp_pdu *resp, void *data)
+{
+ struct context context;
+ u_int i;
+ enum asn_err err;
+ enum snmp_ret result;
+
+ memset(&context, 0, sizeof(context));
+ context.ctx.data = data;
+
+ memset(resp, 0, sizeof(*resp));
+ strcpy(resp->community, pdu->community);
+ resp->type = SNMP_PDU_RESPONSE;
+ resp->request_id = pdu->request_id;
+ resp->version = pdu->version;
+
+ if (snmp_pdu_encode_header(resp_b, resp))
+ return (SNMP_RET_IGN);
+
+ for (i = 0; i < pdu->nbindings; i++) {
+ result = do_getnext(&context, &pdu->bindings[i],
+ &resp->bindings[i], pdu);
+
+ if (result != SNMP_RET_OK) {
+ pdu->error_index = i + 1;
+ snmp_pdu_free(resp);
+ return (result);
+ }
+
+ resp->nbindings++;
+
+ err = snmp_binding_encode(resp_b, &resp->bindings[i]);
+
+ if (err == ASN_ERR_EOBUF) {
+ pdu->error_status = SNMP_ERR_TOOBIG;
+ pdu->error_index = 0;
+ snmp_pdu_free(resp);
+ return (SNMP_RET_ERR);
+ }
+ if (err != ASN_ERR_OK) {
+ if (TR(GET))
+ snmp_debug("getnext: binding encoding: %u", err);
+ pdu->error_status = SNMP_ERR_GENERR;
+ pdu->error_index = i + 1;
+ snmp_pdu_free(resp);
+ return (SNMP_RET_ERR);
+ }
+ }
+ return (snmp_fix_encoding(resp_b, resp));
+}
+
+enum snmp_ret
+snmp_getbulk(struct snmp_pdu *pdu, struct asn_buf *resp_b,
+ struct snmp_pdu *resp, void *data)
+{
+ struct context context;
+ u_int i;
+ int cnt;
+ u_int non_rep;
+ int eomib;
+ enum snmp_ret result;
+ enum asn_err err;
+
+ memset(&context, 0, sizeof(context));
+ context.ctx.data = data;
+
+ memset(resp, 0, sizeof(*resp));
+ strcpy(resp->community, pdu->community);
+ resp->version = pdu->version;
+ resp->type = SNMP_PDU_RESPONSE;
+ resp->request_id = pdu->request_id;
+ resp->version = pdu->version;
+
+ if (snmp_pdu_encode_header(resp_b, resp) != SNMP_CODE_OK)
+ /* cannot even encode header - very bad */
+ return (SNMP_RET_IGN);
+
+ if ((non_rep = pdu->error_status) > pdu->nbindings)
+ non_rep = pdu->nbindings;
+
+ /* non-repeaters */
+ for (i = 0; i < non_rep; i++) {
+ result = do_getnext(&context, &pdu->bindings[i],
+ &resp->bindings[resp->nbindings], pdu);
+
+ if (result != SNMP_RET_OK) {
+ pdu->error_index = i + 1;
+ snmp_pdu_free(resp);
+ return (result);
+ }
+
+ err = snmp_binding_encode(resp_b,
+ &resp->bindings[resp->nbindings++]);
+
+ if (err == ASN_ERR_EOBUF)
+ goto done;
+
+ if (err != ASN_ERR_OK) {
+ if (TR(GET))
+ snmp_debug("getnext: binding encoding: %u", err);
+ pdu->error_status = SNMP_ERR_GENERR;
+ pdu->error_index = i + 1;
+ snmp_pdu_free(resp);
+ return (SNMP_RET_ERR);
+ }
+ }
+
+ if (non_rep == pdu->nbindings)
+ goto done;
+
+ /* repeates */
+ for (cnt = 0; cnt < pdu->error_index; cnt++) {
+ eomib = 1;
+ for (i = non_rep; i < pdu->nbindings; i++) {
+ if (cnt == 0)
+ result = do_getnext(&context, &pdu->bindings[i],
+ &resp->bindings[resp->nbindings], pdu);
+ else
+ result = do_getnext(&context,
+ &resp->bindings[resp->nbindings -
+ (pdu->nbindings - non_rep)],
+ &resp->bindings[resp->nbindings], pdu);
+
+ if (result != SNMP_RET_OK) {
+ pdu->error_index = i + 1;
+ snmp_pdu_free(resp);
+ return (result);
+ }
+ if (resp->bindings[resp->nbindings].syntax !=
+ SNMP_SYNTAX_ENDOFMIBVIEW)
+ eomib = 0;
+
+ err = snmp_binding_encode(resp_b,
+ &resp->bindings[resp->nbindings++]);
+
+ if (err == ASN_ERR_EOBUF)
+ goto done;
+
+ if (err != ASN_ERR_OK) {
+ if (TR(GET))
+ snmp_debug("getnext: binding encoding: %u", err);
+ pdu->error_status = SNMP_ERR_GENERR;
+ pdu->error_index = i + 1;
+ snmp_pdu_free(resp);
+ return (SNMP_RET_ERR);
+ }
+ }
+ if (eomib)
+ break;
+ }
+
+ done:
+ return (snmp_fix_encoding(resp_b, resp));
+}
+
+/*
+ * Rollback a SET operation. Failed index is 'i'.
+ */
+static void
+rollback(struct context *context, struct snmp_pdu *pdu, u_int i)
+{
+ struct snmp_value *b;
+ const struct snmp_node *np;
+ int ret;
+
+ while (i-- > 0) {
+ b = &pdu->bindings[i];
+ np = context->node[i];
+
+ context->ctx.scratch = &context->scratch[i];
+
+ ret = (*np->op)(&context->ctx, b, np->oid.len, np->index,
+ SNMP_OP_ROLLBACK);
+
+ if (ret != SNMP_ERR_NOERROR) {
+ snmp_error("set: rollback failed (%d) on variable %s "
+ "index %u", ret, asn_oid2str(&b->var), i);
+ if (pdu->version != SNMP_V1) {
+ pdu->error_status = SNMP_ERR_UNDO_FAILED;
+ pdu->error_index = 0;
+ }
+ }
+ }
+}
+
+/*
+ * Commit dependencies.
+ */
+int
+snmp_dep_commit(struct snmp_context *ctx)
+{
+ struct context *context = (struct context *)ctx;
+ int ret;
+
+ TAILQ_FOREACH(context->depend, &context->dlist, link) {
+ ctx->dep = &context->depend->dep;
+
+ if (TR(SET))
+ snmp_debug("set: dependency commit %s",
+ asn_oid2str(&ctx->dep->obj));
+
+ ret = context->depend->func(ctx, ctx->dep, SNMP_DEPOP_COMMIT);
+
+ if (ret != SNMP_ERR_NOERROR) {
+ if (TR(SET))
+ snmp_debug("set: dependency failed %d", ret);
+ return (ret);
+ }
+ }
+ return (SNMP_ERR_NOERROR);
+}
+
+/*
+ * Rollback dependencies
+ */
+int
+snmp_dep_rollback(struct snmp_context *ctx)
+{
+ struct context *context = (struct context *)ctx;
+ int ret, ret1;
+ char objbuf[ASN_OIDSTRLEN];
+ char idxbuf[ASN_OIDSTRLEN];
+
+ ret1 = SNMP_ERR_NOERROR;
+ while ((context->depend =
+ TAILQ_PREV(context->depend, depend_list, link)) != NULL) {
+ ctx->dep = &context->depend->dep;
+
+ if (TR(SET))
+ snmp_debug("set: dependency rollback %s",
+ asn_oid2str(&ctx->dep->obj));
+
+ ret = context->depend->func(ctx, ctx->dep, SNMP_DEPOP_ROLLBACK);
+
+ if (ret != SNMP_ERR_NOERROR) {
+ snmp_debug("set: dep rollback returns %u: %s %s", ret,
+ asn_oid2str_r(&ctx->dep->obj, objbuf),
+ asn_oid2str_r(&ctx->dep->idx, idxbuf));
+ if (ret1 == SNMP_ERR_NOERROR)
+ ret1 = ret;
+ }
+ }
+ return (ret1);
+}
+
+/*
+ * Do a SET operation.
+ */
+enum snmp_ret
+snmp_set(struct snmp_pdu *pdu, struct asn_buf *resp_b,
+ struct snmp_pdu *resp, void *data)
+{
+ int ret;
+ u_int i;
+ enum snmp_ret code;
+ enum asn_err asnerr;
+ struct context context;
+ const struct snmp_node *np;
+ struct finish *f;
+ struct depend *d;
+ struct snmp_value *b;
+ enum snmp_syntax except;
+
+ memset(&context, 0, sizeof(context));
+ TAILQ_INIT(&context.dlist);
+ STAILQ_INIT(&context.flist);
+ context.ctx.data = data;
+
+ memset(resp, 0, sizeof(*resp));
+ strcpy(resp->community, pdu->community);
+ resp->type = SNMP_PDU_RESPONSE;
+ resp->request_id = pdu->request_id;
+ resp->version = pdu->version;
+
+ if (snmp_pdu_encode_header(resp_b, resp))
+ return (SNMP_RET_IGN);
+
+ /*
+ * 1. Find all nodes, check that they are writeable and
+ * that the syntax is ok, copy over the binding to the response.
+ */
+ for (i = 0; i < pdu->nbindings; i++) {
+ b = &pdu->bindings[i];
+
+ if ((np = context.node[i] = find_node(b, &except)) == NULL) {
+ /* not found altogether or LEAF with wrong index */
+ if (TR(SET))
+ snmp_debug("set: node not found %s",
+ asn_oid2str_r(&b->var, oidbuf));
+ if (pdu->version == SNMP_V1) {
+ pdu->error_index = i + 1;
+ pdu->error_status = SNMP_ERR_NOSUCHNAME;
+ } else if ((np = find_subnode(b)) != NULL) {
+ /* 2. intermediate object */
+ pdu->error_index = i + 1;
+ pdu->error_status = SNMP_ERR_NOT_WRITEABLE;
+ } else if (except == SNMP_SYNTAX_NOSUCHOBJECT) {
+ pdu->error_index = i + 1;
+ pdu->error_status = SNMP_ERR_NO_ACCESS;
+ } else {
+ pdu->error_index = i + 1;
+ pdu->error_status = SNMP_ERR_NO_CREATION;
+ }
+ snmp_pdu_free(resp);
+ return (SNMP_RET_ERR);
+ }
+ /*
+ * 2. write/createable?
+ * Can check this for leafs only, because in v2 we have
+ * to differentiate between NOT_WRITEABLE and NO_CREATION
+ * and only the action routine for COLUMNS knows, whether
+ * a column exists.
+ */
+ if (np->type == SNMP_NODE_LEAF &&
+ !(np->flags & SNMP_NODE_CANSET)) {
+ if (pdu->version == SNMP_V1) {
+ pdu->error_index = i + 1;
+ pdu->error_status = SNMP_ERR_NOSUCHNAME;
+ } else {
+ pdu->error_index = i + 1;
+ pdu->error_status = SNMP_ERR_NOT_WRITEABLE;
+ }
+ snmp_pdu_free(resp);
+ return (SNMP_RET_ERR);
+ }
+ /*
+ * 3. Ensure the right syntax
+ */
+ if (np->syntax != b->syntax) {
+ if (pdu->version == SNMP_V1) {
+ pdu->error_index = i + 1;
+ pdu->error_status = SNMP_ERR_BADVALUE; /* v2: wrongType */
+ } else {
+ pdu->error_index = i + 1;
+ pdu->error_status = SNMP_ERR_WRONG_TYPE;
+ }
+ snmp_pdu_free(resp);
+ return (SNMP_RET_ERR);
+ }
+ /*
+ * 4. Copy binding
+ */
+ if (snmp_value_copy(&resp->bindings[i], b)) {
+ pdu->error_index = i + 1;
+ pdu->error_status = SNMP_ERR_GENERR;
+ snmp_pdu_free(resp);
+ return (SNMP_RET_ERR);
+ }
+ asnerr = snmp_binding_encode(resp_b, &resp->bindings[i]);
+ if (asnerr == ASN_ERR_EOBUF) {
+ pdu->error_index = i + 1;
+ pdu->error_status = SNMP_ERR_TOOBIG;
+ snmp_pdu_free(resp);
+ return (SNMP_RET_ERR);
+ } else if (asnerr != ASN_ERR_OK) {
+ pdu->error_index = i + 1;
+ pdu->error_status = SNMP_ERR_GENERR;
+ snmp_pdu_free(resp);
+ return (SNMP_RET_ERR);
+ }
+ resp->nbindings++;
+ }
+
+ code = SNMP_RET_OK;
+
+ /*
+ * 2. Call the SET method for each node. If a SET fails, rollback
+ * everything. Map error codes depending on the version.
+ */
+ for (i = 0; i < pdu->nbindings; i++) {
+ b = &pdu->bindings[i];
+ np = context.node[i];
+
+ context.ctx.var_index = i + 1;
+ context.ctx.scratch = &context.scratch[i];
+
+ ret = (*np->op)(&context.ctx, b, np->oid.len, np->index,
+ SNMP_OP_SET);
+
+ if (TR(SET))
+ snmp_debug("set: action %s returns %d", np->name, ret);
+
+ if (pdu->version == SNMP_V1) {
+ switch (ret) {
+ case SNMP_ERR_NO_ACCESS:
+ ret = SNMP_ERR_NOSUCHNAME;
+ break;
+ case SNMP_ERR_WRONG_TYPE:
+ /* should no happen */
+ ret = SNMP_ERR_BADVALUE;
+ break;
+ case SNMP_ERR_WRONG_LENGTH:
+ ret = SNMP_ERR_BADVALUE;
+ break;
+ case SNMP_ERR_WRONG_ENCODING:
+ /* should not happen */
+ ret = SNMP_ERR_BADVALUE;
+ break;
+ case SNMP_ERR_WRONG_VALUE:
+ ret = SNMP_ERR_BADVALUE;
+ break;
+ case SNMP_ERR_NO_CREATION:
+ ret = SNMP_ERR_NOSUCHNAME;
+ break;
+ case SNMP_ERR_INCONS_VALUE:
+ ret = SNMP_ERR_BADVALUE;
+ break;
+ case SNMP_ERR_RES_UNAVAIL:
+ ret = SNMP_ERR_GENERR;
+ break;
+ case SNMP_ERR_COMMIT_FAILED:
+ ret = SNMP_ERR_GENERR;
+ break;
+ case SNMP_ERR_UNDO_FAILED:
+ ret = SNMP_ERR_GENERR;
+ break;
+ case SNMP_ERR_AUTH_ERR:
+ /* should not happen */
+ ret = SNMP_ERR_GENERR;
+ break;
+ case SNMP_ERR_NOT_WRITEABLE:
+ ret = SNMP_ERR_NOSUCHNAME;
+ break;
+ case SNMP_ERR_INCONS_NAME:
+ ret = SNMP_ERR_BADVALUE;
+ break;
+ }
+ }
+ if (ret != SNMP_ERR_NOERROR) {
+ pdu->error_index = i + 1;
+ pdu->error_status = ret;
+
+ rollback(&context, pdu, i);
+ snmp_pdu_free(resp);
+
+ code = SNMP_RET_ERR;
+
+ goto errout;
+ }
+ }
+
+ /*
+ * 3. Call dependencies
+ */
+ if (TR(SET))
+ snmp_debug("set: set operations ok");
+
+ if ((ret = snmp_dep_commit(&context.ctx)) != SNMP_ERR_NOERROR) {
+ pdu->error_status = ret;
+ pdu->error_index = context.ctx.var_index;
+
+ if ((ret = snmp_dep_rollback(&context.ctx)) != SNMP_ERR_NOERROR) {
+ if (pdu->version != SNMP_V1) {
+ pdu->error_status = SNMP_ERR_UNDO_FAILED;
+ pdu->error_index = 0;
+ }
+ }
+ rollback(&context, pdu, i);
+ snmp_pdu_free(resp);
+
+ code = SNMP_RET_ERR;
+
+ goto errout;
+ }
+
+ /*
+ * 4. Commit and copy values from the original packet to the response.
+ * This is not the commit operation from RFC 1905 but rather an
+ * 'FREE RESOURCES' operation. It shouldn't fail.
+ */
+ if (TR(SET))
+ snmp_debug("set: commiting");
+
+ for (i = 0; i < pdu->nbindings; i++) {
+ b = &resp->bindings[i];
+ np = context.node[i];
+
+ context.ctx.var_index = i + 1;
+ context.ctx.scratch = &context.scratch[i];
+
+ ret = (*np->op)(&context.ctx, b, np->oid.len, np->index,
+ SNMP_OP_COMMIT);
+
+ if (ret != SNMP_ERR_NOERROR)
+ snmp_error("set: commit failed (%d) on"
+ " variable %s index %u", ret,
+ asn_oid2str_r(&b->var, oidbuf), i);
+ }
+
+ if (snmp_fix_encoding(resp_b, resp) != SNMP_CODE_OK) {
+ snmp_error("set: fix_encoding failed");
+ snmp_pdu_free(resp);
+ code = SNMP_RET_IGN;
+ }
+
+ /*
+ * Done
+ */
+ errout:
+ while ((d = TAILQ_FIRST(&context.dlist)) != NULL) {
+ TAILQ_REMOVE(&context.dlist, d, link);
+ free(d);
+ }
+
+ /*
+ * call finish function
+ */
+ while ((f = STAILQ_FIRST(&context.flist)) != NULL) {
+ STAILQ_REMOVE_HEAD(&context.flist, link);
+ (*f->func)(&context.ctx, code != SNMP_RET_OK, f->arg);
+ free(f);
+ }
+
+ if (TR(SET))
+ snmp_debug("set: returning %d", code);
+
+ return (code);
+}
+/*
+ * Lookup a dependency. If it doesn't exist, create one
+ */
+struct snmp_dependency *
+snmp_dep_lookup(struct snmp_context *ctx, const struct asn_oid *obj,
+ const struct asn_oid *idx, size_t len, snmp_depop_t func)
+{
+ struct context *context;
+ struct depend *d;
+
+ context = (struct context *)(void *)
+ ((char *)ctx - offsetof(struct context, ctx));
+ if (TR(DEPEND)) {
+ snmp_debug("depend: looking for %s", asn_oid2str(obj));
+ if (idx)
+ snmp_debug("depend: index is %s", asn_oid2str(idx));
+ }
+ TAILQ_FOREACH(d, &context->dlist, link)
+ if (asn_compare_oid(obj, &d->dep.obj) == 0 &&
+ ((idx == NULL && d->dep.idx.len == 0) ||
+ (idx != NULL && asn_compare_oid(idx, &d->dep.idx) == 0))) {
+ if(TR(DEPEND))
+ snmp_debug("depend: found");
+ return (&d->dep);
+ }
+
+ if(TR(DEPEND))
+ snmp_debug("depend: creating");
+
+ if ((d = malloc(offsetof(struct depend, dep) + len)) == NULL)
+ return (NULL);
+ memset(&d->dep, 0, len);
+
+ d->dep.obj = *obj;
+ if (idx == NULL)
+ d->dep.idx.len = 0;
+ else
+ d->dep.idx = *idx;
+ d->len = len;
+ d->func = func;
+
+ TAILQ_INSERT_TAIL(&context->dlist, d, link);
+
+ return (&d->dep);
+}
+
+/*
+ * Register a finish function.
+ */
+int
+snmp_set_atfinish(struct snmp_context *ctx, snmp_set_finish_t func, void *arg)
+{
+ struct context *context;
+ struct finish *f;
+
+ context = (struct context *)(void *)
+ ((char *)ctx - offsetof(struct context, ctx));
+ if ((f = malloc(sizeof(struct finish))) == NULL)
+ return (-1);
+ f->func = func;
+ f->arg = arg;
+ STAILQ_INSERT_TAIL(&context->flist, f, link);
+
+ return (0);
+}
+
+/*
+ * Make an error response from a PDU. We do this without decoding the
+ * variable bindings. This means we can sent the junk back to a caller
+ * that has sent us junk in the first place.
+ */
+enum snmp_ret
+snmp_make_errresp(const struct snmp_pdu *pdu, struct asn_buf *pdu_b,
+ struct asn_buf *resp_b)
+{
+ asn_len_t len;
+ struct snmp_pdu resp;
+ enum asn_err err;
+ enum snmp_code code;
+
+ memset(&resp, 0, sizeof(resp));
+
+ /* Message sequence */
+ if (asn_get_sequence(pdu_b, &len) != ASN_ERR_OK)
+ return (SNMP_RET_IGN);
+ if (pdu_b->asn_len < len)
+ return (SNMP_RET_IGN);
+
+ err = snmp_parse_message_hdr(pdu_b, &resp, &len);
+ if (ASN_ERR_STOPPED(err))
+ return (SNMP_RET_IGN);
+ if (pdu_b->asn_len < len)
+ return (SNMP_RET_IGN);
+ pdu_b->asn_len = len;
+
+ err = snmp_parse_pdus_hdr(pdu_b, &resp, &len);
+ if (ASN_ERR_STOPPED(err))
+ return (SNMP_RET_IGN);
+ if (pdu_b->asn_len < len)
+ return (SNMP_RET_IGN);
+ pdu_b->asn_len = len;
+
+ /* now we have the bindings left - construct new message */
+ resp.error_status = pdu->error_status;
+ resp.error_index = pdu->error_index;
+ resp.type = SNMP_PDU_RESPONSE;
+
+ code = snmp_pdu_encode_header(resp_b, &resp);
+ if (code != SNMP_CODE_OK)
+ return (SNMP_RET_IGN);
+
+ if (pdu_b->asn_len > resp_b->asn_len)
+ /* too short */
+ return (SNMP_RET_IGN);
+ (void)memcpy(resp_b->asn_ptr, pdu_b->asn_cptr, pdu_b->asn_len);
+ resp_b->asn_len -= pdu_b->asn_len;
+ resp_b->asn_ptr += pdu_b->asn_len;
+
+ code = snmp_fix_encoding(resp_b, &resp);
+ if (code != SNMP_CODE_OK)
+ return (SNMP_RET_IGN);
+
+ return (SNMP_RET_OK);
+}
+
+static void
+snmp_debug_func(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ fprintf(stderr, "\n");
+}
diff --git a/contrib/bsnmp/lib/snmpagent.h b/contrib/bsnmp/lib/snmpagent.h
new file mode 100644
index 0000000..a854138
--- /dev/null
+++ b/contrib/bsnmp/lib/snmpagent.h
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 2001-2003
+ * Fraunhofer Institute for Open Communication Systems (FhG Fokus).
+ * All rights reserved.
+ *
+ * Author: Harti Brandt <harti@freebsd.org>
+ *
+ * Redistribution of this software and documentation and use in source and
+ * binary forms, with or without modification, are permitted provided that
+ * the following conditions are met:
+ *
+ * 1. Redistributions of source code or documentation must retain the above
+ * copyright notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY FRAUNHOFER FOKUS
+ * AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * FRAUNHOFER FOKUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Begemot: bsnmp/lib/snmpagent.h,v 1.9 2002/03/08 14:24:58 hbb Exp $
+ *
+ * Header file for SNMP functions. This requires snmp.h to be included.
+ */
+#ifndef snmp_agent_h_
+#define snmp_agent_h_
+
+struct snmp_dependency;
+
+/* Semi-Opaque object for SET operations */
+struct snmp_context {
+ u_int var_index;
+ struct snmp_scratch *scratch;
+ struct snmp_dependency *dep;
+ void *data; /* user data */
+};
+
+struct snmp_scratch {
+ void *ptr1;
+ void *ptr2;
+ u_int32_t int1;
+ u_int32_t int2;
+};
+
+enum snmp_depop {
+ SNMP_DEPOP_COMMIT,
+ SNMP_DEPOP_ROLLBACK
+};
+
+typedef int (*snmp_depop_t)(struct snmp_context *, struct snmp_dependency *,
+ enum snmp_depop);
+
+struct snmp_dependency {
+ struct asn_oid obj;
+ struct asn_oid idx;
+};
+
+/*
+ * Functions to be called at the end of a SET operation.
+ */
+typedef void (*snmp_set_finish_t)(struct snmp_context *, int fail, void *);
+
+/*
+ * The TREE
+ */
+enum snmp_node_type {
+ SNMP_NODE_LEAF = 1,
+ SNMP_NODE_COLUMN
+};
+
+enum snmp_op {
+ SNMP_OP_GET = 1,
+ SNMP_OP_GETNEXT,
+ SNMP_OP_SET,
+ SNMP_OP_COMMIT,
+ SNMP_OP_ROLLBACK,
+};
+
+enum snmp_ret {
+ /* OK, generate a response */
+ SNMP_RET_OK = 0,
+ /* Error, ignore packet (no response) */
+ SNMP_RET_IGN = 1,
+ /* Error, generate response from original packet */
+ SNMP_RET_ERR = 2
+};
+
+typedef int (*snmp_op_t)(struct snmp_context *, struct snmp_value *,
+ u_int, u_int, enum snmp_op);
+
+struct snmp_node {
+ struct asn_oid oid;
+ const char *name; /* name of the leaf */
+ enum snmp_node_type type; /* type of this node */
+ enum snmp_syntax syntax;
+ snmp_op_t op;
+ u_int flags;
+ u_int32_t index; /* index data */
+ void *data; /* application data */
+};
+extern struct snmp_node *tree;
+extern u_int tree_size;
+
+#define SNMP_NODE_CANSET 0x0001 /* SET allowed */
+
+#define SNMP_INDEXES_MAX 7
+#define SNMP_INDEX_SHIFT 4
+#define SNMP_INDEX_MASK 0xf
+#define SNMP_INDEX_COUNT(V) ((V) & SNMP_INDEX_MASK)
+#define SNMP_INDEX(V,I) \
+ (((V) >> (((I) + 1) * SNMP_INDEX_SHIFT)) & SNMP_INDEX_MASK)
+
+enum {
+ SNMP_TRACE_GET = 0x00000001,
+ SNMP_TRACE_GETNEXT = 0x00000002,
+ SNMP_TRACE_SET = 0x00000004,
+ SNMP_TRACE_DEPEND = 0x00000008,
+ SNMP_TRACE_FIND = 0x00000010,
+};
+/* trace flag for the following functions */
+extern u_int snmp_trace;
+
+/* called to write the trace */
+extern void (*snmp_debug)(const char *fmt, ...);
+
+enum snmp_ret snmp_get(struct snmp_pdu *pdu, struct asn_buf *resp_b,
+ struct snmp_pdu *resp, void *);
+enum snmp_ret snmp_getnext(struct snmp_pdu *pdu, struct asn_buf *resp_b,
+ struct snmp_pdu *resp, void *);
+enum snmp_ret snmp_getbulk(struct snmp_pdu *pdu, struct asn_buf *resp_b,
+ struct snmp_pdu *resp, void *);
+enum snmp_ret snmp_set(struct snmp_pdu *pdu, struct asn_buf *resp_b,
+ struct snmp_pdu *resp, void *);
+
+enum snmp_ret snmp_make_errresp(const struct snmp_pdu *, struct asn_buf *,
+ struct asn_buf *);
+
+struct snmp_dependency *snmp_dep_lookup(struct snmp_context *,
+ const struct asn_oid *, const struct asn_oid *, size_t, snmp_depop_t);
+
+int snmp_set_atfinish(struct snmp_context *, snmp_set_finish_t func, void *arg);
+
+struct snmp_context *snmp_init_context(void);
+int snmp_dep_commit(struct snmp_context *);
+int snmp_dep_rollback(struct snmp_context *);
+
+#endif
diff --git a/contrib/bsnmp/lib/snmpclient.c b/contrib/bsnmp/lib/snmpclient.c
new file mode 100644
index 0000000..e1206b0
--- /dev/null
+++ b/contrib/bsnmp/lib/snmpclient.c
@@ -0,0 +1,1658 @@
+/*
+ * Copyright (c) 2001-2003
+ * Fraunhofer Institute for Open Communication Systems (FhG Fokus).
+ * All rights reserved.
+ *
+ * Author: Harti Brandt <harti@freebsd.org>
+ * Kendy Kutzner
+ *
+ * Redistribution of this software and documentation and use in source and
+ * binary forms, with or without modification, are permitted provided that
+ * the following conditions are met:
+ *
+ * 1. Redistributions of source code or documentation must retain the above
+ * copyright notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY FRAUNHOFER FOKUS
+ * AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * FRAUNHOFER FOKUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Begemot: bsnmp/lib/snmpclient.c,v 1.24 2003/01/28 13:44:34 hbb Exp $
+ *
+ * Support functions for SNMP clients.
+ */
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <inttypes.h>
+#include <err.h>
+#include <limits.h>
+
+#include "asn1.h"
+#include "snmp.h"
+#include "snmpclient.h"
+#include "snmppriv.h"
+
+/* global context */
+struct snmp_client snmp_client;
+
+
+/* List of all outstanding requests */
+struct sent_pdu {
+ int reqid;
+ struct snmp_pdu *pdu;
+ struct timeval time;
+ u_int retrycount;
+ snmp_send_cb_f callback;
+ void *arg;
+ void *timeout_id;
+ LIST_ENTRY(sent_pdu) entries;
+};
+LIST_HEAD(sent_pdu_list, sent_pdu);
+
+static struct sent_pdu_list sent_pdus;
+
+/*
+ * Prototype table entry. All C-structure produced by the table function must
+ * start with these two fields. This relies on the fact, that all TAILQ_ENTRY
+ * are compatible with each other in the sense implied by ANSI-C.
+ */
+struct entry {
+ TAILQ_ENTRY(entry) link;
+ u_int64_t found;
+};
+TAILQ_HEAD(table, entry);
+
+/*
+ * working list entry. This list is used to hold the Index part of the
+ * table row's. The entry list and the work list parallel each other.
+ */
+struct work {
+ TAILQ_ENTRY(work) link;
+ struct asn_oid index;
+};
+TAILQ_HEAD(worklist, work);
+
+/*
+ * Table working data
+ */
+struct tabwork {
+ const struct snmp_table *descr;
+ struct table *table;
+ struct worklist worklist;
+ u_int32_t last_change;
+ int first;
+ u_int iter;
+ snmp_table_cb_f callback;
+ void *arg;
+ struct snmp_pdu pdu;
+};
+
+/*
+ * Set the error string
+ */
+static void
+seterr(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsnprintf(snmp_client.error, sizeof(snmp_client.error), fmt, ap);
+ va_end(ap);
+}
+
+/*
+ * Free the entire table and work list. If table is NULL only the worklist
+ * is freed.
+ */
+static void
+table_free(struct tabwork *work, int all)
+{
+ struct work *w;
+ struct entry *e;
+ const struct snmp_table_entry *d;
+ u_int i;
+
+ while ((w = TAILQ_FIRST(&work->worklist)) != NULL) {
+ TAILQ_REMOVE(&work->worklist, w, link);
+ free(w);
+ }
+
+ if (all == 0)
+ return;
+
+ while ((e = TAILQ_FIRST(work->table)) != NULL) {
+ for (i = 0; work->descr->entries[i].syntax != SNMP_SYNTAX_NULL;
+ i++) {
+ d = &work->descr->entries[i];
+ if (d->syntax == SNMP_SYNTAX_OCTETSTRING &&
+ (e->found & ((u_int64_t)1 << i)))
+ free(*(void **)(void *)
+ ((u_char *)e + d->offset));
+ }
+ TAILQ_REMOVE(work->table, e, link);
+ free(e);
+ }
+}
+
+/*
+ * Find the correct table entry for the given variable. If non exists,
+ * create one.
+ */
+static struct entry *
+table_find(struct tabwork *work, const struct asn_oid *var)
+{
+ struct entry *e, *e1;
+ struct work *w, *w1;
+ u_int i, p, j;
+ size_t len;
+ u_char *ptr;
+ struct asn_oid oid;
+
+ /* get index */
+ asn_slice_oid(&oid, var, work->descr->table.len + 2, var->len);
+
+ e = TAILQ_FIRST(work->table);
+ w = TAILQ_FIRST(&work->worklist);
+ while (e != NULL) {
+ if (asn_compare_oid(&w->index, &oid) == 0)
+ return (e);
+ e = TAILQ_NEXT(e, link);
+ w = TAILQ_NEXT(w, link);
+ }
+
+ /* Not found create new one */
+ if ((e = malloc(work->descr->entry_size)) == NULL) {
+ seterr("no memory for table entry");
+ return (NULL);
+ }
+ if ((w = malloc(sizeof(*w))) == NULL) {
+ seterr("no memory for table entry");
+ free(e);
+ return (NULL);
+ }
+ w->index = oid;
+ memset(e, 0, work->descr->entry_size);
+
+ /* decode index */
+ p = work->descr->table.len + 2;
+ for (i = 0; i < work->descr->index_size; i++) {
+ switch (work->descr->entries[i].syntax) {
+
+ case SNMP_SYNTAX_INTEGER:
+ if (var->len < p + 1) {
+ seterr("bad index: need integer");
+ goto err;
+ }
+ if (var->subs[p] > INT32_MAX) {
+ seterr("bad index: integer too large");
+ goto err;
+ }
+ *(int32_t *)(void *)((u_char *)e +
+ work->descr->entries[i].offset) = var->subs[p++];
+ break;
+
+ case SNMP_SYNTAX_OCTETSTRING:
+ if (var->len < p + 1) {
+ seterr("bad index: need string length");
+ goto err;
+ }
+ len = var->subs[p++];
+ if (var->len < p + len) {
+ seterr("bad index: string too short");
+ goto err;
+ }
+ if ((ptr = malloc(len + 1)) == NULL) {
+ seterr("no memory for index string");
+ goto err;
+ }
+ for (j = 0; j < len; j++) {
+ if (var->subs[p] > UCHAR_MAX) {
+ seterr("bad index: char too large");
+ free(ptr);
+ goto err;
+ }
+ ptr[j] = var->subs[p++];
+ }
+ ptr[j] = '\0';
+ *(u_char **)(void *)((u_char *)e +
+ work->descr->entries[i].offset) = ptr;
+ *(size_t *)(void *)((u_char *)e +
+ work->descr->entries[i].offset + sizeof(u_char *))
+ = len;
+ break;
+
+ case SNMP_SYNTAX_OID:
+ if (var->len < p + 1) {
+ seterr("bad index: need oid length");
+ goto err;
+ }
+ oid.len = var->subs[p++];
+ if (var->len < p + oid.len) {
+ seterr("bad index: oid too short");
+ goto err;
+ }
+ for (j = 0; j < oid.len; j++)
+ oid.subs[j] = var->subs[p++];
+ *(struct asn_oid *)(void *)((u_char *)e +
+ work->descr->entries[i].offset) = oid;
+ break;
+
+ case SNMP_SYNTAX_IPADDRESS:
+ if (var->len < p + 4) {
+ seterr("bad index: need ip-address");
+ goto err;
+ }
+ for (j = 0; j < 4; j++) {
+ if (var->subs[p] > 0xff) {
+ seterr("bad index: ipaddress too large");
+ goto err;
+ }
+ ((u_char *)e +
+ work->descr->entries[i].offset)[j] =
+ var->subs[p++];
+ }
+ break;
+
+ case SNMP_SYNTAX_GAUGE:
+ if (var->len < p + 1) {
+ seterr("bad index: need unsigned");
+ goto err;
+ }
+ if (var->subs[p] > UINT32_MAX) {
+ seterr("bad index: unsigned too large");
+ goto err;
+ }
+ *(u_int32_t *)(void *)((u_char *)e +
+ work->descr->entries[i].offset) = var->subs[p++];
+ break;
+
+ case SNMP_SYNTAX_COUNTER:
+ case SNMP_SYNTAX_TIMETICKS:
+ case SNMP_SYNTAX_COUNTER64:
+ case SNMP_SYNTAX_NULL:
+ case SNMP_SYNTAX_NOSUCHOBJECT:
+ case SNMP_SYNTAX_NOSUCHINSTANCE:
+ case SNMP_SYNTAX_ENDOFMIBVIEW:
+ abort();
+ }
+ e->found |= (u_int64_t)1 << i;
+ }
+
+ /* link into the correct place */
+ e1 = TAILQ_FIRST(work->table);
+ w1 = TAILQ_FIRST(&work->worklist);
+ while (e1 != NULL) {
+ if (asn_compare_oid(&w1->index, &w->index) > 0)
+ break;
+ e1 = TAILQ_NEXT(e1, link);
+ w1 = TAILQ_NEXT(w1, link);
+ }
+ if (e1 == NULL) {
+ TAILQ_INSERT_TAIL(work->table, e, link);
+ TAILQ_INSERT_TAIL(&work->worklist, w, link);
+ } else {
+ TAILQ_INSERT_BEFORE(e1, e, link);
+ TAILQ_INSERT_BEFORE(w1, w, link);
+ }
+
+ return (e);
+
+ err:
+ /*
+ * Error happend. Free all octet string index parts and the entry
+ * itself.
+ */
+ for (i = 0; i < work->descr->index_size; i++) {
+ if (work->descr->entries[i].syntax == SNMP_SYNTAX_OCTETSTRING &&
+ (e->found & ((u_int64_t)1 << i)))
+ free(*(void **)(void *)((u_char *)e +
+ work->descr->entries[i].offset));
+ }
+ free(e);
+ free(w);
+ return (NULL);
+}
+
+/*
+ * Assign the value
+ */
+static int
+table_value(const struct snmp_table *descr, struct entry *e,
+ const struct snmp_value *b)
+{
+ u_int i;
+ u_char *ptr;
+
+ for (i = descr->index_size;
+ descr->entries[i].syntax != SNMP_SYNTAX_NULL; i++)
+ if (descr->entries[i].subid ==
+ b->var.subs[descr->table.len + 1])
+ break;
+ if (descr->entries[i].syntax == SNMP_SYNTAX_NULL)
+ return (0);
+
+ /* check syntax */
+ if (b->syntax != descr->entries[i].syntax) {
+ seterr("bad syntax (%u instead of %u)", b->syntax,
+ descr->entries[i].syntax);
+ return (-1);
+ }
+
+ switch (b->syntax) {
+
+ case SNMP_SYNTAX_INTEGER:
+ *(int32_t *)(void *)((u_char *)e + descr->entries[i].offset) =
+ b->v.integer;
+ break;
+
+ case SNMP_SYNTAX_OCTETSTRING:
+ if ((ptr = malloc(b->v.octetstring.len + 1)) == NULL) {
+ seterr("no memory for string");
+ return (-1);
+ }
+ memcpy(ptr, b->v.octetstring.octets, b->v.octetstring.len);
+ ptr[b->v.octetstring.len] = '\0';
+ *(u_char **)(void *)((u_char *)e + descr->entries[i].offset) =
+ ptr;
+ *(size_t *)(void *)((u_char *)e + descr->entries[i].offset +
+ sizeof(u_char *)) = b->v.octetstring.len;
+ break;
+
+ case SNMP_SYNTAX_OID:
+ *(struct asn_oid *)(void *)((u_char *)e + descr->entries[i].offset) =
+ b->v.oid;
+ break;
+
+ case SNMP_SYNTAX_IPADDRESS:
+ memcpy((u_char *)e + descr->entries[i].offset,
+ b->v.ipaddress, 4);
+ break;
+
+ case SNMP_SYNTAX_COUNTER:
+ case SNMP_SYNTAX_GAUGE:
+ case SNMP_SYNTAX_TIMETICKS:
+ *(u_int32_t *)(void *)((u_char *)e + descr->entries[i].offset) =
+ b->v.uint32;
+ break;
+
+ case SNMP_SYNTAX_COUNTER64:
+ *(u_int64_t *)(void *)((u_char *)e + descr->entries[i].offset) =
+ b->v.counter64;
+ break;
+
+ case SNMP_SYNTAX_NULL:
+ case SNMP_SYNTAX_NOSUCHOBJECT:
+ case SNMP_SYNTAX_NOSUCHINSTANCE:
+ case SNMP_SYNTAX_ENDOFMIBVIEW:
+ abort();
+ }
+ e->found |= (u_int64_t)1 << i;
+
+ return (0);
+}
+
+/*
+ * Initialize the first PDU to send
+ */
+static void
+table_init_pdu(const struct snmp_table *descr, struct snmp_pdu *pdu)
+{
+ if (snmp_client.version == SNMP_V1)
+ snmp_pdu_create(pdu, SNMP_PDU_GETNEXT);
+ else {
+ snmp_pdu_create(pdu, SNMP_PDU_GETBULK);
+ pdu->error_index = 10;
+ }
+ if (descr->last_change.len != 0) {
+ pdu->bindings[pdu->nbindings].syntax = SNMP_SYNTAX_NULL;
+ pdu->bindings[pdu->nbindings].var = descr->last_change;
+ pdu->nbindings++;
+ if (pdu->version != SNMP_V1)
+ pdu->error_status++;
+ }
+ pdu->bindings[pdu->nbindings].var = descr->table;
+ pdu->bindings[pdu->nbindings].syntax = SNMP_SYNTAX_NULL;
+ pdu->nbindings++;
+}
+
+/*
+ * Return code:
+ * 0 - End Of Table
+ * -1 - Error
+ * -2 - Last change changed - again
+ * +1 - ok, continue
+ */
+static int
+table_check_response(struct tabwork *work, const struct snmp_pdu *resp)
+{
+ const struct snmp_value *b;
+ struct entry *e;
+
+ if (resp->error_status != SNMP_ERR_NOERROR) {
+ if (snmp_client.version == SNMP_V1 &&
+ resp->error_status == SNMP_ERR_NOSUCHNAME &&
+ resp->error_index ==
+ (work->descr->last_change.len == 0) ? 1 : 2)
+ /* EOT */
+ return (0);
+ /* Error */
+ seterr("error fetching table: status=%d index=%d",
+ resp->error_status, resp->error_index);
+ return (-1);
+ }
+
+ for (b = resp->bindings; b < resp->bindings + resp->nbindings; b++) {
+ if (work->descr->last_change.len != 0 && b == resp->bindings) {
+ if (!asn_is_suboid(&work->descr->last_change, &b->var) ||
+ b->var.len != work->descr->last_change.len + 1 ||
+ b->var.subs[work->descr->last_change.len] != 0) {
+ seterr("last_change: bad response");
+ return (-1);
+ }
+ if (b->syntax != SNMP_SYNTAX_TIMETICKS) {
+ seterr("last_change: bad syntax %u", b->syntax);
+ return (-1);
+ }
+ if (work->first) {
+ work->last_change = b->v.uint32;
+ work->first = 0;
+
+ } else if (work->last_change != b->v.uint32) {
+ if (++work->iter >= work->descr->max_iter) {
+ seterr("max iteration count exceeded");
+ return (-1);
+ }
+ table_free(work, 1);
+ return (-2);
+ }
+
+ continue;
+ }
+ if (!asn_is_suboid(&work->descr->table, &b->var) ||
+ b->syntax == SNMP_SYNTAX_ENDOFMIBVIEW)
+ return (0);
+
+ if ((e = table_find(work, &b->var)) == NULL)
+ return (-1);
+ if (table_value(work->descr, e, b))
+ return (-1);
+ }
+ return (+1);
+}
+
+/*
+ * Check table consistency
+ */
+static int
+table_check_cons(struct tabwork *work)
+{
+ struct entry *e;
+
+ TAILQ_FOREACH(e, work->table, link)
+ if ((e->found & work->descr->req_mask) !=
+ work->descr->req_mask) {
+ if (work->descr->last_change.len == 0) {
+ if (++work->iter >= work->descr->max_iter) {
+ seterr("max iteration count exceeded");
+ return (-1);
+ }
+ return (-2);
+ }
+ seterr("inconsistency detected %llx %llx",
+ e->found, work->descr->req_mask);
+ return (-1);
+ }
+ return (0);
+}
+
+/*
+ * Fetch a table. Returns 0 if ok, -1 on errors.
+ * This is the synchronuous variant.
+ */
+int
+snmp_table_fetch(const struct snmp_table *descr, void *list)
+{
+ struct snmp_pdu resp;
+ struct tabwork work;
+ int ret;
+
+ work.descr = descr;
+ work.table = (struct table *)list;
+ work.iter = 0;
+ TAILQ_INIT(work.table);
+ TAILQ_INIT(&work.worklist);
+ work.callback = NULL;
+ work.arg = NULL;
+
+ again:
+ /*
+ * We come to this label when the code detects that the table
+ * has changed while fetching it.
+ */
+ work.first = 1;
+ work.last_change = 0;
+ table_init_pdu(descr, &work.pdu);
+
+ for (;;) {
+ if (snmp_dialog(&work.pdu, &resp)) {
+ seterr("SNMP error: no response");
+ table_free(&work, 1);
+ return (-1);
+ }
+ if ((ret = table_check_response(&work, &resp)) == 0) {
+ snmp_pdu_free(&resp);
+ break;
+ }
+ if (ret == -1) {
+ snmp_pdu_free(&resp);
+ table_free(&work, 1);
+ return (-1);
+ }
+ if (ret == -2) {
+ snmp_pdu_free(&resp);
+ goto again;
+ }
+
+ work.pdu.bindings[work.pdu.nbindings - 1].var =
+ resp.bindings[resp.nbindings - 1].var;
+
+ snmp_pdu_free(&resp);
+ }
+
+ if ((ret = table_check_cons(&work)) == -1) {
+ table_free(&work, 1);
+ return (-1);
+ }
+ if (ret == -2) {
+ table_free(&work, 1);
+ goto again;
+ }
+ /*
+ * Free index list
+ */
+ table_free(&work, 0);
+ return (0);
+}
+
+/*
+ * Callback for table
+ */
+static void
+table_cb(struct snmp_pdu *req __unused, struct snmp_pdu *resp, void *arg)
+{
+ struct tabwork *work = arg;
+ int ret;
+
+ if (resp == NULL) {
+ /* timeout */
+ seterr("no response to fetch table request");
+ table_free(work, 1);
+ work->callback(work->table, work->arg, -1);
+ free(work);
+ return;
+ }
+
+ if ((ret = table_check_response(work, resp)) == 0) {
+ /* EOT */
+ snmp_pdu_free(resp);
+
+ if ((ret = table_check_cons(work)) == -1) {
+ /* error happend */
+ table_free(work, 1);
+ work->callback(work->table, work->arg, -1);
+ free(work);
+ return;
+ }
+ if (ret == -2) {
+ /* restart */
+ again:
+ table_free(work, 1);
+ work->first = 1;
+ work->last_change = 0;
+ table_init_pdu(work->descr, &work->pdu);
+ if (snmp_pdu_send(&work->pdu, table_cb, work) == -1) {
+ work->callback(work->table, work->arg, -1);
+ free(work);
+ return;
+ }
+ return;
+ }
+ /*
+ * Free index list
+ */
+ table_free(work, 0);
+ work->callback(work->table, work->arg, 0);
+ free(work);
+ return;
+ }
+
+ if (ret == -1) {
+ /* error */
+ snmp_pdu_free(resp);
+ table_free(work, 1);
+ work->callback(work->table, work->arg, -1);
+ free(work);
+ return;
+ }
+
+ if (ret == -2) {
+ /* again */
+ snmp_pdu_free(resp);
+ goto again;
+ }
+
+ /* next part */
+
+ work->pdu.bindings[work->pdu.nbindings - 1].var =
+ resp->bindings[resp->nbindings - 1].var;
+
+ snmp_pdu_free(resp);
+
+ if (snmp_pdu_send(&work->pdu, table_cb, work) == -1) {
+ table_free(work, 1);
+ work->callback(work->table, work->arg, -1);
+ free(work);
+ return;
+ }
+}
+
+int
+snmp_table_fetch_async(const struct snmp_table *descr, void *list,
+ snmp_table_cb_f func, void *arg)
+{
+ struct tabwork *work;
+
+ if ((work = malloc(sizeof(*work))) == NULL) {
+ seterr("%s", strerror(errno));
+ return (-1);
+ }
+
+ work->descr = descr;
+ work->table = (struct table *)list;
+ work->iter = 0;
+ TAILQ_INIT(work->table);
+ TAILQ_INIT(&work->worklist);
+
+ work->callback = func;
+ work->arg = arg;
+
+ /*
+ * Start by sending the first PDU
+ */
+ work->first = 1;
+ work->last_change = 0;
+ table_init_pdu(descr, &work->pdu);
+
+ if (snmp_pdu_send(&work->pdu, table_cb, work) == -1)
+ return (-1);
+ return (0);
+}
+
+/*
+ * Append an index to an oid
+ */
+int
+snmp_oid_append(struct asn_oid *oid, const char *fmt, ...)
+{
+ va_list va;
+ int size;
+ char *nextptr;
+ const u_char *str;
+ size_t len;
+ struct in_addr ina;
+ int ret;
+
+ va_start(va, fmt);
+
+ size = 0;
+
+ ret = 0;
+ while (*fmt != '\0') {
+ switch (*fmt++) {
+ case 'i':
+ /* just an integer more */
+ if (oid->len + 1 > ASN_MAXOIDLEN) {
+ warnx("%s: OID too long for integer", __func__);
+ ret = -1;
+ break;
+ }
+ oid->subs[oid->len++] = va_arg(va, asn_subid_t);
+ break;
+
+ case 'a':
+ /* append an IP address */
+ if (oid->len + 4 > ASN_MAXOIDLEN) {
+ warnx("%s: OID too long for ip-addr", __func__);
+ ret = -1;
+ break;
+ }
+ ina = va_arg(va, struct in_addr);
+ ina.s_addr = ntohl(ina.s_addr);
+ oid->subs[oid->len++] = (ina.s_addr >> 24) & 0xff;
+ oid->subs[oid->len++] = (ina.s_addr >> 16) & 0xff;
+ oid->subs[oid->len++] = (ina.s_addr >> 8) & 0xff;
+ oid->subs[oid->len++] = (ina.s_addr >> 0) & 0xff;
+ break;
+
+ case 's':
+ /* append a null-terminated string,
+ * length is computed */
+ str = (const u_char *)va_arg(va, const char *);
+ len = strlen((const char *)str);
+ if (oid->len + len + 1 > ASN_MAXOIDLEN) {
+ warnx("%s: OID too long for string", __func__);
+ ret = -1;
+ break;
+ }
+ oid->subs[oid->len++] = len;
+ while (len--)
+ oid->subs[oid->len++] = *str++;
+ break;
+
+ case '(':
+ /* the integer value between ( and ) is stored
+ * in size */
+ size = strtol(fmt, &nextptr, 10);
+ if (*nextptr != ')')
+ abort();
+ fmt = ++nextptr;
+ break;
+
+ case 'b':
+ /* append `size` characters */
+ str = (const u_char *)va_arg(va, const char *);
+ if (oid->len + size > ASN_MAXOIDLEN) {
+ warnx("%s: OID too long for string", __func__);
+ ret = -1;
+ break;
+ }
+ while (size--)
+ oid->subs[oid->len++] = *str++;
+ break;
+
+ case 'c':
+ /* get size and the octets from the arguments */
+ size = va_arg(va, size_t);
+ str = va_arg(va, const u_char *);
+ if (oid->len + size + 1 > ASN_MAXOIDLEN) {
+ warnx("%s: OID too long for string", __func__);
+ ret = -1;
+ break;
+ }
+ oid->subs[oid->len++] = size;
+ while (size--)
+ oid->subs[oid->len++] = *str++;
+ break;
+
+ default:
+ abort();
+ }
+ }
+ va_end(va);
+ return (ret);
+}
+
+/*
+ * Initialize a client structure
+ */
+void
+snmp_client_init(struct snmp_client *c)
+{
+ memset(c, 0, sizeof(*c));
+
+ c->version = SNMP_V2c;
+ c->local = 0;
+ c->chost = NULL;
+ c->cport = NULL;
+
+ strcpy(c->read_community, "public");
+ strcpy(c->write_community, "private");
+
+ c->timeout.tv_sec = 3;
+ c->timeout.tv_usec = 0;
+ c->retries = 3;
+ c->dump_pdus = 0;
+ c->txbuflen = c->rxbuflen = 10000;
+
+ c->fd = -1;
+
+ c->max_reqid = INT32_MAX;
+ c->min_reqid = 0;
+ c->next_reqid = 0;
+}
+
+
+/*
+ * Open UDP client socket
+ */
+static int
+open_client_udp(const char *host, const char *port)
+{
+ int error;
+ char *ptr;
+ struct addrinfo hints, *res0, *res;
+
+ /* copy host- and portname */
+ if (snmp_client.chost == NULL) {
+ if ((snmp_client.chost = malloc(1 + sizeof(DEFAULT_HOST)))
+ == NULL) {
+ seterr("%s", strerror(errno));
+ return (-1);
+ }
+ strcpy(snmp_client.chost, DEFAULT_HOST);
+ }
+ if (host != NULL) {
+ if ((ptr = malloc(1 + strlen(host))) == NULL) {
+ seterr("%s", strerror(errno));
+ return (-1);
+ }
+ free(snmp_client.chost);
+ snmp_client.chost = ptr;
+ strcpy(snmp_client.chost, host);
+ }
+ if (snmp_client.cport == NULL) {
+ if ((snmp_client.cport = malloc(1 + sizeof(DEFAULT_PORT)))
+ == NULL) {
+ seterr("%s", strerror(errno));
+ return (-1);
+ }
+ strcpy(snmp_client.cport, DEFAULT_PORT);
+ }
+ if (port != NULL) {
+ if ((ptr = malloc(1 + strlen(port))) == NULL) {
+ seterr("%s", strerror(errno));
+ return (-1);
+ }
+ free(snmp_client.cport);
+ snmp_client.cport = ptr;
+ strcpy(snmp_client.cport, port);
+ }
+
+ /* open connection */
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_flags = AI_CANONNAME;
+ hints.ai_family = AF_INET;
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_protocol = 0;
+ error = getaddrinfo(snmp_client.chost, snmp_client.cport, &hints, &res0);
+ if (error != 0) {
+ seterr("%s: %s", snmp_client.chost, gai_strerror(error));
+ return (-1);
+ }
+ res = res0;
+ for (;;) {
+ if ((snmp_client.fd = socket(res->ai_family, res->ai_socktype,
+ res->ai_protocol)) == -1) {
+ if ((res = res->ai_next) == NULL) {
+ seterr("%s", strerror(errno));
+ freeaddrinfo(res0);
+ return (-1);
+ }
+ } else if (connect(snmp_client.fd, res->ai_addr,
+ res->ai_addrlen) == -1) {
+ if ((res = res->ai_next) == NULL) {
+ seterr("%s", strerror(errno));
+ freeaddrinfo(res0);
+ return (-1);
+ }
+ } else
+ break;
+ }
+ freeaddrinfo(res0);
+ return (0);
+}
+
+static void
+remove_local(void)
+{
+ (void)remove(snmp_client.local_path);
+}
+
+/*
+ * Open local socket
+ */
+static int
+open_client_local(const char *path)
+{
+ struct sockaddr_un sa;
+ char *ptr;
+
+ if (snmp_client.chost == NULL) {
+ if ((snmp_client.chost = malloc(1 + sizeof(DEFAULT_LOCAL)))
+ == NULL) {
+ seterr("%s", strerror(errno));
+ return (-1);
+ }
+ strcpy(snmp_client.chost, DEFAULT_LOCAL);
+ }
+ if (path != NULL) {
+ if ((ptr = malloc(1 + strlen(path))) == NULL) {
+ seterr("%s", strerror(errno));
+ return (-1);
+ }
+ free(snmp_client.chost);
+ snmp_client.chost = ptr;
+ strcpy(snmp_client.chost, path);
+ }
+
+ if ((snmp_client.fd = socket(PF_LOCAL, SOCK_DGRAM, 0)) == -1) {
+ seterr("%s", strerror(errno));
+ return (-1);
+ }
+
+ snprintf(snmp_client.local_path, sizeof(snmp_client.local_path),
+ "%s", SNMP_LOCAL_PATH);
+
+ if (mktemp(snmp_client.local_path) == NULL) {
+ seterr("%s", strerror(errno));
+ (void)close(snmp_client.fd);
+ snmp_client.fd = -1;
+ return (-1);
+ }
+
+ sa.sun_family = AF_LOCAL;
+ sa.sun_len = sizeof(sa);
+ strcpy(sa.sun_path, snmp_client.local_path);
+
+ if (bind(snmp_client.fd, (struct sockaddr *)&sa, sizeof(sa)) == -1) {
+ seterr("%s", strerror(errno));
+ (void)close(snmp_client.fd);
+ snmp_client.fd = -1;
+ (void)remove(snmp_client.local_path);
+ return (-1);
+ }
+ atexit(remove_local);
+
+ sa.sun_family = AF_LOCAL;
+ sa.sun_len = offsetof(struct sockaddr_un, sun_path) +
+ strlen(snmp_client.chost);
+ strncpy(sa.sun_path, snmp_client.chost, sizeof(sa.sun_path) - 1);
+ sa.sun_path[sizeof(sa.sun_path) - 1] = '\0';
+
+ if (connect(snmp_client.fd, (struct sockaddr *)&sa, sa.sun_len) == -1) {
+ seterr("%s", strerror(errno));
+ (void)close(snmp_client.fd);
+ snmp_client.fd = -1;
+ (void)remove(snmp_client.local_path);
+ return (-1);
+ }
+ return (0);
+}
+
+/*
+ * SNMP_OPEN
+ */
+int
+snmp_open(const char *host, const char *port, const char *readcomm,
+ const char *writecomm)
+{
+ struct timeval tout;
+
+ /* still open ? */
+ if (snmp_client.fd != -1) {
+ errno = EBUSY;
+ seterr("%s", strerror(errno));
+ return (-1);
+ }
+
+ /* copy community strings */
+ if (readcomm != NULL)
+ strlcpy(snmp_client.read_community, readcomm,
+ sizeof(snmp_client.read_community));
+ if (writecomm != NULL)
+ strlcpy(snmp_client.write_community, writecomm,
+ sizeof(snmp_client.write_community));
+
+ if (!snmp_client.local) {
+ if (open_client_udp(host, port))
+ return (-1);
+ } else {
+ if (open_client_local(host))
+ return (-1);
+ }
+ tout.tv_sec = 0;
+ tout.tv_usec = 0;
+ if (setsockopt(snmp_client.fd, SOL_SOCKET, SO_SNDTIMEO,
+ &tout, sizeof(struct timeval)) == -1) {
+ seterr("%s", strerror(errno));
+ (void)close(snmp_client.fd);
+ snmp_client.fd = -1;
+ if (snmp_client.local)
+ (void)remove(snmp_client.local_path);
+ return (-1);
+ }
+
+ /* initialize list */
+ LIST_INIT(&sent_pdus);
+
+ return (0);
+}
+
+
+/*
+ * SNMP_CLOSE
+ *
+ * closes connection to snmp server
+ * - function cannot fail
+ * - clears connection
+ * - clears list of sent pdus
+ *
+ * input:
+ * void
+ * return:
+ * void
+ */
+void
+snmp_close(void)
+{
+ struct sent_pdu *p1;
+
+ if (snmp_client.fd != -1) {
+ (void)close(snmp_client.fd);
+ snmp_client.fd = -1;
+ if (snmp_client.local)
+ (void)remove(snmp_client.local_path);
+ }
+ while(!LIST_EMPTY(&sent_pdus)){
+ p1 = LIST_FIRST(&sent_pdus);
+ if (p1->timeout_id != NULL)
+ snmp_client.timeout_stop(p1->timeout_id);
+ LIST_REMOVE(p1, entries);
+ free(p1);
+ }
+ free(snmp_client.chost);
+ free(snmp_client.cport);
+}
+
+/*
+ * initialize a snmp_pdu structure
+ */
+void
+snmp_pdu_create(struct snmp_pdu *pdu, u_int op)
+{
+ memset(pdu,0,sizeof(struct snmp_pdu));
+ if (op == SNMP_PDU_SET)
+ strlcpy(pdu->community, snmp_client.write_community,
+ sizeof(pdu->community));
+ else
+ strlcpy(pdu->community, snmp_client.read_community,
+ sizeof(pdu->community));
+
+ pdu->type = op;
+ pdu->version = snmp_client.version;
+ pdu->error_status = 0;
+ pdu->error_index = 0;
+ pdu->nbindings = 0;
+}
+
+/* add pairs of (struct asn_oid, enum snmp_syntax) to an existing pdu */
+/* added 10/04/02 by kek: check for MAX_BINDINGS */
+int
+snmp_add_binding(struct snmp_v1_pdu *pdu, ...)
+{
+ va_list ap;
+ const struct asn_oid *oid;
+ u_int ret;
+
+ va_start(ap, pdu);
+
+ ret = pdu->nbindings;
+ while ((oid = va_arg(ap, const struct asn_oid *)) != NULL) {
+ if (pdu->nbindings >= SNMP_MAX_BINDINGS){
+ va_end(ap);
+ return (-1);
+ }
+ pdu->bindings[pdu->nbindings].var = *oid;
+ pdu->bindings[pdu->nbindings].syntax =
+ va_arg(ap, enum snmp_syntax);
+ pdu->nbindings++;
+ }
+ va_end(ap);
+ return (ret);
+}
+
+
+static int32_t
+snmp_next_reqid(struct snmp_client * c)
+{
+ int32_t i;
+
+ i = c->next_reqid;
+ if (c->next_reqid >= c->max_reqid)
+ c->next_reqid = c->min_reqid;
+ else
+ c->next_reqid++;
+ return (i);
+}
+
+/*
+ * Send request and return request id.
+ */
+static int32_t
+snmp_send_packet(struct snmp_pdu * pdu)
+{
+ u_char *buf;
+ struct asn_buf b;
+ ssize_t ret;
+
+ if ((buf = malloc(snmp_client.txbuflen)) == NULL) {
+ seterr("%s", strerror(errno));
+ return (-1);
+ }
+
+ pdu->request_id = snmp_next_reqid(&snmp_client);
+
+ b.asn_ptr = buf;
+ b.asn_len = snmp_client.txbuflen;
+ if (snmp_pdu_encode(pdu, &b)) {
+ seterr("%s", strerror(errno));
+ free(buf);
+ return (-1);
+ }
+
+ if (snmp_client.dump_pdus)
+ snmp_pdu_dump(pdu);
+
+ if ((ret = send(snmp_client.fd, buf, b.asn_ptr - buf, 0)) == -1) {
+ seterr("%s", strerror(errno));
+ free(buf);
+ return (-1);
+ }
+ free(buf);
+
+ return pdu->request_id;
+}
+
+/*
+ * to be called when a snmp request timed out
+ */
+static void
+snmp_timeout(void * listentry_ptr)
+{
+ struct sent_pdu *listentry = listentry_ptr;
+
+#if 0
+ warnx("snmp request %i timed out, attempt (%i/%i)",
+ listentry->reqid, listentry->retrycount, snmp_client.retries);
+#endif
+
+ listentry->retrycount++;
+ if (listentry->retrycount > snmp_client.retries) {
+ /* there is no answer at all */
+ LIST_REMOVE(listentry, entries);
+ listentry->callback(listentry->pdu, NULL, listentry->arg);
+ free(listentry);
+ } else {
+ /* try again */
+ /* new request with new request ID */
+ listentry->reqid = snmp_send_packet(listentry->pdu);
+ listentry->timeout_id =
+ snmp_client.timeout_start(&snmp_client.timeout,
+ snmp_timeout, listentry);
+ }
+}
+
+int32_t
+snmp_pdu_send(struct snmp_pdu *pdu, snmp_send_cb_f func, void *arg)
+{
+ struct sent_pdu *listentry;
+ int32_t id;
+
+ if ((listentry = malloc(sizeof(struct sent_pdu))) == NULL) {
+ seterr("%s", strerror(errno));
+ return (-1);
+ }
+
+ /* here we really send */
+ if ((id = snmp_send_packet(pdu)) == -1) {
+ free(listentry);
+ return (-1);
+ }
+
+ /* add entry to list of sent PDUs */
+ listentry->pdu = pdu;
+ if (gettimeofday(&listentry->time, NULL) == -1)
+ warn("gettimeofday() failed");
+
+ listentry->reqid = pdu->request_id;
+ listentry->callback = func;
+ listentry->arg = arg;
+ listentry->retrycount=1;
+ listentry->timeout_id =
+ snmp_client.timeout_start(&snmp_client.timeout, snmp_timeout,
+ listentry);
+
+ LIST_INSERT_HEAD(&sent_pdus, listentry, entries);
+
+ return (id);
+}
+
+/*
+ * Receive an SNMP packet.
+ *
+ * tv controls how we wait for a packet: if tv is a NULL pointer,
+ * the receive blocks forever, if tv points to a structure with all
+ * members 0 the socket is polled, in all other cases tv specifies the
+ * maximum time to wait for a packet.
+ *
+ * Return:
+ * -1 on errors
+ * 0 on timeout
+ * +1 if packet received
+ */
+static int
+snmp_receive_packet(struct snmp_pdu *pdu, struct timeval *tv)
+{
+ int dopoll, setpoll;
+ int flags;
+ int saved_errno;
+ u_char *buf;
+ int ret;
+ struct asn_buf abuf;
+ int32_t ip;
+ socklen_t optlen;
+
+ if ((buf = malloc(snmp_client.rxbuflen)) == NULL) {
+ seterr("%s", strerror(errno));
+ return (-1);
+ }
+ dopoll = setpoll = 0;
+ flags = 0;
+ if (tv != NULL) {
+ /* poll or timeout */
+ if (tv->tv_sec != 0 || tv->tv_usec != 0) {
+ /* wait with timeout */
+ if (setsockopt(snmp_client.fd, SOL_SOCKET, SO_RCVTIMEO,
+ tv, sizeof(*tv)) == -1) {
+ seterr("setsockopt: %s", strerror(errno));
+ free(buf);
+ return (-1);
+ }
+ optlen = sizeof(*tv);
+ if (getsockopt(snmp_client.fd, SOL_SOCKET, SO_RCVTIMEO,
+ tv, &optlen) == -1) {
+ seterr("getsockopt: %s", strerror(errno));
+ free(buf);
+ return (-1);
+ }
+ /* at this point tv_sec and tv_usec may appear
+ * as 0. This happens for timeouts lesser than
+ * the clock granularity. The kernel rounds these to
+ * 0 and this would result in a blocking receive.
+ * Instead of an else we check tv_sec and tv_usec
+ * again below and if this rounding happens,
+ * switch to a polling receive. */
+ }
+ if (tv->tv_sec == 0 && tv->tv_usec == 0) {
+ /* poll */
+ dopoll = 1;
+ if ((flags = fcntl(snmp_client.fd, F_GETFL, 0)) == -1) {
+ seterr("fcntl: %s", strerror(errno));
+ free(buf);
+ return (-1);
+ }
+ if (!(flags & O_NONBLOCK)) {
+ setpoll = 1;
+ flags |= O_NONBLOCK;
+ if (fcntl(snmp_client.fd, F_SETFL, flags) == -1) {
+ seterr("fcntl: %s", strerror(errno));
+ free(buf);
+ return (-1);
+ }
+ }
+ }
+ }
+ ret = recv(snmp_client.fd, buf, snmp_client.rxbuflen, 0);
+ saved_errno = errno;
+ if (tv != NULL) {
+ if (dopoll) {
+ if (setpoll) {
+ flags &= ~O_NONBLOCK;
+ (void)fcntl(snmp_client.fd, F_SETFL, flags);
+ }
+ } else {
+ tv->tv_sec = 0;
+ tv->tv_usec = 0;
+ (void)setsockopt(snmp_client.fd, SOL_SOCKET, SO_RCVTIMEO,
+ tv, sizeof(*tv));
+ }
+ }
+ if (ret == -1) {
+ free(buf);
+ if (errno == EAGAIN || errno == EWOULDBLOCK)
+ return (0);
+ seterr("recv: %s", strerror(saved_errno));
+ return (-1);
+ }
+ if (ret == 0)
+ abort();
+
+ abuf.asn_ptr = buf;
+ abuf.asn_len = ret;
+
+ if (SNMP_CODE_OK != (ret = snmp_pdu_decode(&abuf, pdu, &ip))) {
+ seterr("snmp_decode_pdu: failed %d", ret);
+ free(buf);
+ return (-1);
+ }
+ free(buf);
+ if (snmp_client.dump_pdus)
+ snmp_pdu_dump(pdu);
+
+ return (+1);
+}
+
+static int
+snmp_deliver_packet(struct snmp_pdu * resp)
+{
+ struct sent_pdu *listentry;
+
+ if (resp->type != SNMP_PDU_RESPONSE) {
+ warn("ignoring snmp pdu %u", resp->type);
+ return (-1);
+ }
+
+ LIST_FOREACH(listentry, &sent_pdus, entries)
+ if (listentry->reqid == resp->request_id)
+ break;
+ if (listentry == NULL)
+ return (-1);
+
+ LIST_REMOVE(listentry, entries);
+ listentry->callback(listentry->pdu, resp, listentry->arg);
+
+ snmp_client.timeout_stop(listentry->timeout_id);
+
+ free(listentry);
+ return (0);
+}
+
+int
+snmp_receive(int blocking)
+{
+ int ret;
+
+ struct timeval tv;
+ struct snmp_pdu * resp;
+
+ memset(&tv, 0, sizeof(tv));
+
+ resp = malloc(sizeof(struct snmp_pdu));
+ if (resp == NULL) {
+ seterr("no memory for returning PDU");
+ return (-1) ;
+ }
+
+ if ((ret = snmp_receive_packet(resp, blocking ? NULL : &tv)) <= 0) {
+ free(resp);
+ return (ret);
+ }
+ ret = snmp_deliver_packet(resp);
+ snmp_pdu_free(resp);
+ free(resp);
+ return (ret);
+}
+
+
+/*
+ * Check a GETNEXT response. Here we have three possible outcomes: -1 an
+ * unexpected error happened. +1 response is ok and is within the table 0
+ * response is ok, but is behind the table or error is NOSUCHNAME. The req
+ * should point to a template PDU which contains the base OIDs and the
+ * syntaxes. This is really only useful to sweep non-sparse tables.
+ */
+static int
+ok_getnext(const struct snmp_pdu * req, const struct snmp_pdu * resp)
+{
+ u_int i;
+
+ if (resp->version != req->version) {
+ warnx("SNMP GETNEXT: response has wrong version");
+ return (-1);
+ }
+
+ if (resp->error_status == SNMP_ERR_NOSUCHNAME)
+ return (0);
+
+ if (resp->error_status != SNMP_ERR_NOERROR) {
+ warnx("SNMP GETNEXT: error %d", resp->error_status);
+ return (-1);
+ }
+ if (resp->nbindings != req->nbindings) {
+ warnx("SNMP GETNEXT: bad number of bindings in response");
+ return (-1);
+ }
+ for (i = 0; i < req->nbindings; i++) {
+ if (!asn_is_suboid(&req->bindings[i].var,
+ &resp->bindings[i].var)) {
+ if (i != 0)
+ warnx("SNMP GETNEXT: inconsistent table "
+ "response");
+ return (0);
+ }
+ if (resp->version != SNMP_V1 &&
+ resp->bindings[i].syntax == SNMP_SYNTAX_ENDOFMIBVIEW)
+ return (0);
+
+ if (resp->bindings[i].syntax != req->bindings[i].syntax) {
+ warnx("SNMP GETNEXT: bad syntax in response");
+ return (0);
+ }
+ }
+ return (1);
+}
+
+/*
+ * Check a GET response. Here we have three possible outcomes: -1 an
+ * unexpected error happened. +1 response is ok. 0 NOSUCHNAME The req should
+ * point to a template PDU which contains the OIDs and the syntaxes. This
+ * is only useful for SNMPv1 or single object GETS.
+ */
+static int
+ok_get(const struct snmp_pdu * req, const struct snmp_pdu * resp)
+{
+ u_int i;
+
+ if (resp->version != req->version) {
+ warnx("SNMP GET: response has wrong version");
+ return (-1);
+ }
+
+ if (resp->error_status == SNMP_ERR_NOSUCHNAME)
+ return (0);
+
+ if (resp->error_status != SNMP_ERR_NOERROR) {
+ warnx("SNMP GET: error %d", resp->error_status);
+ return (-1);
+ }
+
+ if (resp->nbindings != req->nbindings) {
+ warnx("SNMP GET: bad number of bindings in response");
+ return (-1);
+ }
+ for (i = 0; i < req->nbindings; i++) {
+ if (asn_compare_oid(&req->bindings[i].var,
+ &resp->bindings[i].var) != 0) {
+ warnx("SNMP GET: bad OID in response");
+ return (-1);
+ }
+ if (snmp_client.version != SNMP_V1 &&
+ (resp->bindings[i].syntax == SNMP_SYNTAX_NOSUCHOBJECT ||
+ resp->bindings[i].syntax == SNMP_SYNTAX_NOSUCHINSTANCE))
+ return (0);
+ if (resp->bindings[i].syntax != req->bindings[i].syntax) {
+ warnx("SNMP GET: bad syntax in response");
+ return (-1);
+ }
+ }
+ return (1);
+}
+
+/*
+ * Check the reponse to a SET PDU. We check: - the error status must be 0 -
+ * the number of bindings must be equal in response and request - the
+ * syntaxes must be the same in response and request - the OIDs must be the
+ * same in response and request
+ */
+static int
+ok_set(const struct snmp_pdu * req, const struct snmp_pdu * resp)
+{
+ u_int i;
+
+ if (resp->version != req->version) {
+ warnx("SNMP SET: response has wrong version");
+ return (-1);
+ }
+
+ if (resp->error_status == SNMP_ERR_NOSUCHNAME) {
+ warnx("SNMP SET: error %d", resp->error_status);
+ return (0);
+ }
+ if (resp->error_status != SNMP_ERR_NOERROR) {
+ warnx("SNMP SET: error %d", resp->error_status);
+ return (-1);
+ }
+
+ if (resp->nbindings != req->nbindings) {
+ warnx("SNMP SET: bad number of bindings in response");
+ return (-1);
+ }
+ for (i = 0; i < req->nbindings; i++) {
+ if (asn_compare_oid(&req->bindings[i].var,
+ &resp->bindings[i].var) != 0) {
+ warnx("SNMP SET: wrong OID in response to SET");
+ return (-1);
+ }
+ if (resp->bindings[i].syntax != req->bindings[i].syntax) {
+ warnx("SNMP SET: bad syntax in response");
+ return (-1);
+ }
+ }
+ return (1);
+}
+
+/*
+ * Simple checks for response PDUs against request PDUs. Return values: 1=ok,
+ * 0=nosuchname or similar, -1=failure, -2=no response at all
+ */
+int
+snmp_pdu_check(const struct snmp_pdu *req,
+ const struct snmp_pdu *resp)
+{
+ if (resp == NULL)
+ return (-2);
+
+ switch (req->type) {
+
+ case SNMP_PDU_GET:
+ return (ok_get(req, resp));
+
+ case SNMP_PDU_SET:
+ return (ok_set(req, resp));
+
+ case SNMP_PDU_GETNEXT:
+ return (ok_getnext(req, resp));
+
+ }
+ errx(1, "%s: bad pdu type %i", __func__, req->type);
+}
+
+int
+snmp_dialog(struct snmp_v1_pdu *req, struct snmp_v1_pdu *resp)
+{
+ u_int i;
+ int32_t reqid;
+ int ret;
+ struct timeval tv = snmp_client.timeout;
+ struct timeval end;
+
+ for (i = 0; i <= snmp_client.retries; i++) {
+ (void)gettimeofday(&end, NULL);
+ timeradd(&end, &snmp_client.timeout, &end);
+ if ((reqid = snmp_send_packet(req)) == -1)
+ return (-1);
+ for (;;) {
+ (void)gettimeofday(&tv, NULL);
+ if (timercmp(&end, &tv, <=))
+ break;
+ timersub(&end, &tv, &tv);
+ if ((ret = snmp_receive_packet(resp, &tv)) == 0)
+ /* timeout */
+ break;
+
+ if (ret > 0) {
+ if (reqid == resp->request_id)
+ return (0);
+ /* not for us */
+ (void)snmp_deliver_packet(resp);
+ }
+ }
+ }
+ seterr("retry count exceeded");
+ return (-1);
+}
+
+int
+snmp_client_set_host(struct snmp_client *cl, const char *h)
+{
+ char *np;
+
+ if (h == NULL) {
+ if (cl->chost != NULL)
+ free(cl->chost);
+ cl->chost = NULL;
+ } else {
+ if ((np = malloc(strlen(h) + 1)) == NULL)
+ return (-1);
+ strcpy(np, h);
+ if (cl->chost != NULL)
+ free(cl->chost);
+ cl->chost = np;
+ }
+ return (0);
+}
+
+int
+snmp_client_set_port(struct snmp_client *cl, const char *p)
+{
+ char *np;
+
+ if (p == NULL) {
+ if (cl->cport != NULL)
+ free(cl->cport);
+ cl->cport = NULL;
+ } else {
+ if ((np = malloc(strlen(p) + 1)) == NULL)
+ return (-1);
+ strcpy(np, p);
+ if (cl->cport != NULL)
+ free(cl->cport);
+ cl->cport = np;
+ }
+ return (0);
+}
diff --git a/contrib/bsnmp/lib/snmpclient.h b/contrib/bsnmp/lib/snmpclient.h
new file mode 100644
index 0000000..700d020
--- /dev/null
+++ b/contrib/bsnmp/lib/snmpclient.h
@@ -0,0 +1,182 @@
+/*
+ * Copyright (c) 2001-2003
+ * Fraunhofer Institute for Open Communication Systems (FhG Fokus).
+ * All rights reserved.
+ *
+ * Author: Harti Brandt <harti@freebsd.org>
+ * Kendy Kutzner
+ *
+ * Redistribution of this software and documentation and use in source and
+ * binary forms, with or without modification, are permitted provided that
+ * the following conditions are met:
+ *
+ * 1. Redistributions of source code or documentation must retain the above
+ * copyright notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY FRAUNHOFER FOKUS
+ * AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * FRAUNHOFER FOKUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Begemot: bsnmp/lib/snmpclient.h,v 1.15 2002/12/11 15:54:07 hbb Exp $
+ */
+#ifndef _BSNMP_SNMPCLIENT_H
+#define _BSNMP_SNMPCLIENT_H
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+#include <stddef.h>
+
+
+#define SNMP_STRERROR_LEN 200
+
+#define SNMP_LOCAL_PATH "/tmp/snmpXXXXXXXXXXXXXX"
+
+
+/* type of callback function for responses
+ * this callback function is responsible for free() any memory associated with
+ * any of the PDUs. Therefor it may call snmp_pdu_free() */
+typedef void (*snmp_send_cb_f)(struct snmp_pdu *, struct snmp_pdu *, void *);
+
+/* type of callback function for timeouts */
+typedef void (*snmp_timeout_cb_f)(void * );
+
+/* timeout start function */
+typedef void *(*snmp_timeout_start_f)(struct timeval *timeout,
+ snmp_timeout_cb_f callback, void *);
+
+/* timeout stop function */
+typedef void (*snmp_timeout_stop_f)(void *timeout_id);
+
+/*
+ * Client context.
+ */
+struct snmp_client {
+ enum snmp_version version;
+ int local; /* use local socket */
+
+ /* these two are read-only for the application */
+ char *cport; /* port number as string */
+ char *chost; /* host name or IP address as string */
+
+ char read_community[SNMP_COMMUNITY_MAXLEN + 1];
+ char write_community[SNMP_COMMUNITY_MAXLEN + 1];
+
+ struct timeval timeout;
+ u_int retries;
+
+ int dump_pdus;
+
+ size_t txbuflen;
+ size_t rxbuflen;
+
+ int fd;
+
+ int32_t next_reqid;
+ int32_t max_reqid;
+ int32_t min_reqid;
+
+ char error[SNMP_STRERROR_LEN];
+
+ snmp_timeout_start_f timeout_start;
+ snmp_timeout_stop_f timeout_stop;
+
+ char local_path[sizeof(SNMP_LOCAL_PATH)];
+};
+
+/* the global context */
+extern struct snmp_client snmp_client;
+
+/* initizialies a snmp_client structure */
+void snmp_client_init(struct snmp_client *);
+
+/* initialize fields */
+int snmp_client_set_host(struct snmp_client *, const char *);
+int snmp_client_set_port(struct snmp_client *, const char *);
+
+/* open connection to snmp server (hostname or portname can be NULL) */
+int snmp_open(const char *_hostname, const char *_portname,
+ const char *_read_community, const char *_write_community);
+
+/* close connection */
+void snmp_close(void);
+
+/* initialize a snmp_pdu structure */
+void snmp_pdu_create(struct snmp_pdu *, u_int _op);
+
+/* add pairs of (struct asn_oid *, enum snmp_syntax) to an existing pdu */
+int snmp_add_binding(struct snmp_pdu *, ...);
+
+/* check wheater the answer is valid or not */
+int snmp_pdu_check(const struct snmp_pdu *_req, const struct snmp_pdu *_resp);
+
+int32_t snmp_pdu_send(struct snmp_pdu *_pdu, snmp_send_cb_f _func, void *_arg);
+
+/* append an index to an oid */
+int snmp_oid_append(struct asn_oid *_oid, const char *_fmt, ...);
+
+/* receive a packet */
+int snmp_receive(int _blocking);
+
+/*
+ * This structure is used to describe an SNMP table that is to be fetched.
+ * The C-structure that is produced by the fetch function must start with
+ * a TAILQ_ENTRY and an u_int64_t.
+ */
+struct snmp_table {
+ /* base OID of the table */
+ struct asn_oid table;
+ /* type OID of the LastChange variable for the table if any */
+ struct asn_oid last_change;
+ /* maximum number of iterations if table has changed */
+ u_int max_iter;
+ /* size of the C-structure */
+ size_t entry_size;
+ /* number of index fields */
+ u_int index_size;
+ /* bit mask of required fields */
+ u_int64_t req_mask;
+
+ /* indexes and columns to fetch. Ended by a NULL syntax entry */
+ struct snmp_table_entry {
+ /* the column sub-oid, ignored for index fields */
+ asn_subid_t subid;
+ /* the syntax of the column or index */
+ enum snmp_syntax syntax;
+ /* offset of the field into the C-structure. For octet strings
+ * this points to an u_char * followed by a size_t */
+ off_t offset;
+#if defined(__GNUC__) && __GNUC__ < 3
+ } entries[0];
+#else
+ } entries[];
+#endif
+};
+
+/* callback type for table fetch */
+typedef void (*snmp_table_cb_f)(void *_list, void *_arg, int _res);
+
+/* fetch a table. The argument points to a TAILQ_HEAD */
+int snmp_table_fetch(const struct snmp_table *descr, void *);
+int snmp_table_fetch_async(const struct snmp_table *, void *,
+ snmp_table_cb_f, void *);
+
+/* send a request and wait for the response */
+int snmp_dialog(struct snmp_pdu *_req, struct snmp_pdu *_resp);
+
+#endif /* _BSNMP_SNMPCLIENT_H */
diff --git a/contrib/bsnmp/lib/snmppriv.h b/contrib/bsnmp/lib/snmppriv.h
new file mode 100644
index 0000000..82189f0
--- /dev/null
+++ b/contrib/bsnmp/lib/snmppriv.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2001-2003
+ * Fraunhofer Institute for Open Communication Systems (FhG Fokus).
+ * All rights reserved.
+ *
+ * Author: Harti Brandt <harti@freebsd.org>
+ *
+ * Redistribution of this software and documentation and use in source and
+ * binary forms, with or without modification, are permitted provided that
+ * the following conditions are met:
+ *
+ * 1. Redistributions of source code or documentation must retain the above
+ * copyright notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY FRAUNHOFER FOKUS
+ * AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * FRAUNHOFER FOKUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Begemot: bsnmp/lib/snmppriv.h,v 1.7 2002/12/11 15:54:07 hbb Exp $
+ *
+ * Private functions.
+ */
+enum asn_err snmp_binding_encode(struct asn_buf *, const struct snmp_value *);
+enum snmp_code snmp_pdu_encode_header(struct asn_buf *, struct snmp_pdu *);
+enum snmp_code snmp_fix_encoding(struct asn_buf *, const struct snmp_pdu *);
+enum asn_err snmp_parse_message_hdr(struct asn_buf *b, struct snmp_pdu *pdu,
+ asn_len_t *lenp);
+enum asn_err snmp_parse_pdus_hdr(struct asn_buf *b, struct snmp_pdu *pdu,
+ asn_len_t *lenp);
+
+#define DEFAULT_HOST "localhost"
+#define DEFAULT_PORT "snmp"
+#define DEFAULT_LOCAL "/var/run/snmp.sock"
diff --git a/contrib/bsnmp/snmp_mibII/mibII.c b/contrib/bsnmp/snmp_mibII/mibII.c
new file mode 100644
index 0000000..7481794
--- /dev/null
+++ b/contrib/bsnmp/snmp_mibII/mibII.c
@@ -0,0 +1,1564 @@
+/*
+ * Copyright (c) 2001-2003
+ * Fraunhofer Institute for Open Communication Systems (FhG Fokus).
+ * All rights reserved.
+ *
+ * Author: Harti Brandt <harti@freebsd.org>
+ *
+ * Redistribution of this software and documentation and use in source and
+ * binary forms, with or without modification, are permitted provided that
+ * the following conditions are met:
+ *
+ * 1. Redistributions of source code or documentation must retain the above
+ * copyright notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY FRAUNHOFER FOKUS
+ * AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * FRAUNHOFER FOKUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Begemot: bsnmp/snmp_mibII/mibII.c,v 1.16 2003/01/28 13:44:34 hbb Exp $
+ *
+ * Implementation of the standard interfaces and ip MIB.
+ */
+#include "mibII.h"
+#include "mibII_oid.h"
+#include <net/if_types.h>
+
+
+/*****************************/
+
+/* our module */
+static struct lmodule *module;
+
+/* routing socket */
+static int route;
+static void *route_fd;
+
+/* if-index allocator */
+static u_int32_t next_if_index = 1;
+
+/* re-fetch arp table */
+static int update_arp;
+static int in_update_arp;
+
+/* OR registrations */
+static u_int ifmib_reg;
+static u_int ipmib_reg;
+static u_int tcpmib_reg;
+static u_int udpmib_reg;
+static u_int ipForward_reg;
+
+/*****************************/
+
+/* list of all IP addresses */
+struct mibifa_list mibifa_list = TAILQ_HEAD_INITIALIZER(mibifa_list);
+
+/* list of all interfaces */
+struct mibif_list mibif_list = TAILQ_HEAD_INITIALIZER(mibif_list);
+
+/* list of dynamic interface names */
+struct mibdynif_list mibdynif_list = SLIST_HEAD_INITIALIZER(mibdynif_list);
+
+/* list of all interface index mappings */
+struct mibindexmap_list mibindexmap_list = STAILQ_HEAD_INITIALIZER(mibindexmap_list);
+
+/* list of all stacking entries */
+struct mibifstack_list mibifstack_list = TAILQ_HEAD_INITIALIZER(mibifstack_list);
+
+/* list of all receive addresses */
+struct mibrcvaddr_list mibrcvaddr_list = TAILQ_HEAD_INITIALIZER(mibrcvaddr_list);
+
+/* list of all NetToMedia entries */
+struct mibarp_list mibarp_list = TAILQ_HEAD_INITIALIZER(mibarp_list);
+
+/* number of interfaces */
+int32_t mib_if_number;
+
+/* last change of table */
+u_int32_t mib_iftable_last_change;
+
+/* last change of stack table */
+u_int32_t mib_ifstack_last_change;
+
+/* if this is set, one of our lists may be bad. refresh them when idle */
+int mib_iflist_bad;
+
+/* network socket */
+int mib_netsock;
+
+/* last time refreshed */
+u_int32_t mibarpticks;
+
+/* info on system clocks */
+struct clockinfo clockinfo;
+
+/* list of all New if registrations */
+static struct newifreg_list newifreg_list = TAILQ_HEAD_INITIALIZER(newifreg_list);
+
+/*****************************/
+
+static const struct asn_oid oid_ifMIB = OIDX_ifMIB;
+static const struct asn_oid oid_ipMIB = OIDX_ipMIB;
+static const struct asn_oid oid_tcpMIB = OIDX_tcpMIB;
+static const struct asn_oid oid_udpMIB = OIDX_udpMIB;
+static const struct asn_oid oid_ipForward = OIDX_ipForward;
+static const struct asn_oid oid_linkDown = OIDX_linkDown;
+static const struct asn_oid oid_linkUp = OIDX_linkUp;
+static const struct asn_oid oid_ifIndex = OIDX_ifIndex;
+
+/*****************************/
+
+/*
+ * Find an interface
+ */
+struct mibif *
+mib_find_if(u_int idx)
+{
+ struct mibif *ifp;
+
+ TAILQ_FOREACH(ifp, &mibif_list, link)
+ if (ifp->index == idx)
+ return (ifp);
+ return (NULL);
+}
+
+struct mibif *
+mib_find_if_sys(u_int sysindex)
+{
+ struct mibif *ifp;
+
+ TAILQ_FOREACH(ifp, &mibif_list, link)
+ if (ifp->sysindex == sysindex)
+ return (ifp);
+ return (NULL);
+}
+
+struct mibif *
+mib_find_if_name(const char *name)
+{
+ struct mibif *ifp;
+
+ TAILQ_FOREACH(ifp, &mibif_list, link)
+ if (strcmp(ifp->name, name) == 0)
+ return (ifp);
+ return (NULL);
+}
+
+/*
+ * Check whether an interface is dynamic. The argument may include the
+ * unit number. This assumes, that the name part does NOT contain digits.
+ */
+int
+mib_if_is_dyn(const char *name)
+{
+ size_t len;
+ struct mibdynif *d;
+
+ for (len = 0; name[len] != '\0' && isalpha(name[len]) ; len++)
+ ;
+ SLIST_FOREACH(d, &mibdynif_list, link)
+ if (strlen(d->name) == len && strncmp(d->name, name, len) == 0)
+ return (1);
+ return (0);
+}
+
+/* set an interface name to dynamic mode */
+void
+mib_if_set_dyn(const char *name)
+{
+ struct mibdynif *d;
+
+ SLIST_FOREACH(d, &mibdynif_list, link)
+ if (strcmp(name, d->name) == 0)
+ return;
+ if ((d = malloc(sizeof(*d))) == NULL)
+ err(1, NULL);
+ strcpy(d->name, name);
+ SLIST_INSERT_HEAD(&mibdynif_list, d, link);
+}
+
+/*
+ * register for interface creations
+ */
+int
+mib_register_newif(int (*func)(struct mibif *), const struct lmodule *mod)
+{
+ struct newifreg *reg;
+
+ TAILQ_FOREACH(reg, &newifreg_list, link)
+ if (reg->mod == mod) {
+ reg->func = func;
+ return (0);
+ }
+ if ((reg = malloc(sizeof(*reg))) == NULL) {
+ syslog(LOG_ERR, "newifreg: %m");
+ return (-1);
+ }
+ reg->mod = mod;
+ reg->func = func;
+ TAILQ_INSERT_TAIL(&newifreg_list, reg, link);
+
+ return (0);
+}
+
+void
+mib_unregister_newif(const struct lmodule *mod)
+{
+ struct newifreg *reg;
+
+ TAILQ_FOREACH(reg, &newifreg_list, link)
+ if (reg->mod == mod) {
+ TAILQ_REMOVE(&newifreg_list, reg, link);
+ free(reg);
+ return;
+ }
+
+}
+
+struct mibif *
+mib_first_if(void)
+{
+ return (TAILQ_FIRST(&mibif_list));
+}
+struct mibif *
+mib_next_if(const struct mibif *ifp)
+{
+ return (TAILQ_NEXT(ifp, link));
+}
+
+/*
+ * Change the admin status of an interface
+ */
+int
+mib_if_admin(struct mibif *ifp, int up)
+{
+ struct ifreq ifr;
+
+ strncpy(ifr.ifr_name, ifp->name, sizeof(ifr.ifr_name));
+ if (ioctl(mib_netsock, SIOCGIFFLAGS, &ifr) == -1) {
+ syslog(LOG_ERR, "SIOCGIFFLAGS(%s): %m", ifp->name);
+ return (-1);
+ }
+ if (up)
+ ifr.ifr_flags |= IFF_UP;
+ else
+ ifr.ifr_flags &= ~IFF_UP;
+ if (ioctl(mib_netsock, SIOCSIFFLAGS, &ifr) == -1) {
+ syslog(LOG_ERR, "SIOCSIFFLAGS(%s): %m", ifp->name);
+ return (-1);
+ }
+
+ (void)mib_fetch_ifmib(ifp);
+
+ return (0);
+}
+
+/*
+ * Generate a link up/down trap
+ */
+static void
+link_trap(struct mibif *ifp, int up)
+{
+ struct snmp_value ifindex;
+
+ ifindex.var = oid_ifIndex;
+ ifindex.var.subs[ifindex.var.len++] = ifp->index;
+ ifindex.syntax = SNMP_SYNTAX_INTEGER;
+ ifindex.v.integer = ifp->index;
+
+ snmp_send_trap(up ? &oid_linkUp : &oid_linkDown, &ifindex, NULL);
+}
+
+/*
+ * Fetch new MIB data.
+ */
+int
+mib_fetch_ifmib(struct mibif *ifp)
+{
+ int name[6];
+ size_t len;
+ void *newmib;
+ struct ifmibdata oldmib = ifp->mib;
+
+ name[0] = CTL_NET;
+ name[1] = PF_LINK;
+ name[2] = NETLINK_GENERIC;
+ name[3] = IFMIB_IFDATA;
+ name[4] = ifp->sysindex;
+ name[5] = IFDATA_GENERAL;
+
+ len = sizeof(ifp->mib);
+ if (sysctl(name, 6, &ifp->mib, &len, NULL, 0) == -1) {
+ if (errno != ENOENT)
+ syslog(LOG_WARNING, "sysctl(ifmib, %s) failed %m",
+ ifp->name);
+ return (-1);
+ }
+
+ if (ifp->trap_enable) {
+ if (!(oldmib.ifmd_flags & IFF_UP)) {
+ if (ifp->mib.ifmd_flags & IFF_UP)
+ link_trap(ifp, 1);
+ } else {
+ if (!(ifp->mib.ifmd_flags & IFF_UP))
+ link_trap(ifp, 0);
+ }
+ }
+
+ ifp->flags &= ~(MIBIF_HIGHSPEED | MIBIF_VERYHIGHSPEED);
+ if (ifp->mib.ifmd_data.ifi_baudrate > 20000000) {
+ ifp->flags |= MIBIF_HIGHSPEED;
+ if (ifp->mib.ifmd_data.ifi_baudrate > 650000000)
+ ifp->flags |= MIBIF_VERYHIGHSPEED;
+ }
+
+ /*
+ * linkspecific MIB
+ */
+ name[5] = IFDATA_LINKSPECIFIC;
+ if (sysctl(name, 6, NULL, &len, NULL, 0) == -1) {
+ syslog(LOG_WARNING, "sysctl linkmib estimate (%s): %m",
+ ifp->name);
+ if (ifp->specmib != NULL) {
+ ifp->specmib = NULL;
+ ifp->specmiblen = 0;
+ }
+ goto out;
+ }
+ if (len == 0) {
+ if (ifp->specmib != NULL) {
+ ifp->specmib = NULL;
+ ifp->specmiblen = 0;
+ }
+ goto out;
+ }
+
+ if (ifp->specmiblen != len) {
+ if ((newmib = realloc(ifp->specmib, len)) == NULL) {
+ ifp->specmib = NULL;
+ ifp->specmiblen = 0;
+ goto out;
+ }
+ ifp->specmib = newmib;
+ ifp->specmiblen = len;
+ }
+ if (sysctl(name, 6, ifp->specmib, &len, NULL, 0) == -1) {
+ syslog(LOG_WARNING, "sysctl linkmib (%s): %m", ifp->name);
+ if (ifp->specmib != NULL) {
+ ifp->specmib = NULL;
+ ifp->specmiblen = 0;
+ }
+ }
+
+ out:
+ ifp->mibtick = get_ticks();
+ return (0);
+}
+
+/* find first/next address for a given interface */
+struct mibifa *
+mib_first_ififa(const struct mibif *ifp)
+{
+ struct mibifa *ifa;
+
+ TAILQ_FOREACH(ifa, &mibifa_list, link)
+ if (ifp->index == ifa->ifindex)
+ return (ifa);
+ return (NULL);
+}
+
+struct mibifa *
+mib_next_ififa(struct mibifa *ifa0)
+{
+ struct mibifa *ifa;
+
+ ifa = ifa0;
+ while ((ifa = TAILQ_NEXT(ifa, link)) != NULL)
+ if (ifa->ifindex == ifa0->ifindex)
+ return (ifa);
+ return (NULL);
+}
+
+/*
+ * Allocate a new IFA
+ */
+static struct mibifa *
+alloc_ifa(u_int ifindex, struct in_addr addr)
+{
+ struct mibifa *ifa;
+ u_int32_t ha;
+
+ if ((ifa = malloc(sizeof(struct mibifa))) == NULL) {
+ syslog(LOG_ERR, "ifa: %m");
+ return (NULL);
+ }
+ ifa->inaddr = addr;
+ ifa->ifindex = ifindex;
+
+ ha = ntohl(ifa->inaddr.s_addr);
+ ifa->index.len = 4;
+ ifa->index.subs[0] = (ha >> 24) & 0xff;
+ ifa->index.subs[1] = (ha >> 16) & 0xff;
+ ifa->index.subs[2] = (ha >> 8) & 0xff;
+ ifa->index.subs[3] = (ha >> 0) & 0xff;
+
+ ifa->flags = 0;
+ ifa->inbcast.s_addr = 0;
+ ifa->inmask.s_addr = 0xffffffff;
+
+ INSERT_OBJECT_OID(ifa, &mibifa_list);
+
+ return (ifa);
+}
+
+/*
+ * Delete an interface address
+ */
+static void
+destroy_ifa(struct mibifa *ifa)
+{
+ TAILQ_REMOVE(&mibifa_list, ifa, link);
+ free(ifa);
+}
+
+
+/*
+ * Helper routine to extract the sockaddr structures from a routing
+ * socket message.
+ */
+void
+mib_extract_addrs(int addrs, u_char *info, struct sockaddr **out)
+{
+ u_int i;
+
+ for (i = 0; i < RTAX_MAX; i++) {
+ if ((addrs & (1 << i)) != 0) {
+ *out = (struct sockaddr *)info;
+ info += roundup((*out)->sa_len, sizeof(long));
+ } else
+ *out = NULL;
+ out++;
+ }
+}
+
+/*
+ * save the phys address of an interface. Handle receive address entries here.
+ */
+static void
+get_physaddr(struct mibif *ifp, struct sockaddr_dl *sdl, u_char *ptr)
+{
+ u_char *np;
+ struct mibrcvaddr *rcv;
+
+ if (sdl->sdl_alen == 0) {
+ /* no address */
+ if (ifp->physaddrlen != NULL) {
+ if ((rcv = mib_find_rcvaddr(ifp->index, ifp->physaddr,
+ ifp->physaddrlen)) != NULL)
+ mib_rcvaddr_delete(rcv);
+ free(ifp->physaddr);
+ ifp->physaddr = NULL;
+ ifp->physaddrlen = 0;
+ }
+ return;
+ }
+
+ if (ifp->physaddrlen != sdl->sdl_alen) {
+ /* length changed */
+ if (ifp->physaddrlen) {
+ /* delete olf receive address */
+ if ((rcv = mib_find_rcvaddr(ifp->index, ifp->physaddr,
+ ifp->physaddrlen)) != NULL)
+ mib_rcvaddr_delete(rcv);
+ }
+ if ((np = realloc(ifp->physaddr, sdl->sdl_alen)) == NULL) {
+ free(ifp->physaddr);
+ ifp->physaddr = NULL;
+ ifp->physaddrlen = 0;
+ return;
+ }
+ ifp->physaddr = np;
+ ifp->physaddrlen = sdl->sdl_alen;
+
+ } else if (memcmp(ifp->physaddr, ptr, ifp->physaddrlen) == 0) {
+ /* no change */
+ return;
+
+ } else {
+ /* address changed */
+
+ /* delete olf receive address */
+ if ((rcv = mib_find_rcvaddr(ifp->index, ifp->physaddr,
+ ifp->physaddrlen)) != NULL)
+ mib_rcvaddr_delete(rcv);
+ }
+
+ memcpy(ifp->physaddr, ptr, ifp->physaddrlen);
+
+ /* make new receive address */
+ if ((rcv = mib_rcvaddr_create(ifp, ifp->physaddr, ifp->physaddrlen)) != NULL)
+ rcv->flags |= MIBRCVADDR_HW;
+}
+
+/*
+ * Free an interface
+ */
+static void
+mibif_free(struct mibif *ifp)
+{
+ struct mibindexmap *map;
+ struct mibifa *ifa, *ifa1;
+ struct mibrcvaddr *rcv, *rcv1;
+ struct mibarp *at, *at1;
+
+ if (ifp->xnotify != NULL)
+ (*ifp->xnotify)(ifp, MIBIF_NOTIFY_DESTROY, ifp->xnotify_data);
+
+ (void)mib_ifstack_delete(ifp, NULL);
+ (void)mib_ifstack_delete(NULL, ifp);
+
+ TAILQ_REMOVE(&mibif_list, ifp, link);
+ if (ifp->physaddr != NULL)
+ free(ifp->physaddr);
+ if (ifp->specmib != NULL)
+ free(ifp->specmib);
+
+ STAILQ_FOREACH(map, &mibindexmap_list, link)
+ if (map->mibif == ifp) {
+ map->mibif = NULL;
+ break;
+ }
+
+ /* purge interface addresses */
+ ifa = TAILQ_FIRST(&mibifa_list);
+ while (ifa != NULL) {
+ ifa1 = TAILQ_NEXT(ifa, link);
+ if (ifa->ifindex == ifp->index)
+ destroy_ifa(ifa);
+ ifa = ifa1;
+ }
+
+ /* purge receive addresses */
+ rcv = TAILQ_FIRST(&mibrcvaddr_list);
+ while (rcv != NULL) {
+ rcv1 = TAILQ_NEXT(rcv, link);
+ if (rcv->ifindex == ifp->index)
+ mib_rcvaddr_delete(rcv);
+ rcv = rcv1;
+ }
+
+ /* purge ARP entries */
+ at = TAILQ_FIRST(&mibarp_list);
+ while (at != NULL) {
+ at1 = TAILQ_NEXT(at, link);
+ if (at->index.subs[0] == ifp->index)
+ mib_arp_delete(at);
+ at = at1;
+ }
+
+
+ free(ifp);
+ mib_if_number--;
+ mib_iftable_last_change = this_tick;
+}
+
+/*
+ * Create a new interface
+ */
+static struct mibif *
+mibif_create(u_int sysindex, const char *name)
+{
+ struct mibif *ifp;
+ struct mibindexmap *map;
+
+ if ((ifp = malloc(sizeof(*ifp))) == NULL) {
+ syslog(LOG_WARNING, "%s: %m", __func__);
+ return (NULL);
+ }
+ memset(ifp, 0, sizeof(*ifp));
+ ifp->sysindex = sysindex;
+ strcpy(ifp->name, name);
+ strcpy(ifp->descr, name);
+
+ map = NULL;
+ if (!mib_if_is_dyn(ifp->name)) {
+ /* non-dynamic. look whether we know the interface */
+ STAILQ_FOREACH(map, &mibindexmap_list, link)
+ if (strcmp(map->name, ifp->name) == 0) {
+ ifp->index = map->ifindex;
+ map->mibif = ifp;
+ break;
+ }
+ /* assume it has a connector if it is not dynamic */
+ ifp->has_connector = 1;
+ ifp->trap_enable = 1;
+ }
+ if (map == NULL) {
+ /* new interface - get new index */
+ if (next_if_index > 0x7fffffff)
+ errx(1, "ifindex wrap");
+
+ if ((map = malloc(sizeof(*map))) == NULL) {
+ syslog(LOG_ERR, "ifmap: %m");
+ free(ifp);
+ return (NULL);
+ }
+ map->ifindex = next_if_index++;
+ map->sysindex = ifp->sysindex;
+ strcpy(map->name, ifp->name);
+ map->mibif = ifp;
+ STAILQ_INSERT_TAIL(&mibindexmap_list, map, link);
+ } else {
+ /* re-instantiate. Introduce a counter discontinuity */
+ ifp->counter_disc = get_ticks();
+ }
+ ifp->index = map->ifindex;
+
+ INSERT_OBJECT_INT(ifp, &mibif_list);
+ mib_if_number++;
+ mib_iftable_last_change = this_tick;
+
+ /* instantiate default ifStack entries */
+ (void)mib_ifstack_create(ifp, NULL);
+ (void)mib_ifstack_create(NULL, ifp);
+
+ return (ifp);
+}
+
+/*
+ * Inform all interested parties about a new interface
+ */
+static void
+notify_newif(struct mibif *ifp)
+{
+ struct newifreg *reg;
+
+ TAILQ_FOREACH(reg, &newifreg_list, link)
+ if ((*reg->func)(ifp))
+ return;
+}
+
+/*
+ * This is called for new interfaces after we have fetched the interface
+ * MIB. If this is a broadcast interface try to guess the broadcast address
+ * depending on the interface type.
+ */
+static void
+check_llbcast(struct mibif *ifp)
+{
+ static u_char ether_bcast[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+ static u_char arcnet_bcast = 0;
+ struct mibrcvaddr *rcv;
+
+ if (!(ifp->mib.ifmd_flags & IFF_BROADCAST))
+ return;
+
+ switch (ifp->mib.ifmd_data.ifi_type) {
+
+ case IFT_ETHER:
+ case IFT_FDDI:
+ case IFT_ISO88025:
+ if (mib_find_rcvaddr(ifp->index, ether_bcast, 6) == NULL &&
+ (rcv = mib_rcvaddr_create(ifp, ether_bcast, 6)) != NULL)
+ rcv->flags |= MIBRCVADDR_BCAST;
+ break;
+
+ case IFT_ARCNET:
+ if (mib_find_rcvaddr(ifp->index, &arcnet_bcast, 1) == NULL &&
+ (rcv = mib_rcvaddr_create(ifp, &arcnet_bcast, 1)) != NULL)
+ rcv->flags |= MIBRCVADDR_BCAST;
+ break;
+ }
+}
+
+
+/*
+ * Retrieve the current interface list from the system.
+ */
+void
+mib_refresh_iflist(void)
+{
+ struct mibif *ifp, *ifp1;
+ size_t len;
+ u_short idx;
+ int name[6];
+ int count;
+ struct ifmibdata mib;
+
+ TAILQ_FOREACH(ifp, &mibif_list, link)
+ ifp->flags &= ~MIBIF_FOUND;
+
+ len = sizeof(count);
+ if (sysctlbyname("net.link.generic.system.ifcount", &count, &len,
+ NULL, 0) == -1) {
+ syslog(LOG_ERR, "ifcount: %m");
+ return;
+ }
+ name[0] = CTL_NET;
+ name[1] = PF_LINK;
+ name[2] = NETLINK_GENERIC;
+ name[3] = IFMIB_IFDATA;
+ name[5] = IFDATA_GENERAL;
+ for (idx = 1; idx <= count; idx++) {
+ name[4] = idx;
+ len = sizeof(mib);
+ if (sysctl(name, 6, &mib, &len, NULL, 0) == -1) {
+ if (errno == ENOENT)
+ continue;
+ syslog(LOG_ERR, "ifmib(%u): %m", idx);
+ return;
+ }
+ if ((ifp = mib_find_if_sys(idx)) != NULL) {
+ ifp->flags |= MIBIF_FOUND;
+ continue;
+ }
+ /* Unknown interface - create */
+ if ((ifp = mibif_create(idx, mib.ifmd_name)) != NULL) {
+ ifp->flags |= MIBIF_FOUND;
+ (void)mib_fetch_ifmib(ifp);
+ check_llbcast(ifp);
+ notify_newif(ifp);
+ }
+ }
+
+ /*
+ * Purge interfaces that disappeared
+ */
+ ifp = TAILQ_FIRST(&mibif_list);
+ while (ifp != NULL) {
+ ifp1 = TAILQ_NEXT(ifp, link);
+ if (!(ifp->flags & MIBIF_FOUND))
+ mibif_free(ifp);
+ ifp = ifp1;
+ }
+}
+
+/*
+ * Find an interface address
+ */
+struct mibifa *
+mib_find_ifa(struct in_addr addr)
+{
+ struct mibifa *ifa;
+
+ TAILQ_FOREACH(ifa, &mibifa_list, link)
+ if (ifa->inaddr.s_addr == addr.s_addr)
+ return (ifa);
+ return (NULL);
+}
+
+/*
+ * Process a new ARP entry
+ */
+static void
+process_arp(const struct rt_msghdr *rtm, const struct sockaddr_dl *sdl,
+ const struct sockaddr_in *sa)
+{
+ struct mibif *ifp;
+ struct mibarp *at;
+
+ /* IP arp table entry */
+ if (sdl->sdl_alen == 0) {
+ update_arp = 1;
+ return;
+ }
+ if ((ifp = mib_find_if_sys(sdl->sdl_index)) == NULL)
+ return;
+ /* have a valid entry */
+ if ((at = mib_find_arp(ifp, sa->sin_addr)) == NULL &&
+ (at = mib_arp_create(ifp, sa->sin_addr,
+ sdl->sdl_data + sdl->sdl_nlen, sdl->sdl_alen)) == NULL)
+ return;
+
+ if (rtm->rtm_rmx.rmx_expire == 0)
+ at->flags |= MIBARP_PERM;
+ else
+ at->flags &= ~MIBARP_PERM;
+ at->flags |= MIBARP_FOUND;
+}
+
+/*
+ * Handle a routing socket message.
+ */
+static void
+handle_rtmsg(struct rt_msghdr *rtm)
+{
+ struct sockaddr *addrs[RTAX_MAX];
+ struct if_msghdr *ifm;
+ struct ifa_msghdr *ifam;
+ struct ifma_msghdr *ifmam;
+#ifdef RTM_IFANNOUNCE
+ struct if_announcemsghdr *ifan;
+#endif
+ struct mibif *ifp;
+ struct sockaddr_dl *sdl;
+ struct sockaddr_in *sa;
+ struct mibifa *ifa;
+ struct mibrcvaddr *rcv;
+ u_char *ptr;
+
+ if (rtm->rtm_version != RTM_VERSION) {
+ syslog(LOG_ERR, "Bogus RTM version %u", rtm->rtm_version);
+ return;
+ }
+
+ switch (rtm->rtm_type) {
+
+ case RTM_NEWADDR:
+ ifam = (struct ifa_msghdr *)rtm;
+ mib_extract_addrs(ifam->ifam_addrs, (u_char *)(ifam + 1), addrs);
+ if (addrs[RTAX_IFA] == NULL || addrs[RTAX_NETMASK] == NULL)
+ break;
+
+ sa = (struct sockaddr_in *)(void *)addrs[RTAX_IFA];
+ if ((ifa = mib_find_ifa(sa->sin_addr)) == NULL) {
+ /* unknown address */
+ if ((ifp = mib_find_if_sys(ifam->ifam_index)) == NULL) {
+ syslog(LOG_WARNING, "RTM_NEWADDR for unknown "
+ "interface %u", ifam->ifam_index);
+ break;
+ }
+ if ((ifa = alloc_ifa(ifp->index, sa->sin_addr)) == NULL)
+ break;
+ }
+ sa = (struct sockaddr_in *)(void *)addrs[RTAX_NETMASK];
+ ifa->inmask = sa->sin_addr;
+
+ if (addrs[RTAX_BRD] != NULL) {
+ sa = (struct sockaddr_in *)(void *)addrs[RTAX_BRD];
+ ifa->inbcast = sa->sin_addr;
+ }
+ ifa->flags |= MIBIFA_FOUND;
+ break;
+
+ case RTM_DELADDR:
+ ifam = (struct ifa_msghdr *)rtm;
+ mib_extract_addrs(ifam->ifam_addrs, (u_char *)(ifam + 1), addrs);
+ if (addrs[RTAX_IFA] == NULL)
+ break;
+
+ sa = (struct sockaddr_in *)(void *)addrs[RTAX_IFA];
+ if ((ifa = mib_find_ifa(sa->sin_addr)) != NULL) {
+ ifa->flags |= MIBIFA_FOUND;
+ if (!(ifa->flags & MIBIFA_DESTROYED))
+ destroy_ifa(ifa);
+ }
+ break;
+
+ case RTM_NEWMADDR:
+ ifmam = (struct ifma_msghdr *)rtm;
+ mib_extract_addrs(ifmam->ifmam_addrs, (u_char *)(ifmam + 1), addrs);
+ if (addrs[RTAX_IFA] == NULL ||
+ addrs[RTAX_IFA]->sa_family != AF_LINK)
+ break;
+ sdl = (struct sockaddr_dl *)(void *)addrs[RTAX_IFA];
+ if ((rcv = mib_find_rcvaddr(sdl->sdl_index,
+ sdl->sdl_data + sdl->sdl_nlen, sdl->sdl_alen)) == NULL) {
+ /* unknown address */
+ if ((ifp = mib_find_if_sys(sdl->sdl_index)) == NULL) {
+ syslog(LOG_WARNING, "RTM_NEWMADDR for unknown "
+ "interface %u", sdl->sdl_index);
+ break;
+ }
+ if ((rcv = mib_rcvaddr_create(ifp,
+ sdl->sdl_data + sdl->sdl_nlen, sdl->sdl_alen)) == NULL)
+ break;
+ rcv->flags |= MIBRCVADDR_VOLATILE;
+ }
+ rcv->flags |= MIBRCVADDR_FOUND;
+ break;
+
+ case RTM_DELMADDR:
+ ifmam = (struct ifma_msghdr *)rtm;
+ mib_extract_addrs(ifmam->ifmam_addrs, (u_char *)(ifmam + 1), addrs);
+ if (addrs[RTAX_IFA] == NULL ||
+ addrs[RTAX_IFA]->sa_family != AF_LINK)
+ break;
+ sdl = (struct sockaddr_dl *)(void *)addrs[RTAX_IFA];
+ if ((rcv = mib_find_rcvaddr(sdl->sdl_index,
+ sdl->sdl_data + sdl->sdl_nlen, sdl->sdl_alen)) != NULL)
+ mib_rcvaddr_delete(rcv);
+ break;
+
+ case RTM_IFINFO:
+ ifm = (struct if_msghdr *)rtm;
+ mib_extract_addrs(ifm->ifm_addrs, (u_char *)(ifm + 1), addrs);
+ if ((ifp = mib_find_if_sys(ifm->ifm_index)) == NULL)
+ break;
+ if (addrs[RTAX_IFP] != NULL &&
+ addrs[RTAX_IFP]->sa_family == AF_LINK) {
+ sdl = (struct sockaddr_dl *)(void *)addrs[RTAX_IFP];
+ ptr = sdl->sdl_data + sdl->sdl_nlen;
+ get_physaddr(ifp, sdl, ptr);
+ }
+ (void)mib_fetch_ifmib(ifp);
+ break;
+
+#ifdef RTM_IFANNOUNCE
+ case RTM_IFANNOUNCE:
+ ifan = (struct if_announcemsghdr *)rtm;
+ ifp = mib_find_if_sys(ifan->ifan_index);
+
+ switch (ifan->ifan_what) {
+
+ case IFAN_ARRIVAL:
+ if (ifp == NULL && (ifp = mibif_create(ifan->ifan_index,
+ ifan->ifan_name)) != NULL) {
+ (void)mib_fetch_ifmib(ifp);
+ check_llbcast(ifp);
+ notify_newif(ifp);
+ }
+ break;
+
+ case IFAN_DEPARTURE:
+ if (ifp != NULL)
+ mibif_free(ifp);
+ break;
+ }
+ break;
+#endif
+
+ case RTM_GET:
+ mib_extract_addrs(rtm->rtm_addrs, (u_char *)(rtm + 1), addrs);
+ if (rtm->rtm_flags & RTF_LLINFO) {
+ if (addrs[RTAX_DST] == NULL ||
+ addrs[RTAX_GATEWAY] == NULL ||
+ addrs[RTAX_DST]->sa_family != AF_INET ||
+ addrs[RTAX_GATEWAY]->sa_family != AF_LINK)
+ break;
+ process_arp(rtm,
+ (struct sockaddr_dl *)(void *)addrs[RTAX_GATEWAY],
+ (struct sockaddr_in *)(void *)addrs[RTAX_DST]);
+ }
+ break;
+
+ case RTM_ADD:
+ mib_extract_addrs(rtm->rtm_addrs, (u_char *)(rtm + 1), addrs);
+ if (rtm->rtm_flags & RTF_LLINFO) {
+ if (addrs[RTAX_DST] == NULL ||
+ addrs[RTAX_GATEWAY] == NULL ||
+ addrs[RTAX_DST]->sa_family != AF_INET ||
+ addrs[RTAX_GATEWAY]->sa_family != AF_LINK)
+ break;
+ process_arp(rtm,
+ (struct sockaddr_dl *)(void *)addrs[RTAX_GATEWAY],
+ (struct sockaddr_in *)(void *)addrs[RTAX_DST]);
+ }
+ break;
+ }
+}
+
+/*
+ * Fetch the routing table via sysctl
+ */
+u_char *
+mib_fetch_rtab(int af, int info, int arg, size_t *lenp)
+{
+ int name[6];
+ u_char *buf;
+
+ name[0] = CTL_NET;
+ name[1] = PF_ROUTE;
+ name[2] = 0;
+ name[3] = af;
+ name[4] = info;
+ name[5] = arg;
+
+ if (sysctl(name, 6, NULL, lenp, NULL, 0) == -1) {
+ syslog(LOG_ERR, "sysctl estimate (%d,%d,%d,%d,%d,%d): %m",
+ name[0], name[1], name[2], name[3], name[4], name[5]);
+ return (NULL);
+ }
+
+ if ((buf = malloc(*lenp)) == NULL) {
+ syslog(LOG_ERR, "sysctl buffer: %m");
+ return (NULL);
+ }
+
+ if (sysctl(name, 6, buf, lenp, NULL, 0) == -1) {
+ syslog(LOG_ERR, "sysctl get: %m");
+ free(buf);
+ return (NULL);
+ }
+
+ return (buf);
+}
+
+/*
+ * Update the following info: interface, interface addresses, interface
+ * receive addresses, arp-table.
+ * This does not change the interface list itself.
+ */
+static void
+update_ifa_info(void)
+{
+ u_char *buf, *next;
+ struct rt_msghdr *rtm;
+ struct mibifa *ifa, *ifa1;
+ struct mibrcvaddr *rcv, *rcv1;
+ size_t needed;
+ static const int infos[][3] = {
+ { 0, NET_RT_IFLIST, 0 },
+#ifdef NET_RT_IFMALIST
+ { AF_LINK, NET_RT_IFMALIST, 0 },
+#endif
+ };
+ u_int i;
+
+ TAILQ_FOREACH(ifa, &mibifa_list, link)
+ ifa->flags &= ~MIBIFA_FOUND;
+ TAILQ_FOREACH(rcv, &mibrcvaddr_list, link)
+ rcv->flags &= ~MIBRCVADDR_FOUND;
+
+ for (i = 0; i < sizeof(infos) / sizeof(infos[0]); i++) {
+ if ((buf = mib_fetch_rtab(infos[i][0], infos[i][1], infos[i][2],
+ &needed)) == NULL)
+ continue;
+
+ next = buf;
+ while (next < buf + needed) {
+ rtm = (struct rt_msghdr *)(void *)next;
+ next += rtm->rtm_msglen;
+ handle_rtmsg(rtm);
+ }
+ free(buf);
+ }
+
+ /*
+ * Purge the address list of unused entries. These may happen for
+ * interface aliases that are on the same subnet. We don't receive
+ * routing socket messages for them.
+ */
+ ifa = TAILQ_FIRST(&mibifa_list);
+ while (ifa != NULL) {
+ ifa1 = TAILQ_NEXT(ifa, link);
+ if (!(ifa->flags & MIBIFA_FOUND))
+ destroy_ifa(ifa);
+ ifa = ifa1;
+ }
+
+ rcv = TAILQ_FIRST(&mibrcvaddr_list);
+ while (rcv != NULL) {
+ rcv1 = TAILQ_NEXT(rcv, link);
+ if (!(rcv->flags & (MIBRCVADDR_FOUND | MIBRCVADDR_BCAST |
+ MIBRCVADDR_HW)))
+ mib_rcvaddr_delete(rcv);
+ rcv = rcv1;
+ }
+}
+
+/*
+ * Update arp table
+ */
+void
+mib_arp_update(void)
+{
+ struct mibarp *at, *at1;
+ size_t needed;
+ u_char *buf, *next;
+ struct rt_msghdr *rtm;
+
+ if (in_update_arp)
+ return; /* Aaargh */
+ in_update_arp = 1;
+
+ TAILQ_FOREACH(at, &mibarp_list, link)
+ at->flags &= ~MIBARP_FOUND;
+
+ if ((buf = mib_fetch_rtab(AF_INET, NET_RT_FLAGS, RTF_LLINFO, &needed)) == NULL) {
+ in_update_arp = 0;
+ return;
+ }
+
+ next = buf;
+ while (next < buf + needed) {
+ rtm = (struct rt_msghdr *)(void *)next;
+ next += rtm->rtm_msglen;
+ handle_rtmsg(rtm);
+ }
+ free(buf);
+
+ at = TAILQ_FIRST(&mibarp_list);
+ while (at != NULL) {
+ at1 = TAILQ_NEXT(at, link);
+ if (!(at->flags & MIBARP_FOUND))
+ mib_arp_delete(at);
+ at = at1;
+ }
+ mibarpticks = get_ticks();
+ update_arp = 0;
+ in_update_arp = 0;
+}
+
+
+/*
+ * Intput on the routing socket.
+ */
+static void
+route_input(int fd, void *udata __unused)
+{
+ u_char buf[1024 * 16];
+ ssize_t n;
+ struct rt_msghdr *rtm;
+
+ if ((n = read(fd, buf, sizeof(buf))) == -1)
+ err(1, "read(rt_socket)");
+
+ if (n == 0)
+ errx(1, "EOF on rt_socket");
+
+ rtm = (struct rt_msghdr *)(void *)buf;
+ if ((size_t)n != rtm->rtm_msglen)
+ errx(1, "n=%zu, rtm_msglen=%u", (size_t)n, rtm->rtm_msglen);
+
+ handle_rtmsg(rtm);
+}
+
+/*
+ * execute and SIOCAIFADDR
+ */
+static int
+siocaifaddr(char *ifname, struct in_addr addr, struct in_addr mask,
+ struct in_addr bcast)
+{
+ struct ifaliasreq addreq;
+ struct sockaddr_in *sa;
+
+ memset(&addreq, 0, sizeof(addreq));
+ strncpy(addreq.ifra_name, ifname, sizeof(addreq.ifra_name));
+
+ sa = (struct sockaddr_in *)(void *)&addreq.ifra_addr;
+ sa->sin_family = AF_INET;
+ sa->sin_len = sizeof(*sa);
+ sa->sin_addr = addr;
+
+ sa = (struct sockaddr_in *)(void *)&addreq.ifra_mask;
+ sa->sin_family = AF_INET;
+ sa->sin_len = sizeof(*sa);
+ sa->sin_addr = mask;
+
+ sa = (struct sockaddr_in *)(void *)&addreq.ifra_broadaddr;
+ sa->sin_family = AF_INET;
+ sa->sin_len = sizeof(*sa);
+ sa->sin_addr = bcast;
+
+ return (ioctl(mib_netsock, SIOCAIFADDR, &addreq));
+}
+
+/*
+ * Exececute a SIOCDIFADDR
+ */
+static int
+siocdifaddr(const char *ifname, struct in_addr addr)
+{
+ struct ifreq delreq;
+ struct sockaddr_in *sa;
+
+ memset(&delreq, 0, sizeof(delreq));
+ strncpy(delreq.ifr_name, ifname, sizeof(delreq.ifr_name));
+ sa = (struct sockaddr_in *)(void *)&delreq.ifr_addr;
+ sa->sin_family = AF_INET;
+ sa->sin_len = sizeof(*sa);
+ sa->sin_addr = addr;
+
+ return (ioctl(mib_netsock, SIOCDIFADDR, &delreq));
+}
+
+/*
+ * Verify an interface address without fetching the entire list
+ */
+static int
+verify_ifa(const char *name, struct mibifa *ifa)
+{
+ struct ifreq req;
+ struct sockaddr_in *sa;
+
+ memset(&req, 0, sizeof(req));
+ strncpy(req.ifr_name, name, sizeof(req.ifr_name));
+ sa = (struct sockaddr_in *)(void *)&req.ifr_addr;
+ sa->sin_family = AF_INET;
+ sa->sin_len = sizeof(*sa);
+ sa->sin_addr = ifa->inaddr;
+
+ if (ioctl(mib_netsock, SIOCGIFADDR, &req) == -1)
+ return (-1);
+ if (ifa->inaddr.s_addr != sa->sin_addr.s_addr) {
+ syslog(LOG_ERR, "%s: address mismatch", __func__);
+ return (-1);
+ }
+
+ if (ioctl(mib_netsock, SIOCGIFNETMASK, &req) == -1)
+ return (-1);
+ if (ifa->inmask.s_addr != sa->sin_addr.s_addr) {
+ syslog(LOG_ERR, "%s: netmask mismatch", __func__);
+ return (-1);
+ }
+ return (0);
+}
+
+/*
+ * Restore a deleted interface address. Don't wait for the routing socket
+ * to update us.
+ */
+void
+mib_undestroy_ifa(struct mibifa *ifa)
+{
+ struct mibif *ifp;
+
+ if ((ifp = mib_find_if(ifa->ifindex)) == NULL)
+ /* keep it destroyed */
+ return;
+
+ if (siocaifaddr(ifp->name, ifa->inaddr, ifa->inmask, ifa->inbcast))
+ /* keep it destroyed */
+ return;
+
+ ifa->flags &= ~MIBIFA_DESTROYED;
+}
+
+/*
+ * Destroy an interface address
+ */
+int
+mib_destroy_ifa(struct mibifa *ifa)
+{
+ struct mibif *ifp;
+
+ if ((ifp = mib_find_if(ifa->ifindex)) == NULL) {
+ /* ups. */
+ mib_iflist_bad = 1;
+ return (-1);
+ }
+ if (siocdifaddr(ifp->name, ifa->inaddr)) {
+ /* ups. */
+ syslog(LOG_ERR, "SIOCDIFADDR: %m");
+ mib_iflist_bad = 1;
+ return (-1);
+ }
+ ifa->flags |= MIBIFA_DESTROYED;
+ return (0);
+}
+
+/*
+ * Rollback the modification of an address. Don't bother to wait for
+ * the routing socket.
+ */
+void
+mib_unmodify_ifa(struct mibifa *ifa)
+{
+ struct mibif *ifp;
+
+ if ((ifp = mib_find_if(ifa->ifindex)) == NULL) {
+ /* ups. */
+ mib_iflist_bad = 1;
+ return;
+ }
+
+ if (siocaifaddr(ifp->name, ifa->inaddr, ifa->inmask, ifa->inbcast)) {
+ /* ups. */
+ mib_iflist_bad = 1;
+ return;
+ }
+}
+
+/*
+ * Modify an IFA.
+ */
+int
+mib_modify_ifa(struct mibifa *ifa)
+{
+ struct mibif *ifp;
+
+ if ((ifp = mib_find_if(ifa->ifindex)) == NULL) {
+ /* ups. */
+ mib_iflist_bad = 1;
+ return (-1);
+ }
+
+ if (siocaifaddr(ifp->name, ifa->inaddr, ifa->inmask, ifa->inbcast)) {
+ /* ups. */
+ mib_iflist_bad = 1;
+ return (-1);
+ }
+
+ if (verify_ifa(ifp->name, ifa)) {
+ /* ups. */
+ mib_iflist_bad = 1;
+ return (-1);
+ }
+
+ return (0);
+}
+
+/*
+ * Destroy a freshly created interface address. Don't bother to wait for
+ * the routing socket.
+ */
+void
+mib_uncreate_ifa(struct mibifa *ifa)
+{
+ struct mibif *ifp;
+
+ if ((ifp = mib_find_if(ifa->ifindex)) == NULL) {
+ /* ups. */
+ mib_iflist_bad = 1;
+ return;
+ }
+ if (siocdifaddr(ifp->name, ifa->inaddr)) {
+ /* ups. */
+ mib_iflist_bad = 1;
+ return;
+ }
+
+ destroy_ifa(ifa);
+}
+
+/*
+ * Create a new ifa and verify it
+ */
+struct mibifa *
+mib_create_ifa(u_int ifindex, struct in_addr addr, struct in_addr mask,
+ struct in_addr bcast)
+{
+ struct mibif *ifp;
+ struct mibifa *ifa;
+
+ if ((ifp = mib_find_if(ifindex)) == NULL)
+ return (NULL);
+ if ((ifa = alloc_ifa(ifindex, addr)) == NULL)
+ return (NULL);
+ ifa->inmask = mask;
+ ifa->inbcast = bcast;
+
+ if (siocaifaddr(ifp->name, ifa->inaddr, ifa->inmask, ifa->inbcast)) {
+ syslog(LOG_ERR, "%s: %m", __func__);
+ destroy_ifa(ifa);
+ return (NULL);
+ }
+ if (verify_ifa(ifp->name, ifa)) {
+ destroy_ifa(ifa);
+ return (NULL);
+ }
+ return (ifa);
+}
+
+/*
+ * Get all cloning interfaces and make them dynamic.
+ * Hah! Whe should probably do this on a periodic basis (XXX).
+ */
+static void
+get_cloners(void)
+{
+ struct if_clonereq req;
+ char *buf, *cp;
+ int i;
+
+ memset(&req, 0, sizeof(req));
+ if (ioctl(mib_netsock, SIOCIFGCLONERS, &req) == -1) {
+ syslog(LOG_ERR, "get cloners: %m");
+ return;
+ }
+ if ((buf = malloc(req.ifcr_total * IFNAMSIZ)) == NULL) {
+ syslog(LOG_ERR, "%m");
+ return;
+ }
+ req.ifcr_count = req.ifcr_total;
+ req.ifcr_buffer = buf;
+ if (ioctl(mib_netsock, SIOCIFGCLONERS, &req) == -1) {
+ syslog(LOG_ERR, "get cloners: %m");
+ free(buf);
+ return;
+ }
+ for (cp = buf, i = 0; i < req.ifcr_total; i++, cp += IFNAMSIZ)
+ mib_if_set_dyn(cp);
+ free(buf);
+}
+
+/*
+ * Idle function
+ */
+static void
+mibII_idle(void)
+{
+ struct mibifa *ifa;
+
+ if (mib_iflist_bad) {
+ TAILQ_FOREACH(ifa, &mibifa_list, link)
+ ifa->flags &= ~MIBIFA_DESTROYED;
+
+ /* assume, that all cloning interfaces are dynamic */
+ get_cloners();
+
+ mib_refresh_iflist();
+ update_ifa_info();
+ mib_arp_update();
+ mib_iflist_bad = 0;
+ }
+ if (update_arp)
+ mib_arp_update();
+}
+
+
+/*
+ * Start the module
+ */
+static void
+mibII_start(void)
+{
+ if ((route_fd = fd_select(route, route_input, NULL, module)) == NULL) {
+ syslog(LOG_ERR, "fd_select(route): %m");
+ return;
+ }
+ mib_refresh_iflist();
+ update_ifa_info();
+ mib_arp_update();
+ mib_iftable_last_change = 0;
+ mib_ifstack_last_change = 0;
+
+ ifmib_reg = or_register(&oid_ifMIB,
+ "The MIB module to describe generic objects for network interface"
+ " sub-layers.", module);
+
+ ipmib_reg = or_register(&oid_ipMIB,
+ "The MIB module for managing IP and ICMP implementations, but "
+ "excluding their management of IP routes.", module);
+
+ tcpmib_reg = or_register(&oid_tcpMIB,
+ "The MIB module for managing TCP implementations.", module);
+
+ udpmib_reg = or_register(&oid_udpMIB,
+ "The MIB module for managing UDP implementations.", module);
+
+ ipForward_reg = or_register(&oid_ipForward,
+ "The MIB module for the display of CIDR multipath IP Routes.",
+ module);
+}
+
+/*
+ * Initialize the module
+ */
+static int
+mibII_init(struct lmodule *mod, int argc __unused, char *argv[] __unused)
+{
+ size_t len;
+
+ module = mod;
+
+ len = sizeof(clockinfo);
+ if (sysctlbyname("kern.clockrate", &clockinfo, &len, NULL, 0) == -1) {
+ syslog(LOG_ERR, "kern.clockrate: %m");
+ return (-1);
+ }
+ if (len != sizeof(clockinfo)) {
+ syslog(LOG_ERR, "kern.clockrate: wrong size");
+ return (-1);
+ }
+
+ if ((route = socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC)) == -1) {
+ syslog(LOG_ERR, "PF_ROUTE: %m");
+ return (-1);
+ }
+ (void)shutdown(route, SHUT_WR);
+
+ if ((mib_netsock = socket(PF_INET, SOCK_DGRAM, 0)) == -1) {
+ syslog(LOG_ERR, "PF_INET: %m");
+ (void)close(route);
+ return (-1);
+ }
+ (void)shutdown(mib_netsock, SHUT_RDWR);
+
+ /* assume, that all cloning interfaces are dynamic */
+ get_cloners();
+
+ return (0);
+}
+
+static int
+mibII_fini(void)
+{
+ if (route_fd != NULL)
+ fd_deselect(route_fd);
+ if (route != -1)
+ (void)close(route);
+ if (mib_netsock != -1)
+ (void)close(mib_netsock);
+ /* XXX free memory */
+
+ or_unregister(ipForward_reg);
+ or_unregister(udpmib_reg);
+ or_unregister(tcpmib_reg);
+ or_unregister(ipmib_reg);
+ or_unregister(ifmib_reg);
+
+ return (0);
+}
+
+static void
+mibII_loading(const struct lmodule *mod, int loaded)
+{
+ struct mibif *ifp;
+
+ if (loaded == 1)
+ return;
+
+ TAILQ_FOREACH(ifp, &mibif_list, link)
+ if (ifp->xnotify_mod == mod) {
+ ifp->xnotify_mod = NULL;
+ ifp->xnotify_data = NULL;
+ ifp->xnotify = NULL;
+ }
+
+ mib_unregister_newif(mod);
+}
+
+const struct snmp_module config = {
+ "This module implements the interface and ip groups.",
+ mibII_init,
+ mibII_fini,
+ mibII_idle, /* idle */
+ NULL, /* dump */
+ NULL, /* config */
+ mibII_start,
+ NULL,
+ mibII_ctree,
+ mibII_CTREE_SIZE,
+ mibII_loading
+};
+
+/*
+ * Should have a list of these attached to each interface.
+ */
+void *
+mibif_notify(struct mibif *ifp, const struct lmodule *mod,
+ mibif_notify_f func, void *data)
+{
+ ifp->xnotify = func;
+ ifp->xnotify_data = data;
+ ifp->xnotify_mod = mod;
+
+ return (ifp);
+}
+
+void
+mibif_unnotify(void *arg)
+{
+ struct mibif *ifp = arg;
+
+ ifp->xnotify = NULL;
+ ifp->xnotify_data = NULL;
+ ifp->xnotify_mod = NULL;
+}
diff --git a/contrib/bsnmp/snmp_mibII/mibII.h b/contrib/bsnmp/snmp_mibII/mibII.h
new file mode 100644
index 0000000..89ccd05
--- /dev/null
+++ b/contrib/bsnmp/snmp_mibII/mibII.h
@@ -0,0 +1,238 @@
+/*
+ * Copyright (c) 2001-2003
+ * Fraunhofer Institute for Open Communication Systems (FhG Fokus).
+ * All rights reserved.
+ *
+ * Author: Harti Brandt <harti@freebsd.org>
+ *
+ * Redistribution of this software and documentation and use in source and
+ * binary forms, with or without modification, are permitted provided that
+ * the following conditions are met:
+ *
+ * 1. Redistributions of source code or documentation must retain the above
+ * copyright notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY FRAUNHOFER FOKUS
+ * AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * FRAUNHOFER FOKUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Begemot: bsnmp/snmp_mibII/mibII.h,v 1.11 2002/03/21 10:43:06 hbb Exp $
+ *
+ * Implementation of the interfaces and IP groups of MIB-II.
+ */
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/syslog.h>
+#include <sys/time.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <err.h>
+#include <ctype.h>
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_mib.h>
+#include <net/route.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "asn1.h"
+#include "snmp.h"
+#include "snmpmod.h"
+#include "snmp_mibII.h"
+#include "mibII_tree.h"
+
+
+/*
+ * Interface list and flags.
+ */
+TAILQ_HEAD(mibif_list, mibif);
+enum {
+ MIBIF_FOUND = 0x0001,
+ MIBIF_HIGHSPEED = 0x0002,
+ MIBIF_VERYHIGHSPEED = 0x0004,
+};
+#define hc_inoctets mib.ifmd_data.ifi_ibytes
+#define hc_outoctets mib.ifmd_data.ifi_obytes
+#define hc_omcasts mib.ifmd_data.ifi_omcasts
+#define hc_opackets mib.ifmd_data.ifi_opackets
+#define hc_imcasts mib.ifmd_data.ifi_imcasts
+#define hc_ipackets mib.ifmd_data.ifi_ipackets
+
+/*
+ * Interface addresses.
+ */
+TAILQ_HEAD(mibifa_list, mibifa);
+enum {
+ MIBIFA_FOUND = 0x0001,
+ MIBIFA_DESTROYED = 0x0002,
+};
+
+/*
+ * Receive addresses
+ */
+TAILQ_HEAD(mibrcvaddr_list, mibrcvaddr);
+enum {
+ MIBRCVADDR_FOUND = 0x00010000,
+};
+
+/*
+ * Interface index mapping. The problem here is, that if the same interface
+ * is reinstantiated (for examble by unloading and loading the hardware driver)
+ * we must use the same index for this interface. For dynamic interfaces
+ * (clip, lane) we must use a fresh index, each time a new interface is created.
+ * To differentiate between these types of interfaces we use the following table
+ * which contains an entry for each dynamic interface type. All other interface
+ * types are supposed to be static. The mibindexmap contains an entry for
+ * all interfaces. The mibif pointer is NULL, if the interface doesn't exist
+ * anymore.
+ */
+struct mibdynif {
+ SLIST_ENTRY(mibdynif) link;
+ char name[IFNAMSIZ];
+};
+SLIST_HEAD(mibdynif_list, mibdynif);
+
+struct mibindexmap {
+ STAILQ_ENTRY(mibindexmap) link;
+ u_short sysindex;
+ u_int ifindex;
+ struct mibif *mibif; /* may be NULL */
+ char name[IFNAMSIZ];
+};
+STAILQ_HEAD(mibindexmap_list, mibindexmap);
+
+/*
+ * Interface stacking. The generic code cannot know how the interfaces stack.
+ * For this reason it instantiates only the x.0 and 0.x table elements. All
+ * others have to be instantiated by the interface specific modules.
+ * The table is read-only.
+ */
+struct mibifstack {
+ TAILQ_ENTRY(mibifstack) link;
+ struct asn_oid index;
+};
+TAILQ_HEAD(mibifstack_list, mibifstack);
+
+/*
+ * NetToMediaTable (ArpTable)
+ */
+struct mibarp {
+ TAILQ_ENTRY(mibarp) link;
+ struct asn_oid index; /* contains both the ifindex and addr */
+ u_char phys[128]; /* the physical address */
+ u_int physlen; /* and its length */
+ u_int flags;
+};
+TAILQ_HEAD(mibarp_list, mibarp);
+enum {
+ MIBARP_FOUND = 0x00010000,
+ MIBARP_PERM = 0x00000001,
+};
+
+/*
+ * New if registrations
+ */
+struct newifreg {
+ TAILQ_ENTRY(newifreg) link;
+ const struct lmodule *mod;
+ int (*func)(struct mibif *);
+};
+TAILQ_HEAD(newifreg_list, newifreg);
+
+/* list of all IP addresses */
+extern struct mibifa_list mibifa_list;
+
+/* list of all interfaces */
+extern struct mibif_list mibif_list;
+
+/* list of dynamic interface names */
+extern struct mibdynif_list mibdynif_list;
+
+/* list of all interface index mappings */
+extern struct mibindexmap_list mibindexmap_list;
+
+/* list of all stacking entries */
+extern struct mibifstack_list mibifstack_list;
+
+/* list of all receive addresses */
+extern struct mibrcvaddr_list mibrcvaddr_list;
+
+/* list of all NetToMedia entries */
+extern struct mibarp_list mibarp_list;
+
+/* number of interfaces */
+extern int32_t mib_if_number;
+
+/* last change of interface table */
+extern u_int32_t mib_iftable_last_change;
+
+/* last change of stack table */
+extern u_int32_t mib_ifstack_last_change;
+
+/* if this is set, one of our lists may be bad. refresh them when idle */
+extern int mib_iflist_bad;
+
+/* last time refreshed */
+extern u_int32_t mibarpticks;
+
+/* info on system clocks */
+extern struct clockinfo clockinfo;
+
+/* get interfaces and interface addresses. */
+void mib_fetch_interfaces(void);
+
+/* check whether this interface(type) is dynamic */
+int mib_if_is_dyn(const char *name);
+
+/* destroy an interface address */
+int mib_destroy_ifa(struct mibifa *);
+
+/* restituate a deleted interface address */
+void mib_undestroy_ifa(struct mibifa *);
+
+/* change interface address */
+int mib_modify_ifa(struct mibifa *);
+
+/* undo if address modification */
+void mib_unmodify_ifa(struct mibifa *);
+
+/* create an interface address */
+struct mibifa * mib_create_ifa(u_int ifindex, struct in_addr addr, struct in_addr mask, struct in_addr bcast);
+
+/* delete a freshly created address */
+void mib_uncreate_ifa(struct mibifa *);
+
+/* create/delete arp entries */
+struct mibarp *mib_arp_create(const struct mibif *, struct in_addr, const u_char *, size_t);
+void mib_arp_delete(struct mibarp *);
+
+/* find arp entry */
+struct mibarp *mib_find_arp(const struct mibif *, struct in_addr);
+
+/* update arp table */
+void mib_arp_update(void);
+
+/* fetch routing table */
+u_char *mib_fetch_rtab(int af, int info, int arg, size_t *lenp);
+
+/* extract addresses from routing message */
+void mib_extract_addrs(int, u_char *, struct sockaddr **);
diff --git a/contrib/bsnmp/snmp_mibII/mibII_ifmib.c b/contrib/bsnmp/snmp_mibII/mibII_ifmib.c
new file mode 100644
index 0000000..1f09d59
--- /dev/null
+++ b/contrib/bsnmp/snmp_mibII/mibII_ifmib.c
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2001-2003
+ * Fraunhofer Institute for Open Communication Systems (FhG Fokus).
+ * All rights reserved.
+ *
+ * Author: Harti Brandt <harti@freebsd.org>
+ *
+ * Redistribution of this software and documentation and use in source and
+ * binary forms, with or without modification, are permitted provided that
+ * the following conditions are met:
+ *
+ * 1. Redistributions of source code or documentation must retain the above
+ * copyright notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY FRAUNHOFER FOKUS
+ * AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * FRAUNHOFER FOKUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Begemot: bsnmp/snmp_mibII/mibII_ifmib.c,v 1.7 2003/01/28 13:44:35 hbb Exp $
+ *
+ * Interfaces group.
+ */
+#include "mibII.h"
+#include "mibII_oid.h"
+
+/*
+ * Scalars
+ */
+int
+op_ifmib(struct snmp_context *ctx __unused, struct snmp_value *value,
+ u_int sub, u_int idx __unused, enum snmp_op op)
+{
+ switch (op) {
+
+ case SNMP_OP_GETNEXT:
+ abort();
+
+ case SNMP_OP_GET:
+ break;
+
+ case SNMP_OP_SET:
+ return (SNMP_ERR_NOT_WRITEABLE);
+
+ case SNMP_OP_ROLLBACK:
+ case SNMP_OP_COMMIT:
+ abort();
+ }
+
+ switch (value->var.subs[sub - 1]) {
+
+ case LEAF_ifTableLastChange:
+ if (mib_iftable_last_change > start_tick)
+ value->v.uint32 = mib_iftable_last_change - start_tick;
+ else
+ value->v.uint32 = 0;
+ break;
+
+ case LEAF_ifStackLastChange:
+ if (mib_ifstack_last_change > start_tick)
+ value->v.uint32 = mib_ifstack_last_change - start_tick;
+ else
+ value->v.uint32 = 0;
+ break;
+ }
+ return (SNMP_ERR_NOERROR);
+}
diff --git a/contrib/bsnmp/snmp_mibII/mibII_ifstack.c b/contrib/bsnmp/snmp_mibII/mibII_ifstack.c
new file mode 100644
index 0000000..93aa259
--- /dev/null
+++ b/contrib/bsnmp/snmp_mibII/mibII_ifstack.c
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2001-2003
+ * Fraunhofer Institute for Open Communication Systems (FhG Fokus).
+ * All rights reserved.
+ *
+ * Author: Harti Brandt <harti@freebsd.org>
+ *
+ * Redistribution of this software and documentation and use in source and
+ * binary forms, with or without modification, are permitted provided that
+ * the following conditions are met:
+ *
+ * 1. Redistributions of source code or documentation must retain the above
+ * copyright notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY FRAUNHOFER FOKUS
+ * AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * FRAUNHOFER FOKUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Begemot: bsnmp/snmp_mibII/mibII_ifstack.c,v 1.5 2003/01/28 13:44:35 hbb Exp $
+ *
+ * ifStackTable. Read-only.
+ */
+#include "mibII.h"
+
+int
+mib_ifstack_create(const struct mibif *lower, const struct mibif *upper)
+{
+ struct mibifstack *stack;
+
+ if ((stack = malloc(sizeof(*stack))) == NULL)
+ return (-1);
+
+ stack->index.len = 2;
+ stack->index.subs[0] = upper ? upper->index : 0;
+ stack->index.subs[1] = lower ? lower->index : 0;
+
+ INSERT_OBJECT_OID(stack, &mibifstack_list);
+
+ mib_ifstack_last_change = get_ticks();
+
+ return (0);
+}
+
+void
+mib_ifstack_delete(const struct mibif *lower, const struct mibif *upper)
+{
+ struct mibifstack *stack;
+
+ TAILQ_FOREACH(stack, &mibifstack_list, link)
+ if (stack->index.subs[0] == (upper ? upper->index : 0) &&
+ stack->index.subs[1] == (lower ? lower->index : 0)) {
+ TAILQ_REMOVE(&mibifstack_list, stack, link);
+ free(stack);
+ mib_ifstack_last_change = get_ticks();
+ return;
+ }
+}
+
+int
+op_ifstack(struct snmp_context *ctx __unused, struct snmp_value *value,
+ u_int sub, u_int iidx __unused, enum snmp_op op)
+{
+ struct mibifstack *stack;
+
+ switch (op) {
+
+ case SNMP_OP_GETNEXT:
+ if ((stack = NEXT_OBJECT_OID(&mibifstack_list, &value->var, sub)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ index_append(&value->var, sub, &stack->index);
+ break;
+
+ case SNMP_OP_GET:
+ if ((stack = FIND_OBJECT_OID(&mibifstack_list, &value->var, sub)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ break;
+
+ case SNMP_OP_SET:
+ if ((stack = FIND_OBJECT_OID(&mibifstack_list, &value->var, sub)) == NULL)
+ return (SNMP_ERR_NO_CREATION);
+ return (SNMP_ERR_NOT_WRITEABLE);
+
+ case SNMP_OP_ROLLBACK:
+ case SNMP_OP_COMMIT:
+ abort();
+ }
+
+ switch (value->var.subs[sub - 1]) {
+
+ case LEAF_ifStackStatus:
+ value->v.integer = 1;
+ break;
+ }
+ return (SNMP_ERR_NOERROR);
+}
diff --git a/contrib/bsnmp/snmp_mibII/mibII_interfaces.c b/contrib/bsnmp/snmp_mibII/mibII_interfaces.c
new file mode 100644
index 0000000..02a90ca
--- /dev/null
+++ b/contrib/bsnmp/snmp_mibII/mibII_interfaces.c
@@ -0,0 +1,524 @@
+/*
+ * Copyright (c) 2001-2003
+ * Fraunhofer Institute for Open Communication Systems (FhG Fokus).
+ * All rights reserved.
+ *
+ * Author: Harti Brandt <harti@freebsd.org>
+ *
+ * Redistribution of this software and documentation and use in source and
+ * binary forms, with or without modification, are permitted provided that
+ * the following conditions are met:
+ *
+ * 1. Redistributions of source code or documentation must retain the above
+ * copyright notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY FRAUNHOFER FOKUS
+ * AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * FRAUNHOFER FOKUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Begemot: bsnmp/snmp_mibII/mibII_interfaces.c,v 1.9 2003/01/28 13:44:35 hbb Exp $
+ *
+ * Interfaces group.
+ */
+#include "mibII.h"
+#include "mibII_oid.h"
+
+/*
+ * This structure catches all changes to a interface entry
+ */
+struct ifchange {
+ struct snmp_dependency dep;
+
+ u_int ifindex;
+
+ u_int32_t set;
+ int promisc;
+ int admin;
+ int traps;
+
+ u_int32_t rb;
+ int rb_flags;
+ int rb_traps;
+};
+#define IFC_PROMISC 0x0001
+#define IFC_ADMIN 0x0002
+#define IFC_TRAPS 0x0004
+#define IFRB_FLAGS 0x0001
+#define IFRB_TRAPS 0x0002
+
+static const struct asn_oid
+ oid_ifTable = OIDX_ifTable;
+
+/*
+ * This function handles all changes to the interface table and interface
+ * extension table.
+ */
+static int
+ifchange_func(struct snmp_context *ctx __unused, struct snmp_dependency *dep,
+ enum snmp_depop op)
+{
+ struct ifchange *ifc = (struct ifchange *)dep;
+ struct mibif *ifp;
+ struct ifreq ifr, ifr1;
+
+ if ((ifp = mib_find_if(ifc->ifindex)) == NULL)
+ return (SNMP_ERR_NO_CREATION);
+
+ switch (op) {
+
+ case SNMP_DEPOP_COMMIT:
+ strncpy(ifr.ifr_name, ifp->name, sizeof(ifr.ifr_name));
+ if (ioctl(mib_netsock, SIOCGIFFLAGS, &ifr) == -1) {
+ syslog(LOG_ERR, "GIFFLAGS(%s): %m", ifp->name);
+ return (SNMP_ERR_GENERR);
+ }
+ if (ifc->set & IFC_PROMISC) {
+ ifr.ifr_flags &= ~IFF_PROMISC;
+ if (ifc->promisc)
+ ifr.ifr_flags |= IFF_PROMISC;
+ ifc->rb |= IFRB_FLAGS;
+ }
+ if (ifc->set & IFC_ADMIN) {
+ ifr.ifr_flags &= ~IFF_UP;
+ if (ifc->admin)
+ ifr.ifr_flags |= IFF_UP;
+ ifc->rb |= IFRB_FLAGS;
+ }
+ if (ifc->rb & IFRB_FLAGS) {
+ strncpy(ifr1.ifr_name, ifp->name, sizeof(ifr1.ifr_name));
+ if (ioctl(mib_netsock, SIOCGIFFLAGS, &ifr1) == -1) {
+ syslog(LOG_ERR, "GIFFLAGS(%s): %m", ifp->name);
+ return (SNMP_ERR_GENERR);
+ }
+ ifc->rb_flags = ifr1.ifr_flags;
+ if (ioctl(mib_netsock, SIOCSIFFLAGS, &ifr) == -1) {
+ syslog(LOG_ERR, "SIFFLAGS(%s): %m", ifp->name);
+ return (SNMP_ERR_GENERR);
+ }
+ (void)mib_fetch_ifmib(ifp);
+ }
+ if (ifc->set & IFC_TRAPS) {
+ ifc->rb |= IFRB_TRAPS;
+ ifc->rb_traps = ifp->trap_enable;
+ ifp->trap_enable = ifc->traps;
+ }
+ return (SNMP_ERR_NOERROR);
+
+ case SNMP_DEPOP_ROLLBACK:
+ if (ifc->rb & IFRB_FLAGS) {
+ strncpy(ifr.ifr_name, ifp->name, sizeof(ifr.ifr_name));
+ ifr.ifr_flags = ifc->rb_flags;
+ if (ioctl(mib_netsock, SIOCSIFFLAGS, &ifr) == -1) {
+ syslog(LOG_ERR, "SIFFLAGS(%s): %m", ifp->name);
+ return (SNMP_ERR_UNDO_FAILED);
+ }
+ (void)mib_fetch_ifmib(ifp);
+ }
+ if (ifc->rb & IFRB_TRAPS)
+ ifp->trap_enable = ifc->rb_traps;
+ return (SNMP_ERR_NOERROR);
+
+ }
+ abort();
+}
+
+static u_int32_t
+ticks_get_timeval(struct timeval *tv)
+{
+ u_int32_t v;
+
+ if (tv->tv_sec != 0 || tv->tv_usec != 0) {
+ v = 100 * tv->tv_sec + tv->tv_usec / 10000;
+ if (v > start_tick)
+ return (v - start_tick);
+ }
+ return (0);
+}
+
+/*
+ * Scalars
+ */
+int
+op_interfaces(struct snmp_context *ctx __unused, struct snmp_value *value,
+ u_int sub, u_int idx __unused, enum snmp_op op)
+{
+ switch (op) {
+
+ case SNMP_OP_GETNEXT:
+ abort();
+
+ case SNMP_OP_GET:
+ break;
+
+ case SNMP_OP_SET:
+ return (SNMP_ERR_NOT_WRITEABLE);
+
+ case SNMP_OP_ROLLBACK:
+ case SNMP_OP_COMMIT:
+ abort();
+ }
+
+ switch (value->var.subs[sub - 1]) {
+
+ case LEAF_ifNumber:
+ value->v.integer = mib_if_number;
+ break;
+ }
+ return (SNMP_ERR_NOERROR);
+}
+
+/*
+ * Iftable entry
+ */
+int
+op_ifentry(struct snmp_context *ctx, struct snmp_value *value,
+ u_int sub, u_int iidx __unused, enum snmp_op op)
+{
+ struct mibif *ifp = NULL;
+ int ret;
+ struct ifchange *ifc;
+ struct asn_oid idx;
+
+ switch (op) {
+
+ case SNMP_OP_GETNEXT:
+ if ((ifp = NEXT_OBJECT_INT(&mibif_list, &value->var, sub)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ value->var.len = sub + 1;
+ value->var.subs[sub] = ifp->index;
+ break;
+
+ case SNMP_OP_GET:
+ if (value->var.len - sub != 1)
+ return (SNMP_ERR_NOSUCHNAME);
+ if ((ifp = mib_find_if(value->var.subs[sub])) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ break;
+
+ case SNMP_OP_SET:
+ if (value->var.len - sub != 1)
+ return (SNMP_ERR_NO_CREATION);
+ if ((ifp = mib_find_if(value->var.subs[sub])) == NULL)
+ return (SNMP_ERR_NO_CREATION);
+ if (value->var.subs[sub - 1] != LEAF_ifAdminStatus)
+ return (SNMP_ERR_NOT_WRITEABLE);
+
+ idx.len = 1;
+ idx.subs[0] = ifp->index;
+
+ if (value->v.integer != 1 && value->v.integer != 2)
+ return (SNMP_ERR_WRONG_VALUE);
+
+ if ((ifc = (struct ifchange *)snmp_dep_lookup(ctx,
+ &oid_ifTable, &idx, sizeof(*ifc), ifchange_func)) == NULL)
+ return (SNMP_ERR_RES_UNAVAIL);
+ ifc->ifindex = ifp->index;
+
+ if (ifc->set & IFC_ADMIN)
+ return (SNMP_ERR_INCONS_VALUE);
+ ifc->set |= IFC_ADMIN;
+ ifc->admin = (value->v.integer == 1) ? 1 : 0;
+
+ return (SNMP_ERR_NOERROR);
+
+ case SNMP_OP_ROLLBACK:
+ case SNMP_OP_COMMIT:
+ return (SNMP_ERR_NOERROR);
+ }
+
+ if (ifp->mibtick < this_tick)
+ (void)mib_fetch_ifmib(ifp);
+
+ ret = SNMP_ERR_NOERROR;
+ switch (value->var.subs[sub - 1]) {
+
+ case LEAF_ifIndex:
+ value->v.integer = ifp->index;
+ break;
+
+ case LEAF_ifDescr:
+ ret = string_get(value, ifp->descr, -1);
+ break;
+
+ case LEAF_ifType:
+ value->v.integer = ifp->mib.ifmd_data.ifi_type;
+ break;
+
+ case LEAF_ifMtu:
+ value->v.integer = ifp->mib.ifmd_data.ifi_mtu;
+ break;
+
+ case LEAF_ifSpeed:
+ value->v.integer = ifp->mib.ifmd_data.ifi_baudrate;
+ break;
+
+ case LEAF_ifPhysAddress:
+ ret = string_get(value, ifp->physaddr,
+ ifp->physaddrlen);
+ break;
+
+ case LEAF_ifAdminStatus:
+ value->v.integer =
+ (ifp->mib.ifmd_flags & IFF_UP) ? 1 : 2;
+ break;
+
+ case LEAF_ifOperStatus:
+ value->v.integer =
+ (ifp->mib.ifmd_flags & IFF_RUNNING) ? 1 : 2;
+ break;
+
+ case LEAF_ifLastChange:
+ value->v.uint32 =
+ ticks_get_timeval(&ifp->mib.ifmd_data.ifi_lastchange);
+ break;
+
+ case LEAF_ifInOctets:
+ value->v.uint32 = ifp->mib.ifmd_data.ifi_ibytes;
+ break;
+
+ case LEAF_ifInUcastPkts:
+ value->v.uint32 = ifp->mib.ifmd_data.ifi_ipackets -
+ ifp->mib.ifmd_data.ifi_imcasts;
+ break;
+
+ case LEAF_ifInNUcastPkts:
+ value->v.uint32 = ifp->mib.ifmd_data.ifi_imcasts;
+ break;
+
+ case LEAF_ifInDiscards:
+ value->v.uint32 = ifp->mib.ifmd_data.ifi_iqdrops;
+ break;
+
+ case LEAF_ifInErrors:
+ value->v.uint32 = ifp->mib.ifmd_data.ifi_ierrors;
+ break;
+
+ case LEAF_ifInUnknownProtos:
+ value->v.uint32 = ifp->mib.ifmd_data.ifi_noproto;
+ break;
+
+ case LEAF_ifOutOctets:
+ value->v.uint32 = ifp->mib.ifmd_data.ifi_obytes;
+ break;
+
+ case LEAF_ifOutUcastPkts:
+ value->v.uint32 = ifp->mib.ifmd_data.ifi_opackets -
+ ifp->mib.ifmd_data.ifi_omcasts;
+ break;
+
+ case LEAF_ifOutNUcastPkts:
+ value->v.uint32 = ifp->mib.ifmd_data.ifi_omcasts;
+ break;
+
+ case LEAF_ifOutDiscards:
+ value->v.uint32 = ifp->mib.ifmd_snd_drops;
+ break;
+
+ case LEAF_ifOutErrors:
+ value->v.uint32 = ifp->mib.ifmd_data.ifi_oerrors;
+ break;
+
+ case LEAF_ifOutQLen:
+ value->v.uint32 = ifp->mib.ifmd_snd_len;
+ break;
+
+ case LEAF_ifSpecific:
+ value->v.oid = oid_zeroDotZero;
+ break;
+ }
+ return (SNMP_ERR_NOERROR);
+}
+
+/*
+ * IfXtable entry
+ */
+int
+op_ifxtable(struct snmp_context *ctx, struct snmp_value *value,
+ u_int sub, u_int iidx __unused, enum snmp_op op)
+{
+ struct mibif *ifp = NULL;
+ int ret;
+ struct ifchange *ifc;
+ struct asn_oid idx;
+
+ switch (op) {
+
+ again:
+ if (op != SNMP_OP_GETNEXT)
+ return (SNMP_ERR_NOSUCHNAME);
+ /* FALLTHROUGH */
+
+ case SNMP_OP_GETNEXT:
+ if ((ifp = NEXT_OBJECT_INT(&mibif_list, &value->var, sub)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ value->var.len = sub + 1;
+ value->var.subs[sub] = ifp->index;
+ break;
+
+ case SNMP_OP_GET:
+ if (value->var.len - sub != 1)
+ return (SNMP_ERR_NOSUCHNAME);
+ if ((ifp = mib_find_if(value->var.subs[sub])) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ break;
+
+ case SNMP_OP_SET:
+ if (value->var.len - sub != 1)
+ return (SNMP_ERR_NO_CREATION);
+ if ((ifp = mib_find_if(value->var.subs[sub])) == NULL)
+ return (SNMP_ERR_NO_CREATION);
+
+ idx.len = 1;
+ idx.subs[0] = ifp->index;
+
+ if ((ifc = (struct ifchange *)snmp_dep_lookup(ctx,
+ &oid_ifTable, &idx, sizeof(*ifc), ifchange_func)) == NULL)
+ return (SNMP_ERR_RES_UNAVAIL);
+ ifc->ifindex = ifp->index;
+
+ switch (value->var.subs[sub - 1]) {
+
+ case LEAF_ifLinkUpDownTrapEnable:
+ if (value->v.integer != 1 && value->v.integer != 2)
+ return (SNMP_ERR_WRONG_VALUE);
+ if (ifc->set & IFC_TRAPS)
+ return (SNMP_ERR_INCONS_VALUE);
+ ifc->set |= IFC_TRAPS;
+ ifc->traps = (value->v.integer == 1) ? 1 : 0;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_ifPromiscuousMode:
+ if (value->v.integer != 1 && value->v.integer != 2)
+ return (SNMP_ERR_WRONG_VALUE);
+ if (ifc->set & IFC_PROMISC)
+ return (SNMP_ERR_INCONS_VALUE);
+ ifc->set |= IFC_PROMISC;
+ ifc->promisc = (value->v.integer == 1) ? 1 : 0;
+ return (SNMP_ERR_NOERROR);
+ }
+ return (SNMP_ERR_NOT_WRITEABLE);
+
+ case SNMP_OP_ROLLBACK:
+ case SNMP_OP_COMMIT:
+ return (SNMP_ERR_NOERROR);
+ }
+
+ if (ifp->mibtick < this_tick)
+ (void)mib_fetch_ifmib(ifp);
+
+ ret = SNMP_ERR_NOERROR;
+ switch (value->var.subs[sub - 1]) {
+
+ case LEAF_ifName:
+ ret = string_get(value, ifp->name, -1);
+ break;
+
+ case LEAF_ifInMulticastPkts:
+ value->v.uint32 = ifp->mib.ifmd_data.ifi_imcasts;
+ break;
+
+ case LEAF_ifInBroadcastPkts:
+ value->v.uint32 = 0;
+ break;
+
+ case LEAF_ifOutMulticastPkts:
+ value->v.uint32 = ifp->mib.ifmd_data.ifi_omcasts;
+ break;
+
+ case LEAF_ifOutBroadcastPkts:
+ value->v.uint32 = 0;
+ break;
+
+ case LEAF_ifHCInOctets:
+ if (!(ifp->flags & MIBIF_HIGHSPEED))
+ goto again;
+ value->v.counter64 = ifp->hc_inoctets;
+ break;
+
+ case LEAF_ifHCInUcastPkts:
+ if (!(ifp->flags & (MIBIF_VERYHIGHSPEED|MIBIF_HIGHSPEED)))
+ goto again;
+ value->v.counter64 = ifp->hc_ipackets - ifp->hc_imcasts;
+ break;
+
+ case LEAF_ifHCInMulticastPkts:
+ if (!(ifp->flags & (MIBIF_VERYHIGHSPEED|MIBIF_HIGHSPEED)))
+ goto again;
+ value->v.counter64 = ifp->hc_imcasts;
+ break;
+
+ case LEAF_ifHCInBroadcastPkts:
+ if (!(ifp->flags & (MIBIF_VERYHIGHSPEED|MIBIF_HIGHSPEED)))
+ goto again;
+ value->v.counter64 = 0;
+ break;
+
+ case LEAF_ifHCOutOctets:
+ if (!(ifp->flags & MIBIF_HIGHSPEED))
+ goto again;
+ value->v.counter64 = ifp->hc_inoctets;
+ break;
+
+ case LEAF_ifHCOutUcastPkts:
+ if (!(ifp->flags & (MIBIF_VERYHIGHSPEED|MIBIF_HIGHSPEED)))
+ goto again;
+ value->v.counter64 = ifp->hc_opackets - ifp->hc_omcasts;
+ break;
+
+ case LEAF_ifHCOutMulticastPkts:
+ if (!(ifp->flags & (MIBIF_VERYHIGHSPEED|MIBIF_HIGHSPEED)))
+ goto again;
+ value->v.counter64 = ifp->hc_omcasts;
+ break;
+
+ case LEAF_ifHCOutBroadcastPkts:
+ if (!(ifp->flags & (MIBIF_VERYHIGHSPEED|MIBIF_HIGHSPEED)))
+ goto again;
+ value->v.counter64 = 0;
+ break;
+
+ case LEAF_ifLinkUpDownTrapEnable:
+ value->v.integer = ifp->trap_enable ? 1 : 2;
+ break;
+
+ case LEAF_ifHighSpeed:
+ value->v.integer =
+ (ifp->mib.ifmd_data.ifi_baudrate + 499999) / 1000000;
+ break;
+
+ case LEAF_ifPromiscuousMode:
+ value->v.integer =
+ (ifp->mib.ifmd_flags & IFF_PROMISC) ? 1 : 2;
+ break;
+
+ case LEAF_ifConnectorPresent:
+ value->v.integer = ifp->has_connector ? 1 : 2;
+ break;
+
+ case LEAF_ifAlias:
+ ret = string_get(value, "", -1);
+ break;
+
+ case LEAF_ifCounterDiscontinuityTime:
+ if (ifp->counter_disc > start_tick)
+ value->v.uint32 = ifp->counter_disc - start_tick;
+ else
+ value->v.uint32 = 0;
+ break;
+ }
+ return (ret);
+}
diff --git a/contrib/bsnmp/snmp_mibII/mibII_ip.c b/contrib/bsnmp/snmp_mibII/mibII_ip.c
new file mode 100644
index 0000000..d0d2590
--- /dev/null
+++ b/contrib/bsnmp/snmp_mibII/mibII_ip.c
@@ -0,0 +1,497 @@
+/*
+ * Copyright (c) 2001-2003
+ * Fraunhofer Institute for Open Communication Systems (FhG Fokus).
+ * All rights reserved.
+ *
+ * Author: Harti Brandt <harti@freebsd.org>
+ *
+ * Redistribution of this software and documentation and use in source and
+ * binary forms, with or without modification, are permitted provided that
+ * the following conditions are met:
+ *
+ * 1. Redistributions of source code or documentation must retain the above
+ * copyright notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY FRAUNHOFER FOKUS
+ * AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * FRAUNHOFER FOKUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Begemot: bsnmp/snmp_mibII/mibII_ip.c,v 1.8 2003/01/28 13:44:35 hbb Exp $
+ *
+ * ip group scalars.
+ */
+#include "mibII.h"
+#include "mibII_oid.h"
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/ip_var.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/icmp_var.h>
+
+static struct ipstat ipstat;
+static u_int ip_idrop;
+static struct icmpstat icmpstat;
+
+static int ip_forwarding;
+static int ip_defttl;
+static u_int32_t ip_tick;
+
+static u_int32_t ipstat_tick;
+
+static int
+fetch_ipstat(void)
+{
+ size_t len;
+
+ len = sizeof(ipstat);
+ if (sysctlbyname("net.inet.ip.stats", &ipstat, &len, NULL, 0) == -1) {
+ syslog(LOG_ERR, "net.inet.ip.stats: %m");
+ return (-1);
+ }
+ if (len != sizeof(ipstat)) {
+ syslog(LOG_ERR, "net.inet.ip.stats: wrong size");
+ return (-1);
+ }
+ len = sizeof(ip_idrop);
+ if (sysctlbyname("net.inet.ip.intr_queue_drops", &ip_idrop, &len, NULL, 0) == -1)
+ syslog(LOG_WARNING, "net.inet.ip.intr_queue_drops: %m");
+ if (len != sizeof(ip_idrop)) {
+ syslog(LOG_WARNING, "net.inet.ip.intr_queue_drops: wrong size");
+ ip_idrop = 0;
+ }
+ len = sizeof(icmpstat);
+ if (sysctlbyname("net.inet.icmp.stats", &icmpstat, &len, NULL, 0) == -1) {
+ syslog(LOG_ERR, "net.inet.icmp.stats: %m");
+ return (-1);
+ }
+ if (len != sizeof(icmpstat)) {
+ syslog(LOG_ERR, "net.inet.icmp.stats: wrong size");
+ return (-1);
+ }
+
+ ipstat_tick = get_ticks();
+ return (0);
+}
+
+static int
+fetch_ip(void)
+{
+ size_t len;
+
+ len = sizeof(ip_forwarding);
+ if (sysctlbyname("net.inet.ip.forwarding", &ip_forwarding, &len,
+ NULL, 0) == -1) {
+ syslog(LOG_ERR, "net.inet.ip.forwarding: %m");
+ return (-1);
+ }
+ if (len != sizeof(ip_forwarding)) {
+ syslog(LOG_ERR, "net.inet.ip.forwarding: wrong size");
+ return (-1);
+ }
+
+ len = sizeof(ip_defttl);
+ if (sysctlbyname("net.inet.ip.ttl", &ip_defttl, &len,
+ NULL, 0) == -1) {
+ syslog(LOG_ERR, "net.inet.ip.ttl: %m");
+ return (-1);
+ }
+ if (len != sizeof(ip_defttl)) {
+ syslog(LOG_ERR, "net.inet.ip.ttl: wrong size");
+ return (-1);
+ }
+
+ ip_tick = get_ticks();
+ return (0);
+}
+
+static int
+ip_forward(int forw, int *old)
+{
+ size_t olen;
+
+ olen = sizeof(*old);
+ if (sysctlbyname("net.inet.ip.forwarding", old, old ? &olen : NULL,
+ &forw, sizeof(forw)) == -1) {
+ syslog(LOG_ERR, "set net.inet.ip.forwarding: %m");
+ return (-1);
+ }
+ ip_forwarding = forw;
+ return (0);
+}
+
+static int
+ip_setttl(int ttl, int *old)
+{
+ size_t olen;
+
+ olen = sizeof(*old);
+ if (sysctlbyname("net.inet.ip.ttl", old, old ? &olen : NULL,
+ &ttl, sizeof(ttl)) == -1) {
+ syslog(LOG_ERR, "set net.inet.ip.ttl: %m");
+ return (-1);
+ }
+ ip_defttl = ttl;
+ return (0);
+}
+
+/*
+ * READ/WRITE ip group.
+ */
+int
+op_ip(struct snmp_context *ctx, struct snmp_value *value,
+ u_int sub, u_int idx __unused, enum snmp_op op)
+{
+ int old;
+
+ switch (op) {
+
+ case SNMP_OP_GETNEXT:
+ abort();
+
+ case SNMP_OP_GET:
+ break;
+
+ case SNMP_OP_SET:
+ if (ip_tick < this_tick)
+ if (fetch_ip() == -1)
+ return (SNMP_ERR_GENERR);
+
+ switch (value->var.subs[sub - 1]) {
+
+ case LEAF_ipForwarding:
+ ctx->scratch->int1 = ip_forwarding ? 1 : 2;
+ ctx->scratch->int2 = value->v.integer;
+ if (value->v.integer == 1) {
+ if (!ip_forwarding && ip_forward(1, &old))
+ return (SNMP_ERR_GENERR);
+ ctx->scratch->int1 = old ? 1 : 2;
+ } else if (value->v.integer == 2) {
+ if (ip_forwarding && ip_forward(0, &old))
+ return (SNMP_ERR_GENERR);
+ ctx->scratch->int1 = old;
+ } else
+ return (SNMP_ERR_WRONG_VALUE);
+ break;
+
+ case LEAF_ipDefaultTTL:
+ ctx->scratch->int1 = ip_defttl;
+ ctx->scratch->int2 = value->v.integer;
+ if (value->v.integer < 1 || value->v.integer > 255)
+ return (SNMP_ERR_WRONG_VALUE);
+ if (ip_defttl != value->v.integer &&
+ ip_setttl(value->v.integer, &old))
+ return (SNMP_ERR_GENERR);
+ ctx->scratch->int1 = old;
+ break;
+ }
+ return (SNMP_ERR_NOERROR);
+
+ case SNMP_OP_ROLLBACK:
+ switch (value->var.subs[sub - 1]) {
+
+ case LEAF_ipForwarding:
+ if (ctx->scratch->int1 == 1) {
+ if (ctx->scratch->int2 == 2)
+ (void)ip_forward(1, NULL);
+ } else {
+ if (ctx->scratch->int2 == 1)
+ (void)ip_forward(0, NULL);
+ }
+ break;
+
+ case LEAF_ipDefaultTTL:
+ if (ctx->scratch->int1 != ctx->scratch->int2)
+ (void)ip_setttl(ctx->scratch->int1, NULL);
+ break;
+ }
+ return (SNMP_ERR_NOERROR);
+
+ case SNMP_OP_COMMIT:
+ return (SNMP_ERR_NOERROR);
+ }
+
+ if (ip_tick < this_tick)
+ if (fetch_ip() == -1)
+ return (SNMP_ERR_GENERR);
+
+ switch (value->var.subs[sub - 1]) {
+
+ case LEAF_ipForwarding:
+ value->v.integer = ip_forwarding ? 1 : 2;
+ break;
+
+ case LEAF_ipDefaultTTL:
+ value->v.integer = ip_defttl;
+ break;
+ }
+ return (SNMP_ERR_NOERROR);
+}
+
+/*
+ * READ-ONLY statistics ip group.
+ */
+int
+op_ipstat(struct snmp_context *ctx __unused, struct snmp_value *value,
+ u_int sub, u_int idx __unused, enum snmp_op op)
+{
+ switch (op) {
+
+ case SNMP_OP_GETNEXT:
+ abort();
+
+ case SNMP_OP_GET:
+ break;
+
+ case SNMP_OP_SET:
+ return (SNMP_ERR_NOT_WRITEABLE);
+
+ case SNMP_OP_ROLLBACK:
+ case SNMP_OP_COMMIT:
+ abort();
+ }
+
+ if (ipstat_tick < this_tick)
+ fetch_ipstat();
+
+ switch (value->var.subs[sub - 1]) {
+
+ case LEAF_ipInReceives:
+ value->v.uint32 = ipstat.ips_total;
+ break;
+
+ case LEAF_ipInHdrErrors:
+ value->v.uint32 = ipstat.ips_badsum + ipstat.ips_tooshort
+ + ipstat.ips_toosmall + ipstat.ips_badhlen
+ + ipstat.ips_badlen + ipstat.ips_badvers +
+ + ipstat.ips_toolong;
+ break;
+
+ case LEAF_ipInAddrErrors:
+ value->v.uint32 = ipstat.ips_cantforward;
+ break;
+
+ case LEAF_ipForwDatagrams:
+ value->v.uint32 = ipstat.ips_forward;
+ break;
+
+ case LEAF_ipInUnknownProtos:
+ value->v.uint32 = ipstat.ips_noproto;
+ break;
+
+ case LEAF_ipInDiscards:
+ value->v.uint32 = ip_idrop;
+ break;
+
+ case LEAF_ipInDelivers:
+ value->v.uint32 = ipstat.ips_delivered;
+ break;
+
+ case LEAF_ipOutRequests:
+ value->v.uint32 = ipstat.ips_localout;
+ break;
+
+ case LEAF_ipOutDiscards:
+ value->v.uint32 = ipstat.ips_odropped;
+ break;
+
+ case LEAF_ipOutNoRoutes:
+ value->v.uint32 = ipstat.ips_noroute;
+ break;
+
+ case LEAF_ipReasmTimeout:
+ value->v.integer = IPFRAGTTL;
+ break;
+
+ case LEAF_ipReasmReqds:
+ value->v.uint32 = ipstat.ips_fragments;
+ break;
+
+ case LEAF_ipReasmOKs:
+ value->v.uint32 = ipstat.ips_reassembled;
+ break;
+
+ case LEAF_ipReasmFails:
+ value->v.uint32 = ipstat.ips_fragdropped
+ + ipstat.ips_fragtimeout;
+ break;
+
+ case LEAF_ipFragOKs:
+ value->v.uint32 = ipstat.ips_fragmented;
+ break;
+
+ case LEAF_ipFragFails:
+ value->v.uint32 = ipstat.ips_cantfrag;
+ break;
+
+ case LEAF_ipFragCreates:
+ value->v.uint32 = ipstat.ips_ofragments;
+ break;
+ }
+ return (SNMP_ERR_NOERROR);
+}
+
+/*
+ * READ-ONLY statistics icmp group.
+ */
+int
+op_icmpstat(struct snmp_context *ctx __unused, struct snmp_value *value,
+ u_int sub, u_int idx __unused, enum snmp_op op)
+{
+ u_int i;
+
+ switch (op) {
+
+ case SNMP_OP_GETNEXT:
+ abort();
+
+ case SNMP_OP_GET:
+ break;
+
+ case SNMP_OP_SET:
+ return (SNMP_ERR_NOT_WRITEABLE);
+
+ case SNMP_OP_ROLLBACK:
+ case SNMP_OP_COMMIT:
+ abort();
+ }
+
+ if (ipstat_tick < this_tick)
+ fetch_ipstat();
+
+ switch (value->var.subs[sub - 1]) {
+
+ case LEAF_icmpInMsgs:
+ value->v.integer = 0;
+ for (i = 0; i <= ICMP_MAXTYPE; i++)
+ value->v.integer += icmpstat.icps_inhist[i];
+ value->v.integer += icmpstat.icps_tooshort +
+ icmpstat.icps_checksum;
+ /* missing: bad type and packets on faith */
+ break;
+
+ case LEAF_icmpInErrors:
+ value->v.integer = icmpstat.icps_tooshort +
+ icmpstat.icps_checksum +
+ icmpstat.icps_badlen +
+ icmpstat.icps_badcode +
+ icmpstat.icps_bmcastecho +
+ icmpstat.icps_bmcasttstamp;
+ break;
+
+ case LEAF_icmpInDestUnreachs:
+ value->v.integer = icmpstat.icps_inhist[ICMP_UNREACH];
+ break;
+
+ case LEAF_icmpInTimeExcds:
+ value->v.integer = icmpstat.icps_inhist[ICMP_TIMXCEED];
+ break;
+
+ case LEAF_icmpInParmProbs:
+ value->v.integer = icmpstat.icps_inhist[ICMP_PARAMPROB];
+ break;
+
+ case LEAF_icmpInSrcQuenchs:
+ value->v.integer = icmpstat.icps_inhist[ICMP_SOURCEQUENCH];
+ break;
+
+ case LEAF_icmpInRedirects:
+ value->v.integer = icmpstat.icps_inhist[ICMP_REDIRECT];
+ break;
+
+ case LEAF_icmpInEchos:
+ value->v.integer = icmpstat.icps_inhist[ICMP_ECHO];
+ break;
+
+ case LEAF_icmpInEchoReps:
+ value->v.integer = icmpstat.icps_inhist[ICMP_ECHOREPLY];
+ break;
+
+ case LEAF_icmpInTimestamps:
+ value->v.integer = icmpstat.icps_inhist[ICMP_TSTAMP];
+ break;
+
+ case LEAF_icmpInTimestampReps:
+ value->v.integer = icmpstat.icps_inhist[ICMP_TSTAMPREPLY];
+ break;
+
+ case LEAF_icmpInAddrMasks:
+ value->v.integer = icmpstat.icps_inhist[ICMP_MASKREQ];
+ break;
+
+ case LEAF_icmpInAddrMaskReps:
+ value->v.integer = icmpstat.icps_inhist[ICMP_MASKREPLY];
+ break;
+
+ case LEAF_icmpOutMsgs:
+ value->v.integer = 0;
+ for (i = 0; i <= ICMP_MAXTYPE; i++)
+ value->v.integer += icmpstat.icps_outhist[i];
+ value->v.integer += icmpstat.icps_badaddr +
+ icmpstat.icps_noroute;
+ break;
+
+ case LEAF_icmpOutErrors:
+ value->v.integer = icmpstat.icps_badaddr +
+ icmpstat.icps_noroute;
+ break;
+
+ case LEAF_icmpOutDestUnreachs:
+ value->v.integer = icmpstat.icps_outhist[ICMP_UNREACH];
+ break;
+
+ case LEAF_icmpOutTimeExcds:
+ value->v.integer = icmpstat.icps_outhist[ICMP_TIMXCEED];
+ break;
+
+ case LEAF_icmpOutParmProbs:
+ value->v.integer = icmpstat.icps_outhist[ICMP_PARAMPROB];
+ break;
+
+ case LEAF_icmpOutSrcQuenchs:
+ value->v.integer = icmpstat.icps_outhist[ICMP_SOURCEQUENCH];
+ break;
+
+ case LEAF_icmpOutRedirects:
+ value->v.integer = icmpstat.icps_outhist[ICMP_REDIRECT];
+ break;
+
+ case LEAF_icmpOutEchos:
+ value->v.integer = icmpstat.icps_outhist[ICMP_ECHO];
+ break;
+
+ case LEAF_icmpOutEchoReps:
+ value->v.integer = icmpstat.icps_outhist[ICMP_ECHOREPLY];
+ break;
+
+ case LEAF_icmpOutTimestamps:
+ value->v.integer = icmpstat.icps_outhist[ICMP_TSTAMP];
+ break;
+
+ case LEAF_icmpOutTimestampReps:
+ value->v.integer = icmpstat.icps_outhist[ICMP_TSTAMPREPLY];
+ break;
+
+ case LEAF_icmpOutAddrMasks:
+ value->v.integer = icmpstat.icps_outhist[ICMP_MASKREQ];
+ break;
+
+ case LEAF_icmpOutAddrMaskReps:
+ value->v.integer = icmpstat.icps_outhist[ICMP_MASKREPLY];
+ break;
+ }
+ return (SNMP_ERR_NOERROR);
+}
diff --git a/contrib/bsnmp/snmp_mibII/mibII_ipaddr.c b/contrib/bsnmp/snmp_mibII/mibII_ipaddr.c
new file mode 100644
index 0000000..0cc5936
--- /dev/null
+++ b/contrib/bsnmp/snmp_mibII/mibII_ipaddr.c
@@ -0,0 +1,351 @@
+/*
+ * Copyright (c) 2001-2002
+ * Fraunhofer Institute for Open Communication Systems (FhG Fokus).
+ * All rights reserved.
+ *
+ * Author: Harti Brandt <harti@freebsd.org>
+ *
+ * Redistribution of this software and documentation and use in source and
+ * binary forms, with or without modification, are permitted provided that
+ * the following conditions are met:
+ *
+ * 1. Redistributions of source code or documentation must retain the above
+ * copyright notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY FRAUNHOFER FOKUS
+ * AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * FRAUNHOFER FOKUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Begemot: bsnmp/snmp_mibII/mibII_ipaddr.c,v 1.6 2003/01/28 13:44:35 hbb Exp $
+ *
+ * IP address table. This table is writeable!
+ *
+ * Writing to this table will add a new IP address to the interface.
+ * An address can be deleted with writing the interface index 0.
+ */
+#include "mibII.h"
+#include "mibII_oid.h"
+
+static const struct asn_oid
+ oid_ipAddrTable = OIDX_ipAddrTable;
+
+/*
+ * Be careful not to hold any pointers during the SET processing - the
+ * interface and address lists can be relocated at any time.
+ */
+struct update {
+ struct snmp_dependency dep;
+
+ u_int32_t set;
+ struct in_addr addr;
+ struct in_addr mask;
+ int bcast;
+ u_int ifindex;
+
+ u_int32_t rb;
+ struct in_addr rb_mask;
+ struct in_addr rb_bcast;
+};
+#define UPD_IFINDEX 0x0001
+#define UPD_MASK 0x0002
+#define UPD_BCAST 0x0004
+#define RB_CREATE 0x0001
+#define RB_DESTROY 0x0002
+#define RB_MODIFY 0x0004
+
+/*
+ * Create a new interface address
+ */
+static int
+create(struct update *upd)
+{
+ struct in_addr bcast;
+ struct mibifa *ifa;
+
+ if (!(upd->set & UPD_MASK)) {
+ if (IN_CLASSA(ntohl(upd->addr.s_addr)))
+ upd->mask.s_addr = htonl(IN_CLASSA_NET);
+ else if (IN_CLASSB(ntohl(upd->addr.s_addr)))
+ upd->mask.s_addr = htonl(IN_CLASSB_NET);
+ else if (IN_CLASSC(ntohl(upd->addr.s_addr)))
+ upd->mask.s_addr = htonl(IN_CLASSC_NET);
+ else
+ upd->mask.s_addr = 0xffffffff;
+ }
+
+ bcast.s_addr = upd->addr.s_addr & upd->mask.s_addr;
+ if (!(upd->set & UPD_BCAST) || upd->bcast)
+ bcast.s_addr |= htonl(0xffffffff & ~ntohl(upd->mask.s_addr));
+
+ if ((ifa = mib_create_ifa(upd->ifindex, upd->addr, upd->mask, bcast)) == NULL)
+ return (SNMP_ERR_GENERR);
+
+ upd->rb |= RB_CREATE;
+ return (SNMP_ERR_NOERROR);
+}
+
+/*
+ * Modify the netmask or broadcast address. The ifindex cannot be
+ * changed (obviously).
+ */
+static int
+modify(struct update *upd, struct mibifa *ifa)
+{
+ struct mibif *ifp;
+
+ if ((ifp = mib_find_if(ifa->ifindex)) == NULL)
+ return (SNMP_ERR_WRONG_VALUE);
+ if ((upd->set & UPD_IFINDEX) && upd->ifindex != ifa->ifindex)
+ return (SNMP_ERR_INCONS_VALUE);
+
+ upd->rb_mask = ifa->inmask;
+ upd->rb_bcast = ifa->inbcast;
+ if (((upd->set & UPD_MASK) && upd->mask.s_addr != ifa->inmask.s_addr) ||
+ (upd->set & UPD_BCAST)) {
+ if (upd->set & UPD_MASK)
+ ifa->inmask = upd->mask;
+ if (upd->set & UPD_BCAST) {
+ ifa->inbcast.s_addr = ifa->inaddr.s_addr
+ & ifa->inmask.s_addr;
+ if (upd->bcast)
+ ifa->inbcast.s_addr |= 0xffffffff
+ & ~ifa->inmask.s_addr;
+ }
+ if (mib_modify_ifa(ifa)) {
+ syslog(LOG_ERR, "set netmask/bcast: %m");
+ ifa->inmask = upd->rb_mask;
+ ifa->inbcast = upd->rb_bcast;
+ mib_unmodify_ifa(ifa);
+ return (SNMP_ERR_GENERR);
+ }
+ upd->rb |= RB_MODIFY;
+ }
+ return (SNMP_ERR_NOERROR);
+}
+
+/*
+ * Remove an IP address from an interface. This is called when
+ * the SET finishes.
+ */
+static void
+destroy_func(struct snmp_context *ctx __unused, int fail __unused, void *arg)
+{
+ struct mibifa *ifa = arg;
+
+ if (ifa->flags & MIBIFA_DESTROYED) {
+ TAILQ_REMOVE(&mibifa_list, ifa, link);
+ free(ifa);
+ }
+}
+
+/*
+ * Destroy the given row in the table. We remove the address from the
+ * system, but keep the structure around for the COMMIT. It's deleted
+ * only in the finish function.
+ */
+static int
+destroy(struct snmp_context *ctx, struct update *upd, struct mibifa *ifa)
+{
+ if (mib_destroy_ifa(ifa))
+ return (SNMP_ERR_GENERR);
+ if (snmp_set_atfinish(ctx, destroy_func, ifa)) {
+ syslog(LOG_ERR, "atfinish: %m");
+ mib_undestroy_ifa(ifa);
+ return (SNMP_ERR_GENERR);
+ }
+ upd->rb |= RB_DESTROY;
+ return (SNMP_ERR_NOERROR);
+}
+
+/*
+ * This function is called to commit/rollback a SET on an IpAddrEntry
+ */
+static int
+update_func(struct snmp_context *ctx, struct snmp_dependency *dep,
+ enum snmp_depop op)
+{
+ struct update *upd = (struct update *)dep;
+ struct mibifa *ifa;
+
+ switch (op) {
+
+ case SNMP_DEPOP_COMMIT:
+ if ((ifa = mib_find_ifa(upd->addr)) == NULL) {
+ /* non existing entry - must have ifindex */
+ if (!(upd->set & UPD_IFINDEX))
+ return (SNMP_ERR_INCONS_NAME);
+ return (create(upd));
+ }
+ /* existing entry */
+ if ((upd->set & UPD_IFINDEX) && upd->ifindex == 0) {
+ /* delete */
+ return (destroy(ctx, upd, ifa));
+ }
+ /* modify entry */
+ return (modify(upd, ifa));
+
+ case SNMP_DEPOP_ROLLBACK:
+ if ((ifa = mib_find_ifa(upd->addr)) == NULL) {
+ /* ups */
+ mib_iflist_bad = 1;
+ return (SNMP_ERR_NOERROR);
+ }
+ if (upd->rb & RB_CREATE) {
+ mib_uncreate_ifa(ifa);
+ return (SNMP_ERR_NOERROR);
+ }
+ if (upd->rb & RB_DESTROY) {
+ mib_undestroy_ifa(ifa);
+ return (SNMP_ERR_NOERROR);
+ }
+ if (upd->rb & RB_MODIFY) {
+ ifa->inmask = upd->rb_mask;
+ ifa->inbcast = upd->rb_bcast;
+ mib_unmodify_ifa(ifa);
+ return (SNMP_ERR_NOERROR);
+ }
+ return (SNMP_ERR_NOERROR);
+ }
+ abort();
+}
+
+/**********************************************************************/
+/*
+ * ACTION
+ */
+int
+op_ipaddr(struct snmp_context *ctx, struct snmp_value *value,
+ u_int sub, u_int iidx, enum snmp_op op)
+{
+ asn_subid_t which;
+ struct mibifa *ifa;
+ struct update *upd;
+ struct asn_oid idx;
+ u_char ipaddr[4];
+
+ which = value->var.subs[sub - 1];
+
+ ifa = NULL;
+
+ switch (op) {
+
+ case SNMP_OP_GETNEXT:
+ if ((ifa = NEXT_OBJECT_OID(&mibifa_list, &value->var, sub)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ index_append(&value->var, sub, &ifa->index);
+ break;
+
+ case SNMP_OP_GET:
+ if ((ifa = FIND_OBJECT_OID(&mibifa_list, &value->var, sub)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ break;
+
+ case SNMP_OP_SET:
+ if (index_decode(&value->var, sub, iidx, ipaddr))
+ return (SNMP_ERR_NO_CREATION);
+ ifa = FIND_OBJECT_OID(&mibifa_list, &value->var, sub);
+ idx.len = 4;
+ idx.subs[0] = ipaddr[0];
+ idx.subs[1] = ipaddr[1];
+ idx.subs[2] = ipaddr[2];
+ idx.subs[3] = ipaddr[3];
+
+ if ((upd = (struct update *)snmp_dep_lookup(ctx,
+ &oid_ipAddrTable, &idx, sizeof(*upd), update_func)) == NULL)
+ return (SNMP_ERR_RES_UNAVAIL);
+
+ upd->addr.s_addr = htonl((ipaddr[0] << 24) | (ipaddr[1] << 16) |
+ (ipaddr[2] << 8) | (ipaddr[3] << 0));
+
+ switch (which) {
+
+ case LEAF_ipAdEntIfIndex:
+ if (upd->set & UPD_IFINDEX)
+ return (SNMP_ERR_INCONS_VALUE);
+ if (value->v.integer < 0 ||
+ value->v.integer > 0x07fffffff)
+ return (SNMP_ERR_WRONG_VALUE);
+ if (ifa != NULL) {
+ if (ifa->ifindex != (u_int)value->v.integer &&
+ value->v.integer != 0)
+ return (SNMP_ERR_INCONS_VALUE);
+ }
+ upd->set |= UPD_IFINDEX;
+ upd->ifindex = (u_int)value->v.integer;
+ break;
+
+ case LEAF_ipAdEntNetMask:
+ if (upd->set & UPD_MASK)
+ return (SNMP_ERR_INCONS_VALUE);
+ upd->mask.s_addr = htonl((value->v.ipaddress[0] << 24)
+ | (value->v.ipaddress[1] << 16)
+ | (value->v.ipaddress[2] << 8)
+ | (value->v.ipaddress[3] << 0));
+ upd->set |= UPD_MASK;
+ break;
+
+ case LEAF_ipAdEntBcastAddr:
+ if (upd->set & UPD_BCAST)
+ return (SNMP_ERR_INCONS_VALUE);
+ if (value->v.integer != 0 && value->v.integer != 1)
+ return (SNMP_ERR_WRONG_VALUE);
+ upd->bcast = value->v.integer;
+ upd->set |= UPD_BCAST;
+ break;
+
+ }
+ return (SNMP_ERR_NOERROR);
+
+ case SNMP_OP_ROLLBACK:
+ case SNMP_OP_COMMIT:
+ return (SNMP_ERR_NOERROR);
+ }
+
+ switch (which) {
+
+ case LEAF_ipAdEntAddr:
+ value->v.ipaddress[0] = ifa->index.subs[0];
+ value->v.ipaddress[1] = ifa->index.subs[1];
+ value->v.ipaddress[2] = ifa->index.subs[2];
+ value->v.ipaddress[3] = ifa->index.subs[3];
+ break;
+
+ case LEAF_ipAdEntIfIndex:
+ if (ifa->flags & MIBIFA_DESTROYED)
+ value->v.integer = 0;
+ else
+ value->v.integer = ifa->ifindex;
+ break;
+
+ case LEAF_ipAdEntNetMask:
+ value->v.ipaddress[0] = (ntohl(ifa->inmask.s_addr) >> 24) & 0xff;
+ value->v.ipaddress[1] = (ntohl(ifa->inmask.s_addr) >> 16) & 0xff;
+ value->v.ipaddress[2] = (ntohl(ifa->inmask.s_addr) >> 8) & 0xff;
+ value->v.ipaddress[3] = (ntohl(ifa->inmask.s_addr) >> 0) & 0xff;
+ break;
+
+ case LEAF_ipAdEntBcastAddr:
+ value->v.integer = ntohl(ifa->inbcast.s_addr) & 1;
+ break;
+
+
+ case LEAF_ipAdEntReasmMaxSize:
+ value->v.integer = 65535;
+ break;
+ }
+ return (SNMP_ERR_NOERROR);
+}
diff --git a/contrib/bsnmp/snmp_mibII/mibII_nettomedia.c b/contrib/bsnmp/snmp_mibII/mibII_nettomedia.c
new file mode 100644
index 0000000..e8a7919
--- /dev/null
+++ b/contrib/bsnmp/snmp_mibII/mibII_nettomedia.c
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 2001-2003
+ * Fraunhofer Institute for Open Communication Systems (FhG Fokus).
+ * All rights reserved.
+ *
+ * Author: Harti Brandt <harti@freebsd.org>
+ *
+ * Redistribution of this software and documentation and use in source and
+ * binary forms, with or without modification, are permitted provided that
+ * the following conditions are met:
+ *
+ * 1. Redistributions of source code or documentation must retain the above
+ * copyright notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY FRAUNHOFER FOKUS
+ * AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * FRAUNHOFER FOKUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Begemot: bsnmp/snmp_mibII/mibII_nettomedia.c,v 1.6 2003/01/28 13:44:35 hbb Exp $
+ *
+ * Read-only implementation of the Arp table (ipNetToMediaTable)
+ *
+ * The problem with the table is, that we don't receive routing message
+ * when a) an arp table entry is resolved and b) when an arp table entry is
+ * deleted automatically. Therefor we need to poll the table from time to
+ * time.
+ */
+#include "mibII.h"
+#include "mibII_oid.h"
+
+#define ARPREFRESH 30
+
+struct mibarp *
+mib_find_arp(const struct mibif *ifp, struct in_addr in)
+{
+ struct mibarp *at;
+ u_int32_t a = ntohl(in.s_addr);
+
+ if (get_ticks() >= mibarpticks + ARPREFRESH)
+ mib_arp_update();
+
+ TAILQ_FOREACH(at, &mibarp_list, link)
+ if (at->index.subs[0] == ifp->index &&
+ (at->index.subs[1] == ((a >> 24) & 0xff)) &&
+ (at->index.subs[2] == ((a >> 16) & 0xff)) &&
+ (at->index.subs[3] == ((a >> 8) & 0xff)) &&
+ (at->index.subs[4] == ((a >> 0) & 0xff)))
+ return (at);
+ return (NULL);
+}
+
+struct mibarp *
+mib_arp_create(const struct mibif *ifp, struct in_addr in, const u_char *phys,
+ size_t physlen)
+{
+ struct mibarp *at;
+ u_int32_t a = ntohl(in.s_addr);
+
+ if ((at = malloc(sizeof(*at))) == NULL)
+ return (NULL);
+ at->flags = 0;
+
+ at->index.len = 5;
+ at->index.subs[0] = ifp->index;
+ at->index.subs[1] = (a >> 24) & 0xff;
+ at->index.subs[2] = (a >> 16) & 0xff;
+ at->index.subs[3] = (a >> 8) & 0xff;
+ at->index.subs[4] = (a >> 0) & 0xff;
+ if ((at->physlen = physlen) > sizeof(at->phys))
+ at->physlen = sizeof(at->phys);
+ memcpy(at->phys, phys, at->physlen);
+
+ INSERT_OBJECT_OID(at, &mibarp_list);
+
+ return (at);
+}
+
+void
+mib_arp_delete(struct mibarp *at)
+{
+ TAILQ_REMOVE(&mibarp_list, at, link);
+ free(at);
+}
+
+int
+op_nettomedia(struct snmp_context *ctx __unused, struct snmp_value *value,
+ u_int sub, u_int iidx __unused, enum snmp_op op)
+{
+ struct mibarp *at;
+
+ at = NULL; /* gcc */
+
+ if (get_ticks() >= mibarpticks + ARPREFRESH)
+ mib_arp_update();
+
+ switch (op) {
+
+ case SNMP_OP_GETNEXT:
+ if ((at = NEXT_OBJECT_OID(&mibarp_list, &value->var, sub)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ index_append(&value->var, sub, &at->index);
+ break;
+
+ case SNMP_OP_GET:
+ if ((at = FIND_OBJECT_OID(&mibarp_list, &value->var, sub)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ break;
+
+ case SNMP_OP_SET:
+ if ((at = FIND_OBJECT_OID(&mibarp_list, &value->var, sub)) == NULL)
+ return (SNMP_ERR_NO_CREATION);
+ return (SNMP_ERR_NOT_WRITEABLE);
+
+ case SNMP_OP_ROLLBACK:
+ case SNMP_OP_COMMIT:
+ abort();
+ }
+
+ switch (value->var.subs[sub - 1]) {
+
+ case LEAF_ipNetToMediaIfIndex:
+ value->v.integer = at->index.subs[0];
+ break;
+
+ case LEAF_ipNetToMediaPhysAddress:
+ return (string_get(value, at->phys, at->physlen));
+
+ case LEAF_ipNetToMediaNetAddress:
+ value->v.ipaddress[0] = at->index.subs[1];
+ value->v.ipaddress[1] = at->index.subs[2];
+ value->v.ipaddress[2] = at->index.subs[3];
+ value->v.ipaddress[3] = at->index.subs[4];
+ break;
+
+ case LEAF_ipNetToMediaType:
+ value->v.integer = (at->flags & MIBARP_PERM) ? 4 : 3;
+ break;
+ }
+ return (SNMP_ERR_NOERROR);
+}
diff --git a/contrib/bsnmp/snmp_mibII/mibII_rcvaddr.c b/contrib/bsnmp/snmp_mibII/mibII_rcvaddr.c
new file mode 100644
index 0000000..7226e10
--- /dev/null
+++ b/contrib/bsnmp/snmp_mibII/mibII_rcvaddr.c
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2001-2003
+ * Fraunhofer Institute for Open Communication Systems (FhG Fokus).
+ * All rights reserved.
+ *
+ * Author: Harti Brandt <harti@freebsd.org>
+ *
+ * Redistribution of this software and documentation and use in source and
+ * binary forms, with or without modification, are permitted provided that
+ * the following conditions are met:
+ *
+ * 1. Redistributions of source code or documentation must retain the above
+ * copyright notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY FRAUNHOFER FOKUS
+ * AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * FRAUNHOFER FOKUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Begemot: bsnmp/snmp_mibII/mibII_rcvaddr.c,v 1.7 2003/01/28 13:44:35 hbb Exp $
+ *
+ * Interface receive address table.
+ */
+#include "mibII.h"
+#include "mibII_oid.h"
+
+/*
+ * find receive address
+ */
+struct mibrcvaddr *
+mib_find_rcvaddr(u_int ifindex, const u_char *addr, size_t addrlen)
+{
+ struct mibrcvaddr *rcv;
+
+ TAILQ_FOREACH(rcv, &mibrcvaddr_list, link)
+ if (rcv->ifindex == ifindex &&
+ rcv->addrlen == addrlen &&
+ memcmp(rcv->addr, addr, addrlen) == 0)
+ return (rcv);
+ return (NULL);
+}
+
+/*
+ * Create receive address
+ */
+struct mibrcvaddr *
+mib_rcvaddr_create(struct mibif *ifp, const u_char *addr, size_t addrlen)
+{
+ struct mibrcvaddr *rcv;
+ u_int i;
+
+ if (addrlen + OIDLEN_ifRcvAddressEntry + 1 > ASN_MAXOIDLEN)
+ return (NULL);
+
+ if ((rcv = malloc(sizeof(*rcv))) == NULL)
+ return (NULL);
+ rcv->ifindex = ifp->index;
+ rcv->addrlen = addrlen;
+ memcpy(rcv->addr, addr, addrlen);
+ rcv->flags = 0;
+
+ rcv->index.len = addrlen + 2;
+ rcv->index.subs[0] = ifp->index;
+ rcv->index.subs[1] = addrlen;
+ for (i = 0; i < addrlen; i++)
+ rcv->index.subs[i + 2] = addr[i];
+
+ INSERT_OBJECT_OID(rcv, &mibrcvaddr_list);
+
+ return (rcv);
+}
+
+/*
+ * Delete a receive address
+ */
+void
+mib_rcvaddr_delete(struct mibrcvaddr *rcv)
+{
+ TAILQ_REMOVE(&mibrcvaddr_list, rcv, link);
+ free(rcv);
+}
+
+int
+op_rcvaddr(struct snmp_context *ctx __unused, struct snmp_value *value,
+ u_int sub, u_int iidx __unused, enum snmp_op op)
+{
+ struct mibrcvaddr *rcv;
+
+ rcv = NULL; /* make compiler happy */
+
+ switch (op) {
+
+ case SNMP_OP_GETNEXT:
+ if ((rcv = NEXT_OBJECT_OID(&mibrcvaddr_list, &value->var, sub)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ index_append(&value->var, sub, &rcv->index);
+ break;
+
+ case SNMP_OP_GET:
+ if ((rcv = FIND_OBJECT_OID(&mibrcvaddr_list, &value->var, sub)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ break;
+
+ case SNMP_OP_SET:
+ if ((rcv = FIND_OBJECT_OID(&mibrcvaddr_list, &value->var, sub)) == NULL)
+ return (SNMP_ERR_NO_CREATION);
+ return (SNMP_ERR_NOT_WRITEABLE);
+
+ case SNMP_OP_ROLLBACK:
+ case SNMP_OP_COMMIT:
+ abort();
+ }
+
+ switch (value->var.subs[sub - 1]) {
+
+ case LEAF_ifRcvAddressStatus:
+ value->v.integer = 1;
+ break;
+
+ case LEAF_ifRcvAddressType:
+ value->v.integer = (rcv->flags & MIBRCVADDR_VOLATILE) ? 2 : 3;
+ break;
+ }
+ return (SNMP_ERR_NOERROR);
+}
diff --git a/contrib/bsnmp/snmp_mibII/mibII_route.c b/contrib/bsnmp/snmp_mibII/mibII_route.c
new file mode 100644
index 0000000..d2683ce
--- /dev/null
+++ b/contrib/bsnmp/snmp_mibII/mibII_route.c
@@ -0,0 +1,299 @@
+/*
+ * Copyright (c) 2001-2003
+ * Fraunhofer Institute for Open Communication Systems (FhG Fokus).
+ * All rights reserved.
+ *
+ * Author: Harti Brandt <harti@freebsd.org>
+ *
+ * Redistribution of this software and documentation and use in source and
+ * binary forms, with or without modification, are permitted provided that
+ * the following conditions are met:
+ *
+ * 1. Redistributions of source code or documentation must retain the above
+ * copyright notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY FRAUNHOFER FOKUS
+ * AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * FRAUNHOFER FOKUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Begemot: bsnmp/snmp_mibII/mibII_route.c,v 1.3 2003/01/28 13:44:35 hbb Exp $
+ *
+ * Routing table
+ */
+#include "mibII.h"
+#include "mibII_oid.h"
+
+struct sroute {
+ TAILQ_ENTRY(sroute) link;
+ struct asn_oid index;
+ u_int ifindex;
+ u_int type;
+ u_int proto;
+};
+static TAILQ_HEAD(, sroute) sroute_list = TAILQ_HEAD_INITIALIZER(sroute_list);
+
+static u_int32_t route_tick;
+static u_int route_total;
+
+static int
+fetch_route(void)
+{
+ u_char *rtab, *next;
+ size_t len;
+ struct sroute *r;
+ struct rt_msghdr *rtm;
+ struct sockaddr *addrs[RTAX_MAX];
+ struct sockaddr_in *sa, *gw;
+ struct in_addr mask, nhop;
+ in_addr_t ha;
+ struct mibif *ifp;
+
+ while ((r = TAILQ_FIRST(&sroute_list)) != NULL) {
+ TAILQ_REMOVE(&sroute_list, r, link);
+ free(r);
+ }
+ route_total = 0;
+
+ if ((rtab = mib_fetch_rtab(AF_INET, NET_RT_DUMP, 0, &len)) == NULL)
+ return (-1);
+
+ next = rtab;
+ for (next = rtab; next < rtab + len; next += rtm->rtm_msglen) {
+ rtm = (struct rt_msghdr *)(void *)next;
+ if (rtm->rtm_type != RTM_GET ||
+ !(rtm->rtm_flags & RTF_UP))
+ continue;
+ mib_extract_addrs(rtm->rtm_addrs, (u_char *)(rtm + 1), addrs);
+
+ if (addrs[RTAX_DST] == NULL || addrs[RTAX_GATEWAY] == NULL ||
+ addrs[RTAX_DST]->sa_family != AF_INET)
+ continue;
+
+ sa = (struct sockaddr_in *)(void *)addrs[RTAX_DST];
+
+ if (rtm->rtm_flags & RTF_HOST) {
+ mask.s_addr = 0xffffffff;
+ } else {
+ if (addrs[RTAX_NETMASK] == NULL ||
+ addrs[RTAX_NETMASK]->sa_len == 0)
+ mask.s_addr = 0;
+ else
+ mask = ((struct sockaddr_in *)(void *)
+ addrs[RTAX_NETMASK])->sin_addr;
+ }
+ if (addrs[RTAX_GATEWAY] == NULL) {
+ nhop.s_addr = 0;
+ } else if (rtm->rtm_flags & RTF_LLINFO) {
+ nhop = sa->sin_addr;
+ } else {
+ gw = (struct sockaddr_in *)(void *)addrs[RTAX_GATEWAY];
+ if (gw->sin_family != AF_INET)
+ continue;
+ nhop = gw->sin_addr;
+ }
+ if ((ifp = mib_find_if_sys(rtm->rtm_index)) == NULL) {
+ mib_iflist_bad = 1;
+ continue;
+ }
+
+ if ((r = malloc(sizeof(*r))) == NULL) {
+ syslog(LOG_ERR, "%m");
+ continue;
+ }
+
+ route_total++;
+
+ r->index.len = 13;
+ ha = ntohl(sa->sin_addr.s_addr);
+ r->index.subs[0] = (ha >> 24) & 0xff;
+ r->index.subs[1] = (ha >> 16) & 0xff;
+ r->index.subs[2] = (ha >> 8) & 0xff;
+ r->index.subs[3] = (ha >> 0) & 0xff;
+ ha = ntohl(mask.s_addr);
+ r->index.subs[4] = (ha >> 24) & 0xff;
+ r->index.subs[5] = (ha >> 16) & 0xff;
+ r->index.subs[6] = (ha >> 8) & 0xff;
+ r->index.subs[7] = (ha >> 0) & 0xff;
+
+ r->index.subs[8] = 0;
+
+ ha = ntohl(nhop.s_addr);
+ r->index.subs[9] = (ha >> 24) & 0xff;
+ r->index.subs[10] = (ha >> 16) & 0xff;
+ r->index.subs[11] = (ha >> 8) & 0xff;
+ r->index.subs[12] = (ha >> 0) & 0xff;
+
+ r->ifindex = ifp->index;
+
+ r->type = (rtm->rtm_flags & RTF_LLINFO) ? 3 :
+ (rtm->rtm_flags & RTF_REJECT) ? 2 : 4;
+
+ /* cannot really know, what protocol it runs */
+ r->proto = (rtm->rtm_flags & RTF_LOCAL) ? 2 :
+ (rtm->rtm_flags & RTF_STATIC) ? 3 :
+ (rtm->rtm_flags & RTF_DYNAMIC) ? 4 : 10;
+
+ INSERT_OBJECT_OID(r, &sroute_list);
+ }
+
+ free(rtab);
+ route_tick = get_ticks();
+
+ return (0);
+}
+
+/*
+ * Table
+ */
+int
+op_route_table(struct snmp_context *ctx __unused, struct snmp_value *value,
+ u_int sub, u_int iidx __unused, enum snmp_op op)
+{
+ static struct sroute *r;
+
+ if (route_tick < this_tick)
+ if (fetch_route() == -1)
+ return (SNMP_ERR_GENERR);
+
+ switch (op) {
+
+ case SNMP_OP_GETNEXT:
+ if ((r = NEXT_OBJECT_OID(&sroute_list, &value->var, sub)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ index_append(&value->var, sub, &r->index);
+ break;
+
+ case SNMP_OP_GET:
+ if ((r = FIND_OBJECT_OID(&sroute_list, &value->var, sub)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ break;
+
+ case SNMP_OP_SET:
+ if ((r = FIND_OBJECT_OID(&sroute_list, &value->var, sub)) == NULL)
+ return (SNMP_ERR_NO_CREATION);
+ return (SNMP_ERR_NOT_WRITEABLE);
+
+ case SNMP_OP_ROLLBACK:
+ case SNMP_OP_COMMIT:
+ abort();
+
+ default:
+ abort();
+ }
+
+ switch (value->var.subs[sub - 1]) {
+
+ case LEAF_ipCidrRouteDest:
+ value->v.ipaddress[0] = r->index.subs[0];
+ value->v.ipaddress[1] = r->index.subs[1];
+ value->v.ipaddress[2] = r->index.subs[2];
+ value->v.ipaddress[3] = r->index.subs[3];
+ break;
+
+ case LEAF_ipCidrRouteMask:
+ value->v.ipaddress[0] = r->index.subs[4];
+ value->v.ipaddress[1] = r->index.subs[5];
+ value->v.ipaddress[2] = r->index.subs[6];
+ value->v.ipaddress[3] = r->index.subs[7];
+ break;
+
+ case LEAF_ipCidrRouteTos:
+ value->v.integer = r->index.subs[8];
+ break;
+
+ case LEAF_ipCidrRouteNextHop:
+ value->v.ipaddress[0] = r->index.subs[9];
+ value->v.ipaddress[1] = r->index.subs[10];
+ value->v.ipaddress[2] = r->index.subs[11];
+ value->v.ipaddress[3] = r->index.subs[12];
+ break;
+
+ case LEAF_ipCidrRouteIfIndex:
+ value->v.integer = r->ifindex;
+ break;
+
+ case LEAF_ipCidrRouteType:
+ value->v.integer = r->type;
+ break;
+
+ case LEAF_ipCidrRouteProto:
+ value->v.integer = r->proto;
+ break;
+
+ case LEAF_ipCidrRouteAge:
+ value->v.integer = 0;
+ break;
+
+ case LEAF_ipCidrRouteInfo:
+ value->v.oid = oid_zeroDotZero;
+ break;
+
+ case LEAF_ipCidrRouteNextHopAS:
+ value->v.integer = 0;
+ break;
+
+ case LEAF_ipCidrRouteMetric1:
+ case LEAF_ipCidrRouteMetric2:
+ case LEAF_ipCidrRouteMetric3:
+ case LEAF_ipCidrRouteMetric4:
+ case LEAF_ipCidrRouteMetric5:
+ value->v.integer = -1;
+ break;
+
+ case LEAF_ipCidrRouteStatus:
+ value->v.integer = 1;
+ break;
+ }
+ return (SNMP_ERR_NOERROR);
+}
+
+/*
+ * scalars
+ */
+int
+op_route(struct snmp_context *ctx __unused, struct snmp_value *value,
+ u_int sub, u_int iidx __unused, enum snmp_op op)
+{
+ switch (op) {
+
+ case SNMP_OP_GETNEXT:
+ abort();
+
+ case SNMP_OP_GET:
+ break;
+
+ case SNMP_OP_SET:
+ return (SNMP_ERR_NOT_WRITEABLE);
+
+ case SNMP_OP_ROLLBACK:
+ case SNMP_OP_COMMIT:
+ abort();
+ }
+
+ if (route_tick < this_tick)
+ if (fetch_route() == -1)
+ return (SNMP_ERR_GENERR);
+
+ switch (value->var.subs[sub - 1]) {
+
+ case LEAF_ipCidrRouteNumber:
+ value->v.uint32 = route_total;
+ break;
+
+ }
+ return (SNMP_ERR_NOERROR);
+}
diff --git a/contrib/bsnmp/snmp_mibII/mibII_tcp.c b/contrib/bsnmp/snmp_mibII/mibII_tcp.c
new file mode 100644
index 0000000..f4509f9
--- /dev/null
+++ b/contrib/bsnmp/snmp_mibII/mibII_tcp.c
@@ -0,0 +1,361 @@
+/*
+ * Copyright (c) 2001-2003
+ * Fraunhofer Institute for Open Communication Systems (FhG Fokus).
+ * All rights reserved.
+ *
+ * Author: Harti Brandt <harti@freebsd.org>
+ *
+ * Redistribution of this software and documentation and use in source and
+ * binary forms, with or without modification, are permitted provided that
+ * the following conditions are met:
+ *
+ * 1. Redistributions of source code or documentation must retain the above
+ * copyright notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY FRAUNHOFER FOKUS
+ * AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * FRAUNHOFER FOKUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Begemot: bsnmp/snmp_mibII/mibII_tcp.c,v 1.4 2003/01/28 13:44:35 hbb Exp $
+ *
+ * tcp
+ */
+#include "mibII.h"
+#include "mibII_oid.h"
+#include <sys/socketvar.h>
+#include <netinet/in_pcb.h>
+#include <netinet/tcp.h>
+#include <netinet/tcp_var.h>
+#include <netinet/tcp_timer.h>
+#include <netinet/tcp_fsm.h>
+
+struct tcp_index {
+ struct asn_oid index;
+ struct xtcpcb *tp;
+};
+
+static u_int32_t tcp_tick;
+static struct tcpstat tcpstat;
+static struct xinpgen *xinpgen;
+static size_t xinpgen_len;
+static u_int tcp_count;
+static u_int tcp_total;
+
+static u_int oidnum;
+static struct tcp_index *tcpoids;
+
+static int
+tcp_compare(const void *p1, const void *p2)
+{
+ const struct tcp_index *t1 = p1;
+ const struct tcp_index *t2 = p2;
+
+ return (asn_compare_oid(&t1->index, &t2->index));
+}
+
+static int
+fetch_tcp(void)
+{
+ size_t len;
+ struct xinpgen *ptr;
+ struct xtcpcb *tp;
+ struct tcp_index *oid;
+ in_addr_t inaddr;
+
+ len = sizeof(tcpstat);
+ if (sysctlbyname("net.inet.tcp.stats", &tcpstat, &len, NULL, 0) == -1) {
+ syslog(LOG_ERR, "net.inet.tcp.stats: %m");
+ return (-1);
+ }
+ if (len != sizeof(tcpstat)) {
+ syslog(LOG_ERR, "net.inet.tcp.stats: wrong size");
+ return (-1);
+ }
+
+ len = 0;
+ if (sysctlbyname("net.inet.tcp.pcblist", NULL, &len, NULL, 0) == -1) {
+ syslog(LOG_ERR, "net.inet.tcp.pcblist: %m");
+ return (-1);
+ }
+ if (len > xinpgen_len) {
+ if ((ptr = realloc(xinpgen, len)) == NULL) {
+ syslog(LOG_ERR, "%zu: %m", len);
+ return (-1);
+ }
+ xinpgen = ptr;
+ xinpgen_len = len;
+ }
+ if (sysctlbyname("net.inet.tcp.pcblist", xinpgen, &len, NULL, 0) == -1) {
+ syslog(LOG_ERR, "net.inet.tcp.pcblist: %m");
+ return (-1);
+ }
+
+ tcp_tick = get_ticks();
+
+ tcp_count = 0;
+ tcp_total = 0;
+ for (ptr = (struct xinpgen *)(void *)((char *)xinpgen + xinpgen->xig_len);
+ ptr->xig_len > sizeof(struct xinpgen);
+ ptr = (struct xinpgen *)(void *)((char *)ptr + ptr->xig_len)) {
+ tp = (struct xtcpcb *)ptr;
+ if (tp->xt_inp.inp_gencnt > xinpgen->xig_gen ||
+ (tp->xt_inp.inp_vflag & INP_IPV4) == 0)
+ continue;
+
+ tcp_total++;
+ if (tp->xt_tp.t_state == TCPS_ESTABLISHED ||
+ tp->xt_tp.t_state == TCPS_CLOSE_WAIT)
+ tcp_count++;
+ }
+
+ if (oidnum < tcp_total) {
+ oid = realloc(tcpoids, tcp_total * sizeof(tcpoids[0]));
+ if (oid == NULL) {
+ free(tcpoids);
+ oidnum = 0;
+ return (0);
+ }
+ tcpoids = oid;
+ oidnum = tcp_total;
+ }
+
+ oid = tcpoids;
+ for (ptr = (struct xinpgen *)(void *)((char *)xinpgen + xinpgen->xig_len);
+ ptr->xig_len > sizeof(struct xinpgen);
+ ptr = (struct xinpgen *)(void *)((char *)ptr + ptr->xig_len)) {
+ tp = (struct xtcpcb *)ptr;
+ if (tp->xt_inp.inp_gencnt > xinpgen->xig_gen ||
+ (tp->xt_inp.inp_vflag & INP_IPV4) == 0)
+ continue;
+ oid->tp = tp;
+ oid->index.len = 10;
+ inaddr = ntohl(tp->xt_inp.inp_laddr.s_addr);
+ oid->index.subs[0] = (inaddr >> 24) & 0xff;
+ oid->index.subs[1] = (inaddr >> 16) & 0xff;
+ oid->index.subs[2] = (inaddr >> 8) & 0xff;
+ oid->index.subs[3] = (inaddr >> 0) & 0xff;
+ oid->index.subs[4] = ntohs(tp->xt_inp.inp_lport);
+ inaddr = ntohl(tp->xt_inp.inp_faddr.s_addr);
+ oid->index.subs[5] = (inaddr >> 24) & 0xff;
+ oid->index.subs[6] = (inaddr >> 16) & 0xff;
+ oid->index.subs[7] = (inaddr >> 8) & 0xff;
+ oid->index.subs[8] = (inaddr >> 0) & 0xff;
+ oid->index.subs[9] = ntohs(tp->xt_inp.inp_fport);
+ oid++;
+ }
+
+ qsort(tcpoids, tcp_total, sizeof(tcpoids[0]), tcp_compare);
+
+ return (0);
+}
+
+/*
+ * Scalars
+ */
+int
+op_tcp(struct snmp_context *ctx __unused, struct snmp_value *value,
+ u_int sub, u_int iidx __unused, enum snmp_op op)
+{
+ switch (op) {
+
+ case SNMP_OP_GETNEXT:
+ abort();
+
+ case SNMP_OP_GET:
+ break;
+
+ case SNMP_OP_SET:
+ return (SNMP_ERR_NOT_WRITEABLE);
+
+ case SNMP_OP_ROLLBACK:
+ case SNMP_OP_COMMIT:
+ abort();
+ }
+
+ if (tcp_tick < this_tick)
+ if (fetch_tcp() == -1)
+ return (SNMP_ERR_GENERR);
+
+ switch (value->var.subs[sub - 1]) {
+
+ case LEAF_tcpRtoAlgorithm:
+ value->v.integer = 4; /* Van Jacobson */
+ break;
+
+#define hz clockinfo.hz
+
+ case LEAF_tcpRtoMin:
+ value->v.integer = 1000 * TCPTV_MIN / hz;
+ break;
+
+ case LEAF_tcpRtoMax:
+ value->v.integer = 1000 * TCPTV_REXMTMAX / hz;
+ break;
+#undef hz
+
+ case LEAF_tcpMaxConn:
+ value->v.integer = -1;
+ break;
+
+ case LEAF_tcpActiveOpens:
+ value->v.uint32 = tcpstat.tcps_connattempt;
+ break;
+
+ case LEAF_tcpPassiveOpens:
+ value->v.uint32 = tcpstat.tcps_accepts;
+ break;
+
+ case LEAF_tcpAttemptFails:
+ value->v.uint32 = tcpstat.tcps_conndrops;
+ break;
+
+ case LEAF_tcpEstabResets:
+ value->v.uint32 = tcpstat.tcps_drops;
+ break;
+
+ case LEAF_tcpCurrEstab:
+ value->v.uint32 = tcp_count;
+ break;
+
+ case LEAF_tcpInSegs:
+ value->v.uint32 = tcpstat.tcps_rcvtotal;
+ break;
+
+ case LEAF_tcpOutSegs:
+ value->v.uint32 = tcpstat.tcps_sndtotal -
+ tcpstat.tcps_sndrexmitpack;
+ break;
+
+ case LEAF_tcpRetransSegs:
+ value->v.uint32 = tcpstat.tcps_sndrexmitpack;
+ break;
+
+ case LEAF_tcpInErrs:
+ value->v.uint32 = tcpstat.tcps_rcvbadsum +
+ tcpstat.tcps_rcvbadoff +
+ tcpstat.tcps_rcvshort;
+ break;
+ }
+ return (SNMP_ERR_NOERROR);
+}
+
+int
+op_tcpconn(struct snmp_context *ctx __unused, struct snmp_value *value,
+ u_int sub, u_int iidx __unused, enum snmp_op op)
+{
+ u_int i;
+
+ if (tcp_tick < this_tick)
+ if (fetch_tcp() == -1)
+ return (SNMP_ERR_GENERR);
+
+ switch (op) {
+
+ case SNMP_OP_GETNEXT:
+ for (i = 0; i < tcp_total; i++)
+ if (index_compare(&value->var, sub, &tcpoids[i].index) < 0)
+ break;
+ if (i == tcp_total)
+ return (SNMP_ERR_NOSUCHNAME);
+ index_append(&value->var, sub, &tcpoids[i].index);
+ break;
+
+ case SNMP_OP_GET:
+ for (i = 0; i < tcp_total; i++)
+ if (index_compare(&value->var, sub, &tcpoids[i].index) == 0)
+ break;
+ if (i == tcp_total)
+ return (SNMP_ERR_NOSUCHNAME);
+ break;
+
+ case SNMP_OP_SET:
+ return (SNMP_ERR_NOT_WRITEABLE);
+
+ case SNMP_OP_ROLLBACK:
+ case SNMP_OP_COMMIT:
+ default:
+ abort();
+ }
+
+ switch (value->var.subs[sub - 1]) {
+
+ case LEAF_tcpConnState:
+ switch (tcpoids[i].tp->xt_tp.t_state) {
+
+ case TCPS_CLOSED:
+ value->v.integer = 1;
+ break;
+ case TCPS_LISTEN:
+ value->v.integer = 2;
+ break;
+ case TCPS_SYN_SENT:
+ value->v.integer = 3;
+ break;
+ case TCPS_SYN_RECEIVED:
+ value->v.integer = 4;
+ break;
+ case TCPS_ESTABLISHED:
+ value->v.integer = 5;
+ break;
+ case TCPS_CLOSE_WAIT:
+ value->v.integer = 8;
+ break;
+ case TCPS_FIN_WAIT_1:
+ value->v.integer = 6;
+ break;
+ case TCPS_CLOSING:
+ value->v.integer = 10;
+ break;
+ case TCPS_LAST_ACK:
+ value->v.integer = 9;
+ break;
+ case TCPS_FIN_WAIT_2:
+ value->v.integer = 7;
+ break;
+ case TCPS_TIME_WAIT:
+ value->v.integer = 11;
+ break;
+ default:
+ value->v.integer = 0;
+ break;
+ }
+ break;
+
+ case LEAF_tcpConnLocalAddress:
+ value->v.ipaddress[0] = tcpoids[i].index.subs[0];
+ value->v.ipaddress[1] = tcpoids[i].index.subs[1];
+ value->v.ipaddress[2] = tcpoids[i].index.subs[2];
+ value->v.ipaddress[3] = tcpoids[i].index.subs[3];
+ break;
+
+ case LEAF_tcpConnLocalPort:
+ value->v.integer = tcpoids[i].index.subs[4];
+ break;
+
+ case LEAF_tcpConnRemAddress:
+ value->v.ipaddress[0] = tcpoids[i].index.subs[5];
+ value->v.ipaddress[1] = tcpoids[i].index.subs[6];
+ value->v.ipaddress[2] = tcpoids[i].index.subs[7];
+ value->v.ipaddress[3] = tcpoids[i].index.subs[8];
+ break;
+
+ case LEAF_tcpConnRemPort:
+ value->v.integer = tcpoids[i].index.subs[9];
+ break;
+ }
+ return (SNMP_ERR_NOERROR);
+}
diff --git a/contrib/bsnmp/snmp_mibII/mibII_tree.def b/contrib/bsnmp/snmp_mibII/mibII_tree.def
new file mode 100644
index 0000000..6384264
--- /dev/null
+++ b/contrib/bsnmp/snmp_mibII/mibII_tree.def
@@ -0,0 +1,248 @@
+#
+# Copyright (c) 2001-2003
+# Fraunhofer Institute for Open Communication Systems (FhG Fokus).
+# All rights reserved.
+#
+# Author: Harti Brandt <harti@freebsd.org>
+#
+# Redistribution of this software and documentation and use in source and
+# binary forms, with or without modification, are permitted provided that
+# the following conditions are met:
+#
+# 1. Redistributions of source code or documentation must retain the above
+# copyright notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. Neither the name of the Institute nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY FRAUNHOFER FOKUS
+# AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+# FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+# FRAUNHOFER FOKUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+# EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# $Begemot: bsnmp/snmp_mibII/mibII_tree.def,v 1.10 2002/02/06 12:43:51 hbb Exp $
+#
+# Definition of the standard interfaces and ip trees.
+#
+(1 internet
+ (2 mgmt
+ (1 mib2
+ (2 interfaces
+ (1 ifNumber INTEGER op_interfaces GET)
+ (2 ifTable
+ (1 ifEntry : INTEGER op_ifentry
+ (1 ifIndex INTEGER GET)
+ (2 ifDescr OCTETSTRING GET)
+ (3 ifType INTEGER GET)
+ (4 ifMtu INTEGER32 GET)
+ (5 ifSpeed GAUGE GET)
+ (6 ifPhysAddress OCTETSTRING GET)
+ (7 ifAdminStatus INTEGER GET SET)
+ (8 ifOperStatus INTEGER GET)
+ (9 ifLastChange TIMETICKS GET)
+ (10 ifInOctets COUNTER GET)
+ (11 ifInUcastPkts COUNTER GET)
+ (12 ifInNUcastPkts COUNTER GET)
+ (13 ifInDiscards COUNTER GET)
+ (14 ifInErrors COUNTER GET)
+ (15 ifInUnknownProtos COUNTER GET)
+ (16 ifOutOctets COUNTER GET)
+ (17 ifOutUcastPkts COUNTER GET)
+ (18 ifOutNUcastPkts COUNTER GET)
+ (19 ifOutDiscards COUNTER GET)
+ (20 ifOutErrors COUNTER GET)
+ (21 ifOutQLen GAUGE GET)
+ (22 ifSpecific OID GET)
+ ))
+ )
+ (4 ip
+ (1 ipForwarding INTEGER op_ip GET SET)
+ (2 ipDefaultTTL INTEGER op_ip GET SET)
+ (3 ipInReceives COUNTER op_ipstat GET)
+ (4 ipInHdrErrors COUNTER op_ipstat GET)
+ (5 ipInAddrErrors COUNTER op_ipstat GET)
+ (6 ipForwDatagrams COUNTER op_ipstat GET)
+ (7 ipInUnknownProtos COUNTER op_ipstat GET)
+ (8 ipInDiscards COUNTER op_ipstat GET)
+ (9 ipInDelivers COUNTER op_ipstat GET)
+ (10 ipOutRequests COUNTER op_ipstat GET)
+ (11 ipOutDiscards COUNTER op_ipstat GET)
+ (12 ipOutNoRoutes COUNTER op_ipstat GET)
+ (13 ipReasmTimeout INTEGER32 op_ipstat GET)
+ (14 ipReasmReqds COUNTER op_ipstat GET)
+ (15 ipReasmOKs COUNTER op_ipstat GET)
+ (16 ipReasmFails COUNTER op_ipstat GET)
+ (17 ipFragOKs COUNTER op_ipstat GET)
+ (18 ipFragFails COUNTER op_ipstat GET)
+ (19 ipFragCreates COUNTER op_ipstat GET)
+ (20 ipAddrTable
+ (1 ipAddrEntry : IPADDRESS op_ipaddr
+ (1 ipAdEntAddr IPADDRESS GET)
+ (2 ipAdEntIfIndex INTEGER GET SET)
+ (3 ipAdEntNetMask IPADDRESS GET SET)
+ (4 ipAdEntBcastAddr INTEGER GET SET)
+ (5 ipAdEntReasmMaxSize INTEGER GET)
+ ))
+ (22 ipNetToMediaTable
+ (1 ipNetToMediaEntry : INTEGER IPADDRESS op_nettomedia
+ (1 ipNetToMediaIfIndex INTEGER GET)
+ (2 ipNetToMediaPhysAddress OCTETSTRING GET)
+ (3 ipNetToMediaNetAddress IPADDRESS GET)
+ (4 ipNetToMediaType INTEGER GET)
+ ))
+ (23 ipRoutingDiscards INTEGER op_ipstat) # not available
+ (24 ipForward
+ (3 ipCidrRouteNumber GAUGE op_route GET)
+ (4 ipCidrRouteTable
+ (1 ipCidrRouteEntry : IPADDRESS IPADDRESS INTEGER IPADDRESS op_route_table
+ (1 ipCidrRouteDest IPADDRESS GET)
+ (2 ipCidrRouteMask IPADDRESS GET)
+ (3 ipCidrRouteTos INTEGER GET)
+ (4 ipCidrRouteNextHop IPADDRESS GET)
+ (5 ipCidrRouteIfIndex INTEGER GET) # SET
+ (6 ipCidrRouteType INTEGER GET) # SET
+ (7 ipCidrRouteProto INTEGER GET)
+ (8 ipCidrRouteAge INTEGER GET)
+ (9 ipCidrRouteInfo OID GET) # SET
+ (10 ipCidrRouteNextHopAS INTEGER GET) # SET
+ (11 ipCidrRouteMetric1 INTEGER GET) # SET
+ (12 ipCidrRouteMetric2 INTEGER GET) # SET
+ (13 ipCidrRouteMetric3 INTEGER GET) # SET
+ (14 ipCidrRouteMetric4 INTEGER GET) # SET
+ (15 ipCidrRouteMetric5 INTEGER GET) # SET
+ (16 ipCidrRouteStatus INTEGER GET) # SET
+ ))
+ )
+ )
+ (5 icmp
+ (1 icmpInMsgs COUNTER op_icmpstat GET)
+ (2 icmpInErrors COUNTER op_icmpstat GET)
+ (3 icmpInDestUnreachs COUNTER op_icmpstat GET)
+ (4 icmpInTimeExcds COUNTER op_icmpstat GET)
+ (5 icmpInParmProbs COUNTER op_icmpstat GET)
+ (6 icmpInSrcQuenchs COUNTER op_icmpstat GET)
+ (7 icmpInRedirects COUNTER op_icmpstat GET)
+ (8 icmpInEchos COUNTER op_icmpstat GET)
+ (9 icmpInEchoReps COUNTER op_icmpstat GET)
+ (10 icmpInTimestamps COUNTER op_icmpstat GET)
+ (11 icmpInTimestampReps COUNTER op_icmpstat GET)
+ (12 icmpInAddrMasks COUNTER op_icmpstat GET)
+ (13 icmpInAddrMaskReps COUNTER op_icmpstat GET)
+ (14 icmpOutMsgs COUNTER op_icmpstat GET)
+ (15 icmpOutErrors COUNTER op_icmpstat GET)
+ (16 icmpOutDestUnreachs COUNTER op_icmpstat GET)
+ (17 icmpOutTimeExcds COUNTER op_icmpstat GET)
+ (18 icmpOutParmProbs COUNTER op_icmpstat GET)
+ (19 icmpOutSrcQuenchs COUNTER op_icmpstat GET)
+ (20 icmpOutRedirects COUNTER op_icmpstat GET)
+ (21 icmpOutEchos COUNTER op_icmpstat GET)
+ (22 icmpOutEchoReps COUNTER op_icmpstat GET)
+ (23 icmpOutTimestamps COUNTER op_icmpstat GET)
+ (24 icmpOutTimestampReps COUNTER op_icmpstat GET)
+ (25 icmpOutAddrMasks COUNTER op_icmpstat GET)
+ (26 icmpOutAddrMaskReps COUNTER op_icmpstat GET)
+ )
+ (6 tcp
+ (1 tcpRtoAlgorithm INTEGER op_tcp GET)
+ (2 tcpRtoMin INTEGER32 op_tcp GET)
+ (3 tcpRtoMax INTEGER32 op_tcp GET)
+ (4 tcpMaxConn INTEGER32 op_tcp GET)
+ (5 tcpActiveOpens COUNTER op_tcp GET)
+ (6 tcpPassiveOpens COUNTER op_tcp GET)
+ (7 tcpAttemptFails COUNTER op_tcp GET)
+ (8 tcpEstabResets COUNTER op_tcp GET)
+ (9 tcpCurrEstab GAUGE op_tcp GET)
+ (10 tcpInSegs COUNTER op_tcp GET)
+ (11 tcpOutSegs COUNTER op_tcp GET)
+ (12 tcpRetransSegs COUNTER op_tcp GET)
+ (13 tcpConnTable
+ (1 tcpConnEntry : IPADDRESS INTEGER IPADDRESS INTEGER op_tcpconn
+ (1 tcpConnState INTEGER GET)
+ (2 tcpConnLocalAddress IPADDRESS GET)
+ (3 tcpConnLocalPort INTEGER GET)
+ (4 tcpConnRemAddress IPADDRESS GET)
+ (5 tcpConnRemPort INTEGER GET)
+ ))
+ (14 tcpInErrs COUNTER op_tcp GET)
+ (15 tcpOutRsts COUNTER op_tcp) # don't know
+ )
+ (7 udp
+ (1 udpInDatagrams COUNTER op_udp GET)
+ (2 udpNoPorts COUNTER op_udp GET)
+ (3 udpInErrors COUNTER op_udp GET)
+ (4 udpOutDatagrams COUNTER op_udp GET)
+ (5 udpTable
+ (1 udpEntry : IPADDRESS INTEGER op_udptable
+ (1 udpLocalAddress IPADDRESS GET)
+ (2 udpLocalPort INTEGER GET)
+ ))
+ )
+ (31 ifMIB
+ (1 ifMIBObjects
+ (1 ifXTable
+ (1 ifXEntry : INTEGER op_ifxtable
+ (1 ifName OCTETSTRING GET)
+ (2 ifInMulticastPkts COUNTER GET)
+ (3 ifInBroadcastPkts COUNTER GET)
+ (4 ifOutMulticastPkts COUNTER GET)
+ (5 ifOutBroadcastPkts COUNTER GET)
+ (6 ifHCInOctets COUNTER64 GET)
+ (7 ifHCInUcastPkts COUNTER64 GET)
+ (8 ifHCInMulticastPkts COUNTER64 GET)
+ (9 ifHCInBroadcastPkts COUNTER64 GET)
+ (10 ifHCOutOctets COUNTER64 GET)
+ (11 ifHCOutUcastPkts COUNTER64 GET)
+ (12 ifHCOutMulticastPkts COUNTER64 GET)
+ (13 ifHCOutBroadcastPkts COUNTER64 GET)
+ (14 ifLinkUpDownTrapEnable INTEGER GET SET)
+ (15 ifHighSpeed GAUGE GET)
+ (16 ifPromiscuousMode INTEGER GET SET)
+ (17 ifConnectorPresent INTEGER GET)
+ (18 ifAlias OCTETSTRING GET)
+ (19 ifCounterDiscontinuityTime TIMETICKS GET)
+ ))
+ (2 ifStackTable
+ (1 ifStackEntry : INTEGER INTEGER op_ifstack
+ (1 ifStackHigherLayer INTEGER)
+ (2 ifStackLowerLayer INTEGER)
+ (3 ifStackStatus INTEGER GET)
+ ))
+ (4 ifRcvAddressTable
+ (1 ifRcvAddressEntry : INTEGER OCTETSTRING op_rcvaddr
+ (1 ifRcvAddressAddress OCTETSTRING)
+ (2 ifRcvAddressStatus INTEGER GET)
+ (3 ifRcvAddressType INTEGER GET)
+ ))
+ (5 ifTableLastChange TIMETICKS op_ifmib GET)
+ (6 ifStackLastChange TIMETICKS op_ifmib GET)
+ )
+ )
+ (48 ipMIB
+ )
+ (49 tcpMIB
+ )
+ (50 udpMIB
+ )
+ ))
+ (6 snmpV2
+ (3 snmpModules
+ (1 snmpMIB
+ (1 snmpMIBObjects
+ (5 snmpTraps
+ (3 linkDown OID op_snmp_trap)
+ (4 linkUp OID op_snmp_trap)
+ )
+ )
+ )
+ ))
+)
diff --git a/contrib/bsnmp/snmp_mibII/mibII_udp.c b/contrib/bsnmp/snmp_mibII/mibII_udp.c
new file mode 100644
index 0000000..232bc45
--- /dev/null
+++ b/contrib/bsnmp/snmp_mibII/mibII_udp.c
@@ -0,0 +1,257 @@
+/*
+ * Copyright (c) 2001-2003
+ * Fraunhofer Institute for Open Communication Systems (FhG Fokus).
+ * All rights reserved.
+ *
+ * Author: Harti Brandt <harti@freebsd.org>
+ *
+ * Redistribution of this software and documentation and use in source and
+ * binary forms, with or without modification, are permitted provided that
+ * the following conditions are met:
+ *
+ * 1. Redistributions of source code or documentation must retain the above
+ * copyright notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY FRAUNHOFER FOKUS
+ * AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * FRAUNHOFER FOKUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Begemot: bsnmp/snmp_mibII/mibII_udp.c,v 1.4 2003/01/28 13:44:35 hbb Exp $
+ *
+ * udp
+ */
+#include "mibII.h"
+#include "mibII_oid.h"
+#include <sys/socketvar.h>
+#include <netinet/in_pcb.h>
+#include <netinet/udp.h>
+#include <netinet/ip_var.h>
+#include <netinet/udp_var.h>
+
+struct udp_index {
+ struct asn_oid index;
+ struct xinpcb *inp;
+};
+
+static u_int32_t udp_tick;
+static struct udpstat udpstat;
+static struct xinpgen *xinpgen;
+static size_t xinpgen_len;
+static u_int udp_total;
+
+static u_int oidnum;
+static struct udp_index *udpoids;
+
+static int
+udp_compare(const void *p1, const void *p2)
+{
+ const struct udp_index *t1 = p1;
+ const struct udp_index *t2 = p2;
+
+ return (asn_compare_oid(&t1->index, &t2->index));
+}
+
+static int
+fetch_udp(void)
+{
+ size_t len;
+ struct xinpgen *ptr;
+ struct xinpcb *inp;
+ struct udp_index *oid;
+ in_addr_t inaddr;
+
+ len = sizeof(udpstat);
+ if (sysctlbyname("net.inet.udp.stats", &udpstat, &len, NULL, 0) == -1) {
+ syslog(LOG_ERR, "net.inet.udp.stats: %m");
+ return (-1);
+ }
+ if (len != sizeof(udpstat)) {
+ syslog(LOG_ERR, "net.inet.udp.stats: wrong size");
+ return (-1);
+ }
+
+ udp_tick = get_ticks();
+
+ len = 0;
+ if (sysctlbyname("net.inet.udp.pcblist", NULL, &len, NULL, 0) == -1) {
+ syslog(LOG_ERR, "net.inet.udp.pcblist: %m");
+ return (-1);
+ }
+ if (len > xinpgen_len) {
+ if ((ptr = realloc(xinpgen, len)) == NULL) {
+ syslog(LOG_ERR, "%zu: %m", len);
+ return (-1);
+ }
+ xinpgen = ptr;
+ xinpgen_len = len;
+ }
+ if (sysctlbyname("net.inet.udp.pcblist", xinpgen, &len, NULL, 0) == -1) {
+ syslog(LOG_ERR, "net.inet.udp.pcblist: %m");
+ return (-1);
+ }
+
+ udp_total = 0;
+ for (ptr = (struct xinpgen *)(void *)((char *)xinpgen + xinpgen->xig_len);
+ ptr->xig_len > sizeof(struct xinpgen);
+ ptr = (struct xinpgen *)(void *)((char *)ptr + ptr->xig_len)) {
+ inp = (struct xinpcb *)ptr;
+ if (inp->xi_inp.inp_gencnt > xinpgen->xig_gen ||
+ (inp->xi_inp.inp_vflag & INP_IPV4) == 0)
+ continue;
+
+ udp_total++;
+ }
+
+ if (oidnum < udp_total) {
+ oid = realloc(udpoids, udp_total * sizeof(udpoids[0]));
+ if (oid == NULL) {
+ free(udpoids);
+ oidnum = 0;
+ return (0);
+ }
+ udpoids = oid;
+ oidnum = udp_total;
+ }
+
+ oid = udpoids;
+ for (ptr = (struct xinpgen *)(void *)((char *)xinpgen + xinpgen->xig_len);
+ ptr->xig_len > sizeof(struct xinpgen);
+ ptr = (struct xinpgen *)(void *)((char *)ptr + ptr->xig_len)) {
+ inp = (struct xinpcb *)ptr;
+ if (inp->xi_inp.inp_gencnt > xinpgen->xig_gen ||
+ (inp->xi_inp.inp_vflag & INP_IPV4) == 0)
+ continue;
+ oid->inp = inp;
+ oid->index.len = 5;
+ inaddr = ntohl(inp->xi_inp.inp_laddr.s_addr);
+ oid->index.subs[0] = (inaddr >> 24) & 0xff;
+ oid->index.subs[1] = (inaddr >> 16) & 0xff;
+ oid->index.subs[2] = (inaddr >> 8) & 0xff;
+ oid->index.subs[3] = (inaddr >> 0) & 0xff;
+ oid->index.subs[4] = ntohs(inp->xi_inp.inp_lport);
+ oid++;
+ }
+
+ qsort(udpoids, udp_total, sizeof(udpoids[0]), udp_compare);
+
+ return (0);
+}
+
+int
+op_udp(struct snmp_context *ctx __unused, struct snmp_value *value,
+ u_int sub, u_int iidx __unused, enum snmp_op op)
+{
+ switch (op) {
+
+ case SNMP_OP_GETNEXT:
+ abort();
+
+ case SNMP_OP_GET:
+ break;
+
+ case SNMP_OP_SET:
+ return (SNMP_ERR_NOT_WRITEABLE);
+
+ case SNMP_OP_ROLLBACK:
+ case SNMP_OP_COMMIT:
+ abort();
+ }
+
+ if (udp_tick < this_tick)
+ if (fetch_udp() == -1)
+ return (SNMP_ERR_GENERR);
+
+ switch (value->var.subs[sub - 1]) {
+
+ case LEAF_udpInDatagrams:
+ value->v.uint32 = udpstat.udps_ipackets;
+ break;
+
+ case LEAF_udpNoPorts:
+ value->v.uint32 = udpstat.udps_noport +
+ udpstat.udps_noportbcast +
+ udpstat.udps_noportmcast;
+ break;
+
+ case LEAF_udpInErrors:
+ value->v.uint32 = udpstat.udps_hdrops +
+ udpstat.udps_badsum +
+ udpstat.udps_badlen +
+ udpstat.udps_fullsock;
+ break;
+
+ case LEAF_udpOutDatagrams:
+ value->v.uint32 = udpstat.udps_opackets;
+ break;
+ }
+ return (SNMP_ERR_NOERROR);
+}
+
+int
+op_udptable(struct snmp_context *ctx __unused, struct snmp_value *value,
+ u_int sub, u_int iidx __unused, enum snmp_op op)
+{
+ u_int i;
+
+ if (udp_tick < this_tick)
+ if (fetch_udp() == -1)
+ return (SNMP_ERR_GENERR);
+
+ switch (op) {
+
+ case SNMP_OP_GETNEXT:
+ for (i = 0; i < udp_total; i++)
+ if (index_compare(&value->var, sub, &udpoids[i].index) < 0)
+ break;
+ if (i == udp_total)
+ return (SNMP_ERR_NOSUCHNAME);
+ index_append(&value->var, sub, &udpoids[i].index);
+ break;
+
+ case SNMP_OP_GET:
+ for (i = 0; i < udp_total; i++)
+ if (index_compare(&value->var, sub, &udpoids[i].index) == 0)
+ break;
+ if (i == udp_total)
+ return (SNMP_ERR_NOSUCHNAME);
+ break;
+
+ case SNMP_OP_SET:
+ return (SNMP_ERR_NOT_WRITEABLE);
+
+ case SNMP_OP_ROLLBACK:
+ case SNMP_OP_COMMIT:
+ default:
+ abort();
+ }
+
+ switch (value->var.subs[sub - 1]) {
+
+ case LEAF_udpLocalAddress:
+ value->v.ipaddress[0] = udpoids[i].index.subs[0];
+ value->v.ipaddress[1] = udpoids[i].index.subs[1];
+ value->v.ipaddress[2] = udpoids[i].index.subs[2];
+ value->v.ipaddress[3] = udpoids[i].index.subs[3];
+ break;
+
+ case LEAF_udpLocalPort:
+ value->v.integer = udpoids[i].index.subs[4];
+ break;
+
+ }
+ return (SNMP_ERR_NOERROR);
+}
diff --git a/contrib/bsnmp/snmp_mibII/snmp_mibII.3 b/contrib/bsnmp/snmp_mibII/snmp_mibII.3
new file mode 100644
index 0000000..62b354d
--- /dev/null
+++ b/contrib/bsnmp/snmp_mibII/snmp_mibII.3
@@ -0,0 +1,348 @@
+.\"
+.\" Copyright (c) 2001-2003
+.\" Fraunhofer Institute for Open Communication Systems (FhG Fokus).
+.\" All rights reserved.
+.\"
+.\" Author: Harti Brandt <harti@freebsd.org>
+.\"
+.\" Redistribution of this software and documentation and use in source and
+.\" binary forms, with or without modification, are permitted provided that
+.\" the following conditions are met:
+.\"
+.\" 1. Redistributions of source code or documentation must retain the above
+.\" copyright notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the Institute nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY FRAUNHOFER FOKUS
+.\" AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+.\" INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+.\" FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+.\" FRAUNHOFER FOKUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+.\" LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+.\" OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+.\" LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+.\" NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+.\" EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $Begemot: bsnmp/snmp_mibII/snmp_mibII.3,v 1.1 2002/08/19 09:30:14 hbb Exp $
+.\"
+.Dd August 19, 2002
+.Dt snmp_mibII 3
+.Os
+.Sh NAME
+.Nm mibif_notify_f ,
+.Nm mib_netsock ,
+.Nm mib_if_set_dyn ,
+.Nm mib_refresh_iflist ,
+.Nm mib_find_if ,
+.Nm mib_find_if_sys ,
+.Nm mib_find_if_name ,
+.Nm mib_first_if ,
+.Nm mib_next_if ,
+.Nm mib_register_newif ,
+.Nm mib_unregister_newif ,
+.Nm mib_fetch_ifmib ,
+.Nm mib_if_admin ,
+.Nm mib_find_ifa ,
+.Nm mib_first_ififa ,
+.Nm mib_next_ififa ,
+.Nm mib_ifstack_create ,
+.Nm mib_ifstack_delete ,
+.Nm mib_find_rcvaddr ,
+.Nm mib_rcvaddr_create ,
+.Nm mib_rcvaddr_delete ,
+.Nm mibif_notify ,
+.Nm mibif_unnotify
+.Nd "mib-2 module for snmpd.
+.Sh LIBRARY
+.Pq begemotSnmpdModulePath."mibII" = "/usr/local/lib/snmp_mibII.so"
+.Sh SYNOPSIS
+.In bsnmp/snmpmod.h
+.In bsnmp/snmp_mibII.h
+.Ft typedef void
+.Fn (*mibif_notify_f) "struct mibif *ifp" "enum mibif_notify event" "void *uarg"
+.Vt extern int mib_netsock ;
+.Ft void
+.Fn mib_if_set_dyn "const char *ifname"
+.Ft void
+.Fn mib_refresh_iflist "void"
+.Ft struct mibif *
+.Fn mib_find_if "u_int ifindex"
+.Ft struct mibif *
+.Fn mib_find_if_sys "u_int sysindex"
+.Ft struct mibif *
+.Fn mib_find_if_name "const char *ifname"
+.Ft struct mibif *
+.Fn mib_first_if "void"
+.Ft struct mibif *
+.Fn mib_next_if "const struct mibif *ifp"
+.Ft int
+.Fn mib_register_newif "int (*func)(struct mibif *)" "const struct lmodule *mod"
+.Ft void
+.Fn mib_unregister_newif "const struct lmodule *mod"
+.Ft int
+.Fn mib_fetch_ifmib "struct mibif *ifp"
+.Ft int
+.Fn mib_if_admin "struct mibif *ifp" "int up"
+.Ft struct mibifa *
+.Fn mib_find_ifa "struct in_addr ipa"
+.Ft struct mibifa *
+.Fn mib_first_ififa "const struct mibif *ifp"
+.Ft struct mibifa *
+.Fn mib_next_ififa "struct mibifa *ifa"
+.Ft int
+.Fn mib_ifstack_create "const struct mibif *lower" "const struct mibif *upper"
+.Ft void
+.Fn mib_ifstack_delete "const struct mibif *lower" "const struct mibif *upper"
+.Ft struct mibrcvaddr *
+.Fn mib_find_rcvaddr "u_int ifindex" "const u_char *addr" "size_t addrlen"
+.Ft struct mibrcvaddr *
+.Fn mib_rcvaddr_create "struct mibif *ifp" "const u_char *addr" "size_t addrlen"
+.Ft void
+.Fn mib_rcvaddr_delete "struct mibrcvaddr *addr"
+.Ft void *
+.Fn mibif_notify "struct mibif *ifp" "const struct lmodule *mod" "mibif_notify_f func" "void *uarg"
+.Ft void
+.Fn mibif_unnotify "void *reg"
+.Sh DESCRIPTION
+The
+.Nm snmp_mibII
+module implements parts of the internet standard MIB-2. Most of the relevant
+MIBs are implemented. Some of the tables are restricted to be read-only
+instead of read-write. The exact current implementation can be found in
+.Pa /usr/local/include/bsnmp/mibII_tree.def .
+The module also exports a number of functions and global variables for use
+by other modules, that need to handle network interfaces. This man page describes
+these functions.
+.Ss DIRECT NETWORK ACCESS
+The
+.Nm
+module opens a socket that is used to execute all network related
+.Xr ioctl 2
+functions. This socket is globally available under the name
+.Va mib_netsock .
+.Ss NETWORK INTERFACES
+The
+.Nm
+module handles a list of all currently existing network interfaces. It allows
+other modules to handle their own interface lists with special information
+by providing a mechanism to register to events that change the interface list
+(see below). The basic data structure is the interface structure:
+.Bd -literal -offset indent
+struct mibif {
+ TAILQ_ENTRY(mibif) link;
+ u_int flags;
+ u_int index; /* logical ifindex */
+ u_int sysindex;
+ char name[IFNAMSIZ];
+ char descr[256];
+ struct ifmibdata mib;
+ u_int32_t mibtick;
+ void *specmib;
+ size_t specmiblen;
+ u_char *physaddr;
+ u_int physaddrlen;
+ int has_connector;
+ int trap_enable;
+ u_int32_t counter_disc;
+ mibif_notify_f xnotify;
+ void *xnotify_data;
+ const struct lmodule *xnotify_mod;
+};
+.Ed
+.Pp
+The
+.Nm
+module tries to implement the semantic if
+.Va ifIndex
+as described in RFC-2863. This RFC states, that an interface indexes may not
+be reused. That means, for example, if
+.Pa tun
+is a synthetic interface type and the system creates the interface
+.Pa tun0 ,
+destroys this interfaces and again creates a
+.Pa tun 0 ,
+then these interfaces must have different interface indexes, because in fact
+they are different interfaces. If, on the other hand, there is a hardware
+interface
+.Pa xl0
+and this interface disappears, because its driver is unloaded and appears
+again, because the driver is loaded again, the interface index must stay
+the same.
+.Nm
+implements this by differentiating between real and synthetic (dynamic)
+interfaces. An interface type can be declared dynamic by calling the function
+.Fn mib_if_set_dyn
+with the name if the interface type (for example
+.Qq tun ).
+For real interfaces, the module keeps the mapping between the interface name
+and its
+.Va ifIndex
+in a special list, if the interface is unloaded. For dynamic interfaces
+a new
+.Va ifIndex
+is generated each time the interface comes into existance. This
+means, that the interface index as seen by SNMP is not the same index
+as used by the system. The SNMP
+.Va ifIndex
+is held in field
+.Va index ,
+the system's interface index is
+.Va sysindex .
+.Pp
+A call to
+.Nm mib_refresh_iflist
+causes the entire interface list to be re-created.
+.Pp
+The interface list can be traversed with the functions
+.Fn mib_first_if
+and
+.Fn mib_next_if .
+Be sure not to change the interface list while traversing the list with
+these two calls.
+.Pp
+There are three functions to find an interface by name or index.
+.Fn mib_find_if
+finds an interface by searching for an SNMP
+.Va ifIndex ,
+.Fn mib_find_if_sys
+finds an interface by searching for a system interface index and
+.Fn mib_find_if_name
+finds an interface by looking for an interface name. Each of the
+function returns
+.Li NULL
+if the interface cannot be found.
+.Pp
+The function
+.Fn mib_fetch_ifmib
+causes the interface MIB to be refreshed from the kernel.
+.Pp
+The function
+.Fn mib_if_admin
+can be used to change the interface administrative state to up
+(argument is 1) or down (argument is 0).
+.Ss INTERFACE EVENTS
+A module can register itself to receive a notification when a new entry is
+created in the interface list. This is done by calling
+.Fn mib_register_newif .
+A module can register only one function, a second call to
+.Fn mib_register_newif
+causes the registration to be overwritten. The registration can be removed
+with a call to
+.Fn mib_unregister_newif .
+If is unregistered automatically, when the registering module is unloaded.
+.Pp
+A module can also register to events on a specific interface. This is done
+by calling
+.Fn mibif_notify .
+This causes the given callback
+.Fa func
+to be called with the interface pointer, a notification code and
+the user argument
+.Fa uarg
+when any of the following events occur:
+.Bl -tag -width "XXXXX"
+.It Li MIBIF_NOTIFY_DESTROY
+The interface is destroyed.
+.El
+.Pp
+This mechanism can be used to implement interface type specific MIB parts
+in other modules. The registration can be removed with
+.Fn mib_unnotify
+which the return value from
+.Fa mib_notify .
+Any notification registration is removed automatically when the interface
+is destroyed or the registering module is unloaded.
+.Em Note that only one module can register to any given interface .
+.Ss INTERFACE ADDRESSES
+The
+.Nm
+module handles a table of interface IP-addresses. These addresses are held
+in a
+.Bd -literal -offset indent
+struct mibifa {
+ TAILQ_ENTRY(mibifa) link;
+ struct in_addr inaddr;
+ struct in_addr inmask;
+ struct in_addr inbcast;
+ struct asn_oid index;
+ u_int ifindex;
+ u_int flags;
+};
+.Ed
+.Pp
+The (ordered) list of IP-addresses on a given interface can be traversed by
+calling
+.Fn mib_first_ififa
+and
+.Fn mib_next_ififa .
+The list should not be considered read-only.
+.Ss INTERFACE RECEIVE ADDRESSES
+The internet MIB-2 contains a table of interface receive addresses. These
+addresses are handled in:
+.Bd -literal -offset indent
+struct mibrcvaddr {
+ TAILQ_ENTRY(mibrcvaddr) link;
+ struct asn_oid index;
+ u_int ifindex;
+ u_char addr[ASN_MAXOIDLEN];
+ size_t addrlen;
+ u_int flags;
+};
+enum {
+ MIBRCVADDR_VOLATILE = 0x00000001,
+ MIBRCVADDR_BCAST = 0x00000002,
+ MIBRCVADDR_HW = 0x00000004,
+};
+.Ed
+.Pp
+Note, that the assignment of
+.Li MIBRCVADDR_BCAST
+is based on a list of known interface types. The flags should be handled
+by modules inplementing interface type specific MIBs.
+.Pp
+A receive address can be created with
+.Fn mib_rcvaddr_create
+and deleted with
+.Fn mib_rcvaddr_delete .
+This needs to be done only for addresses that are not automatically handled
+by the system.
+.Pp
+A receive address can be found with
+.Fn mib_find_rcvaddr .
+.Ss INTERFACE STACK TABLE
+The
+.Nm
+module maintains also the interface stack table. Because for complex stacks,
+there is no system supported generic way of getting this information, interface
+type specific modules need to help setting up stack entries. The
+.Nm
+module handles only the top and bottom entries.
+.Pp
+A table entry is created with
+.Fn mib_ifstack_create
+and deleted with
+.Fn mib_ifstack_delete .
+Both functions need the pointers to the interfaces. Entries are automatically
+deleted if any of the interfaces of the entry is destroyed. The functions handle
+both the stack table and the reverse stack table.
+.Sh FILES
+.Bl -tag -width ".It Pa /usr/local/include/bsnmp/mibII_tree.def" -compact
+.It Pa /usr/local/include/bsnmp/mibII_tree.def
+The description of the MIB tree implemented by
+.Nm .
+.It Pa /usr/local/share/snmp/mibs
+The various internet MIBs.
+.Sh SEE ALSO
+.Xr snmpmod 3 ,
+.Xr gensnmptree 1
+.Sh STANDARDS
+This implementation conforms to the applicable IETF RFCs.
+.Sh AUTHORS
+.An Hartmut Brandt Aq brandt@fokus.gmd.de
diff --git a/contrib/bsnmp/snmp_mibII/snmp_mibII.h b/contrib/bsnmp/snmp_mibII/snmp_mibII.h
new file mode 100644
index 0000000..79423cb
--- /dev/null
+++ b/contrib/bsnmp/snmp_mibII/snmp_mibII.h
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 2001-2003
+ * Fraunhofer Institute for Open Communication Systems (FhG Fokus).
+ * All rights reserved.
+ *
+ * Author: Harti Brandt <harti@freebsd.org>
+ *
+ * Redistribution of this software and documentation and use in source and
+ * binary forms, with or without modification, are permitted provided that
+ * the following conditions are met:
+ *
+ * 1. Redistributions of source code or documentation must retain the above
+ * copyright notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY FRAUNHOFER FOKUS
+ * AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * FRAUNHOFER FOKUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Begemot: bsnmp/snmp_mibII/snmp_mibII.h,v 1.13 2002/03/21 11:18:51 hbb Exp $
+ *
+ * Implementation of the interfaces and IP groups of MIB-II.
+ */
+#ifndef snmp_mibII_h_
+#define snmp_mibII_h_
+
+/* forward declaration */
+struct mibif;
+
+enum mibif_notify {
+ MIBIF_NOTIFY_DESTROY
+};
+
+typedef void (*mibif_notify_f)(struct mibif *, enum mibif_notify, void *);
+
+/*
+ * Interfaces. This structure describes one interface as seen in the MIB.
+ * Interfaces are indexed by ifindex. This is not the same as the index
+ * used by the system because of the rules in RFC-2863 section 3.1.5. This
+ * RFC requires, that an ifindex is not to be re-used for ANOTHER dynamically
+ * interfaces once the interface was deleted. The system's ifindex is in
+ * sysindex. Mapping is via the mapping table below.
+ */
+struct mibif {
+ TAILQ_ENTRY(mibif) link;
+ u_int flags;
+ u_int index; /* the logical ifindex */
+ u_int sysindex;
+ char name[IFNAMSIZ];
+ char descr[256];
+ struct ifmibdata mib;
+ u_int32_t mibtick;
+ void *specmib;
+ size_t specmiblen;
+ u_char *physaddr;
+ u_int physaddrlen;
+ int has_connector;
+ int trap_enable;
+ u_int32_t counter_disc;
+
+ /*
+ * This is needed to handle interface type specific information
+ * in sub-modules. It contains a function pointer which handles
+ * notifications and a data pointer to arbitrary data.
+ * Should be set via the mibif_notify function.
+ */
+ mibif_notify_f xnotify;
+ void *xnotify_data;
+ const struct lmodule *xnotify_mod;
+};
+
+/*
+ * Interface IP-address table.
+ */
+struct mibifa {
+ TAILQ_ENTRY(mibifa) link;
+ struct in_addr inaddr;
+ struct in_addr inmask;
+ struct in_addr inbcast;
+ struct asn_oid index; /* index for table search */
+ u_int ifindex;
+ u_int flags;
+};
+
+/*
+ * Interface receive addresses. Interface link-level multicast, broadcast
+ * and hardware addresses are handled automatically.
+ */
+struct mibrcvaddr {
+ TAILQ_ENTRY(mibrcvaddr) link;
+ struct asn_oid index;
+ u_int ifindex;
+ u_char addr[ASN_MAXOIDLEN];
+ size_t addrlen;
+ u_int flags;
+};
+enum {
+ MIBRCVADDR_VOLATILE = 0x00000001,
+ MIBRCVADDR_BCAST = 0x00000002,
+ MIBRCVADDR_HW = 0x00000004,
+};
+
+/* network socket */
+extern int mib_netsock;
+
+/* set an interface name to dynamic mode */
+void mib_if_set_dyn(const char *);
+
+/* re-read the systems interface list */
+void mib_refresh_iflist(void);
+
+/* find interface by index */
+struct mibif *mib_find_if(u_int);
+struct mibif *mib_find_if_sys(u_int);
+struct mibif *mib_find_if_name(const char *);
+
+/* iterate through all interfaces */
+struct mibif *mib_first_if(void);
+struct mibif *mib_next_if(const struct mibif *);
+
+/* register for interface creations */
+int mib_register_newif(int (*)(struct mibif *), const struct lmodule *);
+void mib_unregister_newif(const struct lmodule *);
+
+/* get fresh MIB data */
+int mib_fetch_ifmib(struct mibif *);
+
+/* change the ADMIN status of an interface and refresh the MIB */
+int mib_if_admin(struct mibif *, int up);
+
+/* find interface address by address */
+struct mibifa *mib_find_ifa(struct in_addr);
+
+/* find first/next address for a given interface */
+struct mibifa *mib_first_ififa(const struct mibif *);
+struct mibifa *mib_next_ififa(struct mibifa *);
+
+/* create/delete stacking entries */
+int mib_ifstack_create(const struct mibif *lower, const struct mibif *upper);
+void mib_ifstack_delete(const struct mibif *lower, const struct mibif *upper);
+
+/* find receive address */
+struct mibrcvaddr *mib_find_rcvaddr(u_int, const u_char *, size_t);
+
+/* create/delete receive addresses */
+struct mibrcvaddr *mib_rcvaddr_create(struct mibif *, const u_char *, size_t);
+void mib_rcvaddr_delete(struct mibrcvaddr *);
+
+/* register for interface notification */
+void *mibif_notify(struct mibif *, const struct lmodule *, mibif_notify_f,
+ void *);
+void mibif_unnotify(void *);
+
+#endif
diff --git a/contrib/bsnmp/snmpd/.gdbinit b/contrib/bsnmp/snmpd/.gdbinit
new file mode 100644
index 0000000..acfacec
--- /dev/null
+++ b/contrib/bsnmp/snmpd/.gdbinit
@@ -0,0 +1,2 @@
+dir ../snmp_netgraph
+dir ../snmp_mibII
diff --git a/contrib/bsnmp/snmpd/BEGEMOT-MIB.txt b/contrib/bsnmp/snmpd/BEGEMOT-MIB.txt
new file mode 100644
index 0000000..f282276
--- /dev/null
+++ b/contrib/bsnmp/snmpd/BEGEMOT-MIB.txt
@@ -0,0 +1,63 @@
+--
+-- Copyright (c) 2001-2003
+-- Fraunhofer Institute for Open Communication Systems (FhG Fokus).
+-- All rights reserved.
+--
+-- Author: Harti Brandt <harti@freebsd.org>
+--
+-- Redistribution of this software and documentation and use in source and
+-- binary forms, with or without modification, are permitted provided that
+-- the following conditions are met:
+--
+-- 1. Redistributions of source code or documentation must retain the above
+-- copyright notice, this list of conditions and the following disclaimer.
+-- 2. Redistributions in binary form must reproduce the above copyright
+-- notice, this list of conditions and the following disclaimer in the
+-- documentation and/or other materials provided with the distribution.
+-- 3. Neither the name of the Institute nor the names of its contributors
+-- may be used to endorse or promote products derived from this software
+-- without specific prior written permission.
+--
+-- THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY FRAUNHOFER FOKUS
+-- AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+-- INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+-- FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+-- FRAUNHOFER FOKUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+-- INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+-- LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+-- OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+-- LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+-- NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+-- EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+--
+-- $Begemot: bsnmp/snmpd/BEGEMOT-MIB.txt,v 1.3 2002/02/06 12:43:51 hbb Exp $
+--
+-- Begemot private definitions and root.
+--
+BEGEMOT-MIB DEFINITIONS ::= BEGIN
+
+IMPORTS
+ MODULE-IDENTITY
+ FROM SNMPv2-SMI
+ fokus
+ FROM FOKUS-MIB;
+
+begemot MODULE-IDENTITY
+ LAST-UPDATED "200201300000Z"
+ ORGANIZATION "Fraunhofer FOKUS, CATS"
+ CONTACT-INFO
+ " Hartmut Brandt
+
+ Postal: Fraunhofer Institute for Open Communication Systems
+ Kaiserin-Augusta-Allee 31
+ 10589 Berlin
+ Germany
+
+ Fax: +49 30 3463 7352
+
+ E-mail: harti@freebsd.org"
+ DESCRIPTION
+ "The root of the Begemot subtree of the fokus tree."
+ ::= { fokus 1 }
+
+END
diff --git a/contrib/bsnmp/snmpd/BEGEMOT-SNMPD.txt b/contrib/bsnmp/snmpd/BEGEMOT-SNMPD.txt
new file mode 100644
index 0000000..8c0996b
--- /dev/null
+++ b/contrib/bsnmp/snmpd/BEGEMOT-SNMPD.txt
@@ -0,0 +1,482 @@
+--
+-- Copyright (c) 2001-2003
+-- Fraunhofer Institute for Open Communication Systems (FhG Fokus).
+-- All rights reserved.
+--
+-- Author: Harti Brandt <harti@freebsd.org>
+--
+-- Redistribution of this software and documentation and use in source and
+-- binary forms, with or without modification, are permitted provided that
+-- the following conditions are met:
+--
+-- 1. Redistributions of source code or documentation must retain the above
+-- copyright notice, this list of conditions and the following disclaimer.
+-- 2. Redistributions in binary form must reproduce the above copyright
+-- notice, this list of conditions and the following disclaimer in the
+-- documentation and/or other materials provided with the distribution.
+-- 3. Neither the name of the Institute nor the names of its contributors
+-- may be used to endorse or promote products derived from this software
+-- without specific prior written permission.
+--
+-- THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY FRAUNHOFER FOKUS
+-- AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+-- INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+-- FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+-- FRAUNHOFER FOKUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+-- INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+-- LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+-- OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+-- LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+-- NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+-- EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+--
+-- $Begemot: bsnmp/snmpd/BEGEMOT-SNMPD.txt,v 1.18 2002/12/11 15:54:07 hbb Exp $
+--
+-- Begemot Private SNMPd MIB.
+--
+BEGEMOT-SNMPD-MIB DEFINITIONS ::= BEGIN
+
+IMPORTS
+ MODULE-IDENTITY, OBJECT-TYPE, OBJECT-IDENTITY, Counter32,
+ Unsigned32
+ FROM SNMPv2-SMI
+ TEXTUAL-CONVENTION, TruthValue, RowStatus
+ FROM SNMPv2-TC
+ MODULE-COMPLIANCE, OBJECT-GROUP
+ FROM SNMPv2-CONF
+ begemot
+ FROM BEGEMOT-MIB;
+
+begemotSnmpd MODULE-IDENTITY
+ LAST-UPDATED "200212040000Z"
+ ORGANIZATION "Fraunhofer FOKUS, CATS"
+ CONTACT-INFO
+ " Hartmut Brandt
+
+ Postal: Fraunhofer Institute for Open Communication Systems
+ Kaiserin-Augusta-Allee 31
+ 10589 Berlin
+ Germany
+
+ Fax: +49 30 3463 7352
+
+ E-mail: harti@freebsd.org"
+ DESCRIPTION
+ "The MIB module for the Begemot SNMP daemon."
+ ::= { begemot 1 }
+
+begemotSnmpdObjects OBJECT IDENTIFIER ::= { begemotSnmpd 1 }
+begemotSnmpdDefs OBJECT IDENTIFIER ::= { begemotSnmpd 2 }
+
+-- --------------------------------------------------------------------------
+
+SectionName ::= TEXTUAL-CONVENTION
+ DISPLAY-HINT "14a"
+ STATUS current
+ DESCRIPTION
+ "Name of a loadable module. Should consist of alphanumeric characers
+ only, the first character must be a letter."
+ SYNTAX OCTET STRING (SIZE(1..14))
+
+-- --------------------------------------------------------------------------
+--
+-- Agent types
+--
+begemotSnmpdAgent OBJECT IDENTIFIER ::= { begemotSnmpdDefs 1 }
+
+begemotSnmpdAgentFreeBSD OBJECT-IDENTITY
+ STATUS current
+ DESCRIPTION
+ "Identifies the agent as running on FreeBSD."
+ ::= { begemotSnmpdAgent 1 }
+
+-- --------------------------------------------------------------------------
+--
+-- The Config Group
+--
+begemotSnmpdConfig OBJECT IDENTIFIER ::= { begemotSnmpdObjects 1 }
+
+begemotSnmpdTransmitBuffer OBJECT-TYPE
+ SYNTAX INTEGER (484..65535)
+ MAX-ACCESS read-write
+ STATUS current
+ DESCRIPTION
+ "The size of the receive buffer in bytes. Larger messages
+ are dropped by SNMPd."
+ DEFVAL { 2048 }
+ ::= { begemotSnmpdConfig 1 }
+
+begemotSnmpdReceiveBuffer OBJECT-TYPE
+ SYNTAX INTEGER (484..65535)
+ MAX-ACCESS read-write
+ STATUS current
+ DESCRIPTION
+ "The size of the transmit buffer in bytes. Larger messages
+ cannot be sent by the SNMPd."
+ DEFVAL { 2048 }
+ ::= { begemotSnmpdConfig 2 }
+
+begemotSnmpdCommunityDisable OBJECT-TYPE
+ SYNTAX TruthValue
+ MAX-ACCESS read-write
+ STATUS current
+ DESCRIPTION
+ "Disables all access to the CommunityTable from SNMP. Once
+ set it cannot be cleared."
+ DEFVAL { false }
+ ::= { begemotSnmpdConfig 3 }
+
+begemotSnmpdTrap1Addr OBJECT-TYPE
+ SYNTAX IpAddress
+ MAX-ACCESS read-write
+ STATUS current
+ DESCRIPTION
+ "The trap sink for v1 traps."
+ ::= { begemotSnmpdConfig 4 }
+
+--
+-- Trap destinations
+--
+begemotTrapSinkTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF BegemotTrapSinkEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "A table with destinations for standard traps."
+ INDEX { begemotTrapSinkAddr, begemotTrapSinkPort }
+ ::= { begemotSnmpdObjects 2 }
+
+begemotTrapSinkEntry OBJECT-TYPE
+ SYNTAX BegemotTrapSinkEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "Entry describes one trap destination."
+ INDEX { begemotTrapSinkAddr, begemotTrapSinkPort }
+ ::= { begemotTrapSinkTable 1 }
+
+BegemotTrapSinkEntry ::= SEQUENCE {
+ begemotTrapSinkAddr IpAddress,
+ begemotTrapSinkPort INTEGER,
+ begemotTrapSinkStatus RowStatus
+}
+
+begemotTrapSinkAddr OBJECT-TYPE
+ SYNTAX IpAddress
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "Destination IP address of the manager station where to send
+ traps."
+ ::= { begemotTrapSinkEntry 1 }
+
+begemotTrapSinkPort OBJECT-TYPE
+ SYNTAX INTEGER (1..65535)
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "Destination UDP port of the manager station where to send
+ traps."
+ ::= { begemotTrapSinkEntry 2 }
+
+begemotTrapSinkStatus OBJECT-TYPE
+ SYNTAX RowStatus
+ MAX-ACCESS read-create
+ STATUS current
+ DESCRIPTION
+ "Used to create/activate/destroy the entry."
+ ::= { begemotTrapSinkEntry 3 }
+
+--
+-- SNMP port table
+--
+begemotSnmpdPortTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF BegemotSnmpdPortEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "A table with descriptions of UDP ports to listen on
+ for SNMP messages."
+ ::= { begemotSnmpdObjects 4 }
+
+begemotSnmpdPortEntry OBJECT-TYPE
+ SYNTAX BegemotSnmpdPortEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "An entry in the table with descriptions of UDP ports to
+ listen on for SNMP messages."
+ INDEX { begemotSnmpdPortAddress, begemotSnmpdPortPort }
+ ::= { begemotSnmpdPortTable 1 }
+
+BegemotSnmpdPortEntry ::= SEQUENCE {
+ begemotSnmpdPortAddress IpAddress,
+ begemotSnmpdPortPort INTEGER,
+ begemotSnmpdPortStatus INTEGER
+}
+
+begemotSnmpdPortAddress OBJECT-TYPE
+ SYNTAX IpAddress
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "The IP address to bind to."
+ ::= { begemotSnmpdPortEntry 1 }
+
+begemotSnmpdPortPort OBJECT-TYPE
+ SYNTAX INTEGER (1..65535)
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "The UDP port to listen on for SNMP messages."
+ ::= { begemotSnmpdPortEntry 2 }
+
+begemotSnmpdPortStatus OBJECT-TYPE
+ SYNTAX INTEGER { valid(1), invalid(2) }
+ MAX-ACCESS read-create
+ STATUS current
+ DESCRIPTION
+ "Set status to 1 to create entry, set it to 2 to delete it."
+ ::= { begemotSnmpdPortEntry 3 }
+
+---
+--- Community table
+---
+begemotSnmpdCommunityTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF BegemotSnmpdCommunityEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "A table with the community strings for access control."
+ ::= { begemotSnmpdObjects 5 }
+
+begemotSnmpdCommunityEntry OBJECT-TYPE
+ SYNTAX BegemotSnmpdCommunityEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "A table with the community strings for access control.
+ When begemotSnmpdCommDisable is true, this table disappears."
+ INDEX { begemotSnmpdCommunityModule, begemotSnmpdCommunityIndex }
+ ::= { begemotSnmpdCommunityTable 1 }
+
+BegemotSnmpdCommunityEntry ::= SEQUENCE {
+ begemotSnmpdCommunityModule SectionName,
+ begemotSnmpdCommunityIndex Unsigned32,
+ begemotSnmpdCommunityString OCTET STRING,
+ begemotSnmpdCommunityDescr OCTET STRING
+}
+
+begemotSnmpdCommunityModule OBJECT-TYPE
+ SYNTAX SectionName
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "Index of the module that has registered this community.
+ For global communities this is the empty string."
+ ::= { begemotSnmpdCommunityEntry 1 }
+
+begemotSnmpdCommunityIndex OBJECT-TYPE
+ SYNTAX Unsigned32 (1..4294967295)
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "The numerical index of the community (private to the module)."
+ ::= { begemotSnmpdCommunityEntry 2 }
+
+begemotSnmpdCommunityString OBJECT-TYPE
+ SYNTAX OCTET STRING
+ MAX-ACCESS read-write
+ STATUS current
+ DESCRIPTION
+ "The string for access to SNMPd."
+ ::= { begemotSnmpdCommunityEntry 3 }
+
+begemotSnmpdCommunityDescr OBJECT-TYPE
+ SYNTAX OCTET STRING
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "A description what this community is good for."
+ ::= { begemotSnmpdCommunityEntry 4 }
+
+--
+-- Module table
+--
+begemotSnmpdModuleTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF BegemotSnmpdModuleEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "A table describing all the currently loaded dynamic modules.
+ Writing to this table loads and unloads modules."
+ ::= { begemotSnmpdObjects 6 }
+
+begemotSnmpdModuleEntry OBJECT-TYPE
+ SYNTAX BegemotSnmpdModuleEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "A table entry describing a loadable module."
+ INDEX { begemotSnmpdModuleSection }
+ ::= { begemotSnmpdModuleTable 1 }
+
+BegemotSnmpdModuleEntry ::= SEQUENCE {
+ begemotSnmpdModuleSection SectionName,
+ begemotSnmpdModulePath OCTET STRING,
+ begemotSnmpdModuleComment OCTET STRING
+}
+
+begemotSnmpdModuleSection OBJECT-TYPE
+ SYNTAX SectionName
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "The string used for matching configuration file sections
+ and indexes the module table."
+ ::= { begemotSnmpdModuleEntry 1 }
+
+
+begemotSnmpdModulePath OBJECT-TYPE
+ SYNTAX OCTET STRING
+ MAX-ACCESS read-create
+ STATUS current
+ DESCRIPTION
+ "The path name of the module. Set to empty string
+ to unload a module. The path of an existing module
+ may not be changed."
+ ::= { begemotSnmpdModuleEntry 2 }
+
+begemotSnmpdModuleComment OBJECT-TYPE
+ SYNTAX OCTET STRING
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "A comment describing this module."
+ ::= { begemotSnmpdModuleEntry 3 }
+
+
+-- --------------------------------------------------------------------------
+--
+-- The STATISTICS Group
+--
+begemotSnmpdStats OBJECT IDENTIFIER ::= { begemotSnmpdObjects 7 }
+
+begemotSnmpdStatsNoRxBufs OBJECT-TYPE
+ SYNTAX Counter32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of times a receive buffer could not be allocated
+ for a packet."
+ ::= { begemotSnmpdStats 1 }
+
+begemotSnmpdStatsNoTxBufs OBJECT-TYPE
+ SYNTAX Counter32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of times a transmit buffer could not be allocated
+ for a packet."
+ ::= { begemotSnmpdStats 2 }
+
+begemotSnmpdStatsInTooLongPkts OBJECT-TYPE
+ SYNTAX Counter32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of packets received that were longer than the
+ receive buffer. These packets are dropped."
+ ::= { begemotSnmpdStats 3 }
+
+begemotSnmpdStatsInBadPduTypes OBJECT-TYPE
+ SYNTAX Counter32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of packets received with a bad type field."
+ ::= { begemotSnmpdStats 4 }
+
+--
+-- The Debug Group
+--
+begemotSnmpdDebug OBJECT IDENTIFIER ::= { begemotSnmpdObjects 8 }
+
+begemotSnmpdDebugDumpPdus OBJECT-TYPE
+ SYNTAX TruthValue
+ MAX-ACCESS read-write
+ STATUS current
+ DESCRIPTION
+ "Dump PDUs to log file if true."
+ DEFVAL { false }
+ ::= { begemotSnmpdDebug 1 }
+
+begemotSnmpdDebugSnmpTrace OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-write
+ STATUS current
+ DESCRIPTION
+ "Tracing flags for the SNMP library. These flags have the
+ following meaning:
+ 0x00000001 trace GET operator
+ 0x00000002 trace GETNEXT operator
+ 0x00000004 trace SET operator
+ 0x00000008 trace dependency processing
+ 0x00000010 trace node finding
+ Individual values can be or-ed together."
+ DEFVAL { 0 }
+ ::= { begemotSnmpdDebug 2 }
+
+begemotSnmpdDebugSyslogPri OBJECT-TYPE
+ SYNTAX INTEGER (0..8)
+ MAX-ACCESS read-write
+ STATUS current
+ DESCRIPTION
+ "Events with this or higher priority should not be logged."
+ DEFVAL { 7 } -- don't log debug messages
+ ::= { begemotSnmpdDebug 3 }
+
+--
+-- Local port table
+--
+begemotSnmpdLocalPortTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF BegemotSnmpdLocalPortEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "A table with descriptions of local (unix domain) ports to listen
+ on for SNMP messages."
+ ::= { begemotSnmpdObjects 9 }
+
+begemotSnmpdLocalPortEntry OBJECT-TYPE
+ SYNTAX BegemotSnmpdLocalPortEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "An entry in the table with descriptions of local ports to
+ listen on for SNMP messages."
+ INDEX { begemotSnmpdLocalPortPath }
+ ::= { begemotSnmpdLocalPortTable 1 }
+
+BegemotSnmpdLocalPortEntry ::= SEQUENCE {
+ begemotSnmpdLocalPortPath OCTET STRING,
+ begemotSnmpdLocalPortStatus INTEGER
+}
+
+begemotSnmpdLocalPortPath OBJECT-TYPE
+ SYNTAX OCTET STRING (SIZE(1..104))
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "The path name to create and listen on."
+ ::= { begemotSnmpdLocalPortEntry 1 }
+
+begemotSnmpdLocalPortStatus OBJECT-TYPE
+ SYNTAX INTEGER { valid(1), invalid(2) }
+ MAX-ACCESS read-create
+ STATUS current
+ DESCRIPTION
+ "Set status to 1 to create entry, set it to 2 to delete it."
+ ::= { begemotSnmpdLocalPortEntry 2 }
+
+END
diff --git a/contrib/bsnmp/snmpd/FOKUS-MIB.txt b/contrib/bsnmp/snmpd/FOKUS-MIB.txt
new file mode 100644
index 0000000..c0939ba
--- /dev/null
+++ b/contrib/bsnmp/snmpd/FOKUS-MIB.txt
@@ -0,0 +1,61 @@
+--
+-- Copyright (c) 2001-2003
+-- Fraunhofer Institute for Open Communication Systems (FhG Fokus).
+-- All rights reserved.
+--
+-- Author: Harti Brandt <harti@freebsd.org>
+--
+-- Redistribution of this software and documentation and use in source and
+-- binary forms, with or without modification, are permitted provided that
+-- the following conditions are met:
+--
+-- 1. Redistributions of source code or documentation must retain the above
+-- copyright notice, this list of conditions and the following disclaimer.
+-- 2. Redistributions in binary form must reproduce the above copyright
+-- notice, this list of conditions and the following disclaimer in the
+-- documentation and/or other materials provided with the distribution.
+-- 3. Neither the name of the Institute nor the names of its contributors
+-- may be used to endorse or promote products derived from this software
+-- without specific prior written permission.
+--
+-- THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY FRAUNHOFER FOKUS
+-- AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+-- INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+-- FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+-- FRAUNHOFER FOKUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+-- INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+-- LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+-- OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+-- LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+-- NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+-- EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+--
+-- $Begemot: bsnmp/snmpd/FOKUS-MIB.txt,v 1.3 2002/02/06 12:43:51 hbb Exp $
+--
+-- Begemot private definitions and fokus root.
+--
+FOKUS-MIB DEFINITIONS ::= BEGIN
+
+IMPORTS
+ MODULE-IDENTITY, enterprises
+ FROM SNMPv2-SMI;
+
+fokus MODULE-IDENTITY
+ LAST-UPDATED "200202050000Z"
+ ORGANIZATION "Fraunhofer FOKUS, CATS"
+ CONTACT-INFO
+ " Hartmut Brandt
+
+ Postal: Fraunhofer Institute for Open Communication Systems
+ Kaiserin-Augusta-Allee 31
+ 10589 Berlin
+ Germany
+
+ Fax: +49 30 3463 7352
+
+ E-mail: harti@freebsd.org"
+ DESCRIPTION
+ "The root of the Fokus enterprises tree."
+ ::= { enterprises 12325 }
+
+END
diff --git a/contrib/bsnmp/snmpd/action.c b/contrib/bsnmp/snmpd/action.c
new file mode 100644
index 0000000..7c87bea
--- /dev/null
+++ b/contrib/bsnmp/snmpd/action.c
@@ -0,0 +1,1145 @@
+/*
+ * Copyright (c) 2001-2003
+ * Fraunhofer Institute for Open Communication Systems (FhG Fokus).
+ * All rights reserved.
+ *
+ * Author: Harti Brandt <harti@freebsd.org>
+ *
+ * Redistribution of this software and documentation and use in source and
+ * binary forms, with or without modification, are permitted provided that
+ * the following conditions are met:
+ *
+ * 1. Redistributions of source code or documentation must retain the above
+ * copyright notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY FRAUNHOFER FOKUS
+ * AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * FRAUNHOFER FOKUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Begemot: bsnmp/snmpd/action.c,v 1.53 2003/01/28 13:44:35 hbb Exp $
+ *
+ * Variable access for SNMPd
+ */
+#include <sys/types.h>
+#include <sys/sysctl.h>
+#include <sys/un.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <ctype.h>
+#include <syslog.h>
+
+#include "snmpmod.h"
+#include "snmpd.h"
+#include "tree.h"
+#include "oid.h"
+
+static const struct asn_oid
+ oid_begemotSnmpdModuleTable = OIDX_begemotSnmpdModuleTable;
+
+/*
+ * Get a string value from the KERN sysctl subtree.
+ */
+static char *
+act_getkernstring(int id)
+{
+ int mib[2];
+ size_t len;
+ char *string;
+
+ mib[0] = CTL_KERN;
+ mib[1] = id;
+ if (sysctl(mib, 2, NULL, &len, NULL, 0) != 0)
+ return (NULL);
+ if ((string = malloc(len)) == NULL)
+ return (NULL);
+ if (sysctl(mib, 2, string, &len, NULL, 0) != 0) {
+ free(string);
+ return (NULL);
+ }
+ return (string);
+}
+
+/*
+ * Get an integer value from the KERN sysctl subtree.
+ */
+static char *
+act_getkernint(int id)
+{
+ int mib[2];
+ size_t len;
+ u_long value;
+ char *string;
+
+ mib[0] = CTL_KERN;
+ mib[1] = id;
+ len = sizeof(value);
+ if (sysctl(mib, 2, &value, &len, NULL, 0) != 0)
+ return (NULL);
+
+ if ((string = malloc(20)) == NULL)
+ return (NULL);
+ sprintf(string, "%lu", value);
+ return (string);
+}
+
+/*
+ * Initialize global variables of the system group.
+ */
+int
+init_actvals(void)
+{
+ char *v[4];
+ u_int i;
+ size_t len;
+
+ if ((systemg.name = act_getkernstring(KERN_HOSTNAME)) == NULL)
+ return (-1);
+
+ for (i = 0; i < 4; i++)
+ v[1] = NULL;
+
+ if ((v[0] = act_getkernstring(KERN_HOSTNAME)) == NULL)
+ goto err;
+ if ((v[1] = act_getkernint(KERN_HOSTID)) == NULL)
+ goto err;
+ if ((v[2] = act_getkernstring(KERN_OSTYPE)) == NULL)
+ goto err;
+ if ((v[3] = act_getkernstring(KERN_OSRELEASE)) == NULL)
+ goto err;
+
+ for (i = 0, len = 0; i < 4; i++)
+ len += strlen(v[i]) + 1;
+
+ if ((systemg.descr = malloc(len)) == NULL)
+ goto err;
+ sprintf(systemg.descr, "%s %s %s %s", v[0], v[1], v[2], v[3]);
+
+ return (0);
+
+ err:
+ for (i = 0; i < 4; i++)
+ if (v[i] != NULL)
+ free(v[i]);
+ return (-1);
+}
+
+
+
+/*************************************************************
+ *
+ * System group
+ */
+int
+op_system_group(struct snmp_context *ctx, struct snmp_value *value,
+ u_int sub, u_int iidx __unused, enum snmp_op op)
+{
+ asn_subid_t which = value->var.subs[sub - 1];
+
+ switch (op) {
+
+ case SNMP_OP_GETNEXT:
+ abort();
+
+ case SNMP_OP_GET:
+ break;
+
+ case SNMP_OP_SET:
+ switch (which) {
+
+ case LEAF_sysDescr:
+ if (community != COMM_INITIALIZE)
+ return (SNMP_ERR_NOT_WRITEABLE);
+ return (string_save(value, ctx, -1, &systemg.descr));
+
+ case LEAF_sysObjectId:
+ if (community != COMM_INITIALIZE)
+ return (SNMP_ERR_NOT_WRITEABLE);
+ return (oid_save(value, ctx, &systemg.object_id));
+
+ case LEAF_sysContact:
+ return (string_save(value, ctx, -1, &systemg.contact));
+
+ case LEAF_sysName:
+ return (string_save(value, ctx, -1, &systemg.name));
+
+ case LEAF_sysLocation:
+ return (string_save(value, ctx, -1, &systemg.location));
+ }
+ return (SNMP_ERR_NO_CREATION);
+
+ case SNMP_OP_ROLLBACK:
+ switch (which) {
+
+ case LEAF_sysDescr:
+ string_rollback(ctx, &systemg.descr);
+ return (SNMP_ERR_NOERROR);
+ case LEAF_sysObjectId:
+ oid_rollback(ctx, &systemg.object_id);
+ return (SNMP_ERR_NOERROR);
+ case LEAF_sysContact:
+ string_rollback(ctx, &systemg.contact);
+ return (SNMP_ERR_NOERROR);
+ case LEAF_sysName:
+ string_rollback(ctx, &systemg.name);
+ return (SNMP_ERR_NOERROR);
+ case LEAF_sysLocation:
+ string_rollback(ctx, &systemg.location);
+ return (SNMP_ERR_NOERROR);
+ }
+ abort();
+
+ case SNMP_OP_COMMIT:
+ switch (which) {
+
+ case LEAF_sysDescr:
+ string_commit(ctx);
+ return (SNMP_ERR_NOERROR);
+ case LEAF_sysObjectId:
+ oid_commit(ctx);
+ return (SNMP_ERR_NOERROR);
+ case LEAF_sysContact:
+ string_commit(ctx);
+ return (SNMP_ERR_NOERROR);
+ case LEAF_sysName:
+ string_commit(ctx);
+ return (SNMP_ERR_NOERROR);
+ case LEAF_sysLocation:
+ string_commit(ctx);
+ return (SNMP_ERR_NOERROR);
+ }
+ abort();
+ }
+
+ /*
+ * Come here for GET.
+ */
+ switch (which) {
+
+ case LEAF_sysDescr:
+ return (string_get(value, systemg.descr, -1));
+ case LEAF_sysObjectId:
+ return (oid_get(value, &systemg.object_id));
+ case LEAF_sysUpTime:
+ value->v.uint32 = get_ticks() - start_tick;
+ break;
+ case LEAF_sysContact:
+ return (string_get(value, systemg.contact, -1));
+ case LEAF_sysName:
+ return (string_get(value, systemg.name, -1));
+ case LEAF_sysLocation:
+ return (string_get(value, systemg.location, -1));
+ case LEAF_sysServices:
+ value->v.integer = systemg.services;
+ break;
+ case LEAF_sysORLastChange:
+ value->v.uint32 = systemg.or_last_change;
+ break;
+ }
+ return (SNMP_ERR_NOERROR);
+}
+
+/*************************************************************
+ *
+ * Debug group
+ */
+int
+op_debug(struct snmp_context *ctx, struct snmp_value *value, u_int sub,
+ u_int iidx __unused, enum snmp_op op)
+{
+ asn_subid_t which = value->var.subs[sub - 1];
+
+ switch (op) {
+
+ case SNMP_OP_GETNEXT:
+ abort();
+
+ case SNMP_OP_GET:
+ switch (which) {
+
+ case LEAF_begemotSnmpdDebugDumpPdus:
+ value->v.integer = TRUTH_MK(debug.dump_pdus);
+ break;
+
+ case LEAF_begemotSnmpdDebugSnmpTrace:
+ value->v.uint32 = snmp_trace;
+ break;
+
+ case LEAF_begemotSnmpdDebugSyslogPri:
+ value->v.integer = debug.logpri;
+ break;
+ }
+ return (SNMP_ERR_NOERROR);
+
+ case SNMP_OP_SET:
+ switch (which) {
+
+ case LEAF_begemotSnmpdDebugDumpPdus:
+ if (!TRUTH_OK(value->v.integer))
+ return (SNMP_ERR_WRONG_VALUE);
+ ctx->scratch->int1 = debug.dump_pdus;
+ debug.dump_pdus = TRUTH_GET(value->v.integer);
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotSnmpdDebugSnmpTrace:
+ ctx->scratch->int1 = snmp_trace;
+ snmp_trace = value->v.uint32;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotSnmpdDebugSyslogPri:
+ if (value->v.integer < 0 || value->v.integer > 8)
+ return (SNMP_ERR_WRONG_VALUE);
+ ctx->scratch->int1 = debug.logpri;
+ debug.logpri = (u_int)value->v.integer;
+ return (SNMP_ERR_NOERROR);
+ }
+ return (SNMP_ERR_NO_CREATION);
+
+ case SNMP_OP_ROLLBACK:
+ switch (which) {
+
+ case LEAF_begemotSnmpdDebugDumpPdus:
+ debug.dump_pdus = ctx->scratch->int1;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotSnmpdDebugSnmpTrace:
+ snmp_trace = ctx->scratch->int1;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotSnmpdDebugSyslogPri:
+ debug.logpri = ctx->scratch->int1;
+ return (SNMP_ERR_NOERROR);
+ }
+ abort();
+
+ case SNMP_OP_COMMIT:
+ switch (which) {
+
+ case LEAF_begemotSnmpdDebugDumpPdus:
+ case LEAF_begemotSnmpdDebugSnmpTrace:
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotSnmpdDebugSyslogPri:
+ if (debug.logpri == 0)
+ setlogmask(0);
+ else
+ setlogmask(LOG_UPTO(debug.logpri - 1));
+ return (SNMP_ERR_NOERROR);
+ }
+ abort();
+ }
+ abort();
+}
+
+/*************************************************************
+ *
+ * OR Table
+ */
+int
+op_or_table(struct snmp_context *ctx __unused, struct snmp_value *value,
+ u_int sub, u_int iidx __unused, enum snmp_op op)
+{
+ struct objres *objres;
+
+ switch (op) {
+
+ case SNMP_OP_GETNEXT:
+ if ((objres = NEXT_OBJECT_INT(&objres_list, &value->var, sub))
+ == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ value->var.subs[sub] = objres->index;
+ value->var.len = sub + 1;
+ break;
+
+ case SNMP_OP_GET:
+ if ((objres = FIND_OBJECT_INT(&objres_list, &value->var, sub))
+ == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ break;
+
+ case SNMP_OP_SET:
+ if ((objres = FIND_OBJECT_INT(&objres_list, &value->var, sub))
+ == NULL)
+ return (SNMP_ERR_NO_CREATION);
+ return (SNMP_ERR_NOT_WRITEABLE);
+
+ case SNMP_OP_ROLLBACK:
+ case SNMP_OP_COMMIT:
+ default:
+ abort();
+ }
+
+ /*
+ * Come here for GET, GETNEXT.
+ */
+ switch (value->var.subs[sub - 1]) {
+
+ case LEAF_sysORID:
+ value->v.oid = objres->oid;
+ break;
+
+ case LEAF_sysORDescr:
+ return (string_get(value, objres->descr, -1));
+
+ case LEAF_sysORUpTime:
+ value->v.uint32 = objres->uptime;
+ break;
+ }
+ return (SNMP_ERR_NOERROR);
+}
+
+/*************************************************************
+ *
+ * mib-2 snmp
+ */
+int
+op_snmp(struct snmp_context *ctx, struct snmp_value *value,
+ u_int sub, u_int iidx __unused, enum snmp_op op)
+{
+ switch (op) {
+
+ case SNMP_OP_GETNEXT:
+ abort();
+
+ case SNMP_OP_GET:
+ switch (value->var.subs[sub - 1]) {
+
+ case LEAF_snmpInPkts:
+ value->v.uint32 = snmpd_stats.inPkts;
+ break;
+
+ case LEAF_snmpInBadVersions:
+ value->v.uint32 = snmpd_stats.inBadVersions;
+ break;
+
+ case LEAF_snmpInBadCommunityNames:
+ value->v.uint32 = snmpd_stats.inBadCommunityNames;
+ break;
+
+ case LEAF_snmpInBadCommunityUses:
+ value->v.uint32 = snmpd_stats.inBadCommunityUses;
+ break;
+
+ case LEAF_snmpInASNParseErrs:
+ value->v.uint32 = snmpd_stats.inASNParseErrs;
+ break;
+
+ case LEAF_snmpEnableAuthenTraps:
+ value->v.integer = TRUTH_MK(snmpd.auth_traps);
+ break;
+
+ case LEAF_snmpSilentDrops:
+ value->v.uint32 = snmpd_stats.silentDrops;
+ break;
+
+ case LEAF_snmpProxyDrops:
+ value->v.uint32 = snmpd_stats.proxyDrops;
+ break;
+
+ default:
+ return (SNMP_ERR_NOSUCHNAME);
+
+ }
+ return (SNMP_ERR_NOERROR);
+
+ case SNMP_OP_SET:
+ switch (value->var.subs[sub - 1]) {
+ case LEAF_snmpEnableAuthenTraps:
+ if (!TRUTH_OK(value->v.integer))
+ return (SNMP_ERR_WRONG_VALUE);
+ ctx->scratch->int1 = value->v.integer;
+ snmpd.auth_traps = TRUTH_GET(value->v.integer);
+ return (SNMP_ERR_NOERROR);
+ }
+ abort();
+
+ case SNMP_OP_ROLLBACK:
+ switch (value->var.subs[sub - 1]) {
+ case LEAF_snmpEnableAuthenTraps:
+ snmpd.auth_traps = ctx->scratch->int1;
+ return (SNMP_ERR_NOERROR);
+ }
+ abort();
+
+ case SNMP_OP_COMMIT:
+ switch (value->var.subs[sub - 1]) {
+ case LEAF_snmpEnableAuthenTraps:
+ return (SNMP_ERR_NOERROR);
+ }
+ abort();
+ }
+ abort();
+}
+
+/*************************************************************
+ *
+ * SNMPd statistics group
+ */
+int
+op_snmpd_stats(struct snmp_context *ctx __unused, struct snmp_value *value,
+ u_int sub, u_int iidx __unused, enum snmp_op op)
+{
+ switch (op) {
+
+ case SNMP_OP_GET:
+ switch (value->var.subs[sub - 1]) {
+
+ case LEAF_begemotSnmpdStatsNoRxBufs:
+ value->v.uint32 = snmpd_stats.noRxbuf;
+ break;
+
+ case LEAF_begemotSnmpdStatsNoTxBufs:
+ value->v.uint32 = snmpd_stats.noTxbuf;
+ break;
+
+ case LEAF_begemotSnmpdStatsInTooLongPkts:
+ value->v.uint32 = snmpd_stats.inTooLong;
+ break;
+
+ case LEAF_begemotSnmpdStatsInBadPduTypes:
+ value->v.uint32 = snmpd_stats.inBadPduTypes;
+ break;
+
+ default:
+ return (SNMP_ERR_NOSUCHNAME);
+ }
+ return (SNMP_ERR_NOERROR);
+
+ case SNMP_OP_SET:
+ case SNMP_OP_ROLLBACK:
+ case SNMP_OP_COMMIT:
+ case SNMP_OP_GETNEXT:
+ abort();
+ }
+ abort();
+}
+
+/*
+ * SNMPd configuration scalars
+ */
+int
+op_snmpd_config(struct snmp_context *ctx, struct snmp_value *value,
+ u_int sub, u_int iidx __unused, enum snmp_op op)
+{
+ asn_subid_t which = value->var.subs[sub - 1];
+
+ switch (op) {
+
+ case SNMP_OP_GETNEXT:
+ abort();
+
+ case SNMP_OP_GET:
+ switch (which) {
+
+ case LEAF_begemotSnmpdTransmitBuffer:
+ value->v.integer = snmpd.txbuf;
+ break;
+ case LEAF_begemotSnmpdReceiveBuffer:
+ value->v.integer = snmpd.rxbuf;
+ break;
+ case LEAF_begemotSnmpdCommunityDisable:
+ value->v.integer = TRUTH_MK(snmpd.comm_dis);
+ break;
+ case LEAF_begemotSnmpdTrap1Addr:
+ return (ip_get(value, snmpd.trap1addr));
+ default:
+ return (SNMP_ERR_NOSUCHNAME);
+ }
+ return (SNMP_ERR_NOERROR);
+
+ case SNMP_OP_SET:
+ switch (which) {
+
+ case LEAF_begemotSnmpdTransmitBuffer:
+ ctx->scratch->int1 = snmpd.txbuf;
+ if (value->v.integer < 484 ||
+ value->v.integer > 65535)
+ return (SNMP_ERR_WRONG_VALUE);
+ snmpd.txbuf = value->v.integer;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotSnmpdReceiveBuffer:
+ ctx->scratch->int1 = snmpd.rxbuf;
+ if (value->v.integer < 484 ||
+ value->v.integer > 65535)
+ return (SNMP_ERR_WRONG_VALUE);
+ snmpd.rxbuf = value->v.integer;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotSnmpdCommunityDisable:
+ ctx->scratch->int1 = snmpd.comm_dis;
+ if (!TRUTH_OK(value->v.integer))
+ return (SNMP_ERR_WRONG_VALUE);
+ if (TRUTH_GET(value->v.integer)) {
+ snmpd.comm_dis = 1;
+ } else {
+ if (snmpd.comm_dis)
+ return (SNMP_ERR_WRONG_VALUE);
+ }
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotSnmpdTrap1Addr:
+ return (ip_save(value, ctx, snmpd.trap1addr));
+ }
+ abort();
+
+ case SNMP_OP_ROLLBACK:
+ switch (which) {
+
+ case LEAF_begemotSnmpdTransmitBuffer:
+ snmpd.rxbuf = ctx->scratch->int1;
+ return (SNMP_ERR_NOERROR);
+ case LEAF_begemotSnmpdReceiveBuffer:
+ snmpd.txbuf = ctx->scratch->int1;
+ return (SNMP_ERR_NOERROR);
+ case LEAF_begemotSnmpdCommunityDisable:
+ snmpd.comm_dis = ctx->scratch->int1;
+ return (SNMP_ERR_NOERROR);
+ case LEAF_begemotSnmpdTrap1Addr:
+ ip_rollback(ctx, snmpd.trap1addr);
+ return (SNMP_ERR_NOERROR);
+ }
+ abort();
+
+ case SNMP_OP_COMMIT:
+ switch (which) {
+
+ case LEAF_begemotSnmpdTransmitBuffer:
+ case LEAF_begemotSnmpdReceiveBuffer:
+ case LEAF_begemotSnmpdCommunityDisable:
+ return (SNMP_ERR_NOERROR);
+ case LEAF_begemotSnmpdTrap1Addr:
+ ip_commit(ctx);
+ return (SNMP_ERR_NOERROR);
+ }
+ abort();
+ }
+ abort();
+}
+
+/*
+ * The community table
+ */
+int
+op_community(struct snmp_context *ctx, struct snmp_value *value,
+ u_int sub, u_int iidx __unused, enum snmp_op op)
+{
+ asn_subid_t which = value->var.subs[sub - 1];
+ struct community *c;
+
+ switch (op) {
+
+ case SNMP_OP_GETNEXT:
+ if ((community != COMM_INITIALIZE && snmpd.comm_dis) ||
+ (c = NEXT_OBJECT_OID(&community_list, &value->var, sub)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ index_append(&value->var, sub, &c->index);
+ break;
+
+ case SNMP_OP_GET:
+ if ((community != COMM_INITIALIZE && snmpd.comm_dis) ||
+ (c = FIND_OBJECT_OID(&community_list, &value->var, sub)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ break;
+
+ case SNMP_OP_SET:
+ if ((community != COMM_INITIALIZE && snmpd.comm_dis) ||
+ (c = FIND_OBJECT_OID(&community_list, &value->var, sub)) == NULL)
+ return (SNMP_ERR_NO_CREATION);
+ if (which != LEAF_begemotSnmpdCommunityString)
+ return (SNMP_ERR_NOT_WRITEABLE);
+ return (string_save(value, ctx, -1, &c->string));
+
+ case SNMP_OP_ROLLBACK:
+ if (which == LEAF_begemotSnmpdCommunityString) {
+ if ((c = FIND_OBJECT_OID(&community_list, &value->var,
+ sub)) == NULL)
+ string_free(ctx);
+ else
+ string_rollback(ctx, &c->string);
+ return (SNMP_ERR_NOERROR);
+ }
+ abort();
+
+ case SNMP_OP_COMMIT:
+ if (which == LEAF_begemotSnmpdCommunityString) {
+ if ((c = FIND_OBJECT_OID(&community_list, &value->var,
+ sub)) == NULL)
+ string_free(ctx);
+ else
+ string_commit(ctx);
+ return (SNMP_ERR_NOERROR);
+ }
+ abort();
+
+ default:
+ abort();
+ }
+
+ switch (which) {
+
+ case LEAF_begemotSnmpdCommunityString:
+ return (string_get(value, c->string, -1));
+
+ case LEAF_begemotSnmpdCommunityDescr:
+ return (string_get(value, c->descr, -1));
+ }
+ abort();
+}
+
+/*
+ * Port table
+ */
+int
+op_snmp_port(struct snmp_context *ctx, struct snmp_value *value,
+ u_int sub, u_int iidx, enum snmp_op op)
+{
+ asn_subid_t which = value->var.subs[sub-1];
+ struct snmp_port *p;
+ u_int8_t addr[4];
+ u_int32_t port;
+
+ switch (op) {
+
+ case SNMP_OP_GETNEXT:
+ if ((p = NEXT_OBJECT_OID(&snmp_port_list, &value->var, sub))
+ == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ index_append(&value->var, sub, &p->index);
+ break;
+
+ case SNMP_OP_GET:
+ if ((p = FIND_OBJECT_OID(&snmp_port_list, &value->var, sub))
+ == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ break;
+
+ case SNMP_OP_SET:
+ p = FIND_OBJECT_OID(&snmp_port_list, &value->var, sub);
+ ctx->scratch->int1 = (p != NULL);
+
+ if (which != LEAF_begemotSnmpdPortStatus)
+ abort();
+ if (!TRUTH_OK(value->v.integer))
+ return (SNMP_ERR_WRONG_VALUE);
+
+ ctx->scratch->int2 = TRUTH_GET(value->v.integer);
+
+ if (ctx->scratch->int2) {
+ /* open an SNMP port */
+ if (p != NULL)
+ /* already open - do nothing */
+ return (SNMP_ERR_NOERROR);
+
+ if (index_decode(&value->var, sub, iidx, addr, &port))
+ return (SNMP_ERR_NO_CREATION);
+ return (open_snmp_port(addr, port, &p));
+
+ } else {
+ /* close SNMP port - do in commit */
+ }
+ return (SNMP_ERR_NOERROR);
+
+ case SNMP_OP_ROLLBACK:
+ p = FIND_OBJECT_OID(&snmp_port_list, &value->var, sub);
+ if (ctx->scratch->int1 == 0) {
+ /* did not exist */
+ if (ctx->scratch->int2 == 1) {
+ /* created */
+ if (p != NULL)
+ close_snmp_port(p);
+ }
+ }
+ return (SNMP_ERR_NOERROR);
+
+ case SNMP_OP_COMMIT:
+ p = FIND_OBJECT_OID(&snmp_port_list, &value->var, sub);
+ if (ctx->scratch->int1 == 1) {
+ /* did exist */
+ if (ctx->scratch->int2 == 0) {
+ /* delete */
+ if (p != NULL)
+ close_snmp_port(p);
+ }
+ }
+ return (SNMP_ERR_NOERROR);
+
+ default:
+ abort();
+ }
+
+ /*
+ * Come here to fetch the value
+ */
+ switch (which) {
+
+ case LEAF_begemotSnmpdPortStatus:
+ value->v.integer = 1;
+ break;
+
+ default:
+ abort();
+ }
+
+ return (SNMP_ERR_NOERROR);
+}
+
+/*
+ * Local port table
+ */
+int
+op_local_port(struct snmp_context *ctx, struct snmp_value *value,
+ u_int sub, u_int iidx, enum snmp_op op)
+{
+ asn_subid_t which = value->var.subs[sub-1];
+ struct local_port *p;
+ u_char *name;
+ size_t namelen;
+
+ switch (op) {
+
+ case SNMP_OP_GETNEXT:
+ if ((p = NEXT_OBJECT_OID(&local_port_list, &value->var, sub))
+ == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ index_append(&value->var, sub, &p->index);
+ break;
+
+ case SNMP_OP_GET:
+ if ((p = FIND_OBJECT_OID(&local_port_list, &value->var, sub))
+ == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ break;
+
+ case SNMP_OP_SET:
+ p = FIND_OBJECT_OID(&local_port_list, &value->var, sub);
+ ctx->scratch->int1 = (p != NULL);
+
+ if (which != LEAF_begemotSnmpdLocalPortStatus)
+ abort();
+ if (!TRUTH_OK(value->v.integer))
+ return (SNMP_ERR_WRONG_VALUE);
+
+ ctx->scratch->int2 = TRUTH_GET(value->v.integer);
+
+ if (ctx->scratch->int2) {
+ /* open a local port */
+ if (p != NULL)
+ /* already open - do nothing */
+ return (SNMP_ERR_NOERROR);
+
+ if (index_decode(&value->var, sub, iidx,
+ &name, &namelen))
+ return (SNMP_ERR_NO_CREATION);
+ return (open_local_port(name, namelen, &p));
+
+ } else {
+ /* close local port - do in commit */
+ }
+ return (SNMP_ERR_NOERROR);
+
+ case SNMP_OP_ROLLBACK:
+ p = FIND_OBJECT_OID(&local_port_list, &value->var, sub);
+ if (ctx->scratch->int1 == 0) {
+ /* did not exist */
+ if (ctx->scratch->int2 == 1) {
+ /* created */
+ if (p != NULL)
+ close_local_port(p);
+ }
+ }
+ return (SNMP_ERR_NOERROR);
+
+ case SNMP_OP_COMMIT:
+ p = FIND_OBJECT_OID(&local_port_list, &value->var, sub);
+ if (ctx->scratch->int1 == 1) {
+ /* did exist */
+ if (ctx->scratch->int2 == 0) {
+ /* delete */
+ if (p != NULL)
+ close_local_port(p);
+ }
+ }
+ return (SNMP_ERR_NOERROR);
+
+ default:
+ abort();
+ }
+
+ /*
+ * Come here to fetch the value
+ */
+ switch (which) {
+
+ case LEAF_begemotSnmpdLocalPortStatus:
+ value->v.integer = 1;
+ break;
+
+ default:
+ abort();
+ }
+
+ return (SNMP_ERR_NOERROR);
+}
+
+
+
+/*
+ * Module table.
+ */
+struct module_dep {
+ struct snmp_dependency dep;
+ u_char section[LM_SECTION_MAX + 1];
+ u_char *path;
+ struct lmodule *m;
+};
+
+static void
+finish_unload(struct snmp_context *ctx __unused, int fail, void *arg)
+{
+ struct lmodule *m = arg;
+
+ if (!fail) {
+ lm_unload(m);
+ }
+}
+
+static void
+finish_load(struct snmp_context *ctx __unused, int fail, void *arg)
+{
+ struct lmodule *m = arg;
+
+ if (!fail) {
+ lm_start(m);
+ }
+}
+
+static int
+dep_modules(struct snmp_context *ctx, struct snmp_dependency *dep,
+ enum snmp_depop op)
+{
+ struct module_dep *mdep = (struct module_dep *)(void *)dep;
+
+ switch (op) {
+
+ case SNMP_DEPOP_COMMIT:
+ if (mdep->path == NULL) {
+ /* unload - find the module */
+ TAILQ_FOREACH(mdep->m, &lmodules, link)
+ if (strcmp(mdep->m->section, mdep->section) == 0)
+ break;
+ if (mdep->m == NULL)
+ return (SNMP_ERR_NOERROR);
+ if (snmp_set_atfinish(ctx, finish_unload, mdep->m))
+ return (SNMP_ERR_RES_UNAVAIL);
+ return (SNMP_ERR_NOERROR);
+ }
+ /* load */
+ if ((mdep->m = lm_load(mdep->path, mdep->section)) == NULL)
+ return (SNMP_ERR_RES_UNAVAIL);
+ if (snmp_set_atfinish(ctx, finish_load, mdep->m)) {
+ lm_unload(mdep->m);
+ return (SNMP_ERR_RES_UNAVAIL);
+ }
+ return (SNMP_ERR_NOERROR);
+
+ case SNMP_DEPOP_ROLLBACK:
+ if (mdep->path == NULL) {
+ /* rollback unload - the finish function takes care */
+ return (SNMP_ERR_NOERROR);
+ }
+ /* rollback load */
+ lm_unload(mdep->m);
+ return (SNMP_ERR_NOERROR);
+ }
+ abort();
+}
+
+int
+op_modules(struct snmp_context *ctx, struct snmp_value *value,
+ u_int sub, u_int iidx, enum snmp_op op)
+{
+ asn_subid_t which = value->var.subs[sub - 1];
+ struct lmodule *m;
+ u_char *section, *ptr;
+ size_t seclen;
+ struct module_dep *mdep;
+ struct asn_oid idx;
+
+ switch (op) {
+
+ case SNMP_OP_GETNEXT:
+ if ((m = NEXT_OBJECT_OID(&lmodules, &value->var, sub)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ index_append(&value->var, sub, &m->index);
+ break;
+
+ case SNMP_OP_GET:
+ if ((m = FIND_OBJECT_OID(&lmodules, &value->var, sub)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ break;
+
+ case SNMP_OP_SET:
+ m = FIND_OBJECT_OID(&lmodules, &value->var, sub);
+ if (which != LEAF_begemotSnmpdModulePath) {
+ if (m == NULL)
+ return (SNMP_ERR_NO_CREATION);
+ return (SNMP_ERR_NOT_WRITEABLE);
+ }
+
+ /* the errors in the next few statements can only happen when
+ * m is NULL, hence the NO_CREATION error. */
+ if (index_decode(&value->var, sub, iidx,
+ &section, &seclen))
+ return (SNMP_ERR_NO_CREATION);
+
+ /* check the section name */
+ if (seclen > LM_SECTION_MAX || seclen == 0) {
+ free(section);
+ return (SNMP_ERR_NO_CREATION);
+ }
+ for (ptr = section; ptr < section + seclen; ptr++)
+ if (!isascii(*ptr) || !isalnum(*ptr)) {
+ free(section);
+ return (SNMP_ERR_NO_CREATION);
+ }
+ if (!isalpha(section[0])) {
+ free(section);
+ return (SNMP_ERR_NO_CREATION);
+ }
+
+ /* check the path */
+ for (ptr = value->v.octetstring.octets;
+ ptr < value->v.octetstring.octets + value->v.octetstring.len;
+ ptr++) {
+ if (*ptr == '\0') {
+ free(section);
+ return (SNMP_ERR_WRONG_VALUE);
+ }
+ }
+
+ if (m == NULL) {
+ if (value->v.octetstring.len == 0) {
+ free(section);
+ return (SNMP_ERR_INCONS_VALUE);
+ }
+ } else {
+ if (value->v.octetstring.len != 0) {
+ free(section);
+ return (SNMP_ERR_INCONS_VALUE);
+ }
+ }
+
+ asn_slice_oid(&idx, &value->var, sub, value->var.len);
+
+ /* so far, so good */
+ mdep = (struct module_dep *)(void *)snmp_dep_lookup(ctx,
+ &oid_begemotSnmpdModuleTable, &idx,
+ sizeof(*mdep), dep_modules);
+ if (mdep == NULL) {
+ free(section);
+ return (SNMP_ERR_RES_UNAVAIL);
+ }
+
+ if (mdep->section[0] != '\0') {
+ /* two writes to the same entry - bad */
+ free(section);
+ return (SNMP_ERR_INCONS_VALUE);
+ }
+
+ strncpy(mdep->section, section, seclen);
+ mdep->section[seclen] = '\0';
+ free(section);
+
+ if (value->v.octetstring.len == 0)
+ mdep->path = NULL;
+ else {
+ if ((mdep->path = malloc(value->v.octetstring.len + 1)) == NULL)
+ return (SNMP_ERR_RES_UNAVAIL);
+ strncpy(mdep->path, value->v.octetstring.octets,
+ value->v.octetstring.len);
+ mdep->path[value->v.octetstring.len] = '\0';
+ }
+ ctx->scratch->ptr1 = mdep;
+ return (SNMP_ERR_NOERROR);
+
+ case SNMP_OP_ROLLBACK:
+ /* must be module path */
+ free(ctx->scratch->ptr1);
+ case SNMP_OP_COMMIT:
+ return (SNMP_ERR_NOERROR);
+
+ default:
+ abort();
+ }
+
+ switch (which) {
+
+ case LEAF_begemotSnmpdModulePath:
+ return (string_get(value, m->path, -1));
+
+ case LEAF_begemotSnmpdModuleComment:
+ return (string_get(value, m->config->comment, -1));
+ }
+ abort();
+}
+
+int
+op_snmp_set(struct snmp_context *ctx __unused, struct snmp_value *value,
+ u_int sub, u_int iidx __unused, enum snmp_op op)
+{
+ switch (op) {
+
+ case SNMP_OP_GETNEXT:
+ abort();
+
+ case SNMP_OP_GET:
+ switch (value->var.subs[sub - 1]) {
+
+ case LEAF_snmpSetSerialNo:
+ value->v.integer = snmp_serial_no;
+ break;
+
+ default:
+ abort();
+ }
+ return (SNMP_ERR_NOERROR);
+
+ case SNMP_OP_SET:
+ switch (value->var.subs[sub - 1]) {
+
+ case LEAF_snmpSetSerialNo:
+ if (value->v.integer != snmp_serial_no)
+ return (SNMP_ERR_INCONS_VALUE);
+ break;
+
+ default:
+ abort();
+ }
+ return (SNMP_ERR_NOERROR);
+
+ case SNMP_OP_ROLLBACK:
+ return (SNMP_ERR_NOERROR);
+
+ case SNMP_OP_COMMIT:
+ if (snmp_serial_no++ == 2147483647)
+ snmp_serial_no = 0;
+ return (SNMP_ERR_NOERROR);
+ }
+ abort();
+}
diff --git a/contrib/bsnmp/snmpd/bsnmpd.1 b/contrib/bsnmp/snmpd/bsnmpd.1
new file mode 100644
index 0000000..01c7a1c
--- /dev/null
+++ b/contrib/bsnmp/snmpd/bsnmpd.1
@@ -0,0 +1,256 @@
+.\"
+.\" Copyright (c) 2001-2003
+.\" Fraunhofer Institute for Open Communication Systems (FhG Fokus).
+.\" All rights reserved.
+.\"
+.\" Author: Harti Brandt <harti@freebsd.org>
+.\"
+.\" Redistribution of this software and documentation and use in source and
+.\" binary forms, with or without modification, are permitted provided that
+.\" the following conditions are met:
+.\"
+.\" 1. Redistributions of source code or documentation must retain the above
+.\" copyright notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the Institute nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY FRAUNHOFER FOKUS
+.\" AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+.\" INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+.\" FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+.\" FRAUNHOFER FOKUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+.\" LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+.\" OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+.\" LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+.\" NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+.\" EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $Begemot: bsnmp/snmpd/snmpd.1,v 1.2 2002/08/15 13:27:47 hbb Exp $
+.\"
+.Dd August 15, 2002
+.Dt SNMPD 1
+.Os
+.Sh NAME
+.Nm snmpd
+.Nd "simple and extendable SNMP daemon"
+.Sh SYNOPSIS
+.Nm
+.Op Fl dh
+.Op Fl c Ar file
+.Op Fl D Ar options
+.Op Fl I Ar paths
+.Op Fl l Ar prefix
+.Op Fl m Ar variable Ns Op = Ns Ar value
+.Op Fl p Ar file
+.Sh DESCRIPTION
+The
+.Nm
+daemon servers the internet SNMP (Simple Network Managment Protocol).
+It is intended to server only the absolute basic MIBs and implement all other
+MIBs through loadable modules. In this way the
+.Nm
+can be used in unexpected ways.
+.Pp
+The options are as follows:
+.Bl -tag -width ".It Fl D Ar options"
+.It Fl d
+This option is used for debugging
+.Nm
+and causes it not to daemonize itself.
+.It Fl h
+This option prints a short usage message.
+.It Fl c Ar file
+Use
+.Ar file
+as configuration file instead of the standard one.
+.It Fl D Ar options
+Debugging options are specified with a
+.Fl o
+flag followed by a comma separated string of options.
+The following options are available.
+.Bl -tag -width ".It Cm trace Ns Cm = Ns Cm level"
+.It Cm dump
+This option causes all sent and received PDUs to be dumped to the terminal.
+.It Cm events
+This causes the debugging level of the event library (see
+.Xr eventlib 3 )
+to be set to 10.
+.It Cm trace Ns Cm = Ns Cm level
+This option causes the snmp library trace flag to be set to the specified
+value. The value can be specified in the usual C-syntax for numbers.
+.El
+.It Fl I Ar paths
+This option specifies a colon separated list of directories to search for
+configuration include files. The default is
+.Pa /etc:/usr/etc/:/usr/local/etc .
+These paths are only searched for include specified within <> parantheses.
+.It Fl l Ar prefix
+The
+.Ar prefix
+is used as the default basename for the pid and the configuration files.
+.It Fl m Ar variable Ns Op = Ns Ar value
+Define a configuration variable.
+.It Fl p Ar file
+Specify an alternate pid file instead of the default one.
+.El
+.Sh CONFIGURATION
+The
+.Nm
+reads its configuration from either the default or the user specified
+configuration file. The configuration file consists of the following types of
+lines:
+.Bl -bullet -offset indent
+.It
+variable assignments
+.It
+section separators
+.It
+include directives
+.It
+MIB variable assignments
+.El
+.Pp
+If a line is too long it can be continued on the next line by ending it with
+a backslash. Empty lines and lines who's first non-blank character is a
+.Dq #
+sign are ignored.
+.Pp
+All MIB variable assignments of the entire configuration (including nested
+configuration files) are handled as one transaction, i.e. as if they arrived
+in a single SET PDU. Any failure during the initial configuration read causes
+.Nm
+to exit. A failure during the configuration read caused by a module load
+causes the loading of the module to fail.
+.Pp
+The configuration is red during initialisation of
+.Nm ,
+when a module is loaded and when
+.Nm
+receives a SIGHUP.
+.Ss VARIABLE ASSIGNMENTS
+Variable assignments can take one of two forms:
+.Bd -unfilled -offset indent
+variable := string
+variable ?= string
+.Ed
+.Pp
+The string reaches from the first non-blank character after the
+equal sign until the first new line or
+.Dq #
+character. In the first case
+the string is assigned to the variable unconditionally, in the second case the
+variable is only assigned if it does not exist yet.
+.Pp
+Variable names must begin with a letter or underscore and contain only letters,
+digits or underscores.
+.Ss SECTION SEPARATORS
+The configuration consists of named sections. The MIB variable assignments in
+the section named
+.Dq snmpd
+are executed only during initial setup or when
+.Nm
+receives a SIGHUP. All other sections are executed when either a module
+with the same name as the section is loaded or
+.Nm
+receives a SIGHUP and that module is already loaded. The default section
+at the start of the configuration is
+.Dq snmpd .
+One can switch to another section with the syntax
+.Bd -unfilled -offset indent
+%secname
+.Ed
+.Pp
+Where
+.Ar secname
+is the name of the section. The same
+.Ar secname
+can be used in more than one place in the configuration. All of these parts are
+collected into one section.
+.Ss INCLUDE DIRECTIVES
+Another configuration file can be included into the current one with the
+include directive that takes one of two forms:
+.Bd -unfilled -offset indent
+\&.include "file"
+\&.include <"file">
+.Ed
+.Pp
+The first form causes the file to be searched in the current directory, the
+second form causes the file to be searched in the directories specified in
+the system include path. Nesting depths is only restricted by available
+memory.
+.Ss MIB VARIABLE ASSIGNMENTS
+A MIB variable is assigned with the syntax
+.Bd -unfilled -offset indent
+oid [ suboids ] = value
+.Ed
+.Pp
+.Va oid
+is the name of the variable to be set. Only the last component of the entire
+name is used here. If the variable is a scalar, the index (.0) is automatically
+appended and need not to be specified. If the variable is a table column,
+the index (
+.Va suboids )
+must be specified. The index consist of elements each seperated from the
+previous one by a dot. Elements may be either numbers, strings or hostnames
+enclosed in [] brackets. If the element is a number it is appended
+to the current oid. If the element is a string, its length and the
+.Tn ASCII
+code of each of its characters are appended to the current oid. If the
+element is a hostname, the IP address of the host is looked up and the four
+elements of the IP address are appended to the oid.
+.Pp
+For example a oid of
+.Bd -unfilled -offset indent
+myvariable.27.foooll.[localhost]."&^!"
+.Ed
+.Pp
+results in the oid
+.Bd -unfilled -offset indent
+myvariable.27.6.102.111.111.111.108.108.127.0.0.1.38.94.33
+.Ed
+.Pp
+The value of the assignment may be either empty, a string or a number.
+If a string starts with a letter or an underscore and consists only of
+letters, digits, underscores and minus signs, it can be written without
+quotes. In all other cases the string must be enclosed in double quotes.
+.Sh SUBSTITUTIONS
+A variable substitution is written as
+.Bd -unfilled -offset indent
+$(variable)
+.Ed
+.Pp
+where
+.Ar variable
+is the name of the variable to substitute. Using an undefined variable is
+considered an error.
+.Sh FILES
+.Bl -tag -width ".It Pa /var/run/ Ns Ao Ar prefix Ac Ns \&.pid" -compact
+.It Pa /etc/ Ns Ao Ar prefix Ac Ns \&.config
+Default configuration file, where the default
+.Aq prefix
+is
+.Dq snmpd .
+.It Pa /var/run/ Ns Ao Ar prefix Ac Ns \&.pid
+Default pid file.
+.It Pa /etc:/usr/etc/:/usr/local/etc
+This is the default search path for system include files.
+.It Pa /usr/local/share/snmp/mibs/FOKUS-MIB.txt
+.It Pa /usr/local/share/snmp/mibs/BEGEMOT-MIB.txt
+.It Pa /usr/local/share/snmp/mibs/BEGEMOT-SNMPD.txt
+The definitions for the MIBs implemented in the daemon.
+.El
+.Sh SEE ALSO
+.Xr gensnmptree 1
+.Sh STANDARDS
+The
+.Nm
+conforms to the applicable IETF RFCs.
+.Sh AUTHORS
+.An Hartmut Brandt Aq brandt@fokus.gmd.de
+.Sh BUGS
+Sure.
diff --git a/contrib/bsnmp/snmpd/config.c b/contrib/bsnmp/snmpd/config.c
new file mode 100644
index 0000000..e247797
--- /dev/null
+++ b/contrib/bsnmp/snmpd/config.c
@@ -0,0 +1,1361 @@
+/*
+ * Copyright (c) 2001-2003
+ * Fraunhofer Institute for Open Communication Systems (FhG Fokus).
+ * All rights reserved.
+ *
+ * Author: Harti Brandt <harti@freebsd.org>
+ *
+ * Redistribution of this software and documentation and use in source and
+ * binary forms, with or without modification, are permitted provided that
+ * the following conditions are met:
+ *
+ * 1. Redistributions of source code or documentation must retain the above
+ * copyright notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY FRAUNHOFER FOKUS
+ * AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * FRAUNHOFER FOKUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Begemot: bsnmp/snmpd/config.c,v 1.18 2003/03/11 15:30:01 hbb Exp $
+ *
+ * Parse configuration file.
+ */
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <errno.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <limits.h>
+#include <netdb.h>
+#include <setjmp.h>
+#include <inttypes.h>
+
+#include "snmpmod.h"
+#include "snmpd.h"
+#include "tree.h"
+
+/*
+#define DEBUGGING
+*/
+
+/*
+ * config_file: EMPTY | config_file line
+ *
+ * line: oid '=' value
+ * | '%' STRING
+ * | STRING := REST_OF_LINE
+ * | STRING ?= REST_OF_LINE
+ * | . INCLUDE STRING
+ *
+ * oid: STRING suboid
+ *
+ * suboid: EMPTY | suboid '.' subid
+ *
+ * subid: NUM | STRING | '[' STRING ']'
+ *
+ * value: EMPTY | STRING | NUM
+ */
+
+/*
+ * Input context for macros and includes
+ */
+enum input_type {
+ INPUT_FILE = 1,
+ INPUT_STRING
+};
+struct input {
+ enum input_type type;
+ union {
+ struct {
+ FILE *fp;
+ char *filename;
+ u_int lno;
+ } file;
+ struct {
+ char *macro;
+ char *str;
+ char *ptr;
+ size_t left;
+ } str;
+ } u;
+ LIST_ENTRY(input) link;
+};
+static LIST_HEAD(, input) inputs;
+
+#define input_fp u.file.fp
+#define input_filename u.file.filename
+#define input_lno u.file.lno
+#define input_macro u.str.macro
+#define input_str u.str.str
+#define input_ptr u.str.ptr
+#define input_left u.str.left
+
+static int input_push;
+static int input_buf[2];
+
+/*
+ * Configuration data. The configuration file is handled as one single
+ * SNMP transaction. So we need to keep the assignment data for the
+ * commit or rollback pass. Note, that dependencies and finish functions
+ * are NOT allowed here.
+ */
+struct assign {
+ struct snmp_value value;
+ struct snmp_scratch scratch;
+ const char *node_name;
+
+ TAILQ_ENTRY(assign) link;
+};
+static TAILQ_HEAD(assigns, assign) assigns = TAILQ_HEAD_INITIALIZER(assigns);
+
+
+static struct snmp_context *snmp_ctx;
+
+struct macro {
+ char *name;
+ char *value;
+ size_t length;
+ LIST_ENTRY(macro) link;
+ int perm;
+};
+static LIST_HEAD(, macro) macros = LIST_HEAD_INITIALIZER(&macros);
+
+enum {
+ TOK_EOF = 0200,
+ TOK_EOL,
+ TOK_NUM,
+ TOK_STR,
+ TOK_HOST,
+ TOK_ASSIGN,
+ TOK_QASSIGN,
+};
+
+/* lexer values and last token */
+static uint64_t numval;
+static char strval[_POSIX2_LINE_MAX];
+static size_t strvallen;
+static int token;
+
+/* error return */
+static jmp_buf errjmp[4];
+static volatile int errstk;
+
+# define ERRPUSH() (setjmp(errjmp[errstk++]))
+# define ERRPOP() ((void)(errstk--))
+# define ERRNEXT() (longjmp(errjmp[--errstk], 1))
+# define ERR() (longjmp(errjmp[--errstk], 1))
+
+/* section context */
+static int ignore;
+
+/*
+ * Report an error and jump to the error label
+ */
+static void report(const char *fmt, ...) __dead2 __printflike(1, 2);
+
+static void
+report(const char *fmt, ...)
+{
+ va_list ap;
+ const struct input *input;
+
+ va_start(ap, fmt);
+ vsyslog(LOG_ERR, fmt, ap);
+ va_end(ap);
+
+ LIST_FOREACH(input, &inputs, link) {
+ switch (input->type) {
+
+ case INPUT_FILE:
+ syslog(LOG_ERR, " in file %s line %u",
+ input->input_filename, input->input_lno);
+ break;
+
+ case INPUT_STRING:
+ syslog(LOG_ERR, " in macro %s pos %td",
+ input->input_macro,
+ input->input_ptr - input->input_str);
+ break;
+ }
+ }
+ ERR();
+}
+
+/*
+ * Open a file for input
+ */
+static int
+input_open_file(const char *fname, int sysdir)
+{
+ struct input *input;
+ FILE *fp;
+ char path[PATH_MAX + 1];
+ char *col;
+ const char *ptr;
+
+ if (sysdir) {
+ ptr = syspath;
+ fp = NULL;
+ while (*ptr != '\0') {
+ if ((col = strchr(ptr, ':')) == NULL)
+ snprintf(path, sizeof(path), "%s/%s",
+ ptr, fname);
+ else if (col == ptr)
+ snprintf(path, sizeof(path), "./%s", fname);
+ else
+ snprintf(path, sizeof(path), "%.*s/%s",
+ (int)(col - ptr), ptr, fname);
+ if ((fp = fopen(path, "r")) != NULL)
+ break;
+ ptr = col + 1;
+ }
+ } else
+ fp = fopen(fname, "r");
+
+ if (fp == NULL)
+ report("%s: %m", fname);
+
+ if ((input = malloc(sizeof(*input))) == NULL) {
+ fclose(fp);
+ return (-1);
+ }
+ if ((input->input_filename = malloc(strlen(fname) + 1)) == NULL) {
+ fclose(fp);
+ free(input);
+ return (-1);
+ }
+ strcpy(input->input_filename, fname);
+ input->input_fp = fp;
+ input->input_lno = 1;
+ input->type = INPUT_FILE;
+ LIST_INSERT_HEAD(&inputs, input, link);
+ return (0);
+}
+
+/*
+ * Make a macro the next input
+ */
+static void
+input_open_macro(struct macro *m)
+{
+ struct input *input;
+
+ if ((input = malloc(sizeof(*input))) == NULL)
+ report("%m");
+ input->type = INPUT_STRING;
+ input->input_macro = m->name;
+ if ((input->input_str = malloc(m->length)) == NULL) {
+ free(input);
+ report("%m");
+ }
+ memcpy(input->input_str, m->value, m->length);
+ input->input_ptr = input->input_str;
+ input->input_left = m->length;
+ LIST_INSERT_HEAD(&inputs, input, link);
+}
+
+/*
+ * Close top input source
+ */
+static void
+input_close(void)
+{
+ struct input *input;
+
+ if ((input = LIST_FIRST(&inputs)) == NULL)
+ abort();
+ switch (input->type) {
+
+ case INPUT_FILE:
+ fclose(input->input_fp);
+ free(input->input_filename);
+ break;
+
+ case INPUT_STRING:
+ free(input->input_str);
+ break;
+ }
+ LIST_REMOVE(input, link);
+ free(input);
+}
+
+/*
+ * Close all inputs
+ */
+static void
+input_close_all(void)
+{
+ while (!LIST_EMPTY(&inputs))
+ input_close();
+}
+
+/*
+ * Push back one character
+ */
+static void
+input_ungetc(int c)
+{
+ if (c == EOF)
+ report("pushing EOF");
+ if (input_push == 2)
+ report("pushing third char");
+ input_buf[input_push++] = c;
+}
+
+
+/*
+ * Return next character from the input without preprocessing.
+ */
+static int
+input_getc_raw(void)
+{
+ int c;
+ struct input *input;
+
+ if (input_push != 0) {
+ c = input_buf[--input_push];
+ goto ok;
+ }
+ while ((input = LIST_FIRST(&inputs)) != NULL) {
+ switch (input->type) {
+
+ case INPUT_FILE:
+ if ((c = getc(input->input_fp)) == EOF) {
+ if (ferror(input->input_fp))
+ report("read error: %m");
+ input_close();
+ break;
+ }
+ if (c == '\n')
+ input->input_lno++;
+ goto ok;
+
+ case INPUT_STRING:
+ if (input->input_left-- == 0) {
+ input_close();
+ break;
+ }
+ c = *input->input_ptr++;
+ goto ok;
+ }
+ }
+# ifdef DEBUGGING
+ fprintf(stderr, "EOF");
+# endif
+ return (EOF);
+
+ ok:
+# ifdef DEBUGGING
+ if (!isascii(c) || !isprint(c))
+ fprintf(stderr, "'%#2x'", c);
+ else
+ fprintf(stderr, "'%c'", c);
+# endif
+ return (c);
+}
+
+/*
+ * Get character with and \\n -> processing.
+ */
+static int
+input_getc_plain(void)
+{
+ int c;
+
+ again:
+ if ((c = input_getc_raw()) == '\\') {
+ if ((c = input_getc_raw()) == '\n')
+ goto again;
+ if (c != EOF)
+ input_ungetc(c);
+ return ('\\');
+ }
+ return (c);
+}
+
+/*
+ * Get next character with substitution of macros
+ */
+static int
+input_getc(void)
+{
+ int c;
+ struct macro *m;
+ char name[_POSIX2_LINE_MAX];
+ size_t namelen;
+
+ again:
+ if ((c = input_getc_plain()) != '$')
+ return (c);
+
+ if ((c = input_getc()) == EOF)
+ report("unexpected EOF");
+ if (c != '(')
+ report("expecting '(' after '$'");
+
+ namelen = 0;
+ while ((c = input_getc()) != EOF && c != ')') {
+ if (isalpha(c) || c == '_' || (namelen != 0 && isdigit(c)))
+ name[namelen++] = c;
+ else
+ goto badchar;
+ }
+ if (c == EOF)
+ report("unexpected EOF");
+ name[namelen++] = '\0';
+
+ LIST_FOREACH(m, &macros, link)
+ if (strcmp(m->name, name) == 0)
+ break;
+ if (m == NULL)
+ report("undefined macro '%s'", name);
+
+ input_open_macro(m);
+ goto again;
+
+ badchar:
+ if (!isascii(c) || !isprint(c))
+ report("unexpected character %#2x", (u_int)c);
+ else
+ report("bad character '%c'", c);
+}
+
+
+static void
+input_getnum(u_int base, u_int flen)
+{
+ int c;
+ u_int cnt;
+
+ cnt = 0;
+ numval = 0;
+ while (flen == 0 || cnt < flen) {
+ if ((c = input_getc()) == EOF) {
+ if (cnt == 0)
+ report("bad number");
+ return;
+ }
+ if (isdigit(c)) {
+ if (base == 8 && (c == '8' || c == '9')) {
+ input_ungetc(c);
+ if (cnt == 0)
+ report("bad number");
+ return;
+ }
+ numval = numval * base + (c - '0');
+ } else if (base == 16 && isxdigit(c)) {
+ if (islower(c))
+ numval = numval * base + (c - 'a' + 10);
+ else
+ numval = numval * base + (c - 'A' + 10);
+ } else {
+ input_ungetc(c);
+ if (cnt == 0)
+ report("bad number");
+ return;
+ }
+ cnt++;
+ }
+}
+
+static int
+# ifdef DEBUGGING
+_gettoken(void)
+# else
+gettoken(void)
+# endif
+{
+ int c;
+ char *end;
+ static const char esc[] = "abfnrtv";
+ static const char chr[] = "\a\b\f\n\r\t\v";
+
+ /*
+ * Skip any whitespace before the next token
+ */
+ while ((c = input_getc()) != EOF) {
+ if (!isspace(c) || c == '\n')
+ break;
+ }
+ if (c == EOF)
+ return (token = TOK_EOF);
+ if (!isascii(c))
+ goto badchar;
+
+ /*
+ * Skip comments
+ */
+ if (c == '#') {
+ while ((c = input_getc_plain()) != EOF) {
+ if (c == '\n')
+ return (token = TOK_EOL);
+ }
+ goto badeof;
+ }
+
+ /*
+ * Single character tokens
+ */
+ if (c == '\n')
+ return (token = TOK_EOL);
+ if (c == '.' || c == '%' || c == '=' || c == '<' || c == '>')
+ return (token = c);
+ if (c == ':') {
+ if ((c = input_getc()) == '=')
+ return (token = TOK_ASSIGN);
+ input_ungetc(c);
+ return (token = ':');
+ }
+ if (c == '?') {
+ if ((c = input_getc()) == '=')
+ return (token = TOK_QASSIGN);
+ input_ungetc(c);
+ goto badchar;
+ }
+
+ /*
+ * Sort out numbers
+ */
+ if (isdigit(c)) {
+ if (c == '0') {
+ if ((c = input_getc()) == 'x' || c == 'X') {
+ input_getnum(16, 0);
+ } else if (isdigit(c)) {
+ input_ungetc(c);
+ input_getnum(8, 0);
+ } else {
+ if (c != EOF)
+ input_ungetc(c);
+ numval = 0;
+ c = 1;
+ }
+ } else {
+ input_ungetc(c);
+ input_getnum(10, 0);
+ }
+ return (token = TOK_NUM);
+ }
+
+ /*
+ * Must be a string then
+ */
+ strvallen = 0;
+
+# define GETC(C) do { \
+ if ((c = input_getc()) == EOF) \
+ goto badeof; \
+ if (!isascii(c) || (!isprint(c) && c != '\t')) \
+ goto badchar; \
+} while(0)
+
+ if (c == '"') {
+ for(;;) {
+ GETC(c);
+ if (c == '"') {
+ strval[strvallen] = '\0';
+ break;
+ }
+ if (c != '\\') {
+ strval[strvallen++] = c;
+ continue;
+ }
+ GETC(c);
+ if ((end = strchr(esc, c)) != NULL) {
+ strval[strvallen++] = chr[end - esc];
+ continue;
+ }
+ if (c == 'x') {
+ input_getnum(16, 2);
+ c = numval;
+ } else if (c >= '0' && c <= '7') {
+ input_ungetc(c);
+ input_getnum(8, 3);
+ c = numval;
+ }
+ strval[strvallen++] = c;
+ }
+# undef GETC
+
+ } else if (c == '[') {
+ /*
+ * Skip leading space
+ */
+ while ((c = input_getc()) != EOF && isspace(c))
+ ;
+ if (c == EOF)
+ goto badeof;
+ while (c != ']' && !isspace(c)) {
+ if (!isalnum(c) && c != '.' && c != '-')
+ goto badchar;
+ strval[strvallen++] = c;
+ if ((c = input_getc()) == EOF)
+ goto badeof;
+ }
+ while (c != ']' && isspace(c)) {
+ if ((c = input_getc()) == EOF)
+ goto badeof;
+ }
+ if (c != ']')
+ goto badchar;
+ strval[strvallen] = '\0';
+ return (token = TOK_HOST);
+
+ } else if (!isalpha(c) && c != '_') {
+ goto badchar;
+
+ } else {
+ for (;;) {
+ strval[strvallen++] = c;
+ if ((c = input_getc()) == EOF)
+ goto badeof;
+ if (!isalnum(c) && c != '_' && c != '-') {
+ input_ungetc(c);
+ strval[strvallen] = '\0';
+ break;
+ }
+ }
+ }
+
+ return (token = TOK_STR);
+
+ badeof:
+ report("unexpected EOF");
+
+ badchar:
+ if (!isascii(c) || !isprint(c))
+ report("unexpected character %#2x", (u_int)c);
+ else
+ report("bad character '%c'", c);
+}
+
+# ifdef DEBUGGING
+static int
+gettoken()
+{
+ _gettoken();
+ if (isascii(token) && isprint(token))
+ printf("(%c)", token);
+ else {
+ switch (token) {
+
+ case TOK_EOF:
+ printf("(EOF)");
+ break;
+ case TOK_EOL:
+ printf("(EOL)");
+ break;
+ case TOK_NUM:
+ printf("(NUM %llu)", numval);
+ break;
+ case TOK_STR:
+ printf("(STR %.*s)", (int)strvallen, strval);
+ break;
+ case TOK_HOST:
+ printf("(HOST %s)", strval);
+ break;
+ default:
+ printf("(%#2x)", token);
+ break;
+ }
+ }
+ return (token);
+}
+#endif
+
+
+/*
+ * Try to execute the assignment.
+ */
+static void
+handle_assignment(const struct snmp_node *node, struct asn_oid *vindex,
+ const struct snmp_value *value)
+{
+ u_int i;
+ int err;
+ struct assign *tp;
+ char nodename[100];
+
+ if (node->type == SNMP_NODE_LEAF) {
+ /* index must be one single zero or no index at all */
+ if (vindex->len > 1 || (vindex->len == 1 &&
+ vindex->subs[0] != 0))
+ report("bad index on leaf node");
+ vindex->len = 1;
+ vindex->subs[0] = 0;
+ } else {
+ /* resulting oid must not be too long */
+ if (node->oid.len + vindex->len > ASN_MAXOIDLEN)
+ report("resulting OID too long");
+ }
+
+ /*
+ * Get the next assignment entry for the transaction.
+ */
+ if ((tp = malloc(sizeof(*tp))) == NULL)
+ report("%m");
+
+ tp->value = *value;
+ tp->node_name = node->name;
+
+ /*
+ * Build the OID
+ */
+ tp->value.var = node->oid;
+ for (i = 0; i < vindex->len; i++)
+ tp->value.var.subs[tp->value.var.len++] = vindex->subs[i];
+
+ /*
+ * Puzzle together the variables for the call and call the
+ * set routine. The set routine may make our node pointer
+ * invalid (if we happend to call the module loader) so
+ * get a copy of the node name beforehands.
+ */
+ snprintf(nodename, sizeof(nodename), "%s", node->name);
+ snmp_ctx->scratch = &tp->scratch;
+ snmp_ctx->var_index = 0;
+ err = (*node->op)(snmp_ctx, &tp->value, node->oid.len, node->index,
+ SNMP_OP_SET);
+ if (err != 0) {
+ free(tp);
+ report("assignment to %s.%s returns %d", nodename,
+ asn_oid2str(vindex), err);
+ }
+
+ TAILQ_INSERT_TAIL(&assigns, tp, link);
+}
+
+
+/*
+ * Parse the section statement
+ */
+static void
+parse_section(const struct lmodule *mod)
+{
+ if (token != TOK_STR)
+ report("expecting section name");
+
+ if (strcmp(strval, "snmpd") == 0) {
+ if (mod != NULL)
+ /* loading a module - ignore common stuff */
+ ignore = 1;
+ else
+ /* global configuration - don't ignore */
+ ignore = 0;
+ } else {
+ if (mod == NULL) {
+ /* global configuration - ignore module stuff */
+ ignore = 1;
+ } else {
+ /* loading module - check if it's our section */
+ ignore = (strcmp(strval, mod->section) != 0);
+ }
+ }
+ gettoken();
+}
+
+/*
+ * Convert a hostname to four u_chars
+ */
+static void
+gethost(const char *host, u_char *ip)
+{
+ struct addrinfo hints, *res;
+ int error;
+ struct sockaddr_in *sain;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_INET;
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_protocol = IPPROTO_UDP;
+ hints.ai_flags = AI_PASSIVE;
+ error = getaddrinfo(host, NULL, &hints, &res);
+ if (error != 0)
+ report("%s: %s", host, gai_strerror(error));
+ if (res == NULL)
+ report("%s: unknown hostname", host);
+
+ sain = (struct sockaddr_in *)(void *)res->ai_addr;
+ sain->sin_addr.s_addr = ntohl(sain->sin_addr.s_addr);
+ ip[0] = sain->sin_addr.s_addr >> 24;
+ ip[1] = sain->sin_addr.s_addr >> 16;
+ ip[2] = sain->sin_addr.s_addr >> 8;
+ ip[3] = sain->sin_addr.s_addr >> 0;
+
+ freeaddrinfo(res);
+}
+
+/*
+ * Parse the left hand side of a config line.
+ */
+static const struct snmp_node *
+parse_oid(const char *varname, struct asn_oid *oid)
+{
+ struct snmp_node *node;
+ u_int i;
+ u_char ip[4];
+
+ for (node = tree; node < &tree[tree_size]; node++)
+ if (strcmp(varname, node->name) == 0)
+ break;
+ if (node == &tree[tree_size])
+ node = NULL;
+
+ oid->len = 0;
+ while (token == '.') {
+ if (gettoken() == TOK_NUM) {
+ if (numval > ASN_MAXID)
+ report("subid too large %#"PRIx64, numval);
+ if (oid->len == ASN_MAXOIDLEN)
+ report("index too long");
+ oid->subs[oid->len++] = numval;
+
+ } else if (token == TOK_STR) {
+ if (strvallen + oid->len + 1 > ASN_MAXOIDLEN)
+ report("oid too long");
+ oid->subs[oid->len++] = strvallen;
+ for (i = 0; i < strvallen; i++)
+ oid->subs[oid->len++] = strval[i];
+
+ } else if (token == TOK_HOST) {
+ gethost(strval, ip);
+ if (oid->len + 4 > ASN_MAXOIDLEN)
+ report("index too long");
+ for (i = 0; i < 4; i++)
+ oid->subs[oid->len++] = ip[i];
+
+ } else
+ report("bad token in index");
+ gettoken();
+ }
+
+ return (node);
+}
+
+/*
+ * Parse the value for an assignment.
+ */
+static void
+parse_syntax_null(struct snmp_value *value __unused)
+{
+ if (token != TOK_EOL)
+ report("bad NULL syntax");
+}
+
+static void
+parse_syntax_integer(struct snmp_value *value)
+{
+ if (token != TOK_NUM)
+ report("bad INTEGER syntax");
+ if (numval > 0x7fffffff)
+ report("INTEGER too large %"PRIu64, numval);
+
+ value->v.integer = numval;
+ gettoken();
+}
+
+static void
+parse_syntax_counter64(struct snmp_value *value)
+{
+ if (token != TOK_NUM)
+ report("bad COUNTER64 syntax");
+
+ value->v.counter64 = numval;
+ gettoken();
+}
+
+static void
+parse_syntax_octetstring(struct snmp_value *value)
+{
+ u_long alloc;
+ u_char *noct;
+
+ if (token == TOK_STR) {
+ value->v.octetstring.len = strvallen;
+ value->v.octetstring.octets = malloc(strvallen);
+ (void)memcpy(value->v.octetstring.octets, strval, strvallen);
+ gettoken();
+ return;
+ }
+
+ /* XX:XX:XX syntax */
+ value->v.octetstring.octets = NULL;
+ value->v.octetstring.len = 0;
+
+ if (token != TOK_NUM)
+ /* empty string is allowed */
+ return;
+
+ if (ERRPUSH()) {
+ free(value->v.octetstring.octets);
+ ERRNEXT();
+ }
+
+ alloc = 0;
+ for (;;) {
+ if (token != TOK_NUM)
+ report("bad OCTETSTRING syntax");
+ if (numval > 0xff)
+ report("byte value too large");
+ if (alloc == value->v.octetstring.len) {
+ alloc += 100;
+ noct = realloc(value->v.octetstring.octets, alloc);
+ if (noct == NULL)
+ report("%m");
+ value->v.octetstring.octets = noct;
+ }
+ value->v.octetstring.octets[value->v.octetstring.len++]
+ = numval;
+ if (gettoken() != ':')
+ break;
+ gettoken();
+ }
+ ERRPOP();
+}
+
+static void
+parse_syntax_oid(struct snmp_value *value)
+{
+ value->v.oid.len = 0;
+
+ if (token != TOK_NUM)
+ return;
+
+ for (;;) {
+ if (token != TOK_NUM)
+ report("bad OID syntax");
+ if (numval > ASN_MAXID)
+ report("subid too large");
+ if (value->v.oid.len == ASN_MAXOIDLEN)
+ report("OID too long");
+ value->v.oid.subs[value->v.oid.len++] = numval;
+ if (gettoken() != '.')
+ break;
+ gettoken();
+ }
+}
+
+static void
+parse_syntax_ipaddress(struct snmp_value *value)
+{
+ int i;
+ u_char ip[4];
+
+ if (token == TOK_NUM) {
+ /* numerical address */
+ i = 0;
+ for (;;) {
+ if (numval >= 256)
+ report("ip address part too large");
+ value->v.ipaddress[i++] = numval;
+ if (i == 4)
+ break;
+ if (gettoken() != '.')
+ report("expecting '.' in ip address");
+ }
+ gettoken();
+
+ } else if (token == TOK_HOST) {
+ /* host name */
+ gethost(strval, ip);
+ for (i = 0; i < 4; i++)
+ value->v.ipaddress[i] = ip[i];
+ gettoken();
+
+ } else
+ report("bad ip address syntax");
+}
+
+static void
+parse_syntax_uint32(struct snmp_value *value)
+{
+
+ if (token != TOK_NUM)
+ report("bad number syntax");
+ if (numval > 0xffffffff)
+ report("number too large");
+ value->v.uint32 = numval;
+ gettoken();
+}
+
+/*
+ * Parse an assignement line
+ */
+static void
+parse_assign(const char *varname)
+{
+ struct snmp_value value;
+ struct asn_oid vindex;
+ const struct snmp_node *node;
+
+ node = parse_oid(varname, &vindex);
+ if (token != '=')
+ report("'=' expected");
+ gettoken();
+
+ if (ignore) {
+ /* skip rest of line */
+ while (token != TOK_EOL && token != TOK_EOF)
+ gettoken();
+ return;
+ }
+ if (node == NULL)
+ report("unknown variable");
+
+ switch (value.syntax = node->syntax) {
+
+ case SNMP_SYNTAX_NULL:
+ parse_syntax_null(&value);
+ break;
+
+ case SNMP_SYNTAX_INTEGER:
+ parse_syntax_integer(&value);
+ break;
+
+ case SNMP_SYNTAX_COUNTER64:
+ parse_syntax_counter64(&value);
+ break;
+
+ case SNMP_SYNTAX_OCTETSTRING:
+ parse_syntax_octetstring(&value);
+ break;
+
+ case SNMP_SYNTAX_OID:
+ parse_syntax_oid(&value);
+ break;
+
+ case SNMP_SYNTAX_IPADDRESS:
+ parse_syntax_ipaddress(&value);
+ break;
+
+ case SNMP_SYNTAX_COUNTER:
+ case SNMP_SYNTAX_GAUGE:
+ case SNMP_SYNTAX_TIMETICKS:
+ parse_syntax_uint32(&value);
+ break;
+
+ case SNMP_SYNTAX_NOSUCHOBJECT:
+ case SNMP_SYNTAX_NOSUCHINSTANCE:
+ case SNMP_SYNTAX_ENDOFMIBVIEW:
+ abort();
+ }
+
+ if (ERRPUSH()) {
+ snmp_value_free(&value);
+ ERRNEXT();
+ }
+
+ handle_assignment(node, &vindex, &value);
+
+ ERRPOP();
+}
+
+/*
+ * Handle macro definition line
+ * We have already seen the := and the input now stands at the character
+ * after the =. Skip whitespace and then call the input routine directly to
+ * eat up characters.
+ */
+static void
+parse_define(const char *varname)
+{
+ char *volatile string;
+ char *new;
+ volatile size_t alloc, length;
+ int c;
+ struct macro *m;
+ int t = token;
+
+ alloc = 100;
+ length = 0;
+ if ((string = malloc(alloc)) == NULL)
+ report("%m");
+
+ if (ERRPUSH()) {
+ free(string);
+ ERRNEXT();
+ }
+
+ while ((c = input_getc_plain()) != EOF) {
+ if (c == '\n' || !isspace(c))
+ break;
+ }
+
+ while (c != EOF && c != '#' && c != '\n') {
+ if (alloc == length) {
+ alloc *= 2;
+ if ((new = realloc(string, alloc)) == NULL)
+ report("%m");
+ string = new;
+ }
+ string[length++] = c;
+ c = input_getc_plain();
+ }
+ if (c == '#') {
+ while ((c = input_getc_plain()) != EOF && c != '\n')
+ ;
+ }
+ if (c == EOF)
+ report("EOF in macro definition");
+
+ LIST_FOREACH(m, &macros, link)
+ if (strcmp(m->name, varname) == 0)
+ break;
+
+ if (m == NULL) {
+ if ((m = malloc(sizeof(*m))) == NULL)
+ report("%m");
+ if ((m->name = malloc(strlen(varname) + 1)) == NULL) {
+ free(m);
+ report("%m");
+ }
+ strcpy(m->name, varname);
+ m->perm = 0;
+ LIST_INSERT_HEAD(&macros, m, link);
+
+ m->value = string;
+ m->length = length;
+ } else {
+ if (t != TOK_ASSIGN) {
+ free(m->value);
+ m->value = string;
+ m->length = length;
+ }
+ }
+
+ token = TOK_EOL;
+
+ ERRPOP();
+}
+
+/*
+ * Free all macros
+ */
+static void
+macro_free_all(void)
+{
+ static struct macro *m, *m1;
+
+ m = LIST_FIRST(&macros);
+ while (m != NULL) {
+ m1 = LIST_NEXT(m, link);
+ if (!m->perm) {
+ free(m->name);
+ free(m->value);
+ LIST_REMOVE(m, link);
+ free(m);
+ }
+ m = m1;
+ }
+}
+
+/*
+ * Parse an include directive and switch to the new file
+ */
+static void
+parse_include(void)
+{
+ int sysdir = 0;
+ char fname[_POSIX2_LINE_MAX];
+
+ if (gettoken() == '<') {
+ sysdir = 1;
+ if (gettoken() != TOK_STR)
+ report("expecting filename after in .include");
+ } else if (token != TOK_STR)
+ report("expecting filename after in .include");
+
+ strcpy(fname, strval);
+ if (sysdir && gettoken() != '>')
+ report("expecting '>'");
+ gettoken();
+ if (input_open_file(fname, sysdir) == -1)
+ report("%s: %m", fname);
+}
+
+/*
+ * Parse the configuration file
+ */
+static void
+parse_file(const struct lmodule *mod)
+{
+ char varname[_POSIX2_LINE_MAX];
+
+ while (gettoken() != TOK_EOF) {
+ if (token == TOK_EOL)
+ /* empty line */
+ continue;
+ if (token == '%') {
+ gettoken();
+ parse_section(mod);
+ } else if (token == '.') {
+ if (gettoken() != TOK_STR)
+ report("keyword expected after '.'");
+ if (strcmp(strval, "include") == 0)
+ parse_include();
+ else
+ report("unknown keyword '%s'", strval);
+ } else if (token == TOK_STR) {
+ strcpy(varname, strval);
+ if (gettoken() == TOK_ASSIGN || token == TOK_QASSIGN)
+ parse_define(varname);
+ else
+ parse_assign(varname);
+ }
+ if (token != TOK_EOL)
+ report("eol expected");
+ }
+}
+
+/*
+ * Do rollback on errors
+ */
+static void
+do_rollback(void)
+{
+ struct assign *tp;
+ struct snmp_node *node;
+
+ while ((tp = TAILQ_LAST(&assigns, assigns)) != NULL) {
+ TAILQ_REMOVE(&assigns, tp, link);
+ for (node = tree; node < &tree[tree_size]; node++)
+ if (node->name == tp->node_name) {
+ snmp_ctx->scratch = &tp->scratch;
+ (void)(*node->op)(snmp_ctx, &tp->value,
+ node->oid.len, node->index,
+ SNMP_OP_ROLLBACK);
+ break;
+ }
+ if (node == &tree[tree_size])
+ syslog(LOG_ERR, "failed to find node for "
+ "rollback");
+ snmp_value_free(&tp->value);
+ free(tp);
+ }
+}
+
+/*
+ * Do commit
+ */
+static void
+do_commit(void)
+{
+ struct assign *tp;
+ struct snmp_node *node;
+
+ while ((tp = TAILQ_FIRST(&assigns)) != NULL) {
+ TAILQ_REMOVE(&assigns, tp, link);
+ for (node = tree; node < &tree[tree_size]; node++)
+ if (node->name == tp->node_name) {
+ snmp_ctx->scratch = &tp->scratch;
+ (void)(*node->op)(snmp_ctx, &tp->value,
+ node->oid.len, node->index, SNMP_OP_COMMIT);
+ break;
+ }
+ if (node == &tree[tree_size])
+ syslog(LOG_ERR, "failed to find node for commit");
+ snmp_value_free(&tp->value);
+ free(tp);
+ }
+}
+
+/*
+ * Read the configuration file. Handle the entire file as one transaction.
+ *
+ * If lodmod is NULL, the sections for 'snmpd' and all loaded modules are
+ * executed. If it is not NULL, only the sections for that module are handled.
+ */
+int
+read_config(const char *fname, struct lmodule *lodmod)
+{
+ int err;
+ char objbuf[ASN_OIDSTRLEN];
+ char idxbuf[ASN_OIDSTRLEN];
+
+ ignore = 0;
+
+ input_push = 0;
+ if (input_open_file(fname, 0) == -1) {
+ syslog(LOG_ERR, "%s: %m", fname);
+ return (-1);
+ }
+ community = COMM_INITIALIZE;
+
+ if ((snmp_ctx = snmp_init_context()) == NULL) {
+ syslog(LOG_ERR, "%m");
+ return (-1);
+ }
+
+ if (ERRPUSH()) {
+ do_rollback();
+ input_close_all();
+ macro_free_all();
+ free(snmp_ctx);
+ return (-1);
+ }
+ parse_file(lodmod);
+ ERRPOP();
+
+ if ((err = snmp_dep_commit(snmp_ctx)) != SNMP_ERR_NOERROR) {
+ syslog(LOG_ERR, "init dep failed: %u %s %s", err,
+ asn_oid2str_r(&snmp_ctx->dep->obj, objbuf),
+ asn_oid2str_r(&snmp_ctx->dep->idx, idxbuf));
+ snmp_dep_rollback(snmp_ctx);
+ do_rollback();
+ input_close_all();
+ macro_free_all();
+ free(snmp_ctx);
+ return (-1);
+ }
+
+ do_commit();
+ macro_free_all();
+
+ free(snmp_ctx);
+
+ return (0);
+}
+
+/*
+ * Define a permanent macro
+ */
+int
+define_macro(const char *name, const char *value)
+{
+ struct macro *m;
+
+ if ((m = malloc(sizeof(*m))) == NULL)
+ return (-1);
+ if ((m->name = malloc(strlen(name) + 1)) == NULL) {
+ free(m);
+ return (-1);
+ }
+ strcpy(m->name, name);
+ if ((m->value = malloc(strlen(value) + 1)) == NULL) {
+ free(m->name);
+ free(m);
+ return (-1);
+ }
+ strcpy(m->value, value);
+ m->length = strlen(value);
+ return (0);
+}
diff --git a/contrib/bsnmp/snmpd/export.c b/contrib/bsnmp/snmpd/export.c
new file mode 100644
index 0000000..4cebdb3
--- /dev/null
+++ b/contrib/bsnmp/snmpd/export.c
@@ -0,0 +1,378 @@
+/*
+ * Copyright (c) 2001-2003
+ * Fraunhofer Institute for Open Communication Systems (FhG Fokus).
+ * All rights reserved.
+ *
+ * Author: Harti Brandt <harti@freebsd.org>
+ *
+ * Redistribution of this software and documentation and use in source and
+ * binary forms, with or without modification, are permitted provided that
+ * the following conditions are met:
+ *
+ * 1. Redistributions of source code or documentation must retain the above
+ * copyright notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY FRAUNHOFER FOKUS
+ * AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * FRAUNHOFER FOKUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Begemot: bsnmp/snmpd/export.c,v 1.5 2003/01/28 13:44:35 hbb Exp $
+ *
+ * Support functions for modules.
+ */
+#include <sys/types.h>
+#include <sys/un.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <stdarg.h>
+
+#include "snmpmod.h"
+#include "snmpd.h"
+#include "tree.h"
+
+/*
+ * Support functions
+ */
+
+/*
+ * This is user for SET of string variables. If 'req' is not -1 then
+ * the arguments is checked to be of that length. The old value is saved
+ * in scratch->ptr1 and the new value is allocated and copied.
+ * If there is an old values it must have been allocated by malloc.
+ */
+int
+string_save(struct snmp_value *value, struct snmp_context *ctx,
+ ssize_t req_size, u_char **valp)
+{
+ if (req_size != -1 && value->v.octetstring.len != (u_long)req_size)
+ return (SNMP_ERR_BADVALUE);
+
+ ctx->scratch->ptr1 = *valp;
+
+ if ((*valp = malloc(value->v.octetstring.len + 1)) == NULL) {
+ *valp = ctx->scratch->ptr1;
+ return (SNMP_ERR_RES_UNAVAIL);
+ }
+
+ memcpy(*valp, value->v.octetstring.octets, value->v.octetstring.len);
+ (*valp)[value->v.octetstring.len] = '\0';
+
+ return (0);
+}
+
+/*
+ * Commit a string. This is easy - free the old value.
+ */
+void
+string_commit(struct snmp_context *ctx)
+{
+ free(ctx->scratch->ptr1);
+}
+
+/*
+ * Rollback a string - free new value and copy back old one.
+ */
+void
+string_rollback(struct snmp_context *ctx, u_char **valp)
+{
+ free(*valp);
+ *valp = ctx->scratch->ptr1;
+}
+
+/*
+ * ROLLBACK or COMMIT fails because instance has disappeared. Free string.
+ */
+void
+string_free(struct snmp_context *ctx)
+{
+ free(ctx->scratch->ptr1);
+}
+
+/*
+ * Get a string value for a response packet
+ */
+int
+string_get(struct snmp_value *value, const u_char *ptr, ssize_t len)
+{
+ if (ptr == NULL) {
+ value->v.octetstring.len = 0;
+ value->v.octetstring.octets = NULL;
+ return (SNMP_ERR_NOERROR);
+ }
+ if (len == -1)
+ len = strlen(ptr);
+ value->v.octetstring.len = (u_long)len;
+ if ((value->v.octetstring.octets = malloc((size_t)len)) == NULL)
+ return (SNMP_ERR_RES_UNAVAIL);
+ memcpy(value->v.octetstring.octets, ptr, (size_t)len);
+ return (SNMP_ERR_NOERROR);
+}
+
+/*
+ * Support for IPADDRESS
+ *
+ * Save the old IP address in scratch->int1 and set the new one.
+ */
+int
+ip_save(struct snmp_value *value, struct snmp_context *ctx, u_char *valp)
+{
+ ctx->scratch->int1 = (valp[0] << 24) | (valp[1] << 16) | (valp[2] << 8)
+ | valp[3];
+
+ valp[0] = value->v.ipaddress[0];
+ valp[1] = value->v.ipaddress[1];
+ valp[2] = value->v.ipaddress[2];
+ valp[3] = value->v.ipaddress[3];
+
+ return (0);
+}
+
+/*
+ * Rollback the address by copying back the old one
+ */
+void
+ip_rollback(struct snmp_context *ctx, u_char *valp)
+{
+ valp[0] = ctx->scratch->int1 >> 24;
+ valp[1] = ctx->scratch->int1 >> 16;
+ valp[2] = ctx->scratch->int1 >> 8;
+ valp[3] = ctx->scratch->int1;
+}
+
+/*
+ * Nothing to do for commit
+ */
+void
+ip_commit(struct snmp_context *ctx __unused)
+{
+}
+
+/*
+ * Retrieve an IP address
+ */
+int
+ip_get(struct snmp_value *value, u_char *valp)
+{
+ value->v.ipaddress[0] = valp[0];
+ value->v.ipaddress[1] = valp[1];
+ value->v.ipaddress[2] = valp[2];
+ value->v.ipaddress[3] = valp[3];
+ return (SNMP_ERR_NOERROR);
+}
+
+/*
+ * Object ID support
+ *
+ * Save the old value in a fresh allocated oid pointed to by scratch->ptr1.
+ */
+int
+oid_save(struct snmp_value *value, struct snmp_context *ctx,
+ struct asn_oid *oid)
+{
+ if ((ctx->scratch->ptr1 = malloc(sizeof(struct asn_oid))) == NULL)
+ return (SNMP_ERR_RES_UNAVAIL);
+ *(struct asn_oid *)ctx->scratch->ptr1 = *oid;
+ *oid = value->v.oid;
+
+ return (0);
+}
+
+void
+oid_rollback(struct snmp_context *ctx, struct asn_oid *oid)
+{
+ *oid = *(struct asn_oid *)ctx->scratch->ptr1;
+ free(ctx->scratch->ptr1);
+}
+
+void
+oid_commit(struct snmp_context *ctx)
+{
+ free(ctx->scratch->ptr1);
+}
+
+int
+oid_get(struct snmp_value *value, const struct asn_oid *oid)
+{
+ value->v.oid = *oid;
+ return (SNMP_ERR_NOERROR);
+}
+
+/*
+ * Decode an index
+ */
+int
+index_decode(const struct asn_oid *oid, u_int sub, u_int code, ...)
+{
+ va_list ap;
+ u_int index_count;
+ void *octs[10];
+ u_int nocts;
+ u_int idx;
+
+ va_start(ap, code);
+ index_count = SNMP_INDEX_COUNT(code);
+ nocts = 0;
+
+ for (idx = 0; idx < index_count; idx++) {
+ switch (SNMP_INDEX(code, idx)) {
+
+ case SNMP_SYNTAX_NULL:
+ break;
+
+ case SNMP_SYNTAX_INTEGER:
+ if (sub == oid->len)
+ goto err;
+ *va_arg(ap, int32_t *) = oid->subs[sub++];
+ break;
+
+ case SNMP_SYNTAX_COUNTER64:
+ if (sub == oid->len)
+ goto err;
+ *va_arg(ap, u_int64_t *) = oid->subs[sub++];
+ break;
+
+ case SNMP_SYNTAX_OCTETSTRING:
+ {
+ u_char **cval;
+ size_t *sval;
+ u_int i;
+
+ /* only variable size supported */
+ if (sub == oid->len)
+ goto err;
+ cval = va_arg(ap, u_char **);
+ sval = va_arg(ap, size_t *);
+ *sval = oid->subs[sub++];
+ if (sub + *sval > oid->len)
+ goto err;
+ if ((*cval = malloc(*sval)) == NULL) {
+ syslog(LOG_ERR, "%s: %m", __func__);
+ goto err;
+ }
+ octs[nocts++] = *cval;
+ for (i = 0; i < *sval; i++) {
+ if (oid->subs[sub] > 0xff)
+ goto err;
+ (*cval)[i] = oid->subs[sub++];
+ }
+ break;
+ }
+
+ case SNMP_SYNTAX_OID:
+ {
+ struct asn_oid *aval;
+ u_int i;
+
+ if (sub == oid->len)
+ goto err;
+ aval = va_arg(ap, struct asn_oid *);
+ aval->len = oid->subs[sub++];
+ if (aval->len > ASN_MAXOIDLEN)
+ goto err;
+ for (i = 0; i < aval->len; i++)
+ aval->subs[i] = oid->subs[sub++];
+ break;
+ }
+
+ case SNMP_SYNTAX_IPADDRESS:
+ {
+ u_int8_t *pval;
+ u_int i;
+
+ if (sub + 4 > oid->len)
+ goto err;
+ pval = va_arg(ap, u_int8_t *);
+ for (i = 0; i < 4; i++) {
+ if (oid->subs[sub] > 0xff)
+ goto err;
+ pval[i] = oid->subs[sub++];
+ }
+ break;
+ }
+
+ case SNMP_SYNTAX_COUNTER:
+ case SNMP_SYNTAX_GAUGE:
+ case SNMP_SYNTAX_TIMETICKS:
+ if (sub == oid->len)
+ goto err;
+ if (oid->subs[sub] > 0xffffffff)
+ goto err;
+ *va_arg(ap, u_int32_t *) = oid->subs[sub++];
+ break;
+ }
+ }
+
+ va_end(ap);
+ return (0);
+
+ err:
+ va_end(ap);
+ while(nocts > 0)
+ free(octs[--nocts]);
+ return (-1);
+}
+
+/*
+ * Compare the index part of an OID and an index.
+ */
+int
+index_compare_off(const struct asn_oid *oid, u_int sub,
+ const struct asn_oid *idx, u_int off)
+{
+ u_int i;
+
+ for (i = off; i < idx->len && i < oid->len - sub; i++) {
+ if (oid->subs[sub + i] < idx->subs[i])
+ return (-1);
+ if (oid->subs[sub + i] > idx->subs[i])
+ return (+1);
+ }
+ if (oid->len - sub < idx->len)
+ return (-1);
+ if (oid->len - sub > idx->len)
+ return (+1);
+
+ return (0);
+}
+
+int
+index_compare(const struct asn_oid *oid, u_int sub, const struct asn_oid *idx)
+{
+ return (index_compare_off(oid, sub, idx, 0));
+}
+
+/*
+ * Append an index to an oid
+ */
+void
+index_append_off(struct asn_oid *var, u_int sub, const struct asn_oid *idx,
+ u_int off)
+{
+ u_int i;
+
+ var->len = sub + idx->len;
+ for (i = off; i < idx->len; i++)
+ var->subs[sub + i] = idx->subs[i];
+}
+void
+index_append(struct asn_oid *var, u_int sub, const struct asn_oid *idx)
+{
+ index_append_off(var, sub, idx, 0);
+}
+
diff --git a/contrib/bsnmp/snmpd/main.c b/contrib/bsnmp/snmpd/main.c
new file mode 100644
index 0000000..1568b4b
--- /dev/null
+++ b/contrib/bsnmp/snmpd/main.c
@@ -0,0 +1,2038 @@
+/*
+ * Copyright (c) 2001-2003
+ * Fraunhofer Institute for Open Communication Systems (FhG Fokus).
+ * All rights reserved.
+ *
+ * Author: Harti Brandt <harti@freebsd.org>
+ *
+ * Redistribution of this software and documentation and use in source and
+ * binary forms, with or without modification, are permitted provided that
+ * the following conditions are met:
+ *
+ * 1. Redistributions of source code or documentation must retain the above
+ * copyright notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY FRAUNHOFER FOKUS
+ * AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * FRAUNHOFER FOKUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Begemot: bsnmp/snmpd/main.c,v 1.76 2003/01/28 13:44:35 hbb Exp $
+ *
+ * SNMPd main stuff.
+ */
+#include <sys/param.h>
+#include <sys/un.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <errno.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <signal.h>
+#include <dlfcn.h>
+#include <inttypes.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "snmpmod.h"
+#include "snmpd.h"
+#include "tree.h"
+#include "oid.h"
+
+#define PATH_PID "/var/run/%s.pid"
+#define PATH_CONFIG "/etc/%s.config"
+
+u_int32_t this_tick; /* start of processing of current packet */
+u_int32_t start_tick; /* start of processing */
+
+struct systemg systemg = {
+ NULL,
+ { 8, { 1, 3, 6, 1, 4, 1, 1115, 7352 }},
+ NULL, NULL, NULL,
+ 64 + 8 + 4,
+ 0
+};
+struct debug debug = {
+ 0, /* dump_pdus */
+ LOG_DEBUG, /* log_pri */
+ 0, /* evdebug */
+};
+
+struct snmpd snmpd = {
+ 2048, /* txbuf */
+ 2048, /* rxbuf */
+ 0, /* comm_dis */
+ 0, /* auth_traps */
+ {0, 0, 0, 0}, /* trap1addr */
+};
+struct snmpd_stats snmpd_stats;
+
+/* snmpSerialNo */
+int32_t snmp_serial_no;
+
+/* search path for config files */
+const char *syspath = PATH_SYSCONFIG;
+
+/* list of all loaded modules */
+struct lmodules lmodules = TAILQ_HEAD_INITIALIZER(lmodules);
+
+/* list of loaded modules during start-up in the order they were loaded */
+static struct lmodules modules_start = TAILQ_HEAD_INITIALIZER(modules_start);
+
+/* list of all known communities */
+struct community_list community_list = TAILQ_HEAD_INITIALIZER(community_list);
+
+/* list of all installed object resources */
+struct objres_list objres_list = TAILQ_HEAD_INITIALIZER(objres_list);
+
+/* community value generator */
+static u_int next_community_index = 1;
+
+/* list of all known ranges */
+struct idrange_list idrange_list = TAILQ_HEAD_INITIALIZER(idrange_list);
+
+/* identifier generator */
+u_int next_idrange = 1;
+
+/* list of all current timers */
+struct timer_list timer_list = LIST_HEAD_INITIALIZER(timer_list);
+
+/* list of file descriptors */
+struct fdesc_list fdesc_list = LIST_HEAD_INITIALIZER(fdesc_list);
+
+/* program arguments */
+static char **progargs;
+static int nprogargs;
+
+/* current community */
+u_int community;
+static struct community *comm;
+
+/* list of all IP ports we are listening on */
+struct snmp_port_list snmp_port_list =
+ TAILQ_HEAD_INITIALIZER(snmp_port_list);
+
+/* list of all local ports we are listening on */
+struct local_port_list local_port_list =
+ TAILQ_HEAD_INITIALIZER(local_port_list);
+
+/* file names */
+static char config_file[MAXPATHLEN + 1];
+static char pid_file[MAXPATHLEN + 1];
+
+/* event context */
+static evContext evctx;
+
+/* signal mask */
+static sigset_t blocked_sigs;
+
+/* signal handling */
+static int work;
+#define WORK_DOINFO 0x0001
+#define WORK_RECONFIG 0x0002
+
+/* oids */
+static const struct asn_oid
+ oid_snmpMIB = OIDX_snmpMIB,
+ oid_begemotSnmpd = OIDX_begemotSnmpd,
+ oid_coldStart = OIDX_coldStart,
+ oid_authenticationFailure = OIDX_authenticationFailure;
+
+const struct asn_oid oid_zeroDotZero = { 2, { 0, 0 }};
+
+/* request id generator for traps */
+u_int trap_reqid;
+
+/* help text */
+static const char usgtxt[] = "\
+Begemot simple SNMP daemon. Copyright (c) 2001-2002 Fraunhofer Institute for\n\
+Open Communication Systems (FhG Fokus). All rights reserved.\n\
+usage: snmpd [-dh] [-c file] [-D options] [-I path] [-l prefix]\n\
+ [-m variable=value] [-p file]\n\
+options:\n\
+ -d don't daemonize\n\
+ -h print this info\n\
+ -c file specify configuration file\n\
+ -D options debugging options\n\
+ -I path system include path\n\
+ -l prefix default basename for pid and config file\n\
+ -m var=val define variable\n\
+ -p file specify pid file\n\
+";
+
+/* forward declarations */
+static void snmp_printf_func(const char *fmt, ...);
+static void snmp_error_func(const char *err, ...);
+static void snmp_debug_func(const char *err, ...);
+static void asn_error_func(const struct asn_buf *b, const char *err, ...);
+
+/*
+ * Allocate rx/tx buffer. We allocate one byte more for rx.
+ */
+void *
+buf_alloc(int tx)
+{
+ void *buf;
+
+ if ((buf = malloc(tx ? snmpd.txbuf : (snmpd.rxbuf + 1))) == NULL) {
+ syslog(LOG_CRIT, "cannot allocate buffer");
+ if (tx)
+ snmpd_stats.noTxbuf++;
+ else
+ snmpd_stats.noRxbuf++;
+ return (NULL);
+ }
+ return (buf);
+}
+
+/*
+ * Return the buffer size. (one more for RX).
+ */
+size_t
+buf_size(int tx)
+{
+ return (tx ? snmpd.txbuf : (snmpd.rxbuf + 1));
+}
+
+/*
+ * Prepare a PDU for output
+ */
+void
+snmp_output(struct snmp_v1_pdu *pdu, u_char *sndbuf, size_t *sndlen,
+ const char *dest)
+{
+ struct asn_buf resp_b;
+
+ resp_b.asn_ptr = sndbuf;
+ resp_b.asn_len = snmpd.txbuf;
+
+ if (snmp_pdu_encode(pdu, &resp_b) != 0) {
+ syslog(LOG_ERR, "cannot encode message");
+ abort();
+ }
+ if (debug.dump_pdus) {
+ snmp_printf("%s <- ", dest);
+ snmp_pdu_dump(pdu);
+ }
+ *sndlen = (size_t)(resp_b.asn_ptr - sndbuf);
+}
+
+/*
+ * Send a PDU to a given port
+ */
+void
+snmp_send_port(const struct asn_oid *port, struct snmp_v1_pdu *pdu,
+ const struct sockaddr *addr, socklen_t addrlen)
+{
+ struct snmp_port *p;
+ u_char *sndbuf;
+ size_t sndlen;
+ ssize_t len;
+
+ TAILQ_FOREACH(p, &snmp_port_list, link)
+ if (asn_compare_oid(port, &p->index) == 0)
+ break;
+
+ if (p == 0)
+ return;
+
+ if ((sndbuf = buf_alloc(1)) == NULL)
+ return;
+
+ snmp_output(pdu, sndbuf, &sndlen, "SNMP PROXY");
+
+ if ((len = sendto(p->sock, sndbuf, sndlen, 0, addr, addrlen)) == -1)
+ syslog(LOG_ERR, "sendto: %m");
+ else if ((size_t)len != sndlen)
+ syslog(LOG_ERR, "sendto: short write %zu/%zu",
+ sndlen, (size_t)len);
+
+ free(sndbuf);
+}
+
+/*
+ * SNMP input. Start: decode the PDU, find the community.
+ */
+enum snmpd_input_err
+snmp_input_start(const u_char *buf, size_t len, const char *source,
+ struct snmp_v1_pdu *pdu, int32_t *ip)
+{
+ struct asn_buf b;
+ enum snmp_code code;
+ enum snmpd_input_err ret;
+
+ snmpd_stats.inPkts++;
+
+ b.asn_cptr = buf;
+ b.asn_len = len;
+ code = snmp_pdu_decode(&b, pdu, ip);
+
+ ret = SNMPD_INPUT_OK;
+ switch (code) {
+
+ case SNMP_CODE_FAILED:
+ snmpd_stats.inASNParseErrs++;
+ return (SNMPD_INPUT_FAILED);
+
+ case SNMP_CODE_BADVERS:
+ snmpd_stats.inBadVersions++;
+ return (SNMPD_INPUT_FAILED);
+
+ case SNMP_CODE_BADLEN:
+ if (pdu->type == SNMP_OP_SET)
+ ret = SNMPD_INPUT_VALBADLEN;
+ break;
+
+ case SNMP_CODE_OORANGE:
+ if (pdu->type == SNMP_OP_SET)
+ ret = SNMPD_INPUT_VALRANGE;
+ break;
+
+ case SNMP_CODE_BADENC:
+ if (pdu->type == SNMP_OP_SET)
+ ret = SNMPD_INPUT_VALBADENC;
+ break;
+
+ case SNMP_CODE_OK:
+ break;
+ }
+
+ if (debug.dump_pdus) {
+ snmp_printf("%s -> ", source);
+ snmp_pdu_dump(pdu);
+ }
+
+ /*
+ * Look, whether we know the community
+ */
+ TAILQ_FOREACH(comm, &community_list, link)
+ if (comm->string != NULL &&
+ strcmp(comm->string, pdu->community) == 0)
+ break;
+
+ if (comm == NULL) {
+ snmpd_stats.inBadCommunityNames++;
+ snmp_pdu_free(pdu);
+ if (snmpd.auth_traps)
+ snmp_send_trap(&oid_authenticationFailure, NULL);
+ return (SNMPD_INPUT_FAILED);
+ }
+ community = comm->value;
+
+ /* update uptime */
+ this_tick = get_ticks();
+
+ return (ret);
+}
+
+/*
+ * Will return only _OK or _FAILED
+ */
+enum snmpd_input_err
+snmp_input_finish(struct snmp_pdu *pdu, const u_char *rcvbuf, size_t rcvlen,
+ u_char *sndbuf, size_t *sndlen, const char *source,
+ enum snmpd_input_err ierr, int32_t ivar, void *data)
+{
+ struct snmp_pdu resp;
+ struct asn_buf resp_b, pdu_b;
+ enum snmp_ret ret;
+
+ resp_b.asn_ptr = sndbuf;
+ resp_b.asn_len = snmpd.txbuf;
+
+ pdu_b.asn_cptr = rcvbuf;
+ pdu_b.asn_len = rcvlen;
+
+ if (ierr != SNMPD_INPUT_OK) {
+ /* error decoding the input of a SET */
+ if (pdu->version == SNMP_V1)
+ pdu->error_status = SNMP_ERR_BADVALUE;
+ else if (ierr == SNMPD_INPUT_VALBADLEN)
+ pdu->error_status = SNMP_ERR_WRONG_LENGTH;
+ else if (ierr == SNMPD_INPUT_VALRANGE)
+ pdu->error_status = SNMP_ERR_WRONG_VALUE;
+ else
+ pdu->error_status = SNMP_ERR_WRONG_ENCODING;
+
+ pdu->error_index = ivar;
+
+ if (snmp_make_errresp(pdu, &pdu_b, &resp_b) == SNMP_RET_IGN) {
+ syslog(LOG_WARNING, "could not encode error response");
+ snmpd_stats.silentDrops++;
+ return (SNMPD_INPUT_FAILED);
+ }
+
+ if (debug.dump_pdus) {
+ snmp_printf("%s <- ", source);
+ snmp_pdu_dump(pdu);
+ }
+ *sndlen = (size_t)(resp_b.asn_ptr - sndbuf);
+ return (SNMPD_INPUT_OK);
+ }
+
+ switch (pdu->type) {
+
+ case SNMP_PDU_GET:
+ ret = snmp_get(pdu, &resp_b, &resp, data);
+ break;
+
+ case SNMP_PDU_GETNEXT:
+ ret = snmp_getnext(pdu, &resp_b, &resp, data);
+ break;
+
+ case SNMP_PDU_SET:
+ ret = snmp_set(pdu, &resp_b, &resp, data);
+ break;
+
+ case SNMP_PDU_GETBULK:
+ ret = snmp_getbulk(pdu, &resp_b, &resp, data);
+ break;
+
+ default:
+ ret = SNMP_RET_IGN;
+ break;
+ }
+
+ switch (ret) {
+
+ case SNMP_RET_OK:
+ /* normal return - send a response */
+ if (debug.dump_pdus) {
+ snmp_printf("%s <- ", source);
+ snmp_pdu_dump(&resp);
+ }
+ *sndlen = (size_t)(resp_b.asn_ptr - sndbuf);
+ snmp_pdu_free(&resp);
+ return (SNMPD_INPUT_OK);
+
+ case SNMP_RET_IGN:
+ /* error - send nothing */
+ snmpd_stats.silentDrops++;
+ return (SNMPD_INPUT_FAILED);
+
+ case SNMP_RET_ERR:
+ /* error - send error response. The snmp routine has
+ * changed the error fields in the original message. */
+ resp_b.asn_ptr = sndbuf;
+ resp_b.asn_len = snmpd.txbuf;
+ if (snmp_make_errresp(pdu, &pdu_b, &resp_b) == SNMP_RET_IGN) {
+ syslog(LOG_WARNING, "could not encode error response");
+ snmpd_stats.silentDrops++;
+ return (SNMPD_INPUT_FAILED);
+ } else {
+ if (debug.dump_pdus) {
+ snmp_printf("%s <- ", source);
+ snmp_pdu_dump(pdu);
+ }
+ *sndlen = (size_t)(resp_b.asn_ptr - sndbuf);
+ return (SNMPD_INPUT_OK);
+ }
+ }
+ abort();
+}
+
+
+
+/*
+ * File descriptor support
+ */
+static void
+input(evContext ctx __unused, void *uap, int fd, int mask __unused)
+{
+ struct fdesc *f = uap;
+
+ (*f->func)(fd, f->udata);
+}
+
+void
+fd_suspend(void *p)
+{
+ struct fdesc *f = p;
+
+ if (evTestID(f->id)) {
+ (void)evDeselectFD(evctx, f->id);
+ evInitID(&f->id);
+ }
+}
+
+int
+fd_resume(void *p)
+{
+ struct fdesc *f = p;
+ int err;
+
+ if (evTestID(f->id))
+ return (0);
+ if (evSelectFD(evctx, f->fd, EV_READ, input, f, &f->id)) {
+ err = errno;
+ syslog(LOG_ERR, "select fd %d: %m", f->fd);
+ errno = err;
+ return (-1);
+ }
+ return (0);
+}
+
+void *
+fd_select(int fd, void (*func)(int, void *), void *udata, struct lmodule *mod)
+{
+ struct fdesc *f;
+ int err;
+
+ if ((f = malloc(sizeof(struct fdesc))) == NULL) {
+ err = errno;
+ syslog(LOG_ERR, "fd_select: %m");
+ errno = err;
+ return (NULL);
+ }
+ f->fd = fd;
+ f->func = func;
+ f->udata = udata;
+ f->owner = mod;
+ evInitID(&f->id);
+
+ if (fd_resume(f)) {
+ err = errno;
+ free(f);
+ errno = err;
+ return (NULL);
+ }
+
+ LIST_INSERT_HEAD(&fdesc_list, f, link);
+
+ return (f);
+}
+
+void
+fd_deselect(void *p)
+{
+ struct fdesc *f = p;
+
+ LIST_REMOVE(f, link);
+ fd_suspend(f);
+ free(f);
+}
+
+static void
+fd_flush(struct lmodule *mod)
+{
+ struct fdesc *t, *t1;
+
+ t = LIST_FIRST(&fdesc_list);
+ while (t != NULL) {
+ t1 = LIST_NEXT(t, link);
+ if (t->owner == mod)
+ fd_deselect(t);
+ t = t1;
+ }
+
+}
+
+
+/*
+ * Input from UDP socket
+ */
+static void
+do_input(int fd, const struct asn_oid *port_index,
+ struct sockaddr *ret, socklen_t *retlen)
+{
+ u_char *resbuf, embuf[100];
+ u_char *sndbuf;
+ size_t sndlen;
+ ssize_t len;
+ struct snmp_v1_pdu pdu;
+ enum snmpd_input_err ierr, ferr;
+ enum snmpd_proxy_err perr;
+ int32_t vi;
+
+ if ((resbuf = buf_alloc(0)) == NULL) {
+ (void)recvfrom(fd, embuf, sizeof(embuf), 0, ret, retlen);
+ return;
+ }
+ if ((len = recvfrom(fd, resbuf, buf_size(0), 0, ret, retlen)) == -1) {
+ free(resbuf);
+ return;
+ }
+ if (len == 0) {
+ free(resbuf);
+ return;
+ }
+ if ((size_t)len == buf_size(0)) {
+ free(resbuf);
+ snmpd_stats.silentDrops++;
+ snmpd_stats.inTooLong++;
+ return;
+ }
+
+ /*
+ * Handle input
+ */
+ ierr = snmp_input_start(resbuf, (size_t)len, "SNMP", &pdu, &vi);
+
+ /* can't check for bad SET pdus here, because a proxy may have to
+ * check the access first. We don't want to return an error response
+ * to a proxy PDU with a wrong community */
+ if (ierr == SNMPD_INPUT_FAILED) {
+ free(resbuf);
+ return;
+ }
+
+ /*
+ * If that is a module community and the module has a proxy function,
+ * the hand it over to the module.
+ */
+ if (comm->owner != NULL && comm->owner->config->proxy != NULL) {
+ perr = (*comm->owner->config->proxy)(&pdu, port_index,
+ ret, *retlen, ierr, vi);
+
+ switch (perr) {
+
+ case SNMPD_PROXY_OK:
+ free(resbuf);
+ return;
+
+ case SNMPD_PROXY_REJ:
+ break;
+
+ case SNMPD_PROXY_DROP:
+ free(resbuf);
+ snmp_pdu_free(&pdu);
+ snmpd_stats.proxyDrops++;
+ return;
+
+ case SNMPD_PROXY_BADCOMM:
+ free(resbuf);
+ snmp_pdu_free(&pdu);
+ snmpd_stats.inBadCommunityNames++;
+ if (snmpd.auth_traps)
+ snmp_send_trap(&oid_authenticationFailure,
+ NULL);
+ return;
+
+ case SNMPD_PROXY_BADCOMMUSE:
+ free(resbuf);
+ snmp_pdu_free(&pdu);
+ snmpd_stats.inBadCommunityUses++;
+ if (snmpd.auth_traps)
+ snmp_send_trap(&oid_authenticationFailure,
+ NULL);
+ return;
+ }
+ }
+
+ /*
+ * Check type
+ */
+ if (pdu.type == SNMP_PDU_RESPONSE ||
+ pdu.type == SNMP_PDU_TRAP ||
+ pdu.type == SNMP_PDU_TRAP2) {
+ snmpd_stats.silentDrops++;
+ snmpd_stats.inBadPduTypes++;
+ snmp_pdu_free(&pdu);
+ free(resbuf);
+ return;
+ }
+
+ /*
+ * Check community
+ */
+ if (community != COMM_WRITE &&
+ (pdu.type == SNMP_PDU_SET || community != COMM_READ)) {
+ snmpd_stats.inBadCommunityUses++;
+ snmp_pdu_free(&pdu);
+ free(resbuf);
+ if (snmpd.auth_traps)
+ snmp_send_trap(&oid_authenticationFailure, NULL);
+ return;
+ }
+
+ /*
+ * Execute it.
+ */
+ if ((sndbuf = buf_alloc(1)) == NULL) {
+ snmpd_stats.silentDrops++;
+ snmp_pdu_free(&pdu);
+ free(resbuf);
+ return;
+ }
+ ferr = snmp_input_finish(&pdu, resbuf, len, sndbuf, &sndlen, "SNMP",
+ ierr, vi, NULL);
+
+ if (ferr == SNMPD_INPUT_OK) {
+ if ((len = sendto(fd, sndbuf, sndlen, 0, ret, *retlen)) == -1)
+ syslog(LOG_ERR, "sendto: %m");
+ else if ((size_t)len != sndlen)
+ syslog(LOG_ERR, "sendto: short write %zu/%zu",
+ sndlen, (size_t)len);
+ }
+ snmp_pdu_free(&pdu);
+ free(sndbuf);
+ free(resbuf);
+}
+
+static void
+ssock_input(int fd, void *udata)
+{
+ struct snmp_port *p = udata;
+
+ p->retlen = sizeof(p->ret);
+ do_input(fd, &p->index, (struct sockaddr *)&p->ret, &p->retlen);
+}
+
+static void
+lsock_input(int fd, void *udata)
+{
+ struct local_port *p = udata;
+
+ p->retlen = sizeof(p->ret);
+ do_input(fd, &p->index, (struct sockaddr *)&p->ret, &p->retlen);
+}
+
+
+/*
+ * Create a UDP socket and bind it to the given port
+ */
+static int
+init_snmp(struct snmp_port *p)
+{
+ struct sockaddr_in addr;
+ u_int32_t ip;
+
+ if ((p->sock = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
+ syslog(LOG_ERR, "creating UDP socket: %m");
+ return (SNMP_ERR_RES_UNAVAIL);
+ }
+ ip = (p->addr[0] << 24) | (p->addr[1] << 16) | (p->addr[2] << 8) |
+ p->addr[3];
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_addr.s_addr = htonl(ip);
+ addr.sin_port = htons(p->port);
+ addr.sin_family = AF_INET;
+ addr.sin_len = sizeof(addr);
+ if (bind(p->sock, (struct sockaddr *)&addr, sizeof(addr))) {
+ if (errno == EADDRNOTAVAIL) {
+ close(p->sock);
+ p->sock = -1;
+ return (SNMP_ERR_INCONS_NAME);
+ }
+ syslog(LOG_ERR, "bind: %s:%u %m", inet_ntoa(addr.sin_addr),
+ p->port);
+ close(p->sock);
+ p->sock = -1;
+ return (SNMP_ERR_GENERR);
+ }
+ if ((p->id = fd_select(p->sock, ssock_input, p, NULL)) == NULL) {
+ close(p->sock);
+ p->sock = -1;
+ return (SNMP_ERR_GENERR);
+ }
+ return (SNMP_ERR_NOERROR);
+}
+
+
+/*
+ * Create a new SNMP Port object and start it, if we are not
+ * in initialisation mode. The arguments are in host byte order.
+ */
+int
+open_snmp_port(u_int8_t *addr, u_int32_t port, struct snmp_port **pp)
+{
+ struct snmp_port *snmp, *p;
+ int err;
+
+ if (port > 0xffff)
+ return (SNMP_ERR_NO_CREATION);
+ if ((snmp = malloc(sizeof(*snmp))) == NULL)
+ return (SNMP_ERR_GENERR);
+ snmp->addr[0] = addr[0];
+ snmp->addr[1] = addr[1];
+ snmp->addr[2] = addr[2];
+ snmp->addr[3] = addr[3];
+ snmp->port = port;
+ snmp->sock = -1;
+ snmp->id = NULL;
+ snmp->index.len = 5;
+ snmp->index.subs[0] = addr[0];
+ snmp->index.subs[1] = addr[1];
+ snmp->index.subs[2] = addr[2];
+ snmp->index.subs[3] = addr[3];
+ snmp->index.subs[4] = port;
+
+ /*
+ * Insert it into the right place
+ */
+ TAILQ_FOREACH(p, &snmp_port_list, link) {
+ if (asn_compare_oid(&p->index, &snmp->index) > 0) {
+ TAILQ_INSERT_BEFORE(p, snmp, link);
+ break;
+ }
+ }
+ if (p == NULL)
+ TAILQ_INSERT_TAIL(&snmp_port_list, snmp, link);
+
+ if (community != COMM_INITIALIZE &&
+ (err = init_snmp(snmp)) != SNMP_ERR_NOERROR) {
+ TAILQ_REMOVE(&snmp_port_list, snmp, link);
+ free(snmp);
+ return (err);
+ }
+ *pp = snmp;
+ return (SNMP_ERR_NOERROR);
+}
+
+/*
+ * Close an SNMP port
+ */
+void
+close_snmp_port(struct snmp_port *snmp)
+{
+ if (snmp->id != NULL)
+ fd_deselect(snmp->id);
+ if (snmp->sock >= 0)
+ (void)close(snmp->sock);
+
+ TAILQ_REMOVE(&snmp_port_list, snmp, link);
+ free(snmp);
+}
+
+/*
+ * Create a local socket
+ */
+static int
+init_local(struct local_port *p)
+{
+ struct sockaddr_un sa;
+
+ if ((p->sock = socket(PF_LOCAL, SOCK_DGRAM, 0)) < 0) {
+ syslog(LOG_ERR, "creating local socket: %m");
+ return (SNMP_ERR_RES_UNAVAIL);
+ }
+ strcpy(sa.sun_path, p->name);
+ sa.sun_family = AF_LOCAL;
+ sa.sun_len = strlen(p->name) + offsetof(struct sockaddr_un, sun_path);
+
+ (void)remove(p->name);
+
+ if (bind(p->sock, (struct sockaddr *)&sa, sizeof(sa))) {
+ if (errno == EADDRNOTAVAIL) {
+ close(p->sock);
+ p->sock = -1;
+ return (SNMP_ERR_INCONS_NAME);
+ }
+ syslog(LOG_ERR, "bind: %s %m", p->name);
+ close(p->sock);
+ p->sock = -1;
+ return (SNMP_ERR_GENERR);
+ }
+ if (chmod(p->name, 0666) == -1)
+ syslog(LOG_WARNING, "chmod(%s,0666): %m", p->name);
+ if ((p->id = fd_select(p->sock, lsock_input, p, NULL)) == NULL) {
+ (void)remove(p->name);
+ close(p->sock);
+ p->sock = -1;
+ return (SNMP_ERR_GENERR);
+ }
+ return (SNMP_ERR_NOERROR);
+}
+
+
+/*
+ * Open a local port
+ */
+int
+open_local_port(u_char *name, size_t namelen, struct local_port **pp)
+{
+ struct local_port *port, *p;
+ size_t u;
+ int err;
+ struct sockaddr_un sa;
+
+ if (namelen == 0 || namelen + 1 > sizeof(sa.sun_path)) {
+ free(name);
+ return (SNMP_ERR_BADVALUE);
+ }
+ if ((port = malloc(sizeof(*port))) == NULL) {
+ free(name);
+ return (SNMP_ERR_GENERR);
+ }
+ if ((port->name = malloc(namelen + 1)) == NULL) {
+ free(name);
+ free(port);
+ return (SNMP_ERR_GENERR);
+ }
+ strncpy(port->name, name, namelen);
+ port->name[namelen] = '\0';
+
+ port->sock = -1;
+ port->id = NULL;
+ port->index.len = namelen + 1;
+ port->index.subs[0] = namelen;
+ for (u = 0; u < namelen; u++)
+ port->index.subs[u + 1] = name[u];
+
+ /*
+ * Insert it into the right place
+ */
+ TAILQ_FOREACH(p, &local_port_list, link) {
+ if (asn_compare_oid(&p->index, &port->index) > 0) {
+ TAILQ_INSERT_BEFORE(p, port, link);
+ break;
+ }
+ }
+ if (p == NULL)
+ TAILQ_INSERT_TAIL(&local_port_list, port, link);
+
+ if (community != COMM_INITIALIZE &&
+ (err = init_local(port)) != SNMP_ERR_NOERROR) {
+ TAILQ_REMOVE(&local_port_list, port, link);
+ free(port->name);
+ free(port);
+ return (err);
+ }
+
+ *pp = p;
+
+ return (SNMP_ERR_NOERROR);
+}
+
+/*
+ * Close a local port
+ */
+void
+close_local_port(struct local_port *port)
+{
+ if (port->id != NULL)
+ fd_deselect(port->id);
+ if (port->sock >= 0)
+ (void)close(port->sock);
+ (void)remove(port->name);
+
+ TAILQ_REMOVE(&local_port_list, port, link);
+ free(port->name);
+ free(port);
+}
+
+/*
+ * Dump internal state.
+ */
+static void
+info_func(evContext ctx __unused, void *uap __unused, const void *tag __unused)
+{
+ struct lmodule *m;
+ u_int i;
+ char buf[10000];
+
+ syslog(LOG_DEBUG, "Dump of SNMPd %lu\n", (u_long)getpid());
+ for (i = 0; i < tree_size; i++) {
+ switch (tree[i].type) {
+
+ case SNMP_NODE_LEAF:
+ sprintf(buf, "LEAF: %s %s", tree[i].name,
+ asn_oid2str(&tree[i].oid));
+ break;
+
+ case SNMP_NODE_COLUMN:
+ sprintf(buf, "COL: %s %s", tree[i].name,
+ asn_oid2str(&tree[i].oid));
+ break;
+ }
+ syslog(LOG_DEBUG, "%s", buf);
+ }
+
+ TAILQ_FOREACH(m, &lmodules, link)
+ if (m->config->dump)
+ (*m->config->dump)();
+}
+
+/*
+ * Re-read configuration
+ */
+static void
+config_func(evContext ctx __unused, void *uap __unused,
+ const void *tag __unused)
+{
+ struct lmodule *m;
+
+ if (read_config(config_file, NULL)) {
+ syslog(LOG_ERR, "error reading config file '%s'", config_file);
+ return;
+ }
+ TAILQ_FOREACH(m, &lmodules, link)
+ if (m->config->config)
+ (*m->config->config)();
+}
+
+/*
+ * On USR1 dump actual configuration.
+ */
+static void
+onusr1(int s __unused)
+{
+ work |= WORK_DOINFO;
+}
+static void
+onhup(int s __unused)
+{
+ work |= WORK_RECONFIG;
+}
+
+static void
+onterm(int s __unused)
+{
+ struct local_port *p;
+
+ TAILQ_FOREACH(p, &local_port_list, link)
+ (void)remove(p->name);
+
+ exit(0);
+}
+
+
+static void
+init_sigs(void)
+{
+ struct sigaction sa;
+
+ sa.sa_handler = onusr1;
+ sa.sa_flags = SA_RESTART;
+ sigemptyset(&sa.sa_mask);
+ if (sigaction(SIGUSR1, &sa, NULL)) {
+ syslog(LOG_ERR, "sigaction: %m");
+ exit(1);
+ }
+
+ sa.sa_handler = onhup;
+ if (sigaction(SIGHUP, &sa, NULL)) {
+ syslog(LOG_ERR, "sigaction: %m");
+ exit(1);
+ }
+
+ sa.sa_handler = onterm;
+ sa.sa_flags = 0;
+ sigemptyset(&sa.sa_mask);
+ if (sigaction(SIGTERM, &sa, NULL)) {
+ syslog(LOG_ERR, "sigaction: %m");
+ exit(1);
+ }
+ if (sigaction(SIGINT, &sa, NULL)) {
+ syslog(LOG_ERR, "sigaction: %m");
+ exit(1);
+ }
+}
+
+static void
+block_sigs(void)
+{
+ sigset_t set;
+
+ sigfillset(&set);
+ if (sigprocmask(SIG_BLOCK, &set, &blocked_sigs) == -1) {
+ syslog(LOG_ERR, "SIG_BLOCK: %m");
+ exit(1);
+ }
+}
+static void
+unblock_sigs(void)
+{
+ if (sigprocmask(SIG_SETMASK, &blocked_sigs, NULL) == -1) {
+ syslog(LOG_ERR, "SIG_SETMASK: %m");
+ exit(1);
+ }
+}
+
+/*
+ * Shut down
+ */
+static void
+term(void)
+{
+ (void)unlink(pid_file);
+}
+
+/*
+ * Define a macro from the command line
+ */
+static void
+do_macro(char *arg)
+{
+ char *eq;
+ int err;
+
+ if ((eq = strchr(arg, '=')) == NULL)
+ err = define_macro(arg, "");
+ else {
+ *eq++ = '\0';
+ err = define_macro(arg, eq);
+ }
+ if (err == -1) {
+ syslog(LOG_ERR, "cannot save macro: %m");
+ exit(1);
+ }
+}
+
+/*
+ * Re-implement getsubopt from scratch, because the second argument is broken
+ * and will not compile with WARNS=5.
+ */
+static int
+getsubopt1(char **arg, const char *const *options, char **valp, char **optp)
+{
+ static const char *const delim = ",\t ";
+ u_int i;
+ char *ptr;
+
+ *optp = NULL;
+
+ /* skip leading junk */
+ for (ptr = *arg; *ptr != '\0'; ptr++)
+ if (strchr(delim, *ptr) == NULL)
+ break;
+ if (*ptr == '\0') {
+ *arg = ptr;
+ return (-1);
+ }
+ *optp = ptr;
+
+ /* find the end of the option */
+ while (*++ptr != '\0')
+ if (strchr(delim, *ptr) != NULL || *ptr == '=')
+ break;
+
+ if (*ptr != '\0') {
+ if (*ptr == '=') {
+ *ptr++ = '\0';
+ *valp = ptr;
+ while (*ptr != '\0' && strchr(delim, *ptr) == NULL)
+ ptr++;
+ if (*ptr != '\0')
+ *ptr++ = '\0';
+ } else
+ *ptr++ = '\0';
+ }
+
+ *arg = ptr;
+
+ for (i = 0; *options != NULL; options++, i++)
+ if (strcmp(suboptarg, *options) == 0)
+ return (i);
+ return (-1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int opt;
+ FILE *fp;
+ int background = 1;
+ struct snmp_port *p;
+ struct local_port *pl;
+ const char *prefix = "snmpd";
+ struct lmodule *m;
+ char *value, *option;
+
+#define DBG_DUMP 0
+#define DBG_EVENTS 1
+#define DBG_TRACE 2
+ static const char *const debug_opts[] = {
+ "dump",
+ "events",
+ "trace",
+ NULL
+ };
+
+ snmp_printf = snmp_printf_func;
+ snmp_error = snmp_error_func;
+ snmp_debug = snmp_debug_func;
+ asn_error = asn_error_func;
+
+ while ((opt = getopt(argc, argv, "c:dD:hI:l:m:p:")) != EOF)
+ switch (opt) {
+
+ case 'c':
+ strlcpy(config_file, optarg, sizeof(config_file));
+ break;
+
+ case 'd':
+ background = 0;
+ break;
+
+ case 'D':
+ while (*optarg) {
+ switch (getsubopt1(&optarg, debug_opts,
+ &value, &option)) {
+
+ case DBG_DUMP:
+ debug.dump_pdus = 1;
+ break;
+
+ case DBG_EVENTS:
+ debug.evdebug++;
+ break;
+
+ case DBG_TRACE:
+ if (value == NULL)
+ syslog(LOG_ERR,
+ "no value for 'trace'");
+ snmp_trace = strtoul(value, NULL, 0);
+ break;
+
+ case -1:
+ if (suboptarg)
+ syslog(LOG_ERR,
+ "unknown debug flag '%s'",
+ option);
+ else
+ syslog(LOG_ERR,
+ "missing debug flag");
+ break;
+ }
+ }
+ break;
+
+ case 'h':
+ fprintf(stderr, "%s", usgtxt);
+ exit(0);
+
+ case 'I':
+ syspath = optarg;
+ break;
+
+ case 'l':
+ prefix = optarg;
+ break;
+
+ case 'm':
+ do_macro(optarg);
+ break;
+
+ case 'p':
+ strlcpy(pid_file, optarg, sizeof(pid_file));
+ break;
+ }
+
+ openlog(prefix, LOG_PID | (background ? 0 : LOG_PERROR), LOG_USER);
+ setlogmask(LOG_UPTO(debug.logpri - 1));
+
+ if (background && daemon(0, 0) < 0) {
+ syslog(LOG_ERR, "daemon: %m");
+ exit(1);
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ progargs = argv;
+ nprogargs = argc;
+
+ srandomdev();
+
+ snmp_serial_no = random();
+
+ /*
+ * Initialize the tree.
+ */
+ if ((tree = malloc(sizeof(struct snmp_node) * CTREE_SIZE)) == NULL) {
+ syslog(LOG_ERR, "%m");
+ exit(1);
+ }
+ memcpy(tree, ctree, sizeof(struct snmp_node) * CTREE_SIZE);
+ tree_size = CTREE_SIZE;
+
+ /*
+ * Get standard communities
+ */
+ (void)comm_define(1, "SNMP read", NULL, "public");
+ (void)comm_define(2, "SNMP write", NULL, "public");
+ community = COMM_INITIALIZE;
+
+ trap_reqid = reqid_allocate(512, NULL);
+
+ if (config_file[0] == '\0')
+ snprintf(config_file, sizeof(config_file), PATH_CONFIG, prefix);
+
+ init_actvals();
+ if (read_config(config_file, NULL)) {
+ syslog(LOG_ERR, "error in config file");
+ exit(1);
+ }
+
+ if (evCreate(&evctx)) {
+ syslog(LOG_ERR, "evCreate: %m");
+ exit(1);
+ }
+ if (debug.evdebug > 0)
+ evSetDebug(evctx, 10, stderr);
+
+ TAILQ_FOREACH(p, &snmp_port_list, link)
+ (void)init_snmp(p);
+ TAILQ_FOREACH(pl, &local_port_list, link)
+ (void)init_local(pl);
+
+ init_sigs();
+
+ if (pid_file[0] == '\0')
+ snprintf(pid_file, sizeof(pid_file), PATH_PID, prefix);
+
+ if ((fp = fopen(pid_file, "w")) != NULL) {
+ fprintf(fp, "%u", getpid());
+ fclose(fp);
+ atexit(term);
+ }
+
+ start_tick = get_ticks();
+ this_tick = get_ticks();
+
+ if (or_register(&oid_snmpMIB, "The MIB module for SNMPv2 entities.",
+ NULL) == 0) {
+ syslog(LOG_ERR, "cannot register SNMPv2 MIB");
+ exit(1);
+ }
+ if (or_register(&oid_begemotSnmpd, "The MIB module for the Begemot SNMPd.",
+ NULL) == 0) {
+ syslog(LOG_ERR, "cannot register begemotSnmpd MIB");
+ exit(1);
+ }
+
+ snmp_send_trap(&oid_coldStart, NULL);
+
+ while ((m = TAILQ_FIRST(&modules_start)) != NULL) {
+ m->flags &= ~LM_ONSTARTLIST;
+ TAILQ_REMOVE(&modules_start, m, start);
+ lm_start(m);
+ }
+
+ for (;;) {
+ evEvent event;
+ struct lmodule *mod;
+
+ TAILQ_FOREACH(mod, &lmodules, link)
+ if (mod->config->idle != NULL)
+ (*mod->config->idle)();
+
+ if (evGetNext(evctx, &event, EV_WAIT) == 0) {
+ if (evDispatch(evctx, event))
+ syslog(LOG_ERR, "evDispatch: %m");
+ } else if (errno != EINTR) {
+ syslog(LOG_ERR, "evGetNext: %m");
+ exit(1);
+ }
+
+ if (work != 0) {
+ block_sigs();
+ if (work & WORK_DOINFO) {
+ if (evWaitFor(evctx, &work, info_func,
+ NULL, NULL) == -1) {
+ syslog(LOG_ERR, "evWaitFor: %m");
+ exit(1);
+ }
+ }
+ if (work & WORK_RECONFIG) {
+ if (evWaitFor(evctx, &work, config_func,
+ NULL, NULL) == -1) {
+ syslog(LOG_ERR, "evWaitFor: %m");
+ exit(1);
+ }
+ }
+ work = 0;
+ unblock_sigs();
+ if (evDo(evctx, &work) == -1) {
+ syslog(LOG_ERR, "evDo: %m");
+ exit(1);
+ }
+ }
+ }
+
+ return (0);
+}
+
+
+u_int32_t
+get_ticks()
+{
+ struct timeval tv;
+ u_int32_t ret;
+
+ if (gettimeofday(&tv, NULL))
+ abort();
+ ret = tv.tv_sec * 100 + tv.tv_usec / 10000;
+ return (ret);
+}
+/*
+ * Timer support
+ */
+static void
+tfunc(evContext ctx __unused, void *uap, struct timespec due __unused,
+ struct timespec inter __unused)
+{
+ struct timer *tp = uap;
+
+ LIST_REMOVE(tp, link);
+ tp->func(tp->udata);
+ free(tp);
+}
+
+/*
+ * Start a timer
+ */
+void *
+timer_start(u_int ticks, void (*func)(void *), void *udata, struct lmodule *mod)
+{
+ struct timer *tp;
+ struct timespec due;
+
+ if ((tp = malloc(sizeof(struct timer))) == NULL) {
+ syslog(LOG_CRIT, "out of memory for timer");
+ exit(1);
+ }
+ due = evAddTime(evNowTime(),
+ evConsTime(ticks / 100, (ticks % 100) * 10000));
+
+ tp->udata = udata;
+ tp->owner = mod;
+ tp->func = func;
+
+ LIST_INSERT_HEAD(&timer_list, tp, link);
+
+ if (evSetTimer(evctx, tfunc, tp, due, evConsTime(0, 0), &tp->id)
+ == -1) {
+ syslog(LOG_ERR, "cannot set timer: %m");
+ exit(1);
+ }
+ return (tp);
+}
+
+void
+timer_stop(void *p)
+{
+ struct timer *tp = p;
+
+ LIST_REMOVE(tp, link);
+ if (evClearTimer(evctx, tp->id) == -1) {
+ syslog(LOG_ERR, "cannot stop timer: %m");
+ exit(1);
+ }
+ free(p);
+}
+
+static void
+timer_flush(struct lmodule *mod)
+{
+ struct timer *t, *t1;
+
+ t = LIST_FIRST(&timer_list);
+ while (t != NULL) {
+ t1 = LIST_NEXT(t, link);
+ if (t->owner == mod)
+ timer_stop(t);
+ t = t1;
+ }
+}
+
+static void
+snmp_printf_func(const char *fmt, ...)
+{
+ va_list ap;
+ static char *pend = NULL;
+ char *ret, *new;
+
+ va_start(ap, fmt);
+ vasprintf(&ret, fmt, ap);
+ va_end(ap);
+
+ if (ret == NULL)
+ return;
+ if (pend != NULL) {
+ if ((new = realloc(pend, strlen(pend) + strlen(ret) + 1))
+ == NULL) {
+ free(ret);
+ return;
+ }
+ pend = new;
+ strcat(pend, ret);
+ free(ret);
+ } else
+ pend = ret;
+
+ while ((ret = strchr(pend, '\n')) != NULL) {
+ *ret = '\0';
+ syslog(LOG_DEBUG, "%s", pend);
+ if (strlen(ret + 1) == 0) {
+ free(pend);
+ pend = NULL;
+ break;
+ }
+ strcpy(pend, ret + 1);
+ }
+}
+
+static void
+snmp_error_func(const char *err, ...)
+{
+ char errbuf[1000];
+ va_list ap;
+
+ va_start(ap, err);
+ snprintf(errbuf, sizeof(errbuf), "SNMP: ");
+ vsnprintf(errbuf+strlen(errbuf), sizeof(errbuf)-strlen(errbuf),
+ err, ap);
+ va_end(ap);
+
+ syslog(LOG_ERR, "%s", errbuf);
+}
+
+static void
+snmp_debug_func(const char *err, ...)
+{
+ char errbuf[1000];
+ va_list ap;
+
+ va_start(ap, err);
+ snprintf(errbuf, sizeof(errbuf), "SNMP: ");
+ vsnprintf(errbuf+strlen(errbuf), sizeof(errbuf)-strlen(errbuf),
+ err, ap);
+ va_end(ap);
+
+ syslog(LOG_DEBUG, "%s", errbuf);
+}
+
+static void
+asn_error_func(const struct asn_buf *b, const char *err, ...)
+{
+ char errbuf[1000];
+ va_list ap;
+ u_int i;
+
+ va_start(ap, err);
+ snprintf(errbuf, sizeof(errbuf), "ASN.1: ");
+ vsnprintf(errbuf+strlen(errbuf), sizeof(errbuf)-strlen(errbuf),
+ err, ap);
+ va_end(ap);
+
+ if (b != NULL) {
+ snprintf(errbuf+strlen(errbuf), sizeof(errbuf)-strlen(errbuf),
+ " at");
+ for (i = 0; b->asn_len > i; i++)
+ snprintf(errbuf+strlen(errbuf),
+ sizeof(errbuf)-strlen(errbuf), " %02x", b->asn_cptr[i]);
+ }
+
+ syslog(LOG_ERR, "%s", errbuf);
+}
+
+/*
+ * Create a new community
+ */
+u_int
+comm_define(u_int priv, const char *descr, struct lmodule *owner,
+ const char *str)
+{
+ struct community *c, *p;
+ u_int ncomm;
+
+ /* generate an identifier */
+ do {
+ if ((ncomm = next_community_index++) == UINT_MAX)
+ next_community_index = 1;
+ TAILQ_FOREACH(c, &community_list, link)
+ if (c->value == ncomm)
+ break;
+ } while (c != NULL);
+
+ if ((c = malloc(sizeof(struct community))) == NULL) {
+ syslog(LOG_ERR, "comm_define: %m");
+ return (0);
+ }
+ c->owner = owner;
+ c->value = ncomm;
+ c->descr = descr;
+ c->string = NULL;
+ c->private = priv;
+
+ if (str != NULL) {
+ if((c->string = malloc(strlen(str)+1)) == NULL) {
+ free(c);
+ return (0);
+ }
+ strcpy(c->string, str);
+ }
+
+ /* make index */
+ if (c->owner == NULL) {
+ c->index.len = 1;
+ c->index.subs[0] = 0;
+ } else {
+ c->index = c->owner->index;
+ }
+ c->index.subs[c->index.len++] = c->private;
+
+ /*
+ * Insert ordered
+ */
+ TAILQ_FOREACH(p, &community_list, link) {
+ if (asn_compare_oid(&p->index, &c->index) > 0) {
+ TAILQ_INSERT_BEFORE(p, c, link);
+ break;
+ }
+ }
+ if (p == NULL)
+ TAILQ_INSERT_TAIL(&community_list, c, link);
+ return (c->value);
+}
+
+const char *
+comm_string(u_int ncomm)
+{
+ struct community *p;
+
+ TAILQ_FOREACH(p, &community_list, link)
+ if (p->value == ncomm)
+ return (p->string);
+ return (NULL);
+}
+
+/*
+ * Delete all communities allocated by a module
+ */
+static void
+comm_flush(struct lmodule *mod)
+{
+ struct community *p, *p1;
+
+ p = TAILQ_FIRST(&community_list);
+ while (p != NULL) {
+ p1 = TAILQ_NEXT(p, link);
+ if (p->owner == mod) {
+ free(p->string);
+ TAILQ_REMOVE(&community_list, p, link);
+ free(p);
+ }
+ p = p1;
+ }
+}
+
+/*
+ * Request ID handling.
+ *
+ * Allocate a new range of request ids. Use a first fit algorithm.
+ */
+u_int
+reqid_allocate(int size, struct lmodule *mod)
+{
+ u_int type;
+ struct idrange *r, *r1;
+
+ if (size <= 0 || size > INT32_MAX) {
+ syslog(LOG_CRIT, "%s: size out of range: %d", __func__, size);
+ return (0);
+ }
+ /* allocate a type id */
+ do {
+ if ((type = next_idrange++) == UINT_MAX)
+ next_idrange = 1;
+ TAILQ_FOREACH(r, &idrange_list, link)
+ if (r->type == type)
+ break;
+ } while(r != NULL);
+
+ /* find a range */
+ if (TAILQ_EMPTY(&idrange_list))
+ r = NULL;
+ else {
+ r = TAILQ_FIRST(&idrange_list);
+ if (r->base < size) {
+ while((r1 = TAILQ_NEXT(r, link)) != NULL) {
+ if (r1->base - (r->base + r->size) >= size)
+ break;
+ r = r1;
+ }
+ r = r1;
+ }
+ if (r == NULL) {
+ r1 = TAILQ_LAST(&idrange_list, idrange_list);
+ if (INT32_MAX - size + 1 < r1->base + r1->size) {
+ syslog(LOG_ERR, "out of id ranges (%u)", size);
+ return (0);
+ }
+ }
+ }
+
+ /* allocate structure */
+ if ((r1 = malloc(sizeof(struct idrange))) == NULL) {
+ syslog(LOG_ERR, "%s: %m", __FUNCTION__);
+ return (0);
+ }
+
+ r1->type = type;
+ r1->size = size;
+ r1->owner = mod;
+ if (TAILQ_EMPTY(&idrange_list) || r == TAILQ_FIRST(&idrange_list)) {
+ r1->base = 0;
+ TAILQ_INSERT_HEAD(&idrange_list, r1, link);
+ } else if (r == NULL) {
+ r = TAILQ_LAST(&idrange_list, idrange_list);
+ r1->base = r->base + r->size;
+ TAILQ_INSERT_TAIL(&idrange_list, r1, link);
+ } else {
+ r = TAILQ_PREV(r, idrange_list, link);
+ r1->base = r->base + r->size;
+ TAILQ_INSERT_AFTER(&idrange_list, r, r1, link);
+ }
+ r1->next = r1->base;
+
+ return (type);
+}
+
+int32_t
+reqid_next(u_int type)
+{
+ struct idrange *r;
+ int32_t id;
+
+ TAILQ_FOREACH(r, &idrange_list, link)
+ if (r->type == type)
+ break;
+ if (r == NULL) {
+ syslog(LOG_CRIT, "wrong idrange type");
+ abort();
+ }
+ if ((id = r->next++) == r->base + (r->size - 1))
+ r->next = r->base;
+ return (id);
+}
+
+int32_t
+reqid_base(u_int type)
+{
+ struct idrange *r;
+
+ TAILQ_FOREACH(r, &idrange_list, link)
+ if (r->type == type)
+ return (r->base);
+ syslog(LOG_CRIT, "wrong idrange type");
+ abort();
+}
+
+u_int
+reqid_type(int32_t reqid)
+{
+ struct idrange *r;
+
+ TAILQ_FOREACH(r, &idrange_list, link)
+ if (reqid >= r->base && reqid <= r->base + (r->size - 1))
+ return (r->type);
+ return (0);
+}
+
+int
+reqid_istype(int32_t reqid, u_int type)
+{
+ return (reqid_type(reqid) == type);
+}
+
+/*
+ * Delete all communities allocated by a module
+ */
+static void
+reqid_flush(struct lmodule *mod)
+{
+ struct idrange *p, *p1;
+
+ p = TAILQ_FIRST(&idrange_list);
+ while (p != NULL) {
+ p1 = TAILQ_NEXT(p, link);
+ if (p->owner == mod) {
+ TAILQ_REMOVE(&idrange_list, p, link);
+ free(p);
+ }
+ p = p1;
+ }
+}
+
+/*
+ * Merge the given tree for the given module into the main tree.
+ */
+static int
+compare_node(const void *v1, const void *v2)
+{
+ const struct snmp_node *n1 = v1;
+ const struct snmp_node *n2 = v2;
+
+ return (asn_compare_oid(&n1->oid, &n2->oid));
+}
+static int
+tree_merge(const struct snmp_node *ntree, u_int nsize, struct lmodule *mod)
+{
+ struct snmp_node *xtree;
+ u_int i;
+
+ xtree = realloc(tree, sizeof(*tree) * (tree_size + nsize));
+ if (xtree == NULL) {
+ syslog(LOG_ERR, "lm_load: %m");
+ return (-1);
+ }
+ tree = xtree;
+ memcpy(&tree[tree_size], ntree, sizeof(*tree) * nsize);
+
+ for (i = 0; i < nsize; i++)
+ tree[tree_size + i].data = mod;
+
+ tree_size += nsize;
+
+ qsort(tree, tree_size, sizeof(tree[0]), compare_node);
+
+ return (0);
+}
+
+/*
+ * Remove all nodes belonging to the loadable module
+ */
+static void
+tree_unmerge(struct lmodule *mod)
+{
+ u_int s, d;
+
+ for(s = d = 0; s < tree_size; s++)
+ if (tree[s].data != mod) {
+ if (s != d)
+ tree[d] = tree[s];
+ d++;
+ }
+ tree_size = d;
+}
+
+/*
+ * Loadable modules
+ */
+struct lmodule *
+lm_load(const char *path, const char *section)
+{
+ struct lmodule *m;
+ int err;
+ int i;
+ char *av[MAX_MOD_ARGS + 1];
+ int ac;
+ u_int u;
+
+ if ((m = malloc(sizeof(*m))) == NULL) {
+ syslog(LOG_ERR, "lm_load: %m");
+ return (NULL);
+ }
+ m->handle = NULL;
+ m->flags = 0;
+ strcpy(m->section, section);
+
+ if ((m->path = malloc(strlen(path) + 1)) == NULL) {
+ syslog(LOG_ERR, "lm_load: %m");
+ goto err;
+ }
+ strcpy(m->path, path);
+
+ /*
+ * Make index
+ */
+ m->index.subs[0] = strlen(section);
+ m->index.len = m->index.subs[0] + 1;
+ for (u = 0; u < m->index.subs[0]; u++)
+ m->index.subs[u + 1] = section[u];
+
+ /*
+ * Load the object file and locate the config structure
+ */
+ if ((m->handle = dlopen(m->path, RTLD_NOW|RTLD_GLOBAL)) == NULL) {
+ syslog(LOG_ERR, "lm_load: open %s", dlerror());
+ goto err;
+ }
+
+ if ((m->config = dlsym(m->handle, "config")) == NULL) {
+ syslog(LOG_ERR, "lm_load: no 'config' symbol %s", dlerror());
+ goto err;
+ }
+
+ /*
+ * Insert it into the right place
+ */
+ INSERT_OBJECT_OID(m, &lmodules);
+
+ /* preserve order */
+ if (community == COMM_INITIALIZE) {
+ m->flags |= LM_ONSTARTLIST;
+ TAILQ_INSERT_TAIL(&modules_start, m, start);
+ }
+
+ /*
+ * make the argument vector.
+ */
+ ac = 0;
+ for (i = 0; i < nprogargs; i++) {
+ if (strlen(progargs[i]) >= strlen(section) + 1 &&
+ strncmp(progargs[i], section, strlen(section)) == 0 &&
+ progargs[i][strlen(section)] == ':') {
+ if (ac == MAX_MOD_ARGS) {
+ syslog(LOG_WARNING, "too many arguments for "
+ "module '%s", section);
+ break;
+ }
+ av[ac++] = &progargs[i][strlen(section)+1];
+ }
+ }
+ av[ac] = NULL;
+
+ /*
+ * Run the initialisation function
+ */
+ if ((err = (*m->config->init)(m, ac, av)) != 0) {
+ syslog(LOG_ERR, "lm_load: init failed: %d", err);
+ TAILQ_REMOVE(&lmodules, m, link);
+ goto err;
+ }
+
+ return (m);
+
+ err:
+ if (m->handle)
+ dlclose(m->handle);
+ free(m->path);
+ free(m);
+ return (NULL);
+}
+
+/*
+ * Start a module
+ */
+void
+lm_start(struct lmodule *mod)
+{
+ const struct lmodule *m;
+
+ /*
+ * Merge tree. If this fails, unload the module.
+ */
+ if (tree_merge(mod->config->tree, mod->config->tree_size, mod)) {
+ lm_unload(mod);
+ return;
+ }
+
+ /*
+ * Read configuration
+ */
+ if (read_config(config_file, mod)) {
+ syslog(LOG_ERR, "error in config file");
+ lm_unload(mod);
+ return;
+ }
+ if (mod->config->start)
+ (*mod->config->start)();
+
+ mod->flags |= LM_STARTED;
+
+ /*
+ * Inform other modules
+ */
+ TAILQ_FOREACH(m, &lmodules, link)
+ if (m->config->loading)
+ (*m->config->loading)(mod, 1);
+}
+
+
+/*
+ * Unload a module.
+ */
+void
+lm_unload(struct lmodule *m)
+{
+ int err;
+ const struct lmodule *mod;
+
+ TAILQ_REMOVE(&lmodules, m, link);
+ if (m->flags & LM_ONSTARTLIST)
+ TAILQ_REMOVE(&modules_start, m, start);
+ tree_unmerge(m);
+
+ if ((m->flags & LM_STARTED) && m->config->fini &&
+ (err = (*m->config->fini)()) != 0)
+ syslog(LOG_WARNING, "lm_unload(%s): fini %d", m->section, err);
+
+ comm_flush(m);
+ reqid_flush(m);
+ timer_flush(m);
+ fd_flush(m);
+
+ dlclose(m->handle);
+ free(m->path);
+
+ /*
+ * Inform other modules
+ */
+ TAILQ_FOREACH(mod, &lmodules, link)
+ if (mod->config->loading)
+ (*mod->config->loading)(m, 0);
+
+ free(m);
+}
+
+/*
+ * Register an object resource and return the index (or 0 on failures)
+ */
+u_int
+or_register(const struct asn_oid *or, const char *descr, struct lmodule *mod)
+{
+ struct objres *objres, *or1;
+ u_int idx;
+
+ /* find a free index */
+ idx = 1;
+ for (objres = TAILQ_FIRST(&objres_list);
+ objres != NULL;
+ objres = TAILQ_NEXT(objres, link)) {
+ if ((or1 = TAILQ_NEXT(objres, link)) == NULL ||
+ or1->index > objres->index + 1) {
+ idx = objres->index + 1;
+ break;
+ }
+ }
+
+ if ((objres = malloc(sizeof(*objres))) == NULL)
+ return (0);
+
+ objres->index = idx;
+ objres->oid = *or;
+ strlcpy(objres->descr, descr, sizeof(objres->descr));
+ objres->uptime = get_ticks() - start_tick;
+ objres->module = mod;
+
+ INSERT_OBJECT_INT(objres, &objres_list);
+
+ systemg.or_last_change = objres->uptime;
+
+ return (idx);
+}
+
+void
+or_unregister(u_int idx)
+{
+ struct objres *objres;
+
+ TAILQ_FOREACH(objres, &objres_list, link)
+ if (objres->index == idx) {
+ TAILQ_REMOVE(&objres_list, objres, link);
+ free(objres);
+ return;
+ }
+}
diff --git a/contrib/bsnmp/snmpd/snmpd.config b/contrib/bsnmp/snmpd/snmpd.config
new file mode 100644
index 0000000..128b020
--- /dev/null
+++ b/contrib/bsnmp/snmpd/snmpd.config
@@ -0,0 +1,92 @@
+#
+# Copyright (c) 2001-2003
+# Fraunhofer Institute for Open Communication Systems (FhG Fokus).
+# All rights reserved.
+#
+# Author: Harti Brandt <harti@freebsd.org>
+#
+# Redistribution of this software and documentation and use in source and
+# binary forms, with or without modification, are permitted provided that
+# the following conditions are met:
+#
+# 1. Redistributions of source code or documentation must retain the above
+# copyright notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. Neither the name of the Institute nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY FRAUNHOFER FOKUS
+# AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+# FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+# FRAUNHOFER FOKUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+# EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# $Begemot: bsnmp/snmpd/snmpd.config,v 1.11 2002/12/11 15:54:08 hbb Exp $
+#
+# Example configuration file.
+#
+
+#
+# Set some common variables
+#
+host := foo.bar.com
+location := "Room 200"
+contact := "sysmeister@bar.com"
+system := 1 # FreeBSD
+traphost := noc.bar.com
+trapport := 162
+
+read := "public"
+write := "geheim"
+trap := "mytrap"
+
+#
+# Configuration
+#
+%snmpd
+begemotSnmpdDebugDumpPdus = 2
+begemotSnmpdDebugSyslogPri = 7
+
+begemotSnmpdCommunityString.0.1 = $(read)
+begemotSnmpdCommunityString.0.2 = $(write)
+begemotSnmpdCommunityDisable = 1
+
+# open standard SNMP ports
+begemotSnmpdPortStatus.[$(host)].161 = 1
+begemotSnmpdPortStatus.127.0.0.1.161 = 1
+
+# open a unix domain socket
+begemotSnmpdLocalPortStatus."/var/run/snmpd.sock" = 1
+
+# send traps to the traphost
+begemotTrapSinkStatus[$(traphost)].$(trapport) = 4
+begemotTrapSinkVersion[$(traphost)].$(trapport) = 2
+begemotTrapSinkComm[$(traphost)].$(trapport) = $(trap)
+
+sysContact = $(contact)
+sysLocation = $(location)
+sysObjectId = 1.3.6.1.4.1.12325.1.1.2.1.$(system)
+
+snmpEnableAuthenTraps = 2
+
+#
+# Load MIB-2 module
+#
+begemotSnmpdModulePath."mibII" = "/usr/local/lib/snmp_mibII.so"
+
+#
+# Netgraph module
+#
+begemotSnmpdModulePath."netgraph" = "/usr/local/lib/snmp_netgraph.so"
+
+%netgraph
+begemotNgControlNodeName = "snmpd"
diff --git a/contrib/bsnmp/snmpd/snmpd.h b/contrib/bsnmp/snmpd/snmpd.h
new file mode 100644
index 0000000..5c8e78f
--- /dev/null
+++ b/contrib/bsnmp/snmpd/snmpd.h
@@ -0,0 +1,277 @@
+/*
+ * Copyright (c) 2001-2003
+ * Fraunhofer Institute for Open Communication Systems (FhG Fokus).
+ * All rights reserved.
+ *
+ * Author: Harti Brandt <harti@freebsd.org>
+ *
+ * Redistribution of this software and documentation and use in source and
+ * binary forms, with or without modification, are permitted provided that
+ * the following conditions are met:
+ *
+ * 1. Redistributions of source code or documentation must retain the above
+ * copyright notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY FRAUNHOFER FOKUS
+ * AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * FRAUNHOFER FOKUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Begemot: bsnmp/snmpd/snmpd.h,v 1.17 2003/01/28 13:44:35 hbb Exp $
+ *
+ * Private SNMPd data and functions.
+ */
+#include <sys/queue.h>
+#include <isc/eventlib.h>
+
+#define PATH_SYSCONFIG "/etc:/usr/etc:/usr/local/etc"
+
+/*************************************************************
+ *
+ * Communities
+ */
+struct community {
+ struct lmodule *owner; /* who created the community */
+ u_int private;/* private name for the module */
+ u_int value; /* value of this community */
+ u_char * string; /* the community string */
+ const u_char * descr; /* description */
+ TAILQ_ENTRY(community) link;
+
+ struct asn_oid index;
+};
+/* list of all known communities */
+extern TAILQ_HEAD(community_list, community) community_list;
+
+/*************************************************************
+ *
+ * Request IDs.
+ */
+struct idrange {
+ u_int type; /* type id */
+ int32_t base; /* base of this range */
+ int32_t size; /* size of this range */
+ int32_t next; /* generator */
+ struct lmodule *owner; /* owner module */
+ TAILQ_ENTRY(idrange) link;
+};
+
+/* list of all known ranges */
+extern TAILQ_HEAD(idrange_list, idrange) idrange_list;
+
+/* identifier generator */
+extern u_int next_idrange;
+
+/* request id generator for traps */
+extern u_int trap_reqid;
+
+/*************************************************************
+ *
+ * Timers
+ */
+struct timer {
+ void (*func)(void *);/* user function */
+ void *udata; /* user data */
+ evTimerID id; /* timer id */
+ struct lmodule *owner; /* owner of the timer */
+ LIST_ENTRY(timer) link;
+};
+
+/* list of all current timers */
+extern LIST_HEAD(timer_list, timer) timer_list;
+
+
+/*************************************************************
+ *
+ * File descriptors
+ */
+struct fdesc {
+ int fd; /* the file descriptor */
+ void (*func)(int, void *);/* user function */
+ void *udata; /* user data */
+ evFileID id; /* file id */
+ struct lmodule *owner; /* owner module of the file */
+ LIST_ENTRY(fdesc) link;
+};
+
+/* list of all current selected files */
+extern LIST_HEAD(fdesc_list, fdesc) fdesc_list;
+
+/*************************************************************
+ *
+ * Loadable modules
+ */
+# define LM_SECTION_MAX 14
+struct lmodule {
+ char section[LM_SECTION_MAX + 1]; /* and index */
+ char *path;
+ u_int flags;
+ void *handle;
+ const struct snmp_module *config;
+
+ TAILQ_ENTRY(lmodule) link;
+ TAILQ_ENTRY(lmodule) start;
+
+ struct asn_oid index;
+};
+#define LM_STARTED 0x0001
+#define LM_ONSTARTLIST 0x0002
+
+extern TAILQ_HEAD(lmodules, lmodule) lmodules;
+
+struct lmodule *lm_load(const char *, const char *);
+void lm_unload(struct lmodule *);
+void lm_start(struct lmodule *);
+
+/*************************************************************
+ *
+ * SNMP ports
+ */
+struct snmp_port {
+ u_int8_t addr[4];/* host byteorder */
+ u_int16_t port; /* host byteorder */
+
+ int sock; /* the socket */
+ void * id; /* evSelect handle */
+
+ struct sockaddr_in ret; /* the return address */
+ socklen_t retlen; /* length of that address */
+
+ TAILQ_ENTRY(snmp_port) link;
+
+ struct asn_oid index;
+};
+TAILQ_HEAD(snmp_port_list, snmp_port);
+extern struct snmp_port_list snmp_port_list;
+
+void close_snmp_port(struct snmp_port *);
+int open_snmp_port(u_int8_t *, u_int32_t, struct snmp_port **);
+
+struct local_port {
+ char *name; /* unix path name */
+ int sock; /* the socket */
+ void *id; /* evSelect handle */
+
+ struct sockaddr_un ret; /* the return address */
+ socklen_t retlen; /* length of that address */
+
+ TAILQ_ENTRY(local_port) link;
+
+ struct asn_oid index;
+};
+TAILQ_HEAD(local_port_list, local_port);
+extern struct local_port_list local_port_list;
+
+void close_local_port(struct local_port *);
+int open_local_port(u_char *, size_t, struct local_port **);
+
+/*************************************************************
+ *
+ * SNMPd scalar configuration.
+ */
+struct snmpd {
+ /* transmit buffer size */
+ u_int32_t txbuf;
+
+ /* receive buffer size */
+ u_int32_t rxbuf;
+
+ /* disable community table */
+ int comm_dis;
+
+ /* authentication traps */
+ int auth_traps;
+
+ /* source address for V1 traps */
+ u_char trap1addr[4];
+};
+extern struct snmpd snmpd;
+
+/*
+ * The debug group
+ */
+struct debug {
+ u_int dump_pdus;
+ u_int logpri;
+ u_int evdebug;
+};
+extern struct debug debug;
+
+
+/*
+ * SNMPd statistics table
+ */
+struct snmpd_stats {
+ u_int32_t inPkts; /* total packets received */
+ u_int32_t inBadVersions; /* unknown version number */
+ u_int32_t inASNParseErrs; /* fatal parse errors */
+ u_int32_t inBadCommunityNames;
+ u_int32_t inBadCommunityUses;
+ u_int32_t proxyDrops; /* dropped by proxy function */
+ u_int32_t silentDrops;
+
+ u_int32_t inBadPduTypes;
+ u_int32_t inTooLong;
+ u_int32_t noTxbuf;
+ u_int32_t noRxbuf;
+};
+extern struct snmpd_stats snmpd_stats;
+
+/*
+ * OR Table
+ */
+struct objres {
+ TAILQ_ENTRY(objres) link;
+ u_int index;
+ struct asn_oid oid; /* the resource OID */
+ char descr[256];
+ u_int32_t uptime;
+ struct lmodule *module;
+};
+TAILQ_HEAD(objres_list, objres);
+extern struct objres_list objres_list;
+
+/*
+ * Trap Sink Table
+ */
+struct trapsink {
+ TAILQ_ENTRY(trapsink) link;
+ struct asn_oid index;
+ u_int status;
+ int socket;
+ u_char comm[SNMP_COMMUNITY_MAXLEN];
+ int version;
+};
+enum {
+ TRAPSINK_ACTIVE = 1,
+ TRAPSINK_NOT_IN_SERVICE = 2,
+ TRAPSINK_NOT_READY = 3,
+ TRAPSINK_DESTROY = 6,
+
+ TRAPSINK_V1 = 1,
+ TRAPSINK_V2 = 2,
+};
+TAILQ_HEAD(trapsink_list, trapsink);
+extern struct trapsink_list trapsink_list;
+
+extern const char *syspath;
+
+/* snmpSerialNo */
+extern int32_t snmp_serial_no;
+
+int init_actvals(void);
+int read_config(const char *, struct lmodule *);
+int define_macro(const char *name, const char *value);
diff --git a/contrib/bsnmp/snmpd/snmpd.sh b/contrib/bsnmp/snmpd/snmpd.sh
new file mode 100755
index 0000000..86ec962
--- /dev/null
+++ b/contrib/bsnmp/snmpd/snmpd.sh
@@ -0,0 +1,89 @@
+#!/bin/sh
+#
+# Copyright (c) 2001-2003
+# Fraunhofer Institute for Open Communication Systems (FhG Fokus).
+# All rights reserved.
+#
+# Author: Harti Brandt <harti@freebsd.org>
+#
+# Redistribution of this software and documentation and use in source and
+# binary forms, with or without modification, are permitted provided that
+# the following conditions are met:
+#
+# 1. Redistributions of source code or documentation must retain the above
+# copyright notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. Neither the name of the Institute nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY FRAUNHOFER FOKUS
+# AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+# FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+# FRAUNHOFER FOKUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+# EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# $Begemot: bsnmp/snmpd/snmpd.sh,v 1.1 2002/12/04 11:15:23 hbb Exp $
+#
+# SNMPd startup script
+#
+SNMPD=/usr/local/bin/bsnmpd
+PID=/var/run/snmpd.pid
+CONF=/etc/snmpd.conf
+
+case "$1" in
+
+start)
+ if [ -r ${PID} ] ; then
+ if kill -0 `cat ${PID}` ; then
+ echo "snmpd already running -- pid `cat ${PID}`" >/dev/stderr
+ exit 1
+ fi
+ rm -f ${PID}
+ fi
+ if ${SNMPD} -c ${CONF} -p ${PID} ; then
+ echo "snmpd started"
+ fi
+ ;;
+
+stop)
+ if [ -r ${PID} ] ; then
+ if kill -0 `cat ${PID}` ; then
+ if kill -15 `cat ${PID}` ; then
+ echo "snmpd stopped"
+ exit 0
+ fi
+ echo "cannot kill snmpd" >/dev/stderr
+ exit 1
+ fi
+ echo "stale pid file -- removing" >/dev/stderr
+ rm -f ${PID}
+ exit 1
+ fi
+ echo "snmpd not running" >/dev/stderr
+ ;;
+
+status)
+ if [ ! -r ${PID} ] ; then
+ echo "snmpd not running"
+ elif kill -0 `cat ${PID}` ; then
+ echo "snmpd pid `cat ${PID}`"
+ else
+ echo "stale pid file -- pid `cat ${PID}`"
+ fi
+ ;;
+
+*)
+ echo "usage: `basename $0` {start|stop|status}"
+ exit 1
+esac
+
+exit 0
diff --git a/contrib/bsnmp/snmpd/snmpmod.3 b/contrib/bsnmp/snmpd/snmpmod.3
new file mode 100644
index 0000000..143d12d
--- /dev/null
+++ b/contrib/bsnmp/snmpd/snmpmod.3
@@ -0,0 +1,861 @@
+.\"
+.\" Copyright (c) 2001-2003
+.\" Fraunhofer Institute for Open Communication Systems (FhG Fokus).
+.\" All rights reserved.
+.\"
+.\" Author: Harti Brandt <harti@freebsd.org>
+.\"
+.\" Redistribution of this software and documentation and use in source and
+.\" binary forms, with or without modification, are permitted provided that
+.\" the following conditions are met:
+.\"
+.\" 1. Redistributions of source code or documentation must retain the above
+.\" copyright notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the Institute nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY FRAUNHOFER FOKUS
+.\" AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+.\" INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+.\" FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+.\" FRAUNHOFER FOKUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+.\" LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+.\" OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+.\" LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+.\" NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+.\" EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $Begemot: bsnmp/snmpd/snmpmod.3,v 1.3 2003/01/28 13:44:35 hbb Exp $
+.\"
+.Dd August 16, 2002
+.Dt snmpmod 3
+.Os
+.Sh NAME
+.Nm INSERT_OBJECT_OID_LINK_INDEX ,
+.Nm INSERT_OBJECT_INT_LINK_INDEX ,
+.Nm FIND_OBJECT_OID_LINK_INDEX ,
+.Nm NEXT_OBJECT_OID_LINK_INDEX ,
+.Nm FIND_OBJECT_INT_LINK_INDEX ,
+.Nm NEXT_OBJECT_INT_LINK_INDEX ,
+.Nm INSERT_OBJECT_OID_LINK ,
+.Nm INSERT_OBJECT_INT_LINK ,
+.Nm FIND_OBJECT_OID_LINK ,
+.Nm NEXT_OBJECT_OID_LINK ,
+.Nm FIND_OBJECT_INT_LINK ,
+.Nm NEXT_OBJECT_INT_LINK ,
+.Nm INSERT_OBJECT_OID ,
+.Nm INSERT_OBJECT_INT ,
+.Nm FIND_OBJECT_OID ,
+.Nm FIND_OBJECT_INT ,
+.Nm NEXT_OBJECT_OID ,
+.Nm NEXT_OBJECT_INT ,
+.Nm this_tick ,
+.Nm start_tick ,
+.Nm get_ticks ,
+.Nm systemg ,
+.Nm comm_define ,
+.Nm community ,
+.Nm oid_zeroDotZero ,
+.Nm reqid_allocate ,
+.Nm reqid_next ,
+.Nm reqid_base ,
+.Nm reqid_istype ,
+.Nm reqid_type ,
+.Nm timer_start ,
+.Nm timer_stop ,
+.Nm fd_select ,
+.Nm fd_deselect ,
+.Nm fd_suspend ,
+.Nm fd_resume ,
+.Nm or_register ,
+.Nm or_unregister ,
+.Nm buf_alloc ,
+.Nm buf_size ,
+.Nm snmp_input_start ,
+.Nm snmp_input_finish ,
+.Nm snmp_output ,
+.Nm snmp_send_port ,
+.Nm snmp_send_trap ,
+.Nm string_save ,
+.Nm string_commit ,
+.Nm string_rollback ,
+.Nm string_get ,
+.Nm string_free ,
+.Nm ip_save ,
+.Nm ip_rollback ,
+.Nm ip_commit ,
+.Nm ip_get ,
+.Nm oid_save ,
+.Nm oid_rollback ,
+.Nm oid_commit ,
+.Nm oid_get ,
+.Nm index_decode ,
+.Nm index_compare ,
+.Nm index_compare_off ,
+.Nm index_append ,
+.Nm index_append_off
+.Nd "SNMP daemon loadable module interface"
+.Sh LIBRARY
+Begemot SNMP library
+.Pq libbsnmp, -lbsnmp
+.Sh SYNOPSIS
+.In bsnmp/snmpmod.h
+.Fn INSERT_OBJECT_OID_LINK_INDEX "PTR" "LIST" "LINK" "INDEX"
+.Fn INSERT_OBJECT_INT_LINK_INDEX "PTR" "LIST" "LINK" "INDEX"
+.Fn FIND_OBJECT_OID_LINK_INDEX "LIST" "OID" "SUB" "LINK" "INDEX"
+.Fn FIND_OBJECT_INT_LINK_INDEX "LIST" "OID" "SUB" "LINK" "INDEX"
+.Fn NEXT_OBJECT_OID_LINK_INDEX "LIST" "OID" "SUB" "LINK" "INDEX"
+.Fn NEXT_OBJECT_INT_LINK_INDEX "LIST" "OID" "SUB" "LINK" "INDEX"
+.Fn INSERT_OBJECT_OID_LINK "PTR" "LIST" "LINK"
+.Fn INSERT_OBJECT_INT_LINK "PTR" "LIST" "LINK"
+.Fn FIND_OBJECT_OID_LINK "LIST" "OID" "SUB" "LINK"
+.Fn FIND_OBJECT_INT_LINK "LIST" "OID" "SUB" "LINK"
+.Fn NEXT_OBJECT_OID_LINK "LIST" "OID" "SUB" "LINK"
+.Fn NEXT_OBJECT_INT_LINK "LIST" "OID" "SUB" "LINK"
+.Fn INSERT_OBJECT_OID "PTR" "LIST"
+.Fn INSERT_OBJECT_INT "PTR" "LIST"
+.Fn FIND_OBJECT_OID "LIST" "OID" "SUB"
+.Fn FIND_OBJECT_INT "LIST" "OID" "SUB"
+.Fn NEXT_OBJECT_OID "LIST" "OID" "SUB"
+.Fn NEXT_OBJECT_INT "LIST" "OID" "SUB"
+.Vt extern u_int32_t this_tick ;
+.Vt extern u_int32_t start_tick ;
+.Ft u_int32_t
+.Fn get_ticks "void"
+.Vt extern struct systemg systemg ;
+.Ft u_int
+.Fn comm_define "u_int priv" "const char *descr" "struct lmodule *mod" "const char *str"
+.Ft const char *
+.Fn comm_string "u_int comm"
+.Vt extern u_int community ;
+.Vt extern const struct asn_oid oid_zeroDotZero ;
+.Ft u_int
+.Fn reqid_allocate "int size" "struct lmodule *mod"
+.Ft int32_t
+.Fn reqid_next "u_int type"
+.Ft int32_t
+.Fn reqid_base "u_int type"
+.Ft int
+.Fn reqid_istype "int32_t reqid" "u_int type"
+.Ft u_int
+.Fn reqid_type "int32_t reqid"
+.Ft void *
+.Fn timer_start "u_int ticks" "void (*func)(void *)" "void *uarg" "struct lmodule *mod"
+.Ft void
+.Fn timer_stop "void *timer_id"
+.Ft void *
+.Fn fd_select "int fd" "void (*func)(int, void *)" "void *uarg" "struct lmodule *mod"
+.Ft void
+.Fn fd_deselect "void *fd_id"
+.Ft void
+.Fn fd_suspend "void *fd_id"
+.Ft int
+.Fn fd_resume "void *fd_id"
+.Ft u_int
+.Fn or_register "const struct asn_oid *oid" "const char *descr" "struct lmodule *mod"
+.Ft void
+.Fn or_unregister "u_int or_id"
+.Ft void *
+.Fn buf_alloc "int tx"
+.Ft size_t
+.Fn buf_size "int tx"
+.Ft enum snmpd_input_err
+.Fn snmp_input_start "const u_char *buf" "size_t len" "const char *source" \
+ "struct snmp_pdu *pdu" "int32_t *ip"
+.Ft enum snmpd_input_err
+.Fn snmp_input_finish "struct snmp_pdu *pdu" "const u_char *rcvbuf" \
+ "size_t rcvlen" "u_char *sndbuf" "size_t *sndlen" "const char *source" \
+ "enum snmpd_input_err ierr" "int32_t ip" "void *data"
+.Ft void
+.Fn snmp_output "struct snmp_pdu *pdu" "u_char *sndbuf" "size_t *sndlen" \
+ "const char *dest"
+.Ft void
+.Fn snmp_send_port "const struct asn_oid *port" "struct snmp_pdu *pdu" \
+ "const struct sockaddr *addr" "socklen_t addrlen"
+.Ft void
+.Fn snmp_send_trap "const struct asn_oid *oid" "..."
+.Ft int
+.Fn string_save "struct snmp_value *val" "struct snmp_context *ctx" "ssize_t req_size" "u_char **strp"
+.Ft void
+.Fn string_commit "struct snmp_context *ctx"
+.Ft void
+.Fn string_rollback "struct snmp_context *ctx" "u_char **strp"
+.Ft int
+.Fn string_get "struct snmp_value *val" "const u_char *str" "ssize_t len"
+.Ft void
+.Fn string_free "struct snmp_context *ctx"
+.Ft int
+.Fn ip_save "struct snmp_value *val" "struct snmp_context *ctx" "u_char *ipa"
+.Ft void
+.Fn ip_rollback "struct snmp_context *ctx" "u_char *ipa"
+.Ft void
+.Fn ip_commit "struct snmp_context *ctx"
+.Ft int
+.Fn ip_get "struct snmp_value *val" "u_char *ipa"
+.Ft int
+.Fn oid_save "struct snmp_value *val" "struct snmp_context *ctx" "struct asn_oid *oid"
+.Ft void
+.Fn oid_rollback "struct snmp_context *ctx" "struct asn_oid *oid"
+.Ft void
+.Fn oid_commit "struct snmp_context *ctx"
+.Ft int
+.Fn oid_get "struct snmp_value *val" "const struct asn_oid *oid"
+.Ft int
+.Fn index_decode "const struct asn_oid *oid" "u_int sub" "u_int code" "..."
+.Ft int
+.Fn index_compare "const struct asn_oid *oid1" "u_int sub" "const struct asn_oid *oid2"
+.Ft int
+.Fn index_compare_off "const struct asn_oid *oid1" "u_int sub" "const struct asn_oid *oid2" "u_int off"
+.Ft void
+.Fn index_append "struct asn_oid *dst" "u_int sub" "const struct asn_oid *src"
+.Ft void
+.Fn index_append_off "struct asn_oid *dst" "u_int sub" "const struct asn_oid *src" "u_int off"
+.Sh DESCRIPTION
+The
+.Xr snmpd 1
+SNMP daemon implements a minimal MIB which consists of the system group, part
+of the SNMP MIB, a private configuration MIB, a trap destination table, a
+UDP port table, a community table, a module table, a statistics group and
+a debugging group. All other MIBs are support through loadable modules.
+This allows
+.Xr snmpd 1
+to use for task, that are not the classical SNMP task.
+.Ss MODULE LOADING AND UNLOADING
+Modules are loaded by writing to the module table. This table is indexed by
+a string, that identfies the module to the daemon. This identifier is used
+to select the correct configuration section from the configuration files and
+to identify resources allocated to this module. A row in the module table is
+created by writing a string of non-zero length to the
+.Va begemotSnmpdModulePath
+column. This string must be the complete path to the file containing the module.
+A module can be unloaded by writing a zero length string to the path column
+of an existing row.
+.Pp
+Modules may depend on each other an hence must be loaded in the correct order.
+The dependencies are listed in the corresponding manual pages.
+.Pp
+Upon loading a module the SNMP daemon expects the module file to a export
+a global symbol
+.Va config .
+This symbol should be a variable of type
+.Vt struct snmp_module :
+.Bd -literal -offset indent
+typedef enum snmpd_proxy_err (*proxy_err_f)(struct snmp_pdu *,
+ const struct asn_oid *, const struct sockaddr *, socklen_t,
+ enum snmpd_input_err, int32_t);
+
+
+struct snmp_module {
+ const char *comment;
+ int (*init)(struct lmodule *, int argc, char *argv[]);
+ int (*fini)(void);
+ void (*idle)(void);
+ void (*dump)(void);
+ void (*config)(void);
+ void (*start)(void);
+ proxy_err_f proxy;
+ const struct snmp_node *tree;
+ u_int tree_size;
+ void (*loading)(const struct lmodule *, int);
+};
+.Ed
+.Pp
+This structure must be statically initialized and its fields have the
+following functions:
+.Bl -tag -width ".It Va tree_size"
+.It Va comment
+This is a string that will be visible in the module table. It should give
+some hint about the function of this module.
+.It Va init
+This function is called upon loading the module. The module pointer should
+be stored by the module because it is needed in other calls and the
+argument vector will contain the arguments to this module from the daemons
+command line. This function should return 0 if everything is ok or an
+UNIX error code (see
+.Xr errno 3 ).
+Once the function returns 0, the
+.Va fini
+function is called when the module is unloaded.
+.It Va fini
+The module is unloaded. This gives the module a chance to free resources that
+are not automatically freed. Be sure to free all memory, because daemons tend
+to run very long. This function pointer may be
+.Li NULL
+if it is not needed.
+.It Va idle
+If this function pointer is not
+.Li NULL ,
+the function pointed to by it is called whenever the daemon is going
+to wait for an event. Try to avoid using this feature.
+.It Va dump
+Whenever the daemon receives a
+.Li SIGUSR1
+it dumps it internal state via
+.Xr syslog 3 .
+If the
+.Va dump
+field is not
+.Li NULL
+it is called by the daemon to dump the state of the module.
+.It Va config
+Whenever the daemon receives a
+.Li SIGHUP
+signal it re-reads its configuration file.
+If the
+.Va config
+field is not
+.Li NULL
+it is called after reading the configuration file to give the module a chance
+to adapt to the new configuration.
+.It Va start
+If not
+.Li NULL
+this function is called after successful loading and initializing the module
+to start its actual operation.
+.It Va proxy
+If the daemon receives a PDU and that PDU has a community string who's
+community was registered by this module and
+.Va proxy
+is not
+.Li NULL
+than this function is called to handle the PDU.
+.It Va tree
+This is a pointer to the node array for the MIB tree implemented by this module.
+.It Va tree_size
+This is the number of nodes in
+.Va tree .
+.It Va loading
+If this pointer is not
+.Li NULL
+it is called whenever another module was loaded or unloaded. It gets a
+pointer to that module and a flag that is 0 for unloading and 1 for loading.
+.El
+.Pp
+When everything is ok, the daemon merges the module's MIB tree into its current
+global tree, calls the modules
+.Fn init
+function. If this function returns an error, the modules MIB tree is removed from
+the global one and the module is unloaded. If initialisation is successful,
+the modules
+.Fn start
+function is called.
+After it returns the
+.Fn loaded
+functions of all modules (including the loaded one) are called.
+.Pp
+When the module is unloaded, its MIB tree is removed from the global one,
+the communities, request id ranges, running timers and selected file
+descriptors are released, the
+.Fn fini
+function is called, the module file is unloaded and the
+.Fn loaded
+functions of all other modules are called.
+.Ss IMPLEMENTING TABLES
+There are a number of macros designed to help implementing SNMP tables.
+A problem while implementing a table is the support for the GETNEXT operator.
+The GETNEXT operation has to find out whether, given an arbitrary OID, the
+lessest table row, that has an OID higher than the given OID. The easiest way
+to do this is to keep the table as an ordered list of structures each one
+of which contains an OID that is the index of the table row. This allows easy
+removal, insertion and search.
+.Pp
+The helper macros assume, that the table is organized as a TAILQ (see
+.Xr queue 3
+and each structure contains a
+.Vt struct asn_oid
+that is used as index.
+For simple tables with only a integer or unsigned index, an alternate form
+of the macros is available, that presume the existence of an integer or
+unsigned field as index field.
+.Pp
+The macros have name of the form
+.Bd -literal -offset indent
+{INSERT,FIND,NEXT}_OBJECT_{OID,INT}[_LINK[_INDEX]]
+.Ed
+.Pp
+The
+.Fn INSERT_*
+macros are used in the SET operation to insert a new table row into the table.
+The
+.Fn FIND_*
+macros are used in the GET operation to find a specific row in the table.
+The
+.Fn NEXT_*
+macros are used in the GETNEXT operation to find the next row in the table.
+The last two macros return a pointer to the row structure if a row is found,
+.Li NULL
+otherwise.
+The macros
+.Fn *_OBJECT_OID_*
+assume the existence of a
+.Vt struct asn_oid
+that is used as index, the macros
+.Fn *_OBJECT_INT_*
+assume the existance of an unsigned integer field that is used as index.
+.Pp
+The macros
+.Fn *_INDEX
+allow the explicit naming of the index field in the parameter
+.Fa INDEX ,
+whereas the other macros assume that this field is named
+.Va index .
+The macros
+.Fn *_LINK_*
+allow the explicit naming of the link field of the tail queues, the others
+assume that the link field is named
+.Va link .
+Explicitely naming the link field may be necessary if the same structures
+are held in two or more different tables.
+.Pp
+The arguments to the macros are as follows:
+.Bl -tag -width "INDEX"
+.It Fa PTR
+A pointer to the new structure to be inserted into the table.
+.It Fa LIST
+A pointer to the tail queue head.
+.It Fa LINK
+The name of the link field in the row structure.
+.It Fa INDEX
+The name of the index field in the row structure.
+.It Fa OID
+Must point to the
+.Va var
+field of the
+.Fa value
+argument to the node operation callback. This is the OID to search for.
+.It Fa SUB
+This is the index of the start of the table index in the OID pointed to
+by
+.Fa OID .
+This is usually the same as the
+.Fa sub
+argument to the node operation callback.
+.El
+.Ss DAEMON TIMESTAMPS
+The variable
+.Va this_tick
+contains the tick (there are 100 SNMP ticks in a second) when
+the current PDU processing was started.
+The variable
+.Va start_tick
+contains the tick when the daemon was started.
+The function
+.Fn get_ticks
+returns the current tick. The number of ticks since the daemon was started
+is
+.Bd -literal -offset indent
+get_ticks() - start_tick
+.Ed
+.Ss THE SYSTEM GROUP
+The scalar fields of the system group are held in the global variable
+.Va systemg :
+.Bd -literal -offset indent
+struct systemg {
+ u_char *descr;
+ struct asn_oid object_id;
+ u_char *contact;
+ u_char *name;
+ u_char *location;
+ u_int32_t services;
+ u_int32_t or_last_change;
+};
+.Ed
+.Ss COMMUNITIES
+The SNMP daemon implements a community table. On recipte of a request message
+the community string in that message is compared to each of the community
+strings in that table, if a match is found, the global variable
+.Va community
+is set to the community identifier for that community. Community identifiers
+are unsigned integers. For the three standard communities there are three
+constants defined:
+.Bd -literal -offset indent
+#define COMM_INITIALIZE 0
+#define COMM_READ 1
+#define COMM_WRITE 2
+.Ed
+.Pp
+.Va community
+is set to
+.Li COMM_INITIALIZE
+while the assignments in the configuration file are processed. To
+.Li COMM_READ
+or
+.Li COMM_WRITE
+when the community strings for the read-write or read-only community are found
+in the incoming PDU.
+.Pp
+Modules can define additional communities. This may be necessary to provide
+transport proxying (a PDU received on one communication link is proxied to
+another link) or to implement non-UDP access points to SNMP. A new
+community is defined with the function
+.Fn comm_define .
+It takes the following parameters:
+.Bl -tag -width ".It Fa descr"
+.It Fa priv
+This is an integer identifying the community to the module. Each module has
+its own namespace with regard to this parameter. The community table is
+indexed with the module name and this identifier.
+.It Fa descr
+This is a string providing a human readable description of the community.
+It is visible in the community table.
+.It Fa mod
+This is the module defining the community.
+.It Fa str
+This is the initial community string.
+.El
+.Pp
+The function returns a globally unique community identifier. If a PDU is
+received who's community string matches, this identifier is set into the global
+.Va community .
+.Pp
+The function
+.Fn comm_string
+returns the current community string for the given community.
+.Pp
+All communities defined by a module are automatically released when the module
+is unloaded.
+.Ss WELL KNOWN OIDS
+The global variable
+.Va oid_zeroDotZero
+contains the OID 0.0.
+.Ss REQUEST ID RANGES
+For modules that implement SNMP client functions besides SNMP agent functions
+it may be necessary to identify SNMP requests by their identifier to allow
+easier routing of responses to the correct sub-system. Request id ranges
+provide a way to aquire globally non-overlapping sub-ranges of the entire
+31-bit id range.
+.Pp
+A request id range is allocated with
+.Fn reqid_allocate .
+The arguments are: the size of the range and the module allocating the range.
+For example, the call
+.Bd -literal -offset indent
+id = reqid_allocate(1000, module);
+.Ed
+.Pp
+allocates a range of 1000 request ids. The function returns the request
+id range identifier or 0 if there is not enough identifier space.
+The function
+.Fn reqid_base
+returns the lowest request id in the given range.
+.Pp
+Request id are allocated starting at the lowest one linear throughout the range.
+If the client application may have a lot of outstanding request the range
+must be large enough so that an id is not reused until it is really expired.
+.Fn reqid_next
+returns the sequentially next id in the range.
+.Pp
+The function
+.Fn reqid_istype
+checks whether the request id
+.Fa reqid
+is withing the range identified by
+.Fa type .
+The function
+.Fn reqid_type
+returns the range identifier for the given
+.Fa reqid
+or 0 if the request id is in none of the ranges.
+.Ss TIMERS
+The SNMP daemon supports an arbitrary number of timers with SNMP tick granularity.
+The function
+.Fn timer_start
+arranges for the callback
+.Fa func
+to be called with the argument
+.Fa uarg
+after
+.Fa ticks
+SNMP ticks have expired.
+.Fa mod
+is the module that starts the timer. Timers are one-shot, they are not
+restarted. The function returns a timer identifier that can be used to
+stop the timer via
+.Fn timer_stop .
+If a module is unloaded all timers started by the module that have not expired
+yet are stopped.
+.Ss FILE DESCRIPTOR SUPPORT
+A module may need to get input from socket file descriptors without blocking
+the daemon (for example to implement alternative SNMP transports).
+.Pp
+The function
+.Fn fd_select
+causes the callback function
+.Fa func
+to be called with the file descriptor
+.Fa fd
+and the user argument
+.Fa uarg
+whenever the file descriptor
+.Fa fd
+can be red or has a close condition. If the file descriptor is not in
+non-blocking mode, it is set to non-blocking mode. If the callback is not
+needed anymore,
+.Fn fd_deselect
+may be called with the value returned from
+.Fn fd_select .
+All file descriptors selected by a module are automatically deselected when
+the module is unloaded.
+.Pp
+To temporarily suspend the file descriptor registration
+.Fn fd_suspend
+can be called. This also causes the file descriptor to be switched back to
+blocking mode if it was blocking prior the call to
+.Fn fd_select .
+This is necessary to do synchronuous input on a selected socket.
+The effect of
+.Fn fd_suspend
+can be undone with
+.Fn fd_resume .
+.Ss OBJECT RESOURCES
+The system group contains an object resource table. A module may create
+an entry in this table by calling
+.Fn or_register
+with the
+.Fa oid
+to be registered, a textual description in
+.Fa str
+and a pointer to the module
+.Fa mod .
+The registration can be removed with
+.Fn or_unregister .
+All registrations of a module are automatically removed if the module is
+unloaded.
+.Ss TRANSMIT AND RECEIVE BUFFERS
+A buffer is allocated via
+.Fn buf_alloc .
+The argument must be 1 for transmit and 0 for receive buffers. The function
+may return
+.Li NULL
+if there is no memory available. The current buffersize can be obtained with
+.Fn buf_size .
+.Sh PROCESSING PDUS
+For modules that need to do their own PDU processing (for example for proxying)
+the following functions are available:
+.Pp
+Function
+.Fn snmp_input_start
+decodes the PDU, searches the community, and sets the global
+.Va this_tick .
+It returns one of the following error codes:
+.Bl -tag -width ".It Er SNMPD_INPUT_VALBADLEN"
+.It Er SNMPD_INPUT_OK
+Everything ok, continue with processing.
+.It Er SNMPD_INPUT_FAILED
+The PDU could not be decoded, has a wrong version or an unknown
+community string.
+.It Er SNMPD_INPUT_VALBADLEN
+A SET PDU had a value field in a binding with a wrong length field in an
+ASN.1 header.
+.It Er SNMPD_INPUT_VALRANGE
+A SET PDU had a value field in a binding with a value that is out of range
+for the given ASN.1 type.
+.It Er SNMPD_INPUT_VALBADENC
+A SET PDU had a value field in a binding with wrong ASN.1 encoding.
+.El
+.Pp
+The function
+.Fn snmp_input_finish
+does the other half of processing: if
+.Fn snmp_input_start
+did not return OK, tries to construct an error response. If the start was OK,
+it calls the correct function from
+.Xr bsnmpagent
+to execute the request and depending on the outcome constructs a response or
+error response PDU or ignores the request PDU. It returns either
+.Er SNMPD_INPUT_OK
+or
+.Er SNMPD_INPUT_FAILED .
+In the first case a response PDU was constructed and should be sent.
+.Pp
+The function
+.Fn snmp_output
+takes a PDU and encodes it.
+.Pp
+The function
+.Fn snmp_send_port
+takes a PDU, encodes it and sends it through the given port (identified by
+the index in the port table) to the given address.
+.Pp
+The function
+.Fn snmp_send_trap
+sends a trap to all trap destinations. The arguments are the
+.Fa oid
+identifying the trap and a NULL-terminated list of
+.Vt struct snmp_value
+pointers that are to be inserted into the trap binding list.
+.Ss SIMPLE ACTION SUPPORT
+For simple scalar variables that need no dependencies a number of support
+functions is available to handle the set, commit, rollback and get.
+.Pp
+The following functions are used for OCTET STRING scalars, either NUL terminated
+or not:
+.Bl -tag -width "XXXXXXXXX"
+.It Fn string_save
+should be called for SNMP_OP_SET.
+.Fa value
+and
+.Fa ctx
+are the resp\&. arguments to the node callback.
+.Fa valp
+is a pointer to the pointer that holds the current value and
+.Fa req_size
+should be -1 if any size of the string is acceptable or a number larger or
+equal zero if the string must have a specific size. The function saves
+the old value in the scratch area (note, that any initial value must have
+been allocated by
+.Xr malloc 3 ),
+allocates a new string, copies over the new value, NUL-terminates it and
+sets the new current value.
+.It Fn string_commit
+simply frees the saved old value in the scratch area.
+.It Fn string_rollback
+frees the new value, and puts back the old one.
+.It Fn string_get
+is used for GET or GETNEXT. If
+.Fa len
+is -1, the length is computed via
+.Xr strlen 3
+from the current string value. If the current value is NULL,
+a OCTET STRING of zero length is returned.
+.It Fn string_free
+must be called if either rollback or commit fails to free the saved old value.
+.El
+.Pp
+The following functions are used to process scalars of type IP-address:
+.Bl -tag -width "XXXXXXXXX"
+.It Fn ip_save
+Saves the current value in the scratch area and sets the new value from
+.Fa valp .
+.It Fn ip_commit
+Does nothing.
+.It Fn ip_rollback
+Restores the old IP address from the scratch area.
+.It Fn ip_get
+Retrieves the IP current address.
+.El
+.Pp
+The following functions handle OID-typed variables:
+.Bl -tag -width "XXXXXXXXX"
+.It Fn oid_save
+Saves the current value in the scratch area by allocating a
+.Vt struct asn_oid
+with
+.Xr malloc 3
+and sets the new value from
+.Fa oid .
+.It Fn oid_commit
+Frees the old value in the scratch area.
+.It Fn oid_rollback
+Restores the old OID from the scratch area and frees the old OID.
+.It Fn oid_get
+Retrieves the OID
+.El
+.Ss TABLE INDEX HANDLING
+The following functions help in handling table indexes:
+.Bl -tag -width "XXXXXXXXX"
+.It Fn index_decode
+Decodes the index part of the OID. The parameter
+.Fa oid
+must be a pointer to the
+.Va var
+field of the
+.Fa value
+argument of the node callback. The
+.Fa sub
+argument must be the index of the start of the index in the OID (this is
+the
+.Fa sub
+argument to the node callback).
+.Fa code
+is the index expression (parameter
+.Fa idx
+to the node callback).
+These parameters are followed by parameters depending on the syntax of the index
+elements as follows:
+.Bl -tag -width ".It Li OCTET STRING"
+.It Li INTEGER
+.Vt int32_t *
+expected as argument.
+.It Li COUNTER64
+.Vt u_int64_t *
+expected as argument. Note, that this syntax is illegal for indexes.
+.It Li OCTET STRING
+A
+.Vt u_char **
+and a
+.Vt size_t *
+expected as arguments. A buffer is allocated to hold the decoded string.
+.It Li OID
+A
+.Vt struct asn_oid *
+is expected as argument.
+.It Li IP ADDRESS
+A
+.Vt u_int8_t *
+expected as argument that points to a buffer of at least four byte.
+.It Li COUNTER, GAUGE, TIMETICKS
+A
+.Vt u_int32_t
+expected.
+.It Li NULL
+No argument expected.
+.El
+.It Fn index_compare
+compares the current variable with an OID.
+.Fa oid1
+and
+.Fa sub
+come from the node callback arguments
+.Fa value->var
+and
+.Fa sub
+resp.
+.Fa oid2
+is the OID to compare to. The function returns -1, 0, +1 when the
+variable is lesser, equal, higher to the given OID.
+.Fa oid2
+must contain only the index part of the table column.
+.It Fn index_compare_off
+is equivalent to
+.Fn index_compare
+except that it takes an additional parameter
+.Fa off
+that causes it to ignore the first
+.Fa off
+components of both indexes.
+.It Fn index_append
+appends OID
+.Fa src
+beginning at position
+.Fa sub
+to
+.Fa dst .
+.It Fn index_append_off
+appends OID
+.Fa src
+beginning at position
+.Fa off
+to
+.Fa dst
+beginning at position
+.Fa sub
++
+.Fa off .
+.El
+.Sh SEE ALSO
+.Xr snmpd 1 ,
+.Xr gensnmptree 1 ,
+.Xr bsnmplib 3
+.Xr bsnmpclient 3 ,
+.Xr bsnmpagent 3
+.Sh STANDARDS
+This implementation conforms to the applicable IETF RFCs and ITU-T
+recommendations.
+.Sh AUTHORS
+.An Hartmut Brandt Aq brandt@fokus.gmd.de
diff --git a/contrib/bsnmp/snmpd/snmpmod.h b/contrib/bsnmp/snmpd/snmpmod.h
new file mode 100644
index 0000000..5e4cbbd
--- /dev/null
+++ b/contrib/bsnmp/snmpd/snmpmod.h
@@ -0,0 +1,363 @@
+/*
+ * Copyright (c) 2001-2003
+ * Fraunhofer Institute for Open Communication Systems (FhG Fokus).
+ * All rights reserved.
+ *
+ * Author: Harti Brandt <harti@freebsd.org>
+ *
+ * Redistribution of this software and documentation and use in source and
+ * binary forms, with or without modification, are permitted provided that
+ * the following conditions are met:
+ *
+ * 1. Redistributions of source code or documentation must retain the above
+ * copyright notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY FRAUNHOFER FOKUS
+ * AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * FRAUNHOFER FOKUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Begemot: bsnmp/snmpd/snmpmod.h,v 1.23 2003/01/28 13:44:35 hbb Exp $
+ *
+ * SNMP daemon data and functions exported to modules.
+ */
+#ifndef snmpmod_h_
+#define snmpmod_h_
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include "asn1.h"
+#include "snmp.h"
+#include "snmpagent.h"
+
+#define MAX_MOD_ARGS 16
+
+/*
+ * These macros help to handle object lists for SNMP tables. They use
+ * tail queues to hold the objects in ascending order in the list.
+ * ordering can be done either on an integer/unsigned field or and asn_oid.
+ */
+#define INSERT_OBJECT_OID_LINK_INDEX(PTR, LIST, LINK, INDEX) do { \
+ __typeof (PTR) _lelem; \
+ \
+ TAILQ_FOREACH(_lelem, (LIST), LINK) \
+ if (asn_compare_oid(&_lelem->INDEX, &(PTR)->INDEX) > 0) \
+ break; \
+ if (_lelem == NULL) \
+ TAILQ_INSERT_TAIL((LIST), (PTR), LINK); \
+ else \
+ TAILQ_INSERT_BEFORE(_lelem, (PTR), LINK); \
+ } while(0)
+
+#define INSERT_OBJECT_INT_LINK_INDEX(PTR, LIST, LINK, INDEX) do { \
+ __typeof (PTR) _lelem; \
+ \
+ TAILQ_FOREACH(_lelem, (LIST), LINK) \
+ if ((asn_subid_t)_lelem->INDEX > (asn_subid_t)(PTR)->INDEX)\
+ break; \
+ if (_lelem == NULL) \
+ TAILQ_INSERT_TAIL((LIST), (PTR), LINK); \
+ else \
+ TAILQ_INSERT_BEFORE(_lelem, (PTR), LINK); \
+ } while(0)
+
+#define FIND_OBJECT_OID_LINK_INDEX(LIST, OID, SUB, LINK, INDEX) ({ \
+ __typeof (TAILQ_FIRST(LIST)) _lelem; \
+ \
+ TAILQ_FOREACH(_lelem, (LIST), LINK) \
+ if (index_compare(OID, SUB, &_lelem->INDEX) == 0) \
+ break; \
+ (_lelem); \
+ })
+
+#define NEXT_OBJECT_OID_LINK_INDEX(LIST, OID, SUB, LINK, INDEX) ({ \
+ __typeof (TAILQ_FIRST(LIST)) _lelem; \
+ \
+ TAILQ_FOREACH(_lelem, (LIST), LINK) \
+ if (index_compare(OID, SUB, &_lelem->INDEX) < 0) \
+ break; \
+ (_lelem); \
+ })
+
+#define FIND_OBJECT_INT_LINK_INDEX(LIST, OID, SUB, LINK, INDEX) ({ \
+ __typeof (TAILQ_FIRST(LIST)) _lelem; \
+ \
+ if ((OID)->len - SUB != 1) \
+ _lelem = NULL; \
+ else \
+ TAILQ_FOREACH(_lelem, (LIST), LINK) \
+ if ((OID)->subs[SUB] == (asn_subid_t)_lelem->INDEX)\
+ break; \
+ (_lelem); \
+ })
+
+#define NEXT_OBJECT_INT_LINK_INDEX(LIST, OID, SUB, LINK, INDEX) ({ \
+ __typeof (TAILQ_FIRST(LIST)) _lelem; \
+ \
+ if ((OID)->len - SUB == 0) \
+ _lelem = TAILQ_FIRST(LIST); \
+ else \
+ TAILQ_FOREACH(_lelem, (LIST), LINK) \
+ if ((OID)->subs[SUB] < (asn_subid_t)_lelem->INDEX)\
+ break; \
+ (_lelem); \
+ })
+
+/*
+ * Macros for the case where the index field is called 'index'
+ */
+#define INSERT_OBJECT_OID_LINK(PTR, LIST, LINK) \
+ INSERT_OBJECT_OID_LINK_INDEX(PTR, LIST, LINK, index)
+
+#define INSERT_OBJECT_INT_LINK(PTR, LIST, LINK) do { \
+ INSERT_OBJECT_INT_LINK_INDEX(PTR, LIST, LINK, index)
+
+#define FIND_OBJECT_OID_LINK(LIST, OID, SUB, LINK) \
+ FIND_OBJECT_OID_LINK_INDEX(LIST, OID, SUB, LINK, index)
+
+#define NEXT_OBJECT_OID_LINK(LIST, OID, SUB, LINK) \
+ NEXT_OBJECT_OID_LINK_INDEX(LIST, OID, SUB, LINK, index)
+
+#define FIND_OBJECT_INT_LINK(LIST, OID, SUB, LINK) \
+ FIND_OBJECT_INT_LINK_INDEX(LIST, OID, SUB, LINK, index)
+
+#define NEXT_OBJECT_INT_LINK(LIST, OID, SUB, LINK) \
+ NEXT_OBJECT_INT_LINK_INDEX(LIST, OID, SUB, LINK, index)
+
+/*
+ * Macros for the case where the index field is called 'index' and the
+ * link field 'link'.
+ */
+#define INSERT_OBJECT_OID(PTR, LIST) \
+ INSERT_OBJECT_OID_LINK_INDEX(PTR, LIST, link, index)
+
+#define INSERT_OBJECT_INT(PTR, LIST) \
+ INSERT_OBJECT_INT_LINK_INDEX(PTR, LIST, link, index)
+
+#define FIND_OBJECT_OID(LIST, OID, SUB) \
+ FIND_OBJECT_OID_LINK_INDEX(LIST, OID, SUB, link, index)
+
+#define FIND_OBJECT_INT(LIST, OID, SUB) \
+ FIND_OBJECT_INT_LINK_INDEX(LIST, OID, SUB, link, index)
+
+#define NEXT_OBJECT_OID(LIST, OID, SUB) \
+ NEXT_OBJECT_OID_LINK_INDEX(LIST, OID, SUB, link, index)
+
+#define NEXT_OBJECT_INT(LIST, OID, SUB) \
+ NEXT_OBJECT_INT_LINK_INDEX(LIST, OID, SUB, link, index)
+
+struct lmodule;
+
+/* ticks when program and current packet was started */
+extern u_int32_t this_tick;
+extern u_int32_t start_tick;
+
+u_int32_t get_ticks(void);
+
+/*
+ * Return code for proxy function
+ */
+enum snmpd_proxy_err {
+ /* proxy code will process the PDU */
+ SNMPD_PROXY_OK,
+ /* proxy code does not process PDU */
+ SNMPD_PROXY_REJ,
+ /* drop this PDU */
+ SNMPD_PROXY_DROP,
+ /* drop because of bad community */
+ SNMPD_PROXY_BADCOMM,
+ /* drop because of bad community use */
+ SNMPD_PROXY_BADCOMMUSE
+};
+
+/*
+ * Input handling
+ */
+enum snmpd_input_err {
+ /* proceed with packet */
+ SNMPD_INPUT_OK,
+ /* fatal error in packet, ignore it */
+ SNMPD_INPUT_FAILED,
+ /* value encoding has wrong length in a SET operation */
+ SNMPD_INPUT_VALBADLEN,
+ /* value encoding is out of range */
+ SNMPD_INPUT_VALRANGE,
+ /* value has bad encoding */
+ SNMPD_INPUT_VALBADENC,
+};
+
+/*
+ * Every loadable module must have one of this structures with
+ * the external name 'config'.
+ */
+struct snmp_module {
+ /* a comment describing what this module implements */
+ const char *comment;
+
+ /* the initialisation function */
+ int (*init)(struct lmodule *, int argc, char *argv[]);
+
+ /* the finalisation function */
+ int (*fini)(void);
+
+ /* the idle function */
+ void (*idle)(void);
+
+ /* the dump function */
+ void (*dump)(void);
+
+ /* re-configuration function */
+ void (*config)(void);
+
+ /* start operation */
+ void (*start)(void);
+
+ /* proxy a PDU */
+ enum snmpd_proxy_err (*proxy)(struct snmp_v1_pdu *,
+ const struct asn_oid *, const struct sockaddr *, socklen_t,
+ enum snmpd_input_err, int32_t);
+
+ /* the tree this module is going to server */
+ const struct snmp_node *tree;
+ u_int tree_size;
+
+ /* function called, when another module was unloaded/loaded */
+ void (*loading)(const struct lmodule *, int);
+};
+
+/*
+ * Stuff exported to modules
+ */
+
+/*
+ * The system group.
+ */
+struct systemg {
+ u_char *descr;
+ struct asn_oid object_id;
+ u_char *contact;
+ u_char *name;
+ u_char *location;
+ u_int32_t services;
+ u_int32_t or_last_change;
+};
+extern struct systemg systemg;
+
+/*
+ * Community support.
+ *
+ * We have 2 fixed communities for SNMP read and write access. Modules
+ * can create their communities dynamically. They are deleted automatically
+ * if the module is unloaded.
+ */
+#define COMM_INITIALIZE 0
+#define COMM_READ 1
+#define COMM_WRITE 2
+
+u_int comm_define(u_int, const char *descr, struct lmodule *, const char *str);
+const char * comm_string(u_int);
+
+/* community for current packet */
+extern u_int community;
+
+/*
+ * Well known OIDs
+ */
+extern const struct asn_oid oid_zeroDotZero;
+
+/*
+ * Request ID ranges.
+ *
+ * A module can request a range of request ids and associate them with a
+ * type field. All ranges are deleted if a module is unloaded.
+ */
+u_int reqid_allocate(int size, struct lmodule *);
+int32_t reqid_next(u_int type);
+int32_t reqid_base(u_int type);
+int reqid_istype(int32_t reqid, u_int type);
+u_int reqid_type(int32_t reqid);
+
+/*
+ * Timers.
+ */
+void *timer_start(u_int, void (*)(void *), void *, struct lmodule *);
+void timer_stop(void *);
+
+/*
+ * File descriptors
+ */
+void *fd_select(int, void (*)(int, void *), void *, struct lmodule *);
+void fd_deselect(void *);
+void fd_suspend(void *);
+int fd_resume(void *);
+
+/*
+ * Object resources
+ */
+u_int or_register(const struct asn_oid *, const char *, struct lmodule *);
+void or_unregister(u_int);
+
+/*
+ * Buffers
+ */
+void *buf_alloc(int tx);
+size_t buf_size(int tx);
+
+/* decode PDU and find community */
+enum snmpd_input_err snmp_input_start(const u_char *, size_t, const char *,
+ struct snmp_v1_pdu *, int32_t *);
+
+/* process the pdu. returns either _OK or _FAILED */
+enum snmpd_input_err snmp_input_finish(struct snmp_pdu *, const u_char *,
+ size_t, u_char *, size_t *, const char *, enum snmpd_input_err, int32_t,
+ void *);
+
+void snmp_output(struct snmp_v1_pdu *, u_char *, size_t *, const char *);
+void snmp_send_port(const struct asn_oid *, struct snmp_v1_pdu *,
+ const struct sockaddr *, socklen_t);
+
+/* sending traps */
+void snmp_send_trap(const struct asn_oid *, ...);
+
+/*
+ * Action support
+ */
+int string_save(struct snmp_value *, struct snmp_context *, ssize_t, u_char **);
+void string_commit(struct snmp_context *);
+void string_rollback(struct snmp_context *, u_char **);
+int string_get(struct snmp_value *, const u_char *, ssize_t);
+void string_free(struct snmp_context *);
+
+int ip_save(struct snmp_value *, struct snmp_context *, u_char *);
+void ip_rollback(struct snmp_context *, u_char *);
+void ip_commit(struct snmp_context *);
+int ip_get(struct snmp_value *, u_char *);
+
+int oid_save(struct snmp_value *, struct snmp_context *, struct asn_oid *);
+void oid_rollback(struct snmp_context *, struct asn_oid *);
+void oid_commit(struct snmp_context *);
+int oid_get(struct snmp_value *, const struct asn_oid *);
+
+int index_decode(const struct asn_oid *oid, u_int sub, u_int code, ...);
+int index_compare(const struct asn_oid *, u_int, const struct asn_oid *);
+int index_compare_off(const struct asn_oid *, u_int, const struct asn_oid *,
+ u_int);
+void index_append(struct asn_oid *, u_int, const struct asn_oid *);
+void index_append_off(struct asn_oid *, u_int, const struct asn_oid *, u_int);
+
+#endif
diff --git a/contrib/bsnmp/snmpd/trap.c b/contrib/bsnmp/snmpd/trap.c
new file mode 100644
index 0000000..4853ea9
--- /dev/null
+++ b/contrib/bsnmp/snmpd/trap.c
@@ -0,0 +1,475 @@
+/*
+ * Copyright (c) 2001-2003
+ * Fraunhofer Institute for Open Communication Systems (FhG Fokus).
+ * All rights reserved.
+ *
+ * Author: Harti Brandt <harti@freebsd.org>
+ *
+ * Redistribution of this software and documentation and use in source and
+ * binary forms, with or without modification, are permitted provided that
+ * the following conditions are met:
+ *
+ * 1. Redistributions of source code or documentation must retain the above
+ * copyright notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY FRAUNHOFER FOKUS
+ * AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * FRAUNHOFER FOKUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Begemot: bsnmp/snmpd/trap.c,v 1.5 2003/01/28 13:44:35 hbb Exp $
+ *
+ * TrapSinkTable
+ */
+#include <sys/types.h>
+#include <sys/sysctl.h>
+#include <sys/un.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdarg.h>
+#include <string.h>
+#include <ctype.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "snmpmod.h"
+#include "snmpd.h"
+#include "tree.h"
+#include "oid.h"
+
+struct trapsink_list trapsink_list = TAILQ_HEAD_INITIALIZER(trapsink_list);
+
+static const struct asn_oid oid_begemotTrapSinkTable =
+ OIDX_begemotTrapSinkTable;
+static const struct asn_oid oid_sysUpTime = OIDX_sysUpTime;
+static const struct asn_oid oid_snmpTrapOID = OIDX_snmpTrapOID;
+
+struct trapsink_dep {
+ struct snmp_dependency dep;
+ u_int set;
+ u_int status;
+ u_char comm[SNMP_COMMUNITY_MAXLEN + 1];
+ u_int version;
+ u_int rb;
+ u_int rb_status;
+ u_int rb_version;
+ u_char rb_comm[SNMP_COMMUNITY_MAXLEN + 1];
+};
+enum {
+ TDEP_STATUS = 0x0001,
+ TDEP_COMM = 0x0002,
+ TDEP_VERSION = 0x0004,
+
+ TDEP_CREATE = 0x0001,
+ TDEP_MODIFY = 0x0002,
+ TDEP_DESTROY = 0x0004,
+};
+
+static int
+trapsink_create(struct trapsink_dep *tdep)
+{
+ struct trapsink *t;
+ struct sockaddr_in sa;
+
+ if ((t = malloc(sizeof(*t))) == NULL)
+ return (SNMP_ERR_RES_UNAVAIL);
+
+ t->index = tdep->dep.idx;
+ t->status = TRAPSINK_NOT_READY;
+ t->comm[0] = '\0';
+ t->version = TRAPSINK_V2;
+
+ if ((t->socket = socket(PF_INET, SOCK_DGRAM, 0)) == -1) {
+ syslog(LOG_ERR, "socket(UDP): %m");
+ free(t);
+ return (SNMP_ERR_RES_UNAVAIL);
+ }
+ (void)shutdown(t->socket, SHUT_RD);
+
+ sa.sin_len = sizeof(sa);
+ sa.sin_family = AF_INET;
+ sa.sin_addr.s_addr = htonl((t->index.subs[0] << 24) |
+ (t->index.subs[1] << 16) | (t->index.subs[2] << 8) |
+ (t->index.subs[3] << 0));
+ sa.sin_port = htons(t->index.subs[4]);
+
+ if (connect(t->socket, (struct sockaddr *)&sa, sa.sin_len) == -1) {
+ syslog(LOG_ERR, "connect(%s,%u): %m",
+ inet_ntoa(sa.sin_addr), ntohl(sa.sin_port));
+ (void)close(t->socket);
+ free(t);
+ return (SNMP_ERR_GENERR);
+ }
+
+ if (tdep->set & TDEP_VERSION)
+ t->version = tdep->version;
+ if (tdep->set & TDEP_COMM)
+ strcpy(t->comm, tdep->comm);
+
+ if (t->comm[0] != '\0')
+ t->status = TRAPSINK_NOT_IN_SERVICE;
+
+ /* look whether we should activate */
+ if (tdep->status == 4) {
+ if (t->status == TRAPSINK_NOT_READY) {
+ if (t->socket != -1)
+ (void)close(t->socket);
+ free(t);
+ return (SNMP_ERR_INCONS_VALUE);
+ }
+ t->status = TRAPSINK_ACTIVE;
+ }
+
+ INSERT_OBJECT_OID(t, &trapsink_list);
+
+ tdep->rb |= TDEP_CREATE;
+
+ return (SNMP_ERR_NOERROR);
+}
+
+static void
+trapsink_free(struct trapsink *t)
+{
+ TAILQ_REMOVE(&trapsink_list, t, link);
+ if (t->socket != -1)
+ (void)close(t->socket);
+ free(t);
+}
+
+static int
+trapsink_modify(struct trapsink *t, struct trapsink_dep *tdep)
+{
+ tdep->rb_status = t->status;
+ tdep->rb_version = t->version;
+ strcpy(tdep->rb_comm, t->comm);
+
+ if (tdep->set & TDEP_STATUS) {
+ /* if we are active and should move to not_in_service do
+ * this first */
+ if (tdep->status == 2 && tdep->rb_status == TRAPSINK_ACTIVE) {
+ t->status = TRAPSINK_NOT_IN_SERVICE;
+ tdep->rb |= TDEP_MODIFY;
+ }
+ }
+
+ if (tdep->set & TDEP_VERSION)
+ t->version = tdep->version;
+ if (tdep->set & TDEP_COMM)
+ strcpy(t->comm, tdep->comm);
+
+ if (tdep->set & TDEP_STATUS) {
+ /* if we were inactive and should go active - do this now */
+ if (tdep->status == 1 && tdep->rb_status != TRAPSINK_ACTIVE) {
+ if (t->comm[0] == '\0') {
+ t->status = tdep->rb_status;
+ t->version = tdep->rb_version;
+ strcpy(t->comm, tdep->rb_comm);
+ return (SNMP_ERR_INCONS_VALUE);
+ }
+ t->status = TRAPSINK_ACTIVE;
+ tdep->rb |= TDEP_MODIFY;
+ }
+ }
+ return (SNMP_ERR_NOERROR);
+}
+
+static int
+trapsink_unmodify(struct trapsink *t, struct trapsink_dep *tdep)
+{
+ if (tdep->set & TDEP_STATUS)
+ t->status = tdep->rb_status;
+ if (tdep->set & TDEP_VERSION)
+ t->version = tdep->rb_version;
+ if (tdep->set & TDEP_COMM)
+ strcpy(t->comm, tdep->rb_comm);
+
+ return (SNMP_ERR_NOERROR);
+}
+
+static void
+trapsink_finish(struct snmp_context *ctx __unused, int fail, void *arg)
+{
+ struct trapsink *t = arg;
+
+ if (!fail)
+ trapsink_free(t);
+}
+
+static int
+trapsink_destroy(struct snmp_context *ctx, struct trapsink *t,
+ struct trapsink_dep *tdep)
+{
+ if (snmp_set_atfinish(ctx, trapsink_finish, t))
+ return (SNMP_ERR_RES_UNAVAIL);
+ t->status = TRAPSINK_DESTROY;
+ tdep->rb_status = t->status;
+ tdep->rb |= TDEP_DESTROY;
+ return (SNMP_ERR_NOERROR);
+}
+
+static int
+trapsink_undestroy(struct trapsink *t, struct trapsink_dep *tdep)
+{
+ t->status = tdep->rb_status;
+ return (SNMP_ERR_NOERROR);
+}
+
+static int
+trapsink_dep(struct snmp_context *ctx, struct snmp_dependency *dep,
+ enum snmp_depop op)
+{
+ struct trapsink_dep *tdep = (struct trapsink_dep *)dep;
+ struct trapsink *t;
+
+ t = FIND_OBJECT_OID(&trapsink_list, &dep->idx, 0);
+
+ switch (op) {
+
+ case SNMP_DEPOP_COMMIT:
+ if (tdep->set & TDEP_STATUS) {
+ switch (tdep->status) {
+
+ case 1:
+ case 2:
+ if (t == NULL)
+ return (SNMP_ERR_INCONS_VALUE);
+ return (trapsink_modify(t, tdep));
+
+ case 4:
+ case 5:
+ if (t != NULL)
+ return (SNMP_ERR_INCONS_VALUE);
+ return (trapsink_create(tdep));
+
+ case 6:
+ if (t == NULL)
+ return (SNMP_ERR_NOERROR);
+ return (trapsink_destroy(ctx, t, tdep));
+ }
+ } else if (tdep->set != 0)
+ return (trapsink_modify(t, tdep));
+
+ return (SNMP_ERR_NOERROR);
+
+ case SNMP_DEPOP_ROLLBACK:
+ if (tdep->rb & TDEP_CREATE) {
+ trapsink_free(t);
+ return (SNMP_ERR_NOERROR);
+ }
+ if (tdep->rb & TDEP_MODIFY)
+ return (trapsink_unmodify(t, tdep));
+ if(tdep->rb & TDEP_DESTROY)
+ return (trapsink_undestroy(t, tdep));
+ return (SNMP_ERR_NOERROR);
+ }
+ abort();
+}
+
+int
+op_trapsink(struct snmp_context *ctx, struct snmp_value *value,
+ u_int sub, u_int iidx, enum snmp_op op)
+{
+ struct trapsink *t;
+ u_char ipa[4];
+ int32_t port;
+ struct asn_oid idx;
+ struct trapsink_dep *tdep;
+ u_char *p;
+
+ t = NULL; /* gcc */
+
+ switch (op) {
+
+ case SNMP_OP_GETNEXT:
+ if ((t = NEXT_OBJECT_OID(&trapsink_list, &value->var, sub)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ index_append(&value->var, sub, &t->index);
+ break;
+
+ case SNMP_OP_GET:
+ if ((t = FIND_OBJECT_OID(&trapsink_list, &value->var, sub)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ break;
+
+ case SNMP_OP_SET:
+ if (index_decode(&value->var, sub, iidx, ipa, &port) ||
+ port == 0 || port > 65535)
+ return (SNMP_ERR_NO_CREATION);
+ t = FIND_OBJECT_OID(&trapsink_list, &value->var, sub);
+
+ asn_slice_oid(&idx, &value->var, sub, value->var.len);
+
+ tdep = (struct trapsink_dep *)snmp_dep_lookup(ctx,
+ &oid_begemotTrapSinkTable, &idx,
+ sizeof(*tdep), trapsink_dep);
+ if (tdep == NULL)
+ return (SNMP_ERR_RES_UNAVAIL);
+
+ switch (value->var.subs[sub - 1]) {
+
+ case LEAF_begemotTrapSinkStatus:
+ if (tdep->set & TDEP_STATUS)
+ return (SNMP_ERR_INCONS_VALUE);
+ switch (value->v.integer) {
+
+ case 1:
+ case 2:
+ if (t == NULL)
+ return (SNMP_ERR_INCONS_VALUE);
+ break;
+
+ case 4:
+ case 5:
+ if (t != NULL)
+ return (SNMP_ERR_INCONS_VALUE);
+ break;
+
+ case 6:
+ break;
+
+ default:
+ return (SNMP_ERR_WRONG_VALUE);
+ }
+ tdep->status = value->v.integer;
+ tdep->set |= TDEP_STATUS;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotTrapSinkComm:
+ if (tdep->set & TDEP_COMM)
+ return (SNMP_ERR_INCONS_VALUE);
+ if (value->v.octetstring.len == 0 ||
+ value->v.octetstring.len > SNMP_COMMUNITY_MAXLEN)
+ return (SNMP_ERR_WRONG_VALUE);
+ for (p = value->v.octetstring.octets;
+ p < value->v.octetstring.octets + value->v.octetstring.len;
+ p++) {
+ if (!isascii(*p) || !isprint(*p))
+ return (SNMP_ERR_WRONG_VALUE);
+ }
+ tdep->set |= TDEP_COMM;
+ strncpy(tdep->comm, value->v.octetstring.octets,
+ value->v.octetstring.len);
+ tdep->comm[value->v.octetstring.len] = '\0';
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotTrapSinkVersion:
+ if (tdep->set & TDEP_VERSION)
+ return (SNMP_ERR_INCONS_VALUE);
+ if (value->v.integer != TRAPSINK_V1 &&
+ value->v.integer != TRAPSINK_V2)
+ return (SNMP_ERR_WRONG_VALUE);
+ tdep->version = value->v.integer;
+ tdep->set |= TDEP_VERSION;
+ return (SNMP_ERR_NOERROR);
+ }
+ if (t == NULL)
+ return (SNMP_ERR_INCONS_NAME);
+ else
+ return (SNMP_ERR_NOT_WRITEABLE);
+
+
+ case SNMP_OP_ROLLBACK:
+ case SNMP_OP_COMMIT:
+ return (SNMP_ERR_NOERROR);
+ }
+
+ switch (value->var.subs[sub - 1]) {
+
+ case LEAF_begemotTrapSinkStatus:
+ value->v.integer = t->status;
+ break;
+
+ case LEAF_begemotTrapSinkComm:
+ return (string_get(value, t->comm, -1));
+
+ case LEAF_begemotTrapSinkVersion:
+ value->v.integer = t->version;
+ break;
+
+ }
+ return (SNMP_ERR_NOERROR);
+}
+
+void
+snmp_send_trap(const struct asn_oid *trap_oid, ...)
+{
+ struct snmp_pdu pdu;
+ struct trapsink *t;
+ const struct snmp_value *v;
+ va_list ap;
+ u_char *sndbuf;
+ size_t sndlen;
+ ssize_t len;
+
+ TAILQ_FOREACH(t, &trapsink_list, link) {
+ if (t->status != TRAPSINK_ACTIVE)
+ continue;
+ memset(&pdu, 0, sizeof(pdu));
+ strcpy(pdu.community, t->comm);
+ if (t->version == TRAPSINK_V1) {
+ pdu.version = SNMP_V1;
+ pdu.type = SNMP_PDU_TRAP;
+ pdu.enterprise = systemg.object_id;
+ memcpy(pdu.agent_addr, snmpd.trap1addr, 4);
+ pdu.generic_trap = trap_oid->subs[trap_oid->len - 1] - 1;
+ pdu.specific_trap = 0;
+ pdu.time_stamp = get_ticks() - start_tick;
+
+ pdu.nbindings = 0;
+ } else {
+ pdu.version = SNMP_V2c;
+ pdu.type = SNMP_PDU_TRAP2;
+ pdu.request_id = reqid_next(trap_reqid);
+ pdu.error_index = 0;
+ pdu.error_status = SNMP_ERR_NOERROR;
+
+ pdu.bindings[0].var = oid_sysUpTime;
+ pdu.bindings[0].var.subs[pdu.bindings[0].var.len++] = 0;
+ pdu.bindings[0].syntax = SNMP_SYNTAX_TIMETICKS;
+ pdu.bindings[0].v.uint32 = get_ticks() - start_tick;
+
+ pdu.bindings[1].var = oid_snmpTrapOID;
+ pdu.bindings[1].var.subs[pdu.bindings[1].var.len++] = 0;
+ pdu.bindings[1].syntax = SNMP_SYNTAX_OID;
+ pdu.bindings[1].v.oid = *trap_oid;
+
+ pdu.nbindings = 2;
+ }
+
+ va_start(ap, trap_oid);
+ while ((v = va_arg(ap, const struct snmp_value *)) != NULL)
+ pdu.bindings[pdu.nbindings++] = *v;
+ va_end(ap);
+
+ if ((sndbuf = buf_alloc(1)) == NULL) {
+ syslog(LOG_ERR, "trap send buffer: %m");
+ return;
+ }
+
+ snmp_output(&pdu, sndbuf, &sndlen, "TRAP");
+
+ if ((len = send(t->socket, sndbuf, sndlen, 0)) == -1)
+ syslog(LOG_ERR, "send: %m");
+ else if ((size_t)len != sndlen)
+ syslog(LOG_ERR, "send: short write %zu/%zu",
+ sndlen, (size_t)len);
+
+ free(sndbuf);
+ }
+}
diff --git a/contrib/bsnmp/snmpd/tree.def b/contrib/bsnmp/snmpd/tree.def
new file mode 100644
index 0000000..355a5a0
--- /dev/null
+++ b/contrib/bsnmp/snmpd/tree.def
@@ -0,0 +1,183 @@
+#
+# Copyright (c) 2001-2003
+# Fraunhofer Institute for Open Communication Systems (FhG Fokus).
+# All rights reserved.
+#
+# Author: Harti Brandt <harti@freebsd.org>
+#
+# Redistribution of this software and documentation and use in source and
+# binary forms, with or without modification, are permitted provided that
+# the following conditions are met:
+#
+# 1. Redistributions of source code or documentation must retain the above
+# copyright notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. Neither the name of the Institute nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY FRAUNHOFER FOKUS
+# AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+# FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+# FRAUNHOFER FOKUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+# EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# $Begemot: bsnmp/snmpd/tree.def,v 1.34 2002/12/11 15:54:08 hbb Exp $
+#
+# System group and private Begemot SNMPd MIB.
+#
+(1 internet
+ (2 mgmt
+ (1 mibII
+ (1 system
+#
+# The standard System group
+#
+ (1 sysDescr OCTETSTRING op_system_group GET)
+ (2 sysObjectId OID op_system_group GET)
+ (3 sysUpTime TIMETICKS op_system_group GET)
+ (4 sysContact OCTETSTRING op_system_group GET SET)
+ (5 sysName OCTETSTRING op_system_group GET SET)
+ (6 sysLocation OCTETSTRING op_system_group GET SET)
+ (7 sysServices INTEGER op_system_group GET)
+ (8 sysORLastChange TIMETICKS op_system_group GET)
+ (9 sysORTable
+ (1 sysOREntry : INTEGER op_or_table
+ (1 sysORIndex INTEGER)
+ (2 sysORID OID GET)
+ (3 sysORDescr OCTETSTRING GET)
+ (4 sysORUpTime TIMETICKS GET)
+ ))
+ )
+ (11 snmp
+ (1 snmpInPkts COUNTER op_snmp GET)
+ (3 snmpInBadVersions COUNTER op_snmp GET)
+ (4 snmpInBadCommunityNames COUNTER op_snmp GET)
+ (5 snmpInBadCommunityUses COUNTER op_snmp GET)
+ (6 snmpInASNParseErrs COUNTER op_snmp GET)
+ (30 snmpEnableAuthenTraps INTEGER op_snmp GET SET)
+ (31 snmpSilentDrops COUNTER op_snmp GET)
+ (32 snmpProxyDrops COUNTER op_snmp GET)
+ )
+ ))
+#
+# Private Begemot Stuff
+#
+ (4 private
+ (1 enterprises
+ (12325 fokus
+ (1 begemot
+
+#
+# Daemon infrastructure
+#
+ (1 begemotSnmpd
+ (1 begemotSnmpdObjects
+
+#
+# Configuration
+#
+ (1 begemotSnmpdConfig
+ (1 begemotSnmpdTransmitBuffer INTEGER op_snmpd_config GET SET)
+ (2 begemotSnmpdReceiveBuffer INTEGER op_snmpd_config GET SET)
+ (3 begemotSnmpdCommunityDisable INTEGER op_snmpd_config GET SET)
+ (4 begemotSnmpdTrap1Addr IPADDRESS op_snmpd_config GET SET)
+ )
+ (2 begemotTrapSinkTable
+ (1 begemotTrapSinkEntry : IPADDRESS INTEGER op_trapsink
+ (1 begemotTrapSinkAddr IPADDRESS)
+ (2 begemotTrapSinkPort INTEGER)
+ (3 begemotTrapSinkStatus INTEGER GET SET)
+ (4 begemotTrapSinkComm OCTETSTRING GET SET)
+ (5 begemotTrapSinkVersion INTEGER GET SET)
+ )
+ )
+#
+# Port table
+#
+ (4 begemotSnmpdPortTable
+ (1 begemotSnmpdPortEntry : IPADDRESS INTEGER op_snmp_port
+ (1 begemotSnmpdPortAddress IPADDRESS)
+ (2 begemotSnmpdPortPort UNSIGNED32)
+ (3 begemotSnmpdPortStatus INTEGER GET SET)
+ ))
+#
+# Community table
+#
+ (5 begemotSnmpdCommunityTable
+ (1 begemotSnmpdCommunityEntry : OCTETSTRING UNSIGNED32 op_community
+ (1 begemotSnmpdCommunityModule OCTETSTRING)
+ (2 begemotSnmpdCommunityIndex UNSIGNED32)
+ (3 begemotSnmpdCommunityString OCTETSTRING GET SET)
+ (4 begemotSnmpdCommunityDescr OCTETSTRING GET)
+ ))
+#
+# Module table
+#
+ (6 begemotSnmpdModuleTable
+ (1 begemotSnmpdModuleEntry : OCTETSTRING op_modules
+ (1 begemotSnmpdModuleSection OCTETSTRING)
+ (2 begemotSnmpdModulePath OCTETSTRING GET SET)
+ (3 begemotSnmpdModuleComment OCTETSTRING GET)
+ ))
+#
+# Statistics
+#
+ (7 begemotSnmpdStats
+ (1 begemotSnmpdStatsNoRxBufs COUNTER op_snmpd_stats GET)
+ (2 begemotSnmpdStatsNoTxBufs COUNTER op_snmpd_stats GET)
+ (3 begemotSnmpdStatsInTooLongPkts COUNTER op_snmpd_stats GET)
+ (4 begemotSnmpdStatsInBadPduTypes COUNTER op_snmpd_stats GET))
+#
+# Debugging
+#
+ (8 begemotSnmpdDebug
+ (1 begemotSnmpdDebugDumpPdus INTEGER op_debug GET SET)
+ (2 begemotSnmpdDebugSnmpTrace UNSIGNED32 op_debug GET SET)
+ (3 begemotSnmpdDebugSyslogPri INTEGER op_debug GET SET))
+
+#
+# Local (UNIX domain) port table
+#
+ (9 begemotSnmpdLocalPortTable
+ (1 begemotSnmpdLocalPortEntry : OCTETSTRING op_local_port
+ (1 begemotSnmpdLocalPortPath OCTETSTRING)
+ (2 begemotSnmpdLocalPortStatus INTEGER GET SET)
+ ))
+ )
+ (2 begemotSnmpdDefs
+ (1 begemotSnmpdAgent
+ (1 begemotSnmpdAgentFreeBSD OID op_dummy)
+ )
+ )
+ )
+ ))
+ )
+ )
+ (6 snmpV2
+ (3 snmpModules
+ (1 snmpMIB
+ (1 snmpMIBObjects
+ (4 snmpTrap
+ (1 snmpTrapOID OID op_snmp_trap)
+ )
+ (5 snmpTraps
+ (1 coldStart OID op_snmp_trap)
+ (2 warmStart OID op_snmp_trap)
+ (5 authenticationFailure OID op_snmp_trap)
+ )
+ (6 snmpSet
+ (1 snmpSetSerialNo INTEGER op_snmp_set GET SET)
+ )
+ )
+ )
+ ))
+)
OpenPOWER on IntegriCloud