Index: stable/6/contrib/bsnmp/FREEBSD-Xlist =================================================================== --- stable/6/contrib/bsnmp/FREEBSD-Xlist (revision 157332) +++ stable/6/contrib/bsnmp/FREEBSD-Xlist (revision 157333) @@ -1,14 +1,16 @@ #$FreeBSD$ */Makefile.in */acinclude.m4 */aclocal.m4 */config */config/* */configure */configure.ac */gensnmpdef/Makefile.in */gensnmptree/Makefile.in */lib/Makefile.in */snmp_mibII/Makefile.in +*/snmp_mibII/tree.h +*/snmp_ntp/Makefile.in */snmpd/Makefile.in */snmpd/.gdbinit Index: stable/6/contrib/bsnmp/NEWS =================================================================== --- stable/6/contrib/bsnmp/NEWS (revision 157332) +++ stable/6/contrib/bsnmp/NEWS (revision 157333) @@ -1,164 +1,190 @@ +1.12 + A couple of man page fixes from various submitters. + + Make default communities NULL for security. + + Fix a core dump when -d tracing suboption has no argument (thanks + to Shteryana Shopova). + + Fix bug in parsing the include path in the daemon. + + Fix an uninitialize structure field in gensnmptree (thanks to + jasone@freebsdorg) + + 64bit HC counters in the IF-MIB by polling the OS periodically. + + Fix link traps to be more RFC conform (thanks to glebius@freebsd.org) + + Add fallback definition for SA_SIZE() to support.h. + + Move the porting definitions for U?INT32_{MIN,MAX} into support.h. + + Include a sys/tree.h from FreeBSD-current and add autoconf + check for it. + +1.11a Fix build of modules when stdint.h is included after asn1.h + 1.11 Make the Mib2 routing table use red-black tree. This vastly reduces loading and access time. Load the table only every 10 minutes. In the meantime process message from the routing socket to update the table. Lot of man-page fixes from ru@freebsd.org. Fixes to command line macros and macro redefinition. Trap variables for ntp (still need trap definition and code). Periodic timers from Victor Cruceru. Man-page fixes from Christian Brueffer. Lots of spelling fixes from Giorgios Keramidas. A number of changes to facilitate building on FreeBSD-4 from Andrey Elsukov. Add repeatable timers. 1.10 Change all the tick handling in the daemon from 32-bit to 64-bit. Bump the modules' major version number to 3. snmp_dialog: make the syntax of the variable bindings in the outgoing message SNMP_SYNTAX_NULL regardless of the syntax given by the user. Fix an error in timer handling in the LIBBEGEMOT case. snmpd/main.c: Fix a 64-bit warning. lib/snmpclient: Add snmp_parse_server(). 1.9 tcpwrapper support from glebius@freebsd.org. crude interface to NTP: snmp_ntp 1.8 many man page fixes from ru@freebsd.org and tobez@freebsd.org snmpd/main.c: fix a problem which resulted in the daemon exiting when a local socket was closed. snmpd/trans_lsock.c: include stdio.h to get a prototype for remove. gensnmptree/gensnmptree.c: fix a compile error snmp_mibII: add new field spec_oid to ifmib. This allows ifType specific modules to insert the value to be returned for ifSpecific. 1.7a fix core dump when config file cannot be opened (submitted by Maxim Konovalov) 1.7 snmpd: Move event library initialization before reading of config file (thanks to phk). gensnmptree: can now read more than one tree and merge them. some support for compilation on older systems use standard C fixed width integer types [u]intNN_t 1.6 New gensnmpdef tool. Needs libsmi. Make build infrastructure more intelligent. Fix a typo in the mibII counter retrival code. Add a new field to struct snmp_node for snmp agents: tree_data. This field can be used by the application. Fix a syntax error in the example configuration file (thanks to David J Duchscher). Fix a long standing problem with the module loading code and a memory leak. When a module could not be loaded usually a core dump was generated. This was caused by wrong handling of malloced memory in the module table dependency. While fixing this problem it became obvious that dynamic allocated data which was held in a dependency could be lost, because there was no guarantee that the dependency handler ever was call (in case of an error in a SET handler or an earlier dependency in the same PDU) thus causing this memory to leak. To fix this a third dependency operation is introduced SNMP_DEPOP_FINISH which is invoked just before a dependency is freed at the very end of the SET PDU handling. This means also that finalizers (set_atfinish) can be converted to DEPOP_FINISH handler in almost all cases. The only case where this is not so easy is when a finializer was registered for a simple SET operation. In this case an artifical dependency would be needed. Because this seems to occure very seldom the snmp_set_atfinish() call is removed. Thanks to Paul-Henning Kamp for an initial fix for the core dump. 09-Dec-2003 First step on factoring out the transport mapping stuff into loadable modules. Local stream sockets that check the clients privileges. 03-Dec-2003 Bump version to 1.5 but leave library version intact. 03-Dec-2003 Now works with libbegemot polling stuff instead of libisc. Use --with-libbegemot[=path] to configure. 03-Dec-2003 Fix parsing of non-minimal ASN.1 integers. They are now disallowed. Non-minimal lengths are allowed. Remove copyright clause 3 everywhere. Make an autoconf build infrastructure. 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. Index: stable/6/contrib/bsnmp/TODO =================================================================== --- stable/6/contrib/bsnmp/TODO (revision 157332) +++ stable/6/contrib/bsnmp/TODO (revision 157333) @@ -1,17 +1,14 @@ -snmpd_mibII: - - handle HC counters by periodically polling the kernel counters. - snmpd: - rethink transports a little bit: make them loadable and make a private subtree for transports: OK. Table not writeable yet. There should be a transport table that is indexed by the transport name. This table can be used to load/unload transports at run time. OK. Table not writeable yet. Then there can be a port table that is indexed by transport name and port name. Plus a transport-specific table indexed by port name only. The transport table could contain a column of type OID that points to the transport-specific table. Index: stable/6/contrib/bsnmp/VERSION =================================================================== --- stable/6/contrib/bsnmp/VERSION (revision 157332) +++ stable/6/contrib/bsnmp/VERSION (revision 157333) @@ -1 +1 @@ -1.11 +1.12 Index: stable/6/contrib/bsnmp/gensnmptree/gensnmptree.1 =================================================================== --- stable/6/contrib/bsnmp/gensnmptree/gensnmptree.1 (revision 157332) +++ stable/6/contrib/bsnmp/gensnmptree/gensnmptree.1 (revision 157333) @@ -1,192 +1,197 @@ .\" .\" Copyright (c) 2001-2005 .\" Fraunhofer Institute for Open Communication Systems (FhG Fokus). .\" All rights reserved. .\" .\" Author: Harti Brandt .\" .\" Redistribution 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 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. .\" .\" THIS SOFTWARE IS PROVIDED BY AUTHOR AND 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 AUTHOR OR 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.5 2005/06/15 11:31:25 brandt_h Exp $ +.\" $Begemot: bsnmp/gensnmptree/gensnmptree.1,v 1.7 2006/02/27 09:52:08 brandt_h Exp $ .\" -.Dd June 14, 2005 +.Dd February 27, 2006 .Dt GENSNMPTREE 1 .Os .Sh NAME .Nm gensnmptree .Nd "generate C and header files from a MIB description file" .Sh SYNOPSIS .Nm .Op Fl helt .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 +.Xr bsnmpd 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 +.Xr bsnmpd 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. +name emits three C preprocessor defines on its standard output: +.Bl -tag -width ".Va OIDLEN_ Ns Ar Name" +.It Va OIDX_ Ns Ar name +This define can be used to initialize a +.Va struct asn_oid +in the following way: .Pp +.Dl const struct asn_oid oid_sysDescr = OIDX_sysDescr; +.It Va OIDLEN_ Ns Ar name +is the length of the OID. +.It Va OID_ Ns Ar name +is the last component of the OID. +.El +.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 . +.Xr bsnmpd 1 . .It Fl t Instead of normal output print the resulting tree. .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 file := tree | tree file 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 +.Xr bsnmpd 1 .Sh AUTHORS .An Hartmut Brandt Aq harti@freebsd.org Index: stable/6/contrib/bsnmp/gensnmptree/gensnmptree.c =================================================================== --- stable/6/contrib/bsnmp/gensnmptree/gensnmptree.c (revision 157332) +++ stable/6/contrib/bsnmp/gensnmptree/gensnmptree.c (revision 157333) @@ -1,977 +1,977 @@ /* * Copyright (c) 2001-2003 * Fraunhofer Institute for Open Communication Systems (FhG Fokus). * All rights reserved. * * Copyright (c) 2004 * Hartmut Brandt. * All rights reserved. * * Author: Harti Brandt * * Redistribution 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 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. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND 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 AUTHOR OR 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.43 2005/10/04 11:21:29 brandt_h Exp $ + * $Begemot: bsnmp/gensnmptree/gensnmptree.c,v 1.44 2006/02/14 09:04:17 brandt_h Exp $ * * Generate OID table from table description. * * Syntax is: * --------- * file := tree | tree file * * 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 | column * * index := TYPE | index TYPE * */ #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_ERR_H #include #endif #include #include "support.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 { uint32_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 %zu 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 */ int all_cond; /* all conditions are true */ 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; node->flags = 0; 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, 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) { if (func == NULL) errx(1, "column without function (%s) - probably " "outside of a table", np->name); 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 \n"); fprintf(fp, "#include \n"); #ifdef HAVE_STDINT_H fprintf(fp, "#include \n"); #endif if (localincs) { fprintf(fp, "#include \"asn1.h\"\n"); fprintf(fp, "#include \"snmp.h\"\n"); fprintf(fp, "#include \"snmpagent.h\"\n"); } else { fprintf(fp, "#include \n"); fprintf(fp, "#include \n"); fprintf(fp, "#include \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 void print_syntax(u_int syntax) { u_int i; for (i = 0; keywords[i].str != NULL; i++) if (keywords[i].tok == TOK_TYPE && keywords[i].val == syntax) { printf(" %s", keywords[i].str); return; } abort(); } /* * Generate a tree definition file */ static void gen_tree(const struct node *np, int level) { const struct node *sp; u_int i; printf("%*s(%u %s", 2 * level, "", np->id, np->name); switch (np->type) { case NODE_LEAF: print_syntax(np->u.leaf.syntax); printf(" %s%s%s)\n", np->u.leaf.func, (np->flags & FL_GET) ? " GET" : "", (np->flags & FL_SET) ? " SET" : ""); break; case NODE_TREE: if (TAILQ_EMPTY(&np->u.tree.subs)) { printf(")\n"); } else { printf("\n"); TAILQ_FOREACH(sp, &np->u.tree.subs, link) gen_tree(sp, level + 1); printf("%*s)\n", 2 * level, ""); } break; case NODE_ENTRY: printf(" :"); for (i = 0; i < SNMP_INDEX_COUNT(np->u.entry.index); i++) print_syntax(SNMP_INDEX(np->u.entry.index, i)); printf(" %s\n", np->u.entry.func); TAILQ_FOREACH(sp, &np->u.entry.subs, link) gen_tree(sp, level + 1); printf("%*s)\n", 2 * level, ""); break; case NODE_COLUMN: print_syntax(np->u.column.syntax); printf("%s%s)\n", (np->flags & FL_GET) ? " GET" : "", (np->flags & FL_SET) ? " SET" : ""); break; } } static int extract(const struct node *np, struct asn_oid *oid, const char *obj, const struct asn_oid *idx, const char *iname) { 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) { if (oid->len + idx->len >= ASN_MAXOIDLEN) report_node(np, "OID too long"); fprintf(fp, "#define OID_%s%s\t%u\n", np->name, iname ? iname : "", np->id); fprintf(fp, "#define OIDLEN_%s%s\t%u\n", np->name, iname ? iname : "", oid->len + idx->len); fprintf(fp, "#define OIDX_%s%s\t{ %u, {", np->name, iname ? iname : "", oid->len + idx->len); for (n = 0; n < oid->len; n++) fprintf(fp, " %u,", oid->subs[n]); for (n = 0; n < idx->len; n++) fprintf(fp, " %u,", idx->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, idx, iname)) return (0); } else if (np->type == NODE_ENTRY) { TAILQ_FOREACH(sub, &np->u.entry.subs, link) if (!extract(sub, oid, obj, idx, iname)) return (0); } oid->len--; return (1); } static int gen_extract(const struct node *root, char *object) { struct asn_oid oid; struct asn_oid idx; char *s, *e, *end, *iname; u_long ul; int ret; /* look whether the object to extract has an index part */ idx.len = 0; iname = NULL; s = strchr(object, '.'); if (s != NULL) { iname = malloc(strlen(s) + 1); if (iname == NULL) err(1, "cannot allocated index"); strcpy(iname, s); for (e = iname; *e != '\0'; e++) if (*e == '.') *e = '_'; *s++ = '\0'; while (s != NULL) { if (*s == '\0') errx(1, "bad index syntax"); if ((e = strchr(s, '.')) != NULL) *e++ = '\0'; errno = 0; ul = strtoul(s, &end, 0); if (*end != '\0') errx(1, "bad index syntax '%s'", end); if (errno != 0) err(1, "bad index syntax"); if (idx.len == ASN_MAXOIDLEN) errx(1, "index oid too large"); idx.subs[idx.len++] = ul; s = e; } } oid.len = PREFIX_LEN; memcpy(oid.subs, prefix, sizeof(prefix)); ret = extract(root, &oid, object, &idx, iname); if (iname != NULL) free(iname); return (ret); } 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); } } static void merge_subs(struct node_list *s1, struct node_list *s2) { struct node *n1, *n2; while (!TAILQ_EMPTY(s2)) { n2 = TAILQ_FIRST(s2); TAILQ_REMOVE(s2, n2, link); TAILQ_FOREACH(n1, s1, link) if (n1->id >= n2->id) break; if (n1 == NULL) TAILQ_INSERT_TAIL(s1, n2, link); else if (n1->id > n2->id) TAILQ_INSERT_BEFORE(n1, n2, link); else { if (n1->type == NODE_TREE && n2->type == NODE_TREE) { if (strcmp(n1->name, n2->name) != 0) errx(1, "trees to merge must have " "same name '%s' '%s'", n1->name, n2->name); merge_subs(&n1->u.tree.subs, &n2->u.tree.subs); free(n2); } else if (n1->type == NODE_ENTRY && n2->type == NODE_ENTRY) { if (strcmp(n1->name, n2->name) != 0) errx(1, "entries to merge must have " "same name '%s' '%s'", n1->name, n2->name); if (n1->u.entry.index != n2->u.entry.index) errx(1, "entries to merge must have " "same index '%s'", n1->name); if (strcmp(n1->u.entry.func, n2->u.entry.func) != 0) errx(1, "entries to merge must have " "same op '%s'", n1->name); merge_subs(&n1->u.entry.subs, &n2->u.entry.subs); free(n2); } else errx(1, "entities to merge must be both " "trees or both entries: %s, %s", n1->name, n2->name); } } } static void merge(struct node *root, struct node *t) { /* both must be trees */ if (root->type != NODE_TREE) errx(1, "root is not a tree"); if (t->type != NODE_TREE) errx(1, "can merge only with tree"); if (root->id != t->id) errx(1, "trees to merge must have same id"); merge_subs(&root->u.tree.subs, &t->u.tree.subs); } int main(int argc, char *argv[]) { int do_extract = 0; int do_tree = 0; int opt; struct node *root; char fname[MAXPATHLEN + 1]; int tok; while ((opt = getopt(argc, argv, "help:t")) != 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; case 't': do_tree = 1; break; } if (do_extract && do_tree) errx(1, "conflicting options -e and -t"); if (!do_extract && argc != optind) errx(1, "no arguments allowed"); if (do_extract && argc == optind) errx(1, "no objects specified"); root = parse(gettoken()); while ((tok = gettoken()) != TOK_EOF) merge(root, parse(tok)); 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); } if (do_tree) { gen_tree(root, 0); 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); } Index: stable/6/contrib/bsnmp/lib/asn1.c =================================================================== --- stable/6/contrib/bsnmp/lib/asn1.c (revision 157332) +++ stable/6/contrib/bsnmp/lib/asn1.c (revision 157333) @@ -1,1014 +1,1016 @@ /* * Copyright (c) 2001-2003 * Fraunhofer Institute for Open Communication Systems (FhG Fokus). * All rights reserved. * * Author: Harti Brandt * * Redistribution 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 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. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND 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 AUTHOR OR 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.29 2005/10/04 11:21:31 brandt_h Exp $ + * $Begemot: bsnmp/lib/asn1.c,v 1.31 2005/10/06 07:14:58 brandt_h Exp $ * * ASN.1 for SNMP. */ #include #include #include #include #include #ifdef HAVE_STDINT_H #include #elif defined(HAVE_INTTYPES_H) #include #endif #include + +#include "support.h" #include "asn1.h" #if !defined(INT32_MIN) #define INT32_MIN (-0x7fffffff-1) #endif #if !defined(INT32_MAX) #define INT32_MAX (0x7fffffff) #endif #if !defined(UINT32_MAX) #define UINT32_MAX (0xffffffff) #endif 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. * * */ static enum asn_err asn_get_real_integer(struct asn_buf *b, asn_len_t len, int64_t *vp) { uint64_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; else if (len > 1 && ((*b->asn_cptr == 0x00 && (b->asn_cptr[1] & 0x80) == 0) || (*b->asn_cptr == 0xff && (b->asn_cptr[1] & 0x80) == 0x80))) { asn_error(b, "non-minimal integer"); err = ASN_ERR_BADLEN; } 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]; uint64_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 = (uint64_t)-(ival + 1); neg = 1; } else val = (uint64_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, uint64_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; } else if (len > 1 && *b->asn_cptr == 0x00 && (b->asn_cptr[1] & 0x80) == 0) { asn_error(b, "non-minimal unsigned"); err = ASN_ERR_BADLEN; } 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, uint64_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> * * 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> */ 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> */ 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 ... */ enum asn_err asn_get_uint32_raw(struct asn_buf *b, asn_len_t len, uint32_t *vp) { uint64_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 = (uint32_t)v; } return (err); } enum asn_err asn_put_uint32(struct asn_buf *b, u_char type, uint32_t val) { uint64_t v = val; return (asn_put_real_unsigned(b, ASN_CLASS_APPLICATION|type, v)); } /* * COUNTER64 * 0x46 ... */ enum asn_err asn_get_counter64_raw(struct asn_buf *b, asn_len_t len, uint64_t *vp) { return (asn_get_real_unsigned(b, len, vp)); } enum asn_err asn_put_counter64(struct asn_buf *b, uint64_t val) { return (asn_put_real_unsigned(b, ASN_CLASS_APPLICATION | ASN_APP_COUNTER64, val)); } /* * TimeTicks * 0x43 ... */ enum asn_err asn_get_timeticks(struct asn_buf *b, uint32_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, uint32_t val) { uint64_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"); } Index: stable/6/contrib/bsnmp/lib/asn1.h =================================================================== --- stable/6/contrib/bsnmp/lib/asn1.h (revision 157332) +++ stable/6/contrib/bsnmp/lib/asn1.h (revision 157333) @@ -1,182 +1,182 @@ /* * Copyright (c) 2001-2003 * Fraunhofer Institute for Open Communication Systems (FhG Fokus). * All rights reserved. * * Author: Harti Brandt * * Redistribution 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 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. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND 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 AUTHOR OR 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.19 2005/10/04 11:21:31 brandt_h Exp $ + * $Begemot: bsnmp/lib/asn1.h,v 1.20 2005/10/05 16:43:11 brandt_h Exp $ * * ASN.1 for SNMP */ #ifndef asn1_h_ #define asn1_h_ #include 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 uint32_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 uint16_t would give conversion warnings on gcc */ typedef uint32_t asn_len_t; /* could be also uint16_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, uint32_t *); enum asn_err asn_put_uint32(struct asn_buf *, u_char, uint32_t); enum asn_err asn_get_counter64_raw(struct asn_buf *, asn_len_t, uint64_t *); enum asn_err asn_put_counter64(struct asn_buf *, uint64_t); enum asn_err asn_get_timeticks(struct asn_buf *, uint32_t *); enum asn_err asn_put_timeticks(struct asn_buf *, uint32_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 Index: stable/6/contrib/bsnmp/lib/snmpclient.c =================================================================== --- stable/6/contrib/bsnmp/lib/snmpclient.c (revision 157332) +++ stable/6/contrib/bsnmp/lib/snmpclient.c (revision 157333) @@ -1,1832 +1,1832 @@ /* * Copyright (c) 2004-2005 * Hartmut Brandt. * All rights reserved. * Copyright (c) 2001-2003 * Fraunhofer Institute for Open Communication Systems (FhG Fokus). * All rights reserved. * * Author: Harti Brandt * Kendy Kutzner * * Redistribution 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 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. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND 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 AUTHOR OR 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.34 2005/10/04 14:32:42 brandt_h Exp $ + * $Begemot: bsnmp/lib/snmpclient.c,v 1.36 2005/10/06 07:14:58 brandt_h Exp $ * * Support functions for SNMP clients. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_STDINT_H #include #elif defined(HAVE_INTTYPES_H) #include #endif #include #ifdef HAVE_ERR_H #include #endif #include "support.h" #include "asn1.h" #include "snmp.h" #include "snmpclient.h" #include "snmppriv.h" #if !defined(INT32_MAX) #define INT32_MAX (0x7fffffff) #endif #if !defined(UINT32_MAX) #define UINT32_MAX (0xffffffff) #endif /* 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; uint64_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; uint32_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(struct snmp_client *sc, const char *fmt, ...) { va_list ap; va_start(ap, fmt); vsnprintf(sc->error, sizeof(sc->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 & ((uint64_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(&snmp_client, "no memory for table entry"); return (NULL); } if ((w = malloc(sizeof(*w))) == NULL) { seterr(&snmp_client, "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(&snmp_client, "bad index: need integer"); goto err; } if (var->subs[p] > INT32_MAX) { seterr(&snmp_client, "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(&snmp_client, "bad index: need string length"); goto err; } len = var->subs[p++]; if (var->len < p + len) { seterr(&snmp_client, "bad index: string too short"); goto err; } if ((ptr = malloc(len + 1)) == NULL) { seterr(&snmp_client, "no memory for index string"); goto err; } for (j = 0; j < len; j++) { if (var->subs[p] > UCHAR_MAX) { seterr(&snmp_client, "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(&snmp_client, "bad index: need oid length"); goto err; } oid.len = var->subs[p++]; if (var->len < p + oid.len) { seterr(&snmp_client, "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(&snmp_client, "bad index: need ip-address"); goto err; } for (j = 0; j < 4; j++) { if (var->subs[p] > 0xff) { seterr(&snmp_client, "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(&snmp_client, "bad index: need unsigned"); goto err; } if (var->subs[p] > UINT32_MAX) { seterr(&snmp_client, "bad index: unsigned too large"); goto err; } *(uint32_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 |= (uint64_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 & ((uint64_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(&snmp_client, "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(&snmp_client, "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: *(uint32_t *)(void *)((u_char *)e + descr->entries[i].offset) = b->v.uint32; break; case SNMP_SYNTAX_COUNTER64: *(uint64_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 |= (uint64_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(&snmp_client, "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(&snmp_client, "last_change: bad response"); return (-1); } if (b->syntax != SNMP_SYNTAX_TIMETICKS) { seterr(&snmp_client, "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(&snmp_client, "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(&snmp_client, "max iteration count exceeded"); return (-1); } return (-2); } seterr(&snmp_client, "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 synchronous 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)) { 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(&snmp_client, "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(&snmp_client, "%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->trans = SNMP_TRANS_UDP; 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(&snmp_client, "%s", strerror(errno)); return (-1); } strcpy(snmp_client.chost, DEFAULT_HOST); } if (host != NULL) { if ((ptr = malloc(1 + strlen(host))) == NULL) { seterr(&snmp_client, "%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(&snmp_client, "%s", strerror(errno)); return (-1); } strcpy(snmp_client.cport, DEFAULT_PORT); } if (port != NULL) { if ((ptr = malloc(1 + strlen(port))) == NULL) { seterr(&snmp_client, "%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(&snmp_client, "%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(&snmp_client, "%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(&snmp_client, "%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; int stype; if (snmp_client.chost == NULL) { if ((snmp_client.chost = malloc(1 + sizeof(DEFAULT_LOCAL))) == NULL) { seterr(&snmp_client, "%s", strerror(errno)); return (-1); } strcpy(snmp_client.chost, DEFAULT_LOCAL); } if (path != NULL) { if ((ptr = malloc(1 + strlen(path))) == NULL) { seterr(&snmp_client, "%s", strerror(errno)); return (-1); } free(snmp_client.chost); snmp_client.chost = ptr; strcpy(snmp_client.chost, path); } if (snmp_client.trans == SNMP_TRANS_LOC_DGRAM) stype = SOCK_DGRAM; else stype = SOCK_STREAM; if ((snmp_client.fd = socket(PF_LOCAL, stype, 0)) == -1) { seterr(&snmp_client, "%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(&snmp_client, "%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(&snmp_client, "%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(&snmp_client, "%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(&snmp_client, "%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)); switch (snmp_client.trans) { case SNMP_TRANS_UDP: if (open_client_udp(host, port)) return (-1); break; case SNMP_TRANS_LOC_DGRAM: case SNMP_TRANS_LOC_STREAM: if (open_client_local(host)) return (-1); break; default: seterr(&snmp_client, "bad transport mapping"); 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(&snmp_client, "%s", strerror(errno)); (void)close(snmp_client.fd); snmp_client.fd = -1; if (snmp_client.local_path[0] != '\0') (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_path[0] != '\0') (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(&snmp_client, "%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(&snmp_client, "%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(&snmp_client, "%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(&snmp_client, "%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; #ifdef bsdi int optlen; #else socklen_t optlen; #endif if ((buf = malloc(snmp_client.rxbuflen)) == NULL) { seterr(&snmp_client, "%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(&snmp_client, "setsockopt: %s", strerror(errno)); free(buf); return (-1); } optlen = sizeof(*tv); if (getsockopt(snmp_client.fd, SOL_SOCKET, SO_RCVTIMEO, tv, &optlen) == -1) { seterr(&snmp_client, "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(&snmp_client, "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(&snmp_client, "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(&snmp_client, "recv: %s", strerror(saved_errno)); return (-1); } if (ret == 0) { /* this happens when we have a streaming socket and the * remote side has closed it */ free(buf); seterr(&snmp_client, "recv: socket closed by peer"); errno = EPIPE; return (-1); } abuf.asn_ptr = buf; abuf.asn_len = ret; if (SNMP_CODE_OK != (ret = snmp_pdu_decode(&abuf, pdu, &ip))) { seterr(&snmp_client, "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(&snmp_client, "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 response 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; struct snmp_pdu pdu; /* * Make a copy of the request and replace the syntaxes by NULL * if this is a GET,GETNEXT or GETBULK. */ pdu = *req; if (pdu.type == SNMP_PDU_GET || pdu.type == SNMP_PDU_GETNEXT || pdu.type == SNMP_PDU_GETBULK) { for (i = 0; i < pdu.nbindings; i++) pdu.bindings[i].syntax = SNMP_SYNTAX_NULL; } for (i = 0; i <= snmp_client.retries; i++) { (void)gettimeofday(&end, NULL); timeradd(&end, &snmp_client.timeout, &end); if ((reqid = snmp_send_packet(&pdu)) == -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); } if (ret < 0 && errno == EPIPE) /* stream closed */ return (-1); } } errno = ETIMEDOUT; seterr(&snmp_client, "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); } /* * parse a server specification * * [trans::][community@][server][:port] */ int snmp_parse_server(struct snmp_client *sc, const char *str) { const char *p, *s = str; /* look for a double colon */ for (p = s; *p != '\0'; p++) { if (*p == '\\' && p[1] != '\0') { p++; continue; } if (*p == ':' && p[1] == ':') break; } if (*p != '\0') { if (p > s) { if (p - s == 3 && strncmp(s, "udp", 3) == 0) sc->trans = SNMP_TRANS_UDP; else if (p - s == 6 && strncmp(s, "stream", 6) == 0) sc->trans = SNMP_TRANS_LOC_STREAM; else if (p - s == 5 && strncmp(s, "dgram", 5) == 0) sc->trans = SNMP_TRANS_LOC_DGRAM; else { seterr(sc, "unknown SNMP transport '%.*s'", (int)(p - s), s); return (-1); } } s = p + 2; } /* look for a @ */ for (p = s; *p != '\0'; p++) { if (*p == '\\' && p[1] != '\0') { p++; continue; } if (*p == '@') break; } if (*p != '\0') { if (p - s > SNMP_COMMUNITY_MAXLEN) { seterr(sc, "community string too long"); return (-1); } strncpy(sc->read_community, s, p - s); sc->read_community[p - s] = '\0'; strncpy(sc->write_community, s, p - s); sc->write_community[p - s] = '\0'; s = p + 1; } /* look for a colon */ for (p = s; *p != '\0'; p++) { if (*p == '\\' && p[1] != '\0') { p++; continue; } if (*p == ':') break; } if (*p == ':') { if (p > s) { /* host:port */ free(sc->chost); if ((sc->chost = malloc(p - s + 1)) == NULL) { seterr(sc, "%s", strerror(errno)); return (-1); } strncpy(sc->chost, s, p - s); sc->chost[p - s] = '\0'; } /* port */ free(sc->cport); if ((sc->cport = malloc(strlen(p + 1) + 1)) == NULL) { seterr(sc, "%s", strerror(errno)); return (-1); } strcpy(sc->cport, p + 1); } else if (p > s) { /* host */ free(sc->chost); if ((sc->chost = malloc(strlen(s) + 1)) == NULL) { seterr(sc, "%s", strerror(errno)); return (-1); } strcpy(sc->chost, s); } return (0); } Index: stable/6/contrib/bsnmp/lib/support.h =================================================================== --- stable/6/contrib/bsnmp/lib/support.h (revision 157332) +++ stable/6/contrib/bsnmp/lib/support.h (revision 157333) @@ -1,71 +1,96 @@ /* - * Copyright (C) 2004 + * Copyright (C) 2004-2005 * Hartmut Brandt. * All rights reserved. * * Author: Harti Brandt * * Redistribution 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 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. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND 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 AUTHOR OR 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/support.h,v 1.1 2004/08/06 08:47:59 brandt Exp $ + * $Begemot: bsnmp/lib/support.h,v 1.2 2005/10/06 07:14:59 brandt_h Exp $ * * Functions that are missing on certain systems. This header file is not * to be installed. */ #ifndef bsnmp_support_h_ #define bsnmp_support_h_ #include #ifndef HAVE_ERR_H void err(int, const char *, ...) __printflike(2, 3) __dead2; void errx(int, const char *, ...) __printflike(2, 3) __dead2; void warn(const char *, ...) __printflike(1, 2); void warnx(const char *, ...) __printflike(1, 2); #endif #ifndef HAVE_STRLCPY size_t strlcpy(char *, const char *, size_t); #endif #ifndef HAVE_GETADDRINFO struct addrinfo { u_int ai_flags; int ai_family; int ai_socktype; int ai_protocol; struct sockaddr *ai_addr; int ai_addrlen; struct addrinfo *ai_next; }; #define AI_CANONNAME 0x0001 int getaddrinfo(const char *, const char *, const struct addrinfo *, struct addrinfo **); const char *gai_strerror(int); void freeaddrinfo(struct addrinfo *); + +#endif + +/* + * For systems with missing stdint.h or inttypes.h + */ +#if !defined(INT32_MIN) +#define INT32_MIN (-0x7fffffff-1) +#endif +#if !defined(INT32_MAX) +#define INT32_MAX (0x7fffffff) +#endif +#if !defined(UINT32_MAX) +#define UINT32_MAX (0xffffffff) +#endif + +/* + * Systems missing SA_SIZE(). Taken from FreeBSD net/route.h:1.63 + */ +#ifndef SA_SIZE + +#define SA_SIZE(sa) \ + ( (!(sa) || ((struct sockaddr *)(sa))->sa_len == 0) ? \ + sizeof(long) : \ + 1 + ( (((struct sockaddr *)(sa))->sa_len - 1) | (sizeof(long) - 1) ) ) #endif #endif Index: stable/6/contrib/bsnmp/oid-list =================================================================== --- stable/6/contrib/bsnmp/oid-list (revision 157332) +++ stable/6/contrib/bsnmp/oid-list (revision 157333) @@ -1,23 +1,25 @@ -$Begemot: bsnmp/oid-list,v 1.3 2005/05/23 09:03:22 brandt_h Exp $ +$Begemot: bsnmp/oid-list,v 1.5 2006/02/27 09:55:45 brandt_h Exp $ This file documents the OID assignments under BSNMP's private OID. While I was at Fraunhofer Fokus (www.fokus.fraunhofer.de) I registered an official OID for Fokus: enterprises 12325 and assigned the sub-OID 1 to the software I develop. Under this OID there are: enterprises 12325 FOKUS 1 BEGEMOT 1 BEGEMOT-SNMPD 2 BEGEMOT-NETGRAPH snmpd netgraph module + 3 BEGEMOT-IP snmpd IP related stuff. 100 BEGEMOT-ILMID snmpd ILMID module 101 BEGEMOT-ATM snmpd ATM module 200 BEGEMOT-PF snmpd PF module (phillip@freebsd.org) 201 BEGEMOT-NTP snmpd NTP module + 202 BEGEMOT-HOSTRES snmpd HOSTRES module private stuff 300 BEGEMOT-ACM DLR ACM project If you need an OID and don't know where to stuck it in, I can assign you one - just drop me a mail. harti Index: stable/6/contrib/bsnmp/snmp_mibII/mibII.c =================================================================== --- stable/6/contrib/bsnmp/snmp_mibII/mibII.c (revision 157332) +++ stable/6/contrib/bsnmp/snmp_mibII/mibII.c (revision 157333) @@ -1,1639 +1,1789 @@ /* * Copyright (c) 2001-2003 * Fraunhofer Institute for Open Communication Systems (FhG Fokus). * All rights reserved. * * Author: Harti Brandt * * Redistribution 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 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. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND 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 AUTHOR OR 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.23 2005/06/09 12:36:52 brandt_h Exp $ + * $Begemot: bsnmp/snmp_mibII/mibII.c,v 1.24 2006/02/14 09:04:18 brandt_h Exp $ * * Implementation of the standard interfaces and ip MIB. */ #include "mibII.h" #include "mibII_oid.h" #include /*****************************/ /* our module */ static struct lmodule *module; /* routing socket */ static int route; static void *route_fd; /* if-index allocator */ static uint32_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 */ uint64_t mib_iftable_last_change; /* last change of stack table */ uint64_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 */ uint64_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); +/* baud rate of fastest interface */ +uint64_t mibif_maxspeed; + +/* user-forced update interval */ +u_int mibif_force_hc_update_interval; + +/* current update interval */ +u_int mibif_hc_update_interval; + +/* HC update timer handle */ +static void *hc_update_timer; + /*****************************/ 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, (struct snmp_value *)NULL); } -/* - * Fetch new MIB data. +/** + * Fetch the GENERIC IFMIB and update the HC counters */ -int -mib_fetch_ifmib(struct mibif *ifp) +static int +fetch_generic_mib(struct mibif *ifp, const struct ifmibdata *old) { int name[6]; size_t len; - void *newmib; - struct ifmibdata oldmib = ifp->mib; + struct mibif_private *p = ifp->private; 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); + /* + * Assume that one of the two following compounds is optimized away + */ + if (ULONG_MAX >= 0xffffffffffffffffULL) { + p->hc_inoctets = ifp->mib.ifmd_data.ifi_ibytes; + p->hc_outoctets = ifp->mib.ifmd_data.ifi_obytes; + p->hc_omcasts = ifp->mib.ifmd_data.ifi_omcasts; + p->hc_opackets = ifp->mib.ifmd_data.ifi_opackets; + p->hc_imcasts = ifp->mib.ifmd_data.ifi_imcasts; + p->hc_ipackets = ifp->mib.ifmd_data.ifi_ipackets; + + } else if (ULONG_MAX >= 0xffffffff) { + +#define UPDATE(HC, MIB) \ + if (old->ifmd_data.MIB > ifp->mib.ifmd_data.MIB) \ + p->HC += (0x100000000ULL + \ + ifp->mib.ifmd_data.MIB) - \ + old->ifmd_data.MIB; \ + else \ + p->HC += ifp->mib.ifmd_data.MIB - \ + old->ifmd_data.MIB; + + UPDATE(hc_inoctets, ifi_ibytes) + UPDATE(hc_outoctets, ifi_obytes) + UPDATE(hc_omcasts, ifi_omcasts) + UPDATE(hc_opackets, ifi_opackets) + UPDATE(hc_imcasts, ifi_imcasts) + UPDATE(hc_ipackets, ifi_ipackets) + +#undef UPDATE + } else + abort(); + return (0); +} + +/** + * Update the 64-bit interface counters + */ +static void +update_hc_counters(void *arg __unused) +{ + struct mibif *ifp; + struct ifmibdata oldmib; + + TAILQ_FOREACH(ifp, &mibif_list, link) { + oldmib = ifp->mib; + (void)fetch_generic_mib(ifp, &oldmib); + } +} + +/** + * Recompute the poll timer for the HC counters + */ +void +mibif_reset_hc_timer(void) +{ + u_int ticks; + + if ((ticks = mibif_force_hc_update_interval) == 0) { + if (mibif_maxspeed <= 10000000) { + /* at 10Mbps overflow needs 3436 seconds */ + ticks = 3000 * 100; /* 50 minutes */ + } else if (mibif_maxspeed <= 100000000) { + /* at 100Mbps overflow needs 343 seconds */ + ticks = 300 * 100; /* 5 minutes */ + } else if (mibif_maxspeed < 650000000) { + /* at 622Mbps overflow needs 53 seconds */ + ticks = 40 * 100; /* 40 seconds */ + } else if (mibif_maxspeed <= 1000000000) { + /* at 1Gbps overflow needs 34 seconds */ + ticks = 20 * 100; /* 20 seconds */ } else { - if (!(ifp->mib.ifmd_flags & IFF_UP)) - link_trap(ifp, 0); + /* at 10Gbps overflow needs 3.4 seconds */ + ticks = 100; /* 1 seconds */ } } + if (ticks == mibif_hc_update_interval) + return; + + if (hc_update_timer != NULL) { + timer_stop(hc_update_timer); + hc_update_timer = NULL; + } + update_hc_counters(NULL); + if ((hc_update_timer = timer_start_repeat(ticks * 10, ticks * 10, + update_hc_counters, NULL, module)) == NULL) { + syslog(LOG_ERR, "timer_start(%u): %m", ticks); + return; + } + mibif_hc_update_interval = ticks; +} + +/* + * Fetch new MIB data. + */ +int +mib_fetch_ifmib(struct mibif *ifp) +{ + int name[6]; + size_t len; + void *newmib; + struct ifmibdata oldmib = ifp->mib; + + if (fetch_generic_mib(ifp, &oldmib) == -1) + return (-1); + + /* + * Quoting RFC2863, 3.1.15: "... LinkUp and linkDown traps are + * generated just after ifOperStatus leaves, or just before it + * enters, the down state, respectively;" + */ + if (ifp->trap_enable && ifp->mib.ifmd_data.ifi_link_state != + oldmib.ifmd_data.ifi_link_state && + (ifp->mib.ifmd_data.ifi_link_state == LINK_STATE_DOWN || + oldmib.ifmd_data.ifi_link_state == LINK_STATE_DOWN)) + link_trap(ifp, ifp->mib.ifmd_data.ifi_link_state == + LINK_STATE_UP ? 1 : 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; } + if (ifp->mib.ifmd_data.ifi_baudrate > mibif_maxspeed) { + mibif_maxspeed = ifp->mib.ifmd_data.ifi_baudrate; + mibif_reset_hc_timer(); + } /* * linkspecific MIB */ + name[0] = CTL_NET; + name[1] = PF_LINK; + name[2] = NETLINK_GENERIC; + name[3] = IFMIB_IFDATA; + name[4] = ifp->sysindex; 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; uint32_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 *)(void *)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 != 0) { 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 mibif *ifp1; 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 this was the fastest interface - recompute this */ + if (ifp->mib.ifmd_data.ifi_baudrate == mibif_maxspeed) { + mibif_maxspeed = ifp->mib.ifmd_data.ifi_baudrate; + TAILQ_FOREACH(ifp1, &mibif_list, link) + if (ifp1->mib.ifmd_data.ifi_baudrate > mibif_maxspeed) + mibif_maxspeed = + ifp1->mib.ifmd_data.ifi_baudrate; + mibif_reset_hc_timer(); + } + + free(ifp->private); 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)); + if ((ifp->private = malloc(sizeof(struct mibif_private))) == NULL) { + syslog(LOG_WARNING, "%s: %m", __func__); + free(ifp); + return (NULL); + } + memset(ifp->private, 0, sizeof(struct mibif_private)); + ifp->sysindex = sysindex; strcpy(ifp->name, name); strcpy(ifp->descr, name); ifp->spec_oid = oid_zeroDotZero; 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; + ifp->mib.ifmd_data.ifi_link_state = LINK_STATE_UNKNOWN; 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]); } else { if (rtm->rtm_errno == 0 && (rtm->rtm_flags & RTF_UP)) mib_sroute_process(rtm, addrs[RTAX_GATEWAY], addrs[RTAX_DST], addrs[RTAX_NETMASK]); } 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]); } else { if (rtm->rtm_errno == 0 && (rtm->rtm_flags & RTF_UP)) mib_sroute_process(rtm, addrs[RTAX_GATEWAY], addrs[RTAX_DST], addrs[RTAX_NETMASK]); } break; case RTM_DELETE: mib_extract_addrs(rtm->rtm_addrs, (u_char *)(rtm + 1), addrs); if (rtm->rtm_errno == 0 && !(rtm->rtm_flags & RTF_LLINFO)) mib_sroute_process(rtm, addrs[RTAX_GATEWAY], addrs[RTAX_DST], addrs[RTAX_NETMASK]); break; } } /* * send a routing message */ void mib_send_rtmsg(struct rt_msghdr *rtm, struct sockaddr *gw, struct sockaddr *dst, struct sockaddr *mask) { size_t len; struct rt_msghdr *msg; char *cp; ssize_t sent; len = sizeof(*rtm) + SA_SIZE(gw) + SA_SIZE(dst) + SA_SIZE(mask); if ((msg = malloc(len)) == NULL) { syslog(LOG_ERR, "%s: %m", __func__); return; } cp = (char *)(msg + 1); memset(msg, 0, sizeof(*msg)); msg->rtm_flags = 0; msg->rtm_version = RTM_VERSION; msg->rtm_addrs = RTA_DST | RTA_GATEWAY; memcpy(cp, dst, SA_SIZE(dst)); cp += SA_SIZE(dst); memcpy(cp, gw, SA_SIZE(gw)); cp += SA_SIZE(gw); if (mask != NULL) { memcpy(cp, mask, SA_SIZE(mask)); cp += SA_SIZE(mask); msg->rtm_addrs |= RTA_NETMASK; } msg->rtm_msglen = cp - (char *)msg; msg->rtm_type = RTM_GET; if ((sent = write(route, msg, msg->rtm_msglen)) == -1) { syslog(LOG_ERR, "%s: write: %m", __func__); free(msg); return; } if (sent != msg->rtm_msglen) { syslog(LOG_ERR, "%s: short write", __func__); free(msg); return; } free(msg); } /* * 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, *newbuf; name[0] = CTL_NET; name[1] = PF_ROUTE; name[2] = 0; name[3] = af; name[4] = info; name[5] = arg; *lenp = 0; /* initial estimate */ 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 (*lenp == 0) return (NULL); buf = NULL; for (;;) { if ((newbuf = realloc(buf, *lenp)) == NULL) { syslog(LOG_ERR, "sysctl buffer: %m"); free(buf); return (NULL); } buf = newbuf; if (sysctl(name, 6, buf, lenp, NULL, 0) == 0) break; if (errno != ENOMEM) { syslog(LOG_ERR, "sysctl get: %m"); free(buf); return (NULL); } *lenp += *lenp / 8 + 1; } 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(); (void)mib_fetch_route(); 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); } 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; } Index: stable/6/contrib/bsnmp/snmp_mibII/mibII.h =================================================================== --- stable/6/contrib/bsnmp/snmp_mibII/mibII.h (revision 157332) +++ stable/6/contrib/bsnmp/snmp_mibII/mibII.h (revision 157333) @@ -1,245 +1,263 @@ /* * Copyright (c) 2001-2003 * Fraunhofer Institute for Open Communication Systems (FhG Fokus). * All rights reserved. * * Author: Harti Brandt * * Redistribution 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 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. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND 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 AUTHOR OR 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.15 2005/06/09 12:36:53 brandt_h Exp $ + * $Begemot: bsnmp/snmp_mibII/mibII.h,v 1.16 2006/02/14 09:04:19 brandt_h Exp $ * * Implementation of the interfaces and IP groups of MIB-II. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 /* + * Private mibif data - hang off from the mibif. + */ +struct mibif_private { + uint64_t hc_inoctets; + uint64_t hc_outoctets; + uint64_t hc_omcasts; + uint64_t hc_opackets; + uint64_t hc_imcasts; + uint64_t hc_ipackets; +}; +#define MIBIF_PRIV(IFP) ((struct mibif_private *)((IFP)->private)) + +/* * 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 uint64_t mib_iftable_last_change; /* last change of stack table */ extern uint64_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 uint64_t mibarpticks; /* info on system clocks */ extern struct clockinfo clockinfo; + +/* baud rate of fastest interface */ +extern uint64_t mibif_maxspeed; + +/* user-forced update interval */ +extern u_int mibif_force_hc_update_interval; + +/* current update interval */ +extern u_int mibif_hc_update_interval; + +/* re-compute update interval */ +void mibif_reset_hc_timer(void); /* 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); /* process routing message */ void mib_sroute_process(struct rt_msghdr *, struct sockaddr *, struct sockaddr *, struct sockaddr *); /* send a routing message */ void mib_send_rtmsg(struct rt_msghdr *, struct sockaddr *, struct sockaddr *, struct sockaddr *); /* extract addresses from routing message */ void mib_extract_addrs(int, u_char *, struct sockaddr **); /* fetch routing table */ int mib_fetch_route(void); Index: stable/6/contrib/bsnmp/snmp_mibII/mibII_interfaces.c =================================================================== --- stable/6/contrib/bsnmp/snmp_mibII/mibII_interfaces.c (revision 157332) +++ stable/6/contrib/bsnmp/snmp_mibII/mibII_interfaces.c (revision 157333) @@ -1,541 +1,543 @@ /* * Copyright (c) 2001-2003 * Fraunhofer Institute for Open Communication Systems (FhG Fokus). * All rights reserved. * * Author: Harti Brandt * * Redistribution 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 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. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND 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 AUTHOR OR 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.16 2005/11/02 12:07:40 brandt_h Exp $ + * $Begemot: bsnmp/snmp_mibII/mibII_interfaces.c,v 1.17 2006/02/14 09:04:19 brandt_h 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; uint32_t set; int promisc; int admin; int traps; uint32_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); case SNMP_DEPOP_FINISH: return (SNMP_ERR_NOERROR); } abort(); } /* * Return difference to daemon start time in ticks truncated to a * 32-bit value. If the timeval is 0 then return 0. */ static uint32_t ticks_get_timeval(struct timeval *tv) { uint64_t v; if (tv->tv_sec != 0 || tv->tv_usec != 0) { v = 100ULL * tv->tv_sec + tv->tv_usec / 10000ULL; 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: /* * According to RFC 2863 the state should be Up if the * interface is ready to transmit packets. We takes this to * mean that the interface should be running and should have * a carrier. If it is running and has no carrier we interpret * this as 'waiting for an external event' (plugging in the * cable) and hence return 'dormant'. */ if (ifp->mib.ifmd_flags & IFF_RUNNING) { if (ifp->mib.ifmd_data.ifi_link_state == LINK_STATE_DOWN) value->v.integer = 5; /* state dormant */ else value->v.integer = 1; /* state up */ } else value->v.integer = 2; /* state down */ 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 = ifp->spec_oid; 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; + value->v.counter64 = MIBIF_PRIV(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; + value->v.counter64 = MIBIF_PRIV(ifp)->hc_ipackets - + MIBIF_PRIV(ifp)->hc_imcasts; break; case LEAF_ifHCInMulticastPkts: if (!(ifp->flags & (MIBIF_VERYHIGHSPEED|MIBIF_HIGHSPEED))) goto again; - value->v.counter64 = ifp->hc_imcasts; + value->v.counter64 = MIBIF_PRIV(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_outoctets; + value->v.counter64 = MIBIF_PRIV(ifp)->hc_outoctets; break; case LEAF_ifHCOutUcastPkts: if (!(ifp->flags & (MIBIF_VERYHIGHSPEED|MIBIF_HIGHSPEED))) goto again; - value->v.counter64 = ifp->hc_opackets - ifp->hc_omcasts; + value->v.counter64 = MIBIF_PRIV(ifp)->hc_opackets - + MIBIF_PRIV(ifp)->hc_omcasts; break; case LEAF_ifHCOutMulticastPkts: if (!(ifp->flags & (MIBIF_VERYHIGHSPEED|MIBIF_HIGHSPEED))) goto again; - value->v.counter64 = ifp->hc_omcasts; + value->v.counter64 = MIBIF_PRIV(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); } Index: stable/6/contrib/bsnmp/snmp_mibII/mibII_route.c =================================================================== --- stable/6/contrib/bsnmp/snmp_mibII/mibII_route.c (revision 157332) +++ stable/6/contrib/bsnmp/snmp_mibII/mibII_route.c (revision 157333) @@ -1,509 +1,516 @@ /* * Copyright (c) 2001-2003 * Fraunhofer Institute for Open Communication Systems (FhG Fokus). * All rights reserved. * * Author: Harti Brandt * * Redistribution 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 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. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND 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 AUTHOR OR 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.7 2005/06/09 12:36:53 brandt_h Exp $ + * $Begemot: bsnmp/snmp_mibII/mibII_route.c,v 1.9 2005/10/06 07:15:00 brandt_h Exp $ * * Routing table */ +#include "support.h" + +#ifdef HAVE_SYS_TREE_H #include +#else +#include "tree.h" +#endif + #include "mibII.h" #include "mibII_oid.h" struct sroute { RB_ENTRY(sroute) link; uint32_t ifindex; uint8_t index[13]; uint8_t type; uint8_t proto; }; RB_HEAD(sroutes, sroute) sroutes = RB_INITIALIZER(&sroutes); RB_PROTOTYPE(sroutes, sroute, link, sroute_compare); #define ROUTE_UPDATE_INTERVAL (100 * 60 * 10) /* 10 min */ static uint64_t route_tick; static u_int route_total; /* * Compare two routes */ static int sroute_compare(struct sroute *s1, struct sroute *s2) { return (memcmp(s1->index, s2->index, 13)); } static void sroute_index_append(struct asn_oid *oid, u_int sub, const struct sroute *s) { int i; oid->len = sub + 13; for (i = 0; i < 13; i++) oid->subs[sub + i] = s->index[i]; } #if 0 static void sroute_print(const struct sroute *r) { u_int i; for (i = 0; i < 13 - 1; i++) printf("%u.", r->index[i]); printf("%u proto=%u type=%u", r->index[i], r->proto, r->type); } #endif /* * process routing message */ void mib_sroute_process(struct rt_msghdr *rtm, struct sockaddr *gw, struct sockaddr *dst, struct sockaddr *mask) { struct sockaddr_in *in_dst, *in_gw; struct in_addr in_mask; struct mibif *ifp; struct sroute key; struct sroute *r, *r1; in_addr_t ha; if (dst == NULL || gw == NULL || dst->sa_family != AF_INET || gw->sa_family != AF_INET) return; in_dst = (struct sockaddr_in *)(void *)dst; in_gw = (struct sockaddr_in *)(void *)gw; if (rtm->rtm_flags & RTF_HOST) in_mask.s_addr = 0xffffffff; else if (mask == NULL || mask->sa_len == 0) in_mask.s_addr = 0; else in_mask = ((struct sockaddr_in *)(void *)mask)->sin_addr; /* build the index */ ha = ntohl(in_dst->sin_addr.s_addr); key.index[0] = (ha >> 24) & 0xff; key.index[1] = (ha >> 16) & 0xff; key.index[2] = (ha >> 8) & 0xff; key.index[3] = (ha >> 0) & 0xff; ha = ntohl(in_mask.s_addr); key.index[4] = (ha >> 24) & 0xff; key.index[5] = (ha >> 16) & 0xff; key.index[6] = (ha >> 8) & 0xff; key.index[7] = (ha >> 0) & 0xff; /* ToS */ key.index[8] = 0; ha = ntohl(in_gw->sin_addr.s_addr); key.index[9] = (ha >> 24) & 0xff; key.index[10] = (ha >> 16) & 0xff; key.index[11] = (ha >> 8) & 0xff; key.index[12] = (ha >> 0) & 0xff; if (rtm->rtm_type == RTM_DELETE) { r = RB_FIND(sroutes, &sroutes, &key); if (r == 0) { #ifdef DEBUG_ROUTE syslog(LOG_WARNING, "%s: DELETE: %u.%u.%u.%u " "%u.%u.%u.%u %u %u.%u.%u.%u not found", __func__, key.index[0], key.index[1], key.index[2], key.index[3], key.index[4], key.index[5], key.index[6], key.index[7], key.index[8], key.index[9], key.index[10], key.index[11], key.index[12]); #endif return; } RB_REMOVE(sroutes, &sroutes, r); free(r); route_total--; #ifdef DEBUG_ROUTE printf("%s: DELETE: %u.%u.%u.%u " "%u.%u.%u.%u %u %u.%u.%u.%u\n", __func__, key.index[0], key.index[1], key.index[2], key.index[3], key.index[4], key.index[5], key.index[6], key.index[7], key.index[8], key.index[9], key.index[10], key.index[11], key.index[12]); #endif return; } /* GET or ADD */ ifp = NULL; if ((ifp = mib_find_if_sys(rtm->rtm_index)) == NULL) { if (rtm->rtm_type == RTM_ADD) { /* make it a get so the kernel fills the index */ mib_send_rtmsg(rtm, gw, dst, mask); return; } mib_iflist_bad = 1; } if ((r = malloc(sizeof(*r))) == NULL) { syslog(LOG_ERR, "%m"); return; } memcpy(r->index, key.index, sizeof(r->index)); r->ifindex = (ifp == NULL) ? 0 : 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; r1 = RB_INSERT(sroutes, &sroutes, r); if (r1 != NULL) { #ifdef DEBUG_ROUTE syslog(LOG_WARNING, "%s: %u.%u.%u.%u " "%u.%u.%u.%u %u %u.%u.%u.%u duplicate route", __func__, key.index[0], key.index[1], key.index[2], key.index[3], key.index[4], key.index[5], key.index[6], key.index[7], key.index[8], key.index[9], key.index[10], key.index[11], key.index[12]); #endif r1->ifindex = r->ifindex; r1->type = r->type; r1->proto = r->proto; free(r); return; } route_total++; #ifdef DEBUG_ROUTE printf("%s: ADD/GET: %u.%u.%u.%u " "%u.%u.%u.%u %u %u.%u.%u.%u\n", __func__, key.index[0], key.index[1], key.index[2], key.index[3], key.index[4], key.index[5], key.index[6], key.index[7], key.index[8], key.index[9], key.index[10], key.index[11], key.index[12]); #endif } int mib_fetch_route(void) { u_char *rtab, *next; size_t len; struct sroute *r, *r1; struct rt_msghdr *rtm; struct sockaddr *addrs[RTAX_MAX]; if (route_tick != 0 && route_tick + ROUTE_UPDATE_INTERVAL > this_tick) return (0); /* * Remove all routes */ r = RB_MIN(sroutes, &sroutes); while (r != NULL) { r1 = RB_NEXT(sroutes, &sroutes, r); RB_REMOVE(sroutes, &sroutes, r); free(r); r = r1; } 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); mib_sroute_process(rtm, addrs[RTAX_GATEWAY], addrs[RTAX_DST], addrs[RTAX_NETMASK]); } #if 0 u_int n = 0; r = RB_MIN(sroutes, &sroutes); while (r != NULL) { printf("%u: ", n++); sroute_print(r); printf("\n"); r = RB_NEXT(sroutes, &sroutes, r); } #endif free(rtab); route_tick = get_ticks(); return (0); } /** * Find a route in the table. */ static struct sroute * sroute_get(const struct asn_oid *oid, u_int sub) { struct sroute key; int i; if (oid->len - sub != 13) return (NULL); for (i = 0; i < 13; i++) key.index[i] = oid->subs[sub + i]; return (RB_FIND(sroutes, &sroutes, &key)); } /** * Find next route in the table. There is no such RB_ macro, so must * dig into the innards of the RB stuff. */ static struct sroute * sroute_getnext(struct asn_oid *oid, u_int sub) { u_int i; int comp; struct sroute key; struct sroute *best; struct sroute *s; /* * We now, that the OID is at least the tableEntry OID. If it is, * the user wants the first route. */ if (oid->len == sub) return (RB_MIN(sroutes, &sroutes)); /* * This is also true for any index that consists of zeros and is * shorter than the full index. */ if (oid->len < sub + 13) { for (i = sub; i < oid->len; i++) if (oid->subs[i] != 0) break; if (i == oid->len) return (RB_MIN(sroutes, &sroutes)); /* * Now if the index is too short, we fill it with zeros and then * subtract one from the index. We can do this, because we now, * that there is at least one index element that is not zero. */ for (i = oid->len; i < sub + 13; i++) oid->subs[i] = 0; for (i = sub + 13 - 1; i >= sub; i--) { if (oid->subs[i] != 0) { oid->subs[i]--; break; } oid->subs[i] = ASN_MAXID; } oid->len = sub + 13; } /* build the index */ for (i = sub; i < sub + 13; i++) key.index[i - sub] = oid->subs[i]; /* now find the element */ best = NULL; s = RB_ROOT(&sroutes); while (s != NULL) { comp = sroute_compare(&key, s); if (comp >= 0) { /* The current element is smaller than what we search. * Forget about it and move to the right subtree. */ s = RB_RIGHT(s, link); continue; } /* the current element is larger than what we search. * forget about the right subtree (its even larger), but * the current element may be what we need. */ if (best == NULL || sroute_compare(s, best) < 0) /* this one's better */ best = s; s = RB_LEFT(s, link); } return (best); } /* * 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) { struct sroute *r; if (mib_fetch_route() == -1) return (SNMP_ERR_GENERR); switch (op) { case SNMP_OP_GETNEXT: if ((r = sroute_getnext(&value->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); sroute_index_append(&value->var, sub, r); break; case SNMP_OP_GET: if ((r = sroute_get(&value->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_SET: if ((r = sroute_get(&value->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); 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[0]; value->v.ipaddress[1] = r->index[1]; value->v.ipaddress[2] = r->index[2]; value->v.ipaddress[3] = r->index[3]; break; case LEAF_ipCidrRouteMask: value->v.ipaddress[0] = r->index[4]; value->v.ipaddress[1] = r->index[5]; value->v.ipaddress[2] = r->index[6]; value->v.ipaddress[3] = r->index[7]; break; case LEAF_ipCidrRouteTos: value->v.integer = r->index[8]; break; case LEAF_ipCidrRouteNextHop: value->v.ipaddress[0] = r->index[9]; value->v.ipaddress[1] = r->index[10]; value->v.ipaddress[2] = r->index[11]; value->v.ipaddress[3] = r->index[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 (mib_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); } RB_GENERATE(sroutes, sroute, link, sroute_compare); Index: stable/6/contrib/bsnmp/snmp_mibII/mibII_tree.def =================================================================== --- stable/6/contrib/bsnmp/snmp_mibII/mibII_tree.def (revision 157332) +++ stable/6/contrib/bsnmp/snmp_mibII/mibII_tree.def (revision 157333) @@ -1,244 +1,261 @@ # # Copyright (c) 2001-2003 # Fraunhofer Institute for Open Communication Systems (FhG Fokus). # All rights reserved. # # Author: Harti Brandt # # Redistribution 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 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. # # THIS SOFTWARE IS PROVIDED BY AUTHOR AND 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 AUTHOR OR 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.12 2004/08/06 08:47:05 brandt Exp $ +# $Begemot: bsnmp/snmp_mibII/mibII_tree.def,v 1.13 2006/02/14 09:04:19 brandt_h 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 ) )) + (4 private + (1 enterprises + (12325 fokus + (1 begemot + (3 begemotIp + (1 begemotIpObjects + (1 begemotMib2 + (1 begemotIfMaxspeed COUNTER64 op_begemot_mibII GET) + (2 begemotIfPoll TIMETICKS op_begemot_mibII GET) + (3 begemotIfForcePoll TIMETICKS op_begemot_mibII GET SET) + ) + ) + ) + ) + ) + ) + ) (6 snmpV2 (3 snmpModules (1 snmpMIB (1 snmpMIBObjects (5 snmpTraps (3 linkDown OID op_snmp_trap) (4 linkUp OID op_snmp_trap) ) ) ) )) ) Index: stable/6/contrib/bsnmp/snmp_mibII/snmp_mibII.h =================================================================== --- stable/6/contrib/bsnmp/snmp_mibII/snmp_mibII.h (revision 157332) +++ stable/6/contrib/bsnmp/snmp_mibII/snmp_mibII.h (revision 157333) @@ -1,166 +1,169 @@ /* * Copyright (c) 2001-2003 * Fraunhofer Institute for Open Communication Systems (FhG Fokus). * All rights reserved. * * Author: Harti Brandt * * Redistribution 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 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. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND 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 AUTHOR OR 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.17 2005/05/23 09:03:43 brandt_h Exp $ + * $Begemot: bsnmp/snmp_mibII/snmp_mibII.h,v 1.18 2006/02/14 09:04:19 brandt_h 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; uint64_t mibtick; void *specmib; size_t specmiblen; u_char *physaddr; u_int physaddrlen; int has_connector; int trap_enable; uint64_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; /* to be set by ifType specific modules. This is ifSpecific. */ struct asn_oid spec_oid; + + /* private data - don't touch */ + void *private; }; /* * 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 Index: stable/6/contrib/bsnmp/snmp_ntp/BEGEMOT-NTP-MIB.txt =================================================================== --- stable/6/contrib/bsnmp/snmp_ntp/BEGEMOT-NTP-MIB.txt (revision 157332) +++ stable/6/contrib/bsnmp/snmp_ntp/BEGEMOT-NTP-MIB.txt (revision 157333) @@ -1,117 +1,144 @@ -- -- Copyright (c) 2005 -- Hartmut Brandt -- All rights reserved. -- -- Author: Harti Brandt -- -- Redistribution 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 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. -- -- THIS SOFTWARE IS PROVIDED BY AUTHOR AND 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 AUTHOR OR 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_ntp/BEGEMOT-NTP-MIB.txt,v 1.2 2005/04/26 13:38:01 brandt_h Exp $ +-- $Begemot: bsnmp/snmp_ntp/BEGEMOT-NTP-MIB.txt,v 1.3 2005/10/04 08:13:41 brandt_h Exp $ -- -- Private MIB for NTP module. -- BEGEMOT-NTP-MIB DEFINITIONS ::= BEGIN IMPORTS MODULE-IDENTITY, OBJECT-TYPE, TimeTicks, Unsigned32, Counter64 FROM SNMPv2-SMI + TruthValue + FROM SNMPv2-TC begemot FROM BEGEMOT-MIB; begemotNtp MODULE-IDENTITY - LAST-UPDATED "200503210000Z" + LAST-UPDATED "200509300000Z" ORGANIZATION "German Aerospace Center" CONTACT-INFO " Hartmut Brandt Postal: German Aerospace Center Oberpfaffenhofen 82234 Wessling Germany Fax: +49 8153 28 2843 E-mail: harti@freebsd.org" DESCRIPTION "The MIB for the NTP control module for SNMP." ::= { begemot 201 } begemotNtpObjects OBJECT IDENTIFIER ::= { begemotNtp 1 } begemotNtpHost OBJECT-TYPE SYNTAX OCTET STRING MAX-ACCESS read-only STATUS current DESCRIPTION "The name of the host where the NTP daemon is running that is to be connected." ::= { begemotNtpObjects 1 } begemotNtpPort OBJECT-TYPE SYNTAX OCTET STRING MAX-ACCESS read-only STATUS current DESCRIPTION "The port of the host where the NTP daemon is running that is to be connected." ::= { begemotNtpObjects 2 } begemotNtpTimeout OBJECT-TYPE SYNTAX TimeTicks MAX-ACCESS read-write STATUS current DESCRIPTION "The number of ticks to wait for the response from the NTP daemon to complete." ::= { begemotNtpObjects 3 } begemotNtpDebug OBJECT-TYPE SYNTAX Unsigned32 MAX-ACCESS read-write STATUS current DESCRIPTION "Debug flags. The following flags are currently used: 0x01 - produce a dump of all received/sent NTP packets. 0x02 - print the variable names and values return by the daemon Other bits are ignored." ::= { begemotNtpObjects 4 } begemotNtpJitter OBJECT-TYPE SYNTAX Counter64 MAX-ACCESS read-only STATUS current DESCRIPTION "Current jitter in seconds multiplied by 2^32." ::= { begemotNtpObjects 5 } begemotNtpStability OBJECT-TYPE SYNTAX Counter64 MAX-ACCESS read-only STATUS current DESCRIPTION "Current stability in ppm multiplied by 2^32." ::= { begemotNtpObjects 6 } + +begemotNtpJitterThresh OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Jitter trap threshold in seconds multiplied by 2^32." + ::= { begemotNtpObjects 7 } + +begemotNtpStabilityThresh OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Stability trap threshold in ppm multiplied by 2^32." + ::= { begemotNtpObjects 8 } + +begemotNtpTrapEnable OBJECT-TYPE + SYNTAX TruthValue + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Enables the sending of traps when either the peer is lost/ + found or one of the above thresholds is crossed." + ::= { begemotNtpObjects 9 } END Index: stable/6/contrib/bsnmp/snmp_ntp/snmp_ntp.c =================================================================== --- stable/6/contrib/bsnmp/snmp_ntp/snmp_ntp.c (revision 157332) +++ stable/6/contrib/bsnmp/snmp_ntp/snmp_ntp.c (revision 157333) @@ -1,1592 +1,1598 @@ /* * Copyright (c) 2005 * Hartmut Brandt. * All rights reserved. * * Author: Harti Brandt * * 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. * * 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_ntp/snmp_ntp.c,v 1.4 2005/05/23 09:03:48 brandt_h Exp $ + * $Begemot: bsnmp/snmp_ntp/snmp_ntp.c,v 1.9 2005/10/06 07:15:01 brandt_h Exp $ * * NTP interface for SNMPd. */ #include #include #include #include #include #include #include #include +#ifdef HAVE_STDINT_H #include +#elif defined(HAVE_INTTYPES_H) +#include +#endif #include #include #include #include #include +#include "support.h" #include "snmpmod.h" #include "ntp_tree.h" #include "ntp_oid.h" #define NTPC_MAX 576 #define NTPC_VERSION 3 #define NTPC_MODE 6 #define NTPC_DMAX 468 #define NTPC_BIT_RESP 0x80 #define NTPC_BIT_ERROR 0x40 #define NTPC_BIT_MORE 0x20 #define NTPC_OPMASK 0x1f #define NTPC_OP_READSTAT 1 #define NTPC_OP_READVAR 2 /* our module handle */ static struct lmodule *module; /* debug flag */ static uint32_t ntp_debug; #define DBG_DUMP_PKTS 0x01 #define DBG_DUMP_VARS 0x02 /* OIDs */ static const struct asn_oid oid_ntpMIB = OIDX_ntpMIB; /* the Object Resource registration index */ static u_int reg_index; /* last time we've fetch the system variables */ static uint64_t sysinfo_tick; /* cached system variables */ static int32_t sys_leap; static int sysb_leap; static int32_t sys_stratum; static int sysb_stratum; static int32_t sys_precision; static int sysb_precision; static char *sys_rootdelay; static char *sys_rootdispersion; static char *sys_refid; static char sys_reftime[8]; static int sysb_reftime; static int32_t sys_poll; static int sysb_poll; static uint32_t sys_peer; static int sysb_peer; static u_char sys_clock[8]; static int sysb_clock; static char *sys_system; static char *sys_processor; static int sysb_jitter; static double sys_jitter; static int sysb_stability; static double sys_stability; /* last time we've fetch the peer list */ static uint64_t peers_tick; /* request sequence number generator */ static uint16_t seqno; /* NTPD socket */ static int ntpd_sock; static void *ntpd_fd; struct peer { /* required entries for macros */ uint32_t index; TAILQ_ENTRY(peer) link; int32_t config; /* config bit */ u_char srcadr[4]; /* PeerAddress */ uint32_t srcport; /* PeerPort */ u_char dstadr[4]; /* HostAddress */ uint32_t dstport; /* HostPort */ int32_t leap; /* Leap */ int32_t hmode; /* Mode */ int32_t stratum; /* Stratum */ int32_t ppoll; /* PeerPoll */ int32_t hpoll; /* HostPoll */ int32_t precision; /* Precision */ char *rootdelay; /* RootDelay */ char *rootdispersion;/* RootDispersion */ char *refid; /* RefId */ u_char reftime[8]; /* RefTime */ u_char orgtime[8]; /* OrgTime */ u_char rcvtime[8]; /* ReceiveTime */ u_char xmttime[8]; /* TransmitTime */ u_int32_t reach; /* Reach */ int32_t timer; /* Timer */ char *offset; /* Offset */ char *delay; /* Delay */ char *dispersion; /* Dispersion */ int32_t filt_entries; }; TAILQ_HEAD(peer_list, peer); /* list of peers */ static struct peer_list peers = TAILQ_HEAD_INITIALIZER(peers); struct filt { /* required fields */ struct asn_oid index; TAILQ_ENTRY(filt) link; char *offset; char *delay; char *dispersion; }; TAILQ_HEAD(filt_list, filt); /* list of filters */ static struct filt_list filts = TAILQ_HEAD_INITIALIZER(filts); /* configuration */ static u_char *ntp_host; static u_char *ntp_port; static uint32_t ntp_timeout; static void ntpd_input(int, void *); static int open_socket(void); -/* the initialisation function */ +/* the initialization function */ static int ntp_init(struct lmodule *mod, int argc, char *argv[] __unused) { module = mod; if (argc != 0) { syslog(LOG_ERR, "bad number of arguments for %s", __func__); return (EINVAL); } ntp_host = strdup("localhost"); ntp_port = strdup("ntp"); ntp_timeout = 50; /* 0.5sec */ return (0); } /* * Module is started */ static void ntp_start(void) { if (open_socket() != -1) { ntpd_fd = fd_select(ntpd_sock, ntpd_input, NULL, module); if (ntpd_fd == NULL) { syslog(LOG_ERR, "fd_select failed on ntpd socket: %m"); return; } } reg_index = or_register(&oid_ntpMIB, "The MIB for NTP.", module); } /* * Called, when the module is to be unloaded after it was successfully loaded */ static int ntp_fini(void) { or_unregister(reg_index); fd_deselect(ntpd_fd); return (0); } const struct snmp_module config = { .comment = "This module implements the NTP MIB", .init = ntp_init, .start = ntp_start, .fini = ntp_fini, .tree = ntp_ctree, .tree_size = ntp_CTREE_SIZE, }; /* * Open the NTPD socket */ static int open_socket(void) { struct addrinfo hints, *res, *res0; int error; const char *cause; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET; hints.ai_socktype = SOCK_DGRAM; error = getaddrinfo(ntp_host, ntp_port, &hints, &res0); if (error) { syslog(LOG_ERR, "%s(%s): %s", ntp_host, ntp_port, gai_strerror(error)); return (-1); } ntpd_sock = -1; cause = "no address"; errno = EADDRNOTAVAIL; for (res = res0; res != NULL; res = res->ai_next) { ntpd_sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (ntpd_sock == -1) { cause = "socket"; continue; } if (connect(ntpd_sock, res->ai_addr, res->ai_addrlen) == -1) { cause = "connect"; (void)close(ntpd_sock); ntpd_sock = -1; continue; } break; } if (ntpd_sock == -1) { syslog(LOG_ERR, "%s: %m", cause); return (-1); } freeaddrinfo(res0); return (0); } /* * Dump a packet */ static void dump_packet(const u_char *pkt, size_t ret) { char buf[8 * 3 + 1]; size_t i, j; for (i = 0; i < ret; i += 8) { buf[0] = '\0'; for (j = 0; i + j < (size_t)ret && j < 8; j++) sprintf(buf + strlen(buf), " %02x", pkt[i + j]); syslog(LOG_INFO, "%04zu:%s", i, buf); } } /* * Execute an NTP request. */ static int ntpd_request(u_int op, u_int associd, const char *vars) { u_char *rpkt; u_char *ptr; size_t vlen; ssize_t ret; if ((rpkt = malloc(NTPC_MAX)) == NULL) { syslog(LOG_ERR, "%m"); return (-1); } memset(rpkt, 0, NTPC_MAX); ptr = rpkt; *ptr++ = (NTPC_VERSION << 3) | NTPC_MODE; *ptr++ = op; if (++seqno == 0) seqno++; *ptr++ = seqno >> 8; *ptr++ = seqno; /* skip status */ ptr += 2; *ptr++ = associd >> 8; *ptr++ = associd; /* skip offset */ ptr += 2; if (vars != NULL) { vlen = strlen(vars); if (vlen > NTPC_DMAX) { syslog(LOG_ERR, "NTP request too long (%zu)", vlen); free(rpkt); return (-1); } *ptr++ = vlen >> 8; *ptr++ = vlen; memcpy(ptr, vars, vlen); ptr += vlen; } else /* skip data length (is already zero) */ ptr += 2; while ((ptr - rpkt) % 4 != 0) *ptr++ = 0; if (ntp_debug & DBG_DUMP_PKTS) { syslog(LOG_INFO, "sending %zd bytes", ptr - rpkt); dump_packet(rpkt, ptr - rpkt); } ret = send(ntpd_sock, rpkt, ptr - rpkt, 0); if (ret == -1) { syslog(LOG_ERR, "cannot send to ntpd: %m"); free(rpkt); return (-1); } + return (0); } /* * Callback if packet arrived from NTPD */ static int ntpd_read(uint16_t *op, uint16_t *associd, u_char **data, size_t *datalen) { u_char pkt[NTPC_MAX + 1]; u_char *ptr, *nptr; u_int n; ssize_t ret; size_t z; u_int offset; /* current offset */ int more; /* more flag */ int sel; struct timeval inc, end, rem; fd_set iset; *datalen = 0; *data = NULL; offset = 0; inc.tv_sec = ntp_timeout / 100; inc.tv_usec = (ntp_timeout % 100) * 1000; (void)gettimeofday(&end, NULL); timeradd(&end, &inc, &end); next: /* compute remaining time */ (void)gettimeofday(&rem, NULL); if (timercmp(&rem, &end, >=)) { /* do a poll */ rem.tv_sec = 0; rem.tv_usec = 0; } else { timersub(&end, &rem, &rem); } /* select */ FD_ZERO(&iset); FD_SET(ntpd_sock, &iset); sel = select(ntpd_sock + 1, &iset, NULL, NULL, &rem); if (sel == -1) { if (errno == EINTR) goto next; syslog(LOG_ERR, "select ntpd_sock: %m"); free(*data); return (-1); } if (sel == 0) { syslog(LOG_ERR, "timeout on NTP connection"); free(*data); return (-1); } /* now read it */ ret = recv(ntpd_sock, pkt, sizeof(pkt), 0); if (ret == -1) { syslog(LOG_ERR, "error reading from ntpd: %m"); free(*data); return (-1); } if (ntp_debug & DBG_DUMP_PKTS) { syslog(LOG_INFO, "got %zd bytes", ret); dump_packet(pkt, (size_t)ret); } ptr = pkt; - if (*ptr != ((NTPC_VERSION << 3) | NTPC_MODE)) { + if ((*ptr & 0x3f) != ((NTPC_VERSION << 3) | NTPC_MODE)) { syslog(LOG_ERR, "unexpected packet version 0x%x", *ptr); free(*data); return (-1); } ptr++; if (!(*ptr & NTPC_BIT_RESP)) { syslog(LOG_ERR, "not a response packet"); return (-1); } if (*ptr & NTPC_BIT_ERROR) { z = *datalen - 12; if (z > NTPC_DMAX) z = NTPC_DMAX; syslog(LOG_ERR, "error response: %.*s", (int)z, pkt + 12); free(*data); return (-1); } more = (*ptr & NTPC_BIT_MORE); *op = *ptr++ & NTPC_OPMASK; /* seqno */ n = *ptr++ << 8; n |= *ptr++; if (n != seqno) { syslog(LOG_ERR, "expecting seqno %u, got %u", seqno, n); free(*data); return (-1); } /* status */ n = *ptr++ << 8; n |= *ptr++; /* associd */ *associd = *ptr++ << 8; *associd |= *ptr++; /* offset */ n = *ptr++ << 8; n |= *ptr++; if (n != offset) { syslog(LOG_ERR, "offset: expecting %u, got %u", offset, n); free(*data); return (-1); } /* count */ n = *ptr++ << 8; n |= *ptr++; if ((size_t)ret < 12 + n) { syslog(LOG_ERR, "packet too short"); return (-1); } nptr = realloc(*data, *datalen + n); if (nptr == NULL) { syslog(LOG_ERR, "cannot allocate memory: %m"); free(*data); return (-1); } *data = nptr; memcpy(*data + offset, ptr, n); *datalen += n; if (!more) return (0); offset += n; goto next; } /* * Send a request and wait for the response */ static int ntpd_dialog(u_int op, u_int associd, const char *vars, u_char **data, size_t *datalen) { uint16_t rassocid; uint16_t rop; if (ntpd_request(op, associd, vars) == -1) return (-1); if (ntpd_read(&rop, &rassocid, data, datalen) == -1) return (-1); if (rop != op) { syslog(LOG_ERR, "bad response op 0x%x", rop); free(data); return (-1); } if (associd != rassocid) { syslog(LOG_ERR, "response for wrong associd"); free(data); return (-1); } return (0); } /* * Callback if packet arrived from NTPD */ static void ntpd_input(int fd __unused, void *arg __unused) { uint16_t associd; uint16_t op; u_char *data; size_t datalen; if (ntpd_read(&op, &associd, &data, &datalen) == -1) return; free(data); } /* * Find the value of a variable */ static int ntpd_parse(u_char **data, size_t *datalen, char **namep, char **valp) { u_char *ptr = *data; u_char *end = ptr + *datalen; char *ptr1; char endc; /* skip leading spaces */ while (ptr < end && isspace((int)*ptr)) ptr++; if (ptr == end) return (0); *namep = ptr; /* skip to space or '=' or ','*/ while (ptr < end && !isspace((int)*ptr) && *ptr != '=' && *ptr != ',') ptr++; endc = *ptr; *ptr++ = '\0'; /* skip space */ while (ptr < end && isspace((int)*ptr)) ptr++; if (ptr == end || endc == ',') { /* no value */ *valp = NULL; *datalen -= ptr - *data; *data = ptr; return (1); } if (*ptr == '"') { /* quoted */ ptr++; *valp = ptr; while (ptr < end && *ptr != '"') ptr++; if (ptr == end) return (0); *ptr++ = '\0'; /* find comma */ while (ptr < end && isspace((int)*ptr) && *ptr == ',') ptr++; } else { *valp = ptr; /* skip to end of value */ while (ptr < end && *ptr != ',') ptr++; /* remove trailing blanks */ for (ptr1 = ptr; ptr1 > *valp; ptr1--) if (!isspace((int)ptr1[-1])) break; *ptr1 = '\0'; if (ptr < end) ptr++; } *datalen -= ptr - *data; *data = ptr; return (1); } /* * Parse an int32 value */ static int val_parse_int32(const char *val, int32_t *p, int32_t min, int32_t max, int base) { long n; char *end; errno = 0; n = strtol(val, &end, base); if (errno != 0 || *end != '\0') return (0); if (n < min || n > max) return (0); *p = (int32_t)n; return (1); } /* * Parse an uint32 value */ static int val_parse_uint32(const char *val, uint32_t *p, uint32_t min, uint32_t max, int base) { u_long n; char *end; errno = 0; n = strtoul(val, &end, base); if (errno != 0 || *end != '\0') return (0); if (n < min || n > max) return (0); *p = (uint32_t)n; return (1); } /* * Parse a double */ static int val_parse_double(const char *val, double *p) { char *end; errno = 0; *p = strtod(val, &end); if (errno != 0 || *end != '\0') return (0); return (1); } static int val_parse_ts(const char *val, char *buf) { int r, n; u_int i, f; if (strlen(val) > 2 && val[0] == '0' && val[1] == 'x') { /* hex format */ r = sscanf(val + 2, "%x.%x%n", &i, &f, &n); if (r != 2 || (size_t)n != strlen(val + 2)) return (0); } else { /* probably decimal */ r = sscanf(val, "%d.%d%n", &i, &f, &n); if (r != 2 || (size_t)n != strlen(val)) return (0); } buf[0] = i >> 24; buf[1] = i >> 16; buf[2] = i >> 8; buf[3] = i >> 0; buf[4] = f >> 24; buf[5] = f >> 16; buf[6] = f >> 8; buf[7] = f >> 0; return (1); } /* * Parse an IP address. This resolves non-numeric names. */ static int val_parse_ip(const char *val, u_char ip[4]) { int r, n, error; struct addrinfo hints, *res0; - struct sockaddr_in *sin; + struct sockaddr_in *sin_local; r = sscanf(val, "%hhd.%hhd.%hhd.%hhd%n", &ip[0], &ip[1], &ip[2], &ip[3], &n); if (n == 4 && (size_t)n == strlen(val)) return (0); memset(ip, 0, 4); memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET; hints.ai_socktype = SOCK_DGRAM; error = getaddrinfo(val, NULL, &hints, &res0); if (error) { syslog(LOG_ERR, "%s: %s", val, gai_strerror(error)); return (-1); } if (res0 == NULL) { syslog(LOG_ERR, "%s: no address", val); return (-1); } - sin = (struct sockaddr_in *)(void *)res0->ai_addr; - ip[3] = sin->sin_addr.s_addr >> 24; - ip[2] = sin->sin_addr.s_addr >> 16; - ip[1] = sin->sin_addr.s_addr >> 8; - ip[0] = sin->sin_addr.s_addr >> 0; + sin_local = (struct sockaddr_in *)(void *)res0->ai_addr; + ip[3] = sin_local->sin_addr.s_addr >> 24; + ip[2] = sin_local->sin_addr.s_addr >> 16; + ip[1] = sin_local->sin_addr.s_addr >> 8; + ip[0] = sin_local->sin_addr.s_addr >> 0; freeaddrinfo(res0); return (0); } /* * Fetch system info */ static int fetch_sysinfo(void) { u_char *data; u_char *ptr; size_t datalen; char *name; char *val; if (ntpd_dialog(NTPC_OP_READVAR, 0, "leap,stratum,precision,rootdelay,rootdispersion,refid,reftime," "poll,peer,clock,system,processor,jitter,stability", &data, &datalen)) return (-1); /* clear info */ sysb_leap = 0; sysb_stratum = 0; sysb_precision = 0; free(sys_rootdelay); sys_rootdelay = NULL; free(sys_rootdispersion); sys_rootdispersion = NULL; free(sys_refid); sys_refid = NULL; sysb_reftime = 0; sysb_poll = 0; sysb_peer = 0; sysb_clock = 0; free(sys_system); sys_system = NULL; free(sys_processor); sys_processor = NULL; sysb_jitter = 0; sysb_stability = 0; ptr = data; while (ntpd_parse(&ptr, &datalen, &name, &val)) { if (ntp_debug & DBG_DUMP_VARS) syslog(LOG_DEBUG, "%s: '%s'='%s'", __func__, name, val); if (strcmp(name, "leap") == 0 || strcmp(name, "sys.leap") == 0) { sysb_leap = val_parse_int32(val, &sys_leap, 0, 3, 2); } else if (strcmp(name, "stratum") == 0 || strcmp(name, "sys.stratum") == 0) { sysb_stratum = val_parse_int32(val, &sys_stratum, 0, 255, 0); } else if (strcmp(name, "precision") == 0 || strcmp(name, "sys.precision") == 0) { sysb_precision = val_parse_int32(val, &sys_precision, INT32_MIN, INT32_MAX, 0); } else if (strcmp(name, "rootdelay") == 0 || strcmp(name, "sys.rootdelay") == 0) { sys_rootdelay = strdup(val); } else if (strcmp(name, "rootdispersion") == 0 || strcmp(name, "sys.rootdispersion") == 0) { sys_rootdispersion = strdup(val); } else if (strcmp(name, "refid") == 0 || strcmp(name, "sys.refid") == 0) { sys_refid = strdup(val); } else if (strcmp(name, "reftime") == 0 || strcmp(name, "sys.reftime") == 0) { sysb_reftime = val_parse_ts(val, sys_reftime); } else if (strcmp(name, "poll") == 0 || strcmp(name, "sys.poll") == 0) { sysb_poll = val_parse_int32(val, &sys_poll, INT32_MIN, INT32_MAX, 0); } else if (strcmp(name, "peer") == 0 || strcmp(name, "sys.peer") == 0) { sysb_peer = val_parse_uint32(val, &sys_peer, 0, UINT32_MAX, 0); } else if (strcmp(name, "clock") == 0 || strcmp(name, "sys.clock") == 0) { sysb_clock = val_parse_ts(val, sys_clock); } else if (strcmp(name, "system") == 0 || strcmp(name, "sys.system") == 0) { sys_system = strdup(val); } else if (strcmp(name, "processor") == 0 || strcmp(name, "sys.processor") == 0) { sys_processor = strdup(val); } else if (strcmp(name, "jitter") == 0 || strcmp(name, "sys.jitter") == 0) { sysb_jitter = val_parse_double(val, &sys_jitter); } else if (strcmp(name, "stability") == 0 || strcmp(name, "sys.stability") == 0) { sysb_stability = val_parse_double(val, &sys_stability); } } free(data); return (0); } static int parse_filt(char *val, uint16_t associd, int which) { char *w; int cnt; struct filt *f; cnt = 0; for (w = strtok(val, " \t"); w != NULL; w = strtok(NULL, " \t")) { TAILQ_FOREACH(f, &filts, link) if (f->index.subs[0] == associd && f->index.subs[1] == (asn_subid_t)(cnt + 1)) break; if (f == NULL) { f = malloc(sizeof(*f)); memset(f, 0, sizeof(*f)); f->index.len = 2; f->index.subs[0] = associd; f->index.subs[1] = cnt + 1; INSERT_OBJECT_OID(f, &filts); } switch (which) { case 0: f->offset = strdup(w); break; case 1: f->delay = strdup(w); break; case 2: f->dispersion = strdup(w); break; default: abort(); } cnt++; } return (cnt); } /* * Fetch the complete peer list */ static int fetch_peers(void) { u_char *data, *pdata, *ptr; size_t datalen, pdatalen; int i; struct peer *p; struct filt *f; uint16_t associd; char *name, *val; /* free the old list */ while ((p = TAILQ_FIRST(&peers)) != NULL) { TAILQ_REMOVE(&peers, p, link); free(p->rootdelay); free(p->rootdispersion); free(p->refid); free(p->offset); free(p->delay); free(p->dispersion); free(p); } while ((f = TAILQ_FIRST(&filts)) != NULL) { TAILQ_REMOVE(&filts, f, link); free(f->offset); free(f->delay); free(f->dispersion); free(f); } /* fetch the list of associations */ if (ntpd_dialog(NTPC_OP_READSTAT, 0, NULL, &data, &datalen)) return (-1); for (i = 0; i < (int)(datalen / 4); i++) { associd = data[4 * i + 0] << 8; associd |= data[4 * i + 1] << 0; /* ask for the association variables */ if (ntpd_dialog(NTPC_OP_READVAR, associd, "config,srcadr,srcport,dstadr,dstport,leap,hmode,stratum," "hpoll,ppoll,precision,rootdelay,rootdispersion,refid," "reftime,org,rec,xmt,reach,timer,offset,delay,dispersion," "filtdelay,filtoffset,filtdisp", &pdata, &pdatalen)) { free(data); return (-1); } /* now save and parse the data */ p = malloc(sizeof(*p)); if (p == NULL) { free(data); syslog(LOG_ERR, "%m"); return (-1); } memset(p, 0, sizeof(*p)); p->index = associd; INSERT_OBJECT_INT(p, &peers); ptr = pdata; while (ntpd_parse(&ptr, &pdatalen, &name, &val)) { if (ntp_debug & DBG_DUMP_VARS) syslog(LOG_DEBUG, "%s: '%s'='%s'", __func__, name, val); if (strcmp(name, "config") == 0 || strcmp(name, "peer.config") == 0) { val_parse_int32(val, &p->config, 0, 1, 0); } else if (strcmp(name, "srcadr") == 0 || strcmp(name, "peer.srcadr") == 0) { val_parse_ip(val, p->srcadr); } else if (strcmp(name, "srcport") == 0 || strcmp(name, "peer.srcport") == 0) { val_parse_uint32(val, &p->srcport, 1, 65535, 0); } else if (strcmp(name, "dstadr") == 0 || strcmp(name, "peer.dstadr") == 0) { val_parse_ip(val, p->dstadr); } else if (strcmp(name, "dstport") == 0 || strcmp(name, "peer.dstport") == 0) { val_parse_uint32(val, &p->dstport, 1, 65535, 0); } else if (strcmp(name, "leap") == 0 || strcmp(name, "peer.leap") == 0) { val_parse_int32(val, &p->leap, 0, 3, 2); } else if (strcmp(name, "hmode") == 0 || strcmp(name, "peer.hmode") == 0) { val_parse_int32(val, &p->hmode, 0, 7, 0); } else if (strcmp(name, "stratum") == 0 || strcmp(name, "peer.stratum") == 0) { val_parse_int32(val, &p->stratum, 0, 255, 0); } else if (strcmp(name, "ppoll") == 0 || strcmp(name, "peer.ppoll") == 0) { val_parse_int32(val, &p->ppoll, INT32_MIN, INT32_MAX, 0); } else if (strcmp(name, "hpoll") == 0 || strcmp(name, "peer.hpoll") == 0) { val_parse_int32(val, &p->hpoll, INT32_MIN, INT32_MAX, 0); } else if (strcmp(name, "precision") == 0 || strcmp(name, "peer.precision") == 0) { val_parse_int32(val, &p->hpoll, INT32_MIN, INT32_MAX, 0); } else if (strcmp(name, "rootdelay") == 0 || strcmp(name, "peer.rootdelay") == 0) { p->rootdelay = strdup(val); } else if (strcmp(name, "rootdispersion") == 0 || strcmp(name, "peer.rootdispersion") == 0) { p->rootdispersion = strdup(val); } else if (strcmp(name, "refid") == 0 || strcmp(name, "peer.refid") == 0) { p->refid = strdup(val); } else if (strcmp(name, "reftime") == 0 || strcmp(name, "sys.reftime") == 0) { val_parse_ts(val, p->reftime); } else if (strcmp(name, "org") == 0 || strcmp(name, "sys.org") == 0) { val_parse_ts(val, p->orgtime); } else if (strcmp(name, "rec") == 0 || strcmp(name, "sys.rec") == 0) { val_parse_ts(val, p->rcvtime); } else if (strcmp(name, "xmt") == 0 || strcmp(name, "sys.xmt") == 0) { val_parse_ts(val, p->xmttime); } else if (strcmp(name, "reach") == 0 || strcmp(name, "peer.reach") == 0) { val_parse_uint32(val, &p->reach, 0, 65535, 0); } else if (strcmp(name, "timer") == 0 || strcmp(name, "peer.timer") == 0) { val_parse_int32(val, &p->timer, INT32_MIN, INT32_MAX, 0); } else if (strcmp(name, "offset") == 0 || strcmp(name, "peer.offset") == 0) { p->offset = strdup(val); } else if (strcmp(name, "delay") == 0 || strcmp(name, "peer.delay") == 0) { p->delay = strdup(val); } else if (strcmp(name, "dispersion") == 0 || strcmp(name, "peer.dispersion") == 0) { p->dispersion = strdup(val); } else if (strcmp(name, "filtdelay") == 0 || strcmp(name, "peer.filtdelay") == 0) { p->filt_entries = parse_filt(val, associd, 0); } else if (strcmp(name, "filtoffset") == 0 || strcmp(name, "peer.filtoffset") == 0) { p->filt_entries = parse_filt(val, associd, 1); } else if (strcmp(name, "filtdisp") == 0 || strcmp(name, "peer.filtdisp") == 0) { p->filt_entries = parse_filt(val, associd, 2); } } free(pdata); } free(data); return (0); } /* * System variables - read-only scalars only. */ int op_ntpSystem(struct snmp_context *ctx __unused, 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: if (this_tick > sysinfo_tick) { if (fetch_sysinfo() == -1) return (SNMP_ERR_GENERR); sysinfo_tick = this_tick; } switch (which) { case LEAF_ntpSysLeap: if (!sysb_leap) return (SNMP_ERR_NOSUCHNAME); value->v.integer = sys_leap; break; case LEAF_ntpSysStratum: if (!sysb_stratum) return (SNMP_ERR_NOSUCHNAME); value->v.integer = sys_stratum; break; case LEAF_ntpSysPrecision: if (!sysb_precision) return (SNMP_ERR_NOSUCHNAME); value->v.integer = sys_precision; break; case LEAF_ntpSysRootDelay: if (sys_rootdelay == NULL) return (SNMP_ERR_NOSUCHNAME); return (string_get(value, sys_rootdelay, -1)); case LEAF_ntpSysRootDispersion: if (sys_rootdispersion == NULL) return (SNMP_ERR_NOSUCHNAME); return (string_get(value, sys_rootdispersion, -1)); case LEAF_ntpSysRefId: if (sys_refid == NULL) return (SNMP_ERR_NOSUCHNAME); return (string_get(value, sys_refid, -1)); case LEAF_ntpSysRefTime: if (sysb_reftime == 0) return (SNMP_ERR_NOSUCHNAME); return (string_get(value, sys_reftime, 8)); case LEAF_ntpSysPoll: if (sysb_poll == 0) return (SNMP_ERR_NOSUCHNAME); value->v.integer = sys_poll; break; case LEAF_ntpSysPeer: if (sysb_peer == 0) return (SNMP_ERR_NOSUCHNAME); value->v.uint32 = sys_peer; break; case LEAF_ntpSysClock: if (sysb_clock == 0) return (SNMP_ERR_NOSUCHNAME); return (string_get(value, sys_clock, 8)); case LEAF_ntpSysSystem: if (sys_system == NULL) return (SNMP_ERR_NOSUCHNAME); return (string_get(value, sys_system, -1)); case LEAF_ntpSysProcessor: if (sys_processor == NULL) return (SNMP_ERR_NOSUCHNAME); return (string_get(value, sys_processor, -1)); default: abort(); } return (SNMP_ERR_NOERROR); case SNMP_OP_SET: return (SNMP_ERR_NOT_WRITEABLE); case SNMP_OP_COMMIT: case SNMP_OP_ROLLBACK: abort(); } abort(); } int op_ntpPeersVarTable(struct snmp_context *ctx __unused, struct snmp_value *value, u_int sub, u_int iidx, enum snmp_op op) { asn_subid_t which = value->var.subs[sub - 1]; uint32_t peer; struct peer *t; if (this_tick > peers_tick) { if (fetch_peers() == -1) return (SNMP_ERR_GENERR); peers_tick = this_tick; } switch (op) { case SNMP_OP_GETNEXT: t = NEXT_OBJECT_INT(&peers, &value->var, sub); if (t == NULL) return (SNMP_ERR_NOSUCHNAME); value->var.len = sub + 1; value->var.subs[sub] = t->index; break; case SNMP_OP_GET: t = FIND_OBJECT_INT(&peers, &value->var, sub); if (t == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_SET: if (index_decode(&value->var, sub, iidx, &peer)) return (SNMP_ERR_NO_CREATION); t = FIND_OBJECT_INT(&peers, &value->var, sub); if (t != NULL) return (SNMP_ERR_NOT_WRITEABLE); return (SNMP_ERR_NO_CREATION); case SNMP_OP_COMMIT: case SNMP_OP_ROLLBACK: default: abort(); } /* * Come here for GET and COMMIT */ switch (which) { case LEAF_ntpPeersConfigured: value->v.integer = t->config; break; case LEAF_ntpPeersPeerAddress: return (ip_get(value, t->srcadr)); case LEAF_ntpPeersPeerPort: value->v.uint32 = t->srcport; break; case LEAF_ntpPeersHostAddress: return (ip_get(value, t->dstadr)); case LEAF_ntpPeersHostPort: value->v.uint32 = t->dstport; break; case LEAF_ntpPeersLeap: value->v.integer = t->leap; break; case LEAF_ntpPeersMode: value->v.integer = t->hmode; break; case LEAF_ntpPeersStratum: value->v.integer = t->stratum; break; case LEAF_ntpPeersPeerPoll: value->v.integer = t->ppoll; break; case LEAF_ntpPeersHostPoll: value->v.integer = t->hpoll; break; case LEAF_ntpPeersPrecision: value->v.integer = t->precision; break; case LEAF_ntpPeersRootDelay: return (string_get(value, t->rootdelay, -1)); case LEAF_ntpPeersRootDispersion: return (string_get(value, t->rootdispersion, -1)); case LEAF_ntpPeersRefId: return (string_get(value, t->refid, -1)); case LEAF_ntpPeersRefTime: return (string_get(value, t->reftime, 8)); case LEAF_ntpPeersOrgTime: return (string_get(value, t->orgtime, 8)); case LEAF_ntpPeersReceiveTime: return (string_get(value, t->rcvtime, 8)); case LEAF_ntpPeersTransmitTime: return (string_get(value, t->xmttime, 8)); case LEAF_ntpPeersReach: value->v.uint32 = t->reach; break; case LEAF_ntpPeersTimer: value->v.uint32 = t->timer; break; case LEAF_ntpPeersOffset: return (string_get(value, t->offset, -1)); case LEAF_ntpPeersDelay: return (string_get(value, t->delay, -1)); case LEAF_ntpPeersDispersion: return (string_get(value, t->dispersion, -1)); default: abort(); } return (SNMP_ERR_NOERROR); } int op_ntpFilterPeersVarTable(struct snmp_context *ctx __unused, struct snmp_value *value, u_int sub, u_int iidx, enum snmp_op op) { asn_subid_t which = value->var.subs[sub - 1]; uint32_t peer; struct peer *t; if (this_tick > peers_tick) { if (fetch_peers() == -1) return (SNMP_ERR_GENERR); peers_tick = this_tick; } switch (op) { case SNMP_OP_GETNEXT: t = NEXT_OBJECT_INT(&peers, &value->var, sub); if (t == NULL) return (SNMP_ERR_NOSUCHNAME); value->var.len = sub + 1; value->var.subs[sub] = t->index; break; case SNMP_OP_GET: t = FIND_OBJECT_INT(&peers, &value->var, sub); if (t == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_SET: if (index_decode(&value->var, sub, iidx, &peer)) return (SNMP_ERR_NO_CREATION); t = FIND_OBJECT_INT(&peers, &value->var, sub); if (t != NULL) return (SNMP_ERR_NOT_WRITEABLE); return (SNMP_ERR_NO_CREATION); case SNMP_OP_COMMIT: case SNMP_OP_ROLLBACK: default: abort(); } /* * Come here for GET and COMMIT */ switch (which) { case LEAF_ntpFilterValidEntries: value->v.integer = t->filt_entries; break; default: abort(); } return (SNMP_ERR_NOERROR); } int op_ntpFilterRegisterTable(struct snmp_context *ctx __unused, struct snmp_value *value __unused, u_int sub __unused, u_int iidx __unused, enum snmp_op op __unused) { asn_subid_t which = value->var.subs[sub - 1]; uint32_t peer; uint32_t filt; struct filt *t; if (this_tick > peers_tick) { if (fetch_peers() == -1) return (SNMP_ERR_GENERR); peers_tick = this_tick; } switch (op) { case SNMP_OP_GETNEXT: t = NEXT_OBJECT_OID(&filts, &value->var, sub); if (t == NULL) return (SNMP_ERR_NOSUCHNAME); index_append(&value->var, sub, &t->index); break; case SNMP_OP_GET: t = FIND_OBJECT_OID(&filts, &value->var, sub); if (t == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_SET: if (index_decode(&value->var, sub, iidx, &peer, &filt)) return (SNMP_ERR_NO_CREATION); t = FIND_OBJECT_OID(&filts, &value->var, sub); if (t != NULL) return (SNMP_ERR_NOT_WRITEABLE); return (SNMP_ERR_NO_CREATION); case SNMP_OP_COMMIT: case SNMP_OP_ROLLBACK: default: abort(); } /* * Come here for GET and COMMIT */ switch (which) { case LEAF_ntpFilterPeersOffset: return (string_get(value, t->offset, -1)); case LEAF_ntpFilterPeersDelay: return (string_get(value, t->delay, -1)); case LEAF_ntpFilterPeersDispersion: return (string_get(value, t->dispersion, -1)); default: abort(); } return (SNMP_ERR_NOERROR); } /* * System variables - read-only scalars only. */ int op_begemot_ntp(struct snmp_context *ctx __unused, struct snmp_value *value, u_int sub, u_int iidx __unused, enum snmp_op op) { asn_subid_t which = value->var.subs[sub - 1]; int ret; switch (op) { case SNMP_OP_GETNEXT: abort(); case SNMP_OP_GET: switch (which) { case LEAF_begemotNtpHost: return (string_get(value, ntp_host, -1)); case LEAF_begemotNtpPort: return (string_get(value, ntp_port, -1)); case LEAF_begemotNtpTimeout: value->v.uint32 = ntp_timeout; return (SNMP_ERR_NOERROR); case LEAF_begemotNtpDebug: value->v.uint32 = ntp_debug; return (SNMP_ERR_NOERROR); case LEAF_begemotNtpJitter: if (this_tick > sysinfo_tick) { if (fetch_sysinfo() == -1) return (SNMP_ERR_GENERR); sysinfo_tick = this_tick; } if (!sysb_jitter) return (SNMP_ERR_NOSUCHNAME); value->v.counter64 = sys_jitter / 1000 * (1ULL << 32); return (SNMP_ERR_NOERROR); case LEAF_begemotNtpStability: if (this_tick > sysinfo_tick) { if (fetch_sysinfo() == -1) return (SNMP_ERR_GENERR); sysinfo_tick = this_tick; } if (!sysb_stability) return (SNMP_ERR_NOSUCHNAME); value->v.counter64 = sys_stability * (1ULL << 32); return (SNMP_ERR_NOERROR); } abort(); case SNMP_OP_SET: switch (which) { case LEAF_begemotNtpHost: - /* only at initialisation */ + /* only at initialization */ if (community != COMM_INITIALIZE) return (SNMP_ERR_NOT_WRITEABLE); if ((ret = string_save(value, ctx, -1, &ntp_host)) != SNMP_ERR_NOERROR) return (ret); return (SNMP_ERR_NOERROR); case LEAF_begemotNtpPort: - /* only at initialisation */ + /* only at initialization */ if (community != COMM_INITIALIZE) return (SNMP_ERR_NOT_WRITEABLE); if ((ret = string_save(value, ctx, -1, &ntp_port)) != SNMP_ERR_NOERROR) return (ret); return (SNMP_ERR_NOERROR); case LEAF_begemotNtpTimeout: ctx->scratch->int1 = ntp_timeout; if (value->v.uint32 < 1) return (SNMP_ERR_WRONG_VALUE); ntp_timeout = value->v.integer; return (SNMP_ERR_NOERROR); case LEAF_begemotNtpDebug: ctx->scratch->int1 = ntp_debug; ntp_debug = value->v.integer; return (SNMP_ERR_NOERROR); } abort(); case SNMP_OP_ROLLBACK: switch (which) { case LEAF_begemotNtpHost: string_rollback(ctx, &ntp_host); return (SNMP_ERR_NOERROR); case LEAF_begemotNtpPort: string_rollback(ctx, &ntp_port); return (SNMP_ERR_NOERROR); case LEAF_begemotNtpTimeout: ntp_timeout = ctx->scratch->int1; return (SNMP_ERR_NOERROR); case LEAF_begemotNtpDebug: ntp_debug = ctx->scratch->int1; return (SNMP_ERR_NOERROR); } abort(); case SNMP_OP_COMMIT: switch (which) { case LEAF_begemotNtpHost: string_commit(ctx); return (SNMP_ERR_NOERROR); case LEAF_begemotNtpPort: string_commit(ctx); return (SNMP_ERR_NOERROR); case LEAF_begemotNtpTimeout: case LEAF_begemotNtpDebug: return (SNMP_ERR_NOERROR); } abort(); } abort(); } Index: stable/6/contrib/bsnmp/snmpd/bsnmpd.1 =================================================================== --- stable/6/contrib/bsnmp/snmpd/bsnmpd.1 (revision 157332) +++ stable/6/contrib/bsnmp/snmpd/bsnmpd.1 (revision 157333) @@ -1,271 +1,276 @@ .\" .\" Copyright (c) 2004-2005 .\" Hartmut Brandt. .\" All rights reserved. .\" Copyright (c) 2001-2003 .\" Fraunhofer Institute for Open Communication Systems (FhG Fokus). .\" All rights reserved. .\" .\" Author: Harti Brandt .\" .\" Redistribution 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 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. .\" .\" THIS SOFTWARE IS PROVIDED BY AUTHOR AND 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 AUTHOR OR 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/bsnmpd.1,v 1.9 2005/10/04 08:46:54 brandt_h Exp $ +.\" $Begemot: bsnmp/snmpd/bsnmpd.1,v 1.12 2006/02/27 09:50:03 brandt_h Exp $ .\" -.Dd October 4, 2005 -.Dt SNMPD 1 +.Dd February 27, 2006 +.Dt BSNMPD 1 .Os .Sh NAME .Nm bsnmpd .Nd "simple and extensible 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 Management Protocol). +daemon server the internet SNMP (Simple Network Management Protocol). It is intended to serve 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 <> parentheses. .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 in which the 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 read during initialization 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 depth 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 .Pq Va suboids must be specified. The index consist of elements each separated 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 @MIBSPATH@FOKUS-MIB.txt .It Pa @MIBSPATH@BEGEMOT-MIB.txt .It Pa @MIBSPATH@BEGEMOT-SNMPD.txt The definitions for the MIBs implemented in the daemon. +.It Pa /etc/hosts.allow, /etc/hosts.deny +Access controls that should be enforced by TCP wrappers should be defined here. +Further details are described in +.Xr hosts_access 5 . .El .Sh SEE ALSO -.Xr gensnmptree 1 +.Xr gensnmptree 1 , +.Xr hosts_access 5 .Sh STANDARDS The .Nm conforms to the applicable IETF RFCs. .Sh AUTHORS .An Hartmut Brandt Aq harti@freebsd.org .Sh BUGS Sure. Index: stable/6/contrib/bsnmp/snmpd/config.c =================================================================== --- stable/6/contrib/bsnmp/snmpd/config.c (revision 157332) +++ stable/6/contrib/bsnmp/snmpd/config.c (revision 157333) @@ -1,1367 +1,1367 @@ /* * Copyright (c) 2001-2003 * Fraunhofer Institute for Open Communication Systems (FhG Fokus). * All rights reserved. * * Author: Harti Brandt * * Redistribution 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 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. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND 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 AUTHOR OR 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.24 2005/10/04 11:21:37 brandt_h Exp $ + * $Begemot: bsnmp/snmpd/config.c,v 1.25 2006/02/14 09:04:20 brandt_h Exp $ * * Parse configuration file. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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]; const 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); col = ptr + strlen(ptr) - 1; } 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 %#"QUADXFMT, 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 %"QUADFMT, 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 (ERRPUSH()) return (-1); if (input_open_file(fname, 0) == -1) { syslog(LOG_ERR, "%s: %m", fname); return (-1); } ERRPOP(); community = COMM_INITIALIZE; if ((snmp_ctx = snmp_init_context()) == NULL) { input_close_all(); 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(); snmp_dep_finish(snmp_ctx); 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); m->perm = 1; LIST_INSERT_HEAD(¯os, m, link); return (0); } Index: stable/6/contrib/bsnmp/snmpd/export.c =================================================================== --- stable/6/contrib/bsnmp/snmpd/export.c (revision 157332) +++ stable/6/contrib/bsnmp/snmpd/export.c (revision 157333) @@ -1,374 +1,398 @@ /* * Copyright (c) 2001-2003 * Fraunhofer Institute for Open Communication Systems (FhG Fokus). * All rights reserved. * * Author: Harti Brandt * * Redistribution 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 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. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND 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 AUTHOR OR 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.7 2004/08/06 08:47:11 brandt Exp $ + * $Begemot: bsnmp/snmpd/export.c,v 1.8 2006/02/14 09:04:20 brandt_h Exp $ * * Support functions for modules. */ #include #include #include #include #include #include #include #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); +} + +/* + * Get a string value for a response packet but cut it if it is too long. + */ +int +string_get_max(struct snmp_value *value, const u_char *ptr, ssize_t len, + size_t maxlen) +{ + + if (ptr == NULL) { + value->v.octetstring.len = 0; + value->v.octetstring.octets = NULL; + return (SNMP_ERR_NOERROR); + } + if (len == -1) + len = strlen(ptr); + if ((size_t)len > maxlen) + len = maxlen; 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); } Index: stable/6/contrib/bsnmp/snmpd/main.c =================================================================== --- stable/6/contrib/bsnmp/snmpd/main.c (revision 157332) +++ stable/6/contrib/bsnmp/snmpd/main.c (revision 157333) @@ -1,2375 +1,2378 @@ /* * Copyright (c) 2001-2003 * Fraunhofer Institute for Open Communication Systems (FhG Fokus). * All rights reserved. * * Author: Harti Brandt * * Redistribution 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 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. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND 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 AUTHOR OR 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.97 2005/10/04 14:32:45 brandt_h Exp $ + * $Begemot: bsnmp/snmpd/main.c,v 1.100 2006/02/14 09:04:20 brandt_h Exp $ * * SNMPd main stuff. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef USE_TCPWRAPPERS #include #include #endif +#include "support.h" #include "snmpmod.h" #include "snmpd.h" #include "tree.h" #include "oid.h" #if !defined(INT32_MAX) #define INT32_MAX (0x7fffffff) #endif #define PATH_PID "/var/run/%s.pid" #define PATH_CONFIG "/etc/%s.config" uint64_t this_tick; /* start of processing of current packet (absolute) */ uint64_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 */ VERS_ENABLE_ALL,/* version_enable */ }; 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; /* file names */ static char config_file[MAXPATHLEN + 1]; static char pid_file[MAXPATHLEN + 1]; #ifndef USE_LIBBEGEMOT /* event context */ static evContext evctx; #endif /* 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\ "; /* hosts_access(3) request */ #ifdef USE_TCPWRAPPERS static struct request_info req; #endif /* transports */ extern const struct transport_def udp_trans; extern const struct transport_def lsock_trans; struct transport_list transport_list = TAILQ_HEAD_INITIALIZER(transport_list); /* 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)) == NULL) { syslog(LOG_CRIT, "cannot allocate buffer"); if (tx) snmpd_stats.noTxbuf++; else snmpd_stats.noRxbuf++; return (NULL); } return (buf); } /* * Return the buffer size. */ size_t buf_size(int tx) { return (tx ? snmpd.txbuf : snmpd.rxbuf); } /* * Prepare a PDU for output */ void snmp_output(struct snmp_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); } /* * 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_pdu *pdu, int32_t *ip, size_t *pdulen) { struct asn_buf b; enum snmp_code code; enum snmpd_input_err ret; int sret; b.asn_cptr = buf; b.asn_len = len; /* look whether we have enough bytes for the entire PDU. */ switch (sret = snmp_pdu_snoop(&b)) { case 0: return (SNMPD_INPUT_TRUNC); case -1: snmpd_stats.inASNParseErrs++; return (SNMPD_INPUT_FAILED); } b.asn_len = *pdulen = (size_t)sret; code = snmp_pdu_decode(&b, pdu, ip); snmpd_stats.inPkts++; ret = SNMPD_INPUT_OK; switch (code) { case SNMP_CODE_FAILED: snmpd_stats.inASNParseErrs++; return (SNMPD_INPUT_FAILED); case SNMP_CODE_BADVERS: bad_vers: 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: switch (pdu->version) { case SNMP_V1: if (!(snmpd.version_enable & VERS_ENABLE_V1)) goto bad_vers; break; case SNMP_V2c: if (!(snmpd.version_enable & VERS_ENABLE_V2C)) goto bad_vers; break; case SNMP_Verr: goto bad_vers; } 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, (struct snmp_value *)NULL); ret = SNMPD_INPUT_BAD_COMM; } else 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(); } /* * Insert a port into the right place in the transport's table of ports */ void trans_insert_port(struct transport *t, struct tport *port) { struct tport *p; TAILQ_FOREACH(p, &t->table, link) { if (asn_compare_oid(&p->index, &port->index) > 0) { TAILQ_INSERT_BEFORE(p, port, link); return; } } port->transport = t; TAILQ_INSERT_TAIL(&t->table, port, link); } /* * Remove a port from a transport's list */ void trans_remove_port(struct tport *port) { TAILQ_REMOVE(&port->transport->table, port, link); } /* * Find a port on a transport's list */ struct tport * trans_find_port(struct transport *t, const struct asn_oid *idx, u_int sub) { return (FIND_OBJECT_OID(&t->table, idx, sub)); } /* * Find next port on a transport's list */ struct tport * trans_next_port(struct transport *t, const struct asn_oid *idx, u_int sub) { return (NEXT_OBJECT_OID(&t->table, idx, sub)); } /* * Return first port */ struct tport * trans_first_port(struct transport *t) { return (TAILQ_FIRST(&t->table)); } /* * Iterate through all ports until a function returns a 0. */ struct tport * trans_iter_port(struct transport *t, int (*func)(struct tport *, intptr_t), intptr_t arg) { struct tport *p; TAILQ_FOREACH(p, &t->table, link) if (func(p, arg) == 0) return (p); return (NULL); } /* * Register a transport */ int trans_register(const struct transport_def *def, struct transport **pp) { u_int i; char or_descr[256]; if ((*pp = malloc(sizeof(**pp))) == NULL) return (SNMP_ERR_GENERR); /* construct index */ (*pp)->index.len = strlen(def->name) + 1; (*pp)->index.subs[0] = strlen(def->name); for (i = 0; i < (*pp)->index.subs[0]; i++) (*pp)->index.subs[i + 1] = def->name[i]; (*pp)->vtab = def; if (FIND_OBJECT_OID(&transport_list, &(*pp)->index, 0) != NULL) { free(*pp); return (SNMP_ERR_INCONS_VALUE); } /* register module */ snprintf(or_descr, sizeof(or_descr), "%s transport mapping", def->name); if (((*pp)->or_index = or_register(&def->id, or_descr, NULL)) == 0) { free(*pp); return (SNMP_ERR_GENERR); } INSERT_OBJECT_OID((*pp), &transport_list); TAILQ_INIT(&(*pp)->table); return (SNMP_ERR_NOERROR); } /* * Unregister transport */ int trans_unregister(struct transport *t) { if (!TAILQ_EMPTY(&t->table)) return (SNMP_ERR_INCONS_VALUE); or_unregister(t->or_index); TAILQ_REMOVE(&transport_list, t, link); return (SNMP_ERR_NOERROR); } /* * File descriptor support */ #ifdef USE_LIBBEGEMOT static void input(int fd, int mask __unused, void *uap) #else static void input(evContext ctx __unused, void *uap, int fd, int mask __unused) #endif { struct fdesc *f = uap; (*f->func)(fd, f->udata); } void fd_suspend(void *p) { struct fdesc *f = p; #ifdef USE_LIBBEGEMOT if (f->id >= 0) { poll_unregister(f->id); f->id = -1; } #else if (evTestID(f->id)) { (void)evDeselectFD(evctx, f->id); evInitID(&f->id); } #endif } int fd_resume(void *p) { struct fdesc *f = p; int err; #ifdef USE_LIBBEGEMOT if (f->id >= 0) return (0); if ((f->id = poll_register(f->fd, input, f, POLL_IN)) < 0) { err = errno; syslog(LOG_ERR, "select fd %d: %m", f->fd); errno = err; return (-1); } #else 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); } #endif 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; #ifdef USE_LIBBEGEMOT f->id = -1; #else evInitID(&f->id); #endif 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; } } /* * Consume a message from the input buffer */ static void snmp_input_consume(struct port_input *pi) { if (!pi->stream) { /* always consume everything */ pi->length = 0; return; } if (pi->consumed >= pi->length) { /* all bytes consumed */ pi->length = 0; return; } memmove(pi->buf, pi->buf + pi->consumed, pi->length - pi->consumed); pi->length -= pi->consumed; } struct credmsg { struct cmsghdr hdr; struct cmsgcred cred; }; static void check_priv(struct port_input *pi, struct msghdr *msg) { struct credmsg *cmsg; struct xucred ucred; socklen_t ucredlen; pi->priv = 0; if (msg->msg_controllen == sizeof(*cmsg)) { /* process explicitly sends credentials */ cmsg = (struct credmsg *)msg->msg_control; pi->priv = (cmsg->cred.cmcred_euid == 0); return; } /* ok, obtain the accept time credentials */ ucredlen = sizeof(ucred); if (getsockopt(pi->fd, 0, LOCAL_PEERCRED, &ucred, &ucredlen) == 0 && ucredlen >= sizeof(ucred) && ucred.cr_version == XUCRED_VERSION) pi->priv = (ucred.cr_uid == 0); } /* * Input from a stream socket. */ static int recv_stream(struct port_input *pi) { struct msghdr msg; struct iovec iov[1]; ssize_t len; struct credmsg cmsg; if (pi->buf == NULL) { /* no buffer yet - allocate one */ if ((pi->buf = buf_alloc(0)) == NULL) { /* ups - could not get buffer. Return an error * the caller must close the transport. */ return (-1); } pi->buflen = buf_size(0); pi->consumed = 0; pi->length = 0; } /* try to get a message */ msg.msg_name = pi->peer; msg.msg_namelen = pi->peerlen; msg.msg_iov = iov; msg.msg_iovlen = 1; if (pi->cred) { msg.msg_control = &cmsg; msg.msg_controllen = sizeof(cmsg); cmsg.hdr.cmsg_len = sizeof(cmsg); cmsg.hdr.cmsg_level = SOL_SOCKET; cmsg.hdr.cmsg_type = SCM_CREDS; } else { msg.msg_control = NULL; msg.msg_controllen = 0; } msg.msg_flags = 0; iov[0].iov_base = pi->buf + pi->length; iov[0].iov_len = pi->buflen - pi->length; len = recvmsg(pi->fd, &msg, 0); if (len == -1 || len == 0) /* receive error */ return (-1); pi->length += len; if (pi->cred) check_priv(pi, &msg); return (0); } /* * Input from a datagram socket. * Each receive should return one datagram. */ static int recv_dgram(struct port_input *pi) { u_char embuf[1000]; struct msghdr msg; struct iovec iov[1]; ssize_t len; struct credmsg cmsg; if (pi->buf == NULL) { /* no buffer yet - allocate one */ if ((pi->buf = buf_alloc(0)) == NULL) { /* ups - could not get buffer. Read away input * and drop it */ (void)recvfrom(pi->fd, embuf, sizeof(embuf), 0, NULL, NULL); /* return error */ return (-1); } pi->buflen = buf_size(0); } /* try to get a message */ msg.msg_name = pi->peer; msg.msg_namelen = pi->peerlen; msg.msg_iov = iov; msg.msg_iovlen = 1; if (pi->cred) { msg.msg_control = &cmsg; msg.msg_controllen = sizeof(cmsg); cmsg.hdr.cmsg_len = sizeof(cmsg); cmsg.hdr.cmsg_level = SOL_SOCKET; cmsg.hdr.cmsg_type = SCM_CREDS; } else { msg.msg_control = NULL; msg.msg_controllen = 0; } msg.msg_flags = 0; iov[0].iov_base = pi->buf; iov[0].iov_len = pi->buflen; len = recvmsg(pi->fd, &msg, 0); if (len == -1 || len == 0) /* receive error */ return (-1); if (msg.msg_flags & MSG_TRUNC) { /* truncated - drop */ snmpd_stats.silentDrops++; snmpd_stats.inTooLong++; return (-1); } pi->length = (size_t)len; if (pi->cred) check_priv(pi, &msg); return (0); } /* * Input from a socket */ int snmpd_input(struct port_input *pi, struct tport *tport) { u_char *sndbuf; size_t sndlen; struct snmp_pdu pdu; enum snmpd_input_err ierr, ferr; enum snmpd_proxy_err perr; int32_t vi; int ret; ssize_t slen; #ifdef USE_TCPWRAPPERS char client[16]; #endif /* get input depending on the transport */ if (pi->stream) { ret = recv_stream(pi); } else { ret = recv_dgram(pi); } if (ret == -1) return (-1); #ifdef USE_TCPWRAPPERS /* * In case of AF_INET{6} peer, do hosts_access(5) check. */ if (inet_ntop(pi->peer->sa_family, &((const struct sockaddr_in *)(const void *)pi->peer)->sin_addr, client, sizeof(client)) != NULL) { request_set(&req, RQ_CLIENT_ADDR, client, 0); if (hosts_access(&req) == 0) { syslog(LOG_ERR, "refused connection from %.500s", eval_client(&req)); return (-1); } } else syslog(LOG_ERR, "inet_ntop(): %m"); #endif /* * Handle input */ ierr = snmp_input_start(pi->buf, pi->length, "SNMP", &pdu, &vi, &pi->consumed); if (ierr == SNMPD_INPUT_TRUNC) { /* need more bytes. This is ok only for streaming transports. * but only if we have not reached bufsiz yet. */ if (pi->stream) { if (pi->length == buf_size(0)) { snmpd_stats.silentDrops++; return (-1); } return (0); } snmpd_stats.silentDrops++; return (-1); } /* 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) { /* for streaming transports this is fatal */ if (pi->stream) return (-1); snmp_input_consume(pi); return (0); } if (ierr == SNMPD_INPUT_BAD_COMM) { snmp_input_consume(pi); return (0); } /* * 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, tport->transport, &tport->index, pi->peer, pi->peerlen, ierr, vi, !pi->cred || pi->priv); switch (perr) { case SNMPD_PROXY_OK: snmp_input_consume(pi); return (0); case SNMPD_PROXY_REJ: break; case SNMPD_PROXY_DROP: snmp_input_consume(pi); snmp_pdu_free(&pdu); snmpd_stats.proxyDrops++; return (0); case SNMPD_PROXY_BADCOMM: snmp_input_consume(pi); snmp_pdu_free(&pdu); snmpd_stats.inBadCommunityNames++; if (snmpd.auth_traps) snmp_send_trap(&oid_authenticationFailure, (struct snmp_value *)NULL); return (0); case SNMPD_PROXY_BADCOMMUSE: snmp_input_consume(pi); snmp_pdu_free(&pdu); snmpd_stats.inBadCommunityUses++; if (snmpd.auth_traps) snmp_send_trap(&oid_authenticationFailure, (struct snmp_value *)NULL); return (0); } } /* * 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); snmp_input_consume(pi); return (0); } /* * Check community */ if ((pi->cred && !pi->priv && pdu.type == SNMP_PDU_SET) || (community != COMM_WRITE && (pdu.type == SNMP_PDU_SET || community != COMM_READ))) { snmpd_stats.inBadCommunityUses++; snmp_pdu_free(&pdu); snmp_input_consume(pi); if (snmpd.auth_traps) snmp_send_trap(&oid_authenticationFailure, (struct snmp_value *)NULL); return (0); } /* * Execute it. */ if ((sndbuf = buf_alloc(1)) == NULL) { snmpd_stats.silentDrops++; snmp_pdu_free(&pdu); snmp_input_consume(pi); return (0); } ferr = snmp_input_finish(&pdu, pi->buf, pi->length, sndbuf, &sndlen, "SNMP", ierr, vi, NULL); if (ferr == SNMPD_INPUT_OK) { slen = sendto(pi->fd, sndbuf, sndlen, 0, pi->peer, pi->peerlen); if (slen == -1) syslog(LOG_ERR, "sendto: %m"); else if ((size_t)slen != sndlen) syslog(LOG_ERR, "sendto: short write %zu/%zu", sndlen, (size_t)slen); } snmp_pdu_free(&pdu); free(sndbuf); snmp_input_consume(pi); return (0); } /* * Send a PDU to a given port */ void snmp_send_port(void *targ, const struct asn_oid *port, struct snmp_pdu *pdu, const struct sockaddr *addr, socklen_t addrlen) { struct transport *trans = targ; struct tport *tp; u_char *sndbuf; size_t sndlen; ssize_t len; TAILQ_FOREACH(tp, &trans->table, link) if (asn_compare_oid(port, &tp->index) == 0) break; if (tp == 0) return; if ((sndbuf = buf_alloc(1)) == NULL) return; snmp_output(pdu, sndbuf, &sndlen, "SNMP PROXY"); len = trans->vtab->send(tp, sndbuf, sndlen, addr, addrlen); if (len == -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); } /* * Close an input source */ void snmpd_input_close(struct port_input *pi) { if (pi->id != NULL) fd_deselect(pi->id); if (pi->fd >= 0) (void)close(pi->fd); if (pi->buf != NULL) free(pi->buf); } /* * Dump internal state. */ #ifdef USE_LIBBEGEMOT static void info_func(void) #else static void info_func(evContext ctx __unused, void *uap __unused, const void *tag __unused) #endif { 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 */ #ifdef USE_LIBBEGEMOT static void config_func(void) #else static void config_func(evContext ctx __unused, void *uap __unused, const void *tag __unused) #endif { 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) { /* allow clean-up */ 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); } static void trans_stop(void) { struct transport *t; TAILQ_FOREACH(t, &transport_list, link) (void)t->vtab->stop(1); } /* * 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(*optp, *options) == 0) return (i); return (-1); } int main(int argc, char *argv[]) { int opt; FILE *fp; int background = 1; struct tport *p; const char *prefix = "snmpd"; struct lmodule *m; char *value, *option; struct transport *t; #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); + else + 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(); #ifdef USE_TCPWRAPPERS /* * Initialize hosts_access(3) handler. */ request_init(&req, RQ_DAEMON, "snmpd", 0); sock_methods(&req); #endif /* * 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, NULL); (void)comm_define(2, "SNMP write", NULL, NULL); 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(); this_tick = get_ticks(); start_tick = this_tick; /* start transports */ if (atexit(trans_stop) == -1) { syslog(LOG_ERR, "atexit failed: %m"); exit(1); } if (udp_trans.start() != SNMP_ERR_NOERROR) syslog(LOG_WARNING, "cannot start UDP transport"); if (lsock_trans.start() != SNMP_ERR_NOERROR) syslog(LOG_WARNING, "cannot start LSOCK transport"); #ifdef USE_LIBBEGEMOT if (debug.evdebug > 0) rpoll_trace = 1; #else if (evCreate(&evctx)) { syslog(LOG_ERR, "evCreate: %m"); exit(1); } if (debug.evdebug > 0) evSetDebug(evctx, 10, stderr); #endif if (read_config(config_file, NULL)) { syslog(LOG_ERR, "error in config file"); exit(1); } TAILQ_FOREACH(t, &transport_list, link) TAILQ_FOREACH(p, &t->table, link) t->vtab->init_port(p); 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); if (atexit(term) == -1) { syslog(LOG_ERR, "atexit failed: %m"); (void)remove(pid_file); exit(0); } } 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, (struct snmp_value *)NULL); while ((m = TAILQ_FIRST(&modules_start)) != NULL) { m->flags &= ~LM_ONSTARTLIST; TAILQ_REMOVE(&modules_start, m, start); lm_start(m); } for (;;) { #ifndef USE_LIBBEGEMOT evEvent event; #endif struct lmodule *mod; TAILQ_FOREACH(mod, &lmodules, link) if (mod->config->idle != NULL) (*mod->config->idle)(); #ifndef USE_LIBBEGEMOT 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); } #else poll_dispatch(1); #endif if (work != 0) { block_sigs(); if (work & WORK_DOINFO) { #ifdef USE_LIBBEGEMOT info_func(); #else if (evWaitFor(evctx, &work, info_func, NULL, NULL) == -1) { syslog(LOG_ERR, "evWaitFor: %m"); exit(1); } #endif } if (work & WORK_RECONFIG) { #ifdef USE_LIBBEGEMOT config_func(); #else if (evWaitFor(evctx, &work, config_func, NULL, NULL) == -1) { syslog(LOG_ERR, "evWaitFor: %m"); exit(1); } #endif } work = 0; unblock_sigs(); #ifndef USE_LIBBEGEMOT if (evDo(evctx, &work) == -1) { syslog(LOG_ERR, "evDo: %m"); exit(1); } #endif } } return (0); } uint64_t get_ticks() { struct timeval tv; uint64_t ret; if (gettimeofday(&tv, NULL)) abort(); ret = tv.tv_sec * 100ULL + tv.tv_usec / 10000ULL; return (ret); } /* * Timer support */ /* * Trampoline for the non-repeatable timers. */ #ifdef USE_LIBBEGEMOT static void tfunc(int tid __unused, void *uap) #else static void tfunc(evContext ctx __unused, void *uap, struct timespec due __unused, struct timespec inter __unused) #endif { struct timer *tp = uap; LIST_REMOVE(tp, link); tp->func(tp->udata); free(tp); } /* * Trampoline for the repeatable timers. */ #ifdef USE_LIBBEGEMOT static void trfunc(int tid __unused, void *uap) #else static void trfunc(evContext ctx __unused, void *uap, struct timespec due __unused, struct timespec inter __unused) #endif { struct timer *tp = uap; tp->func(tp->udata); } /* * Start a one-shot timer */ void * timer_start(u_int ticks, void (*func)(void *), void *udata, struct lmodule *mod) { struct timer *tp; #ifndef USE_LIBBEGEMOT struct timespec due; #endif if ((tp = malloc(sizeof(struct timer))) == NULL) { syslog(LOG_CRIT, "out of memory for timer"); exit(1); } #ifndef USE_LIBBEGEMOT due = evAddTime(evNowTime(), evConsTime(ticks / 100, (ticks % 100) * 10000)); #endif tp->udata = udata; tp->owner = mod; tp->func = func; LIST_INSERT_HEAD(&timer_list, tp, link); #ifdef USE_LIBBEGEMOT if ((tp->id = poll_start_timer(ticks * 10, 0, tfunc, tp)) < 0) { syslog(LOG_ERR, "cannot set timer: %m"); exit(1); } #else if (evSetTimer(evctx, tfunc, tp, due, evConsTime(0, 0), &tp->id) == -1) { syslog(LOG_ERR, "cannot set timer: %m"); exit(1); } #endif return (tp); } /* * Start a repeatable timer. When used with USE_LIBBEGEMOT the first argument * is currently ignored and the initial number of ticks is set to the * repeat number of ticks. */ void * timer_start_repeat(u_int ticks __unused, u_int repeat_ticks, void (*func)(void *), void *udata, struct lmodule *mod) { struct timer *tp; #ifndef USE_LIBBEGEMOT struct timespec due; struct timespec inter; #endif if ((tp = malloc(sizeof(struct timer))) == NULL) { syslog(LOG_CRIT, "out of memory for timer"); exit(1); } #ifndef USE_LIBBEGEMOT due = evAddTime(evNowTime(), evConsTime(ticks / 100, (ticks % 100) * 10000)); inter = evConsTime(repeat_ticks / 100, (repeat_ticks % 100) * 10000); #endif tp->udata = udata; tp->owner = mod; tp->func = func; LIST_INSERT_HEAD(&timer_list, tp, link); #ifdef USE_LIBBEGEMOT if ((tp->id = poll_start_timer(repeat_ticks * 10, 1, trfunc, tp)) < 0) { syslog(LOG_ERR, "cannot set timer: %m"); exit(1); } #else if (evSetTimer(evctx, trfunc, tp, due, inter, &tp->id) == -1) { syslog(LOG_ERR, "cannot set timer: %m"); exit(1); } #endif return (tp); } /* * Stop a timer. */ void timer_stop(void *p) { struct timer *tp = p; LIST_REMOVE(tp, link); #ifdef USE_LIBBEGEMOT poll_stop_timer(tp->id); #else if (evClearTimer(evctx, tp->id) == -1) { syslog(LOG_ERR, "cannot stop timer: %m"); exit(1); } #endif 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; if (!(snmp_trace & LOG_SNMP_ERRORS)) return; 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; if (!(snmp_trace & LOG_ASN1_ERRORS)) return; 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, "tree_merge: %m"); return (-1); } tree = xtree; memcpy(&tree[tree_size], ntree, sizeof(*tree) * nsize); for (i = 0; i < nsize; i++) tree[tree_size + i].tree_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].tree_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 initialization 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 = (uint32_t)(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; } } Index: stable/6/contrib/bsnmp/snmpd/snmpd.config =================================================================== --- stable/6/contrib/bsnmp/snmpd/snmpd.config (revision 157332) +++ stable/6/contrib/bsnmp/snmpd/snmpd.config (revision 157333) @@ -1,103 +1,103 @@ # # Copyright (c) 2001-2003 # Fraunhofer Institute for Open Communication Systems (FhG Fokus). # All rights reserved. # # Author: Harti Brandt # # Redistribution 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 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. # # THIS SOFTWARE IS PROVIDED BY AUTHOR AND 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 AUTHOR OR 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.15 2005/02/25 11:50:43 brandt_h Exp $ +# $Begemot: bsnmp/snmpd/snmpd.config,v 1.16 2006/02/14 09:04:20 brandt_h 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" # Uncomment the line below that sets the community string # to enable write access. write := "geheim" trap := "mytrap" # # Configuration # %snmpd begemotSnmpdDebugDumpPdus = 2 begemotSnmpdDebugSyslogPri = 7 # # Set the read and write communities. # # The default value of the community strings is NULL (note, that this is # different from the empty string). This disables both read and write access. # To enable read access only the read community string must be set. Setting # the write community string enables both read and write access with that # string. # # Be sure to understand the security implications of SNMPv2 - the community # strings are readable on the wire! # 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 begemotSnmpdLocalPortType."/var/run/snmpd.sock" = 4 # 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" Index: stable/6/contrib/bsnmp/snmpd/snmpmod.3 =================================================================== --- stable/6/contrib/bsnmp/snmpd/snmpmod.3 (revision 157332) +++ stable/6/contrib/bsnmp/snmpd/snmpmod.3 (revision 157333) @@ -1,921 +1,928 @@ .\" .\" Copyright (c) 2004-2005 .\" Hartmut Brandt. .\" All rights reserved. .\" Copyright (c) 2001-2003 .\" Fraunhofer Institute for Open Communication Systems (FhG Fokus). .\" All rights reserved. .\" .\" Author: Harti Brandt .\" .\" Redistribution 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 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. .\" .\" THIS SOFTWARE IS PROVIDED BY AUTHOR AND 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 AUTHOR OR 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.14 2005/10/04 13:30:35 brandt_h Exp $ .\" -.Dd October 4, 2005 +.Dd February 27, 2006 .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_start_repeat , .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_get_max , .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 uint64_t this_tick ; .Vt extern uint64_t start_tick ; .Ft uint64_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_start_repeat "u_int ticks" "u_int repeat_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 .Fo snmp_input_start .Fa "const u_char *buf" "size_t len" "const char *source" .Fa "struct snmp_pdu *pdu" "int32_t *ip" "size_t *pdulen" .Fc .Ft enum snmpd_input_err .Fo snmp_input_finish .Fa "struct snmp_pdu *pdu" "const u_char *rcvbuf" .Fa "size_t rcvlen" "u_char *sndbuf" "size_t *sndlen" "const char *source" .Fa "enum snmpd_input_err ierr" "int32_t ip" "void *data" .Fc .Ft void .Fo snmp_output .Fa "struct snmp_pdu *pdu" "u_char *sndbuf" "size_t *sndlen" .Fa "const char *dest" .Fc .Ft void .Fo snmp_send_port .Fa "void *trans" "const struct asn_oid *port" .Fa "struct snmp_pdu *pdu" "const struct sockaddr *addr" "socklen_t addrlen" .Fc .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 int +.Fn string_get_max "struct snmp_value *val" "const u_char *str" "ssize_t len" "size_t maxlen" .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 bsnmpd 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 bsnmpd 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 identifies 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 *, void *, 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 whose 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 initialization 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 existence 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 . Explicitly 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; uint32_t services; uint32_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. These timers are one-shot, they are not restarted. Repeatable timers are started with .Fn timer_start_repeat which takes an additional argument .Fa repeat_ticks . The argument .Fa ticks gives the number of ticks until the first execution of the callback, while .Fa repeat_ticks is the number of ticks between invocations of the callback. Note, that currently the number of initial ticks silently may be set identical to the number of ticks between callback invocations. 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 read 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 synchronous 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. .It Er SNMPD_INPUT_TRUNC The buffer appears to contain a valid begin of a PDU, but is too short. For streaming transports this means that the caller must save what he already has and trying to obtain more input and reissue this input to the function. For datagram transports this means that part of the datagram was lost and the input should be ignored. .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 3 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 transport and 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. +The function +.It Fn string_get_max +can be used instead of +.Nf stringto ensure that the returned string has a certain maximum length. 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 uint64_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 gensnmptree 1 , .Xr bsnmpd 1 , .Xr bsnmpagent 3 , .Xr bsnmpclient 3 , .Xr bsnmplib 3 .Sh STANDARDS This implementation conforms to the applicable IETF RFCs and ITU-T recommendations. .Sh AUTHORS .An Hartmut Brandt Aq harti@freebsd.org Index: stable/6/contrib/bsnmp/snmpd/snmpmod.h =================================================================== --- stable/6/contrib/bsnmp/snmpd/snmpmod.h (revision 157332) +++ stable/6/contrib/bsnmp/snmpd/snmpmod.h (revision 157333) @@ -1,422 +1,423 @@ /* * Copyright (c) 2001-2003 * Fraunhofer Institute for Open Communication Systems (FhG Fokus). * All rights reserved. * * Author: Harti Brandt * * Redistribution 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 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. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND 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 AUTHOR OR 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.31 2005/10/04 13:30:36 brandt_h Exp $ + * $Begemot: bsnmp/snmpd/snmpmod.h,v 1.32 2006/02/14 09:04:20 brandt_h Exp $ * * SNMP daemon data and functions exported to modules. */ #ifndef snmpmod_h_ #define snmpmod_h_ #include #include #include #include #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, an asn_oid * or an ordering function. */ #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 INSERT_OBJECT_FUNC_LINK(PTR, LIST, LINK, FUNC) do { \ __typeof (PTR) _lelem; \ \ TAILQ_FOREACH(_lelem, (LIST), LINK) \ if ((FUNC)(_lelem, (PTR)) > 0) \ break; \ if (_lelem == NULL) \ TAILQ_INSERT_TAIL((LIST), (PTR), LINK); \ else \ TAILQ_INSERT_BEFORE(_lelem, (PTR), LINK); \ } while (0) #define INSERT_OBJECT_FUNC_LINK_REV(PTR, LIST, HEAD, LINK, FUNC) do { \ __typeof (PTR) _lelem; \ \ TAILQ_FOREACH_REVERSE(_lelem, (LIST), HEAD, LINK) \ if ((FUNC)(_lelem, (PTR)) < 0) \ break; \ if (_lelem == NULL) \ TAILQ_INSERT_HEAD((LIST), (PTR), LINK); \ else \ TAILQ_INSERT_AFTER((LIST), _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); \ }) #define FIND_OBJECT_FUNC_LINK(LIST, OID, SUB, LINK, FUNC) ({ \ __typeof (TAILQ_FIRST(LIST)) _lelem; \ \ TAILQ_FOREACH(_lelem, (LIST), LINK) \ if ((FUNC)(OID, SUB, _lelem) == 0) \ break; \ (_lelem); \ }) #define NEXT_OBJECT_FUNC_LINK(LIST, OID, SUB, LINK, FUNC) ({ \ __typeof (TAILQ_FIRST(LIST)) _lelem; \ \ TAILQ_FOREACH(_lelem, (LIST), LINK) \ if ((FUNC)(OID, SUB, _lelem) < 0) \ 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 INSERT_OBJECT_FUNC_REV(PTR, LIST, HEAD, FUNC) \ INSERT_OBJECT_FUNC_LINK_REV(PTR, LIST, HEAD, link, FUNC) #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 FIND_OBJECT_FUNC(LIST, OID, SUB, FUNC) \ FIND_OBJECT_FUNC_LINK(LIST, OID, SUB, link, FUNC) #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) #define NEXT_OBJECT_FUNC(LIST, OID, SUB, FUNC) \ NEXT_OBJECT_FUNC_LINK(LIST, OID, SUB, link, FUNC) struct lmodule; /* The tick when the program was started. This is the absolute time of * the start in 100th of a second. */ extern uint64_t start_tick; /* The tick when the current packet was received. This is the absolute * time in 100th of second. */ extern uint64_t this_tick; /* Get the current absolute time in 100th of a second. */ uint64_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, /* need more data (truncated packet) */ SNMPD_INPUT_TRUNC, /* unknown community */ SNMPD_INPUT_BAD_COMM, }; /* * 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 initialization 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_pdu *, void *, const struct asn_oid *, const struct sockaddr *, socklen_t, enum snmpd_input_err, int32_t, int); /* 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_start_repeat(u_int, 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_pdu *, int32_t *, size_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_pdu *, u_char *, size_t *, const char *); void snmp_send_port(void *, const struct asn_oid *, struct snmp_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); +int string_get_max(struct snmp_value *, const u_char *, ssize_t, size_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 Index: stable/6/usr.sbin/bsnmpd/bsnmpd/Makefile =================================================================== --- stable/6/usr.sbin/bsnmpd/bsnmpd/Makefile (revision 157332) +++ stable/6/usr.sbin/bsnmpd/bsnmpd/Makefile (revision 157333) @@ -1,45 +1,45 @@ # $FreeBSD$ # # Author: Harti Brandt CONTRIB=${.CURDIR}/../../../contrib/bsnmp .PATH: ${CONTRIB}/snmpd PROG= bsnmpd SRCS= main.c action.c config.c export.c trap.c trans_udp.c trans_lsock.c SRCS+= oid.h tree.c tree.h XSYM= snmpMIB begemotSnmpdModuleTable begemotSnmpd begemotTrapSinkTable \ sysUpTime snmpTrapOID coldStart authenticationFailure \ begemotSnmpdTransUdp begemotSnmpdTransLsock begemotSnmpdLocalPortTable CLEANFILES= oid.h tree.c tree.h MAN= bsnmpd.1 snmpmod.3 WARNS?= 6 NO_WERROR= FILESGROUPS= BMIBS DEFS BMIBS= FOKUS-MIB.txt BEGEMOT-MIB.txt BEGEMOT-SNMPD.txt BMIBSDIR= ${SHAREDIR}/snmp/mibs DEFS= tree.def DEFSDIR= ${SHAREDIR}/snmp/defs CFLAGS+= -I${CONTRIB}/lib -I${CONTRIB}/snmpd -I. -DUSE_LIBBEGEMOT CFLAGS+= -DUSE_TCPWRAPPERS -DQUADFMT='"llu"' -DQUADXFMT='"llx"' -CFLAGS+= -DHAVE_STDINT_H -DHAVE_INTTYPES_H +CFLAGS+= -DHAVE_STDINT_H -DHAVE_INTTYPES_H -DHAVE_ERR_H -DHAVE_STRLCPY DPADD= ${LIBBEGEMOT} ${LIBBSNMP} LDADD= -lbegemot -lbsnmp -lwrap LDFLAGS= -export-dynamic oid.h: tree.def gensnmptree -e ${XSYM} < ${.ALLSRC} > ${.TARGET} .ORDER: tree.c tree.h tree.c tree.h: tree.def gensnmptree -l < ${.ALLSRC} MANFILTER= sed -e 's%@MODPATH@%${LIBDIR}/%g' \ -e 's%@DEFPATH@%${DEFSDIR}/%g' \ -e 's%@MIBSPATH@%${BMIBSDIR}/%g' .include Index: stable/6/usr.sbin/bsnmpd/modules/snmp_mibII/Makefile =================================================================== --- stable/6/usr.sbin/bsnmpd/modules/snmp_mibII/Makefile (revision 157332) +++ stable/6/usr.sbin/bsnmpd/modules/snmp_mibII/Makefile (revision 157333) @@ -1,21 +1,23 @@ # $FreeBSD$ # # Author: Harti Brandt CONTRIB= ${.CURDIR}/../../../../contrib/bsnmp .PATH: ${CONTRIB}/snmp_mibII MOD= mibII -SRCS= mibII.c mibII_ifmib.c mibII_ip.c mibII_interfaces.c \ - mibII_ipaddr.c mibII_ifstack.c mibII_rcvaddr.c \ - mibII_nettomedia.c mibII_tcp.c mibII_udp.c mibII_route.c +SRCS= mibII.c mibII_begemot.c mibII_ifmib.c mibII_ifstack.c \ + mibII_interfaces.c mibII_ip.c mibII_ipaddr.c mibII_nettomedia.c \ + mibII_rcvaddr.c mibII_route.c mibII_tcp.c mibII_udp.c XSYM= ipAddrTable ifTable ifRcvAddressEntry ifMIB ipMIB tcpMIB udpMIB \ ipForward ifIndex linkDown linkUp MAN= snmp_mibII.3 CFLAGS+= -I${CONTRIB}/lib -I${CONTRIB}/snmpd +CFLAGS+= -DHAVE_ERR_H -DHAVE_GETADDRINFO -DHAVE_STRLCPY -DHAVE_SYS_TREE_H DEFS= ${MOD}_tree.def INCS= snmp_${MOD}.h +BMIBS= BEGEMOT-IP-MIB.txt BEGEMOT-MIB2-MIB.txt .include