diff options
Diffstat (limited to 'contrib')
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, + §ion, &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(¯os); + +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, ¯os, 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, ¯os, 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(¯os, 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(¯os); + 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) + ) + ) + ) + )) +) |