Index: head/contrib/bsnmp/gensnmpdef/gensnmpdef.1 =================================================================== --- head/contrib/bsnmp/gensnmpdef/gensnmpdef.1 (revision 310647) +++ head/contrib/bsnmp/gensnmpdef/gensnmpdef.1 (revision 310648) @@ -1,85 +1,85 @@ .\" .\" Copyright (C) 2004-2006 .\" 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: gensnmpdef.1 383 2006-05-30 07:40:49Z brandt_h $ .\" .Dd May 28, 2006 .Dt GENSNMPDEF 1 .Os .Sh NAME .Nm gensnmpdef .Nd "generate a MIB description file from MIBs" .Sh SYNOPSIS .Nm .Op Fl hEe .Op Fl c Ar cut .Ar name Op Ar ... .Sh DESCRIPTION The .Nm utility is used to create an initial MIB description file from one or more MIBs. The description file must be edited to be actually useful for feeding it into .Xr gensnmptree 1 . .Pp The following options are available: .Bl -tag -width indent .It Fl c Ar cut Specify the number of initial sub-oids that should be omitted from the tree in the output. .Xr gensnmptree 1 automatically adds 1.3.6 in front of all OIDs so the default value of 3 is just correct in most cases. .It Fl E Generate typedefs for named enumerations. These are enumerations defined via the TEXTUAL-CONVENTION macro. The normal tree output is suppressed. .It Fl e Generate typedefs for unnamed enumerations. These are enumerations defined in the SYNTAX clause of an OBJECT-TYPE macro. The name of the enumeration is formed by appending the string .Ql Type to the name of the object. The normal tree output is suppressed. .It Fl h Print a short help text and exit. .El .Pp .Nm does no attempt on sorting the OID tree so in case of complex and non-standard MIBs it is necessary to sort the tree in the resulting definition file by hand. .Sh SEE ALSO .Xr snmpd 1 .Sh AUTHORS .An Hartmut Brandt Aq harti@FreeBSD.org .Sh BUGS The utility is by no means bullet-proof and may fail for complex or non-standard MIBs. Its output is expected to be edited by hand. Index: head/contrib/bsnmp/gensnmpdef/gensnmpdef.c =================================================================== --- head/contrib/bsnmp/gensnmpdef/gensnmpdef.c (revision 310647) +++ head/contrib/bsnmp/gensnmpdef/gensnmpdef.c (revision 310648) @@ -1,583 +1,583 @@ -/* +/* * Copyright (C) 2004-2006 * 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: gensnmpdef.c 383 2006-05-30 07:40:49Z brandt_h $ */ #include #include #include #include #include #include #include #include #include static const char usgtxt[] = "Usage: gensnmpdef [-hEe] [-c ] MIB [MIB ...]\n" "Options:\n" " -c specify the number of initial sub-oids to cut from the oids\n" " -E extract named enum types. Print a typedef for all enums defined\n" " in syntax clauses of normal objects. Suppress normal output.\n" " -e extract unnamed enum types. Print a typedef for all enums defined\n" " as textual conventions. Suppress normal output.\n" " -h print this help\n" "MIBs are searched according to the libsmi(3) search rules and can\n" "be specified either by path or module name\n"; static SmiNode *last_node; static u_int cut = 3; struct tdef { char *name; SLIST_ENTRY(tdef) link; }; static SLIST_HEAD(, tdef) tdefs = SLIST_HEAD_INITIALIZER(tdefs); static int do_typedef = 0; static void print_node(SmiNode *n, u_int level); static void save_node(SmiNode *n) { if (n != NULL) last_node = n; } static void pindent(u_int level) { if (level >= cut) printf("%*s", (level - cut) * 2, ""); } static void print_name(SmiNode *n) { char *p; for (p = n->name; *p != '\0'; p++) { if (*p == '-') printf("_"); else printf("%c", *p); } } static u_int close_node(u_int n, u_int level) { while (n--) { pindent(level); level--; if (level >= cut) printf(")\n"); } return (level); } static u_int open_node(const SmiNode *n, u_int level, SmiNode **last) { SmiNode *n1; u_int i; if (*last != NULL) { for (i = 0; i < (*last)->oidlen - 1; i++) { if (i >= n->oidlen) { level = close_node((*last)->oidlen - n->oidlen, level); break; } if ((*last)->oid[i] != n->oid[i]) break; } if (i < (*last)->oidlen - 1) level = close_node((*last)->oidlen - 1 - i, level - 1) + 1; } while (level < n->oidlen - 1) { if (level >= cut) { pindent(level); printf("(%u", n->oid[level]); n1 = smiGetNodeByOID(level + 1, n->oid); printf(" "); print_name(n1); printf("\n"); } level++; } return (level); } static const char *const type_names[] = { [SMI_BASETYPE_UNKNOWN] = "UNKNOWN_TYPE", [SMI_BASETYPE_INTEGER32] = "INTEGER", [SMI_BASETYPE_OCTETSTRING] = "OCTETSTRING", [SMI_BASETYPE_OBJECTIDENTIFIER] = "OID", [SMI_BASETYPE_UNSIGNED32] = "UNSIGNED32", [SMI_BASETYPE_INTEGER64] = "INTEGER64", [SMI_BASETYPE_UNSIGNED64] = "UNSIGNED64", [SMI_BASETYPE_FLOAT32] = "FLOAT32", [SMI_BASETYPE_FLOAT64] = "FLOAT64", [SMI_BASETYPE_FLOAT128] = "FLOAT128", [SMI_BASETYPE_ENUM] = "ENUM", [SMI_BASETYPE_BITS] = "BITS", }; static const char *const type_map[] = { "Gauge32", "GAUGE", "Gauge", "GAUGE", "TimeTicks", "TIMETICKS", "Counter32", "COUNTER", "Counter", "COUNTER", "Counter64", "COUNTER64", "Integer32", "INTEGER32", "IpAddress", "IPADDRESS", NULL }; static void print_enum(SmiType *t) { SmiNamedNumber *nnum; printf(" ("); for (nnum = smiGetFirstNamedNumber(t); nnum != NULL; nnum = smiGetNextNamedNumber(nnum)) printf(" %ld %s", nnum->value.value.integer32, nnum->name); printf(" )"); } static void print_type(SmiNode *n) { SmiType *type; u_int m; type = smiGetNodeType(n); assert(type != NULL); if (type->name != NULL) { for (m = 0; type_map[m] != NULL; m += 2) if (strcmp(type_map[m], type->name) == 0) { printf("%s", type_map[m + 1]); return; } } printf("%s", type_names[type->basetype]); if (type->basetype == SMI_BASETYPE_ENUM || type->basetype == SMI_BASETYPE_BITS) print_enum(type); else if (type->basetype == SMI_BASETYPE_OCTETSTRING && type->name != NULL) printf(" | %s", type->name); } static void print_access(SmiAccess a) { if (a == SMI_ACCESS_READ_ONLY) printf(" GET"); else if (a == SMI_ACCESS_READ_WRITE) printf(" GET SET"); } static void print_scalar(SmiNode *n, u_int level) { SmiNode *p; assert (n->nodekind == SMI_NODEKIND_SCALAR); save_node(n); pindent(level); printf("(%u ", n->oid[level]); print_name(n); printf(" "); print_type(n); /* generate the operation from the parent node name */ p = smiGetParentNode(n); printf(" op_%s", p->name); print_access(n->access); - + printf(")\n"); } static void print_notification(SmiNode *n, u_int level) { assert (n->nodekind == SMI_NODEKIND_NOTIFICATION); save_node(n); pindent(level); printf("(%u ", n->oid[level]); print_name(n); printf(" OID"); printf(" op_%s)\n", n->name); } static void print_col(SmiNode *n, u_int level) { assert (n->nodekind == SMI_NODEKIND_COLUMN); save_node(n); pindent(level); printf("(%u ", n->oid[level]); print_name(n); printf(" "); print_type(n); print_access(n->access); printf(")\n"); } static void print_index(SmiNode *row) { SmiElement *e; e = smiGetFirstElement(row); while (e != NULL) { printf(" "); print_type(smiGetElementNode(e)); e = smiGetNextElement(e); } } static void print_table(SmiNode *n, u_int level) { SmiNode *row, *col, *rel; assert (n->nodekind == SMI_NODEKIND_TABLE); save_node(n); pindent(level); printf("(%u ", n->oid[level]); print_name(n); printf("\n"); row = smiGetFirstChildNode(n); if (row->nodekind != SMI_NODEKIND_ROW) errx(1, "%s: kind %u, not row", __func__, row->nodekind); save_node(n); pindent(level + 1); printf("(%u ", row->oid[level + 1]); print_name(row); printf(" :"); /* index */ rel = smiGetRelatedNode(row); switch (row->indexkind) { case SMI_INDEX_INDEX: print_index(row); break; case SMI_INDEX_AUGMENT: if (rel == NULL) errx(1, "%s: cannot find augemented table", row->name); print_index(rel); break; default: errx(1, "%s: cannot handle index kind %u", row->name, row->indexkind); } printf(" op_%s", n->name); printf("\n"); col = smiGetFirstChildNode(row); while (col != NULL) { print_col(col, level + 2); col = smiGetNextChildNode(col); } pindent(level + 1); printf(")\n"); pindent(level); printf(")\n"); } static void print_it(SmiNode *n, u_int level) { switch (n->nodekind) { case SMI_NODEKIND_NODE: print_node(n, level); break; case SMI_NODEKIND_SCALAR: print_scalar(n, level); break; case SMI_NODEKIND_TABLE: print_table(n, level); break; case SMI_NODEKIND_COMPLIANCE: case SMI_NODEKIND_GROUP: save_node(n); break; case SMI_NODEKIND_NOTIFICATION: print_notification(n, level); break; default: errx(1, "cannot handle %u nodes", n->nodekind); } } static void print_node(SmiNode *n, u_int level) { assert (n->nodekind == SMI_NODEKIND_NODE); save_node(n); pindent(level); printf("(%u ", n->oid[level]); print_name(n); printf("\n"); n = smiGetFirstChildNode(n); while (n != NULL) { print_it(n, level + 1); n = smiGetNextChildNode(n); } pindent(level); printf(")\n"); } static void save_typdef(char *name) { struct tdef *t; t = malloc(sizeof(struct tdef)); if (t == NULL) err(1, NULL); memset(t, 0 , sizeof(struct tdef)); t->name = name; SLIST_INSERT_HEAD(&tdefs, t, link); } static void tdefs_cleanup(void) { struct tdef *t; while ((t = SLIST_FIRST(&tdefs)) != NULL) { SLIST_REMOVE_HEAD(&tdefs, link); free(t); } } static void print_enum_typedef(SmiType *t) { SmiNamedNumber *nnum; - + for (nnum = smiGetFirstNamedNumber(t); nnum != NULL; nnum = smiGetNextNamedNumber(nnum)) { printf("\t%ld %s\n" , nnum->value.value.integer32, nnum->name); } } static void print_stype(SmiNode *n) { SmiType *type; struct tdef *t = NULL; - + type = smiGetNodeType(n); assert(type != NULL); - + if (type->basetype == SMI_BASETYPE_ENUM) { if (do_typedef == 'e' && type->name != NULL) { SLIST_FOREACH(t, &tdefs, link) { if (strcmp(t->name, type->name) == 0) return; } save_typdef(type->name); printf("typedef %s ENUM (\n", type->name); } else if (do_typedef == 'E' && type->name == NULL) printf("typedef %sType ENUM (\n", n->name); else return; - + print_enum_typedef(type); printf(")\n\n"); } else if (type->basetype == SMI_BASETYPE_BITS) { if (do_typedef == 'e' && type->name != NULL) { SLIST_FOREACH(t, &tdefs, link) { if (strcmp(t->name, type->name) == 0) return; } save_typdef(type->name); printf("typedef %s BITS (\n", type->name); } else if (do_typedef == 'E' && type->name == NULL) printf("typedef %sType BITS (\n", n->name); else return; print_enum_typedef(type); printf(")\n\n"); } } static void print_typdefs(SmiNode *n) { SmiNode *p; - + p = n; n = smiGetFirstChildNode(n); while (n != NULL) { switch (n->nodekind) { case SMI_NODEKIND_SCALAR: case SMI_NODEKIND_COLUMN: print_stype(n); break; case SMI_NODEKIND_COMPLIANCE: case SMI_NODEKIND_GROUP: save_node(n); return; default: break; } n = smiGetNextChildNode(n); } save_node(p); } int main(int argc, char *argv[]) { int opt; int flags; SmiModule **mods; char *name; SmiNode *n, *last; u_int level; long u; char *end; smiInit(NULL); while ((opt = getopt(argc, argv, "c:Eeh")) != -1) switch (opt) { case 'c': errno = 0; u = strtol(optarg, &end, 10); if (errno != 0) err(1, "argument to -c"); if (*end != '\0') err(1, "%s: not a number", optarg); if (u < 0 || u > 5) err(1, "%s: out of range", optarg); cut = (u_int)u; break; case 'E': do_typedef = 'E'; break; case 'e': do_typedef = 'e'; break; case 'h': fprintf(stderr, usgtxt); exit(0); } argc -= optind; argv += optind; flags = smiGetFlags(); flags |= SMI_FLAG_ERRORS; smiSetFlags(flags); mods = malloc(sizeof(mods[0]) * argc); if (mods == NULL) err(1, NULL); for (opt = 0; opt < argc; opt++) { if ((name = smiLoadModule(argv[opt])) == NULL) err(1, "%s: cannot load", argv[opt]); mods[opt] = smiGetModule(name); } level = 0; last = NULL; for (opt = 0; opt < argc; opt++) { n = smiGetFirstNode(mods[opt], SMI_NODEKIND_ANY); for (;;) { if (do_typedef == 0) { level = open_node(n, level, &last); print_it(n, level); last = n; } else print_typdefs(n); if (last_node == NULL || (n = smiGetNextNode(last_node, SMI_NODEKIND_ANY)) == NULL) break; } } if (last != NULL && do_typedef == 0) level = close_node(last->oidlen - 1, level - 1); else if (do_typedef != 0) tdefs_cleanup(); return (0); } Index: head/contrib/bsnmp/gensnmptree/gensnmptree.1 =================================================================== --- head/contrib/bsnmp/gensnmptree/gensnmptree.1 (revision 310647) +++ head/contrib/bsnmp/gensnmptree/gensnmptree.1 (revision 310648) @@ -1,246 +1,246 @@ .\" .\" Copyright (c) 2001-2005 .\" Fraunhofer Institute for Open Communication Systems (FhG Fokus). .\" All rights reserved. .\" Copyright (c) 2006 .\" 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: gensnmptree.1 383 2006-05-30 07:40:49Z brandt_h $ .\" .Dd May 26, 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 dEehlt .Op Fl I Ar directory .Op Fl i Ar infile .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 bsnmpd 1 daemon or for module writers. The second form may be used by SNMP client program writers. .Pp If none of the options .Fl e , .Fl E or .Fl t are 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 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, the table itself and definitions for all enums. .Pp The following options are available: .Bl -tag -width ".Fl E" .It Fl d Switch on debugging. .It Fl E Extract enumerations and bit constructs. In this mode the tool emits a header file that contains for each type given on the command line a C-enum definition and a preprocessor define that may be used to map values to strings. .It Fl e .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 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 .It Fl h Print a short help page. .It Fl I Ar directory Add the named directory to the include path just before the standard include directories. .It Fl i Ar infile Read from the named file instead of standard input. .It Fl l Generate local preprocessor includes. This is used for bootstrapping .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 := top | top file top := tree | typedef | include tree := head elements ')' entry := head ':' index STRING elements ')' leaf := head type STRING ACCESS ')' column := head type ACCESS ')' type := BASETYPE | BASETYPE '|' subtype | enum | bits subtype := STRING enum := ENUM '(' value ')' bits := BITS '(' value ')' value := INT STRING | INT STRING value head := '(' INT STRING elements := EMPTY | elements element element := tree | leaf | column index := type | index type typedef := 'typedef' STRING type include := 'include' filespec filespec := '"' STRING '"' | '<' STRING '>' .Ed .Pp .Ar BASETYPE 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, underscores and minuses, that is not one of the keywords. .Pp The .Ar typedef directive associates a type with a single name. .Pp The .Ar include directive is replaced by the contents of the named file. .Sh EXAMPLES The following MIB description describes the system group: .Bd -literal -offset indent include "tc.def" typedef AdminStatus ENUM ( 1 up 2 down ) (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 bsnmpd 1 .Sh AUTHORS .An Hartmut Brandt Aq harti@FreeBSD.org Index: head/contrib/bsnmp/gensnmptree/gensnmptree.c =================================================================== --- head/contrib/bsnmp/gensnmptree/gensnmptree.c (revision 310647) +++ head/contrib/bsnmp/gensnmptree/gensnmptree.c (revision 310648) @@ -1,1554 +1,1554 @@ /* * Copyright (c) 2001-2003 * Fraunhofer Institute for Open Communication Systems (FhG Fokus). * All rights reserved. * * Copyright (c) 2004-2006 * 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: gensnmptree.c 383 2006-05-30 07:40:49Z brandt_h $ * * Generate OID table from table description. * * Syntax is: * --------- * file := top | top file * * top := tree | typedef | include * * tree := head elements ')' * * entry := head ':' index STRING elements ')' * * leaf := head type STRING ACCESS ')' * * column := head type ACCESS ')' * * type := BASETYPE | BASETYPE '|' subtype | enum | bits * * subtype := STRING * * enum := ENUM '(' value ')' * * bits := BITS '(' value ')' * * value := optminus INT STRING | optminus INT STRING value * * optminus := '-' | EMPTY * * head := '(' INT STRING * * elements := EMPTY | elements element * * element := tree | leaf | column * * index := type | index type * * typedef := 'typedef' STRING type * * include := 'include' filespec * * filespec := '"' STRING '"' | '<' STRING '>' */ #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 = ""; /* if true generate local include paths */ static int localincs = 0; /* if true print tokens */ static int debug; static const char usgtxt[] = "\ Generate SNMP tables.\n\ usage: gensnmptree [-dEehlt] [-I directory] [-i infile] [-p prefix]\n\ [name]...\n\ options:\n\ -d debug mode\n\ -E extract the named enums and bits only\n\ -e extract the named oids or enums\n\ -h print this info\n\ -I directory add directory to include path\n\ -i ifile read from the named file instead of stdin\n\ -l generate local include directives\n\ -p prefix prepend prefix to file and variable names\n\ -t generated a .def file\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); struct enums { const char *name; long value; TAILQ_ENTRY(enums) link; }; struct type { const char *name; const char *from_fname; u_int from_lno; u_int syntax; int is_enum; int is_bits; TAILQ_HEAD(, enums) enums; LIST_ENTRY(type) link; }; static LIST_HEAD(, type) types = LIST_HEAD_INITIALIZER(types); static void report(const char *, ...) __dead2 __printflike(1, 2); static void report_node(const struct node *, const char *, ...) __dead2 __printflike(2, 3); /************************************************************ * * 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); } static char * savestr(const char *s) { if (s == NULL) return (NULL); return (strcpy(xalloc(strlen(s) + 1), s)); } /************************************************************ * * Input stack */ struct input { FILE *fp; u_int lno; char *fname; char *path; LIST_ENTRY(input) link; }; static LIST_HEAD(, input) inputs = LIST_HEAD_INITIALIZER(inputs); static struct input *input = NULL; #define MAX_PATHS 100 static u_int npaths = 2; static u_int stdpaths = 2; static const char *paths[MAX_PATHS + 1] = { "/usr/share/snmp/defs", "/usr/local/share/snmp/defs", NULL }; static int pbchar = -1; static void path_new(const char *path) { if (npaths >= MAX_PATHS) report("too many -I directives"); memmove(&paths[npaths - stdpaths + 1], &paths[npaths - stdpaths], sizeof(path[0]) * stdpaths); paths[npaths - stdpaths] = savestr(path); npaths++; } static void input_new(FILE *fp, const char *path, const char *fname) { struct input *ip; ip = xalloc(sizeof(*ip)); ip->fp = fp; ip->lno = 1; ip->fname = savestr(fname); ip->path = savestr(path); LIST_INSERT_HEAD(&inputs, ip, link); input = ip; } static void input_close(void) { if (input == NULL) return; fclose(input->fp); free(input->fname); free(input->path); LIST_REMOVE(input, link); free(input); input = LIST_FIRST(&inputs); } static FILE * tryopen(const char *path, const char *fname) { char *fn; FILE *fp; if (path == NULL) fn = savestr(fname); else { fn = xalloc(strlen(path) + strlen(fname) + 2); sprintf(fn, "%s/%s", path, fname); } fp = fopen(fn, "r"); free(fn); return (fp); } static void input_fopen(const char *fname, int loc) { FILE *fp; char *path; u_int p; if (fname[0] == '/') { if ((fp = tryopen(NULL, fname)) != NULL) { input_new(fp, NULL, fname); return; } } else { if (loc) { if (input == NULL) path = NULL; else path = input->path; if ((fp = tryopen(path, fname)) != NULL) { input_new(fp, NULL, fname); return; } } for (p = 0; paths[p] != NULL; p++) if ((fp = tryopen(paths[p], fname)) != NULL) { input_new(fp, paths[p], fname); return; } } report("cannot open '%s'", fname); } static int tgetc(void) { int c; if (pbchar != -1) { c = pbchar; pbchar = -1; return (c); } for (;;) { if (input == NULL) return (EOF); if ((c = getc(input->fp)) != EOF) return (c); input_close(); } } static void tungetc(int c) { if (pbchar != -1) abort(); pbchar = c; } /************************************************************ * * 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 */ TOK_ENUM, /* enum token (kind of a type) */ TOK_TYPEDEF, /* typedef directive */ TOK_DEFTYPE, /* defined type */ TOK_INCLUDE, /* include directive */ TOK_FILENAME, /* filename ("foo.bar" or ) */ TOK_BITS, /* bits token (kind of a type) */ }; 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 }, { "ENUM", TOK_ENUM, SNMP_SYNTAX_INTEGER }, { "BITS", TOK_BITS, SNMP_SYNTAX_OCTETSTRING }, { "typedef", TOK_TYPEDEF, 0 }, { "include", TOK_INCLUDE, 0 }, { NULL, 0, 0 } }; /* arbitrary upper limit on node names and function names */ #define MAXSTR 1000 char str[MAXSTR]; u_long val; /* integer values */ int all_cond; /* all conditions are true */ int saved_token = -1; /* * Report an error and exit. */ static void report(const char *fmt, ...) { va_list ap; int c; va_start(ap, fmt); fprintf(stderr, "line %u: ", input->lno); vfprintf(stderr, fmt, ap); fprintf(stderr, "\n"); fprintf(stderr, "context: \""); while ((c = tgetc()) != 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 (savestr(str)); } /* * Get the next token from input. */ static int gettoken_internal(void) { int c; struct type *t; if (saved_token != -1) { c = saved_token; saved_token = -1; return (c); } again: /* * Skip any whitespace before the next token */ while ((c = tgetc()) != EOF) { if (c == '\n') input->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 = tgetc()) != EOF) { if (c == '\n') { input->lno++; goto again; } } report("unexpected EOF in comment"); } /* * Single character tokens */ if (strchr("():|-", c) != NULL) return (c); if (c == '"' || c == '<') { int end = c; size_t n = 0; val = 1; if (c == '<') { val = 0; end = '>'; } while ((c = tgetc()) != EOF) { if (c == end) break; if (n == sizeof(str) - 1) { str[n++] = '\0'; report("filename too long '%s...'", str); } str[n++] = c; } str[n++] = '\0'; return (TOK_FILENAME); } /* * Sort out numbers */ if (isdigit(c)) { size_t n = 0; str[n++] = c; while ((c = tgetc()) != EOF) { if (!isdigit(c)) { tungetc(c); break; } if (n == sizeof(str) - 1) { str[n++] = '\0'; report("number too long '%s...'", str); } str[n++] = c; } str[n++] = '\0'; sscanf(str, "%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 = tgetc()) != EOF) { if (!isalnum(c) && c != '_' && c != '-') { tungetc(c); 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); } LIST_FOREACH(t, &types, link) { if (strcmp(t->name, str) == 0) { val = t->syntax; return (TOK_DEFTYPE); } } return (TOK_STR); } if (isprint(c)) errx(1, "%u: unexpected character '%c'", input->lno, c); else errx(1, "%u: unexpected character 0x%02x", input->lno, (u_int)c); } static int gettoken(void) { int tok = gettoken_internal(); if (debug) { switch (tok) { case TOK_EOF: fprintf(stderr, "EOF "); break; case TOK_NUM: fprintf(stderr, "NUM(%lu) ", val); break; case TOK_STR: fprintf(stderr, "STR(%s) ", str); break; case TOK_ACCESS: fprintf(stderr, "ACCESS(%lu) ", val); break; case TOK_TYPE: fprintf(stderr, "TYPE(%lu) ", val); break; case TOK_ENUM: fprintf(stderr, "ENUM "); break; case TOK_BITS: fprintf(stderr, "BITS "); break; case TOK_TYPEDEF: fprintf(stderr, "TYPEDEF "); break; case TOK_DEFTYPE: fprintf(stderr, "DEFTYPE(%s,%lu) ", str, val); break; case TOK_INCLUDE: fprintf(stderr, "INCLUDE "); break; case TOK_FILENAME: fprintf(stderr, "FILENAME "); break; default: if (tok < TOK_EOF) { if (isprint(tok)) fprintf(stderr, "'%c' ", tok); else if (tok == '\n') fprintf(stderr, "\n"); else fprintf(stderr, "%02x ", tok); } else abort(); break; } } return (tok); } /** * Pushback a token */ static void pushback(enum tok tok) { if (saved_token != -1) abort(); saved_token = tok; } /* * Create a new type */ static struct type * make_type(const char *s) { struct type *t; t = xalloc(sizeof(*t)); t->name = savestr(s); t->is_enum = 0; t->syntax = SNMP_SYNTAX_NULL; t->from_fname = savestr(input->fname); t->from_lno = input->lno; TAILQ_INIT(&t->enums); LIST_INSERT_HEAD(&types, t, link); return (t); } /* * Parse a type. We've seen the ENUM or type keyword already. Leave next * token. */ static u_int parse_type(enum tok *tok, struct type *t, const char *vname) { u_int syntax; struct enums *e; syntax = val; if (*tok == TOK_ENUM || *tok == TOK_BITS) { if (t == NULL && vname != NULL) { t = make_type(vname); t->is_enum = (*tok == TOK_ENUM); t->is_bits = (*tok == TOK_BITS); t->syntax = syntax; } if (gettoken() != '(') report("'(' expected after ENUM"); if ((*tok = gettoken()) == TOK_EOF) report("unexpected EOF in ENUM"); do { e = NULL; if (t != NULL) { e = xalloc(sizeof(*e)); } if (*tok == '-') { if ((*tok = gettoken()) == TOK_EOF) report("unexpected EOF in ENUM"); e->value = -(long)val; } else e->value = val; - + if (*tok != TOK_NUM) report("need value for ENUM/BITS"); if (gettoken() != TOK_STR) report("need string in ENUM/BITS"); e->name = savetok(); TAILQ_INSERT_TAIL(&t->enums, e, link); if ((*tok = gettoken()) == TOK_EOF) report("unexpected EOF in ENUM/BITS"); } while (*tok != ')'); *tok = gettoken(); } else if (*tok == TOK_DEFTYPE) { *tok = gettoken(); } else { if ((*tok = gettoken()) == '|') { if (gettoken() != TOK_STR) report("subtype expected after '|'"); *tok = gettoken(); } } return (syntax); } /* * 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 = input->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 || tok == TOK_DEFTYPE || tok == TOK_ENUM || tok == TOK_BITS) { /* LEAF or COLUM */ u_int syntax = parse_type(&tok, NULL, node->name); if (tok == 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; tok = gettoken(); while (tok == TOK_TYPE || tok == TOK_DEFTYPE || tok == TOK_ENUM || tok == TOK_BITS) { u_int syntax = parse_type(&tok, NULL, node->name); if (index_count++ == SNMP_INDEXES_MAX) report("too many table indexes"); node->u.entry.index |= syntax << (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); } /* * Parse a top level element. Return the tree if it was a tree, NULL * otherwise. */ static struct node * parse_top(enum tok tok) { struct type *t; if (tok == '(') return (parse(tok)); if (tok == TOK_TYPEDEF) { if (gettoken() != TOK_STR) report("type name expected after typedef"); t = make_type(str); tok = gettoken(); t->is_enum = (tok == TOK_ENUM); t->is_bits = (tok == TOK_BITS); t->syntax = parse_type(&tok, t, NULL); pushback(tok); return (NULL); } if (tok == TOK_INCLUDE) { if (gettoken() != TOK_FILENAME) report("filename expected in include directive"); input_fopen(str, val); return (NULL); } report("'(' or 'typedef' expected"); } /* * Generate the C-code table part for one node. */ static void gen_node(FILE *fp, 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(fp, sub, oid, 0, NULL); oid->len--; return; } if (np->type == NODE_ENTRY) { TAILQ_FOREACH(sub, &np->u.entry.subs, link) gen_node(fp, 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(FILE *fp, 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(fp, sub, oidlen, NULL); return; } if (np->type == NODE_ENTRY) { TAILQ_FOREACH(sub, &np->u.entry.subs, link) gen_header(fp, 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 = savestr(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(FILE *fp, 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(fp, 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(FILE *fp, 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(fp, sub, oid, obj, idx, iname)) return (0); } else if (np->type == NODE_ENTRY) { TAILQ_FOREACH(sub, &np->u.entry.subs, link) if (!extract(fp, sub, oid, obj, idx, iname)) return (0); } oid->len--; return (1); } static int gen_extract(FILE *fp, 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(fp, 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) { if (*root == NULL) { *root = t; return; } if (t == NULL) return; /* 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); } static void unminus(FILE *fp, const char *s) { while (*s != '\0') { if (*s == '-') fprintf(fp, "_"); else fprintf(fp, "%c", *s); s++; } } static void gen_enum(FILE *fp, const struct type *t) { const struct enums *e; long min = LONG_MAX; fprintf(fp, "\n"); fprintf(fp, "#ifndef %s_defined__\n", t->name); fprintf(fp, "#define %s_defined__\n", t->name); fprintf(fp, "/*\n"); fprintf(fp, " * From %s:%u\n", t->from_fname, t->from_lno); fprintf(fp, " */\n"); fprintf(fp, "enum %s {\n", t->name); TAILQ_FOREACH(e, &t->enums, link) { fprintf(fp, "\t%s_", t->name); unminus(fp, e->name); fprintf(fp, " = %ld,\n", e->value); if (e->value < min) min = e->value; } fprintf(fp, "};\n"); fprintf(fp, "#define STROFF_%s %ld\n", t->name, min); fprintf(fp, "#define STRING_%s \\\n", t->name); TAILQ_FOREACH(e, &t->enums, link) { fprintf(fp, "\t[%ld] \"%s_", e->value - min, t->name); unminus(fp, e->name); fprintf(fp, "\",\\\n"); } fprintf(fp, "\n"); fprintf(fp, "#endif /* %s_defined__ */\n", t->name); } static void gen_enums(FILE *fp) { const struct type *t; LIST_FOREACH(t, &types, link) if (t->is_enum || t->is_bits) gen_enum(fp, t); } static int extract_enum(FILE *fp, const char *name) { const struct type *t; LIST_FOREACH(t, &types, link) if ((t->is_enum || t->is_bits) && strcmp(t->name, name) == 0) { gen_enum(fp, t); return (0); } return (-1); } int main(int argc, char *argv[]) { int do_extract = 0; int do_tree = 0; int do_enums = 0; int opt; struct node *root; char fname[MAXPATHLEN + 1]; int tok; FILE *fp; char *infile = NULL; while ((opt = getopt(argc, argv, "dEehI:i:lp:t")) != EOF) switch (opt) { case 'd': debug = 1; break; case 'h': fprintf(stderr, "%s", usgtxt); exit(0); case 'E': do_enums = 1; break; case 'e': do_extract = 1; break; case 'I': path_new(optarg); break; case 'i': infile = optarg; 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 + do_enums > 1) errx(1, "conflicting options -e/-t/-E"); if (!do_extract && !do_enums && argc != optind) errx(1, "no arguments allowed"); if ((do_extract || do_enums) && argc == optind) errx(1, "no objects specified"); if (infile == NULL) { input_new(stdin, NULL, ""); } else { if ((fp = fopen(infile, "r")) == NULL) err(1, "%s", infile); input_new(fp, NULL, infile); } root = parse_top(gettoken()); while ((tok = gettoken()) != TOK_EOF) merge(&root, parse_top(tok)); check_tree(root); if (do_extract) { while (optind < argc) { if (gen_extract(stdout, root, argv[optind])) errx(1, "object not found: %s", argv[optind]); optind++; } return (0); } if (do_enums) { while (optind < argc) { if (extract_enum(stdout, argv[optind])) errx(1, "enum 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(fp, root, PREFIX_LEN, NULL); fprintf(fp, "\n#ifdef SNMPTREE_TYPES\n"); gen_enums(fp); fprintf(fp, "\n#endif /* SNMPTREE_TYPES */\n\n"); 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(fp, root); fclose(fp); return (0); } Index: head/contrib/bsnmp/lib/asn1.3 =================================================================== --- head/contrib/bsnmp/lib/asn1.3 (revision 310647) +++ head/contrib/bsnmp/lib/asn1.3 (revision 310648) @@ -1,492 +1,492 @@ .\" .\" 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/lib/asn1.3,v 1.9 2005/10/04 08:46:49 brandt_h Exp $ .\" .Dd October 4, 2005 .Dt ASN1 3 .Os .Sh NAME .Nm asn_get_header , .Nm asn_put_header , .Nm asn_put_temp_header , .Nm asn_commit_header , .Nm asn_get_integer_raw , .Nm asn_get_integer , .Nm asn_put_integer , .Nm asn_get_octetstring_raw , .Nm asn_get_octetstring , .Nm asn_put_octetstring , .Nm asn_get_null_raw , .Nm asn_get_null , .Nm asn_put_null , .Nm asn_put_exception , .Nm asn_get_objid_raw , .Nm asn_get_objid , .Nm asn_put_objid , .Nm asn_get_sequence , .Nm asn_get_ipaddress_raw , .Nm asn_get_ipaddress , .Nm asn_put_ipaddress , .Nm asn_get_uint32_raw , .Nm asn_put_uint32 , .Nm asn_get_counter64_raw , .Nm asn_put_counter64 , .Nm asn_get_timeticks , .Nm asn_put_timeticks , .Nm asn_skip , .Nm asn_slice_oid , .Nm asn_append_oid , .Nm asn_compare_oid , .Nm asn_is_suboid , .Nm asn_oid2str_r , .Nm asn_oid2str .Nd "ASN.1 library for SNMP" .Sh LIBRARY Begemot SNMP library .Pq libbsnmp, -lbsnmp .Sh SYNOPSIS .In bsnmp/asn1.h .Ft enum asn_err .Fn asn_get_header "struct asn_buf *buf" "u_char *type" "asn_len_t *lenp" .Ft enum asn_err .Fn asn_put_header "struct asn_buf *buf" "u_char type" "asn_len_t len" .Ft enum asn_err .Fn asn_put_temp_header "struct asn_buf *buf" "u_char type" "u_char **ptr" .Ft enum asn_err .Fn asn_commit_header "struct asn_buf *buf" "u_char *ptr" .Ft enum asn_err .Fn asn_get_integer_raw "struct asn_buf *buf" "asn_len_t len" "int32_t *res" .Ft enum asn_err .Fn asn_get_integer "struct asn_buf *buf" "int32_t *res" .Ft enum asn_err .Fn asn_put_integer "struct asn_buf *buf" "int32_t arg" .Ft enum asn_err .Fn asn_get_octetstring_raw "struct asn_buf *buf" "asn_len_t len" "u_char *out" "u_int *outsize" .Ft enum asn_err .Fn asn_get_octetstring "struct asn_buf *buf" "u_char *out" "u_int *outsize" .Ft enum asn_err .Fn asn_put_octetstring "struct asn_buf *buf" "const u_char *str" "u_int strsize" .Ft enum asn_err .Fn asn_get_null_raw "struct asn_buf *buf" "asn_len_t len" .Ft enum asn_err .Fn asn_get_null "struct asn_buf *buf" .Ft enum asn_err .Fn asn_put_null "struct asn_buf *buf" .Ft enum asn_err .Fn asn_put_exception "struct asn_buf *buf" "u_int type" .Ft enum asn_err .Fn asn_get_objid_raw "struct asn_buf *buf" "asn_len_t len" "struct asn_oid *oid" .Ft enum asn_err .Fn asn_get_objid "struct asn_buf *buf" "struct asn_oid *oid" .Ft enum asn_err .Fn asn_put_objid "struct asn_buf *buf" "const struct asn_oid *oid" .Ft enum asn_err .Fn asn_get_sequence "struct asn_buf *buf" "asn_len_t *lenp" .Ft enum asn_err .Fn asn_get_ipaddress_raw "struct asn_buf *buf" "asn_len_t len" "u_char *ipa" .Ft enum asn_err .Fn asn_get_ipaddress "struct asn_buf *buf" "u_char *ipa" .Ft enum asn_err .Fn asn_put_ipaddress "struct asn_buf *buf" "const u_char *ipa" .Ft enum asn_err .Fn asn_get_uint32_raw "struct asn_buf *buf" "asn_len_t len" "u_int32_t *res" .Ft enum asn_err .Fn asn_put_uint32 "struct asn_buf *buf" "u_char type" "u_int32_t val" .Ft enum asn_err .Fn asn_get_counter64_raw "struct asn_buf *buf" "asn_len_t len" "u_int64_t *res" .Ft enum asn_err .Fn asn_put_counter64 "struct asn_buf *buf" "u_int64_t val" .Ft enum asn_err .Fn asn_get_timeticks "struct asn_buf *buf" "u_int32_t *valp" .Ft enum asn_err .Fn asn_put_timeticks "struct asn_buf *buf" "u_int32_t val" .Ft enum asn_err .Fn asn_skip "struct asn_buf *buf" "asn_len_t len" .Ft void .Fn asn_slice_oid "struct asn_oid *dest" "const struct asn_oid *src" "u_int from" "u_int to" .Ft void .Fn asn_append_oid "struct asn_oid *to" "const struct asn_oid *from" .Ft int .Fn asn_compare_oid "const struct asn_oid *oid1" "const struct asn_oid *oid2" .Ft int .Fn asn_is_suboid "const struct asn_oid *oid1" "const struct asn_oid *oid2" .Ft char * .Fn asn_oid2str_r "const struct asn_oid *oid" "char *buf" .Ft char * .Fn asn_oid2str "const struct asn_oid *oid" .Sh DESCRIPTION The ASN.1 library contains routines to handle ASN.1 encoding for SNMP. It supports only the restricted form of ASN.1 as required by SNMP. There are two basic structures used throughout the library: .Bd -literal -offset indent /* these restrictions are in the SMI */ #define ASN_MAXID 0xffffffff #define ASN_MAXOIDLEN 128 /* type of subidentifiers */ typedef u_int32_t asn_subid_t; struct asn_oid { u_int len; asn_subid_t subs[ASN_MAXOIDLEN]; }; .Ed .Pp This structure represents an OID with the restrictions defined in the SNMP SMI. .Fa len holds the current length of the OID and .Fa subs holds the elements of the OID. .Bd -literal -offset indent struct asn_buf { union { u_char *ptr; const u_char *cptr; } asn_u; size_t asn_len; }; #define asn_cptr asn_u.cptr #define asn_ptr asn_u.ptr .Ed .Pp This structure is used to encode and decode ASN.1. It describes the output buffer for encoding routines and the input buffer for decoding routines. For encoding .Fa asn_len holds the number of remaining free octets in the buffer. The first free byte is pointed to by .Fa asn_ptr . For decoding .Fa asn_len holds the number of remaining bytes to decode. The next byte to decode is pointed to by .Fa asn_cptr . .Pp Most of the functions return an error code .Fa "enum asn_error" : .Bd -literal -offset indent enum asn_err { /* conversion was ok */ ASN_ERR_OK = 0, /* conversion failed and stopped */ ASN_ERR_FAILED = 1 | 0x1000, /* length field bad, value skipped */ ASN_ERR_BADLEN = 2, /* out of buffer, stopped */ ASN_ERR_EOBUF = 3 | 0x1000, /* length ok, but value is out of range */ ASN_ERR_RANGE = 4, /* not the expected tag, stopped */ ASN_ERR_TAG = 5 | 0x1000, }; #define ASN_ERR_STOPPED(E) (((E) & 0x1000) != 0) .Ed .Pp If .Fn ASN_ERR_STOPPED returns true, the error was fatal and processing has stopped at the point of error. .Pp The function .Fn asn_get_header reads the next header from the input octet stream. It returns the tag in the variable pointed to by .Fa type (note that only single byte tags are supported) and the decoded length field in the value pointed to by .Fa lenp (this is restricted to a unsigned 32-bit value). All errors in this function are fatal and stop processing. .Pp The function .Fn asn_put_header writes an ASN.1 header. .Fa type is the tag to write and is restricted to one byte tags (i.e., tags lesser or equal than 0x30). .Fa len is the length of the value and is restricted to 16-bit. .Pp The functions .Fn asn_put_temp_header and .Fn asn_commit_header are used to write a header when the length of the value is not known in advance, for example, for sequences. .Fn asn_put_temp_header writes a header with the given tag .Fa type and space for the maximum supported length field and sets the pointer pointed to by .Fa ptr to the begin of this length field. This pointer must then be fed into .Fn asn_commit_header directly after writing the value to the buffer. The function will compute the length, insert it into the right place and shift the value if the resulting length field is shorter than the estimated one. .Pp The function .Fn asn_get_integer_raw is used to decode a signed integer value (32-bit). It assumes, that the header of the integer has been decoded already. .Fa len is the length obtained from the ASN.1 header and the integer will be returned in the value pointed to by .Fa res . .Pp The function .Fn asn_get_integer decodes a complete 32-bit signed integer including the header. If the tag is wrong .Li ASN_ERR_TAG is returned. The function .Fn asn_put_integer encodes a 32-bit signed integer. .Pp The function .Fn asn_get_octetstring_raw decodes the value field of an ASN.1 octet string. The length obtained from the header must be fed into the .Fa len argument and .Fa out must point to a buffer to receive the octet string. On entry to the function .Fa outsize must point to the size of the buffer. On exit .Fa outsize will point to the number of octets decoded (if no error occurs this will be equal to .Fa len ). The function .Fn asn_get_octetstring decodes an octetstring including the header. .Fa out must point to a buffer to receive the string, .Fa outsize must point to the size of the buffer. On exit of the function .Fa outsize will point to the number of octets decoded. The function .Fn asn_put_octetstring encodes an octetstring (including the header). .Fa str points to the string to encode and .Fa strsize is the length of the string (the string may contain embedded .Li NUL Ns s). .Pp The function .Fn asn_get_null_raw decodes a null value. .Fa len is the length obtained from the header and must be 0. The function .Fn asn_get_null decodes a null including the header and the function .Fn asn_put_null encodes a null. .Pp The function .Fn asn_put_exception is used to encode an SNMPv2 exception. The exception type is .Fa type . .Pp The function .Fn asn_get_objid_raw is used to decode an OID value. .Fa len must be the value length obtained from the header and .Fa oid will receive the decoded OID. The function .Fn asn_get_objid decodes a complete OID (including the header) and the function .Fn asn_put_objid encodes a complete OID. .Pp The function .Fn asn_get_sequence decodes a sequence header. The length of the sequence value will be stored in the value pointed to by .Fa lenp . .Pp The function .Fn asn_get_ipaddress_raw decodes an IP address value. .Fa len is the length from the header and must be 4. .Fa ipa will receive the decoded IP address and must point to a buffer of at least four bytes. The function .Fn asn_get_ipaddress decodes a complete IP address (including the header) and .Fn asn_put_ipaddress encodes an IP address. .Pp The function .Fn asn_get_uint32_raw decodes an unsigned 32-bit integer value. .Fa len is the length from the header and .Fa res will get the decoded value. The function .Fn asn_put_uint32 encodes an unsigned 32-bit integer value and inserts the tag given in .Fa type into the header. .Pp The function .Fn asn_get_counter64_raw decodes an unsigned 64-bit integer value. .Fa len must be the value length from the header. The resulting value is stored into the variable pointed to by .Fa res . The function .Fn asn_put_counter64 encodes a complete unsigned 64-bit value. .Pp The function .Fn asn_get_timeticks decodes an ASN.1 object of type .Li TIMETICKS and the function .Fn asn_put_timeticks encodes such an object. .Pp The function .Fn asn_skip can be used to skip .Fa len bytes in the input buffer. .Pp The function .Fn asn_slice_oid splits a part out from an OID. It takes all the subids from the OID pointed to by .Fa src starting with the subid at position .Fa from (the first subid being subid 0) up to, but not including, subid .Fa to and generates a new OID in .Fa dest . If .Fa to is less or equal to .Fa from the resulting OID will have a length of zero. .Pp The function .Fn asn_append_oid appends the OID .Fa from to the OID .Fa to given that the resulting OID is not too long. If the maximum length is exceeded the result is undefined. .Pp The function .Fn asn_compare_oid compares two oids and returns the values .Li -1 , .Li 0 or .Li +1 when .Fa oid1 is lesser than, equal, or larger than .Fa oid2 resp. .Pp The function .Fn asn_is_suboid returns 1 if .Fa oid1 is equal to the leading part of .Fa oid2 . It returns 0 otherwise. .Pp The function .Fn asn_oid2str_r makes a printable string from .Fa oid . The buffer pointed to by .Fa str must be large enough to hold the result. The constant .Li ASN_OIDSTRLEN is defined to be the length of the maximum string generated by this function (including the trailing NUL). The function .Fn asn_oid2str makes a printable string from .Fa oid into a private buffer that is overwritten by each call. .Sh DIAGNOSTICS When an error occurs in any of the function the function pointed to by the global pointer .Bd -literal -offset indent extern void (*asn_error)(const struct asn_buf *, const char *, ...); .Ed .Pp is called with the current buffer (this may be .Li NULL ) and a .Xr printf 3 style format string. There is a default error handler in the library that prints a message starting with .Sq ASN.1: followed by the error message and an optional dump of the buffer. .Sh SEE ALSO .Xr 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: head/contrib/bsnmp/lib/asn1.c =================================================================== --- head/contrib/bsnmp/lib/asn1.c (revision 310647) +++ head/contrib/bsnmp/lib/asn1.c (revision 310648) @@ -1,1022 +1,1022 @@ /* * 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.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" 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, size_t *moved) { 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; if (moved != NULL) *moved = 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 + /* 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[1] >= 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); } /* * Add a padding */ enum asn_err asn_pad(struct asn_buf *b, asn_len_t len) { if (b->asn_len < len) return (ASN_ERR_EOBUF); b->asn_ptr += 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: head/contrib/bsnmp/lib/asn1.h =================================================================== --- head/contrib/bsnmp/lib/asn1.h (revision 310647) +++ head/contrib/bsnmp/lib/asn1.h (revision 310648) @@ -1,183 +1,183 @@ /* * 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.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 *, size_t *); 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); enum asn_err asn_pad(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: head/contrib/bsnmp/lib/bsnmpagent.3 =================================================================== --- head/contrib/bsnmp/lib/bsnmpagent.3 (revision 310647) +++ head/contrib/bsnmp/lib/bsnmpagent.3 (revision 310648) @@ -1,444 +1,444 @@ .\" .\" 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/lib/bsnmpagent.3,v 1.10 2005/10/04 08:46:49 brandt_h Exp $ .\" .Dd October 4, 2005 .Dt BSNMPAGENT 3 .Os .Sh NAME .Nm bsnmpagent , .Nm snmp_depop_t , .Nm snmp_op_t , .Nm tree , .Nm tree_size , .Nm snmp_trace , .Nm snmp_debug , .Nm snmp_get , .Nm snmp_getnext , .Nm snmp_getbulk , .Nm snmp_set , .Nm snmp_make_errresp , .Nm snmp_dep_lookup , .Nm snmp_init_context , .Nm snmp_dep_commit , .Nm snmp_dep_rollback , .Nm snmp_dep_finish .Nd "SNMP agent library" .Sh LIBRARY Begemot SNMP library .Pq libbsnmp, -lbsnmp .Sh SYNOPSIS .In asn1.h .In snmp.h .In snmpagent.h .Ft typedef int .Fn (*snmp_depop_t) "struct snmp_context *ctx" "struct snmp_dependency *dep" "enum snmp_depop op" .Ft typedef int .Fn (*snmp_op_t) "struct snmp_context *ctx" "struct snmp_value *val" "u_int len" "u_int idx" "enum snmp_op op" .Vt extern struct snmp_node *tree ; .Vt extern u_int tree_size ; .Vt extern u_int snmp_trace ; .Vt extern void (*snmp_debug)(const char *fmt, ...) ; .Ft enum snmp_ret .Fn snmp_get "struct snmp_pdu *pdu" "struct asn_buf *resp_b" "struct snmp_pdu *resp" "void *data" .Ft enum snmp_ret .Fn snmp_getnext "struct snmp_pdu *pdu" "struct asn_buf *resp_b" "struct snmp_pdu *resp" "void *data" .Ft enum snmp_ret .Fn snmp_getbulk "struct snmp_pdu *pdu" "struct asn_buf *resp_b" "struct snmp_pdu *resp" "void *data" .Ft enum snmp_ret .Fn snmp_set "struct snmp_pdu *pdu" "struct asn_buf *resp_b" "struct snmp_pdu *resp" "void *data" .Ft enum snmp_ret .Fn snmp_make_errresp "const struct snmp_pdu *pdu" "struct asn_buf *req_b" "struct asn_buf *resp_b" .Ft struct snmp_dependency * .Fn snmp_dep_lookup "struct snmp_context *ctx" "const struct asn_oid *base" "const struct asn_oid *idx" "size_t alloc" "snmp_depop_t func" .Ft struct snmp_context * .Fn snmp_init_context "void" .Ft int .Fn snmp_dep_commit "struct snmp_context *ctx" .Ft int .Fn snmp_dep_rollback "struct snmp_context *ctx" .Ft void .Fn snmp_dep_finish "struct snmp_context *ctx" .Sh DESCRIPTION The SNMP library contains routines to easily build SNMP agent applications that use SNMP versions 1 or 2. Note, however, that it may be even easier to build an .Xr bsnmpd 1 loadable module, that handles the new MIB (see .Xr snmpmod 3 ) . .Pp Most of the agent routines operate on a global array that the describes the complete MIB served by the agent. This array is held in the two variables: .Bd -literal -offset indent extern struct snmp_node *tree; extern u_int tree_size; .Ed .Pp The elements of the array are of type .Vt struct snmp_node : .Bd -literal -offset indent typedef int (*snmp_op_t)(struct snmp_context *, struct snmp_value *, u_int, u_int, enum snmp_op); struct snmp_node { struct asn_oid oid; const char *name; /* name of the leaf */ enum snmp_node_type type; /* type of this node */ enum snmp_syntax syntax; snmp_op_t op; u_int flags; u_int32_t index; /* index data */ void *data; /* application data */ void *tree_data; /* application data */ }; .Ed .Pp The fields of this structure are described below. .Bl -tag -width "syntax" .It Va oid Base OID of the scalar or table column. .It Va name Name of this variable. .It Va type Type of this variable. One of: .Bd -literal -offset indent enum snmp_node_type { SNMP_NODE_LEAF = 1, SNMP_NODE_COLUMN }; .Ed .It Va syntax The SNMP syntax of this variable. .It Va op The user supplied handler for this variable. The handler is called with the following arguments: .Bl -tag -width "ctx" .It Fa ctx A pointer to the context (see below). .Li NULL . .It Fa val The value to be set or retrieved. For GETNEXT and GETBULK operations the oid in this value is the current OID. The function (called in this case only for table rows) must find the lexically next existing OID within the same column and set the oid and value subfields accordingly. If the table column is exhausted the function must return .Li SNMP_ERR_NOSUCHNAME . For all other operations the oid in .Fa val is the oid to fetch or set. .It Fa len The length of the base oid without index. .It Fa idx For table columns this is the index expression from the node (see below). .It Fa op This is the operation to execute, one of: .Bd -literal -offset indent enum snmp_op { SNMP_OP_GET = 1, SNMP_OP_GETNEXT, SNMP_OP_SET, SNMP_OP_COMMIT, SNMP_OP_ROLLBACK, }; .Ed .El .Pp The user handler must return an appropriate SNMP v2 error code. If the original PDU was a version 1 PDU, the error code is mapped automatically. .It Va flags Currently only the flag .Li SNMP_NODE_CANSET is defined and set for nodes, that can be written or created. .It Va index This word describes the index for table columns. Each part of the index takes 4 bits starting at bit 4. Bits 0 to 3 hold the number of index parts. This arrangement allows for tables with up to seven indexes. Each bit group contains the syntax for the index part. There are a number of macros to help in parsing this field: .Bd -literal -offset indent #define SNMP_INDEXES_MAX 7 #define SNMP_INDEX_SHIFT 4 #define SNMP_INDEX_MASK 0xf #define SNMP_INDEX_COUNT(V) ((V) & SNMP_INDEX_MASK) #define SNMP_INDEX(V,I) \e (((V) >> (((I) + 1) * SNMP_INDEX_SHIFT)) & \e SNMP_INDEX_MASK) .Ed .It Va data This field may contain arbitrary data and is not used by the library. .El .Pp The easiest way to construct the node table is .Xr gensnmptree 1 . Note, that one must be careful when changing the tree while executing a SET operation. Consult the sources for .Xr bsnmpd 1 . .Pp The global variable .Va snmp_trace together with the function pointed to by .Va snmp_debug help in debugging the library and the agent. .Va snmp_trace is a bit mask with the following bits: .Bd -literal -offset indent enum { SNMP_TRACE_GET, SNMP_TRACE_GETNEXT, SNMP_TRACE_SET, SNMP_TRACE_DEPEND, SNMP_TRACE_FIND, }; .Ed .Pp Setting a bit to true causes the library to call .Fn snmp_debug in strategic places with a debug string. The library contains a default implementation for the debug function that prints a message to standard error. .Pp Many of the functions use a so called context: .Bd -literal -offset indent struct snmp_context { u_int var_index; struct snmp_scratch *scratch; struct snmp_dependency *dep; void *data; /* user data */ enum snmp_ret code; /* return code */ }; struct snmp_scratch { void *ptr1; void *ptr2; uint32_t int1; uint32_t int2; }; .Ed .Pp The fields are used as follows: .Bl -tag -width ".It Va var_index" .It Va va_index For the node operation callback this is the index of the variable binding that should be returned if an error occurs. Set by the library. In all other functions this is undefined. .It Va scratch For the node operation callback this is a pointer to a per variable binding scratch area that can be used to implement the commit and rollback. Set by the library. .It Va dep In the dependency callback function (see below) this is a pointer to the current dependency. Set by the library. .It Va data This is the .Fa data argument from the call to the library and is not used by the library. .El .Pp The next three functions execute different kinds of GET requests. The function .Fn snmp_get executes an SNMP GET operation, the function .Fn snmp_getnext executes an SNMP GETNEXT operation and the function .Fn snmp_getbulk executes an SNMP GETBULK operation. For all three functions the response PDU is constructed and encoded on the fly. If everything is ok, the response PDU is returned in .Fa resp and .Fa resp_b . The caller must call .Fn snmp_pdu_free to free the response PDU in this case. One of the following values may be returned: .Bl -tag -width ".It Li SNMP_RET_ERR" .It Li SNMP_RET_OK Operation successful, response PDU may be sent. .It Li SNMP_RET_IGN Operation failed, no response PDU constructed. Request is ignored. .It Li SNMP_RET_ERR Error in operation. The error code and index have been set in .Fa pdu . No response PDU has been constructed. The caller may construct an error response PDU via .Fn snmp_make_errresp . .El .Pp The function .Fn snmp_set executes an SNMP SET operation. The arguments are the same as for the previous three functions. The operation of this functions is, however, much more complex. .Pp The SET operation occurs in several stages: .Bl -enum -offset indent .It For each binding search the corresponding nodes, check that the variable is writeable and the syntax is ok. The writeable check can be done only for scalars. For columns it must be done in the node's operation callback function. .It For each binding call the node's operation callback with function SNMP_OP_SET. The callback may create dependencies or finalizers (see below). For simple scalars the scratch area may be enough to handle commit and rollback, for interdependent table columns dependencies may be necessary. .It If the previous step fails at any point, the node's operation callback functions are called for all bindings for which SNMP_OP_SET was executed with SNMP_OP_ROLLBACK, in the opposite order. This allows all variables to undo the effect of the SET operation. After this all the dependencies are freed and the finalizers are executed with a fail flag of 1. Then the function returns to the caller with an appropriate error indication. .It If the SET step was successful for all bindings, the dependency callbacks are executed in the order in which the dependencies were created with an operation of SNMP_DEPOP_COMMIT. If any of the dependencies fails, all the committed dependencies are called again in the opposite order with SNMP_DEPOP_ROLLBACK. Than for all bindings from the last to the first the node's operation callback is called with SNMP_OP_ROLLBACK to undo the effect of SNMP_OP_SET. At the end the dependencies are freed and the finalizers are called with a fail flag of 1 and the function returns to the caller with an appropriate error indication. .It If the dependency commits were successful, for each binding the node's operation callback is called with SNMP_OP_COMMIT. Any error returned from the callbacks is ignored (an error message is generated via .Fn snmp_error ). .It Now the dependencies are freed and the finalizers are called with a fail flag of 0. For each dependency just before freeing it its callback is called with .Li SNMP_DEPOP_FINISH. Then the function returns .Li SNMP_ERR_OK . .El .Pp There are to mechanisms to help in complex SET operations: dependencies and finalizers. A dependency is used if several bindings depend on each other. A typical example is the creation of a conceptual row, which requires the setting of several columns to succeed. A dependency is identified by two OIDs. In the table case, the first oid is typically the table's base OID and the second one the index. Both of these can easily be generated from the variables OID with .Fn asn_slice_oid . The function .Fn snmp_dep_lookup tries to find a dependency based on these two OIDs and, if it cannot find one creates a new one. This means for the table example, that the function returns the same dependency for each of the columns of the same table row. This allows during the SNMP_OP_SET processing to collect all information about the row into the dependency. The arguments to .Fn snmp_dep_lookup are: the two OIDs to identify the dependency (they are copied into newly created dependencies), the size of the structure to allocate and the dependency callback. .Pp When all SNMP_OP_SET operations have succeeded the dependencies are executed. At this stage the dependency callback has all information about the given table row that was available in this SET PDU and can operate accordingly. .Pp It is guaranteed that each dependency callback is executed at minimum once - with an operation of .Li SNMP_OP_ROLLBACK . This ensures that all dynamically allocated resources in a callback can be freed correctly. .Pp The function .Fn snmp_make_errresp makes an error response if an operation has failed. It takes the original request PDU (it will look only on the error code and index fields), the buffer containing the original PDU and a buffer for the error PDU. It copies the bindings field from the original PDUs buffer directly to the response PDU and thus does not depend on the decodability of this field. It may return the same values as the operation functions. .Pp The next four functions allow some parts of the SET operation to be executed. This is only used in .Xr bsnmpd 1 to implement the configuration as a single transaction. The function .Fn snmp_init_context creates and initializes a context. The function .Fn snmp_dep_commit executes SNMP_DEPOP_COMMIT for all dependencies in the context stopping at the first error. The function .Fn snmp_dep_rollback executes SNMP_DEPOP_ROLLBACK starting at the previous of the current dependency in the context. The function .Fn snmp_dep_finish executes SNMP_DEPOP_FINISH for all dependencies. .Sh DIAGNOSTICS If an error occurs in any of the function an error indication as described above is returned. Additionally the functions may call snmp_error on unexpected errors. .Sh SEE ALSO .Xr gensnmptree 1 , .Xr bsnmpd 1 , .Xr bsnmpclient 3 , .Xr bsnmplib 3 , .Xr snmpmod 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: head/contrib/bsnmp/lib/bsnmpclient.3 =================================================================== --- head/contrib/bsnmp/lib/bsnmpclient.3 (revision 310647) +++ head/contrib/bsnmp/lib/bsnmpclient.3 (revision 310648) @@ -1,703 +1,703 @@ .\" .\" 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/lib/bsnmpclient.3,v 1.12 2005/10/04 08:46:50 brandt_h Exp $ .\" .Dd September 9, 2010 .Dt BSNMPCLIENT 3 .Os .Sh NAME .Nm snmp_client , .Nm snmp_send_cb_f , .Nm snmp_timeout_cb_f , .Nm snmp_timeout_start_f , .Nm snmp_timeout_stop_f , .Nm snmp_open , .Nm snmp_close , .Nm snmp_pdu_create , .Nm snmp_add_binding , .Nm snmp_pdu_check , .Nm snmp_pdu_send , .Nm snmp_oid_append , .Nm snmp_parse_server , .Nm snmp_receive , .Nm snmp_table_cb_f , .Nm snmp_table_fetch , .Nm snmp_table_fetch_async , .Nm snmp_dialog , .Nm snmp_discover_engine .Nd "SNMP client library" .Sh LIBRARY Begemot SNMP library .Pq libbsnmp, -lbsnmp .Sh SYNOPSIS .In asn1.h .In snmp.h .In snmpclient.h .Ft typedef void .Fn (*snmp_send_cb_f) "struct snmp_pdu *req" "struct snmp_pdu *resp" "void *uarg" .Ft typedef void .Fn (*snmp_timeout_cb_f) "void *uarg" .Ft typedef void * .Fn (*snmp_timeout_start_f) "struct timeval *timeout" "snmp_timeout_cb_f callback" "void *uarg" .Ft typedef void .Fn (*snmp_timeout_stop_f) "void *timeout_id" .Vt extern struct snmp_client snmp_client ; .Ft void .Fn snmp_client_init "struct snmp_client *client" .Ft int .Fn snmp_client_set_host "struct snmp_client *client" "const char *host" .Ft int .Fn snmp_client_set_port "struct snmp_client *client" "const char *port" .Ft int .Fn snmp_open "const char *host" "const char *port" "const char *read_community" "const char *write_community" .Ft void .Fn snmp_close "void" .Ft void .Fn snmp_pdu_create "struct snmp_pdu *pdu" "u_int op" .Ft int .Fn snmp_add_binding "struct snmp_pdu *pdu" "..." .Ft int .Fn snmp_pdu_check "const struct snmp_pdu *req" "const struct snmp_pdu *resp" .Ft int32_t .Fn snmp_pdu_send "struct snmp_pdu *pdu" "snmp_send_cb_f func" "void *uarg" .Ft int .Fn snmp_oid_append "struct asn_oid *oid" "const char *fmt" "..." .Ft int .Fn snmp_parse_server "struct snmp_client *sc" "const char *str" .Ft int .Fn snmp_receive "int blocking" .Ft typedef void .Fn (*snmp_table_cb_f) "void *list" "void *arg" "int res" .Ft int .Fn snmp_table_fetch "const struct snmp_table *descr" "void *list" .Ft int .Fn snmp_table_fetch_async "const struct snmp_table *descr" "void *list" "snmp_table_cb_f callback" "void *uarg" .Ft int .Fn snmp_dialog "struct snmp_pdu *req" "struct snmp_pdu *resp" .Ft int .Fn snmp_discover_engine "void" .Sh DESCRIPTION The SNMP library contains routines to easily build SNMP client applications that use SNMP versions 1, 2 or 3. Most of the routines use a .Vt struct snmp_client : .Bd -literal -offset indent struct snmp_client { enum snmp_version version; int trans; /* which transport to use */ /* these two are read-only for the application */ char *cport; /* port number as string */ char *chost; /* host name or IP address as string */ char read_community[SNMP_COMMUNITY_MAXLEN + 1]; char write_community[SNMP_COMMUNITY_MAXLEN + 1]; /* SNMPv3 specific fields */ int32_t identifier; int32_t security_model; struct snmp_engine engine; struct snmp_user user; /* SNMPv3 Access control - VACM*/ uint32_t clen; uint8_t cengine[SNMP_ENGINE_ID_SIZ]; char cname[SNMP_CONTEXT_NAME_SIZ]; struct timeval timeout; u_int retries; int dump_pdus; size_t txbuflen; size_t rxbuflen; int fd; int32_t next_reqid; int32_t max_reqid; int32_t min_reqid; char error[SNMP_STRERROR_LEN]; snmp_timeout_start_f timeout_start; snmp_timeout_stop_f timeout_stop; char local_path[sizeof(SNMP_LOCAL_PATH)]; }; .Ed .Pp The fields of this structure are described below. .Bl -tag -width "timeout_start" .It Va version This is the version of SNMP to use. See .Xr bsnmplib 3 for applicable values. The default version is .Li SNMP_V2c . .It Va trans If this is .Dv SNMP_TRANS_LOC_DGRAM a local datagram socket is used. If it is .Dv SNMP_TRANS_LOC_STREAM a local stream socket is used. For .Dv SNMP_TRANS_UDP a UDP socket is created. It uses the .Va chost field as the path to the server's socket for local sockets. .It Va cport The SNMP agent's UDP port number. This may be a symbolic port number (from .Pa /etc/services ) or a numeric port number. If this field is .Li NULL (the default) the standard SNMP port is used. This field should not be changed directly but rather by calling .Fn snmp_client_set_port . .It Va chost The SNMP agent's host name, IP address or .Ux domain socket path name. If this is .Li NULL (the default) .Li localhost is assumed. This field should not be changed directly but rather through calling .Fn snmp_client_set_host . .It Va read_community This is the community name to be used for all requests except SET requests. The default is .Sq public . .It Va write_community The community name to be used for SET requests. The default is .Sq private . .It Va identifier The message indentifier value to be used with SNMPv3 PDUs. Incremented with each transmitted PDU. .It Va security_model The security model to be used with SNMPv3 PDUs. Currently only User-Based Security model specified by RFC 3414 (value 3) is supported. .It Va engine The authorative SNMP engine parameters to be used with SNMPv3 PDUs. .It Va user The USM SNMP user credentials to be used with SNMPv3 PDUs. .It Va clen The length of the context engine id to be used with SNMPv3 PDUs. .It Va cengine The context engine id to be used with SNMPv3 PDUs. Default is empty. .It Va cname The context name to be used with SNMPv3 PDUs. Default is .Sq "" . .It Va timeout The maximum time to wait for responses to requests. If the time elapses, the request is resent up to .Va retries times. The default is 3 seconds. .It Va retries Number of times a request PDU is to be resent. If set to 0, the request is sent only once. The default is 3 retransmissions. .It Va dump_pdus If set to a non-zero value all received and sent PDUs are dumped via .Xr snmp_pdu_dump 3 . The default is not to dump PDUs. .It Va txbuflen The encoding buffer size to be allocated for transmitted PDUs. The default is 10000 octets. .It Va rxbuflen The decoding buffer size to be allocated for received PDUs. This is the size of the maximum PDU that can be received. The default is 10000 octets. .It Va fd After calling .Fn snmp_open this is the file socket file descriptor used for sending and receiving PDUs. .It Va next_reqid The request id of the next PDU to send. Used internal by the library. .It Va max_reqid The maximum request id to use for outgoing PDUs. The default is .Li INT32_MAX . .It Va min_reqid The minimum request id to use for outgoing PDUs. Request ids are allocated linearily starting at .Va min_reqid up to .Va max_reqid . .It Va error If an error happens, this field is set to a printable string describing the error. .It Va timeout_start This field must point to a function setting up a one shot timeout. After the timeout has elapsed, the given callback function must be called with the user argument. The .Fn timeout_start function must return a .Vt void * identifying the timeout. .It Va timeout_stop This field must be set to a function that stops a running timeout. The function will be called with the return value of the corresponding .Fn timeout_start function. .It Va local_path If in local socket mode, the name of the clients socket. Not needed by the application. .El .Pp In the current implementation there is a global variable .Pp .D1 Vt extern struct snmp_client snmp_client ; .Pp that is used by all the library functions. The first call into the library must be a call to .Fn snmp_client_init to initialize this global variable to the default values. After this call and before calling .Fn snmp_open the fields of the variable may be modified by the user. The modification of the .Va chost and .Va cport fields should be done only via the functions .Fn snmp_client_set_host and .Fn snmp_client_set_port . .Pp The function .Fn snmp_open creates a UDP or .Ux domain socket and connects it to the agent's IP address and port. If any of the arguments of the call is not .Li NULL the corresponding field in the global .Va snmp_client is set from the argument. Otherwise the values that are already in that variable are used. The function .Fn snmp_close closes the socket, stops all timeouts and frees all dynamically allocated resources. .Pp The next three functions are used to create request PDUs. The function .Fn snmp_pdu_create initializes a PDU of type .Va op . It does not allocate space for the PDU itself. This is the responsibility of the caller. .Fn snmp_add_binding adds bindings to the PDU and returns the (zero based) index of the first new binding. The arguments are pairs of pointer to the OIDs and syntax constants, terminated by a NULL. The call .Bd -literal -offset indent snmp_add_binding(&pdu, &oid1, SNMP_SYNTAX_INTEGER, &oid2, SNMP_SYNTAX_OCTETSTRING, NULL); .Ed .Pp adds two new bindings to the PDU and returns the index of the first one. It is the responsibility of the caller to set the value part of the binding if necessary. The functions returns -1 if the maximum number of bindings is exhausted. The function .Fn snmp_oid_append can be used to construct variable OIDs for requests. It takes a pointer to an .Vt struct asn_oid that is to be constructed, a format string, and a number of arguments the type of which depends on the format string. The format string is interpreted character by character in the following way: .Bl -tag -width ".It Li ( Va N Ns Li )" .It Li i This format expects an argument of type .Vt asn_subid_t and appends this as a single integer to the OID. .It Li a This format expects an argument of type .Vt struct in_addr and appends to four parts of the IP address to the OID. .It Li s This format expects an argument of type .Vt const char * and appends the length of the string (as computed by .Xr strlen 3 ) and each of the characters in the string to the OID. .It ( Va N Ns ) This format expects no argument. .Va N must be a decimal number and is stored into an internal variable .Va size . .It Li b This format expects an argument of type .Vt const char * and appends .Va size characters from the string to the OID. The string may contain .Li NUL characters. .It Li c This format expects two arguments: one of type .Vt size_t and one of type .Vt const u_char * . The first argument gives the number of bytes to append to the OID from the string pointed to by the second argument. .El .Pp The function .Fn snmp_pdu_check may be used to check a response PDU. A number of checks are performed (error code, equal number of bindings, syntaxes and values for SET PDUs). The function returns +1 if everything is ok, 0 if a NOSUCHNAME or similar error was detected, -1 if the response PDU had fatal errors and -2 if .Fa resp is .Li NULL (a timeout occurred). .Pp The function .Fn snmp_pdu_send encodes and sends the given PDU. It records the PDU together with the callback and user pointers in an internal list and arranges for retransmission if no response is received. When a response is received or the retransmission count is exceeded the callback .Fa func is called with the orignal request PDU, the response PDU and the user argument .Fa uarg . If the retransmit count is exceeded, .Fa func is called with the original request PDU, the response pointer set to .Li NULL and the user argument .Fa uarg . The caller should not free the request PDU until the callback function is called. The callback function must free the request PDU and the response PDU (if not .Li NULL ). .Pp The function .Fn snmp_receive tries to receive a PDU. If the argument is zero, the function polls to see whether a packet is available, if the argument is non-zero, the function blocks until the next packet is received. The packet is delivered via the usual callback mechanism (non-response packets are silently dropped). The function returns 0, if a packet was received and successfully dispatched, -1 if an error occurred or no packet was available (in polling mode). .Pp The next two functions are used to retrieve tables from SNMP agents. They use the following input structure, that describes the table: .Bd -literal -offset indent struct snmp_table { struct asn_oid table; struct asn_oid last_change; u_int max_iter; size_t entry_size; u_int index_size; uint64_t req_mask; struct snmp_table_entry { asn_subid_t subid; enum snmp_syntax syntax; off_t offset; } entries[]; }; .Ed .Pp The fields of this structure have the following meaning: .Bl -tag -width "last_change" .It Va table This is the base OID of the table. .It Va last_change Some tables have a scalar variable of type TIMETICKS attached to them, that holds the time when the table was last changed. This OID should be the OID of this variable (without the \&.0 index). When the table is retrieved with multiple GET requests, and the variable changes between two request, the table fetch is restarted. .It Va max_iter Maximum number of tries to fetch the table. .It Va entry_size The table fetching routines return a list of structures one for each table row. This variable is the size of one structure and used to .Xr malloc 3 the structure. .It Va index_size This is the number of index columns in the table. .It Va req_mask This is a bit mask with a 1 for each table column that is required. Bit 0 corresponds to the first element (index 0) in the array .Va entries , bit 1 to the second (index 1) and so on. SNMP tables may be sparse. For sparse columns the bit should not be set. If the bit for a given column is set and the column value cannot be retrieved for a given row, the table fetch is restarted assuming that the table is currently being modified by the agent. The bits for the index columns are ignored. .It Va entries This is a variable sized array of column descriptors. This array is terminated by an element with syntax .Li SNMP_SYNTAX_NULL . The first .Va index_size elements describe all the index columns of the table, the rest are normal columns. If for the column at .Ql entries[N] the expression .Ql req_mask & (1 << N) yields true, the column is considered a required column. The fields of this the array elements have the following meaning: .Bl -tag -width "syntax" .It Va subid This is the OID subid of the column. This is ignored for index entries. Index entries are decoded according to the .Va syntax field. .It Va syntax This is the syntax of the column or index. A syntax of .Li SNMP_SYNTAX_NULL terminates the array. .It Va offset This is the starting offset of the value of the column in the return structures. This field can be set with the ISO-C .Fn offsetof macro. .El .El .Pp Both table fetching functions return TAILQ (see .Xr queue 3 ) of structures--one for each table row. These structures must start with a .Fn TAILQ_ENTRY and a .Vt uint64_t and are allocated via .Xr malloc 3 . The .Fa list argument of the table functions must point to a .Fn TAILQ_HEAD . The .Vt uint64_t fields, usually called .Va found is used to indicate which of the columns have been found for the given row. It is encoded like the .Fa req_mask field. .Pp The function .Fn snmp_table_fetch synchronously fetches the given table. If everything is ok 0 is returned. Otherwise the function returns -1 and sets an appropriate error string. The function .Fn snmp_table_fetch_async fetches the tables asynchronously. If either the entire table is fetch, or an error occurs the callback function .Fa callback is called with the callers arguments .Fa list and .Fa uarg and a parameter that is either 0 if the table was fetched, or -1 if there was an error. The function itself returns -1 if it could not initialize fetching of the table. .Pp The following table description is used to fetch the ATM interface table: .Bd -literal -offset indent /* * ATM interface table */ struct atmif { TAILQ_ENTRY(atmif) link; uint64_t found; int32_t index; u_char *ifname; size_t ifnamelen; uint32_t node_id; uint32_t pcr; int32_t media; uint32_t vpi_bits; uint32_t vci_bits; uint32_t max_vpcs; uint32_t max_vccs; u_char *esi; size_t esilen; int32_t carrier; }; TAILQ_HEAD(atmif_list, atmif); /* list of all ATM interfaces */ struct atmif_list atmif_list; static const struct snmp_table atmif_table = { OIDX_begemotAtmIfTable, OIDX_begemotAtmIfTableLastChange, 2, sizeof(struct atmif), 1, 0x7ffULL, { { 0, SNMP_SYNTAX_INTEGER, offsetof(struct atmif, index) }, { 1, SNMP_SYNTAX_OCTETSTRING, offsetof(struct atmif, ifname) }, { 2, SNMP_SYNTAX_GAUGE, offsetof(struct atmif, node_id) }, { 3, SNMP_SYNTAX_GAUGE, offsetof(struct atmif, pcr) }, { 4, SNMP_SYNTAX_INTEGER, offsetof(struct atmif, media) }, { 5, SNMP_SYNTAX_GAUGE, offsetof(struct atmif, vpi_bits) }, { 6, SNMP_SYNTAX_GAUGE, offsetof(struct atmif, vci_bits) }, { 7, SNMP_SYNTAX_GAUGE, offsetof(struct atmif, max_vpcs) }, { 8, SNMP_SYNTAX_GAUGE, offsetof(struct atmif, max_vccs) }, { 9, SNMP_SYNTAX_OCTETSTRING, offsetof(struct atmif, esi) }, { 10, SNMP_SYNTAX_INTEGER, offsetof(struct atmif, carrier) }, { 0, SNMP_SYNTAX_NULL, 0 } } }; \&... if (snmp_table_fetch(&atmif_table, &atmif_list) != 0) errx(1, "AtmIf table: %s", snmp_client.error); \&... .Ed .Pp The function .Fn snmp_dialog is used to execute a synchonuous dialog with the agent. The request PDU .Fa req is sent and the function blocks until the response PDU is received. Note, that asynchonuous receives are handled (i.e. callback functions of other send calls or table fetches may be called while in the function). The response PDU is returned in .Fa resp . If no response could be received after all timeouts and retries, the function returns -1. If a response was received 0 is returned. .Pp The function .Fn snmp_discover_engine is used to discover the authorative snmpEngineId of a remote SNMPv3 agent. A request PDU with empty USM user name is sent and the client's engine parameters are set according to the snmpEngine parameters received in the response PDU. If the client is configured to use authentication and/or privacy and the snmpEngineBoots and/or snmpEngineTime in the response had zero values, an additional request (possibly encrypted) with the appropriate user credentials is sent to fetch the missing values. Note, that the function blocks until the discovery proccess is completed. If no response could be received after all timeouts and retries, or the response contained errors the function returns -1. If the discovery proccess was completed 0 is returned. .Pp The function .Fn snmp_parse_server is used to parse an SNMP server specification string and fill in the fields of a .Vt struct snmp_client . The syntax of a server specification is .Pp .D1 [trans::][community@][server][:port] .Pp where .Va trans is the transport name (one of udp, stream or dgram), .Va community is the string to be used for both the read and the write community, .Va server is the server's host name in case of UDP and the path name in case of a local socket, and .Va port is the port in case of UDP transport. The function returns 0 in the case of success and return -1 and sets the error string in case of an error. .Sh DIAGNOSTICS If an error occurs in any of the function an error indication as described above is returned. Additionally the function sets a printable error string in the .Va error filed of .Va snmp_client . .Sh SEE ALSO .Xr gensnmptree 1 , .Xr bsnmpd 1 , .Xr bsnmpagent 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 .An Kendy Kutzner Aq kutzner@fokus.gmd.de Index: head/contrib/bsnmp/lib/bsnmplib.3 =================================================================== --- head/contrib/bsnmp/lib/bsnmplib.3 (revision 310647) +++ head/contrib/bsnmp/lib/bsnmplib.3 (revision 310648) @@ -1,514 +1,514 @@ .\" .\" Copyright (c) 2010 The FreeBSD Foundation .\" All rights reserved. .\" .\" Portions of this documentation were written by Shteryana Sotirova Shopova .\" under sponsorship from the FreeBSD Foundation. .\" .\" 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/lib/bsnmplib.3,v 1.9 2005/10/04 08:46:51 brandt_h Exp $ .\" .Dd December 19, 2010 .Dt BSNMPLIB 3 .Os .Sh NAME .Nm snmp_value_free , .Nm snmp_value_parse , .Nm snmp_value_copy , .Nm snmp_pdu_free , .Nm snmp_pdu_decode , .Nm snmp_pdu_encode , .Nm snmp_pdu_decode_header , .Nm snmp_pdu_decode_scoped , .Nm snmp_pdu_decode_secmode , .Nm snmp_pdu_init_secparams , .Nm snmp_pdu_dump , .Nm snmp_passwd_to_keys , .Nm snmp_get_local_keys , .Nm snmp_calc_keychange , .Nm TRUTH_MK , .Nm TRUTH_GET , .Nm TRUTH_OK .Nd "SNMP decoding and encoding library" .Sh LIBRARY Begemot SNMP library .Pq libbsnmp, -lbsnmp .Sh SYNOPSIS .In bsnmp/asn1.h .In bsnmp/snmp.h .Ft void .Fn snmp_value_free "struct snmp_value *value" .Ft int .Fn snmp_value_parse "const char *buf" "enum snmp_syntax" "union snmp_values *value" .Ft int .Fn snmp_value_copy "struct snmp_value *to" "const struct snmp_value *from" .Ft void .Fn snmp_pdu_free "struct snmp_pdu *value" .Ft enum snmp_code .Fn snmp_pdu_decode "struct asn_buf *buf" "struct snmp_pdu *pdu" "int32_t *ip" .Ft enum snmp_code .Fn snmp_pdu_encode "struct snmp_pdu *pdu" "struct asn_buf *buf" .Ft enum snmp_code .Fn snmp_pdu_decode_header "struct snmp_pdu *pdu" "struct asn_buf *buf" .Ft enum snmp_code .Fn snmp_pdu_decode_scoped "struct asn_buf *buf" "struct snmp_pdu *pdu" "int32_t *ip" .Ft enum snmp_code .Fn snmp_pdu_decode_secmode "struct asn_buf *buf" "struct snmp_pdu *pdu" .Ft void .Fn snmp_pdu_init_secparams "struct snmp_pdu *pdu" .Ft void .Fn snmp_pdu_dump "const struct snmp_pdu *pdu" .Ft enum snmp_code .Fn snmp_passwd_to_keys "struct snmp_user *user" "char *passwd" .Ft enum snmp_code .Fn snmp_get_local_keys "struct snmp_user *user" "uint8_t *eid" "uint32_t elen" .Ft enum snmp_code .Fn snmp_calc_keychange "struct snmp_user *user" "uint8_t *keychange" .Ft int .Fn TRUTH_MK "F" .Ft int .Fn TRUTH_GET "T" .Ft int .Fn TRUTH_OK "T" .Sh DESCRIPTION The SNMP library contains routines to handle SNMP version 1, 2 and 3 PDUs. There are several basic structures used throughout the library: .Bd -literal -offset indent struct snmp_value { struct asn_oid var; enum snmp_syntax syntax; union snmp_values { int32_t integer;/* also integer32 */ struct { u_int len; u_char *octets; } octetstring; struct asn_oid oid; u_char ipaddress[4]; uint32_t uint32; /* also gauge32, counter32, unsigned32, timeticks */ uint64_t counter64; } v; }; .Ed .Pp This structure represents one variable binding from an SNMP PDU. The field .Fa var is the ASN.1 of the variable that is bound. .Fa syntax contains either the syntax code of the value or an exception code for SNMPv2 and may be one of: .Bd -literal -offset indent enum snmp_syntax { SNMP_SYNTAX_NULL = 0, SNMP_SYNTAX_INTEGER, /* == INTEGER32 */ SNMP_SYNTAX_OCTETSTRING, SNMP_SYNTAX_OID, SNMP_SYNTAX_IPADDRESS, SNMP_SYNTAX_COUNTER, SNMP_SYNTAX_GAUGE, /* == UNSIGNED32 */ SNMP_SYNTAX_TIMETICKS, /* v2 additions */ SNMP_SYNTAX_COUNTER64, /* exceptions */ SNMP_SYNTAX_NOSUCHOBJECT, SNMP_SYNTAX_NOSUCHINSTANCE, SNMP_SYNTAX_ENDOFMIBVIEW, }; .Ed The field .Fa v holds the actual value depending on .Fa syntax . Note, that if .Fa syntax is .Li SNMP_SYNTAX_OCTETSTRING and .Fa v.octetstring.len is not zero, .Fa v.octetstring.octets points to a string allocated by .Xr malloc 3 . .Bd -literal -offset indent #define SNMP_ENGINE_ID_SIZ 32 struct snmp_engine { uint8_t engine_id[SNMP_ENGINE_ID_SIZ]; uint32_t engine_len; int32_t engine_boots; int32_t engine_time; int32_t max_msg_size; }; .Ed .Pp This structure represents an SNMP engine as specified by the SNMP Management Architecture described in RFC 3411. .Bd -literal -offset indent #define SNMP_ADM_STR32_SIZ (32 + 1) #define SNMP_AUTH_KEY_SIZ 40 #define SNMP_PRIV_KEY_SIZ 32 enum snmp_usm_level { SNMP_noAuthNoPriv = 1, SNMP_authNoPriv = 2, SNMP_authPriv = 3 }; struct snmp_user { char sec_name[SNMP_ADM_STR32_SIZ]; enum snmp_authentication auth_proto; enum snmp_privacy priv_proto; uint8_t auth_key[SNMP_AUTH_KEY_SIZ]; uint8_t priv_key[SNMP_PRIV_KEY_SIZ]; }; .Ed .Pp This structure represents an SNMPv3 user as specified by the User-based Security Model (USM) described in RFC 3414. The field .Fa sec_name is a human readable string containing the security user name. .Fa auth_proto contains the id of the authentication protocol in use by the user and may be one of: .Bd -literal -offset indent enum snmp_authentication { SNMP_AUTH_NOAUTH = 0, SNMP_AUTH_HMAC_MD5, SNMP_AUTH_HMAC_SHA }; .Ed .Fa priv_proto contains the id of the privacy protocol in use by the user and may be one of: .Bd -literal -offset indent enum snmp_privacy { SNMP_PRIV_NOPRIV = 0, SNMP_PRIV_DES = 1, SNMP_PRIV_AES }; .Ed .Fa auth_key and .Fa priv_key contain the authentication and privacy keys for the user. .Bd -literal -offset indent #define SNMP_COMMUNITY_MAXLEN 128 #define SNMP_MAX_BINDINGS 100 #define SNMP_CONTEXT_NAME_SIZ (32 + 1) #define SNMP_TIME_WINDOW 150 #define SNMP_USM_AUTH_SIZE 12 #define SNMP_USM_PRIV_SIZE 8 #define SNMP_MSG_AUTH_FLAG 0x1 #define SNMP_MSG_PRIV_FLAG 0x2 #define SNMP_MSG_REPORT_FLAG 0x4 #define SNMP_MPM_SNMP_V1 0 #define SNMP_MPM_SNMP_V2c 1 #define SNMP_MPM_SNMP_V3 3 struct snmp_pdu { char community[SNMP_COMMUNITY_MAXLEN + 1]; enum snmp_version version; u_int type; /* SNMPv3 PDU header fields */ int32_t identifier; uint8_t flags; int32_t security_model; struct snmp_engine engine; /* Associated USM user parameters */ struct snmp_user user; uint8_t msg_digest[SNMP_USM_AUTH_SIZE]; uint8_t msg_salt[SNMP_USM_PRIV_SIZE]; /* View-based Access Model */ uint32_t context_engine_len; uint8_t context_engine[SNMP_ENGINE_ID_SIZ]; char context_name[SNMP_CONTEXT_NAME_SIZ]; /* trap only */ struct asn_oid enterprise; u_char agent_addr[4]; int32_t generic_trap; int32_t specific_trap; uint32_t time_stamp; /* others */ int32_t request_id; int32_t error_status; int32_t error_index; /* fixes for encoding */ size_t outer_len; size_t scoped_len; u_char *outer_ptr; u_char *digest_ptr; u_char *encrypted_ptr; u_char *scoped_ptr; u_char *pdu_ptr; u_char *vars_ptr; struct snmp_value bindings[SNMP_MAX_BINDINGS]; u_int nbindings; }; .Ed This structure contains a decoded SNMP PDU. .Fa version is one of .Bd -literal -offset indent enum snmp_version { SNMP_Verr = 0, SNMP_V1 = 1, SNMP_V2c, SNMP_V3 }; .Ed and .Fa type is the type of the PDU. .Fa security_model is the security model used for SNMPv3 PDUs. The only supported value currently is 3 (User-based Security Model). Additional values for any, unknown, SNMPv1 and SNMPv2c security models are also enumerated .Bd -literal -offset indent enum snmp_secmodel { SNMP_SECMODEL_ANY = 0, SNMP_SECMODEL_SNMPv1 = 1, SNMP_SECMODEL_SNMPv2c = 2, SNMP_SECMODEL_USM = 3, SNMP_SECMODEL_UNKNOWN }; .Ed .Pp The function .Fn snmp_value_free is used to free all the dynamic allocated contents of an SNMP value. It does not free the structure pointed to by .Fa value itself. .Pp The function .Fn snmp_value_parse parses the ASCII representation of an SNMP value into its binary form. This function is mainly used by the configuration file reader of .Xr bsnmpd 1 . .Pp The function .Fn snmp_value_copy makes a deep copy of the value pointed to by .Fa from to the structure pointed to by .Fa to . It assumes that .Fa to is uninitialized and will overwrite its previous contents. It does not itself allocate the structure pointed to by .Fa to . .Pp The function .Fn snmp_pdu_free frees all the dynamically allocated components of the PDU. It does not itself free the structure pointed to by .Fa pdu . .Pp The function .Fn snmp_pdu_decode decodes the PDU pointed to by .Fa buf and stores the result into .Fa pdu . If an error occurs in a variable binding the (1 based) index of this binding is stored in the variable pointed to by .Fa ip . .Pp The function .Fn snmp_pdu_encode encodes the PDU .Fa pdu into the an octetstring in buffer, and if authentication and privacy are used, calculates a message digest and encrypts the PDU data in the buffer .Fa buf . .Pp The function .Fn snmp_pdu_decode_header decodes the header of the PDU pointed to by .Fa buf . The uncoded PDU contents remain in the buffer. .Pp The function .Fn snmp_pdu_decode_scoped decodes the scoped PDU pointed to by .Fa buf . .Pp The function .Fn snmp_pdu_decode_secmode verifies the authentication parameter contained in the PDU (if present) and if the PDU is encrypted, decrypts the PDU contents pointed to by .Fa buf . If successfull, a plain text scoped PDU is stored in the buffer. .Pp The function .Fn snmp_pdu_init_secparams calculates the initialization vector for the privacy protocol in use before the PDU pointed to by .Fa pdu may be encrypted or decrypted. .Pp The function .Fn snmp_pdu_dump dumps the PDU in a human readable form by calling .Fn snmp_printf . .Pp The function .Fn snmp_passwd_to_keys calculates a binary private authentication key corresponding to a plain text human readable password string. The calculated key is placed in the .Fa auth_key field of the .Fa user . .Pp The function .Fn snmp_get_local_keys calculates a localazied authentication and privacy keys for a specified SNMPv3 engine. The calculateds keys are placed in the .Fa auth_key and .Fa priv_key fields of the .Fa user . .Pp The function .Fn snmp_calc_keychange calculates a binary key change octet string based on the contents of an old and a new binary localized key. The rezult is placed in the buffer pointer to by .Fa keychange and may be used by an SNMPv3 user who wishes to change his/her password or localized key. .Pp The function .Fn TRUTH_MK takes a C truth value (zero or non-zero) and makes an SNMP truth value (2 or 1). The function .Fn TRUTH_GET takes an SNMP truth value and makes a C truth value (0 or 1). The function .Fn TRUTH_OK checks, whether its argument is a legal SNMP truth value. .Sh DIAGNOSTICS When an error occurs in any of the function the function pointed to by the global pointer .Bd -literal -offset indent extern void (*snmp_error)(const char *, ...); .Ed .Pp with a .Xr printf 3 style format string. There is a default error handler in the library that prints a message starting with .Sq SNMP: followed by the error message to standard error. .Pp The function pointed to by .Bd -literal -offset indent extern void (*snmp_printf)(const char *, ...); .Ed .Pp is called by the .Fn snmp_pdu_dump function. The default handler is .Xr printf 3 . .Sh ERRORS .Fn snmp_pdu_decode will return one of the following return codes: .Bl -tag -width Er .It Bq Er SNMP_CODE_OK Success. .It Bq Er SNMP_CODE_FAILED The ASN.1 coding was wrong. .It Bq Er SNMP_CODE_BADLEN A variable binding value had a wrong length field. .It Bq Er SNMP_CODE_OORANGE A variable binding value was out of the allowed range. .It Bq Er SNMP_CODE_BADVERS The PDU is of an unsupported version. .It Bq Er SNMP_CODE_BADENQ There was an ASN.1 value with an unsupported tag. .It Bq Er SNMP_CODE_BADSECLEVEL The requested securityLevel contained in the PDU is not supported. .It Bq Er SNMP_CODE_BADDIGEST The PDU authentication parameter received in the PDU did not match the calculated message digest. .It Bq Er SNMP_CODE_EDECRYPT Error occured while trying to decrypt the PDU. .El .Pp .Fn snmp_pdu_encode will return one of the following return codes: .Bl -tag -width Er .It Bq Er SNMP_CODE_OK Success. .It Bq Er SNMP_CODE_FAILED Encoding failed. .El .Sh SEE ALSO .Xr gensnmptree 1 , .Xr bsnmpd 1 , .Xr bsnmpagent 3 , .Xr bsnmpclient 3 , .Xr bsnmplib 3 .Sh CAVEAT The SNMPv3 message digests, encryption and decryption, and key routines use the cryptographic functions from .Xr crypto 3 . The library may optionally be built without references to the .Xr crypto 3 library. In such case only plain text SNMPv3 PDUs without message digests may be proccessed correctly. .Sh STANDARDS This implementation conforms to the applicable IETF RFCs and ITU-T recommendations. .Sh AUTHORS The Begemot SNMP library was originally written by .An Hartmut Brandt Aq harti@FreeBSD.org .Pp .An Shteryana Shopova Aq syrinx@FreeBSD.org added support for the SNMPv3 message proccessing and User-Based Security model message authentication and privacy. Index: head/contrib/bsnmp/lib/snmp.c =================================================================== --- head/contrib/bsnmp/lib/snmp.c (revision 310647) +++ head/contrib/bsnmp/lib/snmp.c (revision 310648) @@ -1,1444 +1,1444 @@ /* * Copyright (c) 2001-2003 * Fraunhofer Institute for Open Communication Systems (FhG Fokus). * All rights reserved. * * Author: Harti Brandt - * + * * Copyright (c) 2010 The FreeBSD Foundation * All rights reserved. * * Portions of this software were developed by Shteryana Sotirova Shopova * under sponsorship from the FreeBSD Foundation. * * 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/snmp.c,v 1.40 2005/10/04 14:32:42 brandt_h Exp $ * * SNMP */ #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_STDINT_H #include #elif defined(HAVE_INTTYPES_H) #include #endif #include #include #include "asn1.h" #include "snmp.h" #include "snmppriv.h" static void snmp_error_func(const char *, ...); static void snmp_printf_func(const char *, ...); void (*snmp_error)(const char *, ...) = snmp_error_func; void (*snmp_printf)(const char *, ...) = snmp_printf_func; /* * Get the next variable binding from the list. * ASN errors on the sequence or the OID are always fatal. */ static enum asn_err get_var_binding(struct asn_buf *b, struct snmp_value *binding) { u_char type; asn_len_t len, trailer; enum asn_err err; if (asn_get_sequence(b, &len) != ASN_ERR_OK) { snmp_error("cannot parse varbind header"); return (ASN_ERR_FAILED); } /* temporary truncate the length so that the parser does not * eat up bytes behind the sequence in the case the encoding is * wrong of inner elements. */ trailer = b->asn_len - len; b->asn_len = len; if (asn_get_objid(b, &binding->var) != ASN_ERR_OK) { snmp_error("cannot parse binding objid"); return (ASN_ERR_FAILED); } if (asn_get_header(b, &type, &len) != ASN_ERR_OK) { snmp_error("cannot parse binding value header"); return (ASN_ERR_FAILED); } switch (type) { case ASN_TYPE_NULL: binding->syntax = SNMP_SYNTAX_NULL; err = asn_get_null_raw(b, len); break; case ASN_TYPE_INTEGER: binding->syntax = SNMP_SYNTAX_INTEGER; err = asn_get_integer_raw(b, len, &binding->v.integer); break; case ASN_TYPE_OCTETSTRING: binding->syntax = SNMP_SYNTAX_OCTETSTRING; binding->v.octetstring.octets = malloc(len); if (binding->v.octetstring.octets == NULL) { snmp_error("%s", strerror(errno)); return (ASN_ERR_FAILED); } binding->v.octetstring.len = len; err = asn_get_octetstring_raw(b, len, binding->v.octetstring.octets, &binding->v.octetstring.len); if (ASN_ERR_STOPPED(err)) { free(binding->v.octetstring.octets); binding->v.octetstring.octets = NULL; } break; case ASN_TYPE_OBJID: binding->syntax = SNMP_SYNTAX_OID; err = asn_get_objid_raw(b, len, &binding->v.oid); break; case ASN_CLASS_APPLICATION|ASN_APP_IPADDRESS: binding->syntax = SNMP_SYNTAX_IPADDRESS; err = asn_get_ipaddress_raw(b, len, binding->v.ipaddress); break; case ASN_CLASS_APPLICATION|ASN_APP_TIMETICKS: binding->syntax = SNMP_SYNTAX_TIMETICKS; err = asn_get_uint32_raw(b, len, &binding->v.uint32); break; case ASN_CLASS_APPLICATION|ASN_APP_COUNTER: binding->syntax = SNMP_SYNTAX_COUNTER; err = asn_get_uint32_raw(b, len, &binding->v.uint32); break; case ASN_CLASS_APPLICATION|ASN_APP_GAUGE: binding->syntax = SNMP_SYNTAX_GAUGE; err = asn_get_uint32_raw(b, len, &binding->v.uint32); break; case ASN_CLASS_APPLICATION|ASN_APP_COUNTER64: binding->syntax = SNMP_SYNTAX_COUNTER64; err = asn_get_counter64_raw(b, len, &binding->v.counter64); break; case ASN_CLASS_CONTEXT | ASN_EXCEPT_NOSUCHOBJECT: binding->syntax = SNMP_SYNTAX_NOSUCHOBJECT; err = asn_get_null_raw(b, len); break; case ASN_CLASS_CONTEXT | ASN_EXCEPT_NOSUCHINSTANCE: binding->syntax = SNMP_SYNTAX_NOSUCHINSTANCE; err = asn_get_null_raw(b, len); break; case ASN_CLASS_CONTEXT | ASN_EXCEPT_ENDOFMIBVIEW: binding->syntax = SNMP_SYNTAX_ENDOFMIBVIEW; err = asn_get_null_raw(b, len); break; default: if ((err = asn_skip(b, len)) == ASN_ERR_OK) err = ASN_ERR_TAG; snmp_error("bad binding value type 0x%x", type); break; } if (ASN_ERR_STOPPED(err)) { snmp_error("cannot parse binding value"); return (err); } if (b->asn_len != 0) snmp_error("ignoring junk at end of binding"); b->asn_len = trailer; return (err); } /* * Parse the different PDUs contents. Any ASN error in the outer components * are fatal. Only errors in variable values may be tolerated. If all * components can be parsed it returns either ASN_ERR_OK or the first * error that was found. */ enum asn_err snmp_parse_pdus_hdr(struct asn_buf *b, struct snmp_pdu *pdu, asn_len_t *lenp) { if (pdu->type == SNMP_PDU_TRAP) { if (asn_get_objid(b, &pdu->enterprise) != ASN_ERR_OK) { snmp_error("cannot parse trap enterprise"); return (ASN_ERR_FAILED); } if (asn_get_ipaddress(b, pdu->agent_addr) != ASN_ERR_OK) { snmp_error("cannot parse trap agent address"); return (ASN_ERR_FAILED); } if (asn_get_integer(b, &pdu->generic_trap) != ASN_ERR_OK) { snmp_error("cannot parse 'generic-trap'"); return (ASN_ERR_FAILED); } if (asn_get_integer(b, &pdu->specific_trap) != ASN_ERR_OK) { snmp_error("cannot parse 'specific-trap'"); return (ASN_ERR_FAILED); } if (asn_get_timeticks(b, &pdu->time_stamp) != ASN_ERR_OK) { snmp_error("cannot parse trap 'time-stamp'"); return (ASN_ERR_FAILED); } } else { if (asn_get_integer(b, &pdu->request_id) != ASN_ERR_OK) { snmp_error("cannot parse 'request-id'"); return (ASN_ERR_FAILED); } if (asn_get_integer(b, &pdu->error_status) != ASN_ERR_OK) { snmp_error("cannot parse 'error_status'"); return (ASN_ERR_FAILED); } if (asn_get_integer(b, &pdu->error_index) != ASN_ERR_OK) { snmp_error("cannot parse 'error_index'"); return (ASN_ERR_FAILED); } } if (asn_get_sequence(b, lenp) != ASN_ERR_OK) { snmp_error("cannot get varlist header"); return (ASN_ERR_FAILED); } return (ASN_ERR_OK); } static enum asn_err parse_pdus(struct asn_buf *b, struct snmp_pdu *pdu, int32_t *ip) { asn_len_t len, trailer; struct snmp_value *v; enum asn_err err, err1; err = snmp_parse_pdus_hdr(b, pdu, &len); if (ASN_ERR_STOPPED(err)) return (err); trailer = b->asn_len - len; v = pdu->bindings; err = ASN_ERR_OK; while (b->asn_len != 0) { if (pdu->nbindings == SNMP_MAX_BINDINGS) { snmp_error("too many bindings (> %u) in PDU", SNMP_MAX_BINDINGS); return (ASN_ERR_FAILED); } err1 = get_var_binding(b, v); if (ASN_ERR_STOPPED(err1)) return (ASN_ERR_FAILED); if (err1 != ASN_ERR_OK && err == ASN_ERR_OK) { err = err1; *ip = pdu->nbindings + 1; } pdu->nbindings++; v++; } b->asn_len = trailer; return (err); } static enum asn_err parse_secparams(struct asn_buf *b, struct snmp_pdu *pdu) { asn_len_t octs_len; u_char buf[256]; /* XXX: calc max possible size here */ struct asn_buf tb; memset(buf, 0, 256); tb.asn_ptr = buf; tb.asn_len = 256; u_int len = 256; if (asn_get_octetstring(b, buf, &len) != ASN_ERR_OK) { snmp_error("cannot parse usm header"); return (ASN_ERR_FAILED); } tb.asn_len = len; if (asn_get_sequence(&tb, &octs_len) != ASN_ERR_OK) { snmp_error("cannot decode usm header"); return (ASN_ERR_FAILED); } octs_len = SNMP_ENGINE_ID_SIZ; if (asn_get_octetstring(&tb, (u_char *)&pdu->engine.engine_id, &octs_len) != ASN_ERR_OK) { snmp_error("cannot decode msg engine id"); return (ASN_ERR_FAILED); } pdu->engine.engine_len = octs_len; if (asn_get_integer(&tb, &pdu->engine.engine_boots) != ASN_ERR_OK) { snmp_error("cannot decode msg engine boots"); return (ASN_ERR_FAILED); } if (asn_get_integer(&tb, &pdu->engine.engine_time) != ASN_ERR_OK) { snmp_error("cannot decode msg engine time"); return (ASN_ERR_FAILED); } octs_len = SNMP_ADM_STR32_SIZ - 1; if (asn_get_octetstring(&tb, (u_char *)&pdu->user.sec_name, &octs_len) != ASN_ERR_OK) { snmp_error("cannot decode msg user name"); return (ASN_ERR_FAILED); } pdu->user.sec_name[octs_len] = '\0'; octs_len = sizeof(pdu->msg_digest); if (asn_get_octetstring(&tb, (u_char *)&pdu->msg_digest, &octs_len) != ASN_ERR_OK || ((pdu->flags & SNMP_MSG_AUTH_FLAG) != 0 && octs_len != sizeof(pdu->msg_digest))) { snmp_error("cannot decode msg authentication param"); return (ASN_ERR_FAILED); } octs_len = sizeof(pdu->msg_salt); if (asn_get_octetstring(&tb, (u_char *)&pdu->msg_salt, &octs_len) != ASN_ERR_OK ||((pdu->flags & SNMP_MSG_PRIV_FLAG) != 0 && octs_len != sizeof(pdu->msg_salt))) { snmp_error("cannot decode msg authentication param"); return (ASN_ERR_FAILED); } if ((pdu->flags & SNMP_MSG_AUTH_FLAG) != 0) { pdu->digest_ptr = b->asn_ptr - SNMP_USM_AUTH_SIZE; pdu->digest_ptr -= octs_len + ASN_MAXLENLEN; } return (ASN_ERR_OK); } static enum snmp_code pdu_encode_secparams(struct asn_buf *b, struct snmp_pdu *pdu) { u_char buf[256], *sptr; struct asn_buf tb; size_t auth_off, moved = 0; auth_off = 0; memset(buf, 0, 256); tb.asn_ptr = buf; tb.asn_len = 256; if (asn_put_temp_header(&tb, (ASN_TYPE_SEQUENCE|ASN_TYPE_CONSTRUCTED), &sptr) != ASN_ERR_OK) return (SNMP_CODE_FAILED); if (asn_put_octetstring(&tb, (u_char *)pdu->engine.engine_id, pdu->engine.engine_len) != ASN_ERR_OK) return (SNMP_CODE_FAILED); if (asn_put_integer(&tb, pdu->engine.engine_boots) != ASN_ERR_OK) return (SNMP_CODE_FAILED); if (asn_put_integer(&tb, pdu->engine.engine_time) != ASN_ERR_OK) return (SNMP_CODE_FAILED); if (asn_put_octetstring(&tb, (u_char *)pdu->user.sec_name, strlen(pdu->user.sec_name)) != ASN_ERR_OK) return (SNMP_CODE_FAILED); if ((pdu->flags & SNMP_MSG_AUTH_FLAG) != 0) { auth_off = sizeof(buf) - tb.asn_len + ASN_MAXLENLEN; if (asn_put_octetstring(&tb, (u_char *)pdu->msg_digest, sizeof(pdu->msg_digest)) != ASN_ERR_OK) return (SNMP_CODE_FAILED); } else { if (asn_put_octetstring(&tb, (u_char *)pdu->msg_digest, 0) != ASN_ERR_OK) return (SNMP_CODE_FAILED); } if ((pdu->flags & SNMP_MSG_PRIV_FLAG) != 0) { if (asn_put_octetstring(&tb, (u_char *)pdu->msg_salt, sizeof(pdu->msg_salt)) != ASN_ERR_OK) return (SNMP_CODE_FAILED); } else { if (asn_put_octetstring(&tb, (u_char *)pdu->msg_salt, 0) != ASN_ERR_OK) return (SNMP_CODE_FAILED); } if (asn_commit_header(&tb, sptr, &moved) != ASN_ERR_OK) return (SNMP_CODE_FAILED); if ((pdu->flags & SNMP_MSG_AUTH_FLAG) != 0) pdu->digest_ptr = b->asn_ptr + auth_off - moved; if (asn_put_octetstring(b, buf, sizeof(buf) - tb.asn_len) != ASN_ERR_OK) return (SNMP_CODE_FAILED); pdu->digest_ptr += ASN_MAXLENLEN; if ((pdu->flags & SNMP_MSG_PRIV_FLAG) != 0 && asn_put_temp_header(b, ASN_TYPE_OCTETSTRING, &pdu->encrypted_ptr) != ASN_ERR_OK) return (SNMP_CODE_FAILED); return (SNMP_CODE_OK); } /* * Decode the PDU except for the variable bindings itself. * If decoding fails because of a bad binding, but the rest can be * decoded, ip points to the index of the failed variable (errors * OORANGE, BADLEN or BADVERS). */ enum snmp_code snmp_pdu_decode(struct asn_buf *b, struct snmp_pdu *pdu, int32_t *ip) { enum snmp_code code; if ((code = snmp_pdu_decode_header(b, pdu)) != SNMP_CODE_OK) return (code); if (pdu->version == SNMP_V3) { if (pdu->security_model != SNMP_SECMODEL_USM) return (SNMP_CODE_FAILED); if ((code = snmp_pdu_decode_secmode(b, pdu)) != SNMP_CODE_OK) return (code); } code = snmp_pdu_decode_scoped(b, pdu, ip); switch (code) { case SNMP_CODE_FAILED: snmp_pdu_free(pdu); break; case SNMP_CODE_BADENC: if (pdu->version == SNMP_Verr) return (SNMP_CODE_BADVERS); default: break; } return (code); } enum snmp_code snmp_pdu_decode_header(struct asn_buf *b, struct snmp_pdu *pdu) { int32_t version; u_int octs_len; asn_len_t len; pdu->outer_ptr = b->asn_ptr; pdu->outer_len = b->asn_len; if (asn_get_sequence(b, &len) != ASN_ERR_OK) { snmp_error("cannot decode pdu header"); return (SNMP_CODE_FAILED); } if (b->asn_len < len) { snmp_error("outer sequence value too short"); return (SNMP_CODE_FAILED); } if (b->asn_len != len) { snmp_error("ignoring trailing junk in message"); b->asn_len = len; } if (asn_get_integer(b, &version) != ASN_ERR_OK) { snmp_error("cannot decode version"); return (SNMP_CODE_FAILED); } if (version == 0) pdu->version = SNMP_V1; else if (version == 1) pdu->version = SNMP_V2c; else if (version == 3) pdu->version = SNMP_V3; else { pdu->version = SNMP_Verr; snmp_error("unsupported SNMP version"); return (SNMP_CODE_BADENC); } if (pdu->version == SNMP_V3) { if (asn_get_sequence(b, &len) != ASN_ERR_OK) { snmp_error("cannot decode pdu global data header"); return (SNMP_CODE_FAILED); } if (asn_get_integer(b, &pdu->identifier) != ASN_ERR_OK) { snmp_error("cannot decode msg indetifier"); return (SNMP_CODE_FAILED); } if (asn_get_integer(b, &pdu->engine.max_msg_size) != ASN_ERR_OK) { snmp_error("cannot decode msg size"); return (SNMP_CODE_FAILED); } octs_len = 1; if (asn_get_octetstring(b, (u_char *)&pdu->flags, &octs_len) != ASN_ERR_OK) { snmp_error("cannot decode msg flags"); return (SNMP_CODE_FAILED); } if (asn_get_integer(b, &pdu->security_model) != ASN_ERR_OK) { snmp_error("cannot decode msg size"); return (SNMP_CODE_FAILED); } if (pdu->security_model != SNMP_SECMODEL_USM) return (SNMP_CODE_FAILED); if (parse_secparams(b, pdu) != ASN_ERR_OK) return (SNMP_CODE_FAILED); } else { octs_len = SNMP_COMMUNITY_MAXLEN; if (asn_get_octetstring(b, (u_char *)pdu->community, &octs_len) != ASN_ERR_OK) { snmp_error("cannot decode community"); return (SNMP_CODE_FAILED); } pdu->community[octs_len] = '\0'; } return (SNMP_CODE_OK); } enum snmp_code snmp_pdu_decode_scoped(struct asn_buf *b, struct snmp_pdu *pdu, int32_t *ip) { u_char type; asn_len_t len, trailer; enum asn_err err; if (pdu->version == SNMP_V3) { if (asn_get_sequence(b, &len) != ASN_ERR_OK) { snmp_error("cannot decode scoped pdu header"); return (SNMP_CODE_FAILED); } len = SNMP_ENGINE_ID_SIZ; if (asn_get_octetstring(b, (u_char *)&pdu->context_engine, &len) != ASN_ERR_OK) { snmp_error("cannot decode msg context engine"); return (SNMP_CODE_FAILED); } pdu->context_engine_len = len; len = SNMP_CONTEXT_NAME_SIZ; if (asn_get_octetstring(b, (u_char *)&pdu->context_name, &len) != ASN_ERR_OK) { snmp_error("cannot decode msg context name"); return (SNMP_CODE_FAILED); } pdu->context_name[len] = '\0'; } if (asn_get_header(b, &type, &len) != ASN_ERR_OK) { snmp_error("cannot get pdu header"); return (SNMP_CODE_FAILED); } if ((type & ~ASN_TYPE_MASK) != (ASN_TYPE_CONSTRUCTED | ASN_CLASS_CONTEXT)) { snmp_error("bad pdu header tag"); return (SNMP_CODE_FAILED); } pdu->type = type & ASN_TYPE_MASK; switch (pdu->type) { case SNMP_PDU_GET: case SNMP_PDU_GETNEXT: case SNMP_PDU_RESPONSE: case SNMP_PDU_SET: break; case SNMP_PDU_TRAP: if (pdu->version != SNMP_V1) { snmp_error("bad pdu type %u", pdu->type); return (SNMP_CODE_FAILED); } break; case SNMP_PDU_GETBULK: case SNMP_PDU_INFORM: case SNMP_PDU_TRAP2: case SNMP_PDU_REPORT: if (pdu->version == SNMP_V1) { snmp_error("bad pdu type %u", pdu->type); return (SNMP_CODE_FAILED); } break; default: snmp_error("bad pdu type %u", pdu->type); return (SNMP_CODE_FAILED); } trailer = b->asn_len - len; b->asn_len = len; err = parse_pdus(b, pdu, ip); if (ASN_ERR_STOPPED(err)) return (SNMP_CODE_FAILED); if (b->asn_len != 0) snmp_error("ignoring trailing junk after pdu"); b->asn_len = trailer; return (SNMP_CODE_OK); } enum snmp_code snmp_pdu_decode_secmode(struct asn_buf *b, struct snmp_pdu *pdu) { u_char type; enum snmp_code code; uint8_t digest[SNMP_USM_AUTH_SIZE]; if (pdu->user.auth_proto != SNMP_AUTH_NOAUTH && (pdu->flags & SNMP_MSG_AUTH_FLAG) == 0) return (SNMP_CODE_BADSECLEVEL); if ((code = snmp_pdu_calc_digest(pdu, digest)) != SNMP_CODE_OK) return (SNMP_CODE_FAILED); if (pdu->user.auth_proto != SNMP_AUTH_NOAUTH && memcmp(digest, pdu->msg_digest, sizeof(pdu->msg_digest)) != 0) return (SNMP_CODE_BADDIGEST); if (pdu->user.priv_proto != SNMP_PRIV_NOPRIV && (asn_get_header(b, &type, &pdu->scoped_len) != ASN_ERR_OK || type != ASN_TYPE_OCTETSTRING)) { snmp_error("cannot decode encrypted pdu"); return (SNMP_CODE_FAILED); } pdu->scoped_ptr = b->asn_ptr; if (pdu->user.priv_proto != SNMP_PRIV_NOPRIV && (pdu->flags & SNMP_MSG_PRIV_FLAG) == 0) return (SNMP_CODE_BADSECLEVEL); if ((code = snmp_pdu_decrypt(pdu)) != SNMP_CODE_OK) return (SNMP_CODE_FAILED); return (code); } /* * Check whether what we have is the complete PDU by snooping at the * enclosing structure header. This returns: * -1 if there are ASN.1 errors * 0 if we need more data * > 0 the length of this PDU */ int snmp_pdu_snoop(const struct asn_buf *b0) { u_int length; asn_len_t len; struct asn_buf b = *b0; /* <0x10|0x20> */ - + if (b.asn_len == 0) return (0); if (b.asn_cptr[0] != (ASN_TYPE_SEQUENCE | ASN_TYPE_CONSTRUCTED)) { asn_error(&b, "bad sequence type %u", b.asn_cptr[0]); return (-1); } b.asn_len--; b.asn_cptr++; if (b.asn_len == 0) return (0); if (*b.asn_cptr & 0x80) { /* long length */ length = *b.asn_cptr++ & 0x7f; b.asn_len--; if (length == 0) { asn_error(&b, "indefinite length not supported"); return (-1); } if (length > ASN_MAXLENLEN) { asn_error(&b, "long length too long (%u)", length); return (-1); } if (length > b.asn_len) return (0); len = 0; while (length--) { len = (len << 8) | *b.asn_cptr++; b.asn_len--; } } else { len = *b.asn_cptr++; b.asn_len--; } if (len > b.asn_len) return (0); return (len + b.asn_cptr - b0->asn_cptr); } /* * Encode the SNMP PDU without the variable bindings field. * We do this the rather uneffective way by * moving things around and assuming that the length field will never * use more than 2 bytes. * We need a number of pointers to apply the fixes afterwards. */ enum snmp_code snmp_pdu_encode_header(struct asn_buf *b, struct snmp_pdu *pdu) { enum asn_err err; u_char *v3_hdr_ptr; if (asn_put_temp_header(b, (ASN_TYPE_SEQUENCE|ASN_TYPE_CONSTRUCTED), &pdu->outer_ptr) != ASN_ERR_OK) return (SNMP_CODE_FAILED); if (pdu->version == SNMP_V1) err = asn_put_integer(b, 0); else if (pdu->version == SNMP_V2c) err = asn_put_integer(b, 1); else if (pdu->version == SNMP_V3) err = asn_put_integer(b, 3); else return (SNMP_CODE_BADVERS); if (err != ASN_ERR_OK) return (SNMP_CODE_FAILED); if (pdu->version == SNMP_V3) { if (asn_put_temp_header(b, (ASN_TYPE_SEQUENCE | ASN_TYPE_CONSTRUCTED), &v3_hdr_ptr) != ASN_ERR_OK) return (SNMP_CODE_FAILED); - + if (asn_put_integer(b, pdu->identifier) != ASN_ERR_OK) return (SNMP_CODE_FAILED); if (asn_put_integer(b, pdu->engine.max_msg_size) != ASN_ERR_OK) return (SNMP_CODE_FAILED); if (pdu->type != SNMP_PDU_RESPONSE && pdu->type != SNMP_PDU_TRAP && pdu->type != SNMP_PDU_TRAP2 && pdu->type != SNMP_PDU_REPORT) pdu->flags |= SNMP_MSG_REPORT_FLAG; if (asn_put_octetstring(b, (u_char *)&pdu->flags, 1) != ASN_ERR_OK) return (SNMP_CODE_FAILED); if (asn_put_integer(b, pdu->security_model) != ASN_ERR_OK) return (SNMP_CODE_FAILED); if (asn_commit_header(b, v3_hdr_ptr, NULL) != ASN_ERR_OK) return (SNMP_CODE_FAILED); if (pdu->security_model != SNMP_SECMODEL_USM) return (SNMP_CODE_FAILED); if (pdu_encode_secparams(b, pdu) != SNMP_CODE_OK) return (SNMP_CODE_FAILED); /* View-based Access Conntrol information */ if (asn_put_temp_header(b, (ASN_TYPE_SEQUENCE | ASN_TYPE_CONSTRUCTED), &pdu->scoped_ptr) != ASN_ERR_OK) return (SNMP_CODE_FAILED); if (asn_put_octetstring(b, (u_char *)pdu->context_engine, pdu->context_engine_len) != ASN_ERR_OK) return (SNMP_CODE_FAILED); if (asn_put_octetstring(b, (u_char *)pdu->context_name, strlen(pdu->context_name)) != ASN_ERR_OK) return (SNMP_CODE_FAILED); } else { if (asn_put_octetstring(b, (u_char *)pdu->community, strlen(pdu->community)) != ASN_ERR_OK) return (SNMP_CODE_FAILED); } if (asn_put_temp_header(b, (ASN_TYPE_CONSTRUCTED | ASN_CLASS_CONTEXT | pdu->type), &pdu->pdu_ptr) != ASN_ERR_OK) return (SNMP_CODE_FAILED); if (pdu->type == SNMP_PDU_TRAP) { if (pdu->version != SNMP_V1 || asn_put_objid(b, &pdu->enterprise) != ASN_ERR_OK || asn_put_ipaddress(b, pdu->agent_addr) != ASN_ERR_OK || asn_put_integer(b, pdu->generic_trap) != ASN_ERR_OK || asn_put_integer(b, pdu->specific_trap) != ASN_ERR_OK || asn_put_timeticks(b, pdu->time_stamp) != ASN_ERR_OK) return (SNMP_CODE_FAILED); } else { if (pdu->version == SNMP_V1 && (pdu->type == SNMP_PDU_GETBULK || pdu->type == SNMP_PDU_INFORM || pdu->type == SNMP_PDU_TRAP2 || pdu->type == SNMP_PDU_REPORT)) return (SNMP_CODE_FAILED); if (asn_put_integer(b, pdu->request_id) != ASN_ERR_OK || asn_put_integer(b, pdu->error_status) != ASN_ERR_OK || asn_put_integer(b, pdu->error_index) != ASN_ERR_OK) return (SNMP_CODE_FAILED); } if (asn_put_temp_header(b, (ASN_TYPE_SEQUENCE|ASN_TYPE_CONSTRUCTED), &pdu->vars_ptr) != ASN_ERR_OK) return (SNMP_CODE_FAILED); return (SNMP_CODE_OK); } static enum asn_err snmp_pdu_fix_padd(struct asn_buf *b, struct snmp_pdu *pdu) { asn_len_t padlen; if (pdu->user.priv_proto == SNMP_PRIV_DES && pdu->scoped_len % 8 != 0) { padlen = 8 - (pdu->scoped_len % 8); if (asn_pad(b, padlen) != ASN_ERR_OK) return (ASN_ERR_FAILED); pdu->scoped_len += padlen; } return (ASN_ERR_OK); } enum snmp_code snmp_fix_encoding(struct asn_buf *b, struct snmp_pdu *pdu) { size_t moved = 0; enum snmp_code code; if (asn_commit_header(b, pdu->vars_ptr, NULL) != ASN_ERR_OK || asn_commit_header(b, pdu->pdu_ptr, NULL) != ASN_ERR_OK) return (SNMP_CODE_FAILED); if (pdu->version == SNMP_V3) { if (asn_commit_header(b, pdu->scoped_ptr, NULL) != ASN_ERR_OK) return (SNMP_CODE_FAILED); pdu->scoped_len = b->asn_ptr - pdu->scoped_ptr; if (snmp_pdu_fix_padd(b, pdu) != ASN_ERR_OK) return (SNMP_CODE_FAILED); if (pdu->security_model != SNMP_SECMODEL_USM) return (SNMP_CODE_FAILED); if (snmp_pdu_encrypt(pdu) != SNMP_CODE_OK) return (SNMP_CODE_FAILED); if (pdu->user.priv_proto != SNMP_PRIV_NOPRIV && asn_commit_header(b, pdu->encrypted_ptr, NULL) != ASN_ERR_OK) return (SNMP_CODE_FAILED); } if (asn_commit_header(b, pdu->outer_ptr, &moved) != ASN_ERR_OK) return (SNMP_CODE_FAILED); pdu->outer_len = b->asn_ptr - pdu->outer_ptr; pdu->digest_ptr -= moved; if (pdu->version == SNMP_V3) { if ((code = snmp_pdu_calc_digest(pdu, pdu->msg_digest)) != SNMP_CODE_OK) return (SNMP_CODE_FAILED); if ((pdu->flags & SNMP_MSG_AUTH_FLAG) != 0) memcpy(pdu->digest_ptr, pdu->msg_digest, sizeof(pdu->msg_digest)); } return (SNMP_CODE_OK); } /* * Encode a binding. Caller must ensure, that the syntax is ok for that version. * Be sure not to cobber b, when something fails. */ enum asn_err snmp_binding_encode(struct asn_buf *b, const struct snmp_value *binding) { u_char *ptr; enum asn_err err; struct asn_buf save = *b; if ((err = asn_put_temp_header(b, (ASN_TYPE_SEQUENCE | ASN_TYPE_CONSTRUCTED), &ptr)) != ASN_ERR_OK) { *b = save; return (err); } if ((err = asn_put_objid(b, &binding->var)) != ASN_ERR_OK) { *b = save; return (err); } switch (binding->syntax) { case SNMP_SYNTAX_NULL: err = asn_put_null(b); break; case SNMP_SYNTAX_INTEGER: err = asn_put_integer(b, binding->v.integer); break; case SNMP_SYNTAX_OCTETSTRING: err = asn_put_octetstring(b, binding->v.octetstring.octets, binding->v.octetstring.len); break; case SNMP_SYNTAX_OID: err = asn_put_objid(b, &binding->v.oid); break; case SNMP_SYNTAX_IPADDRESS: err = asn_put_ipaddress(b, binding->v.ipaddress); break; case SNMP_SYNTAX_TIMETICKS: err = asn_put_uint32(b, ASN_APP_TIMETICKS, binding->v.uint32); break; case SNMP_SYNTAX_COUNTER: err = asn_put_uint32(b, ASN_APP_COUNTER, binding->v.uint32); break; case SNMP_SYNTAX_GAUGE: err = asn_put_uint32(b, ASN_APP_GAUGE, binding->v.uint32); break; case SNMP_SYNTAX_COUNTER64: err = asn_put_counter64(b, binding->v.counter64); break; case SNMP_SYNTAX_NOSUCHOBJECT: err = asn_put_exception(b, ASN_EXCEPT_NOSUCHOBJECT); break; case SNMP_SYNTAX_NOSUCHINSTANCE: err = asn_put_exception(b, ASN_EXCEPT_NOSUCHINSTANCE); break; case SNMP_SYNTAX_ENDOFMIBVIEW: err = asn_put_exception(b, ASN_EXCEPT_ENDOFMIBVIEW); break; } if (err != ASN_ERR_OK) { *b = save; return (err); } err = asn_commit_header(b, ptr, NULL); if (err != ASN_ERR_OK) { *b = save; return (err); } return (ASN_ERR_OK); } /* * Encode an PDU. */ enum snmp_code snmp_pdu_encode(struct snmp_pdu *pdu, struct asn_buf *resp_b) { u_int idx; enum snmp_code err; if ((err = snmp_pdu_encode_header(resp_b, pdu)) != SNMP_CODE_OK) return (err); for (idx = 0; idx < pdu->nbindings; idx++) if (snmp_binding_encode(resp_b, &pdu->bindings[idx]) != ASN_ERR_OK) return (SNMP_CODE_FAILED); return (snmp_fix_encoding(resp_b, pdu)); } static void dump_binding(const struct snmp_value *b) { u_int i; char buf[ASN_OIDSTRLEN]; snmp_printf("%s=", asn_oid2str_r(&b->var, buf)); switch (b->syntax) { case SNMP_SYNTAX_NULL: snmp_printf("NULL"); break; case SNMP_SYNTAX_INTEGER: snmp_printf("INTEGER %d", b->v.integer); break; case SNMP_SYNTAX_OCTETSTRING: snmp_printf("OCTET STRING %lu:", b->v.octetstring.len); for (i = 0; i < b->v.octetstring.len; i++) snmp_printf(" %02x", b->v.octetstring.octets[i]); break; case SNMP_SYNTAX_OID: snmp_printf("OID %s", asn_oid2str_r(&b->v.oid, buf)); break; case SNMP_SYNTAX_IPADDRESS: snmp_printf("IPADDRESS %u.%u.%u.%u", b->v.ipaddress[0], b->v.ipaddress[1], b->v.ipaddress[2], b->v.ipaddress[3]); break; case SNMP_SYNTAX_COUNTER: snmp_printf("COUNTER %u", b->v.uint32); break; case SNMP_SYNTAX_GAUGE: snmp_printf("GAUGE %u", b->v.uint32); break; case SNMP_SYNTAX_TIMETICKS: snmp_printf("TIMETICKS %u", b->v.uint32); break; case SNMP_SYNTAX_COUNTER64: snmp_printf("COUNTER64 %lld", b->v.counter64); break; case SNMP_SYNTAX_NOSUCHOBJECT: snmp_printf("NoSuchObject"); break; case SNMP_SYNTAX_NOSUCHINSTANCE: snmp_printf("NoSuchInstance"); break; case SNMP_SYNTAX_ENDOFMIBVIEW: snmp_printf("EndOfMibView"); break; default: snmp_printf("UNKNOWN SYNTAX %u", b->syntax); break; } } static __inline void dump_bindings(const struct snmp_pdu *pdu) { u_int i; for (i = 0; i < pdu->nbindings; i++) { snmp_printf(" [%u]: ", i); dump_binding(&pdu->bindings[i]); snmp_printf("\n"); } } static __inline void dump_notrap(const struct snmp_pdu *pdu) { snmp_printf(" request_id=%d", pdu->request_id); snmp_printf(" error_status=%d", pdu->error_status); snmp_printf(" error_index=%d\n", pdu->error_index); dump_bindings(pdu); } void snmp_pdu_dump(const struct snmp_pdu *pdu) { char buf[ASN_OIDSTRLEN]; const char *vers; static const char *types[] = { [SNMP_PDU_GET] = "GET", [SNMP_PDU_GETNEXT] = "GETNEXT", [SNMP_PDU_RESPONSE] = "RESPONSE", [SNMP_PDU_SET] = "SET", [SNMP_PDU_TRAP] = "TRAPv1", [SNMP_PDU_GETBULK] = "GETBULK", [SNMP_PDU_INFORM] = "INFORM", [SNMP_PDU_TRAP2] = "TRAPv2", [SNMP_PDU_REPORT] = "REPORT", }; if (pdu->version == SNMP_V1) vers = "SNMPv1"; else if (pdu->version == SNMP_V2c) vers = "SNMPv2c"; else if (pdu->version == SNMP_V3) vers = "SNMPv3"; else vers = "v?"; switch (pdu->type) { case SNMP_PDU_TRAP: snmp_printf("%s %s '%s'", types[pdu->type], vers, pdu->community); snmp_printf(" enterprise=%s", asn_oid2str_r(&pdu->enterprise, buf)); snmp_printf(" agent_addr=%u.%u.%u.%u", pdu->agent_addr[0], pdu->agent_addr[1], pdu->agent_addr[2], pdu->agent_addr[3]); snmp_printf(" generic_trap=%d", pdu->generic_trap); snmp_printf(" specific_trap=%d", pdu->specific_trap); snmp_printf(" time-stamp=%u\n", pdu->time_stamp); dump_bindings(pdu); break; case SNMP_PDU_GET: case SNMP_PDU_GETNEXT: case SNMP_PDU_RESPONSE: case SNMP_PDU_SET: case SNMP_PDU_GETBULK: case SNMP_PDU_INFORM: case SNMP_PDU_TRAP2: case SNMP_PDU_REPORT: snmp_printf("%s %s '%s'", types[pdu->type], vers, pdu->community); dump_notrap(pdu); break; default: snmp_printf("bad pdu type %u\n", pdu->type); break; } } void snmp_value_free(struct snmp_value *value) { if (value->syntax == SNMP_SYNTAX_OCTETSTRING) free(value->v.octetstring.octets); value->syntax = SNMP_SYNTAX_NULL; } int snmp_value_copy(struct snmp_value *to, const struct snmp_value *from) { to->var = from->var; to->syntax = from->syntax; if (from->syntax == SNMP_SYNTAX_OCTETSTRING) { if ((to->v.octetstring.len = from->v.octetstring.len) == 0) to->v.octetstring.octets = NULL; else { to->v.octetstring.octets = malloc(to->v.octetstring.len); if (to->v.octetstring.octets == NULL) return (-1); (void)memcpy(to->v.octetstring.octets, from->v.octetstring.octets, to->v.octetstring.len); } } else to->v = from->v; return (0); } void snmp_pdu_init_secparams(struct snmp_pdu *pdu) { int32_t rval; if (pdu->user.auth_proto != SNMP_AUTH_NOAUTH) pdu->flags |= SNMP_MSG_AUTH_FLAG; switch (pdu->user.priv_proto) { case SNMP_PRIV_DES: memcpy(pdu->msg_salt, &pdu->engine.engine_boots, sizeof(pdu->engine.engine_boots)); rval = random(); memcpy(pdu->msg_salt + sizeof(pdu->engine.engine_boots), &rval, sizeof(int32_t)); pdu->flags |= SNMP_MSG_PRIV_FLAG; break; case SNMP_PRIV_AES: rval = random(); memcpy(pdu->msg_salt, &rval, sizeof(int32_t)); rval = random(); memcpy(pdu->msg_salt + sizeof(int32_t), &rval, sizeof(int32_t)); pdu->flags |= SNMP_MSG_PRIV_FLAG; break; default: break; } } void snmp_pdu_free(struct snmp_pdu *pdu) { u_int i; for (i = 0; i < pdu->nbindings; i++) snmp_value_free(&pdu->bindings[i]); } /* * Parse an ASCII SNMP value into the binary form */ int snmp_value_parse(const char *str, enum snmp_syntax syntax, union snmp_values *v) { char *end; switch (syntax) { case SNMP_SYNTAX_NULL: case SNMP_SYNTAX_NOSUCHOBJECT: case SNMP_SYNTAX_NOSUCHINSTANCE: case SNMP_SYNTAX_ENDOFMIBVIEW: if (*str != '\0') return (-1); return (0); case SNMP_SYNTAX_INTEGER: v->integer = strtoll(str, &end, 0); if (*end != '\0') return (-1); return (0); case SNMP_SYNTAX_OCTETSTRING: { u_long len; /* actual length of string */ u_long alloc; /* allocate length of string */ u_char *octs; /* actual octets */ u_long oct; /* actual octet */ u_char *nocts; /* to avoid memory leak */ u_char c; /* actual character */ # define STUFFC(C) \ if (alloc == len) { \ alloc += 100; \ if ((nocts = realloc(octs, alloc)) == NULL) { \ free(octs); \ return (-1); \ } \ octs = nocts; \ } \ octs[len++] = (C); len = alloc = 0; octs = NULL; if (*str == '"') { str++; while((c = *str++) != '\0') { if (c == '"') { if (*str != '\0') { free(octs); return (-1); } break; } if (c == '\\') { switch (c = *str++) { case '\\': break; case 'a': c = '\a'; break; case 'b': c = '\b'; break; case 'f': c = '\f'; break; case 'n': c = '\n'; break; case 'r': c = '\r'; break; case 't': c = '\t'; break; case 'v': c = '\v'; break; case 'x': c = 0; if (!isxdigit(*str)) break; if (isdigit(*str)) c = *str++ - '0'; else if (isupper(*str)) c = *str++ - 'A' + 10; else c = *str++ - 'a' + 10; if (!isxdigit(*str)) break; if (isdigit(*str)) c += *str++ - '0'; else if (isupper(*str)) c += *str++ - 'A' + 10; else c += *str++ - 'a' + 10; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': c = *str++ - '0'; if (*str < '0' || *str > '7') break; c = *str++ - '0'; if (*str < '0' || *str > '7') break; c = *str++ - '0'; break; default: break; } } STUFFC(c); } } else { while (*str != '\0') { oct = strtoul(str, &end, 16); str = end; if (oct > 0xff) { free(octs); return (-1); } STUFFC(oct); if (*str == ':') str++; else if(*str != '\0') { free(octs); return (-1); } } } v->octetstring.octets = octs; v->octetstring.len = len; return (0); # undef STUFFC } case SNMP_SYNTAX_OID: { u_long subid; v->oid.len = 0; for (;;) { if (v->oid.len == ASN_MAXOIDLEN) return (-1); subid = strtoul(str, &end, 10); str = end; if (subid > ASN_MAXID) return (-1); v->oid.subs[v->oid.len++] = (asn_subid_t)subid; if (*str == '\0') break; if (*str != '.') return (-1); str++; } return (0); } case SNMP_SYNTAX_IPADDRESS: { struct hostent *he; if (inet_pton(AF_INET, str, &v->ipaddress) == 1) return (0); if ((he = gethostbyname2(str, AF_INET)) == NULL) return (-1); if (he->h_addrtype != AF_INET) return (-1); memcpy(v->ipaddress, he->h_addr, sizeof(v->ipaddress)); return (0); } case SNMP_SYNTAX_COUNTER: case SNMP_SYNTAX_GAUGE: case SNMP_SYNTAX_TIMETICKS: { uint64_t sub; sub = strtoull(str, &end, 0); if (*end != '\0' || sub > 0xffffffff) return (-1); v->uint32 = (uint32_t)sub; return (0); } case SNMP_SYNTAX_COUNTER64: v->counter64 = strtoull(str, &end, 0); if (*end != '\0') return (-1); return (0); } abort(); } static void snmp_error_func(const char *fmt, ...) { va_list ap; va_start(ap, fmt); fprintf(stderr, "SNMP: "); vfprintf(stderr, fmt, ap); fprintf(stderr, "\n"); va_end(ap); } static void snmp_printf_func(const char *fmt, ...) { va_list ap; va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); } Index: head/contrib/bsnmp/lib/snmp.h =================================================================== --- head/contrib/bsnmp/lib/snmp.h (revision 310647) +++ head/contrib/bsnmp/lib/snmp.h (revision 310648) @@ -1,284 +1,284 @@ /* * Copyright (c) 2001-2003 * Fraunhofer Institute for Open Communication Systems (FhG Fokus). * All rights reserved. * * Author: Harti Brandt - * + * * Copyright (c) 2010 The FreeBSD Foundation * All rights reserved. * * Portions of this software were developed by Shteryana Sotirova Shopova * under sponsorship from the FreeBSD Foundation. * - * + * * 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/snmp.h,v 1.30 2004/08/06 08:46:54 brandt Exp $ * * Header file for SNMP functions. */ #ifndef snmp_h_ #define snmp_h_ #include #define SNMP_COMMUNITY_MAXLEN 128 #define SNMP_MAX_BINDINGS 100 #define SNMP_CONTEXT_NAME_SIZ (32 + 1) #define SNMP_ENGINE_ID_SIZ 32 #define SNMP_TIME_WINDOW 150 enum snmp_syntax { SNMP_SYNTAX_NULL = 0, SNMP_SYNTAX_INTEGER, /* == INTEGER32 */ SNMP_SYNTAX_OCTETSTRING, SNMP_SYNTAX_OID, SNMP_SYNTAX_IPADDRESS, SNMP_SYNTAX_COUNTER, SNMP_SYNTAX_GAUGE, /* == UNSIGNED32 */ SNMP_SYNTAX_TIMETICKS, /* v2 additions */ SNMP_SYNTAX_COUNTER64, SNMP_SYNTAX_NOSUCHOBJECT, /* exception */ SNMP_SYNTAX_NOSUCHINSTANCE, /* exception */ SNMP_SYNTAX_ENDOFMIBVIEW, /* exception */ }; struct snmp_value { struct asn_oid var; enum snmp_syntax syntax; union snmp_values { int32_t integer; /* also integer32 */ struct { u_int len; u_char *octets; } octetstring; struct asn_oid oid; u_char ipaddress[4]; uint32_t uint32; /* also gauge32, counter32, unsigned32, timeticks */ uint64_t counter64; } v; }; enum snmp_version { SNMP_Verr = 0, SNMP_V1 = 1, SNMP_V2c = 2, SNMP_V3, }; #define SNMP_MPM_SNMP_V1 0 #define SNMP_MPM_SNMP_V2c 1 #define SNMP_MPM_SNMP_V3 3 #define SNMP_ADM_STR32_SIZ (32 + 1) #define SNMP_AUTH_KEY_SIZ 40 #define SNMP_PRIV_KEY_SIZ 32 #define SNMP_USM_AUTH_SIZE 12 #define SNMP_USM_PRIV_SIZE 8 #define SNMP_AUTH_HMACMD5_KEY_SIZ 16 #define SNMP_AUTH_HMACSHA_KEY_SIZ 20 #define SNMP_PRIV_AES_KEY_SIZ 16 #define SNMP_PRIV_DES_KEY_SIZ 8 enum snmp_secmodel { SNMP_SECMODEL_ANY = 0, SNMP_SECMODEL_SNMPv1 = 1, SNMP_SECMODEL_SNMPv2c = 2, SNMP_SECMODEL_USM = 3, SNMP_SECMODEL_UNKNOWN }; enum snmp_usm_level { SNMP_noAuthNoPriv = 1, SNMP_authNoPriv = 2, SNMP_authPriv = 3 }; enum snmp_authentication { SNMP_AUTH_NOAUTH = 0, SNMP_AUTH_HMAC_MD5, SNMP_AUTH_HMAC_SHA }; enum snmp_privacy { SNMP_PRIV_NOPRIV = 0, SNMP_PRIV_DES = 1, SNMP_PRIV_AES }; struct snmp_engine { uint8_t engine_id[SNMP_ENGINE_ID_SIZ]; uint32_t engine_len; int32_t engine_boots; int32_t engine_time; int32_t max_msg_size; }; struct snmp_user { char sec_name[SNMP_ADM_STR32_SIZ]; enum snmp_authentication auth_proto; enum snmp_privacy priv_proto; uint8_t auth_key[SNMP_AUTH_KEY_SIZ]; uint8_t priv_key[SNMP_PRIV_KEY_SIZ]; }; struct snmp_pdu { char community[SNMP_COMMUNITY_MAXLEN + 1]; enum snmp_version version; u_int type; /* SNMPv3 PDU header fields */ int32_t identifier; uint8_t flags; int32_t security_model; struct snmp_engine engine; /* Associated USM user parameters */ struct snmp_user user; uint8_t msg_digest[SNMP_USM_AUTH_SIZE]; uint8_t msg_salt[SNMP_USM_PRIV_SIZE]; /* View-based Access Model */ /* XXX: put in separate structure - conflicts with struct snmp_context */ uint32_t context_engine_len; uint8_t context_engine[SNMP_ENGINE_ID_SIZ]; char context_name[SNMP_CONTEXT_NAME_SIZ]; /* trap only */ struct asn_oid enterprise; u_char agent_addr[4]; int32_t generic_trap; int32_t specific_trap; uint32_t time_stamp; /* others */ int32_t request_id; int32_t error_status; int32_t error_index; /* fixes for encoding */ size_t outer_len; asn_len_t scoped_len; u_char *outer_ptr; u_char *digest_ptr; u_char *encrypted_ptr; u_char *scoped_ptr; u_char *pdu_ptr; u_char *vars_ptr; struct snmp_value bindings[SNMP_MAX_BINDINGS]; u_int nbindings; }; #define snmp_v1_pdu snmp_pdu #define SNMP_PDU_GET 0 #define SNMP_PDU_GETNEXT 1 #define SNMP_PDU_RESPONSE 2 #define SNMP_PDU_SET 3 #define SNMP_PDU_TRAP 4 /* v1 */ #define SNMP_PDU_GETBULK 5 /* v2 */ #define SNMP_PDU_INFORM 6 /* v2 */ #define SNMP_PDU_TRAP2 7 /* v2 */ #define SNMP_PDU_REPORT 8 /* v2 */ #define SNMP_ERR_NOERROR 0 #define SNMP_ERR_TOOBIG 1 #define SNMP_ERR_NOSUCHNAME 2 /* v1 */ #define SNMP_ERR_BADVALUE 3 /* v1 */ #define SNMP_ERR_READONLY 4 /* v1 */ #define SNMP_ERR_GENERR 5 #define SNMP_ERR_NO_ACCESS 6 /* v2 */ #define SNMP_ERR_WRONG_TYPE 7 /* v2 */ #define SNMP_ERR_WRONG_LENGTH 8 /* v2 */ #define SNMP_ERR_WRONG_ENCODING 9 /* v2 */ #define SNMP_ERR_WRONG_VALUE 10 /* v2 */ #define SNMP_ERR_NO_CREATION 11 /* v2 */ #define SNMP_ERR_INCONS_VALUE 12 /* v2 */ #define SNMP_ERR_RES_UNAVAIL 13 /* v2 */ #define SNMP_ERR_COMMIT_FAILED 14 /* v2 */ #define SNMP_ERR_UNDO_FAILED 15 /* v2 */ #define SNMP_ERR_AUTH_ERR 16 /* v2 */ #define SNMP_ERR_NOT_WRITEABLE 17 /* v2 */ #define SNMP_ERR_INCONS_NAME 18 /* v2 */ #define SNMP_TRAP_COLDSTART 0 #define SNMP_TRAP_WARMSTART 1 #define SNMP_TRAP_LINKDOWN 2 #define SNMP_TRAP_LINKUP 3 #define SNMP_TRAP_AUTHENTICATION_FAILURE 4 #define SNMP_TRAP_EGP_NEIGHBOR_LOSS 5 #define SNMP_TRAP_ENTERPRISE 6 enum snmp_code { SNMP_CODE_OK = 0, SNMP_CODE_FAILED, SNMP_CODE_BADVERS, SNMP_CODE_BADLEN, SNMP_CODE_BADENC, SNMP_CODE_OORANGE, SNMP_CODE_BADSECLEVEL, SNMP_CODE_NOTINTIME, SNMP_CODE_BADUSER, SNMP_CODE_BADENGINE, SNMP_CODE_BADDIGEST, SNMP_CODE_EDECRYPT }; #define SNMP_MSG_AUTH_FLAG 0x1 #define SNMP_MSG_PRIV_FLAG 0x2 #define SNMP_MSG_REPORT_FLAG 0x4 #define SNMP_MSG_AUTODISCOVER 0x80 void snmp_value_free(struct snmp_value *); int snmp_value_parse(const char *, enum snmp_syntax, union snmp_values *); int snmp_value_copy(struct snmp_value *, const struct snmp_value *); void snmp_pdu_free(struct snmp_pdu *); void snmp_pdu_init_secparams(struct snmp_pdu *); enum snmp_code snmp_pdu_decode(struct asn_buf *b, struct snmp_pdu *pdu, int32_t *); enum snmp_code snmp_pdu_decode_header(struct asn_buf *, struct snmp_pdu *); enum snmp_code snmp_pdu_decode_scoped(struct asn_buf *, struct snmp_pdu *, int32_t *); enum snmp_code snmp_pdu_encode(struct snmp_pdu *, struct asn_buf *); enum snmp_code snmp_pdu_decode_secmode(struct asn_buf *, struct snmp_pdu *); int snmp_pdu_snoop(const struct asn_buf *); void snmp_pdu_dump(const struct snmp_pdu *pdu); enum snmp_code snmp_passwd_to_keys(struct snmp_user *, char *); enum snmp_code snmp_get_local_keys(struct snmp_user *, uint8_t *, uint32_t); enum snmp_code snmp_calc_keychange(struct snmp_user *, uint8_t *); extern void (*snmp_error)(const char *, ...); extern void (*snmp_printf)(const char *, ...); #define TRUTH_MK(F) ((F) ? 1 : 2) #define TRUTH_GET(T) (((T) == 1) ? 1 : 0) #define TRUTH_OK(T) ((T) == 1 || (T) == 2) #endif Index: head/contrib/bsnmp/lib/snmpagent.c =================================================================== --- head/contrib/bsnmp/lib/snmpagent.c (revision 310647) +++ head/contrib/bsnmp/lib/snmpagent.c (revision 310648) @@ -1,1070 +1,1070 @@ /* * 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/snmpagent.c,v 1.20 2005/10/04 11:21:33 brandt_h Exp $ * * SNMP Agent functions */ #include #include #include #include #include #include #ifdef HAVE_STDINT_H #include #elif defined(HAVE_INTTYPES_H) #include #endif #include #include "asn1.h" #include "snmp.h" #include "snmppriv.h" #include "snmpagent.h" static void snmp_debug_func(const char *fmt, ...); void (*snmp_debug)(const char *fmt, ...) = snmp_debug_func; struct snmp_node *tree; u_int tree_size; /* * Structure to hold dependencies during SET processing * The last two members of this structure must be the * dependency visible by the user and the user data. */ struct depend { TAILQ_ENTRY(depend) link; size_t len; /* size of data part */ snmp_depop_t func; struct snmp_dependency dep; #if defined(__GNUC__) && __GNUC__ < 3 u_char data[0]; #else u_char data[]; #endif }; TAILQ_HEAD(depend_list, depend); /* * Set context */ struct context { struct snmp_context ctx; struct depend_list dlist; const struct snmp_node *node[SNMP_MAX_BINDINGS]; struct snmp_scratch scratch[SNMP_MAX_BINDINGS]; struct depend *depend; }; #define TR(W) (snmp_trace & SNMP_TRACE_##W) u_int snmp_trace = 0; static char oidbuf[ASN_OIDSTRLEN]; /* * Allocate a context */ struct snmp_context * snmp_init_context(void) { struct context *context; if ((context = malloc(sizeof(*context))) == NULL) return (NULL); memset(context, 0, sizeof(*context)); TAILQ_INIT(&context->dlist); return (&context->ctx); } /* * Find a variable for SET/GET and the first GETBULK pass. * Return the node pointer. If the search fails, set the errp to * the correct SNMPv2 GET exception code. */ static struct snmp_node * find_node(const struct snmp_value *value, enum snmp_syntax *errp) { struct snmp_node *tp; if (TR(FIND)) snmp_debug("find: searching %s", asn_oid2str_r(&value->var, oidbuf)); /* * If we have an exact match (the entry in the table is a * sub-oid from the variable) we have found what we are for. * If the table oid is higher than the variable, there is no match. */ for (tp = tree; tp < tree + tree_size; tp++) { if (asn_is_suboid(&tp->oid, &value->var)) goto found; if (asn_compare_oid(&tp->oid, &value->var) >= 0) break; } if (TR(FIND)) snmp_debug("find: no match"); *errp = SNMP_SYNTAX_NOSUCHOBJECT; return (NULL); found: /* leafs must have a 0 instance identifier */ if (tp->type == SNMP_NODE_LEAF && (value->var.len != tp->oid.len + 1 || value->var.subs[tp->oid.len] != 0)) { if (TR(FIND)) snmp_debug("find: bad leaf index"); *errp = SNMP_SYNTAX_NOSUCHINSTANCE; return (NULL); } if (TR(FIND)) snmp_debug("find: found %s", asn_oid2str_r(&value->var, oidbuf)); return (tp); } static struct snmp_node * find_subnode(const struct snmp_value *value) { struct snmp_node *tp; for (tp = tree; tp < tree + tree_size; tp++) { if (asn_is_suboid(&value->var, &tp->oid)) return (tp); } return (NULL); } static void snmp_pdu_create_response(const struct snmp_pdu *pdu, struct snmp_pdu *resp) { memset(resp, 0, sizeof(*resp)); strcpy(resp->community, pdu->community); resp->version = pdu->version; if (pdu->flags & SNMP_MSG_AUTODISCOVER) resp->type = SNMP_PDU_REPORT; /* RFC 3414.4 */ else resp->type = SNMP_PDU_RESPONSE; resp->request_id = pdu->request_id; resp->version = pdu->version; if (resp->version != SNMP_V3) return; memcpy(&resp->engine, &pdu->engine, sizeof(pdu->engine)); memcpy(&resp->user, &pdu->user, sizeof(pdu->user)); snmp_pdu_init_secparams(resp); resp->identifier = pdu->identifier; resp->security_model = pdu->security_model; resp->context_engine_len = pdu->context_engine_len; memcpy(resp->context_engine, pdu->context_engine, resp->context_engine_len); strlcpy(resp->context_name, pdu->context_name, sizeof(resp->context_name)); } /* * Execute a GET operation. The tree is rooted at the global 'root'. * Build the response PDU on the fly. If the return code is SNMP_RET_ERR * the pdu error status and index will be set. */ enum snmp_ret snmp_get(struct snmp_pdu *pdu, struct asn_buf *resp_b, struct snmp_pdu *resp, void *data) { int ret; u_int i; struct snmp_node *tp; enum snmp_syntax except; struct context context; enum asn_err err; memset(&context, 0, sizeof(context)); context.ctx.data = data; snmp_pdu_create_response(pdu, resp); if (snmp_pdu_encode_header(resp_b, resp) != SNMP_CODE_OK) /* cannot even encode header - very bad */ return (SNMP_RET_IGN); for (i = 0; i < pdu->nbindings; i++) { resp->bindings[i].var = pdu->bindings[i].var; if ((tp = find_node(&pdu->bindings[i], &except)) == NULL) { if (pdu->version == SNMP_V1) { if (TR(GET)) snmp_debug("get: nosuchname"); pdu->error_status = SNMP_ERR_NOSUCHNAME; pdu->error_index = i + 1; snmp_pdu_free(resp); return (SNMP_RET_ERR); } if (TR(GET)) snmp_debug("get: exception %u", except); resp->bindings[i].syntax = except; } else { /* call the action to fetch the value. */ resp->bindings[i].syntax = tp->syntax; ret = (*tp->op)(&context.ctx, &resp->bindings[i], tp->oid.len, tp->index, SNMP_OP_GET); if (TR(GET)) snmp_debug("get: action returns %d", ret); if (ret == SNMP_ERR_NOSUCHNAME) { if (pdu->version == SNMP_V1) { pdu->error_status = SNMP_ERR_NOSUCHNAME; pdu->error_index = i + 1; snmp_pdu_free(resp); return (SNMP_RET_ERR); } if (TR(GET)) snmp_debug("get: exception noSuchInstance"); resp->bindings[i].syntax = SNMP_SYNTAX_NOSUCHINSTANCE; } else if (ret != SNMP_ERR_NOERROR) { pdu->error_status = SNMP_ERR_GENERR; pdu->error_index = i + 1; snmp_pdu_free(resp); return (SNMP_RET_ERR); } } resp->nbindings++; err = snmp_binding_encode(resp_b, &resp->bindings[i]); if (err == ASN_ERR_EOBUF) { pdu->error_status = SNMP_ERR_TOOBIG; pdu->error_index = 0; snmp_pdu_free(resp); return (SNMP_RET_ERR); } if (err != ASN_ERR_OK) { if (TR(GET)) snmp_debug("get: binding encoding: %u", err); pdu->error_status = SNMP_ERR_GENERR; pdu->error_index = i + 1; snmp_pdu_free(resp); return (SNMP_RET_ERR); } } if (snmp_fix_encoding(resp_b, resp) != SNMP_CODE_OK) { snmp_debug("get: failed to encode PDU"); return (SNMP_RET_ERR); } return (SNMP_RET_OK); } static struct snmp_node * next_node(const struct snmp_value *value, int *pnext) { struct snmp_node *tp; if (TR(FIND)) snmp_debug("next: searching %s", asn_oid2str_r(&value->var, oidbuf)); *pnext = 0; for (tp = tree; tp < tree + tree_size; tp++) { if (asn_is_suboid(&tp->oid, &value->var)) { /* the tree OID is a sub-oid of the requested OID. */ if (tp->type == SNMP_NODE_LEAF) { if (tp->oid.len == value->var.len) { /* request for scalar type */ if (TR(FIND)) snmp_debug("next: found scalar %s", asn_oid2str_r(&tp->oid, oidbuf)); return (tp); } /* try next */ } else { if (TR(FIND)) snmp_debug("next: found column %s", asn_oid2str_r(&tp->oid, oidbuf)); return (tp); } } else if (asn_is_suboid(&value->var, &tp->oid) || asn_compare_oid(&tp->oid, &value->var) >= 0) { if (TR(FIND)) snmp_debug("next: found %s", asn_oid2str_r(&tp->oid, oidbuf)); *pnext = 1; return (tp); } } if (TR(FIND)) snmp_debug("next: failed"); return (NULL); } static enum snmp_ret do_getnext(struct context *context, const struct snmp_value *inb, struct snmp_value *outb, struct snmp_pdu *pdu) { const struct snmp_node *tp; int ret, next; if ((tp = next_node(inb, &next)) == NULL) goto eofMib; /* retain old variable if we are doing a GETNEXT on an exact * matched leaf only */ if (tp->type == SNMP_NODE_LEAF || next) outb->var = tp->oid; else outb->var = inb->var; for (;;) { outb->syntax = tp->syntax; if (tp->type == SNMP_NODE_LEAF) { /* make a GET operation */ outb->var.subs[outb->var.len++] = 0; ret = (*tp->op)(&context->ctx, outb, tp->oid.len, tp->index, SNMP_OP_GET); } else { /* make a GETNEXT */ ret = (*tp->op)(&context->ctx, outb, tp->oid.len, tp->index, SNMP_OP_GETNEXT); } if (ret != SNMP_ERR_NOSUCHNAME) { /* got something */ if (ret != SNMP_ERR_NOERROR && TR(GETNEXT)) snmp_debug("getnext: %s returns %u", asn_oid2str(&outb->var), ret); break; } /* object has no data - try next */ if (++tp == tree + tree_size) break; if (TR(GETNEXT)) snmp_debug("getnext: no data - avancing to %s", asn_oid2str(&tp->oid)); outb->var = tp->oid; } if (ret == SNMP_ERR_NOSUCHNAME) { eofMib: outb->var = inb->var; if (pdu->version == SNMP_V1) { pdu->error_status = SNMP_ERR_NOSUCHNAME; return (SNMP_RET_ERR); } outb->syntax = SNMP_SYNTAX_ENDOFMIBVIEW; } else if (ret != SNMP_ERR_NOERROR) { pdu->error_status = SNMP_ERR_GENERR; return (SNMP_RET_ERR); } return (SNMP_RET_OK); } /* * Execute a GETNEXT operation. The tree is rooted at the global 'root'. * Build the response PDU on the fly. The return is: */ enum snmp_ret snmp_getnext(struct snmp_pdu *pdu, struct asn_buf *resp_b, struct snmp_pdu *resp, void *data) { struct context context; u_int i; enum asn_err err; enum snmp_ret result; memset(&context, 0, sizeof(context)); context.ctx.data = data; snmp_pdu_create_response(pdu, resp); if (snmp_pdu_encode_header(resp_b, resp)) return (SNMP_RET_IGN); for (i = 0; i < pdu->nbindings; i++) { result = do_getnext(&context, &pdu->bindings[i], &resp->bindings[i], pdu); if (result != SNMP_RET_OK) { pdu->error_index = i + 1; snmp_pdu_free(resp); return (result); } resp->nbindings++; err = snmp_binding_encode(resp_b, &resp->bindings[i]); if (err == ASN_ERR_EOBUF) { pdu->error_status = SNMP_ERR_TOOBIG; pdu->error_index = 0; snmp_pdu_free(resp); return (SNMP_RET_ERR); } if (err != ASN_ERR_OK) { if (TR(GET)) snmp_debug("getnext: binding encoding: %u", err); pdu->error_status = SNMP_ERR_GENERR; pdu->error_index = i + 1; snmp_pdu_free(resp); return (SNMP_RET_ERR); } } if (snmp_fix_encoding(resp_b, resp) != SNMP_CODE_OK) { snmp_debug("getnext: failed to encode PDU"); return (SNMP_RET_ERR); } return (SNMP_RET_OK); } enum snmp_ret snmp_getbulk(struct snmp_pdu *pdu, struct asn_buf *resp_b, struct snmp_pdu *resp, void *data) { struct context context; u_int i; int cnt; u_int non_rep; int eomib; enum snmp_ret result; enum asn_err err; memset(&context, 0, sizeof(context)); context.ctx.data = data; snmp_pdu_create_response(pdu, resp); if (snmp_pdu_encode_header(resp_b, resp) != SNMP_CODE_OK) /* cannot even encode header - very bad */ return (SNMP_RET_IGN); if ((non_rep = pdu->error_status) > pdu->nbindings) non_rep = pdu->nbindings; /* non-repeaters */ for (i = 0; i < non_rep; i++) { result = do_getnext(&context, &pdu->bindings[i], &resp->bindings[resp->nbindings], pdu); if (result != SNMP_RET_OK) { pdu->error_index = i + 1; snmp_pdu_free(resp); return (result); } err = snmp_binding_encode(resp_b, &resp->bindings[resp->nbindings++]); if (err == ASN_ERR_EOBUF) goto done; if (err != ASN_ERR_OK) { if (TR(GET)) snmp_debug("getnext: binding encoding: %u", err); pdu->error_status = SNMP_ERR_GENERR; pdu->error_index = i + 1; snmp_pdu_free(resp); return (SNMP_RET_ERR); } } if (non_rep == pdu->nbindings) goto done; /* repeates */ for (cnt = 0; cnt < pdu->error_index; cnt++) { eomib = 1; for (i = non_rep; i < pdu->nbindings; i++) { if (resp->nbindings == SNMP_MAX_BINDINGS) /* PDU is full */ goto done; - if (cnt == 0) + if (cnt == 0) result = do_getnext(&context, &pdu->bindings[i], &resp->bindings[resp->nbindings], pdu); else result = do_getnext(&context, &resp->bindings[resp->nbindings - (pdu->nbindings - non_rep)], &resp->bindings[resp->nbindings], pdu); if (result != SNMP_RET_OK) { pdu->error_index = i + 1; snmp_pdu_free(resp); return (result); } if (resp->bindings[resp->nbindings].syntax != SNMP_SYNTAX_ENDOFMIBVIEW) eomib = 0; err = snmp_binding_encode(resp_b, &resp->bindings[resp->nbindings++]); if (err == ASN_ERR_EOBUF) goto done; if (err != ASN_ERR_OK) { if (TR(GET)) snmp_debug("getnext: binding encoding: %u", err); pdu->error_status = SNMP_ERR_GENERR; pdu->error_index = i + 1; snmp_pdu_free(resp); return (SNMP_RET_ERR); } } if (eomib) break; } done: if (snmp_fix_encoding(resp_b, resp) != SNMP_CODE_OK) { snmp_debug("getnext: failed to encode PDU"); return (SNMP_RET_ERR); } return (SNMP_RET_OK); } /* * Rollback a SET operation. Failed index is 'i'. */ static void rollback(struct context *context, struct snmp_pdu *pdu, u_int i) { struct snmp_value *b; const struct snmp_node *np; int ret; while (i-- > 0) { b = &pdu->bindings[i]; np = context->node[i]; context->ctx.scratch = &context->scratch[i]; ret = (*np->op)(&context->ctx, b, np->oid.len, np->index, SNMP_OP_ROLLBACK); if (ret != SNMP_ERR_NOERROR) { snmp_error("set: rollback failed (%d) on variable %s " "index %u", ret, asn_oid2str(&b->var), i); if (pdu->version != SNMP_V1) { pdu->error_status = SNMP_ERR_UNDO_FAILED; pdu->error_index = 0; } } } } /* * Commit dependencies. */ int snmp_dep_commit(struct snmp_context *ctx) { struct context *context = (struct context *)ctx; int ret; TAILQ_FOREACH(context->depend, &context->dlist, link) { ctx->dep = &context->depend->dep; if (TR(SET)) snmp_debug("set: dependency commit %s", asn_oid2str(&ctx->dep->obj)); ret = context->depend->func(ctx, ctx->dep, SNMP_DEPOP_COMMIT); if (ret != SNMP_ERR_NOERROR) { if (TR(SET)) snmp_debug("set: dependency failed %d", ret); return (ret); } } return (SNMP_ERR_NOERROR); } /* * Rollback dependencies */ int snmp_dep_rollback(struct snmp_context *ctx) { struct context *context = (struct context *)ctx; int ret, ret1; char objbuf[ASN_OIDSTRLEN]; char idxbuf[ASN_OIDSTRLEN]; ret1 = SNMP_ERR_NOERROR; while ((context->depend = TAILQ_PREV(context->depend, depend_list, link)) != NULL) { ctx->dep = &context->depend->dep; if (TR(SET)) snmp_debug("set: dependency rollback %s", asn_oid2str(&ctx->dep->obj)); ret = context->depend->func(ctx, ctx->dep, SNMP_DEPOP_ROLLBACK); if (ret != SNMP_ERR_NOERROR) { snmp_debug("set: dep rollback returns %u: %s %s", ret, asn_oid2str_r(&ctx->dep->obj, objbuf), asn_oid2str_r(&ctx->dep->idx, idxbuf)); if (ret1 == SNMP_ERR_NOERROR) ret1 = ret; } } return (ret1); } void snmp_dep_finish(struct snmp_context *ctx) { struct context *context = (struct context *)ctx; struct depend *d; while ((d = TAILQ_FIRST(&context->dlist)) != NULL) { ctx->dep = &d->dep; (void)d->func(ctx, ctx->dep, SNMP_DEPOP_FINISH); TAILQ_REMOVE(&context->dlist, d, link); free(d); } } /* * Do a SET operation. */ enum snmp_ret snmp_set(struct snmp_pdu *pdu, struct asn_buf *resp_b, struct snmp_pdu *resp, void *data) { int ret; u_int i; enum asn_err asnerr; struct context context; const struct snmp_node *np; struct snmp_value *b; enum snmp_syntax except; memset(&context, 0, sizeof(context)); TAILQ_INIT(&context.dlist); context.ctx.data = data; snmp_pdu_create_response(pdu, resp); if (snmp_pdu_encode_header(resp_b, resp)) return (SNMP_RET_IGN); - /* + /* * 1. Find all nodes, check that they are writeable and * that the syntax is ok, copy over the binding to the response. */ for (i = 0; i < pdu->nbindings; i++) { b = &pdu->bindings[i]; if ((np = context.node[i] = find_node(b, &except)) == NULL) { /* not found altogether or LEAF with wrong index */ if (TR(SET)) snmp_debug("set: node not found %s", asn_oid2str_r(&b->var, oidbuf)); if (pdu->version == SNMP_V1) { pdu->error_index = i + 1; pdu->error_status = SNMP_ERR_NOSUCHNAME; } else if ((np = find_subnode(b)) != NULL) { /* 2. intermediate object */ pdu->error_index = i + 1; pdu->error_status = SNMP_ERR_NOT_WRITEABLE; } else if (except == SNMP_SYNTAX_NOSUCHOBJECT) { pdu->error_index = i + 1; pdu->error_status = SNMP_ERR_NO_ACCESS; } else { pdu->error_index = i + 1; pdu->error_status = SNMP_ERR_NO_CREATION; } snmp_pdu_free(resp); return (SNMP_RET_ERR); } /* * 2. write/createable? * Can check this for leafs only, because in v2 we have * to differentiate between NOT_WRITEABLE and NO_CREATION * and only the action routine for COLUMNS knows, whether * a column exists. */ if (np->type == SNMP_NODE_LEAF && !(np->flags & SNMP_NODE_CANSET)) { if (pdu->version == SNMP_V1) { pdu->error_index = i + 1; pdu->error_status = SNMP_ERR_NOSUCHNAME; } else { pdu->error_index = i + 1; pdu->error_status = SNMP_ERR_NOT_WRITEABLE; } snmp_pdu_free(resp); return (SNMP_RET_ERR); } /* * 3. Ensure the right syntax */ if (np->syntax != b->syntax) { if (pdu->version == SNMP_V1) { pdu->error_index = i + 1; pdu->error_status = SNMP_ERR_BADVALUE; /* v2: wrongType */ } else { pdu->error_index = i + 1; pdu->error_status = SNMP_ERR_WRONG_TYPE; } snmp_pdu_free(resp); return (SNMP_RET_ERR); } /* * 4. Copy binding */ if (snmp_value_copy(&resp->bindings[i], b)) { pdu->error_index = i + 1; pdu->error_status = SNMP_ERR_GENERR; snmp_pdu_free(resp); return (SNMP_RET_ERR); } asnerr = snmp_binding_encode(resp_b, &resp->bindings[i]); if (asnerr == ASN_ERR_EOBUF) { pdu->error_index = i + 1; pdu->error_status = SNMP_ERR_TOOBIG; snmp_pdu_free(resp); return (SNMP_RET_ERR); } else if (asnerr != ASN_ERR_OK) { pdu->error_index = i + 1; pdu->error_status = SNMP_ERR_GENERR; snmp_pdu_free(resp); return (SNMP_RET_ERR); } resp->nbindings++; } context.ctx.code = SNMP_RET_OK; /* * 2. Call the SET method for each node. If a SET fails, rollback * everything. Map error codes depending on the version. */ for (i = 0; i < pdu->nbindings; i++) { b = &pdu->bindings[i]; np = context.node[i]; context.ctx.var_index = i + 1; context.ctx.scratch = &context.scratch[i]; ret = (*np->op)(&context.ctx, b, np->oid.len, np->index, SNMP_OP_SET); if (TR(SET)) snmp_debug("set: action %s returns %d", np->name, ret); if (pdu->version == SNMP_V1) { switch (ret) { case SNMP_ERR_NO_ACCESS: ret = SNMP_ERR_NOSUCHNAME; break; case SNMP_ERR_WRONG_TYPE: /* should no happen */ ret = SNMP_ERR_BADVALUE; break; case SNMP_ERR_WRONG_LENGTH: ret = SNMP_ERR_BADVALUE; break; case SNMP_ERR_WRONG_ENCODING: /* should not happen */ ret = SNMP_ERR_BADVALUE; break; case SNMP_ERR_WRONG_VALUE: ret = SNMP_ERR_BADVALUE; break; case SNMP_ERR_NO_CREATION: ret = SNMP_ERR_NOSUCHNAME; break; case SNMP_ERR_INCONS_VALUE: ret = SNMP_ERR_BADVALUE; break; case SNMP_ERR_RES_UNAVAIL: ret = SNMP_ERR_GENERR; break; case SNMP_ERR_COMMIT_FAILED: ret = SNMP_ERR_GENERR; break; case SNMP_ERR_UNDO_FAILED: ret = SNMP_ERR_GENERR; break; case SNMP_ERR_AUTH_ERR: /* should not happen */ ret = SNMP_ERR_GENERR; break; case SNMP_ERR_NOT_WRITEABLE: ret = SNMP_ERR_NOSUCHNAME; break; case SNMP_ERR_INCONS_NAME: ret = SNMP_ERR_BADVALUE; break; } } if (ret != SNMP_ERR_NOERROR) { pdu->error_index = i + 1; pdu->error_status = ret; rollback(&context, pdu, i); snmp_pdu_free(resp); context.ctx.code = SNMP_RET_ERR; goto errout; } } /* * 3. Call dependencies */ if (TR(SET)) snmp_debug("set: set operations ok"); if ((ret = snmp_dep_commit(&context.ctx)) != SNMP_ERR_NOERROR) { pdu->error_status = ret; pdu->error_index = context.ctx.var_index; if ((ret = snmp_dep_rollback(&context.ctx)) != SNMP_ERR_NOERROR) { if (pdu->version != SNMP_V1) { pdu->error_status = SNMP_ERR_UNDO_FAILED; pdu->error_index = 0; } } rollback(&context, pdu, i); snmp_pdu_free(resp); context.ctx.code = SNMP_RET_ERR; goto errout; } /* * 4. Commit and copy values from the original packet to the response. * This is not the commit operation from RFC 1905 but rather an * 'FREE RESOURCES' operation. It shouldn't fail. */ if (TR(SET)) snmp_debug("set: commiting"); for (i = 0; i < pdu->nbindings; i++) { b = &resp->bindings[i]; np = context.node[i]; context.ctx.var_index = i + 1; context.ctx.scratch = &context.scratch[i]; ret = (*np->op)(&context.ctx, b, np->oid.len, np->index, SNMP_OP_COMMIT); if (ret != SNMP_ERR_NOERROR) snmp_error("set: commit failed (%d) on" " variable %s index %u", ret, asn_oid2str_r(&b->var, oidbuf), i); } if (snmp_fix_encoding(resp_b, resp) != SNMP_CODE_OK) { snmp_error("set: fix_encoding failed"); snmp_pdu_free(resp); context.ctx.code = SNMP_RET_IGN; } /* * Done */ errout: snmp_dep_finish(&context.ctx); if (TR(SET)) snmp_debug("set: returning %d", context.ctx.code); return (context.ctx.code); } /* * Lookup a dependency. If it doesn't exist, create one */ struct snmp_dependency * snmp_dep_lookup(struct snmp_context *ctx, const struct asn_oid *obj, const struct asn_oid *idx, size_t len, snmp_depop_t func) { struct context *context; struct depend *d; context = (struct context *)(void *) ((char *)ctx - offsetof(struct context, ctx)); if (TR(DEPEND)) { snmp_debug("depend: looking for %s", asn_oid2str(obj)); if (idx) snmp_debug("depend: index is %s", asn_oid2str(idx)); } TAILQ_FOREACH(d, &context->dlist, link) if (asn_compare_oid(obj, &d->dep.obj) == 0 && ((idx == NULL && d->dep.idx.len == 0) || (idx != NULL && asn_compare_oid(idx, &d->dep.idx) == 0))) { if(TR(DEPEND)) snmp_debug("depend: found"); return (&d->dep); } if(TR(DEPEND)) snmp_debug("depend: creating"); if ((d = malloc(offsetof(struct depend, dep) + len)) == NULL) return (NULL); memset(&d->dep, 0, len); d->dep.obj = *obj; if (idx == NULL) d->dep.idx.len = 0; else d->dep.idx = *idx; d->len = len; d->func = func; TAILQ_INSERT_TAIL(&context->dlist, d, link); return (&d->dep); } /* * Make an error response from a PDU. We do this without decoding the * variable bindings. This means we can sent the junk back to a caller - * that has sent us junk in the first place. + * that has sent us junk in the first place. */ enum snmp_ret snmp_make_errresp(const struct snmp_pdu *pdu, struct asn_buf *pdu_b, struct asn_buf *resp_b) { u_char type; asn_len_t len; struct snmp_pdu resp; enum asn_err err; enum snmp_code code; snmp_pdu_create_response(pdu, &resp); if ((code = snmp_pdu_decode_header(pdu_b, &resp)) != SNMP_CODE_OK) return (SNMP_RET_IGN); if (pdu->version == SNMP_V3) { if (resp.user.priv_proto != SNMP_PRIV_NOPRIV && (asn_get_header(pdu_b, &type, &resp.scoped_len) != ASN_ERR_OK || type != ASN_TYPE_OCTETSTRING)) { snmp_error("cannot decode encrypted pdu"); return (SNMP_RET_IGN); } if (asn_get_sequence(pdu_b, &len) != ASN_ERR_OK) { snmp_error("cannot decode scoped pdu header"); return (SNMP_RET_IGN); } len = SNMP_ENGINE_ID_SIZ; if (asn_get_octetstring(pdu_b, (u_char *)resp.context_engine, &len) != ASN_ERR_OK) { snmp_error("cannot decode msg context engine"); return (SNMP_RET_IGN); } resp.context_engine_len = len; len = SNMP_CONTEXT_NAME_SIZ; if (asn_get_octetstring(pdu_b, (u_char *)resp.context_name, &len) != ASN_ERR_OK) { snmp_error("cannot decode msg context name"); return (SNMP_RET_IGN); } resp.context_name[len] = '\0'; } if (asn_get_header(pdu_b, &type, &len) != ASN_ERR_OK) { snmp_error("cannot get pdu header"); return (SNMP_RET_IGN); } if ((type & ~ASN_TYPE_MASK) != (ASN_TYPE_CONSTRUCTED | ASN_CLASS_CONTEXT)) { snmp_error("bad pdu header tag"); return (SNMP_RET_IGN); } err = snmp_parse_pdus_hdr(pdu_b, &resp, &len); if (ASN_ERR_STOPPED(err)) return (SNMP_RET_IGN); if (pdu_b->asn_len < len) return (SNMP_RET_IGN); pdu_b->asn_len = len; /* now we have the bindings left - construct new message */ resp.error_status = pdu->error_status; resp.error_index = pdu->error_index; resp.type = SNMP_PDU_RESPONSE; code = snmp_pdu_encode_header(resp_b, &resp); if (code != SNMP_CODE_OK) return (SNMP_RET_IGN); if (pdu_b->asn_len > resp_b->asn_len) /* too short */ return (SNMP_RET_IGN); (void)memcpy(resp_b->asn_ptr, pdu_b->asn_cptr, pdu_b->asn_len); resp_b->asn_len -= pdu_b->asn_len; resp_b->asn_ptr += pdu_b->asn_len; code = snmp_fix_encoding(resp_b, &resp); if (code != SNMP_CODE_OK) return (SNMP_RET_IGN); return (SNMP_RET_OK); } static void snmp_debug_func(const char *fmt, ...) { va_list ap; va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); fprintf(stderr, "\n"); } Index: head/contrib/bsnmp/lib/snmpagent.h =================================================================== --- head/contrib/bsnmp/lib/snmpagent.h (revision 310647) +++ head/contrib/bsnmp/lib/snmpagent.h (revision 310648) @@ -1,152 +1,152 @@ /* * 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/snmpagent.h,v 1.13 2004/08/06 08:46:56 brandt Exp $ * * Header file for SNMP functions. This requires snmp.h to be included. */ #ifndef snmp_agent_h_ #define snmp_agent_h_ struct snmp_dependency; enum snmp_ret { /* OK, generate a response */ SNMP_RET_OK = 0, /* Error, ignore packet (no response) */ SNMP_RET_IGN = 1, /* Error, generate response from original packet */ SNMP_RET_ERR = 2 }; /* Semi-Opaque object for SET operations */ struct snmp_context { u_int var_index; struct snmp_scratch *scratch; struct snmp_dependency *dep; void *data; /* user data */ enum snmp_ret code; /* return code */ }; struct snmp_scratch { void *ptr1; void *ptr2; uint32_t int1; uint32_t int2; }; enum snmp_depop { SNMP_DEPOP_COMMIT, SNMP_DEPOP_ROLLBACK, SNMP_DEPOP_FINISH }; typedef int (*snmp_depop_t)(struct snmp_context *, struct snmp_dependency *, enum snmp_depop); struct snmp_dependency { struct asn_oid obj; struct asn_oid idx; }; /* * The TREE */ enum snmp_node_type { SNMP_NODE_LEAF = 1, SNMP_NODE_COLUMN }; enum snmp_op { SNMP_OP_GET = 1, SNMP_OP_GETNEXT, SNMP_OP_SET, SNMP_OP_COMMIT, SNMP_OP_ROLLBACK, }; typedef int (*snmp_op_t)(struct snmp_context *, struct snmp_value *, u_int, u_int, enum snmp_op); struct snmp_node { struct asn_oid oid; const char *name; /* name of the leaf */ enum snmp_node_type type; /* type of this node */ enum snmp_syntax syntax; snmp_op_t op; u_int flags; uint32_t index; /* index data */ void *data; /* application data */ void *tree_data; /* application data */ }; extern struct snmp_node *tree; extern u_int tree_size; #define SNMP_NODE_CANSET 0x0001 /* SET allowed */ #define SNMP_INDEXES_MAX 7 #define SNMP_INDEX_SHIFT 4 #define SNMP_INDEX_MASK 0xf #define SNMP_INDEX_COUNT(V) ((V) & SNMP_INDEX_MASK) #define SNMP_INDEX(V,I) \ (((V) >> (((I) + 1) * SNMP_INDEX_SHIFT)) & SNMP_INDEX_MASK) enum { SNMP_TRACE_GET = 0x00000001, SNMP_TRACE_GETNEXT = 0x00000002, SNMP_TRACE_SET = 0x00000004, SNMP_TRACE_DEPEND = 0x00000008, SNMP_TRACE_FIND = 0x00000010, }; /* trace flag for the following functions */ extern u_int snmp_trace; /* called to write the trace */ extern void (*snmp_debug)(const char *fmt, ...); enum snmp_ret snmp_get(struct snmp_pdu *pdu, struct asn_buf *resp_b, struct snmp_pdu *resp, void *); enum snmp_ret snmp_getnext(struct snmp_pdu *pdu, struct asn_buf *resp_b, struct snmp_pdu *resp, void *); enum snmp_ret snmp_getbulk(struct snmp_pdu *pdu, struct asn_buf *resp_b, struct snmp_pdu *resp, void *); enum snmp_ret snmp_set(struct snmp_pdu *pdu, struct asn_buf *resp_b, struct snmp_pdu *resp, void *); enum snmp_ret snmp_make_errresp(const struct snmp_pdu *, struct asn_buf *, struct asn_buf *); struct snmp_dependency *snmp_dep_lookup(struct snmp_context *, const struct asn_oid *, const struct asn_oid *, size_t, snmp_depop_t); struct snmp_context *snmp_init_context(void); int snmp_dep_commit(struct snmp_context *); int snmp_dep_rollback(struct snmp_context *); void snmp_dep_finish(struct snmp_context *); #endif Index: head/contrib/bsnmp/lib/snmpclient.c =================================================================== --- head/contrib/bsnmp/lib/snmpclient.c (revision 310647) +++ head/contrib/bsnmp/lib/snmpclient.c (revision 310648) @@ -1,1956 +1,1956 @@ /* * 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.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" /* global context */ struct snmp_client snmp_client; /* List of all outstanding requests */ -struct sent_pdu { +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': + 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 */ + /* 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->security_model = SNMP_SECMODEL_USM; strcpy(c->cname, ""); 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; c->engine.max_msg_size = 1500; /* XXX */ } /* * 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; if (snmp_client.version != SNMP_V3) return; pdu->identifier = ++snmp_client.identifier; pdu->engine.max_msg_size = snmp_client.engine.max_msg_size; pdu->flags = 0; pdu->security_model = snmp_client.security_model; if (snmp_client.security_model == SNMP_SECMODEL_USM) { memcpy(&pdu->engine, &snmp_client.engine, sizeof(pdu->engine)); memcpy(&pdu->user, &snmp_client.user, sizeof(pdu->user)); snmp_pdu_init_secparams(pdu); } else seterr(&snmp_client, "unknown security model"); if (snmp_client.clen > 0) { memcpy(pdu->context_engine, snmp_client.cengine, snmp_client.clen); pdu->context_engine_len = snmp_client.clen; } else { memcpy(pdu->context_engine, snmp_client.engine.engine_id, snmp_client.engine.engine_len); pdu->context_engine_len = snmp_client.engine.engine_len; } strlcpy(pdu->context_name, snmp_client.cname, sizeof(pdu->context_name)); } /* 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; memset(pdu, 0, sizeof(*pdu)); if (snmp_client.security_model == SNMP_SECMODEL_USM) { memcpy(&pdu->engine, &snmp_client.engine, sizeof(pdu->engine)); memcpy(&pdu->user, &snmp_client.user, sizeof(pdu->user)); snmp_pdu_init_secparams(pdu); } 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); snmp_client.engine.engine_time = pdu->engine.engine_time; snmp_client.engine.engine_boots = pdu->engine.engine_boots; 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) { struct timeval tv = snmp_client.timeout; struct timeval end; struct snmp_pdu pdu; int ret; int32_t reqid; u_int i; /* * 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_discover_engine(char *passwd) { char cname[SNMP_ADM_STR32_SIZ]; enum snmp_authentication cap; enum snmp_privacy cpp; struct snmp_pdu req, resp; if (snmp_client.version != SNMP_V3) seterr(&snmp_client, "wrong version"); strlcpy(cname, snmp_client.user.sec_name, sizeof(cname)); cap = snmp_client.user.auth_proto; cpp = snmp_client.user.priv_proto; snmp_client.engine.engine_len = 0; snmp_client.engine.engine_boots = 0; snmp_client.engine.engine_time = 0; snmp_client.user.auth_proto = SNMP_AUTH_NOAUTH; snmp_client.user.priv_proto = SNMP_PRIV_NOPRIV; memset(snmp_client.user.sec_name, 0, sizeof(snmp_client.user.sec_name)); snmp_pdu_create(&req, SNMP_PDU_GET); if (snmp_dialog(&req, &resp) == -1) return (-1); if (resp.version != req.version) { seterr(&snmp_client, "wrong version"); return (-1); } if (resp.error_status != SNMP_ERR_NOERROR) { seterr(&snmp_client, "Error %d in responce", resp.error_status); return (-1); } snmp_client.engine.engine_len = resp.engine.engine_len; snmp_client.engine.max_msg_size = resp.engine.max_msg_size; memcpy(snmp_client.engine.engine_id, resp.engine.engine_id, resp.engine.engine_len); strlcpy(snmp_client.user.sec_name, cname, sizeof(snmp_client.user.sec_name)); snmp_client.user.auth_proto = cap; snmp_client.user.priv_proto = cpp; if (snmp_client.user.auth_proto == SNMP_AUTH_NOAUTH) return (0); if (passwd == NULL || snmp_passwd_to_keys(&snmp_client.user, passwd) != SNMP_CODE_OK || snmp_get_local_keys(&snmp_client.user, snmp_client.engine.engine_id, snmp_client.engine.engine_len) != SNMP_CODE_OK) return (-1); if (resp.engine.engine_boots != 0) snmp_client.engine.engine_boots = resp.engine.engine_boots; if (resp.engine.engine_time != 0) { snmp_client.engine.engine_time = resp.engine.engine_time; return (0); } snmp_pdu_create(&req, SNMP_PDU_GET); req.engine.engine_boots = 0; req.engine.engine_time = 0; if (snmp_dialog(&req, &resp) == -1) return (-1); if (resp.version != req.version) { seterr(&snmp_client, "wrong version"); return (-1); } if (resp.error_status != SNMP_ERR_NOERROR) { seterr(&snmp_client, "Error %d in responce", resp.error_status); return (-1); } snmp_client.engine.engine_boots = resp.engine.engine_boots; snmp_client.engine.engine_time = resp.engine.engine_time; return (0); } 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: head/contrib/bsnmp/lib/snmpclient.h =================================================================== --- head/contrib/bsnmp/lib/snmpclient.h (revision 310647) +++ head/contrib/bsnmp/lib/snmpclient.h (revision 310648) @@ -1,201 +1,201 @@ /* * 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.h,v 1.19 2005/05/23 11:10:14 brandt_h Exp $ */ #ifndef _BSNMP_SNMPCLIENT_H #define _BSNMP_SNMPCLIENT_H #include #include #include #include #include #define SNMP_STRERROR_LEN 200 #define SNMP_LOCAL_PATH "/tmp/snmpXXXXXXXXXXXXXX" /* * transport methods */ #define SNMP_TRANS_UDP 0 #define SNMP_TRANS_LOC_DGRAM 1 #define SNMP_TRANS_LOC_STREAM 2 /* type of callback function for responses * this callback function is responsible for free() any memory associated with * any of the PDUs. Therefor it may call snmp_pdu_free() */ typedef void (*snmp_send_cb_f)(struct snmp_pdu *, struct snmp_pdu *, void *); /* type of callback function for timeouts */ typedef void (*snmp_timeout_cb_f)(void * ); /* timeout start function */ -typedef void *(*snmp_timeout_start_f)(struct timeval *timeout, +typedef void *(*snmp_timeout_start_f)(struct timeval *timeout, snmp_timeout_cb_f callback, void *); /* timeout stop function */ typedef void (*snmp_timeout_stop_f)(void *timeout_id); /* * Client context. */ struct snmp_client { enum snmp_version version; int trans; /* which transport to use */ /* these two are read-only for the application */ char *cport; /* port number as string */ char *chost; /* host name or IP address as string */ char read_community[SNMP_COMMUNITY_MAXLEN + 1]; char write_community[SNMP_COMMUNITY_MAXLEN + 1]; /* SNMPv3 specific fields */ int32_t identifier; int32_t security_model; struct snmp_engine engine; struct snmp_user user; /* SNMPv3 Access control - VACM*/ uint32_t clen; uint8_t cengine[SNMP_ENGINE_ID_SIZ]; char cname[SNMP_CONTEXT_NAME_SIZ]; struct timeval timeout; u_int retries; int dump_pdus; size_t txbuflen; size_t rxbuflen; int fd; int32_t next_reqid; int32_t max_reqid; int32_t min_reqid; char error[SNMP_STRERROR_LEN]; snmp_timeout_start_f timeout_start; snmp_timeout_stop_f timeout_stop; char local_path[sizeof(SNMP_LOCAL_PATH)]; }; /* the global context */ extern struct snmp_client snmp_client; /* initizialies a snmp_client structure */ void snmp_client_init(struct snmp_client *); /* initialize fields */ int snmp_client_set_host(struct snmp_client *, const char *); int snmp_client_set_port(struct snmp_client *, const char *); /* open connection to snmp server (hostname or portname can be NULL) */ int snmp_open(const char *_hostname, const char *_portname, const char *_read_community, const char *_write_community); /* close connection */ void snmp_close(void); /* initialize a snmp_pdu structure */ void snmp_pdu_create(struct snmp_pdu *, u_int _op); /* add pairs of (struct asn_oid *, enum snmp_syntax) to an existing pdu */ int snmp_add_binding(struct snmp_pdu *, ...); /* check wheater the answer is valid or not */ int snmp_pdu_check(const struct snmp_pdu *_req, const struct snmp_pdu *_resp); int32_t snmp_pdu_send(struct snmp_pdu *_pdu, snmp_send_cb_f _func, void *_arg); /* append an index to an oid */ int snmp_oid_append(struct asn_oid *_oid, const char *_fmt, ...); /* receive a packet */ int snmp_receive(int _blocking); /* * This structure is used to describe an SNMP table that is to be fetched. * The C-structure that is produced by the fetch function must start with * a TAILQ_ENTRY and an u_int64_t. */ struct snmp_table { /* base OID of the table */ struct asn_oid table; /* type OID of the LastChange variable for the table if any */ struct asn_oid last_change; /* maximum number of iterations if table has changed */ u_int max_iter; /* size of the C-structure */ size_t entry_size; /* number of index fields */ u_int index_size; /* bit mask of required fields */ uint64_t req_mask; /* indexes and columns to fetch. Ended by a NULL syntax entry */ struct snmp_table_entry { /* the column sub-oid, ignored for index fields */ asn_subid_t subid; /* the syntax of the column or index */ enum snmp_syntax syntax; /* offset of the field into the C-structure. For octet strings * this points to an u_char * followed by a size_t */ off_t offset; #if defined(__GNUC__) && __GNUC__ < 3 } entries[0]; #else } entries[]; #endif }; /* callback type for table fetch */ typedef void (*snmp_table_cb_f)(void *_list, void *_arg, int _res); /* fetch a table. The argument points to a TAILQ_HEAD */ int snmp_table_fetch(const struct snmp_table *descr, void *); int snmp_table_fetch_async(const struct snmp_table *, void *, snmp_table_cb_f, void *); /* send a request and wait for the response */ int snmp_dialog(struct snmp_pdu *_req, struct snmp_pdu *_resp); /* discover an authorative snmpEngineId */ int snmp_discover_engine(char *); /* parse a server specification */ int snmp_parse_server(struct snmp_client *, const char *); #endif /* _BSNMP_SNMPCLIENT_H */ Index: head/contrib/bsnmp/lib/snmpcrypto.c =================================================================== --- head/contrib/bsnmp/lib/snmpcrypto.c (revision 310647) +++ head/contrib/bsnmp/lib/snmpcrypto.c (revision 310648) @@ -1,402 +1,402 @@ /*- * Copyright (c) 2010 The FreeBSD Foundation * All rights reserved. * * This software was developed by Shteryana Sotirova Shopova under * sponsorship from the FreeBSD Foundation. * * 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 THE 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 THE 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. * * $FreeBSD$ */ #include #include #include #include #include #include #ifdef HAVE_STDINT_H #include #elif defined(HAVE_INTTYPES_H) #include #endif #include #include #include #include #ifdef HAVE_LIBCRYPTO #include #endif #include "asn1.h" #include "snmp.h" #include "snmppriv.h" #define SNMP_PRIV_AES_IV_SIZ 16 #define SNMP_EXTENDED_KEY_SIZ 64 #define SNMP_AUTH_KEY_LOOPCNT 1048576 #define SNMP_AUTH_BUF_SIZE 72 static const uint8_t ipad = 0x36; static const uint8_t opad = 0x5c; #ifdef HAVE_LIBCRYPTO static int32_t snmp_digest_init(const struct snmp_user *user, EVP_MD_CTX *ctx, const EVP_MD **dtype, uint32_t *keylen) { if (user->auth_proto == SNMP_AUTH_HMAC_MD5) { *dtype = EVP_md5(); *keylen = SNMP_AUTH_HMACMD5_KEY_SIZ; } else if (user->auth_proto == SNMP_AUTH_HMAC_SHA) { *dtype = EVP_sha1(); *keylen = SNMP_AUTH_HMACSHA_KEY_SIZ; } else if (user->auth_proto == SNMP_AUTH_NOAUTH) return (0); else { snmp_error("unknown authentication option - %d", user->auth_proto); return (-1); } if (EVP_DigestInit(ctx, *dtype) != 1) return (-1); return (1); } enum snmp_code snmp_pdu_calc_digest(const struct snmp_pdu *pdu, uint8_t *digest) { uint8_t md[EVP_MAX_MD_SIZE], extkey[SNMP_EXTENDED_KEY_SIZ]; uint8_t key1[SNMP_EXTENDED_KEY_SIZ], key2[SNMP_EXTENDED_KEY_SIZ]; uint32_t i, keylen, olen; int32_t err; const EVP_MD *dtype; EVP_MD_CTX ctx; err = snmp_digest_init(&pdu->user, &ctx, &dtype, &keylen); if (err < 0) return (SNMP_CODE_BADDIGEST); else if (err == 0) return (SNMP_CODE_OK); memset(pdu->digest_ptr, 0, sizeof(pdu->msg_digest)); memcpy(extkey, pdu->user.auth_key, keylen); memset(extkey + keylen, 0, sizeof(extkey) - keylen); for (i = 0; i < SNMP_EXTENDED_KEY_SIZ; i++) { key1[i] = extkey[i] ^ ipad; key2[i] = extkey[i] ^ opad; } if (EVP_DigestUpdate(&ctx, key1, SNMP_EXTENDED_KEY_SIZ) != 1 || EVP_DigestUpdate(&ctx, pdu->outer_ptr, pdu->outer_len) != 1 || EVP_DigestFinal(&ctx, md, &olen) != 1) goto failed; if (EVP_DigestInit(&ctx, dtype) != 1 || EVP_DigestUpdate(&ctx, key2, SNMP_EXTENDED_KEY_SIZ) != 1 || EVP_DigestUpdate(&ctx, md, olen) != 1 || EVP_DigestFinal(&ctx, md, &olen) != 1) goto failed; if (olen < SNMP_USM_AUTH_SIZE) { snmp_error("bad digest size - %d", olen); EVP_MD_CTX_cleanup(&ctx); return (SNMP_CODE_BADDIGEST); } memcpy(digest, md, SNMP_USM_AUTH_SIZE); EVP_MD_CTX_cleanup(&ctx); return (SNMP_CODE_OK); failed: EVP_MD_CTX_cleanup(&ctx); return (SNMP_CODE_BADDIGEST); } static int32_t snmp_pdu_cipher_init(const struct snmp_pdu *pdu, int32_t len, const EVP_CIPHER **ctype, uint8_t *piv) { int i; uint32_t netint; if (pdu->user.priv_proto == SNMP_PRIV_DES) { if (len % 8 != 0) return (-1); *ctype = EVP_des_cbc(); memcpy(piv, pdu->msg_salt, sizeof(pdu->msg_salt)); for (i = 0; i < 8; i++) piv[i] = piv[i] ^ pdu->user.priv_key[8 + i]; } else if (pdu->user.priv_proto == SNMP_PRIV_AES) { *ctype = EVP_aes_128_cfb128(); netint = htonl(pdu->engine.engine_boots); memcpy(piv, &netint, sizeof(netint)); piv += sizeof(netint); netint = htonl(pdu->engine.engine_time); memcpy(piv, &netint, sizeof(netint)); piv += sizeof(netint); memcpy(piv, pdu->msg_salt, sizeof(pdu->msg_salt)); } else if (pdu->user.priv_proto == SNMP_PRIV_NOPRIV) return (0); else { snmp_error("unknown privacy option - %d", pdu->user.priv_proto); return (-1); } return (1); } enum snmp_code snmp_pdu_encrypt(const struct snmp_pdu *pdu) { int32_t err, olen; uint8_t iv[SNMP_PRIV_AES_IV_SIZ]; const EVP_CIPHER *ctype; EVP_CIPHER_CTX ctx; err = snmp_pdu_cipher_init(pdu, pdu->scoped_len, &ctype, iv); if (err < 0) return (SNMP_CODE_EDECRYPT); else if (err == 0) return (SNMP_CODE_OK); if (EVP_EncryptInit(&ctx, ctype, pdu->user.priv_key, iv) != 1) return (SNMP_CODE_FAILED); if (EVP_EncryptUpdate(&ctx, pdu->scoped_ptr, &olen, pdu->scoped_ptr, pdu->scoped_len) != 1 || EVP_EncryptFinal(&ctx, pdu->scoped_ptr + olen, &olen) != 1) { EVP_CIPHER_CTX_cleanup(&ctx); return (SNMP_CODE_FAILED); } EVP_CIPHER_CTX_cleanup(&ctx); return (SNMP_CODE_OK); } enum snmp_code snmp_pdu_decrypt(const struct snmp_pdu *pdu) { int32_t err, olen; uint8_t iv[SNMP_PRIV_AES_IV_SIZ]; const EVP_CIPHER *ctype; EVP_CIPHER_CTX ctx; err = snmp_pdu_cipher_init(pdu, pdu->scoped_len, &ctype, iv); if (err < 0) return (SNMP_CODE_EDECRYPT); else if (err == 0) return (SNMP_CODE_OK); if (EVP_DecryptInit(&ctx, ctype, pdu->user.priv_key, iv) != 1 || EVP_CIPHER_CTX_set_padding(&ctx, 0) != 1) return (SNMP_CODE_EDECRYPT); if (EVP_DecryptUpdate(&ctx, pdu->scoped_ptr, &olen, pdu->scoped_ptr, pdu->scoped_len) != 1 || EVP_DecryptFinal(&ctx, pdu->scoped_ptr + olen, &olen) != 1) { EVP_CIPHER_CTX_cleanup(&ctx); return (SNMP_CODE_EDECRYPT); } EVP_CIPHER_CTX_cleanup(&ctx); return (SNMP_CODE_OK); } /* [RFC 3414] - A.2. Password to Key Algorithm */ enum snmp_code snmp_passwd_to_keys(struct snmp_user *user, char *passwd) { int err, loop, i, pwdlen; uint32_t keylen, olen; const EVP_MD *dtype; EVP_MD_CTX ctx; uint8_t authbuf[SNMP_AUTH_BUF_SIZE]; if (passwd == NULL || user == NULL) return (SNMP_CODE_FAILED); err = snmp_digest_init(user, &ctx, &dtype, &keylen); if (err < 0) return (SNMP_CODE_BADDIGEST); else if (err == 0) return (SNMP_CODE_OK); memset(user->auth_key, 0, sizeof(user->auth_key)); pwdlen = strlen(passwd); for (loop = 0; loop < SNMP_AUTH_KEY_LOOPCNT; loop += i) { for (i = 0; i < SNMP_EXTENDED_KEY_SIZ; i++) authbuf[i] = passwd[(loop + i) % pwdlen]; if (EVP_DigestUpdate(&ctx, authbuf, SNMP_EXTENDED_KEY_SIZ) != 1) goto failed; } if (EVP_DigestFinal(&ctx, user->auth_key, &olen) != 1) goto failed; EVP_MD_CTX_cleanup(&ctx); return (SNMP_CODE_OK); failed: EVP_MD_CTX_cleanup(&ctx); return (SNMP_CODE_BADDIGEST); } /* [RFC 3414] - 2.6. Key Localization Algorithm */ enum snmp_code snmp_get_local_keys(struct snmp_user *user, uint8_t *eid, uint32_t elen) { int err; uint32_t keylen, olen; const EVP_MD *dtype; EVP_MD_CTX ctx; uint8_t authbuf[SNMP_AUTH_BUF_SIZE]; if (user == NULL || eid == NULL || elen > SNMP_ENGINE_ID_SIZ) return (SNMP_CODE_FAILED); memset(user->priv_key, 0, sizeof(user->priv_key)); memset(authbuf, 0, sizeof(authbuf)); err = snmp_digest_init(user, &ctx, &dtype, &keylen); if (err < 0) return (SNMP_CODE_BADDIGEST); else if (err == 0) return (SNMP_CODE_OK); memcpy(authbuf, user->auth_key, keylen); memcpy(authbuf + keylen, eid, elen); memcpy(authbuf + keylen + elen, user->auth_key, keylen); if (EVP_DigestUpdate(&ctx, authbuf, 2 * keylen + elen) != 1 || EVP_DigestFinal(&ctx, user->auth_key, &olen) != 1) { EVP_MD_CTX_cleanup(&ctx); return (SNMP_CODE_BADDIGEST); } EVP_MD_CTX_cleanup(&ctx); if (user->priv_proto != SNMP_PRIV_NOPRIV) memcpy(user->priv_key, user->auth_key, sizeof(user->priv_key)); return (SNMP_CODE_OK); } enum snmp_code snmp_calc_keychange(struct snmp_user *user, uint8_t *keychange) { int32_t err, rvalue[SNMP_AUTH_HMACSHA_KEY_SIZ / 4]; uint32_t i, keylen, olen; const EVP_MD *dtype; EVP_MD_CTX ctx; err = snmp_digest_init(user, &ctx, &dtype, &keylen); if (err < 0) return (SNMP_CODE_BADDIGEST); else if (err == 0) return (SNMP_CODE_OK); for (i = 0; i < keylen / 4; i++) rvalue[i] = random(); - + memcpy(keychange, user->auth_key, keylen); memcpy(keychange + keylen, rvalue, keylen); if (EVP_DigestUpdate(&ctx, keychange, 2 * keylen) != 1 || EVP_DigestFinal(&ctx, keychange, &olen) != 1) { EVP_MD_CTX_cleanup(&ctx); return (SNMP_CODE_BADDIGEST); } EVP_MD_CTX_cleanup(&ctx); return (SNMP_CODE_OK); } #else /* !HAVE_LIBCRYPTO */ enum snmp_code snmp_pdu_calc_digest(const struct snmp_pdu *pdu, uint8_t *digest __unused) { if (pdu->user.auth_proto != SNMP_AUTH_NOAUTH) return (SNMP_CODE_BADSECLEVEL); return (SNMP_CODE_OK); } enum snmp_code snmp_pdu_encrypt(const struct snmp_pdu *pdu) { if (pdu->user.priv_proto != SNMP_PRIV_NOPRIV) return (SNMP_CODE_BADSECLEVEL); return (SNMP_CODE_OK); } enum snmp_code snmp_pdu_decrypt(const struct snmp_pdu *pdu) { if (pdu->user.priv_proto != SNMP_PRIV_NOPRIV) return (SNMP_CODE_BADSECLEVEL); return (SNMP_CODE_OK); } enum snmp_code snmp_passwd_to_keys(struct snmp_user *user, char *passwd __unused) { if (user->auth_proto == SNMP_AUTH_NOAUTH && user->priv_proto == SNMP_PRIV_NOPRIV) return (SNMP_CODE_OK); errno = EPROTONOSUPPORT; return (SNMP_CODE_FAILED); } enum snmp_code snmp_get_local_keys(struct snmp_user *user, uint8_t *eid __unused, uint32_t elen __unused) { if (user->auth_proto == SNMP_AUTH_NOAUTH && user->priv_proto == SNMP_PRIV_NOPRIV) return (SNMP_CODE_OK); errno = EPROTONOSUPPORT; return (SNMP_CODE_FAILED); } enum snmp_code snmp_calc_keychange(struct snmp_user *user __unused, uint8_t *keychange __unused) { errno = EPROTONOSUPPORT; return (SNMP_CODE_FAILED); } #endif /* HAVE_LIBCRYPTO */ Index: head/contrib/bsnmp/lib/snmppriv.h =================================================================== --- head/contrib/bsnmp/lib/snmppriv.h (revision 310647) +++ head/contrib/bsnmp/lib/snmppriv.h (revision 310648) @@ -1,47 +1,47 @@ /* * 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/snmppriv.h,v 1.9 2004/08/06 08:46:58 brandt Exp $ * * Private functions. */ #include enum asn_err snmp_binding_encode(struct asn_buf *, const struct snmp_value *); enum snmp_code snmp_pdu_encode_header(struct asn_buf *, struct snmp_pdu *); enum snmp_code snmp_fix_encoding(struct asn_buf *, struct snmp_pdu *); enum asn_err snmp_parse_pdus_hdr(struct asn_buf *b, struct snmp_pdu *pdu, asn_len_t *lenp); enum snmp_code snmp_pdu_calc_digest(const struct snmp_pdu *, uint8_t *); enum snmp_code snmp_pdu_encrypt(const struct snmp_pdu *); enum snmp_code snmp_pdu_decrypt(const struct snmp_pdu *); #define DEFAULT_HOST "localhost" #define DEFAULT_PORT "snmp" #define DEFAULT_LOCAL "/var/run/snmp.sock" Index: head/contrib/bsnmp/lib/support.c =================================================================== --- head/contrib/bsnmp/lib/support.c (revision 310647) +++ head/contrib/bsnmp/lib/support.c (revision 310648) @@ -1,194 +1,194 @@ -/* +/* * 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/lib/support.c,v 1.1 2004/08/06 08:47:58 brandt Exp $ * * Functions that are missing on certain systems. */ #include #include #include #include #include #include "support.h" #ifndef HAVE_ERR_H void warnx(const char *fmt, ...) { va_list ap; va_start(ap, fmt); fprintf(stderr, "warning: "); vfprintf(stderr, fmt, ap); fprintf(stderr, "\n"); va_end(ap); } void warn(const char *fmt, ...) { va_list ap; int e = errno; va_start(ap, fmt); fprintf(stderr, "warning: "); vfprintf(stderr, fmt, ap); fprintf(stderr, ": %s\n", strerror(e)); va_end(ap); } void errx(int code, const char *fmt, ...) { va_list ap; va_start(ap, fmt); fprintf(stderr, "error: "); vfprintf(stderr, fmt, ap); fprintf(stderr, "\n"); va_end(ap); exit(code); } void err(int code, const char *fmt, ...) { va_list ap; int e = errno; va_start(ap, fmt); fprintf(stderr, "error: "); vfprintf(stderr, fmt, ap); fprintf(stderr, ": %s\n", strerror(e)); va_end(ap); exit(code); } #endif #ifndef HAVE_STRLCPY size_t strlcpy(char *dst, const char *src, size_t len) { size_t ret = strlen(dst); while (len > 1) { *dst++ = *src++; len--; } if (len > 0) *dst = '\0'; return (ret); } #endif #ifndef HAVE_GETADDRINFO #include #include #include #include extern int h_nerr; extern int h_errno; extern const char *h_errlist[]; /* * VERY poor man's implementation */ int getaddrinfo(const char *host, const char *port, const struct addrinfo *hints, struct addrinfo **res) { struct hostent *hent; struct sockaddr_in *s; struct servent *sent; if ((hent = gethostbyname(host)) == NULL) return (h_errno); if (hent->h_addrtype != hints->ai_family) return (HOST_NOT_FOUND); if (hent->h_addrtype != AF_INET) return (HOST_NOT_FOUND); if ((*res = malloc(sizeof(**res))) == NULL) return (HOST_NOT_FOUND); (*res)->ai_flags = hints->ai_flags; (*res)->ai_family = hints->ai_family; (*res)->ai_socktype = hints->ai_socktype; (*res)->ai_protocol = hints->ai_protocol; (*res)->ai_next = NULL; if (((*res)->ai_addr = malloc(sizeof(struct sockaddr_in))) == NULL) { freeaddrinfo(*res); return (HOST_NOT_FOUND); } (*res)->ai_addrlen = sizeof(struct sockaddr_in); s = (struct sockaddr_in *)(*res)->ai_addr; s->sin_family = hints->ai_family; s->sin_len = sizeof(*s); memcpy(&s->sin_addr, hent->h_addr, 4); if ((sent = getservbyname(port, NULL)) == NULL) { freeaddrinfo(*res); return (HOST_NOT_FOUND); } s->sin_port = sent->s_port; return (0); } const char * gai_strerror(int e) { if (e < 0 || e >= h_nerr) return ("unknown error"); return (h_errlist[e]); } void freeaddrinfo(struct addrinfo *p) { struct addrinfo *next; while (p != NULL) { next = p->ai_next; if (p->ai_addr != NULL) free(p->ai_addr); free(p); p = next; } } #endif Index: head/contrib/bsnmp/lib/support.h =================================================================== --- head/contrib/bsnmp/lib/support.h (revision 310647) +++ head/contrib/bsnmp/lib/support.h (revision 310648) @@ -1,96 +1,96 @@ /* * 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.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: head/contrib/bsnmp/snmp_mibII/mibII.c =================================================================== --- head/contrib/bsnmp/snmp_mibII/mibII.c (revision 310647) +++ head/contrib/bsnmp/snmp_mibII/mibII.c (revision 310648) @@ -1,1818 +1,1818 @@ /* * 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: mibII.c 516 2006-10-27 15:54:02Z brandt_h $ * * Implementation of the standard interfaces and ip MIB. */ #include "mibII.h" #include "mibII_oid.h" #include #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; /* currently fetching the arp table */ 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; /* Idle poll timer */ static void *mibII_poll_timer; /* interfaces' data poll interval */ u_int mibII_poll_ticks; /* Idle poll hook */ static void mibII_idle(void *arg __unused); /*****************************/ 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 the GENERIC IFMIB and update the HC counters */ static int fetch_generic_mib(struct mibif *ifp, const struct ifmibdata *old) { int name[6]; size_t len; 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); } /* * 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 <= IF_Mbps(10)) { /* at 10Mbps overflow needs 3436 seconds */ ticks = 3000 * 100; /* 50 minutes */ } else if (mibif_maxspeed <= IF_Mbps(100)) { /* at 100Mbps overflow needs 343 seconds */ ticks = 300 * 100; /* 5 minutes */ } else if (mibif_maxspeed < IF_Mbps(622)) { /* at 622Mbps overflow needs 53 seconds */ ticks = 40 * 100; /* 40 seconds */ } else if (mibif_maxspeed <= IF_Mbps(1000)) { /* at 1Gbps overflow needs 34 seconds */ ticks = 20 * 100; /* 20 seconds */ } else { /* 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; } /** * Restart the idle poll timer. */ void mibif_restart_mibII_poll_timer(void) { if (mibII_poll_timer != NULL) timer_stop(mibII_poll_timer); if ((mibII_poll_timer = timer_start_repeat(mibII_poll_ticks * 10, mibII_poll_ticks * 10, mibII_idle, NULL, module)) == NULL) syslog(LOG_ERR, "timer_start(%u): %m", mibII_poll_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; struct ifreq irr; 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: strncpy(irr.ifr_name, ifp->name, sizeof(irr.ifr_name)); irr.ifr_buffer.buffer = MIBIF_PRIV(ifp)->alias; irr.ifr_buffer.length = sizeof(MIBIF_PRIV(ifp)->alias); if (ioctl(mib_netsock, SIOCGIFDESCR, &irr) == -1) { MIBIF_PRIV(ifp)->alias[0] = 0; if (errno != ENOMSG) syslog(LOG_WARNING, "SIOCGIFDESCR (%s): %m", ifp->name); } else if (irr.ifr_buffer.buffer == NULL) { MIBIF_PRIV(ifp)->alias[0] = 0; syslog(LOG_WARNING, "SIOCGIFDESCR (%s): too long (%zu)", ifp->name, irr.ifr_buffer.length); } 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: case IFT_L2VLAN: 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) 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, *ifamp; 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: ifamp = (struct ifa_msghdr *)rtm; memcpy(&ifam, ifamp, sizeof(ifam)); mib_extract_addrs(ifam.ifam_addrs, (u_char *)(ifamp + 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: ifamp = (struct ifa_msghdr *)rtm; memcpy(&ifam, ifamp, sizeof(ifam)); mib_extract_addrs(ifam.ifam_addrs, (u_char *)(ifamp + 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 *)(void *)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: 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_UP)) 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, 0, &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(); 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. + * 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 *arg __unused) { 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; } 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); mibII_poll_timer = NULL; mibII_poll_ticks = MIBII_POLL_TICKS; mibif_restart_mibII_poll_timer(); } /* * 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 (mibII_poll_timer != NULL ) { timer_stop(mibII_poll_timer); mibII_poll_timer = NULL; } 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, NULL, /* 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: head/contrib/bsnmp/snmp_mibII/mibII.h =================================================================== --- head/contrib/bsnmp/snmp_mibII/mibII.h (revision 310647) +++ head/contrib/bsnmp/snmp_mibII/mibII.h (revision 310648) @@ -1,278 +1,278 @@ /* * 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.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 #include "asn1.h" #include "snmp.h" #include "snmpmod.h" #include "snmp_mibII.h" #include "mibII_tree.h" /* maximum size of the interface alias */ #define MIBIF_ALIAS_SIZE (64 + 1) /* * Interface list and flags. */ TAILQ_HEAD(mibif_list, mibif); enum { MIBIF_FOUND = 0x0001, MIBIF_HIGHSPEED = 0x0002, MIBIF_VERYHIGHSPEED = 0x0004, }; /* * 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; /* this should be made public */ char alias[MIBIF_ALIAS_SIZE]; }; #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); /* interfaces' data poll interval */ extern u_int mibII_poll_ticks; /* restart the data poll timer */ void mibif_restart_mibII_poll_timer(void); #define MIBII_POLL_TICKS 100 /* 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: head/contrib/bsnmp/snmp_mibII/mibII_begemot.c =================================================================== --- head/contrib/bsnmp/snmp_mibII/mibII_begemot.c (revision 310647) +++ head/contrib/bsnmp/snmp_mibII/mibII_begemot.c (revision 310648) @@ -1,120 +1,120 @@ /* * Copyright (c) 2006 * 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_mibII/mibII_begemot.c,v 1.1 2006/02/14 09:04:19 brandt_h Exp $ * * Private MIB. */ #include "mibII.h" #include "mibII_oid.h" /* * Scalars */ int op_begemot_mibII(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: goto get; case SNMP_OP_SET: switch (value->var.subs[sub - 1]) { case LEAF_begemotIfMaxspeed: case LEAF_begemotIfPoll: return (SNMP_ERR_NOT_WRITEABLE); case LEAF_begemotIfForcePoll: ctx->scratch->int1 = mibif_force_hc_update_interval; mibif_force_hc_update_interval = value->v.uint32; return (SNMP_ERR_NOERROR); case LEAF_begemotIfDataPoll: ctx->scratch->int1 = mibII_poll_ticks; mibII_poll_ticks = value->v.uint32; return (SNMP_ERR_NOERROR); } abort(); case SNMP_OP_ROLLBACK: switch (value->var.subs[sub - 1]) { case LEAF_begemotIfForcePoll: mibif_force_hc_update_interval = ctx->scratch->int1; return (SNMP_ERR_NOERROR); case LEAF_begemotIfDataPoll: mibII_poll_ticks = ctx->scratch->int1; return (SNMP_ERR_NOERROR); } abort(); case SNMP_OP_COMMIT: switch (value->var.subs[sub - 1]) { case LEAF_begemotIfForcePoll: mibif_force_hc_update_interval = ctx->scratch->int1; mibif_reset_hc_timer(); return (SNMP_ERR_NOERROR); case LEAF_begemotIfDataPoll: mibif_restart_mibII_poll_timer(); return (SNMP_ERR_NOERROR); } abort(); } abort(); get: switch (value->var.subs[sub - 1]) { case LEAF_begemotIfMaxspeed: value->v.counter64 = mibif_maxspeed; return (SNMP_ERR_NOERROR); case LEAF_begemotIfPoll: value->v.uint32 = mibif_hc_update_interval; return (SNMP_ERR_NOERROR); case LEAF_begemotIfForcePoll: value->v.uint32 = mibif_force_hc_update_interval; return (SNMP_ERR_NOERROR); case LEAF_begemotIfDataPoll: value->v.uint32 = mibII_poll_ticks; return (SNMP_ERR_NOERROR); } abort(); } Index: head/contrib/bsnmp/snmp_mibII/mibII_ifmib.c =================================================================== --- head/contrib/bsnmp/snmp_mibII/mibII_ifmib.c (revision 310647) +++ head/contrib/bsnmp/snmp_mibII/mibII_ifmib.c (revision 310648) @@ -1,76 +1,76 @@ /* * 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_ifmib.c,v 1.9 2004/08/06 08:47:00 brandt Exp $ * * Interfaces group. */ #include "mibII.h" #include "mibII_oid.h" /* * Scalars */ int op_ifmib(struct snmp_context *ctx __unused, struct snmp_value *value, u_int sub, u_int idx __unused, enum snmp_op op) { switch (op) { case SNMP_OP_GETNEXT: abort(); case SNMP_OP_GET: break; case SNMP_OP_SET: return (SNMP_ERR_NOT_WRITEABLE); case SNMP_OP_ROLLBACK: case SNMP_OP_COMMIT: abort(); } switch (value->var.subs[sub - 1]) { case LEAF_ifTableLastChange: if (mib_iftable_last_change > start_tick) value->v.uint32 = mib_iftable_last_change - start_tick; else value->v.uint32 = 0; break; case LEAF_ifStackLastChange: if (mib_ifstack_last_change > start_tick) value->v.uint32 = mib_ifstack_last_change - start_tick; else value->v.uint32 = 0; break; } return (SNMP_ERR_NOERROR); } Index: head/contrib/bsnmp/snmp_mibII/mibII_ifstack.c =================================================================== --- head/contrib/bsnmp/snmp_mibII/mibII_ifstack.c (revision 310647) +++ head/contrib/bsnmp/snmp_mibII/mibII_ifstack.c (revision 310648) @@ -1,105 +1,105 @@ /* * 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_ifstack.c,v 1.7 2004/08/06 08:47:00 brandt Exp $ * * ifStackTable. Read-only. */ #include "mibII.h" int mib_ifstack_create(const struct mibif *lower, const struct mibif *upper) { struct mibifstack *stack; if ((stack = malloc(sizeof(*stack))) == NULL) return (-1); stack->index.len = 2; stack->index.subs[0] = upper ? upper->index : 0; stack->index.subs[1] = lower ? lower->index : 0; INSERT_OBJECT_OID(stack, &mibifstack_list); mib_ifstack_last_change = get_ticks(); return (0); } void mib_ifstack_delete(const struct mibif *lower, const struct mibif *upper) { struct mibifstack *stack; TAILQ_FOREACH(stack, &mibifstack_list, link) if (stack->index.subs[0] == (upper ? upper->index : 0) && stack->index.subs[1] == (lower ? lower->index : 0)) { TAILQ_REMOVE(&mibifstack_list, stack, link); free(stack); mib_ifstack_last_change = get_ticks(); return; } } int op_ifstack(struct snmp_context *ctx __unused, struct snmp_value *value, u_int sub, u_int iidx __unused, enum snmp_op op) { struct mibifstack *stack; switch (op) { case SNMP_OP_GETNEXT: if ((stack = NEXT_OBJECT_OID(&mibifstack_list, &value->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); index_append(&value->var, sub, &stack->index); break; case SNMP_OP_GET: if ((stack = FIND_OBJECT_OID(&mibifstack_list, &value->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_SET: if ((stack = FIND_OBJECT_OID(&mibifstack_list, &value->var, sub)) == NULL) return (SNMP_ERR_NO_CREATION); return (SNMP_ERR_NOT_WRITEABLE); case SNMP_OP_ROLLBACK: case SNMP_OP_COMMIT: abort(); } switch (value->var.subs[sub - 1]) { case LEAF_ifStackStatus: value->v.integer = 1; break; } return (SNMP_ERR_NOERROR); } Index: head/contrib/bsnmp/snmp_mibII/mibII_interfaces.c =================================================================== --- head/contrib/bsnmp/snmp_mibII/mibII_interfaces.c (revision 310647) +++ head/contrib/bsnmp/snmp_mibII/mibII_interfaces.c (revision 310648) @@ -1,542 +1,542 @@ /* * 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.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_UP) 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 (ret); } /* * 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 = MIBIF_PRIV(ifp)->hc_inoctets; break; case LEAF_ifHCInUcastPkts: if (!(ifp->flags & (MIBIF_VERYHIGHSPEED|MIBIF_HIGHSPEED))) goto again; 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 = 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 = MIBIF_PRIV(ifp)->hc_outoctets; break; case LEAF_ifHCOutUcastPkts: if (!(ifp->flags & (MIBIF_VERYHIGHSPEED|MIBIF_HIGHSPEED))) goto again; 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 = 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, MIBIF_PRIV(ifp)->alias, -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: head/contrib/bsnmp/snmp_mibII/mibII_ip.c =================================================================== --- head/contrib/bsnmp/snmp_mibII/mibII_ip.c (revision 310647) +++ head/contrib/bsnmp/snmp_mibII/mibII_ip.c (revision 310648) @@ -1,493 +1,493 @@ /* * 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_ip.c,v 1.11 2005/05/23 09:03:40 brandt_h Exp $ * * ip group scalars. */ #include "mibII.h" #include "mibII_oid.h" #include #include #include #include #include static struct ipstat ipstat; static u_int ip_idrop; static struct icmpstat icmpstat; static int ip_forwarding; static int ip_defttl; static uint64_t ip_tick; static uint64_t ipstat_tick; static int fetch_ipstat(void) { size_t len; len = sizeof(ipstat); if (sysctlbyname("net.inet.ip.stats", &ipstat, &len, NULL, 0) == -1) { syslog(LOG_ERR, "net.inet.ip.stats: %m"); return (-1); } if (len != sizeof(ipstat)) { syslog(LOG_ERR, "net.inet.ip.stats: wrong size"); return (-1); } len = sizeof(ip_idrop); if (sysctlbyname("net.inet.ip.intr_queue_drops", &ip_idrop, &len, NULL, 0) == -1) syslog(LOG_WARNING, "net.inet.ip.intr_queue_drops: %m"); if (len != sizeof(ip_idrop)) { syslog(LOG_WARNING, "net.inet.ip.intr_queue_drops: wrong size"); ip_idrop = 0; } len = sizeof(icmpstat); if (sysctlbyname("net.inet.icmp.stats", &icmpstat, &len, NULL, 0) == -1) { syslog(LOG_ERR, "net.inet.icmp.stats: %m"); return (-1); } if (len != sizeof(icmpstat)) { syslog(LOG_ERR, "net.inet.icmp.stats: wrong size"); return (-1); } ipstat_tick = get_ticks(); return (0); } static int fetch_ip(void) { size_t len; len = sizeof(ip_forwarding); if (sysctlbyname("net.inet.ip.forwarding", &ip_forwarding, &len, NULL, 0) == -1) { syslog(LOG_ERR, "net.inet.ip.forwarding: %m"); return (-1); } if (len != sizeof(ip_forwarding)) { syslog(LOG_ERR, "net.inet.ip.forwarding: wrong size"); return (-1); } len = sizeof(ip_defttl); if (sysctlbyname("net.inet.ip.ttl", &ip_defttl, &len, NULL, 0) == -1) { syslog(LOG_ERR, "net.inet.ip.ttl: %m"); return (-1); } if (len != sizeof(ip_defttl)) { syslog(LOG_ERR, "net.inet.ip.ttl: wrong size"); return (-1); } ip_tick = get_ticks(); return (0); } static int ip_forward(int forw, int *old) { size_t olen; olen = sizeof(*old); if (sysctlbyname("net.inet.ip.forwarding", old, old ? &olen : NULL, &forw, sizeof(forw)) == -1) { syslog(LOG_ERR, "set net.inet.ip.forwarding: %m"); return (-1); } ip_forwarding = forw; return (0); } static int ip_setttl(int ttl, int *old) { size_t olen; olen = sizeof(*old); if (sysctlbyname("net.inet.ip.ttl", old, old ? &olen : NULL, &ttl, sizeof(ttl)) == -1) { syslog(LOG_ERR, "set net.inet.ip.ttl: %m"); return (-1); } ip_defttl = ttl; return (0); } /* * READ/WRITE ip group. */ int op_ip(struct snmp_context *ctx, struct snmp_value *value, u_int sub, u_int idx __unused, enum snmp_op op) { int old = 0; switch (op) { case SNMP_OP_GETNEXT: abort(); case SNMP_OP_GET: break; case SNMP_OP_SET: if (ip_tick < this_tick) if (fetch_ip() == -1) return (SNMP_ERR_GENERR); switch (value->var.subs[sub - 1]) { case LEAF_ipForwarding: ctx->scratch->int1 = ip_forwarding ? 1 : 2; ctx->scratch->int2 = value->v.integer; if (value->v.integer == 1) { if (!ip_forwarding && ip_forward(1, &old)) return (SNMP_ERR_GENERR); ctx->scratch->int1 = old ? 1 : 2; } else if (value->v.integer == 2) { if (ip_forwarding && ip_forward(0, &old)) return (SNMP_ERR_GENERR); ctx->scratch->int1 = old; } else return (SNMP_ERR_WRONG_VALUE); break; case LEAF_ipDefaultTTL: ctx->scratch->int1 = ip_defttl; ctx->scratch->int2 = value->v.integer; if (value->v.integer < 1 || value->v.integer > 255) return (SNMP_ERR_WRONG_VALUE); if (ip_defttl != value->v.integer && ip_setttl(value->v.integer, &old)) return (SNMP_ERR_GENERR); ctx->scratch->int1 = old; break; } return (SNMP_ERR_NOERROR); case SNMP_OP_ROLLBACK: switch (value->var.subs[sub - 1]) { case LEAF_ipForwarding: if (ctx->scratch->int1 == 1) { if (ctx->scratch->int2 == 2) (void)ip_forward(1, NULL); } else { if (ctx->scratch->int2 == 1) (void)ip_forward(0, NULL); } break; case LEAF_ipDefaultTTL: if (ctx->scratch->int1 != ctx->scratch->int2) (void)ip_setttl(ctx->scratch->int1, NULL); break; } return (SNMP_ERR_NOERROR); case SNMP_OP_COMMIT: return (SNMP_ERR_NOERROR); } if (ip_tick < this_tick) if (fetch_ip() == -1) return (SNMP_ERR_GENERR); switch (value->var.subs[sub - 1]) { case LEAF_ipForwarding: value->v.integer = ip_forwarding ? 1 : 2; break; case LEAF_ipDefaultTTL: value->v.integer = ip_defttl; break; } return (SNMP_ERR_NOERROR); } /* * READ-ONLY statistics ip group. */ int op_ipstat(struct snmp_context *ctx __unused, struct snmp_value *value, u_int sub, u_int idx __unused, enum snmp_op op) { switch (op) { case SNMP_OP_GETNEXT: abort(); case SNMP_OP_GET: break; case SNMP_OP_SET: return (SNMP_ERR_NOT_WRITEABLE); case SNMP_OP_ROLLBACK: case SNMP_OP_COMMIT: abort(); } if (ipstat_tick < this_tick) fetch_ipstat(); switch (value->var.subs[sub - 1]) { case LEAF_ipInReceives: value->v.uint32 = ipstat.ips_total; break; case LEAF_ipInHdrErrors: value->v.uint32 = ipstat.ips_badsum + ipstat.ips_tooshort + ipstat.ips_toosmall + ipstat.ips_badhlen + ipstat.ips_badlen + ipstat.ips_badvers + + ipstat.ips_toolong; break; case LEAF_ipInAddrErrors: value->v.uint32 = ipstat.ips_cantforward; break; case LEAF_ipForwDatagrams: value->v.uint32 = ipstat.ips_forward; break; case LEAF_ipInUnknownProtos: value->v.uint32 = ipstat.ips_noproto; break; case LEAF_ipInDiscards: value->v.uint32 = ip_idrop; break; case LEAF_ipInDelivers: value->v.uint32 = ipstat.ips_delivered; break; case LEAF_ipOutRequests: value->v.uint32 = ipstat.ips_localout; break; case LEAF_ipOutDiscards: value->v.uint32 = ipstat.ips_odropped; break; case LEAF_ipOutNoRoutes: value->v.uint32 = ipstat.ips_noroute; break; case LEAF_ipReasmTimeout: value->v.integer = IPFRAGTTL; break; case LEAF_ipReasmReqds: value->v.uint32 = ipstat.ips_fragments; break; case LEAF_ipReasmOKs: value->v.uint32 = ipstat.ips_reassembled; break; case LEAF_ipReasmFails: value->v.uint32 = ipstat.ips_fragdropped + ipstat.ips_fragtimeout; break; case LEAF_ipFragOKs: value->v.uint32 = ipstat.ips_fragmented; break; case LEAF_ipFragFails: value->v.uint32 = ipstat.ips_cantfrag; break; case LEAF_ipFragCreates: value->v.uint32 = ipstat.ips_ofragments; break; } return (SNMP_ERR_NOERROR); } /* * READ-ONLY statistics icmp group. */ int op_icmpstat(struct snmp_context *ctx __unused, struct snmp_value *value, u_int sub, u_int idx __unused, enum snmp_op op) { u_int i; switch (op) { case SNMP_OP_GETNEXT: abort(); case SNMP_OP_GET: break; case SNMP_OP_SET: return (SNMP_ERR_NOT_WRITEABLE); case SNMP_OP_ROLLBACK: case SNMP_OP_COMMIT: abort(); } if (ipstat_tick < this_tick) fetch_ipstat(); switch (value->var.subs[sub - 1]) { case LEAF_icmpInMsgs: value->v.integer = 0; for (i = 0; i <= ICMP_MAXTYPE; i++) value->v.integer += icmpstat.icps_inhist[i]; value->v.integer += icmpstat.icps_tooshort + icmpstat.icps_checksum; /* missing: bad type and packets on faith */ break; case LEAF_icmpInErrors: value->v.integer = icmpstat.icps_tooshort + icmpstat.icps_checksum + icmpstat.icps_badlen + icmpstat.icps_badcode + icmpstat.icps_bmcastecho + icmpstat.icps_bmcasttstamp; break; case LEAF_icmpInDestUnreachs: value->v.integer = icmpstat.icps_inhist[ICMP_UNREACH]; break; case LEAF_icmpInTimeExcds: value->v.integer = icmpstat.icps_inhist[ICMP_TIMXCEED]; break; case LEAF_icmpInParmProbs: value->v.integer = icmpstat.icps_inhist[ICMP_PARAMPROB]; break; case LEAF_icmpInSrcQuenchs: value->v.integer = icmpstat.icps_inhist[ICMP_SOURCEQUENCH]; break; case LEAF_icmpInRedirects: value->v.integer = icmpstat.icps_inhist[ICMP_REDIRECT]; break; case LEAF_icmpInEchos: value->v.integer = icmpstat.icps_inhist[ICMP_ECHO]; break; case LEAF_icmpInEchoReps: value->v.integer = icmpstat.icps_inhist[ICMP_ECHOREPLY]; break; case LEAF_icmpInTimestamps: value->v.integer = icmpstat.icps_inhist[ICMP_TSTAMP]; break; case LEAF_icmpInTimestampReps: value->v.integer = icmpstat.icps_inhist[ICMP_TSTAMPREPLY]; break; case LEAF_icmpInAddrMasks: value->v.integer = icmpstat.icps_inhist[ICMP_MASKREQ]; break; case LEAF_icmpInAddrMaskReps: value->v.integer = icmpstat.icps_inhist[ICMP_MASKREPLY]; break; case LEAF_icmpOutMsgs: value->v.integer = 0; for (i = 0; i <= ICMP_MAXTYPE; i++) value->v.integer += icmpstat.icps_outhist[i]; value->v.integer += icmpstat.icps_badaddr + icmpstat.icps_noroute; break; case LEAF_icmpOutErrors: value->v.integer = icmpstat.icps_badaddr + icmpstat.icps_noroute; break; case LEAF_icmpOutDestUnreachs: value->v.integer = icmpstat.icps_outhist[ICMP_UNREACH]; break; case LEAF_icmpOutTimeExcds: value->v.integer = icmpstat.icps_outhist[ICMP_TIMXCEED]; break; case LEAF_icmpOutParmProbs: value->v.integer = icmpstat.icps_outhist[ICMP_PARAMPROB]; break; case LEAF_icmpOutSrcQuenchs: value->v.integer = icmpstat.icps_outhist[ICMP_SOURCEQUENCH]; break; case LEAF_icmpOutRedirects: value->v.integer = icmpstat.icps_outhist[ICMP_REDIRECT]; break; case LEAF_icmpOutEchos: value->v.integer = icmpstat.icps_outhist[ICMP_ECHO]; break; case LEAF_icmpOutEchoReps: value->v.integer = icmpstat.icps_outhist[ICMP_ECHOREPLY]; break; case LEAF_icmpOutTimestamps: value->v.integer = icmpstat.icps_outhist[ICMP_TSTAMP]; break; case LEAF_icmpOutTimestampReps: value->v.integer = icmpstat.icps_outhist[ICMP_TSTAMPREPLY]; break; case LEAF_icmpOutAddrMasks: value->v.integer = icmpstat.icps_outhist[ICMP_MASKREQ]; break; case LEAF_icmpOutAddrMaskReps: value->v.integer = icmpstat.icps_outhist[ICMP_MASKREPLY]; break; } return (SNMP_ERR_NOERROR); } Index: head/contrib/bsnmp/snmp_mibII/mibII_ipaddr.c =================================================================== --- head/contrib/bsnmp/snmp_mibII/mibII_ipaddr.c (revision 310647) +++ head/contrib/bsnmp/snmp_mibII/mibII_ipaddr.c (revision 310648) @@ -1,340 +1,340 @@ /* * Copyright (c) 2001-2002 * 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_ipaddr.c,v 1.10 2005/10/04 11:21:35 brandt_h Exp $ * * IP address table. This table is writeable! * * Writing to this table will add a new IP address to the interface. * An address can be deleted with writing the interface index 0. */ #include "mibII.h" #include "mibII_oid.h" static const struct asn_oid oid_ipAddrTable = OIDX_ipAddrTable; /* * Be careful not to hold any pointers during the SET processing - the * interface and address lists can be relocated at any time. */ struct update { struct snmp_dependency dep; uint32_t set; struct in_addr addr; struct in_addr mask; int bcast; u_int ifindex; uint32_t rb; struct in_addr rb_mask; struct in_addr rb_bcast; }; #define UPD_IFINDEX 0x0001 #define UPD_MASK 0x0002 #define UPD_BCAST 0x0004 #define RB_CREATE 0x0001 #define RB_DESTROY 0x0002 #define RB_MODIFY 0x0004 /* * Create a new interface address */ static int create(struct update *upd) { struct in_addr bcast; struct mibifa *ifa; if (!(upd->set & UPD_MASK)) { if (IN_CLASSA(ntohl(upd->addr.s_addr))) upd->mask.s_addr = htonl(IN_CLASSA_NET); else if (IN_CLASSB(ntohl(upd->addr.s_addr))) upd->mask.s_addr = htonl(IN_CLASSB_NET); else if (IN_CLASSC(ntohl(upd->addr.s_addr))) upd->mask.s_addr = htonl(IN_CLASSC_NET); else upd->mask.s_addr = 0xffffffff; } bcast.s_addr = upd->addr.s_addr & upd->mask.s_addr; if (!(upd->set & UPD_BCAST) || upd->bcast) { uint32_t tmp = ~ntohl(upd->mask.s_addr); bcast.s_addr |= htonl(0xffffffff & ~tmp); } if ((ifa = mib_create_ifa(upd->ifindex, upd->addr, upd->mask, bcast)) == NULL) return (SNMP_ERR_GENERR); upd->rb |= RB_CREATE; return (SNMP_ERR_NOERROR); } /* * Modify the netmask or broadcast address. The ifindex cannot be * changed (obviously). */ static int modify(struct update *upd, struct mibifa *ifa) { struct mibif *ifp; if ((ifp = mib_find_if(ifa->ifindex)) == NULL) return (SNMP_ERR_WRONG_VALUE); if ((upd->set & UPD_IFINDEX) && upd->ifindex != ifa->ifindex) return (SNMP_ERR_INCONS_VALUE); upd->rb_mask = ifa->inmask; upd->rb_bcast = ifa->inbcast; if (((upd->set & UPD_MASK) && upd->mask.s_addr != ifa->inmask.s_addr) || (upd->set & UPD_BCAST)) { if (upd->set & UPD_MASK) ifa->inmask = upd->mask; if (upd->set & UPD_BCAST) { ifa->inbcast.s_addr = ifa->inaddr.s_addr & ifa->inmask.s_addr; if (upd->bcast) ifa->inbcast.s_addr |= 0xffffffff & ~ifa->inmask.s_addr; } if (mib_modify_ifa(ifa)) { syslog(LOG_ERR, "set netmask/bcast: %m"); ifa->inmask = upd->rb_mask; ifa->inbcast = upd->rb_bcast; mib_unmodify_ifa(ifa); return (SNMP_ERR_GENERR); } upd->rb |= RB_MODIFY; } return (SNMP_ERR_NOERROR); } /* * Destroy the given row in the table. We remove the address from the * system, but keep the structure around for the COMMIT. It's deleted * only in the FINISH operation. */ static int destroy(struct snmp_context *ctx __unused, struct update *upd, struct mibifa *ifa) { if (mib_destroy_ifa(ifa)) return (SNMP_ERR_GENERR); upd->rb |= RB_DESTROY; return (SNMP_ERR_NOERROR); } /* * This function is called to commit/rollback a SET on an IpAddrEntry */ static int update_func(struct snmp_context *ctx, struct snmp_dependency *dep, enum snmp_depop op) { struct update *upd = (struct update *)dep; struct mibifa *ifa; switch (op) { case SNMP_DEPOP_COMMIT: if ((ifa = mib_find_ifa(upd->addr)) == NULL) { /* non existing entry - must have ifindex */ if (!(upd->set & UPD_IFINDEX)) return (SNMP_ERR_INCONS_NAME); return (create(upd)); } /* existing entry */ if ((upd->set & UPD_IFINDEX) && upd->ifindex == 0) { /* delete */ return (destroy(ctx, upd, ifa)); } /* modify entry */ return (modify(upd, ifa)); case SNMP_DEPOP_ROLLBACK: if ((ifa = mib_find_ifa(upd->addr)) == NULL) { /* ups */ mib_iflist_bad = 1; return (SNMP_ERR_NOERROR); } if (upd->rb & RB_CREATE) { mib_uncreate_ifa(ifa); return (SNMP_ERR_NOERROR); } if (upd->rb & RB_DESTROY) { mib_undestroy_ifa(ifa); return (SNMP_ERR_NOERROR); } if (upd->rb & RB_MODIFY) { ifa->inmask = upd->rb_mask; ifa->inbcast = upd->rb_bcast; mib_unmodify_ifa(ifa); return (SNMP_ERR_NOERROR); } return (SNMP_ERR_NOERROR); case SNMP_DEPOP_FINISH: if ((upd->rb & RB_DESTROY) && (ifa = mib_find_ifa(upd->addr)) != NULL && (ifa->flags & MIBIFA_DESTROYED)) { TAILQ_REMOVE(&mibifa_list, ifa, link); free(ifa); } return (SNMP_ERR_NOERROR); } abort(); } /**********************************************************************/ /* * ACTION */ int op_ipaddr(struct snmp_context *ctx, struct snmp_value *value, u_int sub, u_int iidx, enum snmp_op op) { asn_subid_t which; struct mibifa *ifa; struct update *upd; struct asn_oid idx; u_char ipaddr[4]; which = value->var.subs[sub - 1]; ifa = NULL; switch (op) { case SNMP_OP_GETNEXT: if ((ifa = NEXT_OBJECT_OID(&mibifa_list, &value->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); index_append(&value->var, sub, &ifa->index); break; case SNMP_OP_GET: if ((ifa = FIND_OBJECT_OID(&mibifa_list, &value->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_SET: if (index_decode(&value->var, sub, iidx, ipaddr)) return (SNMP_ERR_NO_CREATION); ifa = FIND_OBJECT_OID(&mibifa_list, &value->var, sub); idx.len = 4; idx.subs[0] = ipaddr[0]; idx.subs[1] = ipaddr[1]; idx.subs[2] = ipaddr[2]; idx.subs[3] = ipaddr[3]; if ((upd = (struct update *)snmp_dep_lookup(ctx, &oid_ipAddrTable, &idx, sizeof(*upd), update_func)) == NULL) return (SNMP_ERR_RES_UNAVAIL); upd->addr.s_addr = htonl((ipaddr[0] << 24) | (ipaddr[1] << 16) | (ipaddr[2] << 8) | (ipaddr[3] << 0)); switch (which) { case LEAF_ipAdEntIfIndex: if (upd->set & UPD_IFINDEX) return (SNMP_ERR_INCONS_VALUE); if (value->v.integer < 0 || value->v.integer > 0x07fffffff) return (SNMP_ERR_WRONG_VALUE); if (ifa != NULL) { if (ifa->ifindex != (u_int)value->v.integer && value->v.integer != 0) return (SNMP_ERR_INCONS_VALUE); } upd->set |= UPD_IFINDEX; upd->ifindex = (u_int)value->v.integer; break; case LEAF_ipAdEntNetMask: if (upd->set & UPD_MASK) return (SNMP_ERR_INCONS_VALUE); upd->mask.s_addr = htonl((value->v.ipaddress[0] << 24) | (value->v.ipaddress[1] << 16) | (value->v.ipaddress[2] << 8) | (value->v.ipaddress[3] << 0)); upd->set |= UPD_MASK; break; case LEAF_ipAdEntBcastAddr: if (upd->set & UPD_BCAST) return (SNMP_ERR_INCONS_VALUE); if (value->v.integer != 0 && value->v.integer != 1) return (SNMP_ERR_WRONG_VALUE); upd->bcast = value->v.integer; upd->set |= UPD_BCAST; break; } return (SNMP_ERR_NOERROR); case SNMP_OP_ROLLBACK: case SNMP_OP_COMMIT: return (SNMP_ERR_NOERROR); } switch (which) { case LEAF_ipAdEntAddr: value->v.ipaddress[0] = ifa->index.subs[0]; value->v.ipaddress[1] = ifa->index.subs[1]; value->v.ipaddress[2] = ifa->index.subs[2]; value->v.ipaddress[3] = ifa->index.subs[3]; break; case LEAF_ipAdEntIfIndex: if (ifa->flags & MIBIFA_DESTROYED) value->v.integer = 0; else value->v.integer = ifa->ifindex; break; case LEAF_ipAdEntNetMask: value->v.ipaddress[0] = (ntohl(ifa->inmask.s_addr) >> 24) & 0xff; value->v.ipaddress[1] = (ntohl(ifa->inmask.s_addr) >> 16) & 0xff; value->v.ipaddress[2] = (ntohl(ifa->inmask.s_addr) >> 8) & 0xff; value->v.ipaddress[3] = (ntohl(ifa->inmask.s_addr) >> 0) & 0xff; break; case LEAF_ipAdEntBcastAddr: value->v.integer = ntohl(ifa->inbcast.s_addr) & 1; break; case LEAF_ipAdEntReasmMaxSize: value->v.integer = 65535; break; } return (SNMP_ERR_NOERROR); } Index: head/contrib/bsnmp/snmp_mibII/mibII_nettomedia.c =================================================================== --- head/contrib/bsnmp/snmp_mibII/mibII_nettomedia.c (revision 310647) +++ head/contrib/bsnmp/snmp_mibII/mibII_nettomedia.c (revision 310648) @@ -1,150 +1,150 @@ /* * 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_nettomedia.c,v 1.8 2004/08/06 08:47:03 brandt Exp $ * * Read-only implementation of the Arp table (ipNetToMediaTable) * * The problem with the table is, that we don't receive routing message * when a) an arp table entry is resolved and b) when an arp table entry is * deleted automatically. Therefor we need to poll the table from time to * time. */ #include "mibII.h" #include "mibII_oid.h" #define ARPREFRESH 30 struct mibarp * mib_find_arp(const struct mibif *ifp, struct in_addr in) { struct mibarp *at; uint32_t a = ntohl(in.s_addr); if (get_ticks() >= mibarpticks + ARPREFRESH) mib_arp_update(); TAILQ_FOREACH(at, &mibarp_list, link) if (at->index.subs[0] == ifp->index && (at->index.subs[1] == ((a >> 24) & 0xff)) && (at->index.subs[2] == ((a >> 16) & 0xff)) && (at->index.subs[3] == ((a >> 8) & 0xff)) && (at->index.subs[4] == ((a >> 0) & 0xff))) return (at); return (NULL); } struct mibarp * mib_arp_create(const struct mibif *ifp, struct in_addr in, const u_char *phys, size_t physlen) { struct mibarp *at; uint32_t a = ntohl(in.s_addr); if ((at = malloc(sizeof(*at))) == NULL) return (NULL); at->flags = 0; at->index.len = 5; at->index.subs[0] = ifp->index; at->index.subs[1] = (a >> 24) & 0xff; at->index.subs[2] = (a >> 16) & 0xff; at->index.subs[3] = (a >> 8) & 0xff; at->index.subs[4] = (a >> 0) & 0xff; if ((at->physlen = physlen) > sizeof(at->phys)) at->physlen = sizeof(at->phys); memcpy(at->phys, phys, at->physlen); INSERT_OBJECT_OID(at, &mibarp_list); return (at); } void mib_arp_delete(struct mibarp *at) { TAILQ_REMOVE(&mibarp_list, at, link); free(at); } int op_nettomedia(struct snmp_context *ctx __unused, struct snmp_value *value, u_int sub, u_int iidx __unused, enum snmp_op op) { struct mibarp *at; at = NULL; /* gcc */ if (get_ticks() >= mibarpticks + ARPREFRESH) mib_arp_update(); switch (op) { case SNMP_OP_GETNEXT: if ((at = NEXT_OBJECT_OID(&mibarp_list, &value->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); index_append(&value->var, sub, &at->index); break; case SNMP_OP_GET: if ((at = FIND_OBJECT_OID(&mibarp_list, &value->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_SET: if ((at = FIND_OBJECT_OID(&mibarp_list, &value->var, sub)) == NULL) return (SNMP_ERR_NO_CREATION); return (SNMP_ERR_NOT_WRITEABLE); case SNMP_OP_ROLLBACK: case SNMP_OP_COMMIT: abort(); } switch (value->var.subs[sub - 1]) { case LEAF_ipNetToMediaIfIndex: value->v.integer = at->index.subs[0]; break; case LEAF_ipNetToMediaPhysAddress: return (string_get(value, at->phys, at->physlen)); case LEAF_ipNetToMediaNetAddress: value->v.ipaddress[0] = at->index.subs[1]; value->v.ipaddress[1] = at->index.subs[2]; value->v.ipaddress[2] = at->index.subs[3]; value->v.ipaddress[3] = at->index.subs[4]; break; case LEAF_ipNetToMediaType: value->v.integer = (at->flags & MIBARP_PERM) ? 4 : 3; break; } return (SNMP_ERR_NOERROR); } Index: head/contrib/bsnmp/snmp_mibII/mibII_rcvaddr.c =================================================================== --- head/contrib/bsnmp/snmp_mibII/mibII_rcvaddr.c (revision 310647) +++ head/contrib/bsnmp/snmp_mibII/mibII_rcvaddr.c (revision 310648) @@ -1,134 +1,134 @@ /* * 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_rcvaddr.c,v 1.9 2004/08/06 08:47:03 brandt Exp $ * * Interface receive address table. */ #include "mibII.h" #include "mibII_oid.h" /* * find receive address */ struct mibrcvaddr * mib_find_rcvaddr(u_int ifindex, const u_char *addr, size_t addrlen) { struct mibrcvaddr *rcv; TAILQ_FOREACH(rcv, &mibrcvaddr_list, link) if (rcv->ifindex == ifindex && rcv->addrlen == addrlen && memcmp(rcv->addr, addr, addrlen) == 0) return (rcv); return (NULL); } /* * Create receive address */ struct mibrcvaddr * mib_rcvaddr_create(struct mibif *ifp, const u_char *addr, size_t addrlen) { struct mibrcvaddr *rcv; u_int i; if (addrlen + OIDLEN_ifRcvAddressEntry + 1 > ASN_MAXOIDLEN) return (NULL); if ((rcv = malloc(sizeof(*rcv))) == NULL) return (NULL); rcv->ifindex = ifp->index; rcv->addrlen = addrlen; memcpy(rcv->addr, addr, addrlen); rcv->flags = 0; rcv->index.len = addrlen + 2; rcv->index.subs[0] = ifp->index; rcv->index.subs[1] = addrlen; for (i = 0; i < addrlen; i++) rcv->index.subs[i + 2] = addr[i]; INSERT_OBJECT_OID(rcv, &mibrcvaddr_list); return (rcv); } /* * Delete a receive address */ void mib_rcvaddr_delete(struct mibrcvaddr *rcv) { TAILQ_REMOVE(&mibrcvaddr_list, rcv, link); free(rcv); } int op_rcvaddr(struct snmp_context *ctx __unused, struct snmp_value *value, u_int sub, u_int iidx __unused, enum snmp_op op) { struct mibrcvaddr *rcv; rcv = NULL; /* make compiler happy */ switch (op) { case SNMP_OP_GETNEXT: if ((rcv = NEXT_OBJECT_OID(&mibrcvaddr_list, &value->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); index_append(&value->var, sub, &rcv->index); break; case SNMP_OP_GET: if ((rcv = FIND_OBJECT_OID(&mibrcvaddr_list, &value->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_SET: if ((rcv = FIND_OBJECT_OID(&mibrcvaddr_list, &value->var, sub)) == NULL) return (SNMP_ERR_NO_CREATION); return (SNMP_ERR_NOT_WRITEABLE); case SNMP_OP_ROLLBACK: case SNMP_OP_COMMIT: abort(); } switch (value->var.subs[sub - 1]) { case LEAF_ifRcvAddressStatus: value->v.integer = 1; break; case LEAF_ifRcvAddressType: value->v.integer = (rcv->flags & MIBRCVADDR_VOLATILE) ? 2 : 3; break; } return (SNMP_ERR_NOERROR); } Index: head/contrib/bsnmp/snmp_mibII/mibII_route.c =================================================================== --- head/contrib/bsnmp/snmp_mibII/mibII_route.c (revision 310647) +++ head/contrib/bsnmp/snmp_mibII/mibII_route.c (revision 310648) @@ -1,514 +1,514 @@ /* * 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.9 2005/10/06 07:15:00 brandt_h Exp $ * * Routing table */ #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_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: head/contrib/bsnmp/snmp_mibII/mibII_tcp.c =================================================================== --- head/contrib/bsnmp/snmp_mibII/mibII_tcp.c (revision 310647) +++ head/contrib/bsnmp/snmp_mibII/mibII_tcp.c (revision 310648) @@ -1,377 +1,377 @@ /* * 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_tcp.c,v 1.7 2005/05/23 09:03:42 brandt_h Exp $ * * tcp */ #include "mibII.h" #include "mibII_oid.h" #include #include #include #include #include #include struct tcp_index { struct asn_oid index; struct xtcpcb *tp; }; static uint64_t tcp_tick; static uint64_t tcp_stats_tick; static struct tcpstat tcpstat; static uint64_t tcps_states[TCP_NSTATES]; static struct xinpgen *xinpgen; static size_t xinpgen_len; static u_int tcp_total; static u_int oidnum; static struct tcp_index *tcpoids; static int tcp_compare(const void *p1, const void *p2) { const struct tcp_index *t1 = p1; const struct tcp_index *t2 = p2; return (asn_compare_oid(&t1->index, &t2->index)); } static int fetch_tcp_stats(void) { size_t len; len = sizeof(tcpstat); if (sysctlbyname("net.inet.tcp.stats", &tcpstat, &len, NULL, 0) == -1) { syslog(LOG_ERR, "net.inet.tcp.stats: %m"); return (-1); } if (len != sizeof(tcpstat)) { syslog(LOG_ERR, "net.inet.tcp.stats: wrong size"); return (-1); } len = sizeof(tcps_states); if (sysctlbyname("net.inet.tcp.states", &tcps_states, &len, NULL, 0) == -1) { syslog(LOG_ERR, "net.inet.tcp.states: %m"); return (-1); } if (len != sizeof(tcps_states)) { syslog(LOG_ERR, "net.inet.tcp.states: wrong size"); return (-1); } tcp_stats_tick = get_ticks(); return (0); } static int fetch_tcp(void) { size_t len; struct xinpgen *ptr; struct xtcpcb *tp; struct tcp_index *oid; in_addr_t inaddr; len = 0; if (sysctlbyname("net.inet.tcp.pcblist", NULL, &len, NULL, 0) == -1) { syslog(LOG_ERR, "net.inet.tcp.pcblist: %m"); return (-1); } if (len > xinpgen_len) { if ((ptr = realloc(xinpgen, len)) == NULL) { syslog(LOG_ERR, "%zu: %m", len); return (-1); } xinpgen = ptr; xinpgen_len = len; } if (sysctlbyname("net.inet.tcp.pcblist", xinpgen, &len, NULL, 0) == -1) { syslog(LOG_ERR, "net.inet.tcp.pcblist: %m"); return (-1); } tcp_tick = get_ticks(); tcp_total = 0; for (ptr = (struct xinpgen *)(void *)((char *)xinpgen + xinpgen->xig_len); ptr->xig_len > sizeof(struct xinpgen); ptr = (struct xinpgen *)(void *)((char *)ptr + ptr->xig_len)) { tp = (struct xtcpcb *)ptr; if (tp->xt_inp.inp_gencnt > xinpgen->xig_gen || (tp->xt_inp.inp_vflag & (INP_IPV4|INP_IPV6)) == 0) continue; if (tp->xt_inp.inp_vflag & INP_IPV4) tcp_total++; } if (oidnum < tcp_total) { oid = realloc(tcpoids, tcp_total * sizeof(tcpoids[0])); if (oid == NULL) { free(tcpoids); oidnum = 0; return (0); } tcpoids = oid; oidnum = tcp_total; } oid = tcpoids; for (ptr = (struct xinpgen *)(void *)((char *)xinpgen + xinpgen->xig_len); ptr->xig_len > sizeof(struct xinpgen); ptr = (struct xinpgen *)(void *)((char *)ptr + ptr->xig_len)) { tp = (struct xtcpcb *)ptr; if (tp->xt_inp.inp_gencnt > xinpgen->xig_gen || (tp->xt_inp.inp_vflag & INP_IPV4) == 0) continue; oid->tp = tp; oid->index.len = 10; inaddr = ntohl(tp->xt_inp.inp_laddr.s_addr); oid->index.subs[0] = (inaddr >> 24) & 0xff; oid->index.subs[1] = (inaddr >> 16) & 0xff; oid->index.subs[2] = (inaddr >> 8) & 0xff; oid->index.subs[3] = (inaddr >> 0) & 0xff; oid->index.subs[4] = ntohs(tp->xt_inp.inp_lport); inaddr = ntohl(tp->xt_inp.inp_faddr.s_addr); oid->index.subs[5] = (inaddr >> 24) & 0xff; oid->index.subs[6] = (inaddr >> 16) & 0xff; oid->index.subs[7] = (inaddr >> 8) & 0xff; oid->index.subs[8] = (inaddr >> 0) & 0xff; oid->index.subs[9] = ntohs(tp->xt_inp.inp_fport); oid++; } qsort(tcpoids, tcp_total, sizeof(tcpoids[0]), tcp_compare); return (0); } /* * Scalars */ int op_tcp(struct snmp_context *ctx __unused, struct snmp_value *value, u_int sub, u_int iidx __unused, enum snmp_op op) { switch (op) { case SNMP_OP_GETNEXT: abort(); case SNMP_OP_GET: break; case SNMP_OP_SET: return (SNMP_ERR_NOT_WRITEABLE); case SNMP_OP_ROLLBACK: case SNMP_OP_COMMIT: abort(); } if (tcp_stats_tick < this_tick) if (fetch_tcp_stats() == -1) return (SNMP_ERR_GENERR); switch (value->var.subs[sub - 1]) { case LEAF_tcpRtoAlgorithm: value->v.integer = 4; /* Van Jacobson */ break; #define hz clockinfo.hz case LEAF_tcpRtoMin: value->v.integer = 1000 * TCPTV_MIN / hz; break; case LEAF_tcpRtoMax: value->v.integer = 1000 * TCPTV_REXMTMAX / hz; break; #undef hz case LEAF_tcpMaxConn: value->v.integer = -1; break; case LEAF_tcpActiveOpens: value->v.uint32 = tcpstat.tcps_connattempt; break; case LEAF_tcpPassiveOpens: value->v.uint32 = tcpstat.tcps_accepts; break; case LEAF_tcpAttemptFails: value->v.uint32 = tcpstat.tcps_conndrops; break; case LEAF_tcpEstabResets: value->v.uint32 = tcpstat.tcps_drops; break; case LEAF_tcpCurrEstab: value->v.uint32 = tcps_states[TCPS_ESTABLISHED] + tcps_states[TCPS_CLOSE_WAIT]; break; case LEAF_tcpInSegs: value->v.uint32 = tcpstat.tcps_rcvtotal; break; case LEAF_tcpOutSegs: value->v.uint32 = tcpstat.tcps_sndtotal - tcpstat.tcps_sndrexmitpack; break; case LEAF_tcpRetransSegs: value->v.uint32 = tcpstat.tcps_sndrexmitpack; break; case LEAF_tcpInErrs: value->v.uint32 = tcpstat.tcps_rcvbadsum + tcpstat.tcps_rcvbadoff + tcpstat.tcps_rcvshort; break; } return (SNMP_ERR_NOERROR); } int op_tcpconn(struct snmp_context *ctx __unused, struct snmp_value *value, u_int sub, u_int iidx __unused, enum snmp_op op) { u_int i; if (tcp_tick < this_tick) if (fetch_tcp() == -1) return (SNMP_ERR_GENERR); switch (op) { case SNMP_OP_GETNEXT: for (i = 0; i < tcp_total; i++) if (index_compare(&value->var, sub, &tcpoids[i].index) < 0) break; if (i == tcp_total) return (SNMP_ERR_NOSUCHNAME); index_append(&value->var, sub, &tcpoids[i].index); break; case SNMP_OP_GET: for (i = 0; i < tcp_total; i++) if (index_compare(&value->var, sub, &tcpoids[i].index) == 0) break; if (i == tcp_total) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_SET: return (SNMP_ERR_NOT_WRITEABLE); case SNMP_OP_ROLLBACK: case SNMP_OP_COMMIT: default: abort(); } switch (value->var.subs[sub - 1]) { case LEAF_tcpConnState: switch (tcpoids[i].tp->xt_tp.t_state) { case TCPS_CLOSED: value->v.integer = 1; break; case TCPS_LISTEN: value->v.integer = 2; break; case TCPS_SYN_SENT: value->v.integer = 3; break; case TCPS_SYN_RECEIVED: value->v.integer = 4; break; case TCPS_ESTABLISHED: value->v.integer = 5; break; case TCPS_CLOSE_WAIT: value->v.integer = 8; break; case TCPS_FIN_WAIT_1: value->v.integer = 6; break; case TCPS_CLOSING: value->v.integer = 10; break; case TCPS_LAST_ACK: value->v.integer = 9; break; case TCPS_FIN_WAIT_2: value->v.integer = 7; break; case TCPS_TIME_WAIT: value->v.integer = 11; break; default: value->v.integer = 0; break; } break; case LEAF_tcpConnLocalAddress: value->v.ipaddress[0] = tcpoids[i].index.subs[0]; value->v.ipaddress[1] = tcpoids[i].index.subs[1]; value->v.ipaddress[2] = tcpoids[i].index.subs[2]; value->v.ipaddress[3] = tcpoids[i].index.subs[3]; break; case LEAF_tcpConnLocalPort: value->v.integer = tcpoids[i].index.subs[4]; break; case LEAF_tcpConnRemAddress: value->v.ipaddress[0] = tcpoids[i].index.subs[5]; value->v.ipaddress[1] = tcpoids[i].index.subs[6]; value->v.ipaddress[2] = tcpoids[i].index.subs[7]; value->v.ipaddress[3] = tcpoids[i].index.subs[8]; break; case LEAF_tcpConnRemPort: value->v.integer = tcpoids[i].index.subs[9]; break; } return (SNMP_ERR_NOERROR); } Index: head/contrib/bsnmp/snmp_mibII/mibII_tree.def =================================================================== --- head/contrib/bsnmp/snmp_mibII/mibII_tree.def (revision 310647) +++ head/contrib/bsnmp/snmp_mibII/mibII_tree.def (revision 310648) @@ -1,262 +1,262 @@ # # 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.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) (4 begemotIfDataPoll 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: head/contrib/bsnmp/snmp_mibII/mibII_udp.c =================================================================== --- head/contrib/bsnmp/snmp_mibII/mibII_udp.c (revision 310647) +++ head/contrib/bsnmp/snmp_mibII/mibII_udp.c (revision 310648) @@ -1,253 +1,253 @@ /* * 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_udp.c,v 1.7 2005/05/23 09:03:42 brandt_h Exp $ * * udp */ #include "mibII.h" #include "mibII_oid.h" #include #include #include #include #include struct udp_index { struct asn_oid index; struct xinpcb *inp; }; static uint64_t udp_tick; static struct udpstat udpstat; static struct xinpgen *xinpgen; static size_t xinpgen_len; static u_int udp_total; static u_int oidnum; static struct udp_index *udpoids; static int udp_compare(const void *p1, const void *p2) { const struct udp_index *t1 = p1; const struct udp_index *t2 = p2; return (asn_compare_oid(&t1->index, &t2->index)); } static int fetch_udp(void) { size_t len; struct xinpgen *ptr; struct xinpcb *inp; struct udp_index *oid; in_addr_t inaddr; len = sizeof(udpstat); if (sysctlbyname("net.inet.udp.stats", &udpstat, &len, NULL, 0) == -1) { syslog(LOG_ERR, "net.inet.udp.stats: %m"); return (-1); } if (len != sizeof(udpstat)) { syslog(LOG_ERR, "net.inet.udp.stats: wrong size"); return (-1); } udp_tick = get_ticks(); len = 0; if (sysctlbyname("net.inet.udp.pcblist", NULL, &len, NULL, 0) == -1) { syslog(LOG_ERR, "net.inet.udp.pcblist: %m"); return (-1); } if (len > xinpgen_len) { if ((ptr = realloc(xinpgen, len)) == NULL) { syslog(LOG_ERR, "%zu: %m", len); return (-1); } xinpgen = ptr; xinpgen_len = len; } if (sysctlbyname("net.inet.udp.pcblist", xinpgen, &len, NULL, 0) == -1) { syslog(LOG_ERR, "net.inet.udp.pcblist: %m"); return (-1); } udp_total = 0; for (ptr = (struct xinpgen *)(void *)((char *)xinpgen + xinpgen->xig_len); ptr->xig_len > sizeof(struct xinpgen); ptr = (struct xinpgen *)(void *)((char *)ptr + ptr->xig_len)) { inp = (struct xinpcb *)ptr; if (inp->xi_inp.inp_gencnt > xinpgen->xig_gen || (inp->xi_inp.inp_vflag & INP_IPV4) == 0) continue; udp_total++; } if (oidnum < udp_total) { oid = realloc(udpoids, udp_total * sizeof(udpoids[0])); if (oid == NULL) { free(udpoids); oidnum = 0; return (0); } udpoids = oid; oidnum = udp_total; } oid = udpoids; for (ptr = (struct xinpgen *)(void *)((char *)xinpgen + xinpgen->xig_len); ptr->xig_len > sizeof(struct xinpgen); ptr = (struct xinpgen *)(void *)((char *)ptr + ptr->xig_len)) { inp = (struct xinpcb *)ptr; if (inp->xi_inp.inp_gencnt > xinpgen->xig_gen || (inp->xi_inp.inp_vflag & INP_IPV4) == 0) continue; oid->inp = inp; oid->index.len = 5; inaddr = ntohl(inp->xi_inp.inp_laddr.s_addr); oid->index.subs[0] = (inaddr >> 24) & 0xff; oid->index.subs[1] = (inaddr >> 16) & 0xff; oid->index.subs[2] = (inaddr >> 8) & 0xff; oid->index.subs[3] = (inaddr >> 0) & 0xff; oid->index.subs[4] = ntohs(inp->xi_inp.inp_lport); oid++; } qsort(udpoids, udp_total, sizeof(udpoids[0]), udp_compare); return (0); } int op_udp(struct snmp_context *ctx __unused, struct snmp_value *value, u_int sub, u_int iidx __unused, enum snmp_op op) { switch (op) { case SNMP_OP_GETNEXT: abort(); case SNMP_OP_GET: break; case SNMP_OP_SET: return (SNMP_ERR_NOT_WRITEABLE); case SNMP_OP_ROLLBACK: case SNMP_OP_COMMIT: abort(); } if (udp_tick < this_tick) if (fetch_udp() == -1) return (SNMP_ERR_GENERR); switch (value->var.subs[sub - 1]) { case LEAF_udpInDatagrams: value->v.uint32 = udpstat.udps_ipackets; break; case LEAF_udpNoPorts: value->v.uint32 = udpstat.udps_noport + udpstat.udps_noportbcast + udpstat.udps_noportmcast; break; case LEAF_udpInErrors: value->v.uint32 = udpstat.udps_hdrops + udpstat.udps_badsum + udpstat.udps_badlen + udpstat.udps_fullsock; break; case LEAF_udpOutDatagrams: value->v.uint32 = udpstat.udps_opackets; break; } return (SNMP_ERR_NOERROR); } int op_udptable(struct snmp_context *ctx __unused, struct snmp_value *value, u_int sub, u_int iidx __unused, enum snmp_op op) { u_int i; if (udp_tick < this_tick) if (fetch_udp() == -1) return (SNMP_ERR_GENERR); switch (op) { case SNMP_OP_GETNEXT: for (i = 0; i < udp_total; i++) if (index_compare(&value->var, sub, &udpoids[i].index) < 0) break; if (i == udp_total) return (SNMP_ERR_NOSUCHNAME); index_append(&value->var, sub, &udpoids[i].index); break; case SNMP_OP_GET: for (i = 0; i < udp_total; i++) if (index_compare(&value->var, sub, &udpoids[i].index) == 0) break; if (i == udp_total) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_SET: return (SNMP_ERR_NOT_WRITEABLE); case SNMP_OP_ROLLBACK: case SNMP_OP_COMMIT: default: abort(); } switch (value->var.subs[sub - 1]) { case LEAF_udpLocalAddress: value->v.ipaddress[0] = udpoids[i].index.subs[0]; value->v.ipaddress[1] = udpoids[i].index.subs[1]; value->v.ipaddress[2] = udpoids[i].index.subs[2]; value->v.ipaddress[3] = udpoids[i].index.subs[3]; break; case LEAF_udpLocalPort: value->v.integer = udpoids[i].index.subs[4]; break; } return (SNMP_ERR_NOERROR); } Index: head/contrib/bsnmp/snmp_mibII/snmp_mibII.3 =================================================================== --- head/contrib/bsnmp/snmp_mibII/snmp_mibII.3 (revision 310647) +++ head/contrib/bsnmp/snmp_mibII/snmp_mibII.3 (revision 310648) @@ -1,366 +1,366 @@ .\" .\" 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/snmp_mibII/snmp_mibII.3,v 1.10 2005/10/04 08:46:52 brandt_h Exp $ .\" .Dd October 4, 2005 .Dt SNMP_MIBII 3 .Os .Sh NAME .Nm mibII , .Nm mibif_notify_f , .Nm mib_netsock , .Nm mib_if_set_dyn , .Nm mib_refresh_iflist , .Nm mib_find_if , .Nm mib_find_if_sys , .Nm mib_find_if_name , .Nm mib_first_if , .Nm mib_next_if , .Nm mib_register_newif , .Nm mib_unregister_newif , .Nm mib_fetch_ifmib , .Nm mib_if_admin , .Nm mib_find_ifa , .Nm mib_first_ififa , .Nm mib_next_ififa , .Nm mib_ifstack_create , .Nm mib_ifstack_delete , .Nm mib_find_rcvaddr , .Nm mib_rcvaddr_create , .Nm mib_rcvaddr_delete , .Nm mibif_notify , .Nm mibif_unnotify .Nd "mib-2 module for bsnmpd." .Sh LIBRARY .Pq begemotSnmpdModulePath."mibII" = "@MODPATH@snmp_mibII.so" .Sh SYNOPSIS .In bsnmp/snmpmod.h .In bsnmp/snmp_mibII.h .Ft typedef void .Fn (*mibif_notify_f) "struct mibif *ifp" "enum mibif_notify event" "void *uarg" .Vt extern int mib_netsock ; .Ft void .Fn mib_if_set_dyn "const char *ifname" .Ft void .Fn mib_refresh_iflist "void" .Ft struct mibif * .Fn mib_find_if "u_int ifindex" .Ft struct mibif * .Fn mib_find_if_sys "u_int sysindex" .Ft struct mibif * .Fn mib_find_if_name "const char *ifname" .Ft struct mibif * .Fn mib_first_if "void" .Ft struct mibif * .Fn mib_next_if "const struct mibif *ifp" .Ft int .Fn mib_register_newif "int (*func)(struct mibif *)" "const struct lmodule *mod" .Ft void .Fn mib_unregister_newif "const struct lmodule *mod" .Ft int .Fn mib_fetch_ifmib "struct mibif *ifp" .Ft int .Fn mib_if_admin "struct mibif *ifp" "int up" .Ft struct mibifa * .Fn mib_find_ifa "struct in_addr ipa" .Ft struct mibifa * .Fn mib_first_ififa "const struct mibif *ifp" .Ft struct mibifa * .Fn mib_next_ififa "struct mibifa *ifa" .Ft int .Fn mib_ifstack_create "const struct mibif *lower" "const struct mibif *upper" .Ft void .Fn mib_ifstack_delete "const struct mibif *lower" "const struct mibif *upper" .Ft struct mibrcvaddr * .Fn mib_find_rcvaddr "u_int ifindex" "const u_char *addr" "size_t addrlen" .Ft struct mibrcvaddr * .Fn mib_rcvaddr_create "struct mibif *ifp" "const u_char *addr" "size_t addrlen" .Ft void .Fn mib_rcvaddr_delete "struct mibrcvaddr *addr" .Ft void * .Fn mibif_notify "struct mibif *ifp" "const struct lmodule *mod" "mibif_notify_f func" "void *uarg" .Ft void .Fn mibif_unnotify "void *reg" .Sh DESCRIPTION The .Nm snmp_mibII module implements parts of the internet standard MIB-2. Most of the relevant MIBs are implemented. Some of the tables are restricted to be read-only instead of read-write. The exact current implementation can be found in .Pa @DEFPATH@mibII_tree.def . The module also exports a number of functions and global variables for use by other modules, that need to handle network interfaces. This man page describes these functions. .Ss DIRECT NETWORK ACCESS The .Nm module opens a socket that is used to execute all network related .Xr ioctl 2 functions. This socket is globally available under the name .Va mib_netsock . .Ss NETWORK INTERFACES The .Nm module handles a list of all currently existing network interfaces. It allows other modules to handle their own interface lists with special information by providing a mechanism to register to events that change the interface list (see below). The basic data structure is the interface structure: .Bd -literal -offset indent struct mibif { TAILQ_ENTRY(mibif) link; u_int flags; u_int index; /* logical ifindex */ u_int sysindex; char name[IFNAMSIZ]; char descr[256]; struct ifmibdata mib; uint64_t mibtick; void *specmib; size_t specmiblen; u_char *physaddr; u_int physaddrlen; int has_connector; int trap_enable; uint64_t counter_disc; mibif_notify_f xnotify; void *xnotify_data; const struct lmodule *xnotify_mod; struct asn_oid spec_oid; }; .Ed .Pp The .Nm module tries to implement the semantic if .Va ifIndex as described in RFC-2863. This RFC states, that an interface indexes may not be reused. That means, for example, if .Pa tun is a synthetic interface type and the system creates the interface .Pa tun0 , destroys this interfaces and again creates a .Pa tun 0 , then these interfaces must have different interface indexes, because in fact they are different interfaces. If, on the other hand, there is a hardware interface .Pa xl0 and this interface disappears, because its driver is unloaded and appears again, because the driver is loaded again, the interface index must stay the same. .Nm implements this by differentiating between real and synthetic (dynamic) interfaces. An interface type can be declared dynamic by calling the function .Fn mib_if_set_dyn with the name if the interface type (for example .Qq tun ). For real interfaces, the module keeps the mapping between the interface name and its .Va ifIndex in a special list, if the interface is unloaded. For dynamic interfaces a new .Va ifIndex is generated each time the interface comes into existence. This means, that the interface index as seen by SNMP is not the same index as used by the system. The SNMP .Va ifIndex is held in field .Va index , the system's interface index is .Va sysindex . .Pp A call to .Nm mib_refresh_iflist causes the entire interface list to be re-created. .Pp The interface list can be traversed with the functions .Fn mib_first_if and .Fn mib_next_if . Be sure not to change the interface list while traversing the list with these two calls. .Pp There are three functions to find an interface by name or index. .Fn mib_find_if finds an interface by searching for an SNMP .Va ifIndex , .Fn mib_find_if_sys finds an interface by searching for a system interface index and .Fn mib_find_if_name finds an interface by looking for an interface name. Each of the function returns .Li NULL if the interface cannot be found. .Pp The function .Fn mib_fetch_ifmib causes the interface MIB to be refreshed from the kernel. .Pp The function .Fn mib_if_admin can be used to change the interface administrative state to up (argument is 1) or down (argument is 0). .Ss INTERFACE EVENTS A module can register itself to receive a notification when a new entry is created in the interface list. This is done by calling .Fn mib_register_newif . A module can register only one function, a second call to .Fn mib_register_newif causes the registration to be overwritten. The registration can be removed with a call to .Fn mib_unregister_newif . It is unregistered automatically, when the registering module is unloaded. .Pp A module can also register to events on a specific interface. This is done by calling .Fn mibif_notify . This causes the given callback .Fa func to be called with the interface pointer, a notification code and the user argument .Fa uarg when any of the following events occur: .Bl -tag -width "XXXXX" .It Li MIBIF_NOTIFY_DESTROY The interface is destroyed. .El .Pp This mechanism can be used to implement interface type specific MIB parts in other modules. The registration can be removed with .Fn mib_unnotify which the return value from .Fa mib_notify . Any notification registration is removed automatically when the interface is destroyed or the registering module is unloaded. .Em Note that only one module can register to any given interface . .Ss INTERFACE ADDRESSES The .Nm module handles a table of interface IP-addresses. These addresses are held in a .Bd -literal -offset indent struct mibifa { TAILQ_ENTRY(mibifa) link; struct in_addr inaddr; struct in_addr inmask; struct in_addr inbcast; struct asn_oid index; u_int ifindex; u_int flags; }; .Ed .Pp The (ordered) list of IP-addresses on a given interface can be traversed by calling .Fn mib_first_ififa and .Fn mib_next_ififa . The list should not be considered read-only. .Ss INTERFACE RECEIVE ADDRESSES The internet MIB-2 contains a table of interface receive addresses. These addresses are handled in: .Bd -literal -offset indent struct mibrcvaddr { TAILQ_ENTRY(mibrcvaddr) link; struct asn_oid index; u_int ifindex; u_char addr[ASN_MAXOIDLEN]; size_t addrlen; u_int flags; }; enum { MIBRCVADDR_VOLATILE = 0x00000001, MIBRCVADDR_BCAST = 0x00000002, MIBRCVADDR_HW = 0x00000004, }; .Ed .Pp Note, that the assignment of .Li MIBRCVADDR_BCAST is based on a list of known interface types. The flags should be handled by modules implementing interface type specific MIBs. .Pp A receive address can be created with .Fn mib_rcvaddr_create and deleted with .Fn mib_rcvaddr_delete . This needs to be done only for addresses that are not automatically handled by the system. .Pp A receive address can be found with .Fn mib_find_rcvaddr . .Ss INTERFACE STACK TABLE The .Nm module maintains also the interface stack table. Because for complex stacks, there is no system supported generic way of getting this information, interface type specific modules need to help setting up stack entries. The .Nm module handles only the top and bottom entries. .Pp A table entry is created with .Fn mib_ifstack_create and deleted with .Fn mib_ifstack_delete . Both functions need the pointers to the interfaces. Entries are automatically deleted if any of the interfaces of the entry is destroyed. The functions handle both the stack table and the reverse stack table. .Sh FILES .Bl -tag -width ".It Pa @DEFPATH@mibII_tree.def" -compact .It Pa @DEFPATH@mibII_tree.def The description of the MIB tree implemented by .Nm . .It Pa /usr/local/share/snmp/mibs .It Pa @MIBSPATH@ The various internet MIBs. .El .Sh SEE ALSO .Xr gensnmptree 1 , .Xr snmpmod 3 .Sh STANDARDS This implementation conforms to the applicable IETF RFCs. .Sh AUTHORS .An Hartmut Brandt Aq harti@FreeBSD.org Index: head/contrib/bsnmp/snmp_mibII/snmp_mibII.h =================================================================== --- head/contrib/bsnmp/snmp_mibII/snmp_mibII.h (revision 310647) +++ head/contrib/bsnmp/snmp_mibII/snmp_mibII.h (revision 310648) @@ -1,169 +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.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: head/contrib/bsnmp/snmp_ntp/NTP-MIB.txt =================================================================== --- head/contrib/bsnmp/snmp_ntp/NTP-MIB.txt (revision 310647) +++ head/contrib/bsnmp/snmp_ntp/NTP-MIB.txt (revision 310648) @@ -1,627 +1,627 @@ -- -- NTP MIB, Revision 0.2, 7/25/97 -- NTP-MIB DEFINITIONS ::= BEGIN IMPORTS - Integer32, IpAddress, MODULE-IDENTITY, OBJECT-TYPE, Unsigned32, + Integer32, IpAddress, MODULE-IDENTITY, OBJECT-TYPE, Unsigned32, enterprises FROM SNMPv2-SMI TEXTUAL-CONVENTION, TruthValue FROM SNMPv2-TC; ntpMIB MODULE-IDENTITY LAST-UPDATED "199707251530Z" - ORGANIZATION + ORGANIZATION "University of Delaware" - CONTACT-INFO + CONTACT-INFO "Adarsh Sethi Department of Computer & Information Sciences University of Delaware Newark, DE 19716 Tel: +1 302 831 1945 E-mail: sethi@cis.udel.edu David Mills Department of Electrical Engineering University of Delaware Newark, DE 19716 Tel: +1 302 831 ???? E-mail: mills@ee.udel.edu" - DESCRIPTION + DESCRIPTION "This MIB module defines a MIB which provides mechanisms to monitor and control an NTP server." ::= { udel 3 } -- -- Position within the OID hierarchy of this MIB: -- udel OBJECT IDENTIFIER ::= { enterprises 1277 } -- -- The various groups defined within this MIB definition: -- ntpSystem OBJECT IDENTIFIER ::= { ntpMIB 1 } ntpPeers OBJECT IDENTIFIER ::= { ntpMIB 2 } ntpFilter OBJECT IDENTIFIER ::= { ntpMIB 3 } -- -- Textual conventions: -- NTPTimeStamp ::= TEXTUAL-CONVENTION DISPLAY-HINT "4x.4x" STATUS current - DESCRIPTION + DESCRIPTION "" SYNTAX OCTET STRING (SIZE(8)) NTPLeapIndicator ::= TEXTUAL-CONVENTION STATUS current - DESCRIPTION + DESCRIPTION "" SYNTAX INTEGER { noWarning(0), addSecond(1), - subtractSecond(2), + subtractSecond(2), alarm(3) } -- -- System Group -- ntpSysLeap OBJECT-TYPE SYNTAX NTPLeapIndicator MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION " two-bit code warning of an impending leap second to be inserted in the NTP timescale." ::= { ntpSystem 1 } ntpSysStratum OBJECT-TYPE SYNTAX Integer32 (0..255) MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION " indicating the stratum of the local clock. 0, unspecified 1, primary reference (e.g.,, calibrated atomic clock, radio clock) 2-255, secondary reference (via NTP)" ::= { ntpSystem 2 } ntpSysPrecision OBJECT-TYPE SYNTAX Integer32 MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION "signed integer indicating the precision of the various clocks, in seconds to the nearest power of two." ::= { ntpSystem 3 } ntpSysRootDelay OBJECT-TYPE SYNTAX OCTET STRING MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION "the total roundtrip delay to the primary reference source at the root of the synchronization subnet, in seconds" ::= { ntpSystem 4 } ntpSysRootDispersion OBJECT-TYPE SYNTAX OCTET STRING MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION "the maximum error relative to the primary reference source at the root of the synchronization subnet, in seconds. Only positive values greater than zero are possible" ::= { ntpSystem 5 } ntpSysRefId OBJECT-TYPE SYNTAX OCTET STRING MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION " the particular reference clock. In the case of stratum 0 (unspecified) or stratum 1 (primary reference source), this is a four-octet, left-justified,zero-padded ASCII string.In the case of stratum 2 and greater (secondary reference) this is the four-octet Internet address of the peer selected for synchronization." ::= { ntpSystem 6 } ntpSysRefTime OBJECT-TYPE SYNTAX NTPTimeStamp MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION " the local time when the local clock was last updated. If the local clock has neverbeen synchronized, the value is zero." ::= { ntpSystem 7 } ntpSysPoll OBJECT-TYPE SYNTAX Integer32 MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION " the minimum interval between transmitted messages, in seconds as a power of two. For instance, a value of six indicates a minimum interval of 64 seconds." ::= { ntpSystem 8 } ntpSysPeer OBJECT-TYPE SYNTAX Unsigned32 MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION " the current synchronization source. Usually this will be a pointer to a structure containing the peer variables. The special value NULL indicates there is no currently valid synchronization source." ::= { ntpSystem 9 } ntpSysPhase OBJECT-TYPE SYNTAX OCTET STRING MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION "" ::= { ntpSystem 10 } ntpSysFreq OBJECT-TYPE SYNTAX OCTET STRING MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION "" ::= { ntpSystem 11 } ntpSysError OBJECT-TYPE SYNTAX OCTET STRING MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION "" ::= { ntpSystem 12 } ntpSysClock OBJECT-TYPE SYNTAX NTPTimeStamp MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION "the current local time. Local time is derived from the hardware clock of the particular machine and increments at intervals depending on the design used." ::= { ntpSystem 13 } ntpSysSystem OBJECT-TYPE SYNTAX OCTET STRING MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION " the type of the local Operating System" ::= { ntpSystem 14 } ntpSysProcessor OBJECT-TYPE SYNTAX OCTET STRING MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION " the type of the local Processor" ::= { ntpSystem 15 } -- -- Peers Group -- -- -- Peer Variables Table -- ntpPeersVarTable OBJECT-TYPE SYNTAX SEQUENCE OF NtpPeersVarEntry MAX-ACCESS not-accessible STATUS current - DESCRIPTION + DESCRIPTION "" ::= { ntpPeers 1 } ntpPeersVarEntry OBJECT-TYPE SYNTAX NtpPeersVarEntry MAX-ACCESS not-accessible STATUS current - DESCRIPTION + DESCRIPTION "" INDEX { ntpPeersAssocId } ::= { ntpPeersVarTable 1 } NtpPeersVarEntry ::= SEQUENCE { ntpPeersAssocId Unsigned32, ntpPeersConfigured TruthValue, ntpPeersPeerAddress IpAddress, ntpPeersPeerPort Unsigned32, ntpPeersHostAddress IpAddress, ntpPeersHostPort Unsigned32, ntpPeersLeap NTPLeapIndicator, ntpPeersMode INTEGER, ntpPeersStratum Integer32, ntpPeersPeerPoll Integer32, ntpPeersHostPoll Integer32, ntpPeersPrecision Integer32, ntpPeersRootDelay OCTET STRING, ntpPeersRootDispersion OCTET STRING, ntpPeersRefId OCTET STRING, ntpPeersRefTime NTPTimeStamp, ntpPeersOrgTime NTPTimeStamp, ntpPeersReceiveTime NTPTimeStamp, ntpPeersTransmitTime NTPTimeStamp, ntpPeersUpdateTime NTPTimeStamp, ntpPeersReach Unsigned32, ntpPeersTimer Integer32, ntpPeersOffset OCTET STRING, ntpPeersDelay OCTET STRING, ntpPeersDispersion OCTET STRING } ntpPeersAssocId OBJECT-TYPE SYNTAX Unsigned32 MAX-ACCESS not-accessible STATUS current - DESCRIPTION + DESCRIPTION "" ::= { ntpPeersVarEntry 1 } ntpPeersConfigured OBJECT-TYPE SYNTAX TruthValue MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION " This is a bit indicating that the association was created from configuration information and should not be demobilized if the peer becomes unreachable." ::= { ntpPeersVarEntry 2 } ntpPeersPeerAddress OBJECT-TYPE SYNTAX IpAddress MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION " the Internet address of the peer" ::= { ntpPeersVarEntry 3 } ntpPeersPeerPort OBJECT-TYPE SYNTAX Unsigned32 MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION " 16-bit port number of the peer." ::= { ntpPeersVarEntry 4 } ntpPeersHostAddress OBJECT-TYPE SYNTAX IpAddress MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION " the Internet address of the host" ::= { ntpPeersVarEntry 5 } ntpPeersHostPort OBJECT-TYPE SYNTAX Unsigned32 MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION " 16-bit port number of the host" ::= { ntpPeersVarEntry 6 } ntpPeersLeap OBJECT-TYPE SYNTAX NTPLeapIndicator MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION " two-bit code warning of an impending leap second to be inserted in the NTP timescale." ::= { ntpPeersVarEntry 7 } ntpPeersMode OBJECT-TYPE SYNTAX INTEGER { unspecified(0), - symmetricActive(1), + symmetricActive(1), symmetricPassive(2), client(3), - server(4), + server(4), broadcast(5), reservedControl(6), reservedPrivate(7) } MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION " the association mode,with values coded as follows: 0, unspecified 1, symmetric active 2, symmetric passive 3, client 4, server 5, broadcast 6, reserved for NTP control messages 7, reserved for private use " ::= { ntpPeersVarEntry 8 } ntpPeersStratum OBJECT-TYPE SYNTAX Integer32 (0..255) MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION " indicating the stratum of the peer clock. 0, unspecified 1, primary reference (e.g.,, calibrated atomic clock, radio clock) 2-255, secondary reference (via NTP)" ::= { ntpPeersVarEntry 9 } ntpPeersPeerPoll OBJECT-TYPE SYNTAX Integer32 MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION "poll interval of the peer" ::= { ntpPeersVarEntry 10 } ntpPeersHostPoll OBJECT-TYPE SYNTAX Integer32 MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION "poll interval of the host" ::= { ntpPeersVarEntry 11 } ntpPeersPrecision OBJECT-TYPE SYNTAX Integer32 MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION "the same as the systemPrecision except this is for the peer" ::= { ntpPeersVarEntry 12 } ntpPeersRootDelay OBJECT-TYPE SYNTAX OCTET STRING MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION "the same as the systemRootDealy except this is for the peer" ::= { ntpPeersVarEntry 13 } ntpPeersRootDispersion OBJECT-TYPE SYNTAX OCTET STRING MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION "the same as the systemDispersion except this is for the peer" ::= { ntpPeersVarEntry 14 } ntpPeersRefId OBJECT-TYPE SYNTAX OCTET STRING MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION "the same as the systemRefid except this is for the peer" ::= { ntpPeersVarEntry 15 } ntpPeersRefTime OBJECT-TYPE SYNTAX NTPTimeStamp MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION "the same as the systemRefTime except this is for the peer" ::= { ntpPeersVarEntry 16 } ntpPeersOrgTime OBJECT-TYPE SYNTAX NTPTimeStamp MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION " the local time at the peer when its latest NTP message was sent. If the peer becomes unreachable the value is set to zero." ::= { ntpPeersVarEntry 17 } ntpPeersReceiveTime OBJECT-TYPE SYNTAX NTPTimeStamp MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION "the local time when the latest NTP message from the peer arrived. If the peer becomes unreachable the value is set to zero." ::= { ntpPeersVarEntry 18 } ntpPeersTransmitTime OBJECT-TYPE SYNTAX NTPTimeStamp MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION "the local time at which the NTP message departed the sender." ::= { ntpPeersVarEntry 19 } ntpPeersUpdateTime OBJECT-TYPE SYNTAX NTPTimeStamp MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION " " ::= { ntpPeersVarEntry 20 } ntpPeersReach OBJECT-TYPE SYNTAX Unsigned32 MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION "a shift register of NTP.WINDOW bits used to determine the reachability status of the peer, with bits entering from the least significant (rightmost) end. A peer is considered reachable if at least one bit in this register is set to one." ::= { ntpPeersVarEntry 21 } ntpPeersTimer OBJECT-TYPE SYNTAX Integer32 MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION "" ::= { ntpPeersVarEntry 22 } ntpPeersOffset OBJECT-TYPE SYNTAX OCTET STRING MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION "" ::= { ntpPeersVarEntry 23 } ntpPeersDelay OBJECT-TYPE SYNTAX OCTET STRING MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION "" ::= { ntpPeersVarEntry 24 } ntpPeersDispersion OBJECT-TYPE SYNTAX OCTET STRING MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION "" ::= { ntpPeersVarEntry 25 } -- -- Filter Group -- -- Implementation of this group is optional. It must be implemented -- when the filter and selection algorithms described in Section 4 -- of RFC 1305 are used. -- -- -- Filter Group Peer Variables Table -- ntpFilterPeersVarTable OBJECT-TYPE SYNTAX SEQUENCE OF NtpFilterPeersVarEntry MAX-ACCESS not-accessible STATUS current - DESCRIPTION + DESCRIPTION "This table is an extension of the Peer Variables Table in the Peer Group." ::= { ntpFilter 1 } ntpFilterPeersVarEntry OBJECT-TYPE SYNTAX NtpFilterPeersVarEntry MAX-ACCESS not-accessible STATUS current - DESCRIPTION + DESCRIPTION "" AUGMENTS { ntpPeersVarEntry } ::= { ntpFilterPeersVarTable 1 } NtpFilterPeersVarEntry ::= SEQUENCE { ntpFilterValidEntries Integer32 } ntpFilterValidEntries OBJECT-TYPE SYNTAX Integer32 MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION "The number of valid entries for a peer in the Filter Register Table." ::= { ntpFilterPeersVarEntry 1 } -- -- Filter Register Table -- ntpFilterRegisterTable OBJECT-TYPE SYNTAX SEQUENCE OF NtpFilterRegisterEntry MAX-ACCESS not-accessible STATUS current - DESCRIPTION + DESCRIPTION "" ::= { ntpFilter 2 } ntpFilterRegisterEntry OBJECT-TYPE SYNTAX NtpFilterRegisterEntry MAX-ACCESS not-accessible STATUS current - DESCRIPTION + DESCRIPTION "" INDEX { ntpPeersAssocId, ntpFilterIndex } ::= { ntpFilterRegisterTable 1 } NtpFilterRegisterEntry ::= SEQUENCE { ntpFilterIndex Unsigned32, ntpFilterPeersOffset OCTET STRING, ntpFilterPeersDelay OCTET STRING, ntpFilterPeersDispersion OCTET STRING } ntpFilterIndex OBJECT-TYPE SYNTAX Unsigned32 MAX-ACCESS not-accessible STATUS current - DESCRIPTION + DESCRIPTION "" ::= { ntpFilterRegisterEntry 1 } ntpFilterPeersOffset OBJECT-TYPE SYNTAX OCTET STRING MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION "the offset of the peer clock relative to the local clock in seconds" ::= { ntpFilterRegisterEntry 2 } ntpFilterPeersDelay OBJECT-TYPE SYNTAX OCTET STRING MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION "roundtrip delay of the peer clock relative to the local clock over the network path between them, in seconds. this variable can take on both positive and negative values, depending on clock precision and skew-error accumulation." ::= { ntpFilterRegisterEntry 3 } ntpFilterPeersDispersion OBJECT-TYPE SYNTAX OCTET STRING MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION "the maximum error of the peer clock relative to the local clock over the network path between them, in seconds. Only positive values greater than zero are possible." ::= { ntpFilterRegisterEntry 4 } END Index: head/contrib/bsnmp/snmp_ntp/NTP-PROXY-MIB.txt =================================================================== --- head/contrib/bsnmp/snmp_ntp/NTP-PROXY-MIB.txt (revision 310647) +++ head/contrib/bsnmp/snmp_ntp/NTP-PROXY-MIB.txt (revision 310648) @@ -1,616 +1,616 @@ -- -- NTP Proxy MIB, Revision 0.2, 7/25/97 -- NTP-PROXY-MIB DEFINITIONS ::= BEGIN IMPORTS NTPLeapIndicator, NTPTimeStamp FROM NTP-MIB - Integer32, IpAddress, MODULE-IDENTITY, OBJECT-TYPE, Unsigned32, + Integer32, IpAddress, MODULE-IDENTITY, OBJECT-TYPE, Unsigned32, enterprises FROM SNMPv2-SMI TEXTUAL-CONVENTION, TruthValue FROM SNMPv2-TC; ntpProxyMIB MODULE-IDENTITY LAST-UPDATED "199707251540Z" - ORGANIZATION + ORGANIZATION "University of Delaware" - CONTACT-INFO + CONTACT-INFO "Adarsh Sethi Department of Computer & Information Sciences University of Delaware Newark, DE 19716 Tel: +1 302 831 1945 E-mail: sethi@cis.udel.edu - + David Mills Department of Electrical Engineering University of Delaware Newark, DE 19716 Tel: +1 302 831 ???? E-mail: mills@ee.udel.edu" - DESCRIPTION + DESCRIPTION "This MIB module defines a MIB which provides mechanisms to monitor and control many NTP servers via a Proxy Agent." ::= { enterprises 1277 4 } -- -- The position within the OID hierarchy of this MIB: -- udel OBJECT IDENTIFIER ::= { enterprises 1277 } -- -- The various groups defined within this MIB definition: -- ntpProxyControl OBJECT IDENTIFIER ::= { ntpProxyMIB 1 } -- -- Textual conventions: -- NTPRowStatus ::= TEXTUAL-CONVENTION STATUS current - DESCRIPTION + DESCRIPTION "The NTPRowStatus textual convention is modeled after the RowStatus textual convention of RFC 1903, but is simpler because it only allows one create operation (the create- and-go of RowStatus) and does not allow row deletion. If the state of the status column is `notInService' and the management station tries to set it to `create', the corresponding row is created and the operation is successful. If the set to `create' is attempted when the status column is in state `active', the operation fails and inconsistentValue is returned. A management station is not permitted to delete the conceptual row; deletion is carried out by the agent in an autonomous manner." SYNTAX INTEGER { -- the following values are states: -- these values may be read, but not written active(1), notInService(2), -- the following value is an action: -- this value may be written, but is never read create(3) } --- +-- -- Control group -- -- -- ProxyControl Table -- ntpProxyControlTable OBJECT-TYPE SYNTAX SEQUENCE OF NtpProxyControlEntry MAX-ACCESS not-accessible STATUS current - DESCRIPTION + DESCRIPTION "" ::= { ntpProxyControl 1 } ntpProxyControlEntry OBJECT-TYPE SYNTAX NtpProxyControlEntry MAX-ACCESS not-accessible STATUS current - DESCRIPTION + DESCRIPTION "" INDEX { ntpProxyServerIPAddr } ::= { ntpProxyControlTable 1 } NtpProxyControlEntry ::= SEQUENCE { ntpProxyServerIPAddr IpAddress, ntpProxyControlStatus NTPRowStatus } ntpProxyServerIPAddr OBJECT-TYPE SYNTAX IpAddress MAX-ACCESS not-accessible STATUS current - DESCRIPTION + DESCRIPTION "" ::= { ntpProxyControlEntry 1 } ntpProxyControlStatus OBJECT-TYPE SYNTAX NTPRowStatus MAX-ACCESS read-create STATUS current - DESCRIPTION + DESCRIPTION "" ::= { ntpProxyControlEntry 2 } -- -- Proxy Server System Table -- ntpProxyServerSystemTable OBJECT-TYPE SYNTAX SEQUENCE OF NtpProxyServerSystemEntry MAX-ACCESS not-accessible STATUS current - DESCRIPTION + DESCRIPTION "" ::= { ntpProxyControl 2 } ntpProxyServerSystemEntry OBJECT-TYPE SYNTAX NtpProxyServerSystemEntry MAX-ACCESS not-accessible STATUS current - DESCRIPTION + DESCRIPTION "" INDEX { ntpProxyServerIPAddr } ::= { ntpProxyServerSystemTable 1 } NtpProxyServerSystemEntry ::= SEQUENCE { ntpProxyServerSysLeap NTPLeapIndicator, ntpProxyServerSysStratum Integer32, ntpProxyServerSysPrecision Integer32, ntpProxyServerSysRootDelay OCTET STRING, ntpProxyServerSysRootDispersion OCTET STRING, ntpProxyServerSysRefId OCTET STRING, ntpProxyServerSysRefTime NTPTimeStamp, ntpProxyServerSysPoll Integer32, ntpProxyServerSysPeer Unsigned32, ntpProxyServerSysPhase OCTET STRING, ntpProxyServerSysFreq OCTET STRING, ntpProxyServerSysError OCTET STRING, ntpProxyServerSysClock NTPTimeStamp, ntpProxyServerSysSystem OCTET STRING, ntpProxyServerSysProcessor OCTET STRING } ntpProxyServerSysLeap OBJECT-TYPE SYNTAX NTPLeapIndicator MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION "" ::= { ntpProxyServerSystemEntry 1 } ntpProxyServerSysStratum OBJECT-TYPE SYNTAX Integer32 (0..255) MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION "" ::= { ntpProxyServerSystemEntry 2 } ntpProxyServerSysPrecision OBJECT-TYPE SYNTAX Integer32 MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION "" ::= { ntpProxyServerSystemEntry 3 } ntpProxyServerSysRootDelay OBJECT-TYPE SYNTAX OCTET STRING MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION "" ::= { ntpProxyServerSystemEntry 4 } ntpProxyServerSysRootDispersion OBJECT-TYPE SYNTAX OCTET STRING MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION "" ::= { ntpProxyServerSystemEntry 5 } ntpProxyServerSysRefId OBJECT-TYPE SYNTAX OCTET STRING MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION "" ::= { ntpProxyServerSystemEntry 6 } ntpProxyServerSysRefTime OBJECT-TYPE SYNTAX NTPTimeStamp MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION "" ::= { ntpProxyServerSystemEntry 7 } ntpProxyServerSysPoll OBJECT-TYPE SYNTAX Integer32 MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION "" ::= { ntpProxyServerSystemEntry 8 } ntpProxyServerSysPeer OBJECT-TYPE SYNTAX Unsigned32 MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION "" ::= { ntpProxyServerSystemEntry 9 } ntpProxyServerSysPhase OBJECT-TYPE SYNTAX OCTET STRING MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION "" ::= { ntpProxyServerSystemEntry 10 } ntpProxyServerSysFreq OBJECT-TYPE SYNTAX OCTET STRING MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION "" ::= { ntpProxyServerSystemEntry 11 } ntpProxyServerSysError OBJECT-TYPE SYNTAX OCTET STRING MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION "" ::= { ntpProxyServerSystemEntry 12 } ntpProxyServerSysClock OBJECT-TYPE SYNTAX NTPTimeStamp MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION "" ::= { ntpProxyServerSystemEntry 13 } ntpProxyServerSysSystem OBJECT-TYPE SYNTAX OCTET STRING MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION "" ::= { ntpProxyServerSystemEntry 14 } ntpProxyServerSysProcessor OBJECT-TYPE SYNTAX OCTET STRING MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION "" ::= { ntpProxyServerSystemEntry 15 } -- -- Proxy Peer Variables Table -- ntpProxyPeersVarTable OBJECT-TYPE SYNTAX SEQUENCE OF NtpProxyPeersVarEntry MAX-ACCESS not-accessible STATUS current - DESCRIPTION + DESCRIPTION "" ::= { ntpProxyControl 3 } ntpProxyPeersVarEntry OBJECT-TYPE SYNTAX NtpProxyPeersVarEntry MAX-ACCESS not-accessible STATUS current - DESCRIPTION + DESCRIPTION "" INDEX { ntpProxyServerIPAddr, ntpProxyPeersAssocId } ::= { ntpProxyPeersVarTable 1 } NtpProxyPeersVarEntry ::= SEQUENCE { ntpProxyPeersAssocId Unsigned32, ntpProxyPeersConfigured TruthValue, ntpProxyPeersPeerAddress IpAddress, ntpProxyPeersPeerPort Unsigned32, ntpProxyPeersHostAddress IpAddress, ntpProxyPeersHostPort Unsigned32, ntpProxyPeersLeap NTPLeapIndicator, ntpProxyPeersMode INTEGER, ntpProxyPeersStratum Integer32, ntpProxyPeersPeerPoll Integer32, ntpProxyPeersHostPoll Integer32, ntpProxyPeersPrecision Integer32, ntpProxyPeersRootDelay OCTET STRING, ntpProxyPeersRootDispersion OCTET STRING, ntpProxyPeersRefId OCTET STRING, ntpProxyPeersRefTime NTPTimeStamp, ntpProxyPeersOrgTime NTPTimeStamp, ntpProxyPeersReceiveTime NTPTimeStamp, ntpProxyPeersTransmitTime NTPTimeStamp, ntpProxyPeersUpdateTime NTPTimeStamp, ntpProxyPeersReach Unsigned32, ntpProxyPeersTimer Integer32, ntpProxyPeersOffset OCTET STRING, ntpProxyPeersDelay OCTET STRING, ntpProxyPeersDispersion OCTET STRING, ntpProxyPeersFilterValidEntries Integer32 } ntpProxyPeersAssocId OBJECT-TYPE SYNTAX Unsigned32 MAX-ACCESS not-accessible STATUS current - DESCRIPTION + DESCRIPTION "" ::= { ntpProxyPeersVarEntry 1 } ntpProxyPeersConfigured OBJECT-TYPE SYNTAX TruthValue MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION "" ::= { ntpProxyPeersVarEntry 2 } ntpProxyPeersPeerAddress OBJECT-TYPE SYNTAX IpAddress MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION "" ::= { ntpProxyPeersVarEntry 3 } ntpProxyPeersPeerPort OBJECT-TYPE SYNTAX Unsigned32 MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION "" ::= { ntpProxyPeersVarEntry 4 } ntpProxyPeersHostAddress OBJECT-TYPE SYNTAX IpAddress MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION "" ::= { ntpProxyPeersVarEntry 5 } ntpProxyPeersHostPort OBJECT-TYPE SYNTAX Unsigned32 MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION "" ::= { ntpProxyPeersVarEntry 6 } ntpProxyPeersLeap OBJECT-TYPE SYNTAX NTPLeapIndicator MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION "" ::= { ntpProxyPeersVarEntry 7 } ntpProxyPeersMode OBJECT-TYPE SYNTAX INTEGER { unspecified(0), - symmetricActive(1), + symmetricActive(1), symmetricPassive(2), client(3), - server(4), + server(4), broadcast(5), reservedControl(6), reservedPrivate(7) } MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION "" ::= { ntpProxyPeersVarEntry 8 } ntpProxyPeersStratum OBJECT-TYPE SYNTAX Integer32 (0..255) MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION "" ::= { ntpProxyPeersVarEntry 9 } ntpProxyPeersPeerPoll OBJECT-TYPE SYNTAX Integer32 MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION "" ::= { ntpProxyPeersVarEntry 10 } ntpProxyPeersHostPoll OBJECT-TYPE SYNTAX Integer32 MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION "" ::= { ntpProxyPeersVarEntry 11 } ntpProxyPeersPrecision OBJECT-TYPE SYNTAX Integer32 MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION "" ::= { ntpProxyPeersVarEntry 12 } ntpProxyPeersRootDelay OBJECT-TYPE SYNTAX OCTET STRING MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION "" ::= { ntpProxyPeersVarEntry 13 } ntpProxyPeersRootDispersion OBJECT-TYPE SYNTAX OCTET STRING MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION "" ::= { ntpProxyPeersVarEntry 14 } ntpProxyPeersRefId OBJECT-TYPE SYNTAX OCTET STRING MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION "" ::= { ntpProxyPeersVarEntry 15 } ntpProxyPeersRefTime OBJECT-TYPE SYNTAX NTPTimeStamp MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION "" ::= { ntpProxyPeersVarEntry 16 } ntpProxyPeersOrgTime OBJECT-TYPE SYNTAX NTPTimeStamp MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION "" ::= { ntpProxyPeersVarEntry 17 } ntpProxyPeersReceiveTime OBJECT-TYPE SYNTAX NTPTimeStamp MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION "" ::= { ntpProxyPeersVarEntry 18 } ntpProxyPeersTransmitTime OBJECT-TYPE SYNTAX NTPTimeStamp MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION "" ::= { ntpProxyPeersVarEntry 19 } ntpProxyPeersUpdateTime OBJECT-TYPE SYNTAX NTPTimeStamp MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION "" ::= { ntpProxyPeersVarEntry 20 } ntpProxyPeersReach OBJECT-TYPE SYNTAX Unsigned32 MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION "" ::= { ntpProxyPeersVarEntry 21 } ntpProxyPeersTimer OBJECT-TYPE SYNTAX Integer32 MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION "" ::= { ntpProxyPeersVarEntry 22 } ntpProxyPeersOffset OBJECT-TYPE SYNTAX OCTET STRING MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION "" ::= { ntpProxyPeersVarEntry 23 } ntpProxyPeersDelay OBJECT-TYPE SYNTAX OCTET STRING MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION "" ::= { ntpProxyPeersVarEntry 24 } ntpProxyPeersDispersion OBJECT-TYPE SYNTAX OCTET STRING MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION "" ::= { ntpProxyPeersVarEntry 25 } ntpProxyPeersFilterValidEntries OBJECT-TYPE SYNTAX Integer32 MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION "The number of valid entries for a peer in the Proxy Filter Register Table. This number can be zero." ::= { ntpProxyPeersVarEntry 26 } -- -- Proxy Filter Register Table -- ntpProxyFilterRegisterTable OBJECT-TYPE SYNTAX SEQUENCE OF NtpProxyFilterRegisterEntry MAX-ACCESS not-accessible STATUS current - DESCRIPTION + DESCRIPTION "" ::= { ntpProxyControl 4 } ntpProxyFilterRegisterEntry OBJECT-TYPE SYNTAX NtpProxyFilterRegisterEntry MAX-ACCESS not-accessible STATUS current - DESCRIPTION + DESCRIPTION "" - INDEX { ntpProxyServerIPAddr, ntpProxyPeersAssocId, + INDEX { ntpProxyServerIPAddr, ntpProxyPeersAssocId, ntpProxyFilterIndex } ::= { ntpProxyFilterRegisterTable 1 } NtpProxyFilterRegisterEntry ::= SEQUENCE { ntpProxyFilterIndex Unsigned32, ntpProxyFilterPeersOffset OCTET STRING, ntpProxyFilterPeersDelay OCTET STRING, ntpProxyFilterPeersDispersion OCTET STRING } ntpProxyFilterIndex OBJECT-TYPE SYNTAX Unsigned32 MAX-ACCESS not-accessible STATUS current - DESCRIPTION + DESCRIPTION "" ::= { ntpProxyFilterRegisterEntry 1 } ntpProxyFilterPeersOffset OBJECT-TYPE SYNTAX OCTET STRING MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION "" ::= { ntpProxyFilterRegisterEntry 2 } ntpProxyFilterPeersDelay OBJECT-TYPE SYNTAX OCTET STRING MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION "" ::= { ntpProxyFilterRegisterEntry 3 } ntpProxyFilterPeersDispersion OBJECT-TYPE SYNTAX OCTET STRING MAX-ACCESS read-only STATUS current - DESCRIPTION + DESCRIPTION "" ::= { ntpProxyFilterRegisterEntry 4 } END Index: head/contrib/bsnmp/snmp_target/snmp_target.3 =================================================================== --- head/contrib/bsnmp/snmp_target/snmp_target.3 (revision 310647) +++ head/contrib/bsnmp/snmp_target/snmp_target.3 (revision 310648) @@ -1,204 +1,204 @@ .\"- .\" Copyright (C) 2010 The FreeBSD Foundation .\" All rights reserved. -.\" +.\" .\" This documentation was written by Shteryana Sotirova Shopova under .\" sponsorship from the FreeBSD Foundation. .\" .\" 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. .\" .\" $FreeBSD$ .\" .Dd December 16, 2010 .Dt SNMP_TARGET 3 .Os .Sh NAME .Nm snmp_target .Nd "Target addresses and notifications module for" .Xr bsnmpd 1 .Sh LIBRARY .Pq begemotSnmpdModulePath."target" = "/usr/lib/snmp_target.so" .Sh DESCRIPTION The .Nm snmp_target module implements SNMPv3 Management Target MIB and basic functionality from Notification MIB as defined in RFC 3413. The module is used to manage the internal list of SNMPv3 notification target addresses in .Nm bsnmpd and their associated transport and encapsulation parameters. The module must be loaded for .Nm bsnmpd to send SNMPv3 Trap-PDUs to the configured notification target addresses. .Sh IMPLEMENTATION NOTES A short description of the objects implemented in the module follows. .Bl -tag -width "XXXXXXXXX" .It Va snmpTargetSpinLock An advisory lock used to coordinate several Command Generator Applications when altering the SNMP Target addresses and their associated parameters. .It Va snmpTargetAddrTable The table contains the transport addresses to be used in generation of SNMP messages. The table contains the following objects .Bl -tag -width ".It Va snmpTargetAddrName" .It Va snmpTargetAddrName A unique local identifier used as entry key. Not accessible for GET or SET operations. .It Va snmpTargetAddrTDomain The transport domain of the target address. Currently only UDP over IPv4 is supported and any attempt to SET the value of this object will return an "inconsistentValue" error. Additional transport domains will be supported in future via the object definitions in TRANSPORT-ADDRESS-MIB (RFC 3419). .It Va snmpTargetAddrTAddress The transport address of this entry interpreted within the context of the value of .Va snmpTargetAddrTDomain . For UDP over IPv4, this is a 6-byte long octetstring, with the first 4 bytes representing the IPv4 address and the last 2 bytes the UDP port number in network-byte order. .It Va snmpTargetAddrTimeout The value of this object is only relevant when the receiver of the SNMP message is to send an acknowledgment that the message was received, i.e for SNMP notifications it is relevant if the notification is SNMP Inform rather than SNMP Trap. Currently .Nm bsnmpd supports only SNMP Trap notifications, so the value of this object is meaningless. .It Va snmpTargetAddrRetryCount As with .Va snmpTargetAddrTimeout the value of this object currently is meaningless. .It Va snmpTargetAddrTagList A list of human-readable tag values used to select target addresses for a particular operation. Recognized ASCII delimiting characters between tags are space (0x20), tab (0x20), carriage return (0xOD) and line feed (0x0A). .It Va snmpTargetAddrParams The value of this object contains the value of a key in snmpTargetParamsTable containing SNMP parameters used when generating messages to this transport address. .It Va snmpTargetAddrStorageType This column always has either of two values. Entries created via .Nm bsnmpd's configuration file always have this column set to readOnly (5) and it is not possible to modify those entries. Entries created by Command Generator Applications always have this column set to volatile(2) and such entries are lost when the module is restarted. A SET operation on this column is not allowed. .It Va snmpTargetAddrRowStatus This column is used to create new target address entries or delete existing ones from the table. .El .It Va snmpTargetParamsTable The table contains the target information to be used in generation of SNMP messages. The table contains the following objects .Bl -tag -width ".It Va snmpTargetParamsName" .It Va snmpTargetParamsName A unique local identifier used as entry key. Not accessible for GET or SET operations. .It Va snmpTargetParamsMPModel The Message Processing Model to be used when generating SNMP PDUs using this entry. Supported values are 0 for SNMPv1, 1 for SNMPv2c and 3 for SNMPv3. .It Va snmpTargetParamsSecurityModel The Security Model to be used when generating SNMP PDUs using this entry. Supported values are 1 for SNMPv1, 2 for SNMPv2c and 3 for SNMPv3 User-Based Security Model. .It Va snmpTargetParamsSecurityName The securityName which identifies the Principal on whose behalf SNMP PDUs will be generated using this entry. For SNMPv1 and SNMPv2c this is the name of a community configured in .Nm bsnmpd , and for SNMPv3 USM, this is the name of an existing user configured via the .Nm snmp_usm module. .It Va snmpTargetParamsSecurityLevel The Security Level to be used when generating SNMP PDUs using this entry. Supported values are noAuthNoPriv(1) for plain-text PDUs with no authentication, authNoPriv(2) for authenticated plain-text PDUs and authPriv(3) for encrypted PDUs. .It Va snmpTargetParamsStorageType As with .Va snmpTargetAddrStorageType this column always has either of two values. Entries created via .Nm bsnmpd's configuration file always have this column set to readOnly (5), while entries created by Command Generator Applications always have this column set to volatile(2). A SET operation on this column is not allowed. .It Va snmpTargetParamsRowStatus This column is used to create new target address parameters entries or delete existing ones from the table. .El .It Va snmpNotifyTable The table is used to select the management targets which should receive SNMP notifications. The table contains the following objects .Bl -tag -width ".It Va snmpNotifyName" .It Va snmpNotifyName A unique local identifier used as entry key. Not accessible for GET or SET operations. .It Va snmpNotifyTag This object contains a single tag value used to select target addresses from the .Va snmpTargetAddrTable to which the notifications will be send. .It Va snmpNotifyType The type of SNMP notifications that will be send to the target addresses matching the corresponding .Va snmpNotifyTag . Possible values are Trap (1) or Inform (2). Currently only SNMP Traps are supported and any attempt to SET the value of this object will return an "inconsistentValue" error. .It Va snmpNotifyStorageType Again this column always has either of two values. Entries created via .Nm bsnmpd's configuration file always have this column set to readOnly (5), while entries created by Command Generator Applications always have this column set to volatile(2). A SET operation on this column is not allowed. .It Va snmpNotifyRowStatus This column is used to create new notification target entries or delete existing ones from the table. .El .El .Pp The .Va snmpNotifyFilterProfileTable and .Va snmpNotifyFilterTable tables from the SNMP-NOTIFICATION-MIB are not supported by the module. Notification filtering is supported via the .Xr snmp_vacm 3 module instead. .Sh FILES .Bl -tag -width "XXXXXXXXX" .It Pa /usr/share/snmp/defs/target_tree.def The description of the MIB tree implemented by .Nm . .El .Sh SEE ALSO .Xr bsnmpd 1 , .Xr gensnmptree 1 , .Xr snmpmod 3 , .Xr snmp_usm 3 , .Xr snmp_vacm 3 .Sh STANDARDS IETF RFC 3413 .Sh AUTHORS .An Shteryana Shopova Aq syrinx@FreeBSD.org Index: head/contrib/bsnmp/snmp_usm/snmp_usm.3 =================================================================== --- head/contrib/bsnmp/snmp_usm/snmp_usm.3 (revision 310647) +++ head/contrib/bsnmp/snmp_usm/snmp_usm.3 (revision 310648) @@ -1,132 +1,132 @@ .\"- .\" Copyright (C) 2010 The FreeBSD Foundation .\" All rights reserved. -.\" +.\" .\" This documentation was written by Shteryana Sotirova Shopova under .\" sponsorship from the FreeBSD Foundation. .\" .\" 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. .\" .\" $FreeBSD$ .\" .Dd September 9, 2010 .Dt SNMP_USM 3 .Os .Sh NAME .Nm snmp_usm .Nd "user-based security module for" .Xr bsnmpd 1 .Sh LIBRARY .Pq begemotSnmpdModulePath."usm" = "/usr/lib/snmp_usm.so" .Sh DESCRIPTION The .Nm snmp_usm module implements SNMPv3 User-Based Security Model MIB as defined in RFC 3414. The module is used to manage the internal list of SNMPv3 USM active users in .Nm bsnmpd . The module must be loaded for .Nm bsnmpd to receive and process SNMPv3 USM PDUs correctly. .Sh IMPLEMENTATION NOTES A short description of the objects in the MIB follows. .Bl -tag -width "XXXXXXXXX" .It Va usmStats The subtree contains statistics for the User-based Security Model PDU processing. The statistics are reset each time the module is loaded. .It Va usmUserSpinLock An advisory lock used to coordinate several Command Generator Applications when altering the SNMP USM users. .It Va usmUserTable The table contains all SNMP USM users configured in .Nm bsnmpd . The table contains the following objects .Bl -tag -width ".It Va usmUserEngineID" .It Va usmUserEngineID An SNMP engine's administratively-unique identifier. Must be set to the same Engine ID as .Nm bsnmpd so that the user will actually be allowed to communicate with the daemon. The column is used as entry key and is not accessible for GET or SET operations. .It Va usmUserName The USM user name. The second entry key, again not accessible for GET or SET operations. .It Va usmUserSecurityName The column has the exact same value as the .Va usmUserName column, however is accessible for GET operations. .It Va usmUserCloneFrom A GET on this column will return an empty OID. SET operations are currently not supported. .It Va usmUserAuthProtocol The value of this column contains the OID corresponding to the authentication protocol used by the USM user. The following protocols and their OIDs are known to .Nm module .Bl -tag -width ".It Va NoAuthProtocol" .It NoAuthProtocol 1.3.6.1.6.3.10.1.1.1 .It HMACMD5AuthProtocol 1.3.6.1.6.3.10.1.1.2 .It HMACSHAAuthProtocol 1.3.6.1.6.3.10.1.1.3 .El .It Va usmUserAuthKeyChange , Va usmUserOwnAuthKeyChange These columns may be used to change the user's authentication key. .It Va usmUserPrivProtocol The value of this column contains the OID corresponding to the privacy protocol used by the USM user. The following protocols and their OIDs are known to .Nm module .Bl -tag -width ".It Va NoPrivProtocol" .It NoPrivProtocol 1.3.6.1.6.3.10.1.2.1 .It DESPrivProtoco 1.3.6.1.6.3.10.1.2.2 .It AesCfb128Protocol 1.3.6.1.6.3.10.1.2.4 .El .It Va usmUserPrivKeyChange , Va usmUserOwnPrivKeyChange These columns may be used to change the user's privacy key. .It Va usmUserPublic An arbitrary octet string that may be modified to confirm a SET operation on any of the columns was successful. .It Va usmUserStorageType This column always has either of two values. Entries created via .Nm bsnmpd's configuration file always have this column set to readOnly (5) and it is not possible to modify those entries. Entries created by Command Generator Applications always have this column set to volatile(2) and such entries are lost when the module is restarted. A SET operation on this column is not allowed. .It Va usmUserStatus This column is used to create new USM user entries or delete existing ones from the table. .El .El .Sh FILES .Bl -tag -width "XXXXXXXXX" .It Pa /usr/share/snmp/defs/usm_tree.def The description of the MIB tree implemented by .Nm . .El .Sh SEE ALSO .Xr bsnmpd 1 , .Xr gensnmptree 1 , .Xr snmpmod 3 .Sh STANDARDS IETF RFC 3414 .Sh AUTHORS .An Shteryana Shopova Aq syrinx@FreeBSD.org Index: head/contrib/bsnmp/snmp_usm/usm_snmp.c =================================================================== --- head/contrib/bsnmp/snmp_usm/usm_snmp.c (revision 310647) +++ head/contrib/bsnmp/snmp_usm/usm_snmp.c (revision 310648) @@ -1,614 +1,614 @@ /*- * Copyright (c) 2010 The FreeBSD Foundation * All rights reserved. * * This software was developed by Shteryana Sotirova Shopova under * sponsorship from the FreeBSD Foundation. * * 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 THE 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 THE 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. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include "asn1.h" #include "snmp.h" #include "snmpmod.h" #include "usm_tree.h" #include "usm_oid.h" static struct lmodule *usm_module; /* For the registration. */ static const struct asn_oid oid_usm = OIDX_snmpUsmMIB; static const struct asn_oid oid_usmNoAuthProtocol = OIDX_usmNoAuthProtocol; static const struct asn_oid oid_usmHMACMD5AuthProtocol = \ OIDX_usmHMACMD5AuthProtocol; static const struct asn_oid oid_usmHMACSHAAuthProtocol = \ OIDX_usmHMACSHAAuthProtocol; static const struct asn_oid oid_usmNoPrivProtocol = OIDX_usmNoPrivProtocol; static const struct asn_oid oid_usmDESPrivProtocol = OIDX_usmDESPrivProtocol; static const struct asn_oid oid_usmAesCfb128Protocol = OIDX_usmAesCfb128Protocol; static const struct asn_oid oid_usmUserSecurityName = OIDX_usmUserSecurityName; /* The registration. */ static uint reg_usm; static int32_t usm_lock; static struct usm_user * usm_get_user(const struct asn_oid *, uint); static struct usm_user * usm_get_next_user(const struct asn_oid *, uint); static void usm_append_userindex(struct asn_oid *, uint, const struct usm_user *); static int usm_user_index_decode(const struct asn_oid *, uint, uint8_t *, uint32_t *, char *); int op_usm_stats(struct snmp_context *ctx __unused, struct snmp_value *val, uint32_t sub __unused, uint32_t iidx __unused, enum snmp_op op) { struct snmpd_usmstat *usmstats; if (op == SNMP_OP_SET) return (SNMP_ERR_NOT_WRITEABLE); if ((usmstats = bsnmpd_get_usm_stats()) == NULL) return (SNMP_ERR_GENERR); if (op == SNMP_OP_GET) { switch (val->var.subs[sub - 1]) { case LEAF_usmStatsUnsupportedSecLevels: val->v.uint32 = usmstats->unsupported_seclevels; break; case LEAF_usmStatsNotInTimeWindows: val->v.uint32 = usmstats->not_in_time_windows; break; case LEAF_usmStatsUnknownUserNames: val->v.uint32 = usmstats->unknown_users; break; case LEAF_usmStatsUnknownEngineIDs: val->v.uint32 = usmstats->unknown_engine_ids; break; case LEAF_usmStatsWrongDigests: val->v.uint32 = usmstats->wrong_digests; break; case LEAF_usmStatsDecryptionErrors: val->v.uint32 = usmstats->decrypt_errors; break; default: return (SNMP_ERR_NOSUCHNAME); } return (SNMP_ERR_NOERROR); } abort(); } int op_usm_lock(struct snmp_context *ctx __unused, struct snmp_value *val, uint32_t sub, uint32_t iidx __unused, enum snmp_op op) { if (val->var.subs[sub - 1] != LEAF_usmUserSpinLock) return (SNMP_ERR_NOSUCHNAME); switch (op) { case SNMP_OP_GET: if (++usm_lock == INT32_MAX) usm_lock = 0; val->v.integer = usm_lock; break; case SNMP_OP_GETNEXT: abort(); case SNMP_OP_SET: if (val->v.integer != usm_lock) return (SNMP_ERR_INCONS_VALUE); break; case SNMP_OP_ROLLBACK: /* FALLTHROUGH */ case SNMP_OP_COMMIT: break; } return (SNMP_ERR_NOERROR); } int op_usm_users(struct snmp_context *ctx, struct snmp_value *val, uint32_t sub, uint32_t iidx __unused, enum snmp_op op) { uint32_t elen; struct usm_user *uuser, *clone; char uname[SNMP_ADM_STR32_SIZ]; uint8_t eid[SNMP_ENGINE_ID_SIZ]; switch (op) { case SNMP_OP_GET: if ((uuser = usm_get_user(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_GETNEXT: if ((uuser = usm_get_next_user(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); usm_append_userindex(&val->var, sub, uuser); break; case SNMP_OP_SET: if ((uuser = usm_get_user(&val->var, sub)) == NULL && val->var.subs[sub - 1] != LEAF_usmUserStatus && val->var.subs[sub - 1] != LEAF_usmUserCloneFrom) return (SNMP_ERR_NOSUCHNAME); if (community != COMM_INITIALIZE && uuser->type == StorageType_readOnly) return (SNMP_ERR_NOT_WRITEABLE); switch (val->var.subs[sub - 1]) { case LEAF_usmUserSecurityName: return (SNMP_ERR_NOT_WRITEABLE); case LEAF_usmUserCloneFrom: if (uuser != NULL || usm_user_index_decode(&val->var, - sub, eid, &elen, uname) < 0 || + sub, eid, &elen, uname) < 0 || !(asn_is_suboid(&oid_usmUserSecurityName, &val->v.oid))) return (SNMP_ERR_WRONG_VALUE); if ((clone = usm_get_user(&val->v.oid, sub)) == NULL) return (SNMP_ERR_INCONS_VALUE); if ((uuser = usm_new_user(eid, elen, uname)) == NULL) return (SNMP_ERR_GENERR); uuser->status = RowStatus_notReady; if (community != COMM_INITIALIZE) uuser->type = StorageType_volatile; else uuser->type = StorageType_readOnly; uuser->suser.auth_proto = clone->suser.auth_proto; uuser->suser.priv_proto = clone->suser.priv_proto; memcpy(uuser->suser.auth_key, clone->suser.auth_key, sizeof(uuser->suser.auth_key)); memcpy(uuser->suser.priv_key, clone->suser.priv_key, sizeof(uuser->suser.priv_key)); ctx->scratch->int1 = RowStatus_createAndWait; break; case LEAF_usmUserAuthProtocol: ctx->scratch->int1 = uuser->suser.auth_proto; if (asn_compare_oid(&oid_usmNoAuthProtocol, &val->v.oid) == 0) uuser->suser.auth_proto = SNMP_AUTH_NOAUTH; else if (asn_compare_oid(&oid_usmHMACMD5AuthProtocol, &val->v.oid) == 0) uuser->suser.auth_proto = SNMP_AUTH_HMAC_MD5; else if (asn_compare_oid(&oid_usmHMACSHAAuthProtocol, &val->v.oid) == 0) uuser->suser.auth_proto = SNMP_AUTH_HMAC_SHA; else return (SNMP_ERR_WRONG_VALUE); break; case LEAF_usmUserAuthKeyChange: case LEAF_usmUserOwnAuthKeyChange: if (val->var.subs[sub - 1] == LEAF_usmUserOwnAuthKeyChange && (usm_user == NULL || strcmp(uuser->suser.sec_name, usm_user->suser.sec_name) != 0)) return (SNMP_ERR_NO_ACCESS); if (val->v.octetstring.len > SNMP_AUTH_KEY_SIZ) return (SNMP_ERR_INCONS_VALUE); ctx->scratch->ptr1 = malloc(SNMP_AUTH_KEY_SIZ); if (ctx->scratch->ptr1 == NULL) return (SNMP_ERR_GENERR); memcpy(ctx->scratch->ptr1, uuser->suser.auth_key, SNMP_AUTH_KEY_SIZ); memcpy(uuser->suser.auth_key, val->v.octetstring.octets, val->v.octetstring.len); break; case LEAF_usmUserPrivProtocol: ctx->scratch->int1 = uuser->suser.priv_proto; if (asn_compare_oid(&oid_usmNoPrivProtocol, &val->v.oid) == 0) uuser->suser.priv_proto = SNMP_PRIV_NOPRIV; else if (asn_compare_oid(&oid_usmDESPrivProtocol, &val->v.oid) == 0) uuser->suser.priv_proto = SNMP_PRIV_DES; else if (asn_compare_oid(&oid_usmAesCfb128Protocol, &val->v.oid) == 0) uuser->suser.priv_proto = SNMP_PRIV_AES; else return (SNMP_ERR_WRONG_VALUE); break; case LEAF_usmUserPrivKeyChange: case LEAF_usmUserOwnPrivKeyChange: if (val->var.subs[sub - 1] == LEAF_usmUserOwnPrivKeyChange && (usm_user == NULL || strcmp(uuser->suser.sec_name, usm_user->suser.sec_name) != 0)) return (SNMP_ERR_NO_ACCESS); if (val->v.octetstring.len > SNMP_PRIV_KEY_SIZ) return (SNMP_ERR_INCONS_VALUE); ctx->scratch->ptr1 = malloc(SNMP_PRIV_KEY_SIZ); if (ctx->scratch->ptr1 == NULL) return (SNMP_ERR_GENERR); memcpy(ctx->scratch->ptr1, uuser->suser.priv_key, sizeof(uuser->suser.priv_key)); memcpy(uuser->suser.priv_key, val->v.octetstring.octets, val->v.octetstring.len); break; case LEAF_usmUserPublic: if (val->v.octetstring.len > SNMP_ADM_STR32_SIZ) return (SNMP_ERR_INCONS_VALUE); if (uuser->user_public_len > 0) { ctx->scratch->ptr2 = malloc(uuser->user_public_len); if (ctx->scratch->ptr2 == NULL) return (SNMP_ERR_GENERR); memcpy(ctx->scratch->ptr2, uuser->user_public, uuser->user_public_len); ctx->scratch->int2 = uuser->user_public_len; } if (val->v.octetstring.len > 0) { memcpy(uuser->user_public, val->v.octetstring.octets, val->v.octetstring.len); uuser->user_public_len = val->v.octetstring.len; } else { memset(uuser->user_public, 0, sizeof(uuser->user_public)); uuser->user_public_len = 0; } break; case LEAF_usmUserStorageType: return (SNMP_ERR_INCONS_VALUE); case LEAF_usmUserStatus: if (uuser == NULL) { if (val->v.integer != RowStatus_createAndWait || usm_user_index_decode(&val->var, sub, eid, &elen, uname) < 0) return (SNMP_ERR_INCONS_VALUE); uuser = usm_new_user(eid, elen, uname); if (uuser == NULL) return (SNMP_ERR_GENERR); uuser->status = RowStatus_notReady; if (community != COMM_INITIALIZE) uuser->type = StorageType_volatile; else uuser->type = StorageType_readOnly; } else if (val->v.integer != RowStatus_active && val->v.integer != RowStatus_destroy) return (SNMP_ERR_INCONS_VALUE); - + uuser->status = val->v.integer; break; } return (SNMP_ERR_NOERROR); case SNMP_OP_COMMIT: switch (val->var.subs[sub - 1]) { case LEAF_usmUserAuthKeyChange: case LEAF_usmUserOwnAuthKeyChange: case LEAF_usmUserPrivKeyChange: case LEAF_usmUserOwnPrivKeyChange: free(ctx->scratch->ptr1); break; case LEAF_usmUserPublic: if (ctx->scratch->ptr2 != NULL) free(ctx->scratch->ptr2); break; case LEAF_usmUserStatus: if (val->v.integer != RowStatus_destroy) break; if ((uuser = usm_get_user(&val->var, sub)) == NULL) return (SNMP_ERR_GENERR); usm_delete_user(uuser); break; default: break; } return (SNMP_ERR_NOERROR); case SNMP_OP_ROLLBACK: if ((uuser = usm_get_user(&val->var, sub)) == NULL) return (SNMP_ERR_GENERR); switch (val->var.subs[sub - 1]) { case LEAF_usmUserAuthProtocol: uuser->suser.auth_proto = ctx->scratch->int1; break; case LEAF_usmUserAuthKeyChange: case LEAF_usmUserOwnAuthKeyChange: memcpy(uuser->suser.auth_key, ctx->scratch->ptr1, sizeof(uuser->suser.auth_key)); free(ctx->scratch->ptr1); break; case LEAF_usmUserPrivProtocol: uuser->suser.priv_proto = ctx->scratch->int1; break; case LEAF_usmUserPrivKeyChange: case LEAF_usmUserOwnPrivKeyChange: memcpy(uuser->suser.priv_key, ctx->scratch->ptr1, sizeof(uuser->suser.priv_key)); free(ctx->scratch->ptr1); break; case LEAF_usmUserPublic: if (ctx->scratch->ptr2 != NULL) { memcpy(uuser->user_public, ctx->scratch->ptr2, ctx->scratch->int2); uuser->user_public_len = ctx->scratch->int2; free(ctx->scratch->ptr2); } else { memset(uuser->user_public, 0, sizeof(uuser->user_public)); uuser->user_public_len = 0; } break; case LEAF_usmUserCloneFrom: case LEAF_usmUserStatus: if (ctx->scratch->int1 == RowStatus_createAndWait) usm_delete_user(uuser); break; default: - break; + break; } return (SNMP_ERR_NOERROR); default: abort(); } switch (val->var.subs[sub - 1]) { case LEAF_usmUserSecurityName: return (string_get(val, uuser->suser.sec_name, -1)); case LEAF_usmUserCloneFrom: memcpy(&val->v.oid, &oid_zeroDotZero, sizeof(oid_zeroDotZero)); break; case LEAF_usmUserAuthProtocol: switch (uuser->suser.auth_proto) { case SNMP_AUTH_HMAC_MD5: memcpy(&val->v.oid, &oid_usmHMACMD5AuthProtocol, sizeof(oid_usmHMACMD5AuthProtocol)); break; case SNMP_AUTH_HMAC_SHA: memcpy(&val->v.oid, &oid_usmHMACSHAAuthProtocol, sizeof(oid_usmHMACSHAAuthProtocol)); break; default: memcpy(&val->v.oid, &oid_usmNoAuthProtocol, sizeof(oid_usmNoAuthProtocol)); break; } break; case LEAF_usmUserAuthKeyChange: case LEAF_usmUserOwnAuthKeyChange: return (string_get(val, (char *)uuser->suser.auth_key, 0)); case LEAF_usmUserPrivProtocol: switch (uuser->suser.priv_proto) { case SNMP_PRIV_DES: memcpy(&val->v.oid, &oid_usmDESPrivProtocol, sizeof(oid_usmDESPrivProtocol)); break; case SNMP_PRIV_AES: memcpy(&val->v.oid, &oid_usmAesCfb128Protocol, sizeof(oid_usmAesCfb128Protocol)); break; default: memcpy(&val->v.oid, &oid_usmNoPrivProtocol, sizeof(oid_usmNoPrivProtocol)); break; } break; case LEAF_usmUserPrivKeyChange: case LEAF_usmUserOwnPrivKeyChange: return (string_get(val, (char *)uuser->suser.priv_key, 0)); case LEAF_usmUserPublic: return (string_get(val, uuser->user_public, uuser->user_public_len)); case LEAF_usmUserStorageType: val->v.integer = uuser->type; break; case LEAF_usmUserStatus: val->v.integer = uuser->status; break; } return (SNMP_ERR_NOERROR); } static int usm_user_index_decode(const struct asn_oid *oid, uint sub, uint8_t *engine, uint32_t *elen, char *uname) { uint32_t i, nlen; int uname_off; if (oid->subs[sub] > SNMP_ENGINE_ID_SIZ) return (-1); for (i = 0; i < oid->subs[sub]; i++) engine[i] = oid->subs[sub + i + 1]; *elen = i; uname_off = sub + oid->subs[sub] + 1; if ((nlen = oid->subs[uname_off]) >= SNMP_ADM_STR32_SIZ) return (-1); for (i = 0; i < nlen; i++) uname[i] = oid->subs[uname_off + i + 1]; uname[nlen] = '\0'; return (0); } static void usm_append_userindex(struct asn_oid *oid, uint sub, const struct usm_user *uuser) { uint32_t i; oid->len = sub + uuser->user_engine_len + strlen(uuser->suser.sec_name); oid->len += 2; oid->subs[sub] = uuser->user_engine_len; for (i = 1; i < uuser->user_engine_len + 1; i++) oid->subs[sub + i] = uuser->user_engine_id[i - 1]; sub += uuser->user_engine_len + 1; oid->subs[sub] = strlen(uuser->suser.sec_name); for (i = 1; i <= oid->subs[sub]; i++) oid->subs[sub + i] = uuser->suser.sec_name[i - 1]; } static struct usm_user * usm_get_user(const struct asn_oid *oid, uint sub) { uint32_t enginelen; char username[SNMP_ADM_STR32_SIZ]; uint8_t engineid[SNMP_ENGINE_ID_SIZ]; if (usm_user_index_decode(oid, sub, engineid, &enginelen, username) < 0) return (NULL); return (usm_find_user(engineid, enginelen, username)); } static struct usm_user * usm_get_next_user(const struct asn_oid *oid, uint sub) { uint32_t enginelen; char username[SNMP_ADM_STR32_SIZ]; uint8_t engineid[SNMP_ENGINE_ID_SIZ]; struct usm_user *uuser; if (oid->len - sub == 0) return (usm_first_user()); if (usm_user_index_decode(oid, sub, engineid, &enginelen, username) < 0) return (NULL); if ((uuser = usm_find_user(engineid, enginelen, username)) != NULL) return (usm_next_user(uuser)); return (NULL); } /* * USM snmp module initialization hook. * Returns 0 on success, < 0 on error. */ static int usm_init(struct lmodule * mod, int argc __unused, char *argv[] __unused) { usm_module = mod; usm_lock = random(); bsnmpd_reset_usm_stats(); return (0); } /* * USM snmp module finalization hook. */ static int usm_fini(void) { usm_flush_users(); or_unregister(reg_usm); return (0); } /* * USM snmp module start operation. */ static void usm_start(void) { reg_usm = or_register(&oid_usm, "The MIB module for managing SNMP User-Based Security Model.", usm_module); } static void usm_dump(void) { struct usm_user *uuser; struct snmpd_usmstat *usmstats; const char *const authstr[] = { "noauth", "md5", "sha", NULL }; const char *const privstr[] = { "nopriv", "des", "aes", NULL }; if ((usmstats = bsnmpd_get_usm_stats()) != NULL) { syslog(LOG_ERR, "UnsupportedSecLevels\t\t%u", usmstats->unsupported_seclevels); syslog(LOG_ERR, "NotInTimeWindows\t\t%u", usmstats->not_in_time_windows); syslog(LOG_ERR, "UnknownUserNames\t\t%u", usmstats->unknown_users); syslog(LOG_ERR, "UnknownEngineIDs\t\t%u", usmstats->unknown_engine_ids); syslog(LOG_ERR, "WrongDigests\t\t%u", usmstats->wrong_digests); syslog(LOG_ERR, "DecryptionErrors\t\t%u", usmstats->decrypt_errors); } syslog(LOG_ERR, "USM users"); for (uuser = usm_first_user(); uuser != NULL; (uuser = usm_next_user(uuser))) syslog(LOG_ERR, "user %s\t\t%s, %s", uuser->suser.sec_name, authstr[uuser->suser.auth_proto], privstr[uuser->suser.priv_proto]); } const char usm_comment[] = \ "This module implements SNMP User-based Security Model defined in RFC 3414."; const struct snmp_module config = { .comment = usm_comment, .init = usm_init, .fini = usm_fini, .start = usm_start, .tree = usm_ctree, .dump = usm_dump, .tree_size = usm_CTREE_SIZE, }; Index: head/contrib/bsnmp/snmp_vacm/snmp_vacm.3 =================================================================== --- head/contrib/bsnmp/snmp_vacm/snmp_vacm.3 (revision 310647) +++ head/contrib/bsnmp/snmp_vacm/snmp_vacm.3 (revision 310648) @@ -1,94 +1,94 @@ .\"- .\" Copyright (C) 2010 The FreeBSD Foundation .\" All rights reserved. -.\" +.\" .\" This documentation was written by Shteryana Sotirova Shopova under .\" sponsorship from the FreeBSD Foundation. .\" .\" 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. .\" .\" $FreeBSD$ .\" .Dd October 7, 2010 .Dt SNMP_VACM 3 .Os .Sh NAME .Nm snmp_vacm .Nd "View-based Access Control module for" .Xr bsnmpd 1 .Sh LIBRARY .Pq begemotSnmpdModulePath."vacm" = "/usr/lib/snmp_vacm.so" .Sh DESCRIPTION The .Nm snmp_vacm module implements SNMPv3 View-based Access Control Model MIB as defined in RFC 3415. The module is used to manage the internal lists of SNMPv1, v2c and v3 user names and groups and their access rights to fetch or modify the values of the MIB objects maintained by .Nm bsnmpd and the modules loaded in the daemon. The module must be loaded for .Nm bsnmpd to implement proper view-based access control. If the module is not loaded, access is granted to all configured SNMPv1 & SNMPv2c communities and SNMPv3 USM users. .Sh IMPLEMENTATION NOTES An entry in any table implemented by this MIB may be created by setting the relevant RowStatus column to createAndGo (4) - in fact, any other value for those columns in a SET operation will cause an error. When an entry is created, any of its columns that is not used as index, is set to the default value as specified in the SNMP-VIEW-BASED-ACM-MIB. All entries maintained by the module are persistent after reboot if created via .Nm bsnmpd 's config file, otherwise entries created via SNMP are lost after reboot. A short description of the objects in the MIB follows. .Bl -tag -width "XXXXXXXXX" .It Va vacmContextTable A read-only table that consists of a list of SNMP contexts available in .Nm bsnmpd . .It Va vacmSecurityToGroupTable The table contains a list of SNMPv1, v2c and v3 user names and the groups they belong to. .It Va vacmAccessTable The table contains a list of SNMP contexts to groups mappings and respectively the names of the SNMP views under those contexts, to which users in the group are granted read-only, read-write access or receive notifications for the objects under the subtree in the relevant view. .It Va vacmViewTreeFamilyTable The table contains a list of SNMP views, i.e. entries specifying the OID of a MIB subtree and whether access to the objects under this subtree is to be allowed or forbidden. .El .Sh FILES .Bl -tag -width "XXXXXXXXX" .It Pa /usr/share/snmp/defs/vacm_tree.def The description of the MIB tree implemented by .Nm . .El .Sh SEE ALSO .Xr bsnmpd 1 , .Xr gensnmptree 1 , .Xr snmpmod 3 .Sh STANDARDS IETF RFC 3415 .Sh AUTHORS .An Shteryana Shopova Aq syrinx@FreeBSD.org Index: head/contrib/bsnmp/snmp_vacm/vacm_snmp.c =================================================================== --- head/contrib/bsnmp/snmp_vacm/vacm_snmp.c (revision 310647) +++ head/contrib/bsnmp/snmp_vacm/vacm_snmp.c (revision 310648) @@ -1,1026 +1,1026 @@ /*- * Copyright (c) 2010 The FreeBSD Foundation * All rights reserved. * * This software was developed by Shteryana Sotirova Shopova under * sponsorship from the FreeBSD Foundation. * * 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 THE 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 THE 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. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include "asn1.h" #include "snmp.h" #include "snmpmod.h" #include "vacm_tree.h" #include "vacm_oid.h" static struct lmodule *vacm_module; /* For the registration. */ static const struct asn_oid oid_vacm = OIDX_snmpVacmMIB; static uint reg_vacm; static int32_t vacm_lock; /* * Internal datastructures and forward declarations. */ static void vacm_append_userindex(struct asn_oid *, uint, const struct vacm_user *); static int vacm_user_index_decode(const struct asn_oid *, uint, int32_t *, char *); static struct vacm_user *vacm_get_user(const struct asn_oid *, uint); static struct vacm_user *vacm_get_next_user(const struct asn_oid *, uint); static void vacm_append_access_rule_index(struct asn_oid *, uint, const struct vacm_access *); static int vacm_access_rule_index_decode(const struct asn_oid *, uint, char *, char *, int32_t *, int32_t *); static struct vacm_access * vacm_get_access_rule(const struct asn_oid *, uint); static struct vacm_access * vacm_get_next_access_rule(const struct asn_oid *, uint); static int vacm_view_index_decode(const struct asn_oid *, uint, char *, struct asn_oid *); static void vacm_append_viewindex(struct asn_oid *, uint, const struct vacm_view *); static struct vacm_view *vacm_get_view(const struct asn_oid *, uint); static struct vacm_view *vacm_get_next_view(const struct asn_oid *, uint); static struct vacm_view *vacm_get_view_by_name(u_char *, u_int); static struct vacm_context *vacm_get_context(const struct asn_oid *, uint); static struct vacm_context *vacm_get_next_context(const struct asn_oid *, uint); static void vacm_append_ctxindex(struct asn_oid *, uint, const struct vacm_context *); int op_vacm_context(struct snmp_context *ctx __unused, struct snmp_value *val, uint32_t sub, uint32_t iidx __unused, enum snmp_op op) { char cname[SNMP_ADM_STR32_SIZ]; size_t cnamelen; struct vacm_context *vacm_ctx; if (val->var.subs[sub - 1] != LEAF_vacmContextName) abort(); switch (op) { case SNMP_OP_GET: if ((vacm_ctx = vacm_get_context(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_GETNEXT: if ((vacm_ctx = vacm_get_next_context(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); vacm_append_ctxindex(&val->var, sub, vacm_ctx); break; case SNMP_OP_SET: if ((vacm_ctx = vacm_get_context(&val->var, sub)) != NULL) return (SNMP_ERR_WRONG_VALUE); if (community != COMM_INITIALIZE) return (SNMP_ERR_NOT_WRITEABLE); if (val->var.subs[sub] >= SNMP_ADM_STR32_SIZ) return (SNMP_ERR_WRONG_VALUE); if (index_decode(&val->var, sub, iidx, &cname, &cnamelen)) return (SNMP_ERR_GENERR); cname[cnamelen] = '\0'; if ((vacm_ctx = vacm_add_context(cname, reg_vacm)) == NULL) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case SNMP_OP_COMMIT: /* FALLTHROUGH*/ case SNMP_OP_ROLLBACK: return (SNMP_ERR_NOERROR); default: abort(); } return (string_get(val, vacm_ctx->ctxname, -1)); } int op_vacm_security_to_group(struct snmp_context *ctx, struct snmp_value *val, uint32_t sub, uint32_t iidx __unused, enum snmp_op op) { int32_t smodel; char uname[SNMP_ADM_STR32_SIZ]; struct vacm_user *user; switch (op) { case SNMP_OP_GET: if ((user = vacm_get_user(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_GETNEXT: if ((user = vacm_get_next_user(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); vacm_append_userindex(&val->var, sub, user); break; case SNMP_OP_SET: if ((user = vacm_get_user(&val->var, sub)) == NULL && val->var.subs[sub - 1] != LEAF_vacmSecurityToGroupStatus) return (SNMP_ERR_NOSUCHNAME); if (user != NULL) { if (community != COMM_INITIALIZE && user->type == StorageType_readOnly) return (SNMP_ERR_NOT_WRITEABLE); if (user->status == RowStatus_active && val->v.integer != RowStatus_destroy) return (SNMP_ERR_INCONS_VALUE); } switch (val->var.subs[sub - 1]) { case LEAF_vacmGroupName: ctx->scratch->ptr1 = user->group->groupname; ctx->scratch->int1 = strlen(user->group->groupname); return (vacm_user_set_group(user, val->v.octetstring.octets,val->v.octetstring.len)); case LEAF_vacmSecurityToGroupStorageType: return (SNMP_ERR_INCONS_VALUE); case LEAF_vacmSecurityToGroupStatus: if (user == NULL) { if (val->v.integer != RowStatus_createAndGo || vacm_user_index_decode(&val->var, sub, &smodel, uname) < 0) return (SNMP_ERR_INCONS_VALUE); user = vacm_new_user(smodel, uname); if (user == NULL) return (SNMP_ERR_GENERR); user->status = RowStatus_destroy; if (community != COMM_INITIALIZE) user->type = StorageType_volatile; else user->type = StorageType_readOnly; } else if (val->v.integer != RowStatus_active && val->v.integer != RowStatus_destroy) return (SNMP_ERR_INCONS_VALUE); ctx->scratch->int1 = user->status; user->status = val->v.integer; break; } return (SNMP_ERR_NOERROR); case SNMP_OP_COMMIT: if (val->var.subs[sub - 1] != LEAF_vacmSecurityToGroupStatus) return (SNMP_ERR_NOERROR); if ((user = vacm_get_user(&val->var, sub)) == NULL) return (SNMP_ERR_GENERR); switch (val->v.integer) { case RowStatus_destroy: return (vacm_delete_user(user)); case RowStatus_createAndGo: user->status = RowStatus_active; break; default: break; } return (SNMP_ERR_NOERROR); case SNMP_OP_ROLLBACK: if ((user = vacm_get_user(&val->var, sub)) == NULL) return (SNMP_ERR_GENERR); switch (val->var.subs[sub - 1]) { case LEAF_vacmGroupName: return (vacm_user_set_group(user, ctx->scratch->ptr1, ctx->scratch->int1)); case LEAF_vacmSecurityToGroupStatus: if (ctx->scratch->int1 == RowStatus_destroy) return (vacm_delete_user(user)); user->status = ctx->scratch->int1; break; default: break; } return (SNMP_ERR_NOERROR); default: abort(); } switch (val->var.subs[sub - 1]) { case LEAF_vacmGroupName: return (string_get(val, user->group->groupname, -1)); case LEAF_vacmSecurityToGroupStorageType: val->v.integer = user->type; break; case LEAF_vacmSecurityToGroupStatus: val->v.integer = user->status; break; default: abort(); } return (SNMP_ERR_NOERROR); } int op_vacm_access(struct snmp_context *ctx, struct snmp_value *val, uint32_t sub, uint32_t iidx __unused, enum snmp_op op) { int32_t smodel, slevel; char gname[SNMP_ADM_STR32_SIZ], cprefix[SNMP_ADM_STR32_SIZ]; struct vacm_access *acl; switch (op) { case SNMP_OP_GET: if ((acl = vacm_get_access_rule(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_GETNEXT: if ((acl = vacm_get_next_access_rule(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); vacm_append_access_rule_index(&val->var, sub, acl); break; case SNMP_OP_SET: if ((acl = vacm_get_access_rule(&val->var, sub)) == NULL && val->var.subs[sub - 1] != LEAF_vacmAccessStatus) return (SNMP_ERR_NOSUCHNAME); if (acl != NULL && community != COMM_INITIALIZE && acl->type == StorageType_readOnly) return (SNMP_ERR_NOT_WRITEABLE); switch (val->var.subs[sub - 1]) { case LEAF_vacmAccessContextMatch: ctx->scratch->int1 = acl->ctx_match; if (val->v.integer == vacmAccessContextMatch_exact) acl->ctx_match = 1; else if (val->v.integer == vacmAccessContextMatch_prefix) acl->ctx_match = 0; else return (SNMP_ERR_WRONG_VALUE); break; case LEAF_vacmAccessReadViewName: ctx->scratch->ptr1 = acl->read_view; acl->read_view = vacm_get_view_by_name(val->v.octetstring.octets, val->v.octetstring.len); if (acl->read_view == NULL) { acl->read_view = ctx->scratch->ptr1; return (SNMP_ERR_INCONS_VALUE); } return (SNMP_ERR_NOERROR); case LEAF_vacmAccessWriteViewName: ctx->scratch->ptr1 = acl->write_view; if ((acl->write_view = vacm_get_view_by_name(val->v.octetstring.octets, val->v.octetstring.len)) == NULL) { acl->write_view = ctx->scratch->ptr1; return (SNMP_ERR_INCONS_VALUE); } break; case LEAF_vacmAccessNotifyViewName: ctx->scratch->ptr1 = acl->notify_view; if ((acl->notify_view = vacm_get_view_by_name(val->v.octetstring.octets, val->v.octetstring.len)) == NULL) { acl->notify_view = ctx->scratch->ptr1; return (SNMP_ERR_INCONS_VALUE); } break; case LEAF_vacmAccessStorageType: return (SNMP_ERR_INCONS_VALUE); case LEAF_vacmAccessStatus: if (acl == NULL) { if (val->v.integer != RowStatus_createAndGo || vacm_access_rule_index_decode(&val->var, sub, gname, cprefix, &smodel, &slevel) < 0) return (SNMP_ERR_INCONS_VALUE); if ((acl = vacm_new_access_rule(gname, cprefix, smodel, slevel)) == NULL) return (SNMP_ERR_GENERR); acl->status = RowStatus_destroy; if (community != COMM_INITIALIZE) acl->type = StorageType_volatile; else acl->type = StorageType_readOnly; } else if (val->v.integer != RowStatus_active && val->v.integer != RowStatus_destroy) return (SNMP_ERR_INCONS_VALUE); ctx->scratch->int1 = acl->status; acl->status = val->v.integer; break; } return (SNMP_ERR_NOERROR); case SNMP_OP_COMMIT: if (val->var.subs[sub - 1] != LEAF_vacmAccessStatus) return (SNMP_ERR_NOERROR); if ((acl = vacm_get_access_rule(&val->var, sub)) == NULL) return (SNMP_ERR_GENERR); if (val->v.integer == RowStatus_destroy) return (vacm_delete_access_rule(acl)); else acl->status = RowStatus_active; return (SNMP_ERR_NOERROR); case SNMP_OP_ROLLBACK: if ((acl = vacm_get_access_rule(&val->var, sub)) == NULL) return (SNMP_ERR_GENERR); switch (val->var.subs[sub - 1]) { case LEAF_vacmAccessContextMatch: acl->ctx_match = ctx->scratch->int1; break; case LEAF_vacmAccessReadViewName: acl->read_view = ctx->scratch->ptr1; break; case LEAF_vacmAccessWriteViewName: acl->write_view = ctx->scratch->ptr1; break; case LEAF_vacmAccessNotifyViewName: acl->notify_view = ctx->scratch->ptr1; break; case LEAF_vacmAccessStatus: if (ctx->scratch->int1 == RowStatus_destroy) return (vacm_delete_access_rule(acl)); default: break; } return (SNMP_ERR_NOERROR); default: abort(); } switch (val->var.subs[sub - 1]) { case LEAF_vacmAccessContextMatch: return (string_get(val, acl->ctx_prefix, -1)); case LEAF_vacmAccessReadViewName: if (acl->read_view != NULL) return (string_get(val, acl->read_view->viewname, -1)); else return (string_get(val, NULL, 0)); case LEAF_vacmAccessWriteViewName: if (acl->write_view != NULL) return (string_get(val, acl->write_view->viewname, -1)); else return (string_get(val, NULL, 0)); case LEAF_vacmAccessNotifyViewName: if (acl->notify_view != NULL) return (string_get(val, acl->notify_view->viewname, -1)); else return (string_get(val, NULL, 0)); case LEAF_vacmAccessStorageType: val->v.integer = acl->type; break; case LEAF_vacmAccessStatus: val->v.integer = acl->status; break; default: abort(); } return (SNMP_ERR_NOERROR); } int op_vacm_view_lock(struct snmp_context *ctx __unused, struct snmp_value *val, uint32_t sub, uint32_t iidx __unused, enum snmp_op op) { if (val->var.subs[sub - 1] != LEAF_vacmViewSpinLock) return (SNMP_ERR_NOSUCHNAME); switch (op) { case SNMP_OP_GET: if (++vacm_lock == INT32_MAX) vacm_lock = 0; val->v.integer = vacm_lock; break; case SNMP_OP_GETNEXT: abort(); case SNMP_OP_SET: if (val->v.integer != vacm_lock) return (SNMP_ERR_INCONS_VALUE); break; case SNMP_OP_ROLLBACK: /* FALLTHROUGH */ case SNMP_OP_COMMIT: break; } return (SNMP_ERR_NOERROR); } int op_vacm_view(struct snmp_context *ctx, struct snmp_value *val, uint32_t sub, uint32_t iidx __unused, enum snmp_op op) { char vname[SNMP_ADM_STR32_SIZ]; struct asn_oid oid; struct vacm_view *view; switch (op) { case SNMP_OP_GET: if ((view = vacm_get_view(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_GETNEXT: if ((view = vacm_get_next_view(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); vacm_append_viewindex(&val->var, sub, view); break; case SNMP_OP_SET: if ((view = vacm_get_view(&val->var, sub)) == NULL && val->var.subs[sub - 1] != LEAF_vacmViewTreeFamilyStatus) return (SNMP_ERR_NOSUCHNAME); if (view != NULL) { if (community != COMM_INITIALIZE && view->type == StorageType_readOnly) return (SNMP_ERR_NOT_WRITEABLE); if (view->status == RowStatus_active && val->v.integer != RowStatus_destroy) return (SNMP_ERR_INCONS_VALUE); } switch (val->var.subs[sub - 1]) { case LEAF_vacmViewTreeFamilyMask: if (val->v.octetstring.len > sizeof(view->mask)) ctx->scratch->ptr1 = malloc(sizeof(view->mask)); if (ctx->scratch->ptr1 == NULL) return (SNMP_ERR_GENERR); memset(ctx->scratch->ptr1, 0, sizeof(view->mask)); memcpy(ctx->scratch->ptr1, view->mask, sizeof(view->mask)); memset(view->mask, 0, sizeof(view->mask)); memcpy(view->mask, val->v.octetstring.octets, val->v.octetstring.len); break; case LEAF_vacmViewTreeFamilyType: ctx->scratch->int1 = view->exclude; if (val->v.integer == vacmViewTreeFamilyType_included) view->exclude = 0; else if (val->v.integer == vacmViewTreeFamilyType_excluded) view->exclude = 1; else return (SNMP_ERR_WRONG_VALUE); break; case LEAF_vacmViewTreeFamilyStorageType: return (SNMP_ERR_INCONS_VALUE); case LEAF_vacmViewTreeFamilyStatus: if (view == NULL) { if (val->v.integer != RowStatus_createAndGo || vacm_view_index_decode(&val->var, sub, vname, &oid) < 0) return (SNMP_ERR_INCONS_VALUE); if ((view = vacm_new_view(vname, &oid)) == NULL) return (SNMP_ERR_GENERR); view->status = RowStatus_destroy; if (community != COMM_INITIALIZE) view->type = StorageType_volatile; else view->type = StorageType_readOnly; } else if (val->v.integer != RowStatus_active && val->v.integer != RowStatus_destroy) return (SNMP_ERR_INCONS_VALUE); ctx->scratch->int1 = view->status; view->status = val->v.integer; break; } return (SNMP_ERR_NOERROR); case SNMP_OP_COMMIT: switch (val->var.subs[sub - 1]) { case LEAF_vacmViewTreeFamilyMask: free(ctx->scratch->ptr1); break; case LEAF_vacmViewTreeFamilyStatus: if ((view = vacm_get_view(&val->var, sub)) == NULL) return (SNMP_ERR_GENERR); switch (val->v.integer) { case RowStatus_destroy: return (vacm_delete_view(view)); case RowStatus_createAndGo: view->status = RowStatus_active; break; default: /* NOTREACHED*/ return (SNMP_ERR_GENERR); } default: break; } return (SNMP_ERR_NOERROR); case SNMP_OP_ROLLBACK: if ((view = vacm_get_view(&val->var, sub)) == NULL) return (SNMP_ERR_GENERR); switch (val->var.subs[sub - 1]) { case LEAF_vacmViewTreeFamilyMask: memcpy(view->mask, ctx->scratch->ptr1, sizeof(view->mask)); free(ctx->scratch->ptr1); break; case LEAF_vacmViewTreeFamilyType: view->exclude = ctx->scratch->int1; break; case LEAF_vacmViewTreeFamilyStatus: if (ctx->scratch->int1 == RowStatus_destroy) return (vacm_delete_view(view)); break; default: - break; + break; } return (SNMP_ERR_NOERROR); default: abort(); } switch (val->var.subs[sub - 1]) { case LEAF_vacmViewTreeFamilyMask: return (string_get(val, view->mask, sizeof(view->mask))); case LEAF_vacmViewTreeFamilyType: if (view->exclude) val->v.integer = vacmViewTreeFamilyType_excluded; else val->v.integer = vacmViewTreeFamilyType_included; break; case LEAF_vacmViewTreeFamilyStorageType: val->v.integer = view->type; break; case LEAF_vacmViewTreeFamilyStatus: val->v.integer = view->status; break; default: abort(); } return (SNMP_ERR_NOERROR); } static void vacm_append_userindex(struct asn_oid *oid, uint sub, const struct vacm_user *user) { uint32_t i; oid->len = sub + strlen(user->secname) + 2; oid->subs[sub++] = user->sec_model; oid->subs[sub] = strlen(user->secname); for (i = 1; i <= strlen(user->secname); i++) oid->subs[sub + i] = user->secname[i - 1]; } static int vacm_user_index_decode(const struct asn_oid *oid, uint sub, int32_t *smodel, char *uname) { uint32_t i; *smodel = oid->subs[sub++]; if (oid->subs[sub] >= SNMP_ADM_STR32_SIZ) return (-1); for (i = 0; i < oid->subs[sub]; i++) uname[i] = oid->subs[sub + i + 1]; uname[i] = '\0'; return (0); } static struct vacm_user * vacm_get_user(const struct asn_oid *oid, uint sub) { int32_t smodel; char uname[SNMP_ADM_STR32_SIZ]; struct vacm_user *user; if (vacm_user_index_decode(oid, sub, &smodel, uname) < 0) return (NULL); for (user = vacm_first_user(); user != NULL; user = vacm_next_user(user)) if (strcmp(uname, user->secname) == 0 && user->sec_model == smodel) return (user); return (NULL); } static struct vacm_user * vacm_get_next_user(const struct asn_oid *oid, uint sub) { int32_t smodel; char uname[SNMP_ADM_STR32_SIZ]; struct vacm_user *user; if (oid->len - sub == 0) return (vacm_first_user()); if (vacm_user_index_decode(oid, sub, &smodel, uname) < 0) return (NULL); for (user = vacm_first_user(); user != NULL; user = vacm_next_user(user)) if (strcmp(uname, user->secname) == 0 && user->sec_model == smodel) return (vacm_next_user(user)); return (NULL); } static void vacm_append_access_rule_index(struct asn_oid *oid, uint sub, const struct vacm_access *acl) { uint32_t i; - + oid->len = sub + strlen(acl->group->groupname) + strlen(acl->ctx_prefix) + 4; oid->subs[sub] = strlen(acl->group->groupname); for (i = 1; i <= strlen(acl->group->groupname); i++) oid->subs[sub + i] = acl->group->groupname[i - 1]; sub += strlen(acl->group->groupname) + 1; oid->subs[sub] = strlen(acl->ctx_prefix); for (i = 1; i <= strlen(acl->ctx_prefix); i++) oid->subs[sub + i] = acl->ctx_prefix[i - 1]; sub += strlen(acl->ctx_prefix) + 1; oid->subs[sub++] = acl->sec_model; oid->subs[sub] = acl->sec_level; } static int vacm_access_rule_index_decode(const struct asn_oid *oid, uint sub, char *gname, char *cprefix, int32_t *smodel, int32_t *slevel) { uint32_t i; if (oid->subs[sub] >= SNMP_ADM_STR32_SIZ) return (-1); for (i = 0; i < oid->subs[sub]; i++) gname[i] = oid->subs[sub + i + 1]; gname[i] = '\0'; sub += strlen(gname) + 1; if (oid->subs[sub] >= SNMP_ADM_STR32_SIZ) return (-1); for (i = 0; i < oid->subs[sub]; i++) cprefix[i] = oid->subs[sub + i + 1]; cprefix[i] = '\0'; sub += strlen(cprefix) + 1; *smodel = oid->subs[sub++]; *slevel = oid->subs[sub]; return (0); } struct vacm_access * vacm_get_access_rule(const struct asn_oid *oid, uint sub) { int32_t smodel, slevel; char gname[SNMP_ADM_STR32_SIZ], prefix[SNMP_ADM_STR32_SIZ]; struct vacm_access *acl; if (vacm_access_rule_index_decode(oid, sub, gname, prefix, &smodel, &slevel) < 0) return (NULL); for (acl = vacm_first_access_rule(); acl != NULL; acl = vacm_next_access_rule(acl)) if (strcmp(gname, acl->group->groupname) == 0 && strcmp(prefix, acl->ctx_prefix) == 0 && smodel == acl->sec_model && slevel == acl->sec_level) return (acl); return (NULL); } struct vacm_access * vacm_get_next_access_rule(const struct asn_oid *oid __unused, uint sub __unused) { int32_t smodel, slevel; char gname[SNMP_ADM_STR32_SIZ], prefix[SNMP_ADM_STR32_SIZ]; struct vacm_access *acl; if (oid->len - sub == 0) return (vacm_first_access_rule()); if (vacm_access_rule_index_decode(oid, sub, gname, prefix, &smodel, &slevel) < 0) return (NULL); for (acl = vacm_first_access_rule(); acl != NULL; acl = vacm_next_access_rule(acl)) if (strcmp(gname, acl->group->groupname) == 0 && strcmp(prefix, acl->ctx_prefix) == 0 && smodel == acl->sec_model && slevel == acl->sec_model) return (vacm_next_access_rule(acl)); return (NULL); } static int vacm_view_index_decode(const struct asn_oid *oid, uint sub, char *vname, struct asn_oid *view_oid) -{ +{ uint32_t i; int viod_off; if (oid->subs[sub] >= SNMP_ADM_STR32_SIZ) return (-1); for (i = 0; i < oid->subs[sub]; i++) vname[i] = oid->subs[sub + i + 1]; vname[i] = '\0'; viod_off = sub + oid->subs[sub] + 1; if ((view_oid->len = oid->subs[viod_off]) > ASN_MAXOIDLEN) return (-1); memcpy(&view_oid->subs[0], &oid->subs[viod_off + 1], view_oid->len * sizeof(view_oid->subs[0])); return (0); } static void vacm_append_viewindex(struct asn_oid *oid, uint sub, const struct vacm_view *view) { uint32_t i; oid->len = sub + strlen(view->viewname) + 1; oid->subs[sub] = strlen(view->viewname); for (i = 1; i <= strlen(view->viewname); i++) oid->subs[sub + i] = view->viewname[i - 1]; sub += strlen(view->viewname) + 1; oid->subs[sub] = view->subtree.len; oid->len++; asn_append_oid(oid, &view->subtree); } struct vacm_view * vacm_get_view(const struct asn_oid *oid, uint sub) { char vname[SNMP_ADM_STR32_SIZ]; struct asn_oid subtree; struct vacm_view *view; if (vacm_view_index_decode(oid, sub, vname, &subtree) < 0) return (NULL); for (view = vacm_first_view(); view != NULL; view = vacm_next_view(view)) if (strcmp(vname, view->viewname) == 0 && asn_compare_oid(&subtree, &view->subtree)== 0) return (view); return (NULL); } struct vacm_view * vacm_get_next_view(const struct asn_oid *oid, uint sub) { char vname[SNMP_ADM_STR32_SIZ]; struct asn_oid subtree; struct vacm_view *view; if (oid->len - sub == 0) return (vacm_first_view()); if (vacm_view_index_decode(oid, sub, vname, &subtree) < 0) return (NULL); for (view = vacm_first_view(); view != NULL; view = vacm_next_view(view)) if (strcmp(vname, view->viewname) == 0 && asn_compare_oid(&subtree, &view->subtree)== 0) return (vacm_next_view(view)); return (NULL); } static struct vacm_view * vacm_get_view_by_name(u_char *octets, u_int len) { struct vacm_view *view; for (view = vacm_first_view(); view != NULL; view = vacm_next_view(view)) if (strlen(view->viewname) == len && memcmp(octets, view->viewname, len) == 0) return (view); return (NULL); } static struct vacm_context * vacm_get_context(const struct asn_oid *oid, uint sub) { char cname[SNMP_ADM_STR32_SIZ]; size_t cnamelen; u_int index_count; struct vacm_context *vacm_ctx; if (oid->subs[sub] >= SNMP_ADM_STR32_SIZ) return (NULL); index_count = 0; index_count = SNMP_INDEX(index_count, 1); if (index_decode(oid, sub, index_count, &cname, &cnamelen)) return (NULL); for (vacm_ctx = vacm_first_context(); vacm_ctx != NULL; vacm_ctx = vacm_next_context(vacm_ctx)) if (strcmp(cname, vacm_ctx->ctxname) == 0) return (vacm_ctx); return (NULL); } static struct vacm_context * vacm_get_next_context(const struct asn_oid *oid, uint sub) { char cname[SNMP_ADM_STR32_SIZ]; size_t cnamelen; u_int index_count; struct vacm_context *vacm_ctx; if (oid->len - sub == 0) return (vacm_first_context()); if (oid->subs[sub] >= SNMP_ADM_STR32_SIZ) return (NULL); index_count = 0; index_count = SNMP_INDEX(index_count, 1); if (index_decode(oid, sub, index_count, &cname, &cnamelen)) return (NULL); for (vacm_ctx = vacm_first_context(); vacm_ctx != NULL; vacm_ctx = vacm_next_context(vacm_ctx)) if (strcmp(cname, vacm_ctx->ctxname) == 0) return (vacm_next_context(vacm_ctx)); return (NULL); } static void vacm_append_ctxindex(struct asn_oid *oid, uint sub, const struct vacm_context *ctx) { uint32_t i; oid->len = sub + strlen(ctx->ctxname) + 1; oid->subs[sub] = strlen(ctx->ctxname); for (i = 1; i <= strlen(ctx->ctxname); i++) oid->subs[sub + i] = ctx->ctxname[i - 1]; } /* * VACM snmp module initialization hook. * Returns 0 on success, < 0 on error. */ static int vacm_init(struct lmodule *mod, int argc __unused, char *argv[] __unused) { vacm_module = mod; vacm_lock = random(); vacm_groups_init(); /* XXX: TODO - initialize structures */ return (0); } /* * VACM snmp module finalization hook. */ static int vacm_fini(void) { /* XXX: TODO - cleanup */ vacm_flush_contexts(reg_vacm); or_unregister(reg_vacm); return (0); } /* * VACM snmp module start operation. */ static void vacm_start(void) { static char dflt_ctx[] = ""; reg_vacm = or_register(&oid_vacm, "The MIB module for managing SNMP View-based Access Control Model.", vacm_module); (void)vacm_add_context(dflt_ctx, reg_vacm); } static void vacm_dump(void) { struct vacm_context *vacmctx; struct vacm_user *vuser; struct vacm_access *vacl; struct vacm_view *view; static char oidbuf[ASN_OIDSTRLEN]; syslog(LOG_ERR, "\n"); syslog(LOG_ERR, "Context list:"); for (vacmctx = vacm_first_context(); vacmctx != NULL; vacmctx = vacm_next_context(vacmctx)) syslog(LOG_ERR, "Context \"%s\", module id %d", vacmctx->ctxname, vacmctx->regid); syslog(LOG_ERR, "VACM users:"); for (vuser = vacm_first_user(); vuser != NULL; vuser = vacm_next_user(vuser)) syslog(LOG_ERR, "Uname %s, Group %s, model %d", vuser->secname, vuser->group!= NULL?vuser->group->groupname:"Unknown", vuser->sec_model); syslog(LOG_ERR, "VACM Access rules:"); for (vacl = vacm_first_access_rule(); vacl != NULL; vacl = vacm_next_access_rule(vacl)) syslog(LOG_ERR, "Group %s, CtxPrefix %s, Model %d, Level %d, " "RV %s, WR %s, NV %s", vacl->group!=NULL? vacl->group->groupname:"Unknown", vacl->ctx_prefix, vacl->sec_model, vacl->sec_level, vacl->read_view!=NULL? vacl->read_view->viewname:"None", vacl->write_view!=NULL? vacl->write_view->viewname:"None", vacl->notify_view!=NULL? vacl->notify_view->viewname:"None"); syslog(LOG_ERR, "VACM Views:"); for (view = vacm_first_view(); view != NULL; view = vacm_next_view(view)) syslog(LOG_ERR, "View %s, Tree %s - %s", view->viewname, asn_oid2str_r(&view->subtree, oidbuf), view->exclude? "excluded":"included"); } const char vacm_comment[] = \ "This module implements SNMP View-based Access Control Model defined in RFC 3415."; const struct snmp_module config = { .comment = vacm_comment, .init = vacm_init, .fini = vacm_fini, .start = vacm_start, .tree = vacm_ctree, .dump = vacm_dump, .tree_size = vacm_CTREE_SIZE, }; Index: head/contrib/bsnmp/snmpd/BEGEMOT-MIB.txt =================================================================== --- head/contrib/bsnmp/snmpd/BEGEMOT-MIB.txt (revision 310647) +++ head/contrib/bsnmp/snmpd/BEGEMOT-MIB.txt (revision 310648) @@ -1,59 +1,59 @@ -- -- 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/BEGEMOT-MIB.txt,v 1.5 2004/08/06 08:47:07 brandt Exp $ -- -- Begemot private definitions and root. -- BEGEMOT-MIB DEFINITIONS ::= BEGIN IMPORTS MODULE-IDENTITY FROM SNMPv2-SMI fokus FROM FOKUS-MIB; begemot MODULE-IDENTITY LAST-UPDATED "200201300000Z" ORGANIZATION "Fraunhofer FOKUS, CATS" CONTACT-INFO " Hartmut Brandt Postal: Fraunhofer Institute for Open Communication Systems Kaiserin-Augusta-Allee 31 10589 Berlin Germany Fax: +49 30 3463 7352 E-mail: harti@freebsd.org" DESCRIPTION "The root of the Begemot subtree of the fokus tree." ::= { fokus 1 } END Index: head/contrib/bsnmp/snmpd/BEGEMOT-SNMPD.txt =================================================================== --- head/contrib/bsnmp/snmpd/BEGEMOT-SNMPD.txt (revision 310647) +++ head/contrib/bsnmp/snmpd/BEGEMOT-SNMPD.txt (revision 310648) @@ -1,570 +1,570 @@ -- -- 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/BEGEMOT-SNMPD.txt,v 1.23 2004/08/06 08:47:08 brandt Exp $ -- -- Begemot Private SNMPd MIB. -- BEGEMOT-SNMPD-MIB DEFINITIONS ::= BEGIN IMPORTS MODULE-IDENTITY, OBJECT-TYPE, OBJECT-IDENTITY, Counter32, Unsigned32, IpAddress FROM SNMPv2-SMI TEXTUAL-CONVENTION, TruthValue, RowStatus FROM SNMPv2-TC MODULE-COMPLIANCE, OBJECT-GROUP FROM SNMPv2-CONF begemot FROM BEGEMOT-MIB; begemotSnmpd MODULE-IDENTITY LAST-UPDATED "200212040000Z" ORGANIZATION "Fraunhofer FOKUS, CATS" CONTACT-INFO " Hartmut Brandt Postal: Fraunhofer Institute for Open Communication Systems Kaiserin-Augusta-Allee 31 10589 Berlin Germany Fax: +49 30 3463 7352 E-mail: harti@freebsd.org" DESCRIPTION "The MIB module for the Begemot SNMP daemon." ::= { begemot 1 } begemotSnmpdObjects OBJECT IDENTIFIER ::= { begemotSnmpd 1 } begemotSnmpdDefs OBJECT IDENTIFIER ::= { begemotSnmpd 2 } -- -------------------------------------------------------------------------- SectionName ::= TEXTUAL-CONVENTION DISPLAY-HINT "14a" STATUS current DESCRIPTION "Name of a loadable module. Should consist of alphanumeric characers only, the first character must be a letter." SYNTAX OCTET STRING (SIZE(1..14)) -- -------------------------------------------------------------------------- -- -- Agent types -- begemotSnmpdAgent OBJECT IDENTIFIER ::= { begemotSnmpdDefs 1 } begemotSnmpdAgentFreeBSD OBJECT-IDENTITY STATUS current DESCRIPTION "Identifies the agent as running on FreeBSD." ::= { begemotSnmpdAgent 1 } -- -------------------------------------------------------------------------- -- -- The Config Group -- begemotSnmpdConfig OBJECT IDENTIFIER ::= { begemotSnmpdObjects 1 } begemotSnmpdTransmitBuffer OBJECT-TYPE SYNTAX INTEGER (484..65535) MAX-ACCESS read-write STATUS current DESCRIPTION "The size of the receive buffer in bytes. Larger messages are dropped by SNMPd." DEFVAL { 2048 } ::= { begemotSnmpdConfig 1 } begemotSnmpdReceiveBuffer OBJECT-TYPE SYNTAX INTEGER (484..65535) MAX-ACCESS read-write STATUS current DESCRIPTION "The size of the transmit buffer in bytes. Larger messages cannot be sent by the SNMPd." DEFVAL { 2048 } ::= { begemotSnmpdConfig 2 } begemotSnmpdCommunityDisable OBJECT-TYPE SYNTAX TruthValue MAX-ACCESS read-write STATUS current DESCRIPTION "Disables all access to the CommunityTable from SNMP. Once set it cannot be cleared." DEFVAL { false } ::= { begemotSnmpdConfig 3 } begemotSnmpdTrap1Addr OBJECT-TYPE SYNTAX IpAddress MAX-ACCESS read-write STATUS current DESCRIPTION "The trap sink for v1 traps." ::= { begemotSnmpdConfig 4 } begemotSnmpdVersionEnable OBJECT-TYPE SYNTAX Unsigned32 MAX-ACCESS read-only STATUS current DESCRIPTION "The SNMP versions that the agent processes. The following bits are defined: 0x00000001 - SNMPv1 0x00000002 - SNMPv2c 0x00000004 - SNMPv3" DEFVAL { 3 } ::= { begemotSnmpdConfig 5 } -- -- Trap destinations -- begemotTrapSinkTable OBJECT-TYPE SYNTAX SEQUENCE OF BegemotTrapSinkEntry MAX-ACCESS not-accessible STATUS current DESCRIPTION "A table with destinations for standard traps." INDEX { begemotTrapSinkAddr, begemotTrapSinkPort } ::= { begemotSnmpdObjects 2 } begemotTrapSinkEntry OBJECT-TYPE SYNTAX BegemotTrapSinkEntry MAX-ACCESS not-accessible STATUS current DESCRIPTION "Entry describes one trap destination." INDEX { begemotTrapSinkAddr, begemotTrapSinkPort } ::= { begemotTrapSinkTable 1 } BegemotTrapSinkEntry ::= SEQUENCE { begemotTrapSinkAddr IpAddress, begemotTrapSinkPort INTEGER, begemotTrapSinkStatus RowStatus } begemotTrapSinkAddr OBJECT-TYPE SYNTAX IpAddress MAX-ACCESS not-accessible STATUS current DESCRIPTION "Destination IP address of the manager station where to send traps." ::= { begemotTrapSinkEntry 1 } begemotTrapSinkPort OBJECT-TYPE SYNTAX INTEGER (1..65535) MAX-ACCESS not-accessible STATUS current DESCRIPTION "Destination UDP port of the manager station where to send traps." ::= { begemotTrapSinkEntry 2 } begemotTrapSinkStatus OBJECT-TYPE SYNTAX RowStatus MAX-ACCESS read-create STATUS current DESCRIPTION "Used to create/activate/destroy the entry." ::= { begemotTrapSinkEntry 3 } -- -- SNMP port table -- begemotSnmpdPortTable OBJECT-TYPE SYNTAX SEQUENCE OF BegemotSnmpdPortEntry MAX-ACCESS not-accessible STATUS current DESCRIPTION "A table with descriptions of UDP ports to listen on for SNMP messages." ::= { begemotSnmpdObjects 4 } begemotSnmpdPortEntry OBJECT-TYPE SYNTAX BegemotSnmpdPortEntry MAX-ACCESS not-accessible STATUS current DESCRIPTION "An entry in the table with descriptions of UDP ports to listen on for SNMP messages." INDEX { begemotSnmpdPortAddress, begemotSnmpdPortPort } ::= { begemotSnmpdPortTable 1 } BegemotSnmpdPortEntry ::= SEQUENCE { begemotSnmpdPortAddress IpAddress, begemotSnmpdPortPort INTEGER, begemotSnmpdPortStatus INTEGER } begemotSnmpdPortAddress OBJECT-TYPE SYNTAX IpAddress MAX-ACCESS not-accessible STATUS current DESCRIPTION "The IP address to bind to." ::= { begemotSnmpdPortEntry 1 } begemotSnmpdPortPort OBJECT-TYPE SYNTAX INTEGER (1..65535) MAX-ACCESS not-accessible STATUS current DESCRIPTION "The UDP port to listen on for SNMP messages." ::= { begemotSnmpdPortEntry 2 } begemotSnmpdPortStatus OBJECT-TYPE SYNTAX INTEGER { valid(1), invalid(2) } MAX-ACCESS read-create STATUS current DESCRIPTION "Set status to 1 to create entry, set it to 2 to delete it." ::= { begemotSnmpdPortEntry 3 } --- --- Community table --- begemotSnmpdCommunityTable OBJECT-TYPE SYNTAX SEQUENCE OF BegemotSnmpdCommunityEntry MAX-ACCESS not-accessible STATUS current DESCRIPTION "A table with the community strings for access control." ::= { begemotSnmpdObjects 5 } begemotSnmpdCommunityEntry OBJECT-TYPE SYNTAX BegemotSnmpdCommunityEntry MAX-ACCESS not-accessible STATUS current DESCRIPTION "A table with the community strings for access control. When begemotSnmpdCommDisable is true, this table disappears." INDEX { begemotSnmpdCommunityModule, begemotSnmpdCommunityIndex } ::= { begemotSnmpdCommunityTable 1 } BegemotSnmpdCommunityEntry ::= SEQUENCE { begemotSnmpdCommunityModule SectionName, begemotSnmpdCommunityIndex Unsigned32, begemotSnmpdCommunityString OCTET STRING, begemotSnmpdCommunityDescr OCTET STRING } begemotSnmpdCommunityModule OBJECT-TYPE SYNTAX SectionName MAX-ACCESS not-accessible STATUS current DESCRIPTION "Index of the module that has registered this community. For global communities this is the empty string." ::= { begemotSnmpdCommunityEntry 1 } begemotSnmpdCommunityIndex OBJECT-TYPE SYNTAX Unsigned32 (1..4294967295) MAX-ACCESS not-accessible STATUS current DESCRIPTION "The numerical index of the community (private to the module)." ::= { begemotSnmpdCommunityEntry 2 } begemotSnmpdCommunityString OBJECT-TYPE SYNTAX OCTET STRING MAX-ACCESS read-write STATUS current DESCRIPTION "The string for access to SNMPd." ::= { begemotSnmpdCommunityEntry 3 } begemotSnmpdCommunityDescr OBJECT-TYPE SYNTAX OCTET STRING MAX-ACCESS read-only STATUS current DESCRIPTION "A description what this community is good for." ::= { begemotSnmpdCommunityEntry 4 } -- -- Module table -- begemotSnmpdModuleTable OBJECT-TYPE SYNTAX SEQUENCE OF BegemotSnmpdModuleEntry MAX-ACCESS not-accessible STATUS current DESCRIPTION "A table describing all the currently loaded dynamic modules. Writing to this table loads and unloads modules." ::= { begemotSnmpdObjects 6 } begemotSnmpdModuleEntry OBJECT-TYPE SYNTAX BegemotSnmpdModuleEntry MAX-ACCESS not-accessible STATUS current DESCRIPTION "A table entry describing a loadable module." INDEX { begemotSnmpdModuleSection } ::= { begemotSnmpdModuleTable 1 } BegemotSnmpdModuleEntry ::= SEQUENCE { begemotSnmpdModuleSection SectionName, begemotSnmpdModulePath OCTET STRING, begemotSnmpdModuleComment OCTET STRING } begemotSnmpdModuleSection OBJECT-TYPE SYNTAX SectionName MAX-ACCESS not-accessible STATUS current DESCRIPTION "The string used for matching configuration file sections and indexes the module table." ::= { begemotSnmpdModuleEntry 1 } begemotSnmpdModulePath OBJECT-TYPE SYNTAX OCTET STRING MAX-ACCESS read-create STATUS current DESCRIPTION "The path name of the module. Set to empty string to unload a module. The path of an existing module may not be changed." ::= { begemotSnmpdModuleEntry 2 } begemotSnmpdModuleComment OBJECT-TYPE SYNTAX OCTET STRING MAX-ACCESS read-only STATUS current DESCRIPTION "A comment describing this module." ::= { begemotSnmpdModuleEntry 3 } -- -------------------------------------------------------------------------- -- -- The STATISTICS Group -- begemotSnmpdStats OBJECT IDENTIFIER ::= { begemotSnmpdObjects 7 } begemotSnmpdStatsNoRxBufs OBJECT-TYPE SYNTAX Counter32 MAX-ACCESS read-only STATUS current DESCRIPTION "Number of times a receive buffer could not be allocated for a packet." ::= { begemotSnmpdStats 1 } begemotSnmpdStatsNoTxBufs OBJECT-TYPE SYNTAX Counter32 MAX-ACCESS read-only STATUS current DESCRIPTION "Number of times a transmit buffer could not be allocated for a packet." ::= { begemotSnmpdStats 2 } begemotSnmpdStatsInTooLongPkts OBJECT-TYPE SYNTAX Counter32 MAX-ACCESS read-only STATUS current DESCRIPTION "Number of packets received that were longer than the receive buffer. These packets are dropped." ::= { begemotSnmpdStats 3 } begemotSnmpdStatsInBadPduTypes OBJECT-TYPE SYNTAX Counter32 MAX-ACCESS read-only STATUS current DESCRIPTION "Number of packets received with a bad type field." ::= { begemotSnmpdStats 4 } -- -- The Debug Group -- begemotSnmpdDebug OBJECT IDENTIFIER ::= { begemotSnmpdObjects 8 } begemotSnmpdDebugDumpPdus OBJECT-TYPE SYNTAX TruthValue MAX-ACCESS read-write STATUS current DESCRIPTION "Dump PDUs to log file if true." DEFVAL { false } ::= { begemotSnmpdDebug 1 } begemotSnmpdDebugSnmpTrace OBJECT-TYPE SYNTAX Unsigned32 MAX-ACCESS read-write STATUS current DESCRIPTION "Tracing flags for the SNMP library. These flags have the following meaning: 0x00000001 trace GET operator 0x00000002 trace GETNEXT operator 0x00000004 trace SET operator 0x00000008 trace dependency processing 0x00000010 trace node finding 0x10000000 log ASN1 errors 0x20000000 log SNMP errors Individual values can be or-ed together." DEFVAL { 0 } ::= { begemotSnmpdDebug 2 } begemotSnmpdDebugSyslogPri OBJECT-TYPE SYNTAX INTEGER (0..8) MAX-ACCESS read-write STATUS current DESCRIPTION "Events with this or higher priority should not be logged." DEFVAL { 7 } -- don't log debug messages ::= { begemotSnmpdDebug 3 } -- -- Local port table -- begemotSnmpdLocalPortTable OBJECT-TYPE SYNTAX SEQUENCE OF BegemotSnmpdLocalPortEntry MAX-ACCESS not-accessible STATUS current DESCRIPTION "A table with descriptions of local (unix domain) ports to listen on for SNMP messages." ::= { begemotSnmpdObjects 9 } begemotSnmpdLocalPortEntry OBJECT-TYPE SYNTAX BegemotSnmpdLocalPortEntry MAX-ACCESS not-accessible STATUS current DESCRIPTION "An entry in the table with descriptions of local ports to listen on for SNMP messages." INDEX { begemotSnmpdLocalPortPath } ::= { begemotSnmpdLocalPortTable 1 } BegemotSnmpdLocalPortEntry ::= SEQUENCE { begemotSnmpdLocalPortPath OCTET STRING, begemotSnmpdLocalPortStatus INTEGER, begemotSnmpdLocalPortType INTEGER } begemotSnmpdLocalPortPath OBJECT-TYPE SYNTAX OCTET STRING (SIZE(1..104)) MAX-ACCESS not-accessible STATUS current DESCRIPTION "The path name to create and listen on." ::= { begemotSnmpdLocalPortEntry 1 } begemotSnmpdLocalPortStatus OBJECT-TYPE SYNTAX INTEGER { valid(1), invalid(2) } MAX-ACCESS read-create STATUS current DESCRIPTION "Set status to 1 to create entry, set it to 2 to delete it." ::= { begemotSnmpdLocalPortEntry 2 } begemotSnmpdLocalPortType OBJECT-TYPE SYNTAX INTEGER { dgram-unpriv(1), dgram-priv(2), stream-unpriv(3), stream-priv(4) } MAX-ACCESS read-create STATUS current DESCRIPTION "Type of the port. If the type is unpriv SET operations are allowed from all clients if the community matches. For priv SET operations are allowed only from peers with uid zero. If the daemon cannot determine the peer uid it disallows the SET operation for -priv ports." ::= { begemotSnmpdLocalPortEntry 3 } -- -- Transport mapping table -- begemotSnmpdTransportMappings OBJECT IDENTIFIER ::= { begemotSnmpdObjects 10 } begemotSnmpdTransportTable OBJECT-TYPE SYNTAX SEQUENCE OF BegemotSnmpdTransportEntry MAX-ACCESS not-accessible STATUS current DESCRIPTION "A table containing all the currently loaded transport mappings." ::= { begemotSnmpdTransportMappings 1 } begemotSnmpdTransportEntry OBJECT-TYPE SYNTAX BegemotSnmpdTransportEntry MAX-ACCESS not-accessible STATUS current DESCRIPTION "An entry in the table with the transport mappings." INDEX { begemotSnmpdTransportName } ::= { begemotSnmpdTransportTable 1 } BegemotSnmpdTransportEntry ::= SEQUENCE { begemotSnmpdTransportName OCTET STRING, begemotSnmpdTransportStatus INTEGER, begemotSnmpdTransportOid OBJECT IDENTIFIER } begemotSnmpdTransportName OBJECT-TYPE SYNTAX OCTET STRING (SIZE(1..256)) MAX-ACCESS not-accessible STATUS current DESCRIPTION "The name of the mapping." ::= { begemotSnmpdTransportEntry 1 } begemotSnmpdTransportStatus OBJECT-TYPE SYNTAX RowStatus MAX-ACCESS read-only STATUS current DESCRIPTION "Used to create/activate/destroy the entry." ::= { begemotSnmpdTransportEntry 2 } begemotSnmpdTransportOid OBJECT-TYPE SYNTAX OBJECT IDENTIFIER MAX-ACCESS read-only STATUS current DESCRIPTION "A pointer to the group with the transport-dependend stuff." ::= { begemotSnmpdTransportEntry 3 } -- -- XXX These should go into their own MIB -- begemotSnmpdTransUdp OBJECT IDENTIFIER ::= { begemotSnmpdTransportMappings 2 } begemotSnmpdTransLsock OBJECT IDENTIFIER ::= { begemotSnmpdTransportMappings 3 } END Index: head/contrib/bsnmp/snmpd/FOKUS-MIB.txt =================================================================== --- head/contrib/bsnmp/snmpd/FOKUS-MIB.txt (revision 310647) +++ head/contrib/bsnmp/snmpd/FOKUS-MIB.txt (revision 310648) @@ -1,57 +1,57 @@ -- -- 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/FOKUS-MIB.txt,v 1.5 2004/08/06 08:47:08 brandt Exp $ -- -- Begemot private definitions and fokus root. -- FOKUS-MIB DEFINITIONS ::= BEGIN IMPORTS MODULE-IDENTITY, enterprises FROM SNMPv2-SMI; fokus MODULE-IDENTITY LAST-UPDATED "200202050000Z" ORGANIZATION "Fraunhofer FOKUS, CATS" CONTACT-INFO " Hartmut Brandt Postal: Fraunhofer Institute for Open Communication Systems Kaiserin-Augusta-Allee 31 10589 Berlin Germany Fax: +49 30 3463 7352 E-mail: harti@freebsd.org" DESCRIPTION "The root of the Fokus enterprises tree." ::= { enterprises 12325 } END Index: head/contrib/bsnmp/snmpd/action.c =================================================================== --- head/contrib/bsnmp/snmpd/action.c (revision 310647) +++ head/contrib/bsnmp/snmpd/action.c (revision 310648) @@ -1,1224 +1,1224 @@ /* * Copyright (c) 2001-2003 * Fraunhofer Institute for Open Communication Systems (FhG Fokus). * All rights reserved. * Copyright (c) 2004-2006 * 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: action.c 517 2006-10-31 08:52:04Z brandt_h $ * * Variable access for SNMPd */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "snmpmod.h" #include "snmpd.h" #include "tree.h" #include "oid.h" static const struct asn_oid oid_begemotSnmpdModuleTable = OIDX_begemotSnmpdModuleTable; #ifdef __FreeBSD__ static const struct asn_oid oid_freeBSDVersion = OIDX_freeBSDVersion; #endif /* * Get an integer value from the KERN sysctl subtree. */ static char * act_getkernint(int id) { int mib[2]; size_t len; u_long value; char *string; mib[0] = CTL_KERN; mib[1] = id; len = sizeof(value); if (sysctl(mib, 2, &value, &len, NULL, 0) != 0) return (NULL); if ((string = malloc(20)) == NULL) return (NULL); sprintf(string, "%lu", value); return (string); } /* * Initialize global variables of the system group. */ int init_actvals(void) { struct utsname uts; char *hostid; size_t len; #ifdef __FreeBSD__ char *rel, *p, *end; u_long num; #endif if (uname(&uts) == -1) return (-1); if ((systemg.name = strdup(uts.nodename)) == NULL) return (-1); if ((hostid = act_getkernint(KERN_HOSTID)) == NULL) return (-1); len = strlen(uts.nodename) + 1; len += strlen(hostid) + 1; len += strlen(uts.sysname) + 1; len += strlen(uts.release) + 1; if ((systemg.descr = malloc(len)) == NULL) { free(hostid); return (-1); } sprintf(systemg.descr, "%s %s %s %s", uts.nodename, hostid, uts.sysname, uts.release); #ifdef __FreeBSD__ /* * Construct a FreeBSD oid */ systemg.object_id = oid_freeBSDVersion; rel = uts.release; while ((p = strsep(&rel, ".")) != NULL && systemg.object_id.len < ASN_MAXOIDLEN) { systemg.object_id.subs[systemg.object_id.len] = 0; if (*p != '\0') { num = strtoul(p, &end, 10); if (end == p) break; systemg.object_id.subs[systemg.object_id.len] = num; } systemg.object_id.len++; } #endif free(hostid); return (0); } /* * Initialize global variables of the snmpEngine group. */ int init_snmpd_engine(void) { char *hostid; snmpd_engine.engine_boots = 1; snmpd_engine.engine_time = 1; snmpd_engine.max_msg_size = 1500; /* XXX */ snmpd_engine.engine_id[0] = ((OID_freeBSD & 0xff000000) >> 24) | 0x80; snmpd_engine.engine_id[1] = (OID_freeBSD & 0xff0000) >> 16; snmpd_engine.engine_id[2] = (OID_freeBSD & 0xff00) >> 8; snmpd_engine.engine_id[3] = OID_freeBSD & 0xff; snmpd_engine.engine_id[4] = 128; snmpd_engine.engine_len = 5; if ((hostid = act_getkernint(KERN_HOSTID)) == NULL) return (-1); if (strlen(hostid) > SNMP_ENGINE_ID_SIZ - snmpd_engine.engine_len) { memcpy(snmpd_engine.engine_id + snmpd_engine.engine_len, hostid, SNMP_ENGINE_ID_SIZ - snmpd_engine.engine_len); snmpd_engine.engine_len = SNMP_ENGINE_ID_SIZ; } else { memcpy(snmpd_engine.engine_id + snmpd_engine.engine_len, hostid, strlen(hostid)); - snmpd_engine.engine_len += strlen(hostid); + snmpd_engine.engine_len += strlen(hostid); } free(hostid); return (0); } int set_snmpd_engine(void) { FILE *fp; uint32_t i; uint8_t *cptr, engine[2 * SNMP_ENGINE_ID_SIZ + 2]; uint8_t myengine[2 * SNMP_ENGINE_ID_SIZ + 2]; if (engine_file[0] == '\0') return (-1); cptr = myengine; for (i = 0; i < snmpd_engine.engine_len; i++) cptr += sprintf(cptr, "%.2x", snmpd_engine.engine_id[i]); *cptr++ = '\n'; *cptr++ = '\0'; if ((fp = fopen(engine_file, "r+")) != NULL) { if (fgets(engine, sizeof(engine) - 1, fp) == NULL || fscanf(fp, "%u", &snmpd_engine.engine_boots) <= 0) { fclose(fp); goto save_boots; } fclose(fp); if (strcmp(myengine, engine) != 0) snmpd_engine.engine_boots = 1; else snmpd_engine.engine_boots++; } else if (errno != ENOENT) return (-1); save_boots: if ((fp = fopen(engine_file, "w+")) == NULL) return (-1); fprintf(fp, "%s%u\n", myengine, snmpd_engine.engine_boots); fclose(fp); return (0); } void update_snmpd_engine_time(void) { uint64_t etime; etime = (get_ticks() - start_tick) / 100ULL; if (etime < INT32_MAX) snmpd_engine.engine_time = etime; else { start_tick = get_ticks(); (void)set_snmpd_engine(); snmpd_engine.engine_time = start_tick; } } /************************************************************* * * System group */ int op_system_group(struct snmp_context *ctx, struct snmp_value *value, u_int sub, u_int iidx __unused, enum snmp_op op) { asn_subid_t which = value->var.subs[sub - 1]; switch (op) { case SNMP_OP_GETNEXT: abort(); case SNMP_OP_GET: break; case SNMP_OP_SET: switch (which) { case LEAF_sysDescr: if (community != COMM_INITIALIZE) return (SNMP_ERR_NOT_WRITEABLE); return (string_save(value, ctx, -1, &systemg.descr)); case LEAF_sysObjectId: if (community != COMM_INITIALIZE) return (SNMP_ERR_NOT_WRITEABLE); return (oid_save(value, ctx, &systemg.object_id)); case LEAF_sysContact: return (string_save(value, ctx, -1, &systemg.contact)); case LEAF_sysName: return (string_save(value, ctx, -1, &systemg.name)); case LEAF_sysLocation: return (string_save(value, ctx, -1, &systemg.location)); } return (SNMP_ERR_NO_CREATION); case SNMP_OP_ROLLBACK: switch (which) { case LEAF_sysDescr: string_rollback(ctx, &systemg.descr); return (SNMP_ERR_NOERROR); case LEAF_sysObjectId: oid_rollback(ctx, &systemg.object_id); return (SNMP_ERR_NOERROR); case LEAF_sysContact: string_rollback(ctx, &systemg.contact); return (SNMP_ERR_NOERROR); case LEAF_sysName: string_rollback(ctx, &systemg.name); return (SNMP_ERR_NOERROR); case LEAF_sysLocation: string_rollback(ctx, &systemg.location); return (SNMP_ERR_NOERROR); } abort(); case SNMP_OP_COMMIT: switch (which) { case LEAF_sysDescr: string_commit(ctx); return (SNMP_ERR_NOERROR); case LEAF_sysObjectId: oid_commit(ctx); return (SNMP_ERR_NOERROR); case LEAF_sysContact: string_commit(ctx); return (SNMP_ERR_NOERROR); case LEAF_sysName: string_commit(ctx); return (SNMP_ERR_NOERROR); case LEAF_sysLocation: string_commit(ctx); return (SNMP_ERR_NOERROR); } abort(); } /* * Come here for GET. */ switch (which) { case LEAF_sysDescr: return (string_get(value, systemg.descr, -1)); case LEAF_sysObjectId: return (oid_get(value, &systemg.object_id)); case LEAF_sysUpTime: value->v.uint32 = get_ticks() - start_tick; break; case LEAF_sysContact: return (string_get(value, systemg.contact, -1)); case LEAF_sysName: return (string_get(value, systemg.name, -1)); case LEAF_sysLocation: return (string_get(value, systemg.location, -1)); case LEAF_sysServices: value->v.integer = systemg.services; break; case LEAF_sysORLastChange: value->v.uint32 = systemg.or_last_change; break; } return (SNMP_ERR_NOERROR); } /************************************************************* * * Debug group */ int op_debug(struct snmp_context *ctx, struct snmp_value *value, u_int sub, u_int iidx __unused, enum snmp_op op) { asn_subid_t which = value->var.subs[sub - 1]; switch (op) { case SNMP_OP_GETNEXT: abort(); case SNMP_OP_GET: switch (which) { case LEAF_begemotSnmpdDebugDumpPdus: value->v.integer = TRUTH_MK(debug.dump_pdus); break; case LEAF_begemotSnmpdDebugSnmpTrace: value->v.uint32 = snmp_trace; break; case LEAF_begemotSnmpdDebugSyslogPri: value->v.integer = debug.logpri; break; } return (SNMP_ERR_NOERROR); case SNMP_OP_SET: switch (which) { case LEAF_begemotSnmpdDebugDumpPdus: if (!TRUTH_OK(value->v.integer)) return (SNMP_ERR_WRONG_VALUE); ctx->scratch->int1 = debug.dump_pdus; debug.dump_pdus = TRUTH_GET(value->v.integer); return (SNMP_ERR_NOERROR); case LEAF_begemotSnmpdDebugSnmpTrace: ctx->scratch->int1 = snmp_trace; snmp_trace = value->v.uint32; return (SNMP_ERR_NOERROR); case LEAF_begemotSnmpdDebugSyslogPri: if (value->v.integer < 0 || value->v.integer > 8) return (SNMP_ERR_WRONG_VALUE); ctx->scratch->int1 = debug.logpri; debug.logpri = (u_int)value->v.integer; return (SNMP_ERR_NOERROR); } return (SNMP_ERR_NO_CREATION); case SNMP_OP_ROLLBACK: switch (which) { case LEAF_begemotSnmpdDebugDumpPdus: debug.dump_pdus = ctx->scratch->int1; return (SNMP_ERR_NOERROR); case LEAF_begemotSnmpdDebugSnmpTrace: snmp_trace = ctx->scratch->int1; return (SNMP_ERR_NOERROR); case LEAF_begemotSnmpdDebugSyslogPri: debug.logpri = ctx->scratch->int1; return (SNMP_ERR_NOERROR); } abort(); case SNMP_OP_COMMIT: switch (which) { case LEAF_begemotSnmpdDebugDumpPdus: case LEAF_begemotSnmpdDebugSnmpTrace: return (SNMP_ERR_NOERROR); case LEAF_begemotSnmpdDebugSyslogPri: if (debug.logpri == 0) setlogmask(0); else setlogmask(LOG_UPTO(debug.logpri - 1)); return (SNMP_ERR_NOERROR); } abort(); } abort(); } /************************************************************* * * OR Table */ int op_or_table(struct snmp_context *ctx __unused, struct snmp_value *value, u_int sub, u_int iidx __unused, enum snmp_op op) { struct objres *objres; switch (op) { case SNMP_OP_GETNEXT: if ((objres = NEXT_OBJECT_INT(&objres_list, &value->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); value->var.subs[sub] = objres->index; value->var.len = sub + 1; break; case SNMP_OP_GET: if ((objres = FIND_OBJECT_INT(&objres_list, &value->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_SET: if ((objres = FIND_OBJECT_INT(&objres_list, &value->var, sub)) == NULL) return (SNMP_ERR_NO_CREATION); return (SNMP_ERR_NOT_WRITEABLE); case SNMP_OP_ROLLBACK: case SNMP_OP_COMMIT: default: abort(); } /* * Come here for GET, GETNEXT. */ switch (value->var.subs[sub - 1]) { case LEAF_sysORID: value->v.oid = objres->oid; break; case LEAF_sysORDescr: return (string_get(value, objres->descr, -1)); case LEAF_sysORUpTime: value->v.uint32 = objres->uptime; break; } return (SNMP_ERR_NOERROR); } /************************************************************* * * mib-2 snmp */ int op_snmp(struct snmp_context *ctx, struct snmp_value *value, u_int sub, u_int iidx __unused, enum snmp_op op) { switch (op) { case SNMP_OP_GETNEXT: abort(); case SNMP_OP_GET: switch (value->var.subs[sub - 1]) { case LEAF_snmpInPkts: value->v.uint32 = snmpd_stats.inPkts; break; case LEAF_snmpInBadVersions: value->v.uint32 = snmpd_stats.inBadVersions; break; case LEAF_snmpInBadCommunityNames: value->v.uint32 = snmpd_stats.inBadCommunityNames; break; case LEAF_snmpInBadCommunityUses: value->v.uint32 = snmpd_stats.inBadCommunityUses; break; case LEAF_snmpInASNParseErrs: value->v.uint32 = snmpd_stats.inASNParseErrs; break; case LEAF_snmpEnableAuthenTraps: value->v.integer = TRUTH_MK(snmpd.auth_traps); break; case LEAF_snmpSilentDrops: value->v.uint32 = snmpd_stats.silentDrops; break; case LEAF_snmpProxyDrops: value->v.uint32 = snmpd_stats.proxyDrops; break; default: return (SNMP_ERR_NOSUCHNAME); } return (SNMP_ERR_NOERROR); case SNMP_OP_SET: switch (value->var.subs[sub - 1]) { case LEAF_snmpEnableAuthenTraps: if (!TRUTH_OK(value->v.integer)) return (SNMP_ERR_WRONG_VALUE); ctx->scratch->int1 = value->v.integer; snmpd.auth_traps = TRUTH_GET(value->v.integer); return (SNMP_ERR_NOERROR); } abort(); case SNMP_OP_ROLLBACK: switch (value->var.subs[sub - 1]) { case LEAF_snmpEnableAuthenTraps: snmpd.auth_traps = ctx->scratch->int1; return (SNMP_ERR_NOERROR); } abort(); case SNMP_OP_COMMIT: switch (value->var.subs[sub - 1]) { case LEAF_snmpEnableAuthenTraps: return (SNMP_ERR_NOERROR); } abort(); } abort(); } /************************************************************* * * SNMPd statistics group */ int op_snmpd_stats(struct snmp_context *ctx __unused, struct snmp_value *value, u_int sub, u_int iidx __unused, enum snmp_op op) { switch (op) { case SNMP_OP_GET: switch (value->var.subs[sub - 1]) { case LEAF_begemotSnmpdStatsNoRxBufs: value->v.uint32 = snmpd_stats.noRxbuf; break; case LEAF_begemotSnmpdStatsNoTxBufs: value->v.uint32 = snmpd_stats.noTxbuf; break; case LEAF_begemotSnmpdStatsInTooLongPkts: value->v.uint32 = snmpd_stats.inTooLong; break; case LEAF_begemotSnmpdStatsInBadPduTypes: value->v.uint32 = snmpd_stats.inBadPduTypes; break; default: return (SNMP_ERR_NOSUCHNAME); } return (SNMP_ERR_NOERROR); case SNMP_OP_SET: case SNMP_OP_ROLLBACK: case SNMP_OP_COMMIT: case SNMP_OP_GETNEXT: abort(); } abort(); } /* * SNMPd configuration scalars */ int op_snmpd_config(struct snmp_context *ctx, struct snmp_value *value, u_int sub, u_int iidx __unused, enum snmp_op op) { asn_subid_t which = value->var.subs[sub - 1]; switch (op) { case SNMP_OP_GETNEXT: abort(); case SNMP_OP_GET: switch (which) { case LEAF_begemotSnmpdTransmitBuffer: value->v.integer = snmpd.txbuf; break; case LEAF_begemotSnmpdReceiveBuffer: value->v.integer = snmpd.rxbuf; break; case LEAF_begemotSnmpdCommunityDisable: value->v.integer = TRUTH_MK(snmpd.comm_dis); break; case LEAF_begemotSnmpdTrap1Addr: return (ip_get(value, snmpd.trap1addr)); case LEAF_begemotSnmpdVersionEnable: value->v.uint32 = snmpd.version_enable; break; default: return (SNMP_ERR_NOSUCHNAME); } return (SNMP_ERR_NOERROR); case SNMP_OP_SET: switch (which) { case LEAF_begemotSnmpdTransmitBuffer: ctx->scratch->int1 = snmpd.txbuf; if (value->v.integer < 484 || value->v.integer > 65535) return (SNMP_ERR_WRONG_VALUE); snmpd.txbuf = value->v.integer; return (SNMP_ERR_NOERROR); case LEAF_begemotSnmpdReceiveBuffer: ctx->scratch->int1 = snmpd.rxbuf; if (value->v.integer < 484 || value->v.integer > 65535) return (SNMP_ERR_WRONG_VALUE); snmpd.rxbuf = value->v.integer; return (SNMP_ERR_NOERROR); case LEAF_begemotSnmpdCommunityDisable: ctx->scratch->int1 = snmpd.comm_dis; if (!TRUTH_OK(value->v.integer)) return (SNMP_ERR_WRONG_VALUE); if (TRUTH_GET(value->v.integer)) { snmpd.comm_dis = 1; } else { if (snmpd.comm_dis) return (SNMP_ERR_WRONG_VALUE); } return (SNMP_ERR_NOERROR); case LEAF_begemotSnmpdTrap1Addr: return (ip_save(value, ctx, snmpd.trap1addr)); case LEAF_begemotSnmpdVersionEnable: if (community != COMM_INITIALIZE) return (SNMP_ERR_NOT_WRITEABLE); ctx->scratch->int1 = snmpd.version_enable; if (value->v.uint32 == 0 || (value->v.uint32 & ~VERS_ENABLE_ALL)) return (SNMP_ERR_WRONG_VALUE); snmpd.version_enable = value->v.uint32; return (SNMP_ERR_NOERROR); } abort(); case SNMP_OP_ROLLBACK: switch (which) { case LEAF_begemotSnmpdTransmitBuffer: snmpd.rxbuf = ctx->scratch->int1; return (SNMP_ERR_NOERROR); case LEAF_begemotSnmpdReceiveBuffer: snmpd.txbuf = ctx->scratch->int1; return (SNMP_ERR_NOERROR); case LEAF_begemotSnmpdCommunityDisable: snmpd.comm_dis = ctx->scratch->int1; return (SNMP_ERR_NOERROR); case LEAF_begemotSnmpdTrap1Addr: ip_rollback(ctx, snmpd.trap1addr); return (SNMP_ERR_NOERROR); case LEAF_begemotSnmpdVersionEnable: snmpd.version_enable = ctx->scratch->int1; return (SNMP_ERR_NOERROR); } abort(); case SNMP_OP_COMMIT: switch (which) { case LEAF_begemotSnmpdTransmitBuffer: case LEAF_begemotSnmpdReceiveBuffer: case LEAF_begemotSnmpdCommunityDisable: return (SNMP_ERR_NOERROR); case LEAF_begemotSnmpdTrap1Addr: ip_commit(ctx); return (SNMP_ERR_NOERROR); case LEAF_begemotSnmpdVersionEnable: return (SNMP_ERR_NOERROR); } abort(); } abort(); } /* * The community table */ int op_community(struct snmp_context *ctx, struct snmp_value *value, u_int sub, u_int iidx __unused, enum snmp_op op) { asn_subid_t which = value->var.subs[sub - 1]; struct community *c; switch (op) { case SNMP_OP_GETNEXT: if ((community != COMM_INITIALIZE && snmpd.comm_dis) || (c = NEXT_OBJECT_OID(&community_list, &value->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); index_append(&value->var, sub, &c->index); break; case SNMP_OP_GET: if ((community != COMM_INITIALIZE && snmpd.comm_dis) || (c = FIND_OBJECT_OID(&community_list, &value->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_SET: if ((community != COMM_INITIALIZE && snmpd.comm_dis) || (c = FIND_OBJECT_OID(&community_list, &value->var, sub)) == NULL) return (SNMP_ERR_NO_CREATION); if (which != LEAF_begemotSnmpdCommunityString) return (SNMP_ERR_NOT_WRITEABLE); return (string_save(value, ctx, -1, &c->string)); case SNMP_OP_ROLLBACK: if (which == LEAF_begemotSnmpdCommunityString) { if ((c = FIND_OBJECT_OID(&community_list, &value->var, sub)) == NULL) string_free(ctx); else string_rollback(ctx, &c->string); return (SNMP_ERR_NOERROR); } abort(); case SNMP_OP_COMMIT: if (which == LEAF_begemotSnmpdCommunityString) { if ((c = FIND_OBJECT_OID(&community_list, &value->var, sub)) == NULL) string_free(ctx); else string_commit(ctx); return (SNMP_ERR_NOERROR); } abort(); default: abort(); } switch (which) { case LEAF_begemotSnmpdCommunityString: return (string_get(value, c->string, -1)); case LEAF_begemotSnmpdCommunityDescr: return (string_get(value, c->descr, -1)); } abort(); } /* * Module table. */ struct module_dep { struct snmp_dependency dep; u_char section[LM_SECTION_MAX + 1]; u_char *path; struct lmodule *m; }; static int dep_modules(struct snmp_context *ctx, struct snmp_dependency *dep, enum snmp_depop op) { struct module_dep *mdep = (struct module_dep *)(void *)dep; switch (op) { case SNMP_DEPOP_COMMIT: if (mdep->path == NULL) { /* unload - find the module */ TAILQ_FOREACH(mdep->m, &lmodules, link) if (strcmp(mdep->m->section, mdep->section) == 0) break; if (mdep->m == NULL) /* no such module - that's ok */ return (SNMP_ERR_NOERROR); /* handle unloading in the finalizer */ return (SNMP_ERR_NOERROR); } /* load */ if ((mdep->m = lm_load(mdep->path, mdep->section)) == NULL) { /* could not load */ return (SNMP_ERR_RES_UNAVAIL); } /* start in finalizer */ return (SNMP_ERR_NOERROR); case SNMP_DEPOP_ROLLBACK: if (mdep->path == NULL) { /* rollback unload - the finalizer takes care */ return (SNMP_ERR_NOERROR); } /* rollback load */ lm_unload(mdep->m); return (SNMP_ERR_NOERROR); case SNMP_DEPOP_FINISH: if (mdep->path == NULL) { if (mdep->m != NULL && ctx->code == SNMP_RET_OK) lm_unload(mdep->m); } else { if (mdep->m != NULL && ctx->code == SNMP_RET_OK && community != COMM_INITIALIZE) lm_start(mdep->m); free(mdep->path); } return (SNMP_ERR_NOERROR); } abort(); } int op_modules(struct snmp_context *ctx, struct snmp_value *value, u_int sub, u_int iidx, enum snmp_op op) { asn_subid_t which = value->var.subs[sub - 1]; struct lmodule *m; u_char *section, *ptr; size_t seclen; struct module_dep *mdep; struct asn_oid idx; switch (op) { case SNMP_OP_GETNEXT: if ((m = NEXT_OBJECT_OID(&lmodules, &value->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); index_append(&value->var, sub, &m->index); break; case SNMP_OP_GET: if ((m = FIND_OBJECT_OID(&lmodules, &value->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_SET: m = FIND_OBJECT_OID(&lmodules, &value->var, sub); if (which != LEAF_begemotSnmpdModulePath) { if (m == NULL) return (SNMP_ERR_NO_CREATION); return (SNMP_ERR_NOT_WRITEABLE); } /* the errors in the next few statements can only happen when * m is NULL, hence the NO_CREATION error. */ if (index_decode(&value->var, sub, iidx, §ion, &seclen)) return (SNMP_ERR_NO_CREATION); /* check the section name */ if (seclen > LM_SECTION_MAX || seclen == 0) { free(section); return (SNMP_ERR_NO_CREATION); } for (ptr = section; ptr < section + seclen; ptr++) if (!isascii(*ptr) || !isalnum(*ptr)) { free(section); return (SNMP_ERR_NO_CREATION); } if (!isalpha(section[0])) { free(section); return (SNMP_ERR_NO_CREATION); } /* check the path */ for (ptr = value->v.octetstring.octets; ptr < value->v.octetstring.octets + value->v.octetstring.len; ptr++) { if (*ptr == '\0') { free(section); return (SNMP_ERR_WRONG_VALUE); } } if (m == NULL) { if (value->v.octetstring.len == 0) { free(section); return (SNMP_ERR_INCONS_VALUE); } } else { if (value->v.octetstring.len != 0) { free(section); return (SNMP_ERR_INCONS_VALUE); } } asn_slice_oid(&idx, &value->var, sub, value->var.len); /* so far, so good */ mdep = (struct module_dep *)(void *)snmp_dep_lookup(ctx, &oid_begemotSnmpdModuleTable, &idx, sizeof(*mdep), dep_modules); if (mdep == NULL) { free(section); return (SNMP_ERR_RES_UNAVAIL); } if (mdep->section[0] != '\0') { /* two writes to the same entry - bad */ free(section); return (SNMP_ERR_INCONS_VALUE); } strncpy(mdep->section, section, seclen); mdep->section[seclen] = '\0'; free(section); if (value->v.octetstring.len == 0) mdep->path = NULL; else { if ((mdep->path = malloc(value->v.octetstring.len + 1)) == NULL) return (SNMP_ERR_RES_UNAVAIL); strncpy(mdep->path, value->v.octetstring.octets, value->v.octetstring.len); mdep->path[value->v.octetstring.len] = '\0'; } ctx->scratch->ptr1 = mdep; return (SNMP_ERR_NOERROR); case SNMP_OP_ROLLBACK: case SNMP_OP_COMMIT: return (SNMP_ERR_NOERROR); default: abort(); } switch (which) { case LEAF_begemotSnmpdModulePath: return (string_get(value, m->path, -1)); case LEAF_begemotSnmpdModuleComment: return (string_get(value, m->config->comment, -1)); } abort(); } int op_snmp_set(struct snmp_context *ctx __unused, struct snmp_value *value, u_int sub, u_int iidx __unused, enum snmp_op op) { switch (op) { case SNMP_OP_GETNEXT: abort(); case SNMP_OP_GET: switch (value->var.subs[sub - 1]) { case LEAF_snmpSetSerialNo: value->v.integer = snmp_serial_no; break; default: abort(); } return (SNMP_ERR_NOERROR); case SNMP_OP_SET: switch (value->var.subs[sub - 1]) { case LEAF_snmpSetSerialNo: if (value->v.integer != snmp_serial_no) return (SNMP_ERR_INCONS_VALUE); break; default: abort(); } return (SNMP_ERR_NOERROR); case SNMP_OP_ROLLBACK: return (SNMP_ERR_NOERROR); case SNMP_OP_COMMIT: if (snmp_serial_no++ == 2147483647) snmp_serial_no = 0; return (SNMP_ERR_NOERROR); } abort(); } /* * SNMP Engine */ int op_snmp_engine(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: break; case SNMP_OP_SET: if (community != COMM_INITIALIZE) return (SNMP_ERR_NOT_WRITEABLE); switch (which) { case LEAF_snmpEngineID: if (value->v.octetstring.len > SNMP_ENGINE_ID_SIZ) return (SNMP_ERR_WRONG_VALUE); ctx->scratch->ptr1 = malloc(snmpd_engine.engine_len); if (ctx->scratch->ptr1 == NULL) return (SNMP_ERR_GENERR); memcpy(ctx->scratch->ptr1, snmpd_engine.engine_id, snmpd_engine.engine_len); ctx->scratch->int1 = snmpd_engine.engine_len; snmpd_engine.engine_len = value->v.octetstring.len; memcpy(snmpd_engine.engine_id, value->v.octetstring.octets, value->v.octetstring.len); break; case LEAF_snmpEngineMaxMessageSize: ctx->scratch->int1 = snmpd_engine.max_msg_size; snmpd_engine.max_msg_size = value->v.integer; break; - + default: return (SNMP_ERR_NOT_WRITEABLE); } return (SNMP_ERR_NOERROR); case SNMP_OP_ROLLBACK: switch (which) { case LEAF_snmpEngineID: snmpd_engine.engine_len = ctx->scratch->int1; memcpy(snmpd_engine.engine_id, ctx->scratch->ptr1, snmpd_engine.engine_len); free(ctx->scratch->ptr1); break; case LEAF_snmpEngineMaxMessageSize: snmpd_engine.max_msg_size = ctx->scratch->int1; break; default: abort(); } return (SNMP_ERR_NOERROR); case SNMP_OP_COMMIT: if (which == LEAF_snmpEngineID) { if (set_snmpd_engine() < 0) { snmpd_engine.engine_len = ctx->scratch->int1; memcpy(snmpd_engine.engine_id, ctx->scratch->ptr1, ctx->scratch->int1); - } + } free(ctx->scratch->ptr1); } return (SNMP_ERR_NOERROR); } switch (which) { case LEAF_snmpEngineID: return (string_get(value, snmpd_engine.engine_id, snmpd_engine.engine_len)); case LEAF_snmpEngineBoots: value->v.integer = snmpd_engine.engine_boots; break; case LEAF_snmpEngineTime: update_snmpd_engine_time(); value->v.integer = snmpd_engine.engine_time; break; case LEAF_snmpEngineMaxMessageSize: value->v.integer = snmpd_engine.max_msg_size; break; default: return (SNMP_ERR_NOSUCHNAME); } return (SNMP_ERR_NOERROR); } /* * Transport table */ int op_transport_table(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]; struct transport *t; u_char *tname, *ptr; size_t tnamelen; switch (op) { case SNMP_OP_GETNEXT: if ((t = NEXT_OBJECT_OID(&transport_list, &value->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); index_append(&value->var, sub, &t->index); break; case SNMP_OP_GET: if ((t = FIND_OBJECT_OID(&transport_list, &value->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_SET: t = FIND_OBJECT_OID(&transport_list, &value->var, sub); if (which != LEAF_begemotSnmpdTransportStatus) { if (t == NULL) return (SNMP_ERR_NO_CREATION); return (SNMP_ERR_NOT_WRITEABLE); } /* the errors in the next few statements can only happen when * t is NULL, hence the NO_CREATION error. */ if (index_decode(&value->var, sub, iidx, &tname, &tnamelen)) return (SNMP_ERR_NO_CREATION); /* check the section name */ if (tnamelen >= TRANS_NAMELEN || tnamelen == 0) { free(tname); return (SNMP_ERR_NO_CREATION); } for (ptr = tname; ptr < tname + tnamelen; ptr++) { if (!isascii(*ptr) || !isalnum(*ptr)) { free(tname); return (SNMP_ERR_NO_CREATION); } } /* for now */ return (SNMP_ERR_NOT_WRITEABLE); case SNMP_OP_ROLLBACK: case SNMP_OP_COMMIT: return (SNMP_ERR_NOERROR); default: abort(); } switch (which) { case LEAF_begemotSnmpdTransportStatus: value->v.integer = 1; break; case LEAF_begemotSnmpdTransportOid: memcpy(&value->v.oid, &t->vtab->id, sizeof(t->vtab->id)); break; } return (SNMP_ERR_NOERROR); } Index: head/contrib/bsnmp/snmpd/config.c =================================================================== --- head/contrib/bsnmp/snmpd/config.c (revision 310647) +++ head/contrib/bsnmp/snmpd/config.c (revision 310648) @@ -1,1383 +1,1383 @@ /* * 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.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 #include "snmpmod.h" #include "snmpd.h" #include "tree.h" /* #define DEBUGGING */ /* * config_file: EMPTY | config_file line * * line: oid '=' value * | '%' STRING * | STRING := REST_OF_LINE * | STRING ?= REST_OF_LINE * | . INCLUDE STRING * * oid: STRING suboid * * suboid: EMPTY | suboid '.' subid * * subid: NUM | STRING | '[' STRING ']' * * value: EMPTY | STRING | NUM */ /* * Input context for macros and includes */ enum input_type { INPUT_FILE = 1, INPUT_STRING }; struct input { enum input_type type; union { struct { FILE *fp; char *filename; u_int lno; } file; struct { char *macro; char *str; char *ptr; size_t left; } str; } u; LIST_ENTRY(input) link; }; static LIST_HEAD(, input) inputs; #define input_fp u.file.fp #define input_filename u.file.filename #define input_lno u.file.lno #define input_macro u.str.macro #define input_str u.str.str #define input_ptr u.str.ptr #define input_left u.str.left static int input_push; static int input_buf[2]; /* * Configuration data. The configuration file is handled as one single * SNMP transaction. So we need to keep the assignment data for the * commit or rollback pass. Note, that dependencies and finish functions * are NOT allowed here. */ struct assign { struct snmp_value value; struct snmp_scratch scratch; const char *node_name; TAILQ_ENTRY(assign) link; }; static TAILQ_HEAD(assigns, assign) assigns = TAILQ_HEAD_INITIALIZER(assigns); static struct snmp_context *snmp_ctx; struct macro { char *name; char *value; size_t length; LIST_ENTRY(macro) link; int perm; }; static LIST_HEAD(, macro) macros = LIST_HEAD_INITIALIZER(macros); enum { TOK_EOF = 0200, TOK_EOL, TOK_NUM, TOK_STR, TOK_HOST, TOK_ASSIGN, TOK_QASSIGN, }; /* lexer values and last token */ static uint64_t numval; static char strval[_POSIX2_LINE_MAX]; static size_t strvallen; static int token; /* error return */ static jmp_buf errjmp[4]; static volatile int errstk; # define ERRPUSH() (setjmp(errjmp[errstk++])) # define ERRPOP() ((void)(errstk--)) # define ERRNEXT() (longjmp(errjmp[--errstk], 1)) # define ERR() (longjmp(errjmp[--errstk], 1)) /* section context */ static int ignore; /* * Report an error and jump to the error label */ static void report(const char *fmt, ...) __dead2 __printflike(1, 2); static void report(const char *fmt, ...) { va_list ap; const struct input *input; va_start(ap, fmt); vsyslog(LOG_ERR, fmt, ap); va_end(ap); LIST_FOREACH(input, &inputs, link) { switch (input->type) { case INPUT_FILE: syslog(LOG_ERR, " in file %s line %u", input->input_filename, input->input_lno); break; case INPUT_STRING: syslog(LOG_ERR, " in macro %s pos %td", input->input_macro, input->input_ptr - input->input_str); break; } } ERR(); } /* * Open a file for input */ static int input_open_file(const char *fname, int sysdir) { struct input *input; FILE *fp; char path[PATH_MAX + 1]; 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]; struct asn_oid str_oid; 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 %#jx", (uintmax_t)numval); if (oid->len == ASN_MAXOIDLEN) report("index too long"); if (gettoken() != ':') oid->subs[oid->len++] = numval; else { str_oid.len = 0; str_oid.subs[str_oid.len++] = numval; while (gettoken() == TOK_NUM) { str_oid.subs[str_oid.len++] = numval; if (gettoken() != ':') break; } oid->subs[oid->len++] = str_oid.len; asn_append_oid(oid, &str_oid); } } 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]; gettoken(); } 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]; gettoken(); } else report("bad token in index"); } 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 %ju", (uintmax_t)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, got '%c'", token); 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; } else free(string); } 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: head/contrib/bsnmp/snmpd/export.c =================================================================== --- head/contrib/bsnmp/snmpd/export.c (revision 310647) +++ head/contrib/bsnmp/snmpd/export.c (revision 310648) @@ -1,399 +1,399 @@ /* * 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.8 2006/02/14 09:04:20 brandt_h Exp $ * * Support functions for modules. */ #include #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: head/contrib/bsnmp/snmpd/main.c =================================================================== --- head/contrib/bsnmp/snmpd/main.c (revision 310647) +++ head/contrib/bsnmp/snmpd/main.c (revision 310648) @@ -1,3099 +1,3099 @@ /* * Copyright (c) 2001-2003 * Fraunhofer Institute for Open Communication Systems (FhG Fokus). * All rights reserved. * * Author: Harti Brandt - * + * * Copyright (c) 2010 The FreeBSD Foundation * All rights reserved. * * Portions of this software were developed by Shteryana Sotirova Shopova * under sponsorship from the FreeBSD Foundation. * * 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.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" #define PATH_PID "/var/run/%s.pid" #define PATH_CONFIG "/etc/%s.config" #define PATH_ENGINE "/var/%s.engine" 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; struct snmpd_usmstat snmpd_usmstats; /* snmpEngine */ struct snmp_engine snmpd_engine; /* snmpSerialNo */ int32_t snmp_serial_no; struct snmpd_target_stats snmpd_target_stats; /* 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 known USM users */ static struct usm_userlist usm_userlist = SLIST_HEAD_INITIALIZER(usm_userlist); /* A list of all VACM users configured, including v1, v2c and v3 */ static struct vacm_userlist vacm_userlist = SLIST_HEAD_INITIALIZER(vacm_userlist); /* A list of all VACM groups */ static struct vacm_grouplist vacm_grouplist = SLIST_HEAD_INITIALIZER(vacm_grouplist); static struct vacm_group vacm_default_group = { .groupname = "", }; /* The list of configured access entries */ static struct vacm_accesslist vacm_accesslist = TAILQ_HEAD_INITIALIZER(vacm_accesslist); /* The list of configured views */ static struct vacm_viewlist vacm_viewlist = SLIST_HEAD_INITIALIZER(vacm_viewlist); /* The list of configured contexts */ static struct vacm_contextlist vacm_contextlist = SLIST_HEAD_INITIALIZER(vacm_contextlist); /* 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; /* current USM user */ struct usm_user *usm_user; /* file names */ static char config_file[MAXPATHLEN + 1]; static char pid_file[MAXPATHLEN + 1]; char engine_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 }}; const struct asn_oid oid_usmUnknownEngineIDs = { 11, { 1, 3, 6, 1, 6, 3, 15, 1, 1, 4, 0}}; const struct asn_oid oid_usmNotInTimeWindows = { 11, { 1, 3, 6, 1, 6, 3, 15, 1, 1, 2, 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\ Copyright (c) 2010 The FreeBSD Foundation. All rights reserved.\n\ usage: snmpd [-dh] [-c file] [-D options] [-e file] [-I path]\n\ [-l prefix] [-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\ -e file specify engine id file\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; enum snmp_code code; resp_b.asn_ptr = sndbuf; resp_b.asn_len = snmpd.txbuf; if ((code = snmp_pdu_encode(pdu, &resp_b)) != SNMP_CODE_OK) { syslog(LOG_ERR, "cannot encode message (code=%d)", code); abort(); } if (debug.dump_pdus) { snmp_printf("%s <- ", dest); snmp_pdu_dump(pdu); } *sndlen = (size_t)(resp_b.asn_ptr - sndbuf); } /* * Check USM PDU header credentials against local SNMP Engine & users. */ static enum snmp_code snmp_pdu_auth_user(struct snmp_pdu *pdu) { usm_user = NULL; /* un-authenticated snmpEngineId discovery */ if (pdu->engine.engine_len == 0 && strlen(pdu->user.sec_name) == 0) { pdu->engine.engine_len = snmpd_engine.engine_len; memcpy(pdu->engine.engine_id, snmpd_engine.engine_id, snmpd_engine.engine_len); update_snmpd_engine_time(); pdu->engine.engine_boots = snmpd_engine.engine_boots; pdu->engine.engine_time = snmpd_engine.engine_time; pdu->flags |= SNMP_MSG_AUTODISCOVER; return (SNMP_CODE_OK); } if ((usm_user = usm_find_user(pdu->engine.engine_id, pdu->engine.engine_len, pdu->user.sec_name)) == NULL || usm_user->status != 1 /* active */) return (SNMP_CODE_BADUSER); if (usm_user->user_engine_len != snmpd_engine.engine_len || memcmp(usm_user->user_engine_id, snmpd_engine.engine_id, snmpd_engine.engine_len) != 0) return (SNMP_CODE_BADENGINE); pdu->user.priv_proto = usm_user->suser.priv_proto; memcpy(pdu->user.priv_key, usm_user->suser.priv_key, sizeof(pdu->user.priv_key)); /* authenticated snmpEngineId discovery */ if ((pdu->flags & SNMP_MSG_AUTH_FLAG) != 0) { update_snmpd_engine_time(); pdu->user.auth_proto = usm_user->suser.auth_proto; memcpy(pdu->user.auth_key, usm_user->suser.auth_key, sizeof(pdu->user.auth_key)); if (pdu->engine.engine_boots == 0 && pdu->engine.engine_time == 0) { update_snmpd_engine_time(); pdu->flags |= SNMP_MSG_AUTODISCOVER; return (SNMP_CODE_OK); } if (pdu->engine.engine_boots != snmpd_engine.engine_boots || abs(pdu->engine.engine_time - snmpd_engine.engine_time) > SNMP_TIME_WINDOW) return (SNMP_CODE_NOTINTIME); } if (((pdu->flags & SNMP_MSG_PRIV_FLAG) != 0 && (pdu->flags & SNMP_MSG_AUTH_FLAG) == 0) || ((pdu->flags & SNMP_MSG_AUTH_FLAG) == 0 && usm_user->suser.auth_proto != SNMP_AUTH_NOAUTH) || ((pdu->flags & SNMP_MSG_PRIV_FLAG) == 0 && usm_user->suser.priv_proto != SNMP_PRIV_NOPRIV)) return (SNMP_CODE_BADSECLEVEL); return (SNMP_CODE_OK); } /* * Check whether access to each of var bindings in the PDU is allowed based * on the user credentials against the configured User groups & VACM views. */ enum snmp_code snmp_pdu_auth_access(struct snmp_pdu *pdu, int32_t *ip) { const char *uname; int32_t suboid, smodel; uint32_t i; struct vacm_user *vuser; struct vacm_access *acl; struct vacm_context *vacmctx; struct vacm_view *view; /* * At least a default context exists if the snmpd_vacm(3) module is * running. */ if (SLIST_EMPTY(&vacm_contextlist) || (pdu->flags & SNMP_MSG_AUTODISCOVER) != 0) return (SNMP_CODE_OK); switch (pdu->version) { case SNMP_V1: if ((uname = comm_string(community)) == NULL) return (SNMP_CODE_FAILED); smodel = SNMP_SECMODEL_SNMPv1; break; case SNMP_V2c: if ((uname = comm_string(community)) == NULL) return (SNMP_CODE_FAILED); smodel = SNMP_SECMODEL_SNMPv2c; break; case SNMP_V3: uname = pdu->user.sec_name; if ((smodel = pdu->security_model) != SNMP_SECMODEL_USM) return (SNMP_CODE_FAILED); /* Compare the PDU context engine id against the agent's */ if (pdu->context_engine_len != snmpd_engine.engine_len || memcmp(pdu->context_engine, snmpd_engine.engine_id, snmpd_engine.engine_len) != 0) return (SNMP_CODE_FAILED); break; default: abort(); } SLIST_FOREACH(vuser, &vacm_userlist, vvu) if (strcmp(uname, vuser->secname) == 0 && vuser->sec_model == smodel) break; if (vuser == NULL || vuser->group == NULL) return (SNMP_CODE_FAILED); /* XXX: shteryana - recheck */ TAILQ_FOREACH_REVERSE(acl, &vacm_accesslist, vacm_accesslist, vva) { if (acl->group != vuser->group) continue; SLIST_FOREACH(vacmctx, &vacm_contextlist, vcl) if (memcmp(vacmctx->ctxname, acl->ctx_prefix, acl->ctx_match) == 0) goto match; } return (SNMP_CODE_FAILED); match: switch (pdu->type) { case SNMP_PDU_GET: case SNMP_PDU_GETNEXT: case SNMP_PDU_GETBULK: if ((view = acl->read_view) == NULL) return (SNMP_CODE_FAILED); break; case SNMP_PDU_SET: if ((view = acl->write_view) == NULL) return (SNMP_CODE_FAILED); break; case SNMP_PDU_TRAP: case SNMP_PDU_INFORM: case SNMP_PDU_TRAP2: case SNMP_PDU_REPORT: if ((view = acl->notify_view) == NULL) return (SNMP_CODE_FAILED); break; case SNMP_PDU_RESPONSE: /* NOTREACHED */ return (SNMP_CODE_FAILED); default: abort(); } for (i = 0; i < pdu->nbindings; i++) { /* XXX - view->mask*/ suboid = asn_is_suboid(&view->subtree, &pdu->bindings[i].var); if ((!suboid && !view->exclude) || (suboid && view->exclude)) { *ip = i + 1; return (SNMP_CODE_FAILED); } } return (SNMP_CODE_OK); } /* * SNMP input. Start: decode the PDU, find the user or 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; /* update uptime */ this_tick = get_ticks(); 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; memset(pdu, 0, sizeof(*pdu)); if ((code = snmp_pdu_decode_header(&b, pdu)) != SNMP_CODE_OK) goto decoded; if (pdu->version == SNMP_V3) { if (pdu->security_model != SNMP_SECMODEL_USM) { code = SNMP_CODE_FAILED; goto decoded; } if ((code = snmp_pdu_auth_user(pdu)) != SNMP_CODE_OK) goto decoded; if ((code = snmp_pdu_decode_secmode(&b, pdu)) != SNMP_CODE_OK) goto decoded; } code = snmp_pdu_decode_scoped(&b, pdu, ip); ret = SNMPD_INPUT_OK; decoded: snmpd_stats.inPkts++; 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_BADSECLEVEL: snmpd_usmstats.unsupported_seclevels++; return (SNMPD_INPUT_FAILED); case SNMP_CODE_NOTINTIME: snmpd_usmstats.not_in_time_windows++; return (SNMPD_INPUT_FAILED); case SNMP_CODE_BADUSER: snmpd_usmstats.unknown_users++; return (SNMPD_INPUT_FAILED); case SNMP_CODE_BADENGINE: snmpd_usmstats.unknown_engine_ids++; return (SNMPD_INPUT_FAILED); case SNMP_CODE_BADDIGEST: snmpd_usmstats.wrong_digests++; return (SNMPD_INPUT_FAILED); case SNMP_CODE_EDECRYPT: snmpd_usmstats.decrypt_errors++; return (SNMPD_INPUT_FAILED); 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_V3: if (!(snmpd.version_enable & VERS_ENABLE_V3)) 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 or user */ if (pdu->version != SNMP_V3) { 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; } else if (pdu->nbindings == 0) { /* RFC 3414 - snmpEngineID Discovery */ if (strlen(pdu->user.sec_name) == 0) { asn_append_oid(&(pdu->bindings[pdu->nbindings++].var), &oid_usmUnknownEngineIDs); pdu->context_engine_len = snmpd_engine.engine_len; memcpy(pdu->context_engine, snmpd_engine.engine_id, snmpd_engine.engine_len); } else if (pdu->engine.engine_boots == 0 && pdu->engine.engine_time == 0) { asn_append_oid(&(pdu->bindings[pdu->nbindings++].var), &oid_usmNotInTimeWindows); update_snmpd_engine_time(); pdu->engine.engine_boots = snmpd_engine.engine_boots; pdu->engine.engine_time = snmpd_engine.engine_time; } } else if (usm_user->suser.auth_proto != SNMP_AUTH_NOAUTH && (pdu->engine.engine_boots == 0 || pdu->engine.engine_time == 0)) { snmpd_usmstats.not_in_time_windows++; ret = SNMPD_INPUT_FAILED; } if ((code = snmp_pdu_auth_access(pdu, ip)) != SNMP_CODE_OK) ret = SNMPD_INPUT_FAILED; 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; } /* * 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; ssize_t ret, slen; int32_t vi; #ifdef USE_TCPWRAPPERS char client[16]; #endif struct msghdr msg; struct iovec iov[1]; ret = tport->transport->vtab->recv(pi); if (ret == -1) return (-1); #ifdef USE_TCPWRAPPERS /* * In case of AF_INET{6} peer, do hosts_access(5) check. */ if (pi->peer->sa_family != AF_LOCAL && 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 if (pi->peer->sa_family != AF_LOCAL) 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 != NULL && 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 (pdu.version < SNMP_V3 && ((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) { msg.msg_name = pi->peer; msg.msg_namelen = pi->peerlen; msg.msg_iov = iov; msg.msg_iovlen = 1; msg.msg_flags = 0; iov[0].iov_base = sndbuf; iov[0].iov_len = sndlen; slen = sendmsg(pi->fd, &msg, 0); if (slen == -1) syslog(LOG_ERR, "sendmsg: %m"); else if ((size_t)slen != sndlen) syslog(LOG_ERR, "sendmsg: 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 = NULL, *option; /* XXX */ 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:e: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'"); 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 'e': strlcpy(engine_file, optarg, sizeof(engine_file)); 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(); init_snmpd_engine(); 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 (engine_file[0] == '\0') snprintf(engine_file, sizeof(engine_file), PATH_ENGINE, prefix); 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); } while ((m = TAILQ_FIRST(&modules_start)) != NULL) { m->flags &= ~LM_ONSTARTLIST; TAILQ_REMOVE(&modules_start, m, start); lm_start(m); } snmp_send_trap(&oid_coldStart, (struct snmp_value *)NULL); 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(void) { 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->flags & LM_ONSTARTLIST) != 0) TAILQ_REMOVE(&modules_start, m, start); 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; } } /* * RFC 3414 User-based Security Model support */ struct snmpd_usmstat * bsnmpd_get_usm_stats(void) { return (&snmpd_usmstats); } void bsnmpd_reset_usm_stats(void) { memset(&snmpd_usmstats, 0, sizeof(snmpd_usmstats)); } struct usm_user * usm_first_user(void) { return (SLIST_FIRST(&usm_userlist)); } struct usm_user * usm_next_user(struct usm_user *uuser) { if (uuser == NULL) return (NULL); return (SLIST_NEXT(uuser, up)); } struct usm_user * usm_find_user(uint8_t *engine, uint32_t elen, char *uname) { struct usm_user *uuser; SLIST_FOREACH(uuser, &usm_userlist, up) if (uuser->user_engine_len == elen && memcmp(uuser->user_engine_id, engine, elen) == 0 && strlen(uuser->suser.sec_name) == strlen(uname) && strcmp(uuser->suser.sec_name, uname) == 0) break; return (uuser); } static int usm_compare_user(struct usm_user *u1, struct usm_user *u2) { uint32_t i; if (u1->user_engine_len < u2->user_engine_len) return (-1); if (u1->user_engine_len > u2->user_engine_len) return (1); for (i = 0; i < u1->user_engine_len; i++) { if (u1->user_engine_id[i] < u2->user_engine_id[i]) return (-1); if (u1->user_engine_id[i] > u2->user_engine_id[i]) return (1); } if (strlen(u1->suser.sec_name) < strlen(u2->suser.sec_name)) return (-1); if (strlen(u1->suser.sec_name) > strlen(u2->suser.sec_name)) return (1); for (i = 0; i < strlen(u1->suser.sec_name); i++) { if (u1->suser.sec_name[i] < u2->suser.sec_name[i]) return (-1); if (u1->suser.sec_name[i] > u2->suser.sec_name[i]) return (1); } return (0); } struct usm_user * usm_new_user(uint8_t *eid, uint32_t elen, char *uname) { int cmp; struct usm_user *uuser, *temp, *prev; for (uuser = usm_first_user(); uuser != NULL; (uuser = usm_next_user(uuser))) { if (uuser->user_engine_len == elen && strlen(uname) == strlen(uuser->suser.sec_name) && strcmp(uname, uuser->suser.sec_name) == 0 && memcmp(eid, uuser->user_engine_id, elen) == 0) return (NULL); } if ((uuser = (struct usm_user *)malloc(sizeof(*uuser))) == NULL) return (NULL); memset(uuser, 0, sizeof(*uuser)); strlcpy(uuser->suser.sec_name, uname, SNMP_ADM_STR32_SIZ); memcpy(uuser->user_engine_id, eid, elen); uuser->user_engine_len = elen; if ((prev = SLIST_FIRST(&usm_userlist)) == NULL || usm_compare_user(uuser, prev) < 0) { SLIST_INSERT_HEAD(&usm_userlist, uuser, up); return (uuser); } SLIST_FOREACH(temp, &usm_userlist, up) { if ((cmp = usm_compare_user(uuser, temp)) <= 0) break; prev = temp; } if (temp == NULL || cmp < 0) SLIST_INSERT_AFTER(prev, uuser, up); else if (cmp > 0) SLIST_INSERT_AFTER(temp, uuser, up); else { syslog(LOG_ERR, "User %s exists", uuser->suser.sec_name); free(uuser); return (NULL); } return (uuser); } void usm_delete_user(struct usm_user *uuser) { SLIST_REMOVE(&usm_userlist, uuser, usm_user, up); free(uuser); } void usm_flush_users(void) { struct usm_user *uuser; while ((uuser = SLIST_FIRST(&usm_userlist)) != NULL) { SLIST_REMOVE_HEAD(&usm_userlist, up); free(uuser); } SLIST_INIT(&usm_userlist); } /* * RFC 3415 View-based Access Control Model support */ struct vacm_user * vacm_first_user(void) { return (SLIST_FIRST(&vacm_userlist)); } struct vacm_user * vacm_next_user(struct vacm_user *vuser) { if (vuser == NULL) return (NULL); return (SLIST_NEXT(vuser, vvu)); } static int vacm_compare_user(struct vacm_user *v1, struct vacm_user *v2) { uint32_t i; if (v1->sec_model < v2->sec_model) return (-1); if (v1->sec_model > v2->sec_model) return (1); if (strlen(v1->secname) < strlen(v2->secname)) return (-1); if (strlen(v1->secname) > strlen(v2->secname)) return (1); for (i = 0; i < strlen(v1->secname); i++) { if (v1->secname[i] < v2->secname[i]) return (-1); if (v1->secname[i] > v2->secname[i]) return (1); } return (0); } struct vacm_user * vacm_new_user(int32_t smodel, char *uname) { int cmp; struct vacm_user *user, *temp, *prev; SLIST_FOREACH(user, &vacm_userlist, vvu) if (strcmp(uname, user->secname) == 0 && smodel == user->sec_model) return (NULL); if ((user = (struct vacm_user *)malloc(sizeof(*user))) == NULL) return (NULL); memset(user, 0, sizeof(*user)); user->group = &vacm_default_group; SLIST_INSERT_HEAD(&vacm_default_group.group_users, user, vvg); user->sec_model = smodel; strlcpy(user->secname, uname, sizeof(user->secname)); if ((prev = SLIST_FIRST(&vacm_userlist)) == NULL || vacm_compare_user(user, prev) < 0) { SLIST_INSERT_HEAD(&vacm_userlist, user, vvu); return (user); } SLIST_FOREACH(temp, &vacm_userlist, vvu) { if ((cmp = vacm_compare_user(user, temp)) <= 0) break; prev = temp; } if (temp == NULL || cmp < 0) SLIST_INSERT_AFTER(prev, user, vvu); else if (cmp > 0) SLIST_INSERT_AFTER(temp, user, vvu); else { syslog(LOG_ERR, "User %s exists", user->secname); free(user); return (NULL); } return (user); } int vacm_delete_user(struct vacm_user *user) { if (user->group != NULL && user->group != &vacm_default_group) { SLIST_REMOVE(&user->group->group_users, user, vacm_user, vvg); if (SLIST_EMPTY(&user->group->group_users)) { SLIST_REMOVE(&vacm_grouplist, user->group, vacm_group, vge); free(user->group); } } SLIST_REMOVE(&vacm_userlist, user, vacm_user, vvu); free(user); return (0); } int vacm_user_set_group(struct vacm_user *user, u_char *octets, u_int len) { struct vacm_group *group; if (len >= SNMP_ADM_STR32_SIZ) return (-1); SLIST_FOREACH(group, &vacm_grouplist, vge) if (strlen(group->groupname) == len && memcmp(octets, group->groupname, len) == 0) break; if (group == NULL) { if ((group = (struct vacm_group *)malloc(sizeof(*group))) == NULL) return (-1); memset(group, 0, sizeof(*group)); memcpy(group->groupname, octets, len); group->groupname[len] = '\0'; SLIST_INSERT_HEAD(&vacm_grouplist, group, vge); } SLIST_REMOVE(&user->group->group_users, user, vacm_user, vvg); SLIST_INSERT_HEAD(&group->group_users, user, vvg); user->group = group; return (0); } void vacm_groups_init(void) { SLIST_INSERT_HEAD(&vacm_grouplist, &vacm_default_group, vge); } struct vacm_access * vacm_first_access_rule(void) { return (TAILQ_FIRST(&vacm_accesslist)); } struct vacm_access * vacm_next_access_rule(struct vacm_access *acl) { if (acl == NULL) return (NULL); return (TAILQ_NEXT(acl, vva)); } static int vacm_compare_access_rule(struct vacm_access *v1, struct vacm_access *v2) { uint32_t i; if (strlen(v1->group->groupname) < strlen(v2->group->groupname)) return (-1); if (strlen(v1->group->groupname) > strlen(v2->group->groupname)) return (1); for (i = 0; i < strlen(v1->group->groupname); i++) { if (v1->group->groupname[i] < v2->group->groupname[i]) return (-1); if (v1->group->groupname[i] > v2->group->groupname[i]) return (1); } if (strlen(v1->ctx_prefix) < strlen(v2->ctx_prefix)) return (-1); if (strlen(v1->ctx_prefix) > strlen(v2->ctx_prefix)) return (1); for (i = 0; i < strlen(v1->ctx_prefix); i++) { if (v1->ctx_prefix[i] < v2->ctx_prefix[i]) return (-1); if (v1->ctx_prefix[i] > v2->ctx_prefix[i]) return (1); } if (v1->sec_model < v2->sec_model) return (-1); if (v1->sec_model > v2->sec_model) return (1); if (v1->sec_level < v2->sec_level) return (-1); if (v1->sec_level > v2->sec_level) return (1); return (0); } struct vacm_access * vacm_new_access_rule(char *gname, char *cprefix, int32_t smodel, int32_t slevel) { struct vacm_group *group; struct vacm_access *acl, *temp; TAILQ_FOREACH(acl, &vacm_accesslist, vva) { if (acl->group == NULL) continue; if (strcmp(gname, acl->group->groupname) == 0 && strcmp(cprefix, acl->ctx_prefix) == 0 && acl->sec_model == smodel && acl->sec_level == slevel) return (NULL); } /* Make sure the group exists */ SLIST_FOREACH(group, &vacm_grouplist, vge) if (strcmp(gname, group->groupname) == 0) break; if (group == NULL) return (NULL); if ((acl = (struct vacm_access *)malloc(sizeof(*acl))) == NULL) return (NULL); memset(acl, 0, sizeof(*acl)); acl->group = group; strlcpy(acl->ctx_prefix, cprefix, sizeof(acl->ctx_prefix)); acl->sec_model = smodel; acl->sec_level = slevel; if ((temp = TAILQ_FIRST(&vacm_accesslist)) == NULL || vacm_compare_access_rule(acl, temp) < 0) { TAILQ_INSERT_HEAD(&vacm_accesslist, acl, vva); return (acl); } TAILQ_FOREACH(temp, &vacm_accesslist, vva) if (vacm_compare_access_rule(acl, temp) < 0) { TAILQ_INSERT_BEFORE(temp, acl, vva); return (acl); } TAILQ_INSERT_TAIL(&vacm_accesslist, acl, vva); return (acl); } int vacm_delete_access_rule(struct vacm_access *acl) { TAILQ_REMOVE(&vacm_accesslist, acl, vva); free(acl); return (0); } struct vacm_view * vacm_first_view(void) { return (SLIST_FIRST(&vacm_viewlist)); } struct vacm_view * vacm_next_view(struct vacm_view *view) { if (view == NULL) return (NULL); return (SLIST_NEXT(view, vvl)); } static int vacm_compare_view(struct vacm_view *v1, struct vacm_view *v2) { uint32_t i; if (strlen(v1->viewname) < strlen(v2->viewname)) return (-1); if (strlen(v1->viewname) > strlen(v2->viewname)) return (1); for (i = 0; i < strlen(v1->viewname); i++) { if (v1->viewname[i] < v2->viewname[i]) return (-1); if (v1->viewname[i] > v2->viewname[i]) return (1); } return (asn_compare_oid(&v1->subtree, &v2->subtree)); } struct vacm_view * vacm_new_view(char *vname, struct asn_oid *oid) { int cmp; struct vacm_view *view, *temp, *prev; SLIST_FOREACH(view, &vacm_viewlist, vvl) if (strcmp(vname, view->viewname) == 0) return (NULL); if ((view = (struct vacm_view *)malloc(sizeof(*view))) == NULL) return (NULL); memset(view, 0, sizeof(*view)); strlcpy(view->viewname, vname, sizeof(view->viewname)); asn_append_oid(&view->subtree, oid); if ((prev = SLIST_FIRST(&vacm_viewlist)) == NULL || vacm_compare_view(view, prev) < 0) { SLIST_INSERT_HEAD(&vacm_viewlist, view, vvl); return (view); } SLIST_FOREACH(temp, &vacm_viewlist, vvl) { if ((cmp = vacm_compare_view(view, temp)) <= 0) break; prev = temp; } if (temp == NULL || cmp < 0) SLIST_INSERT_AFTER(prev, view, vvl); else if (cmp > 0) SLIST_INSERT_AFTER(temp, view, vvl); else { syslog(LOG_ERR, "View %s exists", view->viewname); free(view); return (NULL); } return (view); } int vacm_delete_view(struct vacm_view *view) { SLIST_REMOVE(&vacm_viewlist, view, vacm_view, vvl); free(view); return (0); } struct vacm_context * vacm_first_context(void) { return (SLIST_FIRST(&vacm_contextlist)); } struct vacm_context * vacm_next_context(struct vacm_context *vacmctx) { if (vacmctx == NULL) return (NULL); return (SLIST_NEXT(vacmctx, vcl)); } struct vacm_context * vacm_add_context(char *ctxname, int regid) { int cmp; struct vacm_context *ctx, *temp, *prev; SLIST_FOREACH(ctx, &vacm_contextlist, vcl) if (strcmp(ctxname, ctx->ctxname) == 0) { syslog(LOG_ERR, "Context %s exists", ctx->ctxname); return (NULL); } if ((ctx = (struct vacm_context *)malloc(sizeof(*ctx))) == NULL) return (NULL); memset(ctx, 0, sizeof(*ctx)); strlcpy(ctx->ctxname, ctxname, sizeof(ctx->ctxname)); ctx->regid = regid; if ((prev = SLIST_FIRST(&vacm_contextlist)) == NULL || strlen(ctx->ctxname) < strlen(prev->ctxname) || strcmp(ctx->ctxname, prev->ctxname) < 0) { SLIST_INSERT_HEAD(&vacm_contextlist, ctx, vcl); return (ctx); } SLIST_FOREACH(temp, &vacm_contextlist, vcl) { if (strlen(ctx->ctxname) < strlen(temp->ctxname) || strcmp(ctx->ctxname, temp->ctxname) < 0) { cmp = -1; break; } prev = temp; } if (temp == NULL || cmp < 0) SLIST_INSERT_AFTER(prev, ctx, vcl); else if (cmp > 0) SLIST_INSERT_AFTER(temp, ctx, vcl); else { syslog(LOG_ERR, "Context %s exists", ctx->ctxname); free(ctx); return (NULL); } return (ctx); } void vacm_flush_contexts(int regid) { struct vacm_context *ctx, *temp; SLIST_FOREACH_SAFE(ctx, &vacm_contextlist, vcl, temp) if (ctx->regid == regid) { SLIST_REMOVE(&vacm_contextlist, ctx, vacm_context, vcl); free(ctx); } } Index: head/contrib/bsnmp/snmpd/snmpd.config =================================================================== --- head/contrib/bsnmp/snmpd/snmpd.config (revision 310647) +++ head/contrib/bsnmp/snmpd/snmpd.config (revision 310648) @@ -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.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: head/contrib/bsnmp/snmpd/snmpd.h =================================================================== --- head/contrib/bsnmp/snmpd/snmpd.h (revision 310647) +++ head/contrib/bsnmp/snmpd/snmpd.h (revision 310648) @@ -1,342 +1,342 @@ /* * 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.h,v 1.24 2004/08/06 08:47:13 brandt Exp $ * * Private SNMPd data and functions. */ #ifdef USE_LIBBEGEMOT #include #else #include #endif #define PATH_SYSCONFIG "/etc:/usr/etc:/usr/local/etc" #ifdef USE_LIBBEGEMOT #define evTimerID int #define evFileID int #endif /************************************************************* * * Communities */ struct community { struct lmodule *owner; /* who created the community */ u_int private;/* private name for the module */ u_int value; /* value of this community */ u_char * string; /* the community string */ const u_char * descr; /* description */ TAILQ_ENTRY(community) link; struct asn_oid index; }; /* list of all known communities */ extern TAILQ_HEAD(community_list, community) community_list; /************************************************************* * * Request IDs. */ struct idrange { u_int type; /* type id */ int32_t base; /* base of this range */ int32_t size; /* size of this range */ int32_t next; /* generator */ struct lmodule *owner; /* owner module */ TAILQ_ENTRY(idrange) link; }; /* list of all known ranges */ extern TAILQ_HEAD(idrange_list, idrange) idrange_list; /* identifier generator */ extern u_int next_idrange; /* request id generator for traps */ extern u_int trap_reqid; /************************************************************* * * Timers */ struct timer { void (*func)(void *);/* user function */ void *udata; /* user data */ evTimerID id; /* timer id */ struct lmodule *owner; /* owner of the timer */ LIST_ENTRY(timer) link; }; /* list of all current timers */ extern LIST_HEAD(timer_list, timer) timer_list; /************************************************************* * * File descriptors */ struct fdesc { int fd; /* the file descriptor */ void (*func)(int, void *);/* user function */ void *udata; /* user data */ evFileID id; /* file id */ struct lmodule *owner; /* owner module of the file */ LIST_ENTRY(fdesc) link; }; /* list of all current selected files */ extern LIST_HEAD(fdesc_list, fdesc) fdesc_list; /************************************************************* * * Loadable modules */ # define LM_SECTION_MAX 14 struct lmodule { char section[LM_SECTION_MAX + 1]; /* and index */ char *path; u_int flags; void *handle; const struct snmp_module *config; TAILQ_ENTRY(lmodule) link; TAILQ_ENTRY(lmodule) start; struct asn_oid index; }; #define LM_STARTED 0x0001 #define LM_ONSTARTLIST 0x0002 extern TAILQ_HEAD(lmodules, lmodule) lmodules; struct lmodule *lm_load(const char *, const char *); void lm_unload(struct lmodule *); void lm_start(struct lmodule *); /************************************************************* * * SNMP ports */ /* * Common input stuff */ struct port_input { int fd; /* socket */ void *id; /* evSelect handle */ int stream : 1; /* stream socket */ int cred : 1; /* want credentials */ struct sockaddr *peer; /* last received packet */ socklen_t peerlen; int priv : 1; /* peer is privileged */ u_char *buf; /* receive buffer */ size_t buflen; /* buffer length */ size_t length; /* received length */ size_t consumed; /* how many bytes used */ }; struct tport { struct asn_oid index; /* table index of this tp point */ TAILQ_ENTRY(tport) link; /* table link */ struct transport *transport; /* who handles this */ }; TAILQ_HEAD(tport_list, tport); int snmpd_input(struct port_input *, struct tport *); void snmpd_input_close(struct port_input *); /* * Transport domain */ #define TRANS_NAMELEN 64 struct transport_def { const char *name; /* name of this transport */ struct asn_oid id; /* OBJID of this transport */ int (*start)(void); int (*stop)(int); void (*close_port)(struct tport *); int (*init_port)(struct tport *); ssize_t (*send)(struct tport *, const u_char *, size_t, const struct sockaddr *, size_t); ssize_t (*recv)(struct port_input *); }; struct transport { struct asn_oid index; /* transport table index */ TAILQ_ENTRY(transport) link; /* ... and link */ u_int or_index; /* registration index */ struct tport_list table; /* list of open ports */ const struct transport_def *vtab; }; TAILQ_HEAD(transport_list, transport); extern struct transport_list transport_list; void trans_insert_port(struct transport *, struct tport *); void trans_remove_port(struct tport *); struct tport *trans_find_port(struct transport *, const struct asn_oid *, u_int); struct tport *trans_next_port(struct transport *, const struct asn_oid *, u_int); struct tport *trans_first_port(struct transport *); struct tport *trans_iter_port(struct transport *, int (*)(struct tport *, intptr_t), intptr_t); int trans_register(const struct transport_def *, struct transport **); int trans_unregister(struct transport *); /************************************************************* * * SNMPd scalar configuration. */ struct snmpd { /* transmit buffer size */ u_int32_t txbuf; /* receive buffer size */ u_int32_t rxbuf; /* disable community table */ int comm_dis; /* authentication traps */ int auth_traps; /* source address for V1 traps */ u_char trap1addr[4]; /* version enable flags */ uint32_t version_enable; }; extern struct snmpd snmpd; #define VERS_ENABLE_V1 0x00000001 #define VERS_ENABLE_V2C 0x00000002 #define VERS_ENABLE_V3 0x00000004 #define VERS_ENABLE_ALL (VERS_ENABLE_V1 | VERS_ENABLE_V2C | VERS_ENABLE_V3) /* * The debug group */ struct debug { u_int dump_pdus; u_int logpri; u_int evdebug; }; extern struct debug debug; /* * SNMPd statistics table */ struct snmpd_stats { u_int32_t inPkts; /* total packets received */ u_int32_t inBadVersions; /* unknown version number */ u_int32_t inASNParseErrs; /* fatal parse errors */ u_int32_t inBadCommunityNames; u_int32_t inBadCommunityUses; u_int32_t proxyDrops; /* dropped by proxy function */ u_int32_t silentDrops; u_int32_t inBadPduTypes; u_int32_t inTooLong; u_int32_t noTxbuf; u_int32_t noRxbuf; }; extern struct snmpd_stats snmpd_stats; /* * SNMPd Engine */ extern struct snmp_engine snmpd_engine; /* * OR Table */ struct objres { TAILQ_ENTRY(objres) link; u_int index; struct asn_oid oid; /* the resource OID */ char descr[256]; u_int32_t uptime; struct lmodule *module; }; TAILQ_HEAD(objres_list, objres); extern struct objres_list objres_list; /* * Trap Sink Table */ struct trapsink { TAILQ_ENTRY(trapsink) link; struct asn_oid index; u_int status; int socket; u_char comm[SNMP_COMMUNITY_MAXLEN + 1]; int version; }; enum { TRAPSINK_ACTIVE = 1, TRAPSINK_NOT_IN_SERVICE = 2, TRAPSINK_NOT_READY = 3, TRAPSINK_DESTROY = 6, TRAPSINK_V1 = 1, TRAPSINK_V2 = 2, }; TAILQ_HEAD(trapsink_list, trapsink); extern struct trapsink_list trapsink_list; extern const char *syspath; /* snmpSerialNo */ extern int32_t snmp_serial_no; int init_actvals(void); extern char engine_file[]; int init_snmpd_engine(void); int set_snmpd_engine(void); void update_snmpd_engine_time(void); int read_config(const char *, struct lmodule *); int define_macro(const char *name, const char *value); #define LOG_ASN1_ERRORS 0x10000000 #define LOG_SNMP_ERRORS 0x20000000 Index: head/contrib/bsnmp/snmpd/snmpd.sh =================================================================== --- head/contrib/bsnmp/snmpd/snmpd.sh (revision 310647) +++ head/contrib/bsnmp/snmpd/snmpd.sh (revision 310648) @@ -1,85 +1,85 @@ #!/bin/sh # # 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.sh,v 1.3 2004/08/06 08:47:13 brandt Exp $ # # SNMPd startup script # SNMPD=/usr/local/bin/bsnmpd PID=/var/run/snmpd.pid CONF=/etc/snmpd.conf case "$1" in start) if [ -r ${PID} ] ; then if kill -0 `cat ${PID}` ; then echo "snmpd already running -- pid `cat ${PID}`" >/dev/stderr exit 1 fi rm -f ${PID} fi if ${SNMPD} -c ${CONF} -p ${PID} ; then echo "snmpd started" fi ;; stop) if [ -r ${PID} ] ; then if kill -0 `cat ${PID}` ; then if kill -15 `cat ${PID}` ; then echo "snmpd stopped" exit 0 fi echo "cannot kill snmpd" >/dev/stderr exit 1 fi echo "stale pid file -- removing" >/dev/stderr rm -f ${PID} exit 1 fi echo "snmpd not running" >/dev/stderr ;; status) if [ ! -r ${PID} ] ; then echo "snmpd not running" elif kill -0 `cat ${PID}` ; then echo "snmpd pid `cat ${PID}`" else echo "stale pid file -- pid `cat ${PID}`" fi ;; *) echo "usage: `basename $0` {start|stop|status}" exit 1 esac exit 0 Index: head/contrib/bsnmp/snmpd/snmpmod.3 =================================================================== --- head/contrib/bsnmp/snmpd/snmpmod.3 (revision 310647) +++ head/contrib/bsnmp/snmpd/snmpmod.3 (revision 310648) @@ -1,1184 +1,1184 @@ .\" .\" 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 December 19, 2010 .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 oid_usmUnknownEngineIDs , .Nm oid_usmNotInTimeWindows , .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 snmp_pdu_auth_access .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, .Nm snmpd_usmstats, .Nm bsnmpd_get_usm_stats, .Nm bsnmpd_reset_usm_stats, .Nm usm_first_user, .Nm usm_next_user, .Nm usm_find_user, .Nm usm_new_user, .Nm usm_delete_user, .Nm usm_flush_users, .Nm usm_user .Nm snmpd_target_stat .Nm bsnmpd_get_target_stats .Nm target_first_address .Nm target_next_address .Nm target_new_address .Nm target_activate_address .Nm target_delete_address .Nm target_first_param .Nm target_next_param .Nm target_new_param .Nm target_delete_param .Nm target_first_notify .Nm target_next_notify .Nm target_new_notify .Nm target_delete_notify .Nm target_flush_all .Nm target_address .Nm target_param .Nm target_notify .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 enum snmp_code .Fn snmp_pdu_auth_access "struct snmp_pdu *pdu" "int32_t *ip" .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" .Vt extern struct snmpd_usmstat snmpd_usmstats ; .Ft struct snmpd_usmstat * .Fn bsnmpd_get_usm_stats "void" .Ft void .Fn bsnmpd_reset_usm_stats "void" .Ft struct usm_user * .Fn usm_first_user "void" .Ft struct usm_user * .Fn usm_next_user "struct usm_user *uuser" .Ft struct usm_user * .Fn usm_find_user "uint8_t *engine" "uint32_t elen" "char *uname" .Ft struct usm_user * .Fn usm_new_user "uint8_t *engine" "uint32_t elen" "char *uname" .Ft void .Fn usm_delete_user "struct usm_user *" .Ft void .Fn usm_flush_users "void" .Vt extern struct usm_user *usm_user; .Ft struct snmpd_target_stats * .Fn bsnmpd_get_target_stats "void" .Ft struct target_address * .Fn target_first_address "void" .Ft struct target_address * .Fn target_next_address "struct target_address *" .Ft struct target_address * .Fn target_new_address "char *" .Ft int .Fn target_activate_address "struct target_address *" .Ft int .Fn target_delete_address "struct target_address *" .Ft struct target_param * .Fn target_first_param "void" .Ft struct target_param * .Fn target_next_param "struct target_param *" .Ft struct target_param * .Fn target_new_param "char *" .Ft int .Fn target_delete_param "struct target_param *" .Ft struct target_notify * .Fn target_first_notify "void" .Ft struct target_notify * .Fn target_next_notify "struct target_notify *" .Ft struct target_notify * .Fn target_new_notify "char *" .Ft int .Fn target_delete_notify "struct target_notify *" .Ft void .Fn target_flush_all "void" .Vt extern const struct asn_oid oid_usmUnknownEngineIDs; .Vt extern const struct asn_oid oid_usmNotInTimeWindows; .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 SNMPv1 or SNMPv2 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 THE USER-BASED SECURITY GROUP The scalar statistics of the USM group are held in the global variable .Va snmpd_usmstats : .Bd -literal -offset indent struct snmpd_usmstat { uint32_t unsupported_seclevels; uint32_t not_in_time_windows; uint32_t unknown_users; uint32_t unknown_engine_ids; uint32_t wrong_digests; uint32_t decrypt_errors; }; .Ed .Fn bsnmpd_get_usm_stats returns a pointer to the global structure containing the statistics. .Fn bsnmpd_reset_usm_stats clears the statistics of the USM group. .Pp A global list of configured USM users is maintained by the daemon. .Bd -literal -offset indent struct usm_user { struct snmp_user suser; uint8_t user_engine_id[SNMP_ENGINE_ID_SIZ]; uint32_t user_engine_len; char user_public[SNMP_ADM_STR32_SIZ]; uint32_t user_public_len; int32_t status; int32_t type; SLIST_ENTRY(usm_user) up; }; .Ed This structure represents an USM user. The daemon only responds to SNMPv3 PDUs with user credentials matching an USM user entry in its global list. If a SNMPv3 PDU is received, whose security model is USM, the global .Va usm_user is set to point at the user entry that matches the credentials contained in the PDU. However, the daemon does not create or remove USM users, it gives an interface to external loadable module(s) to manage the list. .Fn usm_new_user adds an user entry in the list, and .Fn usm_delete_user deletes an existing entry from the list. .Fn usm_flush_users is used to remove all configured USM users. .Fn usm_first_user will return the first user in the list, or .Li NULL if the list is empty. .Fn usm_next_user will return the next user of a given entry if one exists, or .Li NULL . The list is sorted according to the USM user name and Engine ID. .Fn usm_find_user returns the USM user entry matching the given .Fa engine and .Fa uname or .Li NULL if an user with the specified name and engine id is not present in the list. .Ss THE MANAGEMENT TARGET GROUP The Management Target group holds target address information used when sending SNMPv3 notifications. .Pp The scalar statistics of the Management Target group are held in the global variable .Va snmpd_target_stats : .Bd -literal -offset indent struct snmpd_target_stats { uint32_t unavail_contexts; uint32_t unknown_contexts; }; .Ed .Fn bsnmpd_get_target_stats returns a pointer to the global structure containing the statistics. .Pp Three global lists of configured management target addresses, parameters and notifications respectively are maintained by the daemon. .Bd -literal -offset indent struct target_address { char name[SNMP_ADM_STR32_SIZ]; uint8_t address[SNMP_UDP_ADDR_SIZ]; int32_t timeout; int32_t retry; char taglist[SNMP_TAG_SIZ]; char paramname[SNMP_ADM_STR32_SIZ]; int32_t type; int32_t socket; int32_t status; SLIST_ENTRY(target_address) ta; }; .Ed This structure represents a SNMPv3 Management Target address. Each time a SNMP TRAP is send the daemon will send the Trap to all active Management Target addresses in its global list. .Bd -literal -offset indent struct target_param { char name[SNMP_ADM_STR32_SIZ]; int32_t mpmodel; int32_t sec_model; char secname[SNMP_ADM_STR32_SIZ]; enum snmp_usm_level sec_level; int32_t type; int32_t status; SLIST_ENTRY(target_param) tp; }; .Ed This structure represents the information used to generate SNMP messages to the associated SNMPv3 Management Target addresses. .Bd -literal -offset indent struct target_notify { char name[SNMP_ADM_STR32_SIZ]; char taglist[SNMP_TAG_SIZ]; int32_t notify_type; int32_t type; int32_t status; SLIST_ENTRY(target_notify) tn; }; .Ed This structure represents Notification Tag entries - SNMP notifications are sent to the Target address for each entry in the Management Target Address list that has a tag matching the specified tag in this structure. .Pp The daemon does not create or remove entries in the Management Target group lists, it gives an interface to external loadable module(s) to manage the lists. .Fn target_new_address adds a target address entry, and .Fn target_delete_address deletes an existing entry from the target address list. .Fn target_activate_address creates a socket associated with the target address entry so that SNMP notifications may actually be send to that target address. .Fn target_first_address will return a pointer to the first target address entry in the list, while .Fn target_next_address will return a pointer to the next target address of a given entry if one exists. .Fn target_new_param adds a target parameters' entry, and .Fn target_delete_param deletes an existing entry from the target parameters list. .Fn target_first_param will return a pointer to the first target parameters' entry in the list, while .Fn target_next_param will return a pointer to the next target parameters of a given entry if one exists. .Fn target_new_notify adds a notification target entry, and .Fn target_delete_notify deletes an existing entry from the notification target list. .Fn target_first_notify will return a pointer to the first notification target entry in the list, while .Fn target_next_notify will return a pointer to the next notification target of a given entry if one exists. .Fn target_flush_all is used to remove all configured data from the three global Management Target Group lists. .Ss WELL KNOWN OIDS The global variable .Va oid_zeroDotZero contains the OID 0.0. The global variables .Va oid_usmUnknownEngineIDs .Va oid_usmNotInTimeWindows contains the OIDs 1.3.6.1.6.3.15.1.1.4.0 and 1.3.6.1.6.3.15.1.1.2.0 used in the SNMPv3 USM Engine Discovery. .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 acquire 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 within 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. .Fn snmp_pdu_auth_access verifies whether access to the object IDs contained in the .Fa pdu should be granted or denied, according to the configured View-Based Access rules. .Fa ip contains the index of the first varbinding to which access was denied, or 0 if access to all varbindings in the PDU is granted. .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 .Fn string_get to 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: head/contrib/bsnmp/snmpd/snmpmod.h =================================================================== --- head/contrib/bsnmp/snmpd/snmpmod.h (revision 310647) +++ head/contrib/bsnmp/snmpd/snmpmod.h (revision 310648) @@ -1,625 +1,625 @@ /* * Copyright (c) 2001-2003 * Fraunhofer Institute for Open Communication Systems (FhG Fokus). * All rights reserved. * * Author: Harti Brandt - * + * * Copyright (c) 2010 The FreeBSD Foundation * All rights reserved. * * Portions of this software were developed by Shteryana Sotirova Shopova * under sponsorship from the FreeBSD Foundation. * * 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.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 #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; /* * SNMP User-based Security Model data. Modified via the snmp_usm(3) module. */ struct snmpd_usmstat { uint32_t unsupported_seclevels; uint32_t not_in_time_windows; uint32_t unknown_users; uint32_t unknown_engine_ids; uint32_t wrong_digests; uint32_t decrypt_errors; }; extern struct snmpd_usmstat snmpd_usmstats; struct snmpd_usmstat *bsnmpd_get_usm_stats(void); void bsnmpd_reset_usm_stats(void); struct usm_user { struct snmp_user suser; uint8_t user_engine_id[SNMP_ENGINE_ID_SIZ]; uint32_t user_engine_len; char user_public[SNMP_ADM_STR32_SIZ]; uint32_t user_public_len; int32_t status; int32_t type; SLIST_ENTRY(usm_user) up; }; SLIST_HEAD(usm_userlist, usm_user); struct usm_user *usm_first_user(void); struct usm_user *usm_next_user(struct usm_user *); struct usm_user *usm_find_user(uint8_t *, uint32_t, char *); struct usm_user *usm_new_user(uint8_t *, uint32_t, char *); void usm_delete_user(struct usm_user *); void usm_flush_users(void); /* USM user for current packet */ extern struct usm_user *usm_user; /* * SNMP View-based Access Control Model data. Modified via the snmp_vacm(3) module. */ struct vacm_group; struct vacm_user { /* Security user name from USM */ char secname[SNMP_ADM_STR32_SIZ]; int32_t sec_model; /* Back pointer to user assigned group name */ struct vacm_group *group; int32_t type; int32_t status; SLIST_ENTRY(vacm_user) vvu; SLIST_ENTRY(vacm_user) vvg; }; SLIST_HEAD(vacm_userlist, vacm_user); struct vacm_group { char groupname[SNMP_ADM_STR32_SIZ]; struct vacm_userlist group_users; SLIST_ENTRY(vacm_group) vge; }; SLIST_HEAD(vacm_grouplist, vacm_group); struct vacm_access { /* The group name is index, not a column in the table */ struct vacm_group *group; char ctx_prefix[SNMP_ADM_STR32_SIZ]; int32_t sec_model; int32_t sec_level; int32_t ctx_match; struct vacm_view *read_view; struct vacm_view *write_view; struct vacm_view *notify_view; int32_t type; int32_t status; TAILQ_ENTRY(vacm_access) vva; }; TAILQ_HEAD(vacm_accesslist, vacm_access); struct vacm_view { char viewname[SNMP_ADM_STR32_SIZ]; /* key */ struct asn_oid subtree; /* key */ uint8_t mask[16]; uint8_t exclude; int32_t type; int32_t status; SLIST_ENTRY(vacm_view) vvl; }; SLIST_HEAD(vacm_viewlist, vacm_view); struct vacm_context { /* The ID of the module that registered this context */ int32_t regid; char ctxname[SNMP_ADM_STR32_SIZ]; SLIST_ENTRY(vacm_context) vcl; }; SLIST_HEAD(vacm_contextlist, vacm_context); void vacm_groups_init(void); struct vacm_user *vacm_first_user(void); struct vacm_user *vacm_next_user(struct vacm_user *); struct vacm_user *vacm_new_user(int32_t, char *); int vacm_delete_user(struct vacm_user *); int vacm_user_set_group(struct vacm_user *, u_char *, u_int); struct vacm_access *vacm_first_access_rule(void); struct vacm_access *vacm_next_access_rule(struct vacm_access *); struct vacm_access *vacm_new_access_rule(char *, char *, int32_t, int32_t); int vacm_delete_access_rule(struct vacm_access *); struct vacm_view *vacm_first_view(void); struct vacm_view *vacm_next_view(struct vacm_view *); struct vacm_view *vacm_new_view(char *, struct asn_oid *); int vacm_delete_view(struct vacm_view *); struct vacm_context *vacm_first_context(void); struct vacm_context *vacm_next_context(struct vacm_context *); struct vacm_context *vacm_add_context(char *, int32_t); void vacm_flush_contexts(int32_t); /* * RFC 3413 SNMP Management Target & Notification MIB */ struct snmpd_target_stats { uint32_t unavail_contexts; uint32_t unknown_contexts; }; #define SNMP_UDP_ADDR_SIZ 6 #define SNMP_TAG_SIZ (255 + 1) struct target_address { char name[SNMP_ADM_STR32_SIZ]; uint8_t address[SNMP_UDP_ADDR_SIZ]; int32_t timeout; int32_t retry; char taglist[SNMP_TAG_SIZ]; char paramname[SNMP_ADM_STR32_SIZ]; int32_t type; int32_t socket; int32_t status; SLIST_ENTRY(target_address) ta; }; SLIST_HEAD(target_addresslist, target_address); struct target_param { char name[SNMP_ADM_STR32_SIZ]; int32_t mpmodel; int32_t sec_model; char secname[SNMP_ADM_STR32_SIZ]; enum snmp_usm_level sec_level; int32_t type; int32_t status; SLIST_ENTRY(target_param) tp; }; SLIST_HEAD(target_paramlist, target_param); struct target_notify { char name[SNMP_ADM_STR32_SIZ]; char taglist[SNMP_TAG_SIZ]; int32_t notify_type; int32_t type; int32_t status; SLIST_ENTRY(target_notify) tn; }; SLIST_HEAD(target_notifylist, target_notify); extern struct snmpd_target_stats snmpd_target_stats; struct snmpd_target_stats *bsnmpd_get_target_stats(void); struct target_address *target_first_address(void); struct target_address *target_next_address(struct target_address *); struct target_address *target_new_address(char *); int target_activate_address(struct target_address *); int target_delete_address(struct target_address *); struct target_param *target_first_param(void); struct target_param *target_next_param(struct target_param *); struct target_param *target_new_param(char *); int target_delete_param(struct target_param *); struct target_notify *target_first_notify(void); struct target_notify *target_next_notify(struct target_notify *); struct target_notify *target_new_notify(char *); int target_delete_notify (struct target_notify *); void target_flush_all(void); /* * Well known OIDs */ extern const struct asn_oid oid_zeroDotZero; /* SNMPv3 Engine Discovery */ extern const struct asn_oid oid_usmUnknownEngineIDs; extern const struct asn_oid oid_usmNotInTimeWindows; /* * 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); enum snmp_code snmp_pdu_auth_access(struct snmp_pdu *, int32_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: head/contrib/bsnmp/snmpd/trans_lsock.c =================================================================== --- head/contrib/bsnmp/snmpd/trans_lsock.c (revision 310647) +++ head/contrib/bsnmp/snmpd/trans_lsock.c (revision 310648) @@ -1,674 +1,674 @@ /* * Copyright (c) 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/trans_lsock.c,v 1.6 2005/02/25 11:50:25 brandt_h Exp $ * * Local domain socket transport */ #include #include #include #include #include #include #include #include #include #include #include #include #include "snmpmod.h" #include "snmpd.h" #include "trans_lsock.h" #include "tree.h" #include "oid.h" static const struct asn_oid oid_begemotSnmpdLocalPortTable = OIDX_begemotSnmpdLocalPortTable; static int lsock_start(void); static int lsock_stop(int); static void lsock_close_port(struct tport *); static int lsock_init_port(struct tport *); static ssize_t lsock_send(struct tport *, const u_char *, size_t, const struct sockaddr *, size_t); static ssize_t lsock_recv(struct port_input *); /* exported */ const struct transport_def lsock_trans = { "lsock", OIDX_begemotSnmpdTransLsock, lsock_start, lsock_stop, lsock_close_port, lsock_init_port, lsock_send, lsock_recv }; static struct transport *my_trans; static int lsock_remove(struct tport *tp, intptr_t arg __unused) { struct lsock_port *port = (struct lsock_port *)tp; (void)remove(port->name); return (-1); } static int lsock_stop(int force) { if (my_trans != NULL) { if (!force && trans_first_port(my_trans) != NULL) return (SNMP_ERR_GENERR); trans_iter_port(my_trans, lsock_remove, 0); return (trans_unregister(my_trans)); } return (SNMP_ERR_NOERROR); } static int lsock_start(void) { return (trans_register(&lsock_trans, &my_trans)); } /* * Open a local port. If this is a datagram socket create also the * one and only peer. */ static int lsock_open_port(u_char *name, size_t namelen, struct lsock_port **pp, int type) { struct lsock_port *port; struct lsock_peer *peer = NULL; int is_stream, need_cred; size_t u; int err; struct sockaddr_un sa; if (namelen == 0 || namelen + 1 > sizeof(sa.sun_path)) return (SNMP_ERR_BADVALUE); switch (type) { case LOCP_DGRAM_UNPRIV: is_stream = 0; need_cred = 0; break; case LOCP_DGRAM_PRIV: is_stream = 0; need_cred = 1; break; case LOCP_STREAM_UNPRIV: is_stream = 1; need_cred = 0; break; case LOCP_STREAM_PRIV: is_stream = 1; need_cred = 1; break; default: return (SNMP_ERR_BADVALUE); } if ((port = malloc(sizeof(*port))) == NULL) return (SNMP_ERR_GENERR); memset(port, 0, sizeof(*port)); if (!is_stream) { if ((peer = malloc(sizeof(*peer))) == NULL) { free(port); return (SNMP_ERR_GENERR); } memset(peer, 0, sizeof(*peer)); } if ((port->name = malloc(namelen + 1)) == NULL) { free(port); if (!is_stream) free(peer); return (SNMP_ERR_GENERR); } strncpy(port->name, name, namelen); port->name[namelen] = '\0'; port->type = type; port->str_sock = -1; LIST_INIT(&port->peers); port->tport.index.len = namelen + 1; port->tport.index.subs[0] = namelen; for (u = 0; u < namelen; u++) port->tport.index.subs[u + 1] = name[u]; if (peer != NULL) { LIST_INSERT_HEAD(&port->peers, peer, link); peer->port = port; peer->input.fd = -1; peer->input.id = NULL; peer->input.stream = is_stream; peer->input.cred = need_cred; peer->input.peer = (struct sockaddr *)&peer->peer; } trans_insert_port(my_trans, &port->tport); if (community != COMM_INITIALIZE && (err = lsock_init_port(&port->tport)) != SNMP_ERR_NOERROR) { lsock_close_port(&port->tport); return (err); } *pp = port; return (SNMP_ERR_NOERROR); } /* * Close a local domain peer */ static void lsock_peer_close(struct lsock_peer *peer) { LIST_REMOVE(peer, link); snmpd_input_close(&peer->input); free(peer); } /* * Close a local port */ static void lsock_close_port(struct tport *tp) { struct lsock_port *port = (struct lsock_port *)tp; struct lsock_peer *peer; if (port->str_id != NULL) fd_deselect(port->str_id); if (port->str_sock >= 0) (void)close(port->str_sock); (void)remove(port->name); trans_remove_port(tp); while ((peer = LIST_FIRST(&port->peers)) != NULL) lsock_peer_close(peer); free(port->name); free(port); } /* * Input on a local socket (either datagram or stream) */ static void lsock_input(int fd __unused, void *udata) { struct lsock_peer *peer = udata; struct lsock_port *p = peer->port; peer->input.peerlen = sizeof(peer->peer); if (snmpd_input(&peer->input, &p->tport) == -1 && peer->input.stream) /* framing or other input error */ lsock_peer_close(peer); } /* * A UNIX domain listening socket is ready. This means we have a peer * that we need to accept */ static void lsock_listen_input(int fd, void *udata) { struct lsock_port *p = udata; struct lsock_peer *peer; if ((peer = malloc(sizeof(*peer))) == NULL) { syslog(LOG_WARNING, "%s: peer malloc failed", p->name); (void)close(accept(fd, NULL, NULL)); return; } memset(peer, 0, sizeof(*peer)); peer->port = p; peer->input.stream = 1; peer->input.cred = (p->type == LOCP_DGRAM_PRIV || p->type == LOCP_STREAM_PRIV); peer->input.peerlen = sizeof(peer->peer); peer->input.peer = (struct sockaddr *)&peer->peer; peer->input.fd = accept(fd, peer->input.peer, &peer->input.peerlen); if (peer->input.fd == -1) { syslog(LOG_WARNING, "%s: accept failed: %m", p->name); free(peer); return; } if ((peer->input.id = fd_select(peer->input.fd, lsock_input, peer, NULL)) == NULL) { close(peer->input.fd); free(peer); return; } LIST_INSERT_HEAD(&p->peers, peer, link); } /* * Create a local socket */ static int lsock_init_port(struct tport *tp) { struct lsock_port *p = (struct lsock_port *)tp; struct sockaddr_un sa; if (p->type == LOCP_STREAM_PRIV || p->type == LOCP_STREAM_UNPRIV) { if ((p->str_sock = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) { syslog(LOG_ERR, "creating local socket: %m"); return (SNMP_ERR_RES_UNAVAIL); } strcpy(sa.sun_path, p->name); sa.sun_family = AF_LOCAL; sa.sun_len = strlen(p->name) + offsetof(struct sockaddr_un, sun_path); (void)remove(p->name); if (bind(p->str_sock, (struct sockaddr *)&sa, sizeof(sa))) { if (errno == EADDRNOTAVAIL) { close(p->str_sock); p->str_sock = -1; return (SNMP_ERR_INCONS_NAME); } syslog(LOG_ERR, "bind: %s %m", p->name); close(p->str_sock); p->str_sock = -1; return (SNMP_ERR_GENERR); } if (chmod(p->name, 0666) == -1) syslog(LOG_WARNING, "chmod(%s,0666): %m", p->name); if (listen(p->str_sock, 10) == -1) { syslog(LOG_ERR, "listen: %s %m", p->name); (void)remove(p->name); close(p->str_sock); p->str_sock = -1; return (SNMP_ERR_GENERR); } p->str_id = fd_select(p->str_sock, lsock_listen_input, p, NULL); if (p->str_id == NULL) { (void)remove(p->name); close(p->str_sock); p->str_sock = -1; return (SNMP_ERR_GENERR); } } else { struct lsock_peer *peer; const int on = 1; peer = LIST_FIRST(&p->peers); if ((peer->input.fd = socket(PF_LOCAL, SOCK_DGRAM, 0)) < 0) { syslog(LOG_ERR, "creating local socket: %m"); return (SNMP_ERR_RES_UNAVAIL); } if (setsockopt(peer->input.fd, 0, LOCAL_CREDS, &on, sizeof(on)) == -1) { syslog(LOG_ERR, "setsockopt(LOCAL_CREDS): %m"); close(peer->input.fd); peer->input.fd = -1; return (SNMP_ERR_GENERR); } strcpy(sa.sun_path, p->name); sa.sun_family = AF_LOCAL; sa.sun_len = strlen(p->name) + offsetof(struct sockaddr_un, sun_path); (void)remove(p->name); if (bind(peer->input.fd, (struct sockaddr *)&sa, sizeof(sa))) { if (errno == EADDRNOTAVAIL) { close(peer->input.fd); peer->input.fd = -1; return (SNMP_ERR_INCONS_NAME); } syslog(LOG_ERR, "bind: %s %m", p->name); close(peer->input.fd); peer->input.fd = -1; return (SNMP_ERR_GENERR); } if (chmod(p->name, 0666) == -1) syslog(LOG_WARNING, "chmod(%s,0666): %m", p->name); peer->input.id = fd_select(peer->input.fd, lsock_input, peer, NULL); if (peer->input.id == NULL) { (void)remove(p->name); close(peer->input.fd); peer->input.fd = -1; return (SNMP_ERR_GENERR); } } return (SNMP_ERR_NOERROR); } /* * Send something */ static ssize_t lsock_send(struct tport *tp, const u_char *buf, size_t len, const struct sockaddr *addr, size_t addrlen) { struct lsock_port *p = (struct lsock_port *)tp; struct lsock_peer *peer; if (p->type == LOCP_DGRAM_PRIV || p->type == LOCP_DGRAM_UNPRIV) { peer = LIST_FIRST(&p->peers); } else { /* search for the peer */ LIST_FOREACH(peer, &p->peers, link) if (peer->input.peerlen == addrlen && memcmp(peer->input.peer, addr, addrlen) == 0) break; if (peer == NULL) { errno = ENOTCONN; return (-1); } } return (sendto(peer->input.fd, buf, len, 0, addr, addrlen)); } static void check_priv_stream(struct port_input *pi) { struct xucred ucred; socklen_t ucredlen; /* 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); else pi->priv = 0; } /* * Receive something */ static ssize_t lsock_recv(struct port_input *pi) { struct msghdr msg; struct iovec iov[1]; ssize_t len; msg.msg_control = NULL; msg.msg_controllen = 0; 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; 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_stream(pi); return (0); } /* * Dependency to create a lsock port */ struct lsock_dep { struct snmp_dependency dep; /* index (path name) */ u_char *path; size_t pathlen; /* the port */ struct lsock_port *port; /* which of the fields are set */ u_int set; /* type of the port */ int type; /* status */ int status; }; #define LD_TYPE 0x01 #define LD_STATUS 0x02 #define LD_CREATE 0x04 /* rollback create */ #define LD_DELETE 0x08 /* rollback delete */ /* * dependency handler for lsock ports */ static int lsock_func(struct snmp_context *ctx, struct snmp_dependency *dep, enum snmp_depop op) { struct lsock_dep *ld = (struct lsock_dep *)(void *)dep; int err = SNMP_ERR_NOERROR; switch (op) { case SNMP_DEPOP_COMMIT: if (!(ld->set & LD_STATUS)) err = SNMP_ERR_BADVALUE; else if (ld->port == NULL) { if (!ld->status) err = SNMP_ERR_BADVALUE; else { /* create */ err = lsock_open_port(ld->path, ld->pathlen, &ld->port, ld->type); if (err == SNMP_ERR_NOERROR) ld->set |= LD_CREATE; } } else if (!ld->status) { /* delete - hard to roll back so defer to finalizer */ ld->set |= LD_DELETE; } else /* modify - read-only */ err = SNMP_ERR_READONLY; return (err); case SNMP_DEPOP_ROLLBACK: if (ld->set & LD_CREATE) { /* was create */ lsock_close_port(&ld->port->tport); } return (SNMP_ERR_NOERROR); case SNMP_DEPOP_FINISH: if ((ld->set & LD_DELETE) && ctx->code == SNMP_RET_OK) lsock_close_port(&ld->port->tport); free(ld->path); return (SNMP_ERR_NOERROR); } abort(); } /* * Local port table */ int op_lsock_port(struct snmp_context *ctx, struct snmp_value *value, u_int sub, u_int iidx, enum snmp_op op) { asn_subid_t which = value->var.subs[sub-1]; struct lsock_port *p; u_char *name; size_t namelen; struct lsock_dep *ld; struct asn_oid didx; switch (op) { case SNMP_OP_GETNEXT: if ((p = (struct lsock_port *)trans_next_port(my_trans, &value->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); index_append(&value->var, sub, &p->tport.index); break; case SNMP_OP_GET: if ((p = (struct lsock_port *)trans_find_port(my_trans, &value->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_SET: p = (struct lsock_port *)trans_find_port(my_trans, &value->var, sub); if (index_decode(&value->var, sub, iidx, &name, &namelen)) return (SNMP_ERR_NO_CREATION); asn_slice_oid(&didx, &value->var, sub, value->var.len); if ((ld = (struct lsock_dep *)(void *)snmp_dep_lookup(ctx, &oid_begemotSnmpdLocalPortTable, &didx, sizeof(*ld), lsock_func)) == NULL) { free(name); return (SNMP_ERR_GENERR); } if (ld->path == NULL) { ld->path = name; ld->pathlen = namelen; } else { free(name); } ld->port = p; switch (which) { case LEAF_begemotSnmpdLocalPortStatus: if (ld->set & LD_STATUS) return (SNMP_ERR_INCONS_VALUE); if (!TRUTH_OK(value->v.integer)) return (SNMP_ERR_WRONG_VALUE); ld->status = TRUTH_GET(value->v.integer); ld->set |= LD_STATUS; break; case LEAF_begemotSnmpdLocalPortType: if (ld->set & LD_TYPE) return (SNMP_ERR_INCONS_VALUE); if (value->v.integer < 1 || value->v.integer > 4) return (SNMP_ERR_WRONG_VALUE); ld->type = value->v.integer; ld->set |= LD_TYPE; break; } return (SNMP_ERR_NOERROR); case SNMP_OP_ROLLBACK: case SNMP_OP_COMMIT: return (SNMP_ERR_NOERROR); default: abort(); } /* * Come here to fetch the value */ switch (which) { case LEAF_begemotSnmpdLocalPortStatus: value->v.integer = 1; break; case LEAF_begemotSnmpdLocalPortType: value->v.integer = p->type; break; default: abort(); } return (SNMP_ERR_NOERROR); } Index: head/contrib/bsnmp/snmpd/trans_lsock.h =================================================================== --- head/contrib/bsnmp/snmpd/trans_lsock.h (revision 310647) +++ head/contrib/bsnmp/snmpd/trans_lsock.h (revision 310648) @@ -1,59 +1,59 @@ /* * Copyright (c) 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/trans_lsock.h,v 1.3 2004/08/06 08:47:15 brandt Exp $ * * Local domain socket transport */ enum locp { LOCP_DGRAM_UNPRIV = 1, LOCP_DGRAM_PRIV = 2, LOCP_STREAM_UNPRIV = 3, LOCP_STREAM_PRIV = 4, }; struct lsock_peer { LIST_ENTRY(lsock_peer) link; struct port_input input; struct sockaddr_un peer; struct lsock_port *port; /* parent port */ }; struct lsock_port { struct tport tport; /* must begin with this */ char *name; /* unix path name */ enum locp type; /* type of port */ int str_sock; /* stream socket */ void *str_id; /* select handle */ LIST_HEAD(, lsock_peer) peers; }; extern const struct transport_def lsock_trans; Index: head/contrib/bsnmp/snmpd/trans_udp.c =================================================================== --- head/contrib/bsnmp/snmpd/trans_udp.c (revision 310647) +++ head/contrib/bsnmp/snmpd/trans_udp.c (revision 310648) @@ -1,439 +1,439 @@ /* * Copyright (c) 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/trans_udp.c,v 1.5 2005/10/04 08:46:56 brandt_h Exp $ * * UDP transport */ #include #include #include #include #include #include #include #include #include #include #include "snmpmod.h" #include "snmpd.h" #include "trans_udp.h" #include "tree.h" #include "oid.h" static int udp_start(void); static int udp_stop(int); static void udp_close_port(struct tport *); static int udp_init_port(struct tport *); static ssize_t udp_send(struct tport *, const u_char *, size_t, const struct sockaddr *, size_t); static ssize_t udp_recv(struct port_input *); /* exported */ const struct transport_def udp_trans = { "udp", OIDX_begemotSnmpdTransUdp, udp_start, udp_stop, udp_close_port, udp_init_port, udp_send, udp_recv }; static struct transport *my_trans; static int udp_start(void) { return (trans_register(&udp_trans, &my_trans)); } static int udp_stop(int force __unused) { if (my_trans != NULL) if (trans_unregister(my_trans) != 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); } /* * A UDP port is ready */ static void udp_input(int fd __unused, void *udata) { struct udp_port *p = udata; p->input.peerlen = sizeof(p->ret); snmpd_input(&p->input, &p->tport); } /* * Create a UDP socket and bind it to the given port */ static int udp_init_port(struct tport *tp) { struct udp_port *p = (struct udp_port *)tp; struct sockaddr_in addr; u_int32_t ip; const int on = 1; if ((p->input.fd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { syslog(LOG_ERR, "creating UDP socket: %m"); return (SNMP_ERR_RES_UNAVAIL); } ip = (p->addr[0] << 24) | (p->addr[1] << 16) | (p->addr[2] << 8) | p->addr[3]; memset(&addr, 0, sizeof(addr)); addr.sin_addr.s_addr = htonl(ip); addr.sin_port = htons(p->port); addr.sin_family = AF_INET; addr.sin_len = sizeof(addr); if (addr.sin_addr.s_addr == INADDR_ANY && setsockopt(p->input.fd, IPPROTO_IP, IP_RECVDSTADDR, &on, sizeof(on)) == -1) { syslog(LOG_ERR, "setsockopt(IP_RECVDSTADDR): %m"); close(p->input.fd); p->input.fd = -1; return (SNMP_ERR_GENERR); } if (bind(p->input.fd, (struct sockaddr *)&addr, sizeof(addr))) { if (errno == EADDRNOTAVAIL) { close(p->input.fd); p->input.fd = -1; return (SNMP_ERR_INCONS_NAME); } syslog(LOG_ERR, "bind: %s:%u %m", inet_ntoa(addr.sin_addr), p->port); close(p->input.fd); p->input.fd = -1; return (SNMP_ERR_GENERR); } if ((p->input.id = fd_select(p->input.fd, udp_input, p, NULL)) == NULL) { close(p->input.fd); p->input.fd = -1; return (SNMP_ERR_GENERR); } return (SNMP_ERR_NOERROR); } /* * Create a new SNMP Port object and start it, if we are not * in initialization mode. The arguments are in host byte order. */ static int udp_open_port(u_int8_t *addr, u_int32_t udp_port, struct udp_port **pp) { struct udp_port *port; int err; if (udp_port > 0xffff) return (SNMP_ERR_NO_CREATION); if ((port = malloc(sizeof(*port))) == NULL) return (SNMP_ERR_GENERR); memset(port, 0, sizeof(*port)); /* initialize common part */ port->tport.index.len = 5; port->tport.index.subs[0] = addr[0]; port->tport.index.subs[1] = addr[1]; port->tport.index.subs[2] = addr[2]; port->tport.index.subs[3] = addr[3]; port->tport.index.subs[4] = udp_port; port->addr[0] = addr[0]; port->addr[1] = addr[1]; port->addr[2] = addr[2]; port->addr[3] = addr[3]; port->port = udp_port; port->input.fd = -1; port->input.id = NULL; port->input.stream = 0; port->input.cred = 0; port->input.peer = (struct sockaddr *)&port->ret; port->input.peerlen = sizeof(port->ret); trans_insert_port(my_trans, &port->tport); if (community != COMM_INITIALIZE && (err = udp_init_port(&port->tport)) != SNMP_ERR_NOERROR) { udp_close_port(&port->tport); return (err); } *pp = port; return (SNMP_ERR_NOERROR); } /* * Close an SNMP port */ static void udp_close_port(struct tport *tp) { struct udp_port *port = (struct udp_port *)tp; snmpd_input_close(&port->input); trans_remove_port(tp); free(port); } /* * Send something */ static ssize_t udp_send(struct tport *tp, const u_char *buf, size_t len, const struct sockaddr *addr, size_t addrlen) { struct udp_port *p = (struct udp_port *)tp; return (sendto(p->input.fd, buf, len, 0, addr, addrlen)); } static void check_priv_dgram(struct port_input *pi, struct sockcred *cred) { /* process explicitly sends credentials */ if (cred) pi->priv = (cred->sc_euid == 0); else pi->priv = 0; } /* * Input from a datagram socket. * Each receive should return one datagram. */ static ssize_t recv_dgram(struct port_input *pi, struct in_addr *laddr) { u_char embuf[1000]; char cbuf[CMSG_SPACE(SOCKCREDSIZE(CMGROUP_MAX)) + CMSG_SPACE(sizeof(struct in_addr))]; struct msghdr msg; struct iovec iov[1]; ssize_t len; struct cmsghdr *cmsg; struct sockcred *cred = NULL; 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; memset(cbuf, 0, sizeof(cbuf)); msg.msg_control = cbuf; msg.msg_controllen = sizeof(cbuf); 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; for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) { if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_RECVDSTADDR) memcpy(laddr, CMSG_DATA(cmsg), sizeof(struct in_addr)); if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_CREDS) cred = (struct sockcred *)CMSG_DATA(cmsg); } if (pi->cred) check_priv_dgram(pi, cred); return (0); } /* * Receive something */ static ssize_t udp_recv(struct port_input *pi) { struct in_addr *laddr; struct msghdr msg; char cbuf[CMSG_SPACE(sizeof(struct in_addr))]; struct cmsghdr *cmsgp; ssize_t ret; memset(cbuf, 0, sizeof(cbuf)); msg.msg_control = cbuf; msg.msg_controllen = sizeof(cbuf); cmsgp = CMSG_FIRSTHDR(&msg); cmsgp->cmsg_len = CMSG_LEN(sizeof(struct in_addr)); cmsgp->cmsg_level = IPPROTO_IP; cmsgp->cmsg_type = IP_SENDSRCADDR; laddr = (struct in_addr *)CMSG_DATA(cmsgp); ret = recv_dgram(pi, laddr); if (laddr->s_addr == INADDR_ANY) { msg.msg_control = NULL; msg.msg_controllen = 0; } return (ret); } /* * Port table */ int op_snmp_port(struct snmp_context *ctx, struct snmp_value *value, u_int sub, u_int iidx, enum snmp_op op) { asn_subid_t which = value->var.subs[sub-1]; struct udp_port *p; u_int8_t addr[4]; u_int32_t port; switch (op) { case SNMP_OP_GETNEXT: if ((p = (struct udp_port *)trans_next_port(my_trans, &value->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); index_append(&value->var, sub, &p->tport.index); break; case SNMP_OP_GET: if ((p = (struct udp_port *)trans_find_port(my_trans, &value->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_SET: p = (struct udp_port *)trans_find_port(my_trans, &value->var, sub); ctx->scratch->int1 = (p != NULL); if (which != LEAF_begemotSnmpdPortStatus) abort(); if (!TRUTH_OK(value->v.integer)) return (SNMP_ERR_WRONG_VALUE); ctx->scratch->int2 = TRUTH_GET(value->v.integer); if (ctx->scratch->int2) { /* open an SNMP port */ if (p != NULL) /* already open - do nothing */ return (SNMP_ERR_NOERROR); if (index_decode(&value->var, sub, iidx, addr, &port)) return (SNMP_ERR_NO_CREATION); return (udp_open_port(addr, port, &p)); } else { /* close SNMP port - do in commit */ } return (SNMP_ERR_NOERROR); case SNMP_OP_ROLLBACK: p = (struct udp_port *)trans_find_port(my_trans, &value->var, sub); if (ctx->scratch->int1 == 0) { /* did not exist */ if (ctx->scratch->int2 == 1) { /* created */ if (p != NULL) udp_close_port(&p->tport); } } return (SNMP_ERR_NOERROR); case SNMP_OP_COMMIT: p = (struct udp_port *)trans_find_port(my_trans, &value->var, sub); if (ctx->scratch->int1 == 1) { /* did exist */ if (ctx->scratch->int2 == 0) { /* delete */ if (p != NULL) udp_close_port(&p->tport); } } return (SNMP_ERR_NOERROR); default: abort(); } /* * Come here to fetch the value */ switch (which) { case LEAF_begemotSnmpdPortStatus: value->v.integer = 1; break; default: abort(); } return (SNMP_ERR_NOERROR); } Index: head/contrib/bsnmp/snmpd/trans_udp.h =================================================================== --- head/contrib/bsnmp/snmpd/trans_udp.h (revision 310647) +++ head/contrib/bsnmp/snmpd/trans_udp.h (revision 310648) @@ -1,50 +1,50 @@ /* * Copyright (c) 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/trans_udp.h,v 1.3 2004/08/06 08:47:16 brandt Exp $ * * UDP transport */ struct udp_port { struct tport tport; /* must begin with this */ uint8_t addr[4]; /* host byteorder */ uint16_t port; /* host byteorder */ struct port_input input; /* common input stuff */ struct sockaddr_in ret; /* the return address */ }; /* argument for open call */ struct udp_open { uint8_t addr[4]; /* host byteorder */ uint16_t port; /* host byteorder */ }; extern const struct transport_def udp_trans; Index: head/contrib/bsnmp/snmpd/trap.c =================================================================== --- head/contrib/bsnmp/snmpd/trap.c (revision 310647) +++ head/contrib/bsnmp/snmpd/trap.c (revision 310648) @@ -1,909 +1,909 @@ /* * Copyright (c) 2001-2003 * Fraunhofer Institute for Open Communication Systems (FhG Fokus). * All rights reserved. * * Author: Harti Brandt * * Copyright (c) 2010 The FreeBSD Foundation * All rights reserved. * * Portions of this software were developed by Shteryana Sotirova Shopova * under sponsorship from the FreeBSD Foundation. * * 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/trap.c,v 1.9 2005/10/04 11:21:39 brandt_h Exp $ * * TrapSinkTable */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "snmpmod.h" #include "snmpd.h" #include "tree.h" #include "oid.h" struct trapsink_list trapsink_list = TAILQ_HEAD_INITIALIZER(trapsink_list); /* List of target addresses */ static struct target_addresslist target_addresslist = SLIST_HEAD_INITIALIZER(target_addresslist); /* List of target parameters */ static struct target_paramlist target_paramlist = SLIST_HEAD_INITIALIZER(target_paramlist); /* List of notification targets */ static struct target_notifylist target_notifylist = SLIST_HEAD_INITIALIZER(target_notifylist); static const struct asn_oid oid_begemotTrapSinkTable = OIDX_begemotTrapSinkTable; static const struct asn_oid oid_sysUpTime = OIDX_sysUpTime; static const struct asn_oid oid_snmpTrapOID = OIDX_snmpTrapOID; struct trapsink_dep { struct snmp_dependency dep; u_int set; u_int status; u_char comm[SNMP_COMMUNITY_MAXLEN + 1]; u_int version; u_int rb; u_int rb_status; u_int rb_version; u_char rb_comm[SNMP_COMMUNITY_MAXLEN + 1]; }; enum { TDEP_STATUS = 0x0001, TDEP_COMM = 0x0002, TDEP_VERSION = 0x0004, TDEP_CREATE = 0x0001, TDEP_MODIFY = 0x0002, TDEP_DESTROY = 0x0004, }; static int trapsink_create(struct trapsink_dep *tdep) { struct trapsink *t; struct sockaddr_in sa; if ((t = malloc(sizeof(*t))) == NULL) return (SNMP_ERR_RES_UNAVAIL); t->index = tdep->dep.idx; t->status = TRAPSINK_NOT_READY; t->comm[0] = '\0'; t->version = TRAPSINK_V2; if ((t->socket = socket(PF_INET, SOCK_DGRAM, 0)) == -1) { syslog(LOG_ERR, "socket(UDP): %m"); free(t); return (SNMP_ERR_RES_UNAVAIL); } (void)shutdown(t->socket, SHUT_RD); memset(&sa, 0, sizeof(sa)); sa.sin_len = sizeof(sa); sa.sin_family = AF_INET; sa.sin_addr.s_addr = htonl((t->index.subs[0] << 24) | (t->index.subs[1] << 16) | (t->index.subs[2] << 8) | (t->index.subs[3] << 0)); sa.sin_port = htons(t->index.subs[4]); if (connect(t->socket, (struct sockaddr *)&sa, sa.sin_len) == -1) { syslog(LOG_ERR, "connect(%s,%u): %m", inet_ntoa(sa.sin_addr), ntohs(sa.sin_port)); (void)close(t->socket); free(t); return (SNMP_ERR_GENERR); } if (tdep->set & TDEP_VERSION) t->version = tdep->version; if (tdep->set & TDEP_COMM) strcpy(t->comm, tdep->comm); if (t->comm[0] != '\0') t->status = TRAPSINK_NOT_IN_SERVICE; /* look whether we should activate */ if (tdep->status == 4) { if (t->status == TRAPSINK_NOT_READY) { if (t->socket != -1) (void)close(t->socket); free(t); return (SNMP_ERR_INCONS_VALUE); } t->status = TRAPSINK_ACTIVE; } INSERT_OBJECT_OID(t, &trapsink_list); tdep->rb |= TDEP_CREATE; return (SNMP_ERR_NOERROR); } static void trapsink_free(struct trapsink *t) { TAILQ_REMOVE(&trapsink_list, t, link); if (t->socket != -1) (void)close(t->socket); free(t); } static int trapsink_modify(struct trapsink *t, struct trapsink_dep *tdep) { tdep->rb_status = t->status; tdep->rb_version = t->version; strcpy(tdep->rb_comm, t->comm); if (tdep->set & TDEP_STATUS) { /* if we are active and should move to not_in_service do * this first */ if (tdep->status == 2 && tdep->rb_status == TRAPSINK_ACTIVE) { t->status = TRAPSINK_NOT_IN_SERVICE; tdep->rb |= TDEP_MODIFY; } } if (tdep->set & TDEP_VERSION) t->version = tdep->version; if (tdep->set & TDEP_COMM) strcpy(t->comm, tdep->comm); if (tdep->set & TDEP_STATUS) { /* if we were inactive and should go active - do this now */ if (tdep->status == 1 && tdep->rb_status != TRAPSINK_ACTIVE) { if (t->comm[0] == '\0') { t->status = tdep->rb_status; t->version = tdep->rb_version; strcpy(t->comm, tdep->rb_comm); return (SNMP_ERR_INCONS_VALUE); } t->status = TRAPSINK_ACTIVE; tdep->rb |= TDEP_MODIFY; } } return (SNMP_ERR_NOERROR); } static int trapsink_unmodify(struct trapsink *t, struct trapsink_dep *tdep) { if (tdep->set & TDEP_STATUS) t->status = tdep->rb_status; if (tdep->set & TDEP_VERSION) t->version = tdep->rb_version; if (tdep->set & TDEP_COMM) strcpy(t->comm, tdep->rb_comm); return (SNMP_ERR_NOERROR); } static int trapsink_destroy(struct snmp_context *ctx __unused, struct trapsink *t, struct trapsink_dep *tdep) { t->status = TRAPSINK_DESTROY; tdep->rb_status = t->status; tdep->rb |= TDEP_DESTROY; return (SNMP_ERR_NOERROR); } static int trapsink_undestroy(struct trapsink *t, struct trapsink_dep *tdep) { t->status = tdep->rb_status; return (SNMP_ERR_NOERROR); } static int trapsink_dep(struct snmp_context *ctx, struct snmp_dependency *dep, enum snmp_depop op) { struct trapsink_dep *tdep = (struct trapsink_dep *)dep; struct trapsink *t; t = FIND_OBJECT_OID(&trapsink_list, &dep->idx, 0); switch (op) { case SNMP_DEPOP_COMMIT: if (tdep->set & TDEP_STATUS) { switch (tdep->status) { case 1: case 2: if (t == NULL) return (SNMP_ERR_INCONS_VALUE); return (trapsink_modify(t, tdep)); case 4: case 5: if (t != NULL) return (SNMP_ERR_INCONS_VALUE); return (trapsink_create(tdep)); case 6: if (t == NULL) return (SNMP_ERR_NOERROR); return (trapsink_destroy(ctx, t, tdep)); } } else if (tdep->set != 0) return (trapsink_modify(t, tdep)); return (SNMP_ERR_NOERROR); case SNMP_DEPOP_ROLLBACK: if (tdep->rb & TDEP_CREATE) { trapsink_free(t); return (SNMP_ERR_NOERROR); } if (tdep->rb & TDEP_MODIFY) return (trapsink_unmodify(t, tdep)); if(tdep->rb & TDEP_DESTROY) return (trapsink_undestroy(t, tdep)); return (SNMP_ERR_NOERROR); case SNMP_DEPOP_FINISH: if ((tdep->rb & TDEP_DESTROY) && t != NULL && ctx->code == SNMP_RET_OK) trapsink_free(t); return (SNMP_ERR_NOERROR); } abort(); } int op_trapsink(struct snmp_context *ctx, struct snmp_value *value, u_int sub, u_int iidx, enum snmp_op op) { struct trapsink *t; u_char ipa[4]; int32_t port; struct asn_oid idx; struct trapsink_dep *tdep; u_char *p; t = NULL; /* gcc */ switch (op) { case SNMP_OP_GETNEXT: if ((t = NEXT_OBJECT_OID(&trapsink_list, &value->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); index_append(&value->var, sub, &t->index); break; case SNMP_OP_GET: if ((t = FIND_OBJECT_OID(&trapsink_list, &value->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_SET: if (index_decode(&value->var, sub, iidx, ipa, &port) || port == 0 || port > 65535) return (SNMP_ERR_NO_CREATION); t = FIND_OBJECT_OID(&trapsink_list, &value->var, sub); asn_slice_oid(&idx, &value->var, sub, value->var.len); tdep = (struct trapsink_dep *)snmp_dep_lookup(ctx, &oid_begemotTrapSinkTable, &idx, sizeof(*tdep), trapsink_dep); if (tdep == NULL) return (SNMP_ERR_RES_UNAVAIL); switch (value->var.subs[sub - 1]) { case LEAF_begemotTrapSinkStatus: if (tdep->set & TDEP_STATUS) return (SNMP_ERR_INCONS_VALUE); switch (value->v.integer) { case 1: case 2: if (t == NULL) return (SNMP_ERR_INCONS_VALUE); break; case 4: case 5: if (t != NULL) return (SNMP_ERR_INCONS_VALUE); break; case 6: break; default: return (SNMP_ERR_WRONG_VALUE); } tdep->status = value->v.integer; tdep->set |= TDEP_STATUS; return (SNMP_ERR_NOERROR); case LEAF_begemotTrapSinkComm: if (tdep->set & TDEP_COMM) return (SNMP_ERR_INCONS_VALUE); if (value->v.octetstring.len == 0 || value->v.octetstring.len > SNMP_COMMUNITY_MAXLEN) return (SNMP_ERR_WRONG_VALUE); for (p = value->v.octetstring.octets; p < value->v.octetstring.octets + value->v.octetstring.len; p++) { if (!isascii(*p) || !isprint(*p)) return (SNMP_ERR_WRONG_VALUE); } tdep->set |= TDEP_COMM; strncpy(tdep->comm, value->v.octetstring.octets, value->v.octetstring.len); tdep->comm[value->v.octetstring.len] = '\0'; return (SNMP_ERR_NOERROR); case LEAF_begemotTrapSinkVersion: if (tdep->set & TDEP_VERSION) return (SNMP_ERR_INCONS_VALUE); if (value->v.integer != TRAPSINK_V1 && value->v.integer != TRAPSINK_V2) return (SNMP_ERR_WRONG_VALUE); tdep->version = value->v.integer; tdep->set |= TDEP_VERSION; return (SNMP_ERR_NOERROR); } if (t == NULL) return (SNMP_ERR_INCONS_NAME); else return (SNMP_ERR_NOT_WRITEABLE); case SNMP_OP_ROLLBACK: case SNMP_OP_COMMIT: return (SNMP_ERR_NOERROR); } switch (value->var.subs[sub - 1]) { case LEAF_begemotTrapSinkStatus: value->v.integer = t->status; break; case LEAF_begemotTrapSinkComm: return (string_get(value, t->comm, -1)); case LEAF_begemotTrapSinkVersion: value->v.integer = t->version; break; } return (SNMP_ERR_NOERROR); } static void snmp_create_v1_trap(struct snmp_pdu *pdu, char *com, const struct asn_oid *trap_oid) { memset(pdu, 0, sizeof(*pdu)); strcpy(pdu->community, com); pdu->version = SNMP_V1; pdu->type = SNMP_PDU_TRAP; pdu->enterprise = systemg.object_id; memcpy(pdu->agent_addr, snmpd.trap1addr, 4); pdu->generic_trap = trap_oid->subs[trap_oid->len - 1] - 1; pdu->specific_trap = 0; pdu->time_stamp = get_ticks() - start_tick; pdu->nbindings = 0; } static void snmp_create_v2_trap(struct snmp_pdu *pdu, char *com, const struct asn_oid *trap_oid) { memset(pdu, 0, sizeof(*pdu)); strcpy(pdu->community, com); pdu->version = SNMP_V2c; pdu->type = SNMP_PDU_TRAP2; pdu->request_id = reqid_next(trap_reqid); pdu->error_index = 0; pdu->error_status = SNMP_ERR_NOERROR; pdu->bindings[0].var = oid_sysUpTime; pdu->bindings[0].var.subs[pdu->bindings[0].var.len++] = 0; pdu->bindings[0].syntax = SNMP_SYNTAX_TIMETICKS; pdu->bindings[0].v.uint32 = get_ticks() - start_tick; pdu->bindings[1].var = oid_snmpTrapOID; pdu->bindings[1].var.subs[pdu->bindings[1].var.len++] = 0; pdu->bindings[1].syntax = SNMP_SYNTAX_OID; pdu->bindings[1].v.oid = *trap_oid; pdu->nbindings = 2; } static void snmp_create_v3_trap(struct snmp_pdu *pdu, struct target_param *target, const struct asn_oid *trap_oid) { struct usm_user *usmuser; memset(pdu, 0, sizeof(*pdu)); pdu->version = SNMP_V3; pdu->type = SNMP_PDU_TRAP2; pdu->request_id = reqid_next(trap_reqid); pdu->error_index = 0; pdu->error_status = SNMP_ERR_NOERROR; pdu->bindings[0].var = oid_sysUpTime; pdu->bindings[0].var.subs[pdu->bindings[0].var.len++] = 0; pdu->bindings[0].syntax = SNMP_SYNTAX_TIMETICKS; pdu->bindings[0].v.uint32 = get_ticks() - start_tick; pdu->bindings[1].var = oid_snmpTrapOID; pdu->bindings[1].var.subs[pdu->bindings[1].var.len++] = 0; pdu->bindings[1].syntax = SNMP_SYNTAX_OID; pdu->bindings[1].v.oid = *trap_oid; pdu->nbindings = 2; update_snmpd_engine_time(); memcpy(pdu->engine.engine_id, snmpd_engine.engine_id, snmpd_engine.engine_len); pdu->engine.engine_len = snmpd_engine.engine_len; pdu->engine.engine_boots = snmpd_engine.engine_boots; pdu->engine.engine_time = snmpd_engine.engine_time; pdu->engine.max_msg_size = snmpd_engine.max_msg_size; strlcpy(pdu->user.sec_name, target->secname, sizeof(pdu->user.sec_name)); pdu->security_model = target->sec_model; pdu->context_engine_len = snmpd_engine.engine_len; memcpy(pdu->context_engine, snmpd_engine.engine_id, snmpd_engine.engine_len); if (target->sec_model == SNMP_SECMODEL_USM && target->sec_level != SNMP_noAuthNoPriv) { usmuser = usm_find_user(pdu->engine.engine_id, pdu->engine.engine_len, pdu->user.sec_name); if (usmuser != NULL) { pdu->user.auth_proto = usmuser->suser.auth_proto; pdu->user.priv_proto = usmuser->suser.priv_proto; memcpy(pdu->user.auth_key, usmuser->suser.auth_key, sizeof(pdu->user.auth_key)); memcpy(pdu->user.priv_key, usmuser->suser.priv_key, sizeof(pdu->user.priv_key)); } snmp_pdu_init_secparams(pdu); } } void snmp_send_trap(const struct asn_oid *trap_oid, ...) { struct snmp_pdu pdu; struct trapsink *t; const struct snmp_value *v; struct target_notify *n; struct target_address *ta; struct target_param *tp; va_list ap; u_char *sndbuf; char *tag; size_t sndlen; ssize_t len; int32_t ip; TAILQ_FOREACH(t, &trapsink_list, link) { if (t->status != TRAPSINK_ACTIVE) continue; if (t->version == TRAPSINK_V1) snmp_create_v1_trap(&pdu, t->comm, trap_oid); else snmp_create_v2_trap(&pdu, t->comm, trap_oid); va_start(ap, trap_oid); while ((v = va_arg(ap, const struct snmp_value *)) != NULL) pdu.bindings[pdu.nbindings++] = *v; va_end(ap); if (snmp_pdu_auth_access(&pdu, &ip) != SNMP_CODE_OK) { syslog(LOG_DEBUG, "send trap to %s failed: no access", t->comm); continue; } if ((sndbuf = buf_alloc(1)) == NULL) { syslog(LOG_ERR, "trap send buffer: %m"); return; } snmp_output(&pdu, sndbuf, &sndlen, "TRAP"); if ((len = send(t->socket, sndbuf, sndlen, 0)) == -1) syslog(LOG_ERR, "send: %m"); else if ((size_t)len != sndlen) syslog(LOG_ERR, "send: short write %zu/%zu", sndlen, (size_t)len); free(sndbuf); } SLIST_FOREACH(n, &target_notifylist, tn) { if (n->status != RowStatus_active || n->taglist[0] == '\0') continue; SLIST_FOREACH(ta, &target_addresslist, ta) if ((tag = strstr(ta->taglist, n->taglist)) != NULL && (tag[strlen(n->taglist)] == ' ' || tag[strlen(n->taglist)] == '\0' || tag[strlen(n->taglist)] == '\t' || tag[strlen(n->taglist)] == '\r' || tag[strlen(n->taglist)] == '\n') && ta->status == RowStatus_active) break; if (ta == NULL) continue; SLIST_FOREACH(tp, &target_paramlist, tp) if (strcmp(tp->name, ta->paramname) == 0 && tp->status == 1) break; if (tp == NULL) continue; switch (tp->mpmodel) { case SNMP_MPM_SNMP_V1: snmp_create_v1_trap(&pdu, tp->secname, trap_oid); break; case SNMP_MPM_SNMP_V2c: snmp_create_v2_trap(&pdu, tp->secname, trap_oid); break; case SNMP_MPM_SNMP_V3: snmp_create_v3_trap(&pdu, tp, trap_oid); break; default: continue; } va_start(ap, trap_oid); while ((v = va_arg(ap, const struct snmp_value *)) != NULL) pdu.bindings[pdu.nbindings++] = *v; va_end(ap); if (snmp_pdu_auth_access(&pdu, &ip) != SNMP_CODE_OK) { syslog(LOG_DEBUG, "send trap to %s failed: no access", t->comm); continue; } if ((sndbuf = buf_alloc(1)) == NULL) { syslog(LOG_ERR, "trap send buffer: %m"); return; } snmp_output(&pdu, sndbuf, &sndlen, "TRAP"); if ((len = send(ta->socket, sndbuf, sndlen, 0)) == -1) syslog(LOG_ERR, "send: %m"); else if ((size_t)len != sndlen) syslog(LOG_ERR, "send: short write %zu/%zu", sndlen, (size_t)len); free(sndbuf); } } /* * RFC 3413 SNMP Management Target MIB */ struct snmpd_target_stats * bsnmpd_get_target_stats(void) { return (&snmpd_target_stats); } struct target_address * target_first_address(void) { return (SLIST_FIRST(&target_addresslist)); } struct target_address * target_next_address(struct target_address *addrs) { if (addrs == NULL) return (NULL); return (SLIST_NEXT(addrs, ta)); } struct target_address * target_new_address(char *aname) { int cmp; struct target_address *addrs, *temp, *prev; SLIST_FOREACH(addrs, &target_addresslist, ta) if (strcmp(aname, addrs->name) == 0) return (NULL); if ((addrs = (struct target_address *)malloc(sizeof(*addrs))) == NULL) return (NULL); memset(addrs, 0, sizeof(*addrs)); strlcpy(addrs->name, aname, sizeof(addrs->name)); addrs->timeout = 150; addrs->retry = 3; /* XXX */ if ((prev = SLIST_FIRST(&target_addresslist)) == NULL || strcmp(aname, prev->name) < 0) { SLIST_INSERT_HEAD(&target_addresslist, addrs, ta); return (addrs); } SLIST_FOREACH(temp, &target_addresslist, ta) { if ((cmp = strcmp(aname, temp->name)) <= 0) break; prev = temp; } if (temp == NULL || cmp < 0) SLIST_INSERT_AFTER(prev, addrs, ta); else if (cmp > 0) SLIST_INSERT_AFTER(temp, addrs, ta); else { syslog(LOG_ERR, "Target address %s exists", addrs->name); free(addrs); return (NULL); } return (addrs); } int target_activate_address(struct target_address *addrs) { struct sockaddr_in sa; if ((addrs->socket = socket(PF_INET, SOCK_DGRAM, 0)) == -1) { syslog(LOG_ERR, "socket(UDP): %m"); return (SNMP_ERR_RES_UNAVAIL); } (void)shutdown(addrs->socket, SHUT_RD); memset(&sa, 0, sizeof(sa)); sa.sin_len = sizeof(sa); sa.sin_family = AF_INET; sa.sin_addr.s_addr = htonl((addrs->address[0] << 24) | (addrs->address[1] << 16) | (addrs->address[2] << 8) | (addrs->address[3] << 0)); sa.sin_port = htons(addrs->address[4]) << 8 | htons(addrs->address[5]) << 0; if (connect(addrs->socket, (struct sockaddr *)&sa, sa.sin_len) == -1) { syslog(LOG_ERR, "connect(%s,%u): %m", inet_ntoa(sa.sin_addr), ntohs(sa.sin_port)); (void)close(addrs->socket); return (SNMP_ERR_GENERR); } addrs->status = RowStatus_active; return (SNMP_ERR_NOERROR); } int target_delete_address(struct target_address *addrs) { SLIST_REMOVE(&target_addresslist, addrs, target_address, ta); if (addrs->status == RowStatus_active) close(addrs->socket); free(addrs); return (0); } struct target_param * target_first_param(void) { return (SLIST_FIRST(&target_paramlist)); } struct target_param * target_next_param(struct target_param *param) { if (param == NULL) return (NULL); return (SLIST_NEXT(param, tp)); } struct target_param * target_new_param(char *pname) { int cmp; struct target_param *param, *temp, *prev; SLIST_FOREACH(param, &target_paramlist, tp) if (strcmp(pname, param->name) == 0) return (NULL); if ((param = (struct target_param *)malloc(sizeof(*param))) == NULL) return (NULL); memset(param, 0, sizeof(*param)); strlcpy(param->name, pname, sizeof(param->name)); if ((prev = SLIST_FIRST(&target_paramlist)) == NULL || strcmp(pname, prev->name) < 0) { SLIST_INSERT_HEAD(&target_paramlist, param, tp); return (param); } SLIST_FOREACH(temp, &target_paramlist, tp) { if ((cmp = strcmp(pname, temp->name)) <= 0) break; prev = temp; } if (temp == NULL || cmp < 0) SLIST_INSERT_AFTER(prev, param, tp); else if (cmp > 0) SLIST_INSERT_AFTER(temp, param, tp); else { syslog(LOG_ERR, "Target parameter %s exists", param->name); free(param); return (NULL); } return (param); } int target_delete_param(struct target_param *param) { SLIST_REMOVE(&target_paramlist, param, target_param, tp); free(param); return (0); } struct target_notify * target_first_notify(void) { return (SLIST_FIRST(&target_notifylist)); } struct target_notify * target_next_notify(struct target_notify *notify) { if (notify == NULL) return (NULL); return (SLIST_NEXT(notify, tn)); } struct target_notify * target_new_notify(char *nname) { int cmp; struct target_notify *notify, *temp, *prev; SLIST_FOREACH(notify, &target_notifylist, tn) if (strcmp(nname, notify->name) == 0) return (NULL); if ((notify = (struct target_notify *)malloc(sizeof(*notify))) == NULL) return (NULL); memset(notify, 0, sizeof(*notify)); strlcpy(notify->name, nname, sizeof(notify->name)); if ((prev = SLIST_FIRST(&target_notifylist)) == NULL || strcmp(nname, prev->name) < 0) { SLIST_INSERT_HEAD(&target_notifylist, notify, tn); return (notify); } SLIST_FOREACH(temp, &target_notifylist, tn) { if ((cmp = strcmp(nname, temp->name)) <= 0) break; prev = temp; } if (temp == NULL || cmp < 0) SLIST_INSERT_AFTER(prev, notify, tn); else if (cmp > 0) SLIST_INSERT_AFTER(temp, notify, tn); else { syslog(LOG_ERR, "Notification target %s exists", notify->name); free(notify); return (NULL); } return (notify); } int target_delete_notify(struct target_notify *notify) { SLIST_REMOVE(&target_notifylist, notify, target_notify, tn); free(notify); return (0); } void target_flush_all(void) { struct target_address *addrs; struct target_param *param; struct target_notify *notify; while ((addrs = SLIST_FIRST(&target_addresslist)) != NULL) { SLIST_REMOVE_HEAD(&target_addresslist, ta); if (addrs->status == RowStatus_active) close(addrs->socket); free(addrs); } SLIST_INIT(&target_addresslist); while ((param = SLIST_FIRST(&target_paramlist)) != NULL) { SLIST_REMOVE_HEAD(&target_paramlist, tp); free(param); } SLIST_INIT(&target_paramlist); while ((notify = SLIST_FIRST(&target_notifylist)) != NULL) { SLIST_REMOVE_HEAD(&target_notifylist, tn); free(notify); } SLIST_INIT(&target_notifylist); } Index: head/contrib/bsnmp/snmpd/tree.def =================================================================== --- head/contrib/bsnmp/snmpd/tree.def (revision 310647) +++ head/contrib/bsnmp/snmpd/tree.def (revision 310648) @@ -1,222 +1,222 @@ # # 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: tree.def 517 2006-10-31 08:52:04Z brandt_h $ # # System group and private Begemot SNMPd MIB. # #include "tc.def" typedef RowStatus ENUM ( 1 active 2 notInService 3 notReady 4 createAndGo 5 createAndWait 6 destroy ) (1 internet (2 mgmt (1 mibII (1 system # # The standard System group -# +# (1 sysDescr OCTETSTRING op_system_group GET) (2 sysObjectId OID op_system_group GET) (3 sysUpTime TIMETICKS op_system_group GET) (4 sysContact OCTETSTRING op_system_group GET SET) (5 sysName OCTETSTRING op_system_group GET SET) (6 sysLocation OCTETSTRING op_system_group GET SET) (7 sysServices INTEGER op_system_group GET) (8 sysORLastChange TIMETICKS op_system_group GET) (9 sysORTable (1 sysOREntry : INTEGER op_or_table (1 sysORIndex INTEGER) (2 sysORID OID GET) (3 sysORDescr OCTETSTRING GET) (4 sysORUpTime TIMETICKS GET) )) ) (11 snmp (1 snmpInPkts COUNTER op_snmp GET) (3 snmpInBadVersions COUNTER op_snmp GET) (4 snmpInBadCommunityNames COUNTER op_snmp GET) (5 snmpInBadCommunityUses COUNTER op_snmp GET) (6 snmpInASNParseErrs COUNTER op_snmp GET) (30 snmpEnableAuthenTraps INTEGER op_snmp GET SET) (31 snmpSilentDrops COUNTER op_snmp GET) (32 snmpProxyDrops COUNTER op_snmp GET) ) )) (4 private (1 enterprises # # FreeBSD stuff # (2238 freeBSD (4 freeBSDVersion) ) # # Private Begemot Stuff # (12325 fokus (1 begemot # # Daemon infrastructure # (1 begemotSnmpd (1 begemotSnmpdObjects # # Configuration # (1 begemotSnmpdConfig (1 begemotSnmpdTransmitBuffer INTEGER op_snmpd_config GET SET) (2 begemotSnmpdReceiveBuffer INTEGER op_snmpd_config GET SET) (3 begemotSnmpdCommunityDisable INTEGER op_snmpd_config GET SET) (4 begemotSnmpdTrap1Addr IPADDRESS op_snmpd_config GET SET) (5 begemotSnmpdVersionEnable UNSIGNED32 op_snmpd_config GET SET) ) (2 begemotTrapSinkTable (1 begemotTrapSinkEntry : IPADDRESS INTEGER op_trapsink (1 begemotTrapSinkAddr IPADDRESS) (2 begemotTrapSinkPort INTEGER) (3 begemotTrapSinkStatus INTEGER GET SET) (4 begemotTrapSinkComm OCTETSTRING GET SET) (5 begemotTrapSinkVersion INTEGER GET SET) ) ) # # Port table # (4 begemotSnmpdPortTable (1 begemotSnmpdPortEntry : IPADDRESS INTEGER op_snmp_port (1 begemotSnmpdPortAddress IPADDRESS) (2 begemotSnmpdPortPort UNSIGNED32) (3 begemotSnmpdPortStatus INTEGER GET SET) )) # # Community table # (5 begemotSnmpdCommunityTable (1 begemotSnmpdCommunityEntry : OCTETSTRING UNSIGNED32 op_community (1 begemotSnmpdCommunityModule OCTETSTRING) (2 begemotSnmpdCommunityIndex UNSIGNED32) (3 begemotSnmpdCommunityString OCTETSTRING GET SET) (4 begemotSnmpdCommunityDescr OCTETSTRING GET) )) # # Module table # (6 begemotSnmpdModuleTable (1 begemotSnmpdModuleEntry : OCTETSTRING op_modules (1 begemotSnmpdModuleSection OCTETSTRING) (2 begemotSnmpdModulePath OCTETSTRING GET SET) (3 begemotSnmpdModuleComment OCTETSTRING GET) )) # # Statistics # (7 begemotSnmpdStats (1 begemotSnmpdStatsNoRxBufs COUNTER op_snmpd_stats GET) (2 begemotSnmpdStatsNoTxBufs COUNTER op_snmpd_stats GET) (3 begemotSnmpdStatsInTooLongPkts COUNTER op_snmpd_stats GET) (4 begemotSnmpdStatsInBadPduTypes COUNTER op_snmpd_stats GET)) # # Debugging # (8 begemotSnmpdDebug (1 begemotSnmpdDebugDumpPdus INTEGER op_debug GET SET) (2 begemotSnmpdDebugSnmpTrace UNSIGNED32 op_debug GET SET) (3 begemotSnmpdDebugSyslogPri INTEGER op_debug GET SET)) # # Local (UNIX domain) port table # (9 begemotSnmpdLocalPortTable (1 begemotSnmpdLocalPortEntry : OCTETSTRING op_lsock_port (1 begemotSnmpdLocalPortPath OCTETSTRING) (2 begemotSnmpdLocalPortStatus INTEGER GET SET) (3 begemotSnmpdLocalPortType INTEGER GET SET) )) (10 begemotSnmpdTransportMappings (1 begemotSnmpdTransportTable (1 begemotSnmpdTransportEntry : OCTETSTRING op_transport_table (1 begemotSnmpdTransportName OCTETSTRING) (2 begemotSnmpdTransportStatus INTEGER GET) (3 begemotSnmpdTransportOid OID GET) )) (2 begemotSnmpdTransUdp OID op_transport_dummy) (3 begemotSnmpdTransLsock OID op_transport_dummy) ) ) (2 begemotSnmpdDefs (1 begemotSnmpdAgent (1 begemotSnmpdAgentFreeBSD OID op_dummy) ) ) ) )) ) ) (6 snmpV2 (3 snmpModules (1 snmpMIB (1 snmpMIBObjects (4 snmpTrap (1 snmpTrapOID OID op_snmp_trap) ) (5 snmpTraps (1 coldStart OID op_snmp_trap) (2 warmStart OID op_snmp_trap) (5 authenticationFailure OID op_snmp_trap) ) (6 snmpSet (1 snmpSetSerialNo INTEGER op_snmp_set GET SET) ) ) ) (10 snmpFrameworkMIB (2 snmpFrameworkMIBObjects (1 snmpEngine (1 snmpEngineID OCTETSTRING | SnmpEngineID op_snmp_engine GET) (2 snmpEngineBoots INTEGER op_snmp_engine GET) (3 snmpEngineTime INTEGER op_snmp_engine GET) (4 snmpEngineMaxMessageSize INTEGER op_snmp_engine GET) ) ) ) )) ) Index: head/usr.sbin/bsnmpd/modules/snmp_atm/BEGEMOT-ATM-FREEBSD-MIB.txt =================================================================== --- head/usr.sbin/bsnmpd/modules/snmp_atm/BEGEMOT-ATM-FREEBSD-MIB.txt (revision 310647) +++ head/usr.sbin/bsnmpd/modules/snmp_atm/BEGEMOT-ATM-FREEBSD-MIB.txt (revision 310648) @@ -1,99 +1,99 @@ -- -- Copyright (c) 2004 -- Hartmut Brandt. -- All rights reserved. -- -- Author: Hartmut 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. --- +-- -- $FreeBSD$ -- -- Private Begemot MIB for ATM interfaces on FreeBSD -- BEGEMOT-ATM-FREEBSD-MIB DEFINITIONS ::= BEGIN IMPORTS MODULE-IDENTITY, OBJECT-TYPE FROM SNMPv2-SMI NgNodeIdOrZero FROM BEGEMOT-NETGRAPH-MIB begemotAtmSysGroup, begemotAtmIfEntry FROM BEGEMOT-ATM-MIB; begemotAtmFreeBSDGroup MODULE-IDENTITY LAST-UPDATED "200408060000Z" ORGANIZATION "German Aerospace Centre" CONTACT-INFO " Hartmut Brandt Postal: German Aerospace Centre (DLR) Institute of Communications and Navigation 82234 Wessling Germany Fax: +49 8153 28 2844 E-mail: harti@freebsd.org" DESCRIPTION "The FreeBSD specific Begemot MIB for ATM interfaces." ::= { begemotAtmSysGroup 1 } -- Netgraph begemotAtmNgGroup OBJECT IDENTIFIER ::= { begemotAtmFreeBSDGroup 1 } -- -- Interfaces table -- begemotAtmNgIfTable OBJECT-TYPE SYNTAX SEQUENCE OF BegemotAtmNgIfEntry MAX-ACCESS not-accessible STATUS current DESCRIPTION "This table contains an entry for each hardware ATM interface. The table is indexed by the interface index." ::= { begemotAtmNgGroup 1 } begemotAtmNgIfEntry OBJECT-TYPE SYNTAX BegemotAtmNgIfEntry MAX-ACCESS not-accessible STATUS current DESCRIPTION "This is a table entry describing one ATM hardware interface." AUGMENTS { begemotAtmIfEntry } ::= { begemotAtmNgIfTable 1 } BegemotAtmNgIfEntry ::= SEQUENCE { begemotAtmNgIfNodeId NgNodeIdOrZero } begemotAtmNgIfNodeId OBJECT-TYPE SYNTAX NgNodeIdOrZero MAX-ACCESS read-only STATUS current DESCRIPTION "The netgraph node id of the interface. If there is no node corresponding to the interface, this is 0." ::= { begemotAtmNgIfEntry 1 } END Index: head/usr.sbin/bsnmpd/modules/snmp_atm/atm_sys.c =================================================================== --- head/usr.sbin/bsnmpd/modules/snmp_atm/atm_sys.c (revision 310647) +++ head/usr.sbin/bsnmpd/modules/snmp_atm/atm_sys.c (revision 310648) @@ -1,301 +1,301 @@ /* * Copyright (c) 2001-2002 * Fraunhofer Institute for Open Communication Systems (FhG Fokus). * All rights reserved. * Copyright (c) 2003-2004 * Hartmut Brandt. * All rights reserved. * * Author: Hartmut 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. * * $FreeBSD$ * * SNMP module for ATM hardware interfaces - FreeBSD/Ng specific part. */ #include "atm.h" #include "atm_tree.h" #include "atm_oid.h" #include #include #include #include #include #include #include static const struct hwinfo { const char *device; const char *vendor; } hwinfo[] = { ATM_DEVICE_NAMES }; struct atmif_sys { ng_ID_t atm_node; void *regc; /* cookie registration */ }; /* * Find the interface for a given node */ struct atmif * atm_node2if(u_int node) { struct atmif_priv *aif; if (node != 0) TAILQ_FOREACH(aif, &atmif_list, link) if (aif->sys->atm_node == node) return (&aif->pub); return (NULL); } u_int atm_if2node(struct atmif *pub) { struct atmif_priv *aif = (struct atmif_priv *)pub; return (aif->sys->atm_node); } /* * Destroy system dependend stuff. */ void atmif_sys_destroy(struct atmif_priv *aif) { ng_unregister_cookie(aif->sys->regc); free(aif->sys); free(aif->pub.mib); } /* * Handle a message from the ATM node */ static void handle_atm_message(const struct ng_mesg *mesg, const char *path __unused, ng_ID_t node, void *uarg) { struct atmif_priv *aif = uarg; enum atmif_carrier_state ost; switch (mesg->header.cmd) { case NGM_ATM_IF_CHANGE: { const struct ngm_atm_if_change *arg; ost = aif->pub.carrier; if (mesg->header.arglen != sizeof(*arg)) { syslog(LOG_ERR, "ATM_IF_CHANGE: wrong size"); atmif_check_carrier(aif); return; } arg = (const struct ngm_atm_if_change *) (const void *)mesg->data; if (arg->carrier) aif->pub.carrier = ATMIF_CARRIER_ON; else aif->pub.carrier = ATMIF_CARRIER_OFF; if (ost != aif->pub.carrier) atmif_send_notification(aif, ATMIF_NOTIFY_CARRIER, (uintptr_t)ost); return; } case NGM_ATM_VCC_CHANGE: { const struct ngm_atm_vcc_change *arg; if (mesg->header.arglen != sizeof(*arg)) { syslog(LOG_ERR, "ATM_VCC_CHANGE: wrong size"); return; } arg = (const struct ngm_atm_vcc_change *) (const void *)mesg->data; atmif_send_notification(aif, ATMIF_NOTIFY_VCC, (uintptr_t)(((arg->vpi & 0xff) << 24) | ((arg->vci & 0xffff) << 8) | (arg->state & 1))); return; } } syslog(LOG_WARNING, "spurious message %u from node [%x]", mesg->header.cmd, node); } /* * Attach to an ATM interface */ int atmif_sys_attach_if(struct atmif_priv *aif) { struct ng_mesg *resp, *resp1; struct namelist *list; u_int i; if ((aif->sys = malloc(sizeof(*aif->sys))) == NULL) { syslog(LOG_CRIT, "out of memory"); return (-1); } memset(aif->sys, 0, sizeof(*aif->sys)); if ((aif->pub.mib = malloc(sizeof(*aif->pub.mib))) == NULL) { free(aif->sys); syslog(LOG_CRIT, "out of memory"); return (-1); } atmif_sys_fill_mib(aif); /* * Get ATM node Id. Must do it the hard way by scanning all nodes * because the name may be wrong. */ if ((resp = ng_dialog_id(snmp_node, NGM_GENERIC_COOKIE, NGM_LISTNODES, NULL, 0)) == NULL) { syslog(LOG_ERR, "cannot fetch node list: %m"); free(aif->sys); return (-1); } list = (struct namelist *)(void *)resp->data; for (i = 0; i < list->numnames; i++) { if (strcmp(list->nodeinfo[i].type, NG_ATM_NODE_TYPE) != 0) continue; if ((resp1 = ng_dialog_id(list->nodeinfo[i].id, NGM_ATM_COOKIE, NGM_ATM_GET_IFNAME, NULL, 0)) == NULL) continue; if (strcmp(resp1->data, aif->pub.ifp->name) == 0) { free(resp1); break; } free(resp1); } if (i == list->numnames) aif->sys->atm_node = 0; else aif->sys->atm_node = list->nodeinfo[i].id; free(resp); if ((aif->sys->regc = ng_register_cookie(module, NGM_ATM_COOKIE, aif->sys->atm_node, handle_atm_message, aif)) == NULL) { syslog(LOG_ERR, "cannot register cookie: %m"); free(aif->sys); return (-1); } return (0); } -/* +/* * Table of all ATM interfaces - Ng part */ int op_atmif_ng(struct snmp_context *ctx __unused, struct snmp_value *value, u_int sub, u_int vindex __unused, enum snmp_op op) { struct atmif_priv *aif; int err; if ((err = atmif_get_aif(value, sub, op, &aif)) != SNMP_ERR_NOERROR) return (err); if (op == SNMP_OP_SET) { switch (value->var.subs[sub - 1]) { default: return (SNMP_ERR_NOT_WRITEABLE); } } switch (value->var.subs[sub - 1]) { case LEAF_begemotAtmIfNodeId: value->v.uint32 = aif->sys->atm_node; return (SNMP_ERR_NOERROR); } abort(); } /* * Get vendor string */ int atm_sys_get_hw_vendor(struct atmif_priv *aif, struct snmp_value *value) { if (aif->pub.mib->device >= sizeof(hwinfo) / sizeof(hwinfo[0])) return (string_get(value, "unknown", -1)); return (string_get(value, hwinfo[aif->pub.mib->device].vendor, -1)); } /* * Get device string */ int atm_sys_get_hw_device(struct atmif_priv *aif, struct snmp_value *value) { if (aif->pub.mib->device >= sizeof(hwinfo) / sizeof(hwinfo[0])) return (string_get(value, "unknown", -1)); return (string_get(value, hwinfo[aif->pub.mib->device].device, -1)); } /* * Extract the ATM MIB from the interface's private MIB */ void atmif_sys_fill_mib(struct atmif_priv *aif) { struct ifatm_mib *mib; if (aif->pub.ifp->specmiblen != sizeof(struct ifatm_mib)) { syslog(LOG_ERR, "atmif MIB has wrong size %zu", aif->pub.ifp->specmiblen); memset(aif->pub.mib, 0, sizeof(*aif->pub.mib)); aif->pub.mib->version = 0; return; } mib = (struct ifatm_mib *)aif->pub.ifp->specmib; aif->pub.mib->device = mib->device; aif->pub.mib->serial = mib->serial; aif->pub.mib->hw_version = mib->hw_version; aif->pub.mib->sw_version = mib->sw_version; aif->pub.mib->media = mib->media; memcpy(aif->pub.mib->esi, mib->esi, 6); aif->pub.mib->pcr = mib->pcr; aif->pub.mib->vpi_bits = mib->vpi_bits; aif->pub.mib->vci_bits = mib->vci_bits; aif->pub.mib->max_vpcs = mib->max_vpcs; aif->pub.mib->max_vccs = mib->max_vccs; } Index: head/usr.sbin/bsnmpd/modules/snmp_bridge/BEGEMOT-BRIDGE-MIB.txt =================================================================== --- head/usr.sbin/bsnmpd/modules/snmp_bridge/BEGEMOT-BRIDGE-MIB.txt (revision 310647) +++ head/usr.sbin/bsnmpd/modules/snmp_bridge/BEGEMOT-BRIDGE-MIB.txt (revision 310648) @@ -1,1169 +1,1169 @@ -- -- Copyright (C) 2006 Shteryana Shopova -- All rights reserved. -- -- 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. -- -- $FreeBSD$ -- BEGEMOT-BRIDGE-MIB DEFINITIONS ::= BEGIN IMPORTS MODULE-IDENTITY, OBJECT-TYPE, NOTIFICATION-TYPE, Counter32, Integer32, TimeTicks, mib-2 FROM SNMPv2-SMI TEXTUAL-CONVENTION, MacAddress, TruthValue, RowStatus FROM SNMPv2-TC BridgeId, Timeout FROM BRIDGE-MIB InterfaceIndex FROM IF-MIB begemot FROM BEGEMOT-MIB; begemotBridge MODULE-IDENTITY LAST-UPDATED "201612170000Z" ORGANIZATION "Sofia University St. Kliment Ohridski" CONTACT-INFO " Shteryana Shopova Postal: Faculty of Mathematics and Informatics 5 James Bourchier Blvd. 1164 Sofia Bulgaria Fax: +359 2 687 180 E-Mail: syrinx@FreeBSD.org" DESCRIPTION "The Begemot MIB for managing bridge interfaces." REVISION "201612170000Z" DESCRIPTION "Address some minor typos and grammar mistakes." REVISION "200708060000Z" DESCRIPTION "Third revision adds begemotBridgeBasePortPrivate object." REVISION "200611210000Z" DESCRIPTION "Second revision adds support for monitoring RSTP specific variables." REVISION "200607270000Z" DESCRIPTION "Initial revision." ::= { begemot 205 } -- ---------------------------------------------------------- -- BridgeIfName ::= TEXTUAL-CONVENTION DISPLAY-HINT "16a" STATUS current DESCRIPTION "Name of a bridge interface." SYNTAX OCTET STRING (SIZE(1..16)) BridgeIfNameOrEmpty ::= TEXTUAL-CONVENTION DISPLAY-HINT "16a" STATUS current DESCRIPTION "Name of a bridge interface." SYNTAX OCTET STRING (SIZE(0..16)) BridgePortId ::= TEXTUAL-CONVENTION DISPLAY-HINT "1x.1x" STATUS current DESCRIPTION "A port identifier that contains a bridge port's STP priority in the first octet and the port number in the second octet." SYNTAX OCTET STRING (SIZE(2)) -- ---------------------------------------------------------- -- -- subtrees in the Begemot Bridge MIB -- ---------------------------------------------------------- -- begemotBridgeNotifications OBJECT IDENTIFIER ::= { begemotBridge 0 } begemotBridgeBase OBJECT IDENTIFIER ::= { begemotBridge 1 } begemotBridgeStp OBJECT IDENTIFIER ::= { begemotBridge 2 } begemotBridgeTp OBJECT IDENTIFIER ::= { begemotBridge 3 } begemotBridgePf OBJECT IDENTIFIER ::= { begemotBridge 4 } begemotBridgeConfigObjects OBJECT IDENTIFIER ::= { begemotBridge 5 } -- ---------------------------------------------------------- -- --- the base Bridge interface table +-- the base Bridge interface table -- ---------------------------------------------------------- -- begemotBridgeBaseTable OBJECT-TYPE SYNTAX SEQUENCE OF BegemotBridgeBaseEntry MAX-ACCESS not-accessible STATUS current DESCRIPTION "A table that contains generic information for each bridge interface on the managed device." ::= { begemotBridgeBase 1 } begemotBridgeBaseEntry OBJECT-TYPE SYNTAX BegemotBridgeBaseEntry MAX-ACCESS not-accessible STATUS current DESCRIPTION "A list of information for the bridge interfaces on the managed device." INDEX { begemotBridgeBaseName } ::= { begemotBridgeBaseTable 1 } BegemotBridgeBaseEntry ::= SEQUENCE { begemotBridgeBaseName BridgeIfName, begemotBridgeBaseAddress MacAddress, begemotBridgeBaseNumPorts Integer32, begemotBridgeBaseType INTEGER, begemotBridgeBaseStatus RowStatus } begemotBridgeBaseName OBJECT-TYPE SYNTAX BridgeIfName MAX-ACCESS read-only STATUS current DESCRIPTION "The name of the bridge interface for which this entry contains management information." ::= { begemotBridgeBaseEntry 1 } begemotBridgeBaseAddress OBJECT-TYPE SYNTAX MacAddress MAX-ACCESS read-only STATUS current DESCRIPTION "The MAC address of the bridge interface." ::= { begemotBridgeBaseEntry 2 } begemotBridgeBaseNumPorts OBJECT-TYPE SYNTAX Integer32 MAX-ACCESS read-only STATUS current DESCRIPTION "The number of ports, members of this bridge." ::= { begemotBridgeBaseEntry 3 } begemotBridgeBaseType OBJECT-TYPE SYNTAX INTEGER { unknown(1), transparent-only(2), sourceroute-only(3), srt(4) } MAX-ACCESS read-only STATUS current DESCRIPTION "Indicates what type of bridging this bridge can perform." ::= { begemotBridgeBaseEntry 4 } -begemotBridgeBaseStatus OBJECT-TYPE +begemotBridgeBaseStatus OBJECT-TYPE SYNTAX RowStatus MAX-ACCESS read-create STATUS current DESCRIPTION "Used to create/destroy bridge interfaces on the managed device." ::= { begemotBridgeBaseEntry 5 } -- ---------------------------------------------------------- -- --- the base Bridge ports table +-- the base Bridge ports table -- ---------------------------------------------------------- -- begemotBridgeBasePortTable OBJECT-TYPE SYNTAX SEQUENCE OF BegemotBridgeBasePortEntry MAX-ACCESS not-accessible STATUS current DESCRIPTION "A table containing generic information about ports, members of each bridge interface." ::= { begemotBridgeBase 2 } begemotBridgeBasePortEntry OBJECT-TYPE SYNTAX BegemotBridgeBasePortEntry MAX-ACCESS not-accessible STATUS current DESCRIPTION "A list of information about a specific port, member of a bridge interface." INDEX { begemotBridgeBaseName, begemotBridgeBasePortIfIndex } ::= { begemotBridgeBasePortTable 1 } BegemotBridgeBasePortEntry ::= SEQUENCE { begemotBridgeBasePort Integer32, begemotBridgeBasePortIfIndex InterfaceIndex, begemotBridgeBaseSpanEnabled INTEGER, begemotBridgeBasePortDelayExceededDiscards Counter32, begemotBridgeBasePortMtuExceededDiscards Counter32, begemotBridgeBasePortStatus RowStatus, begemotBridgeBasePortPrivate TruthValue } begemotBridgeBasePort OBJECT-TYPE SYNTAX Integer32 (1..65535) MAX-ACCESS read-only STATUS current DESCRIPTION "The system interface index of the interface corresponding to this port." ::= { begemotBridgeBasePortEntry 1 } begemotBridgeBasePortIfIndex OBJECT-TYPE SYNTAX InterfaceIndex MAX-ACCESS read-only STATUS current DESCRIPTION "The value of the instance of the ifIndex object, defined in IF-MIB, for the interface corresponding to this port." ::= { begemotBridgeBasePortEntry 2 } begemotBridgeBaseSpanEnabled OBJECT-TYPE SYNTAX INTEGER { enabled(1), disabled(2) } MAX-ACCESS read-write STATUS current DESCRIPTION "The value of this objects reflects whether the port is a span port on the specified bridge interface." ::= { begemotBridgeBasePortEntry 3 } begemotBridgeBasePortDelayExceededDiscards OBJECT-TYPE SYNTAX Counter32 MAX-ACCESS read-only STATUS current DESCRIPTION "The number of frames discarded by this port due to excessive transit delay through the bridge." ::= { begemotBridgeBasePortEntry 4 } begemotBridgeBasePortMtuExceededDiscards OBJECT-TYPE SYNTAX Counter32 MAX-ACCESS read-only STATUS current DESCRIPTION "The number of frames discarded by this port due to an excessive size." ::= { begemotBridgeBasePortEntry 5 } begemotBridgeBasePortStatus OBJECT-TYPE SYNTAX RowStatus MAX-ACCESS read-create STATUS current DESCRIPTION "Used to control addition of member ports to or removal of member ports from a specified bridge." ::= { begemotBridgeBasePortEntry 6 } begemotBridgeBasePortPrivate OBJECT-TYPE SYNTAX TruthValue MAX-ACCESS read-write STATUS current DESCRIPTION "The value of this objects reflects whether the port has a PRIVATE flag set. A port with this flags set can only communicate with ports not having the PRIVATE flag set." ::= { begemotBridgeBasePortEntry 7 } -- ---------------------------------------------------------- -- --- the Bridge interface STP table +-- the Bridge interface STP table -- ---------------------------------------------------------- -- begemotBridgeStpTable OBJECT-TYPE SYNTAX SEQUENCE OF BegemotBridgeStpEntry MAX-ACCESS not-accessible STATUS current DESCRIPTION "A table that contains Spanning Tree Protocol information for each bridge interface on the managed device." ::= { begemotBridgeStp 1 } begemotBridgeStpEntry OBJECT-TYPE SYNTAX BegemotBridgeStpEntry MAX-ACCESS not-accessible STATUS current DESCRIPTION "A list of information about the Spanning Tree Protocol operation on a bridge interface." AUGMENTS { begemotBridgeBaseEntry } ::= { begemotBridgeStpTable 1 } BegemotBridgeStpEntry ::= SEQUENCE { begemotBridgeStpProtocolSpecification INTEGER, begemotBridgeStpPriority Integer32, begemotBridgeStpTimeSinceTopologyChange TimeTicks, begemotBridgeStpTopChanges Counter32, begemotBridgeStpDesignatedRoot BridgeId, begemotBridgeStpRootCost Integer32, begemotBridgeStpRootPort Integer32, begemotBridgeStpMaxAge Timeout, begemotBridgeStpHelloTime Timeout, begemotBridgeStpHoldTime Integer32, begemotBridgeStpForwardDelay Timeout, begemotBridgeStpBridgeMaxAge Timeout, begemotBridgeStpBridgeHelloTime Timeout, begemotBridgeStpBridgeForwardDelay Timeout, begemotBridgeStpVersion INTEGER, begemotBridgeStpTxHoldCount Integer32 } begemotBridgeStpProtocolSpecification OBJECT-TYPE SYNTAX INTEGER { unknown(1), decLb100(2), ieee8021d(3) } MAX-ACCESS read-only STATUS current DESCRIPTION "The Spanning Tree Protocol version being run on the bridge interface. The value 'decLb100(2)' indicates the DEC LANbridge 100 Spanning Tree protocol, 'ieee8021d(3)' indicates the bridge is running IEEE 802.1D STP implementation." ::= { begemotBridgeStpEntry 1 } begemotBridgeStpPriority OBJECT-TYPE SYNTAX Integer32 (0..65535) MAX-ACCESS read-write STATUS current DESCRIPTION "The priority value of the bridge interface forming the first two octets of the bridge identifier. Acceptable values are 0-61440, in steps of 4096." ::= { begemotBridgeStpEntry 2 } begemotBridgeStpTimeSinceTopologyChange OBJECT-TYPE SYNTAX TimeTicks UNITS "centi-seconds" MAX-ACCESS read-only STATUS current DESCRIPTION "The time (in hundreds of a second) since a topology change was last detected by this bridge." ::= { begemotBridgeStpEntry 3 } begemotBridgeStpTopChanges OBJECT-TYPE SYNTAX Counter32 MAX-ACCESS read-only STATUS current DESCRIPTION "The number of times a topology change was detected by the bridge interface since the management entity was initialized or reset." ::= { begemotBridgeStpEntry 4 } begemotBridgeStpDesignatedRoot OBJECT-TYPE SYNTAX BridgeId MAX-ACCESS read-only STATUS current DESCRIPTION "The bridge identifier of the root of the spanning tree as calculated by the Spanning Tree Protocol." ::= { begemotBridgeStpEntry 5 } begemotBridgeStpRootCost OBJECT-TYPE SYNTAX Integer32 MAX-ACCESS read-only STATUS current DESCRIPTION "The cost of the path from this bridge to the root bridge." ::= { begemotBridgeStpEntry 6 } begemotBridgeStpRootPort OBJECT-TYPE SYNTAX Integer32 MAX-ACCESS read-only STATUS current DESCRIPTION "The port number of the port that offers the lowest cost path from this bridge to the root bridge of the spanning tree. If this bridge is the root bridge, this object shall have a value of zero." ::= { begemotBridgeStpEntry 7 } begemotBridgeStpMaxAge OBJECT-TYPE SYNTAX Timeout UNITS "centi-seconds" MAX-ACCESS read-only STATUS current DESCRIPTION "The maximum age of Spanning Tree Protocol information received from the network on any port, before that information is discarded. This is the actual value that the bridge is currently using." ::= { begemotBridgeStpEntry 8 } begemotBridgeStpHelloTime OBJECT-TYPE SYNTAX Timeout UNITS "centi-seconds" MAX-ACCESS read-only STATUS current DESCRIPTION "The amount of time between transmission of Configuration BPDUs by this bridge on any port, when it is the root of the spanning tree or is trying to become so. This is the actual value that this bridge is currently using." ::= { begemotBridgeStpEntry 9 } begemotBridgeStpHoldTime OBJECT-TYPE SYNTAX Integer32 UNITS "centi-seconds" MAX-ACCESS read-only STATUS current DESCRIPTION "This time value determines the interval length during which no more than two Configuration BPDUs shall be transmitted by this node, in units of hundredths of a second." ::= { begemotBridgeStpEntry 10 } begemotBridgeStpForwardDelay OBJECT-TYPE SYNTAX Timeout UNITS "centi-seconds" MAX-ACCESS read-only STATUS current DESCRIPTION "This value, measured in units of hundredths of a second determines how long a port will stay consecutively in the Listening and Learning states before transitioning to Forwarding state. This is the actual value currently used by the bridge as opposed to begemotBridgeStpBridgeForwardDelay, which is the value this and all bridges participating in the spanning tree were to use, if this was the root bridge." ::= { begemotBridgeStpEntry 11 } begemotBridgeStpBridgeMaxAge OBJECT-TYPE SYNTAX Timeout (600..4000) UNITS "centi-seconds" MAX-ACCESS read-write STATUS current DESCRIPTION "The value that all bridges participating in the spanning tree would use for MaxAge if this bridge was the root of the spanning tree." ::= { begemotBridgeStpEntry 12 } begemotBridgeStpBridgeHelloTime OBJECT-TYPE SYNTAX Timeout (100..1000) UNITS "centi-seconds" MAX-ACCESS read-write STATUS current DESCRIPTION "The value that all bridges participating in the spanning tree would use for HelloTime if this bridge was the root of the spanning tree." ::= { begemotBridgeStpEntry 13 } begemotBridgeStpBridgeForwardDelay OBJECT-TYPE SYNTAX Timeout (400..3000) UNITS "centi-seconds" MAX-ACCESS read-write STATUS current DESCRIPTION "The value that all bridges participating in the spanning tree would use for ForwardDelay if this bridge was the root of the spanning tree." ::= { begemotBridgeStpEntry 14 } begemotBridgeStpVersion OBJECT-TYPE SYNTAX INTEGER { stpCompatible(0), rstp(2) } MAX-ACCESS read-write STATUS current DESCRIPTION "The version of Spanning Tree Protocol the bridge is currently running. The value 'stpCompatible(0)' indicates the Spanning Tree Protocol specified in IEEE 802.1D-1998 and 'rstp(2)' indicates the Rapid Spanning Tree Protocol specified in IEEE 802.1w and clause 17 of 802.1D-2004. The values are directly from the IEEE standard. New values may be defined as future versions of the protocol become available. The value of this object MUST be retained across reinitializations of the management system." DEFVAL { rstp } ::= { begemotBridgeStpEntry 15 } begemotBridgeStpTxHoldCount OBJECT-TYPE SYNTAX Integer32 (1..10) MAX-ACCESS read-write STATUS current DESCRIPTION "The value used by the Port Transmit state machine to limit the maximum transmission rate of BPDUs on the bridge interface. The value of this object MUST be retained across reinitializations of the management system." DEFVAL { 3 } ::= { begemotBridgeStpEntry 16 } -- ---------------------------------------------------------- -- --- the Bridge STP ports table +-- the Bridge STP ports table -- ---------------------------------------------------------- -- begemotBridgeStpPortTable OBJECT-TYPE SYNTAX SEQUENCE OF BegemotBridgeStpPortEntry MAX-ACCESS not-accessible STATUS current DESCRIPTION "A table containing Spanning Tree Protocol information about the members of each bridge interface." ::= { begemotBridgeStp 2 } begemotBridgeStpPortEntry OBJECT-TYPE SYNTAX BegemotBridgeStpPortEntry MAX-ACCESS not-accessible STATUS current DESCRIPTION "A list of Spanning Tree Protocol information about a specific member of a bridge interface." INDEX { begemotBridgeBaseName, begemotBridgeBasePortIfIndex } ::= { begemotBridgeStpPortTable 1 } BegemotBridgeStpPortEntry ::= SEQUENCE { begemotBridgeStpPort Integer32, begemotBridgeStpPortPriority Integer32, begemotBridgeStpPortState INTEGER, begemotBridgeStpPortEnable INTEGER, begemotBridgeStpPortPathCost Integer32, begemotBridgeStpPortDesignatedRoot BridgeId, begemotBridgeStpPortDesignatedCost Integer32, begemotBridgeStpPortDesignatedBridge BridgeId, begemotBridgeStpPortDesignatedPort BridgePortId, begemotBridgeStpPortForwardTransitions Counter32 } begemotBridgeStpPort OBJECT-TYPE SYNTAX Integer32 (1..65535) MAX-ACCESS read-only STATUS current DESCRIPTION "The system interface index of the interface corresponding to this port, for which the management entity has Spanning Tree Protocol information." ::= { begemotBridgeStpPortEntry 1 } begemotBridgeStpPortPriority OBJECT-TYPE SYNTAX Integer32 (0..255) MAX-ACCESS read-write STATUS current DESCRIPTION "The STP priority of this port that is contained in the first octet of its Port Identifier. The second octet contains the value of begemotBridgeStpPort." ::= { begemotBridgeStpPortEntry 2 } begemotBridgeStpPortState OBJECT-TYPE SYNTAX INTEGER { disabled(1), blocking(2), listening(3), learning(4), forwarding(5), broken(6) } MAX-ACCESS read-only STATUS current DESCRIPTION "The current state of the port as defined by the operation of the Spanning Tree Protocol. If the Spanning Tree Protocol is administratively disabled on the port, this object shall have value disabled(1). A value of broken(6) does not correspond to any legal state of a port, and if present should indicate error in the operation of either the Spanning Tree Protocol implementation running on the device or the management entity." ::= { begemotBridgeStpPortEntry 3 } begemotBridgeStpPortEnable OBJECT-TYPE SYNTAX INTEGER { enabled(1), disabled(2) } MAX-ACCESS read-write STATUS current DESCRIPTION "The administrative Spanning Tree Protocol state of the port - value of enabled(1) indicates that the port is participating in the Spanning Tree Protocol operation." ::= { begemotBridgeStpPortEntry 4 } begemotBridgeStpPortPathCost OBJECT-TYPE SYNTAX Integer32 (1..65535) MAX-ACCESS read-write STATUS current DESCRIPTION "The contribution of the path through this port, when the port is the Root Port, to the total cost of the path to the root bridge for this bridge." ::= { begemotBridgeStpPortEntry 5 } begemotBridgeStpPortDesignatedRoot OBJECT-TYPE SYNTAX BridgeId MAX-ACCESS read-only STATUS current DESCRIPTION "The unique Bridge Identifier of the bridge recorded as the root in the Root Identifier parameter of Configuration BPDUs transmitted by the Designated Bridge for the LAN to which the port is attached." ::= { begemotBridgeStpPortEntry 6 } begemotBridgeStpPortDesignatedCost OBJECT-TYPE SYNTAX Integer32 MAX-ACCESS read-only STATUS current DESCRIPTION "For a Designated port, the path cost (equal to the Root Path Cost of the bridge) offered to the LAN to which the port is attached otherwise the cost of the path to the Root offered by the Designated Port on the LAN to which this Port is attached." ::= { begemotBridgeStpPortEntry 7 } begemotBridgeStpPortDesignatedBridge OBJECT-TYPE SYNTAX BridgeId MAX-ACCESS read-only STATUS current DESCRIPTION "The unique Bridge Identifier of the bridge to which the port belongs, in the case when the port is a designated port, otherwise the bridge believed to be the Designated Bridge for the LAN to which this port is attached." ::= { begemotBridgeStpPortEntry 8 } begemotBridgeStpPortDesignatedPort OBJECT-TYPE SYNTAX BridgePortId MAX-ACCESS read-only STATUS current DESCRIPTION "The Port Identifier of the Bridge port, on the Designated Bridge, through which the Designated Bridge transmits the Configuration Message information stored by this port." ::= { begemotBridgeStpPortEntry 9 } begemotBridgeStpPortForwardTransitions OBJECT-TYPE SYNTAX Counter32 MAX-ACCESS read-only STATUS current DESCRIPTION "The number of times this port has transitioned from the Learning state to the Forwarding state." ::= { begemotBridgeStpPortEntry 10 } -- ---------------------------------------------------------- -- -- the Bridge STP extended ports table -- ---------------------------------------------------------- -- begemotBridgeStpExtPortTable OBJECT-TYPE SYNTAX SEQUENCE OF BegemotBridgeStpExtPortEntry MAX-ACCESS not-accessible STATUS current DESCRIPTION "A table that contains port-specific Rapid Spanning Tree information for the bridge interface members." ::= { begemotBridgeStp 3 } begemotBridgeStpExtPortEntry OBJECT-TYPE SYNTAX BegemotBridgeStpExtPortEntry MAX-ACCESS not-accessible STATUS current DESCRIPTION "A list of Rapid Spanning Tree information maintained by each bridge interface member." AUGMENTS { begemotBridgeStpPortEntry } ::= { begemotBridgeStpExtPortTable 1 } BegemotBridgeStpExtPortEntry ::= SEQUENCE { begemotBridgeStpPortProtocolMigration TruthValue, begemotBridgeStpPortAdminEdgePort TruthValue, begemotBridgeStpPortOperEdgePort TruthValue, begemotBridgeStpPortAdminPointToPoint INTEGER, begemotBridgeStpPortOperPointToPoint TruthValue, begemotBridgeStpPortAdminPathCost Integer32 } begemotBridgeStpPortProtocolMigration OBJECT-TYPE SYNTAX TruthValue MAX-ACCESS read-write STATUS current DESCRIPTION "When operating in RSTP (version 2) mode, writing true(1) to this object forces this port to transmit RSTP BPDUs. Any other operation on this object has no effect and it always returns false(2) when read." ::= { begemotBridgeStpExtPortEntry 1 } begemotBridgeStpPortAdminEdgePort OBJECT-TYPE SYNTAX TruthValue MAX-ACCESS read-write STATUS current DESCRIPTION "The administrative value of the Edge Port parameter. A value of true(1) indicates that this port should be assumed as an edge-port, and a value of false(2) indicates that this port should be assumed as a non-edge-port. Setting this object will also cause the corresponding instance of begemotBridgeStpPortOperEdgePort to change to the same value. Note that even when this object's value is true, the value of the corresponding instance of begemotBridgeStpPortOperEdgePort can be false if a BPDU has been received. The value of this object MUST be retained across reinitializations of the management system." ::= { begemotBridgeStpExtPortEntry 2 } begemotBridgeStpPortOperEdgePort OBJECT-TYPE SYNTAX TruthValue MAX-ACCESS read-only STATUS current DESCRIPTION "The operational value of the Edge Port parameter. The object is initialized to the value of the corresponding instance of begemotBridgeStpPortAdminEdgePort. When the corresponding instance of begemotBridgeStpPortAdminEdgePort is set, this object will be changed as well. This object will also be changed to false on reception of a BPDU." ::= { begemotBridgeStpExtPortEntry 3 } begemotBridgeStpPortAdminPointToPoint OBJECT-TYPE SYNTAX INTEGER { forceTrue(0), forceFalse(1), auto(2) } MAX-ACCESS read-write STATUS current DESCRIPTION "The administrative point-to-point status of the LAN segment attached to this port, using the enumeration values of the IEEE 802.1w clause. A value of forceTrue(0) indicates that this port should always be treated as if it is connected to a point-to-point link. A value of forceFalse(1) indicates that this port should be treated as having a shared media connection. A value of auto(2) indicates that this port is considered to have a point-to-point link if it is an Aggregator and all of its members are aggregatable, or if the MAC entity is configured for full duplex operation, either through auto-negotiation or by management means. Manipulating this object changes the underlying adminPortToPortMAC. The value of this object MUST be retained across reinitializations of the management system." ::= { begemotBridgeStpExtPortEntry 4 } begemotBridgeStpPortOperPointToPoint OBJECT-TYPE SYNTAX TruthValue MAX-ACCESS read-only STATUS current DESCRIPTION "The operational point-to-point status of the LAN segment attached to this port. It indicates whether a port is considered to have a point-to-point connection. If adminPointToPointMAC is set to auto(2), then the value of operPointToPointMAC is determined in accordance with the specific procedures defined for the MAC entity concerned, as defined in IEEE 802.1w, clause 6.5. The value is determined dynamically; that is, it is re-evaluated whenever the value of adminPointToPointMAC changes, and whenever the specific procedures defined for the MAC entity evaluates a change in its point-to-point status." ::= { begemotBridgeStpExtPortEntry 5 } begemotBridgeStpPortAdminPathCost OBJECT-TYPE SYNTAX Integer32 (0..200000000) MAX-ACCESS read-write STATUS current DESCRIPTION "The administratively assigned value for the contribution of this port to the path cost of paths toward the spanning tree root. Writing a value of '0' assigns the automatically calculated default Path Cost value to the port. If the default Path Cost is being used, this object returns '0' when read. This complements the object begemotBridgeStpPortPathCost or begemotBridgeStpPortPathCost32, which returns the operational value of the path cost. The value of this object MUST be retained across reinitializations of the management system." ::= { begemotBridgeStpExtPortEntry 6 } -- ---------------------------------------------------------- -- --- the Bridge interface Transparent bridging table +-- the Bridge interface Transparent bridging table -- ---------------------------------------------------------- -- begemotBridgeTpTable OBJECT-TYPE SYNTAX SEQUENCE OF BegemotBridgeTpEntry MAX-ACCESS not-accessible STATUS current DESCRIPTION "A table that contains information regarding transparent bridging for each bridge interface on the managed device." ::= { begemotBridgeTp 1 } begemotBridgeTpEntry OBJECT-TYPE SYNTAX BegemotBridgeTpEntry MAX-ACCESS not-accessible STATUS current DESCRIPTION "A list of information regarding transparent bridging on a bridge interface." AUGMENTS { begemotBridgeBaseEntry } ::= { begemotBridgeTpTable 1 } BegemotBridgeTpEntry ::= SEQUENCE { begemotBridgeTpLearnedEntryDiscards Counter32, begemotBridgeTpAgingTime Integer32, begemotBridgeTpMaxAddresses Integer32 } begemotBridgeTpLearnedEntryDiscards OBJECT-TYPE SYNTAX Counter32 MAX-ACCESS read-only STATUS current DESCRIPTION "The total number of Forwarding Database entries that would have been learnt, but have been discarded due to Forwarding Address Table having reached its maximum entries limit." ::= { begemotBridgeTpEntry 1 } begemotBridgeTpAgingTime OBJECT-TYPE SYNTAX Integer32 (10..1000000) UNITS "seconds" MAX-ACCESS read-write STATUS current DESCRIPTION "The timeout period in seconds before aging out dynamically learnt forwarding entries." ::= { begemotBridgeTpEntry 2 } begemotBridgeTpMaxAddresses OBJECT-TYPE SYNTAX Integer32 (1..10000) MAX-ACCESS read-write STATUS current DESCRIPTION "The maximum number of entries that this bridge can learn in its Forwarding Address Table and use for making forwarding decisions." ::= { begemotBridgeTpEntry 3 } -- ---------------------------------------------------------- -- -- The Forwarding Database for Transparent Bridging interfaces -- ---------------------------------------------------------- -- begemotBridgeTpFdbTable OBJECT-TYPE SYNTAX SEQUENCE OF BegemotBridgeTpFdbEntry MAX-ACCESS not-accessible STATUS current DESCRIPTION "A table that contains information about unicast entries for which the bridge interfaces have forwarding and/or filtering information. This information is used by the bridge interfaces to make forwarding decisions." ::= { begemotBridgeTp 2 } begemotBridgeTpFdbEntry OBJECT-TYPE SYNTAX BegemotBridgeTpFdbEntry MAX-ACCESS not-accessible STATUS current DESCRIPTION "Information about a specific unicast MAC address for which the bridge interface has some forwarding and/or filtering information." INDEX { begemotBridgeBaseName, begemotBridgeTpFdbAddress } ::= { begemotBridgeTpFdbTable 1 } BegemotBridgeTpFdbEntry ::= SEQUENCE { begemotBridgeTpFdbAddress MacAddress, begemotBridgeTpFdbPort Integer32, begemotBridgeTpFdbStatus INTEGER } begemotBridgeTpFdbAddress OBJECT-TYPE SYNTAX MacAddress MAX-ACCESS read-only STATUS current DESCRIPTION "A unicast MAC address for which the bridge has which the bridge interface has some forwarding and/or filtering information." ::= { begemotBridgeTpFdbEntry 1 } begemotBridgeTpFdbPort OBJECT-TYPE SYNTAX Integer32 MAX-ACCESS read-only STATUS current DESCRIPTION "The port number of the bridge port on which a frame having a source address equal to the value of the corresponding instance of begemotBridgeTpFdbAddress has been seen." ::= { begemotBridgeTpFdbEntry 2 } begemotBridgeTpFdbStatus OBJECT-TYPE SYNTAX INTEGER { other(1), invalid(2), learned(3), self(4), mgmt(5) } MAX-ACCESS read-only STATUS current DESCRIPTION "The status of this entry. The meanings of the values are: other(1) - none of the following. invalid(2) - this entry is no longer valid (e.g., it was learned but has since aged out), but has not yet been flushed from the table. learned(3) - the value of the corresponding instance of begemotBridgeTpFdbPort was learned, and is being used. self(4) - the value of the corresponding instance of begemotBridgeTpFdbAddress represents one of the bridge's addresses. The corresponding instance of begemotBridgeTpFdbPort indicates which of the bridge's ports has this address. mgmt(5) - the value of the corresponding instance of begemotBridgeTpFdbAddress has been added to the bridge's Forwarding Database by some management means." ::= { begemotBridgeTpFdbEntry 3 } -- ---------------------------------------------------------- -- -- Ports table for Transparent Bridging interfaces -- ---------------------------------------------------------- -- begemotBridgeTpPortTable OBJECT-TYPE SYNTAX SEQUENCE OF BegemotBridgeTpPortEntry MAX-ACCESS not-accessible STATUS current DESCRIPTION "A table that contains information about every bridge port, member of a bridge interface, associated with the transparent bridging function of the bridge." ::= { begemotBridgeTp 3 } begemotBridgeTpPortEntry OBJECT-TYPE SYNTAX BegemotBridgeTpPortEntry MAX-ACCESS not-accessible STATUS current DESCRIPTION "A list of information about every bridge port, member of a bridge interface, associated with the bridge's transparent bridging function." INDEX { begemotBridgeBaseName, begemotBridgeBasePortIfIndex } ::= { begemotBridgeTpPortTable 1 } BegemotBridgeTpPortEntry ::= SEQUENCE { begemotBridgeTpPort Integer32, begemotBridgeTpPortMaxInfo Integer32, begemotBridgeTpPortInFrames Counter32, begemotBridgeTpPortOutFrames Counter32, begemotBridgeTpPortInDiscards Counter32 } begemotBridgeTpPort OBJECT-TYPE SYNTAX Integer32 (1..65535) MAX-ACCESS read-only STATUS current DESCRIPTION "The system interface index of the port for which this entry contains Transparent bridging management information." ::= { begemotBridgeTpPortEntry 1 } begemotBridgeTpPortMaxInfo OBJECT-TYPE SYNTAX Integer32 UNITS "bytes" MAX-ACCESS read-only STATUS current DESCRIPTION "The maximum size of the INFO (non-MAC) field that this port will receive or transmit." ::= { begemotBridgeTpPortEntry 2 } begemotBridgeTpPortInFrames OBJECT-TYPE SYNTAX Counter32 UNITS "frames" MAX-ACCESS read-only STATUS current DESCRIPTION "The number of frames that have been received by this port from its segment. Note that a frame received on the interface corresponding to this port is only counted by this object if and only if it is for a protocol being processed by the local bridging function, including bridge management frames." ::= { begemotBridgeTpPortEntry 3 } begemotBridgeTpPortOutFrames OBJECT-TYPE SYNTAX Counter32 UNITS "frames" MAX-ACCESS read-only STATUS current DESCRIPTION "The number of frames that have been transmitted by this port to its segment. Note that a frame transmitted on the interface corresponding to this port is only counted by this object if and only if it is for a protocol being processed by the local bridging function, including bridge management frames." ::= { begemotBridgeTpPortEntry 4 } begemotBridgeTpPortInDiscards OBJECT-TYPE SYNTAX Counter32 UNITS "frames" MAX-ACCESS read-only STATUS current DESCRIPTION "Count of received valid frames that were discarded (i.e., filtered) by the Forwarding Process." ::= { begemotBridgeTpPortEntry 5 } -- ---------------------------------------------------------- -- -- the begemotBridgePf objects -- ---------------------------------------------------------- -- begemotBridgePfilStatus OBJECT-TYPE SYNTAX TruthValue MAX-ACCESS read-write STATUS current DESCRIPTION "Indicates whether packet filtering by some firewall package is enabled on the bridge interface." ::= { begemotBridgePf 1 } begemotBridgePfilMembers OBJECT-TYPE SYNTAX TruthValue MAX-ACCESS read-write STATUS current DESCRIPTION "A value of true(1) indicates that packet filtering is enabled on both incoming and outgoing bridge member interfaces." ::= { begemotBridgePf 2 } begemotBridgePfilIpOnly OBJECT-TYPE SYNTAX TruthValue MAX-ACCESS read-write STATUS current DESCRIPTION "This value controls the handling of non-IP packets which are not passed on for further processing to a firewall package. A value of false(0) indicates that all non-IP Ethernet frames are passed unconditionally." ::= { begemotBridgePf 3 } begemotBridgeLayer2PfStatus OBJECT-TYPE SYNTAX INTEGER { enabled(1), disabled(2) } MAX-ACCESS read-write STATUS current DESCRIPTION "This value indicates whether layer2 filtering by a firewall package is enabled for bridge interfaces." ::= { begemotBridgePf 4 } -- ---------------------------------------------------------- -- -- the begemotBridgeConfigObjects objects -- ---------------------------------------------------------- -- begemotBridgeDefaultBridgeIf OBJECT-TYPE SYNTAX BridgeIfNameOrEmpty MAX-ACCESS read-write STATUS current DESCRIPTION "The name of the bridge interface that will be managed via objects in IETF BRIDGE-MIB (RFC4188). If the object's value is set to an empty string, bridge interfaces will only be managed via objects in this MIB module." DEFVAL { "bridge0" } ::= { begemotBridgeConfigObjects 1 } begemotBridgeDataUpdate OBJECT-TYPE SYNTAX Timeout (1..300) UNITS "seconds" MAX-ACCESS read-write STATUS current DESCRIPTION "The maximum age in seconds of the cached data." DEFVAL { 10 } ::= { begemotBridgeConfigObjects 2 } begemotBridgeDataPoll OBJECT-TYPE SYNTAX Timeout (1..3600) UNITS "seconds" MAX-ACCESS read-write STATUS current DESCRIPTION "The polling rate of data when the module is idle." DEFVAL { 300 } ::= { begemotBridgeConfigObjects 3 } -- ---------------------------------------------------------- -- -- Notifications for the Spanning Tree Protocol -- ---------------------------------------------------------- -- begemotBridgeNewRoot NOTIFICATION-TYPE OBJECTS { begemotBridgeBaseName } STATUS current DESCRIPTION "The begemotBridgeNewRoot trap indicates that one of the bridge interfaces on the sending agent's device has become the new root of the spanning tree topology it is participating in." ::= { begemotBridgeNotifications 1 } begemotBridgeTopologyChange NOTIFICATION-TYPE OBJECTS { begemotBridgeBaseName } STATUS current DESCRIPTION "A begemotBridgeTopologyChange trap is send when a member port on one of the bridge interfaces, monitored by the agent, transitions from the Learning state to the Forwarding state, or from the Forwarding state to the Blocking state. The trap is not sent if a begemotBridgeNewRoot trap is sent for the same transition." ::= { begemotBridgeNotifications 2 } END Index: head/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_addrs.c =================================================================== --- head/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_addrs.c (revision 310647) +++ head/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_addrs.c (revision 310648) @@ -1,589 +1,589 @@ /*- * Copyright (c) 2006 Shteryana Shopova * All rights reserved. * * 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 THE 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 THE 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. * * Bridge MIB implementation for SNMPd. * Bridge addresses. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "bridge_tree.h" #include "bridge_snmp.h" TAILQ_HEAD(tp_entries, tp_entry); /* * Free the bridge address list. */ static void bridge_tpe_free(struct tp_entries *headp) { struct tp_entry *t; while ((t = TAILQ_FIRST(headp)) != NULL) { TAILQ_REMOVE(headp, t, tp_e); free(t); } } /* * Free the bridge address entries from the address list, * for the specified bridge interface only. */ static void bridge_tpe_bif_free(struct tp_entries *headp, struct bridge_if *bif) { struct tp_entry *tp; while (bif->f_tpa != NULL && bif->sysindex == bif->f_tpa->sysindex) { tp = TAILQ_NEXT(bif->f_tpa, tp_e); TAILQ_REMOVE(headp, bif->f_tpa, tp_e); free(bif->f_tpa); bif->f_tpa = tp; } } /* * Compare two mac addresses. * m1 < m2 : -1 * m1 > m2 : +1 * m1 = m2 : 0 */ static int bridge_compare_macs(const uint8_t *m1, const uint8_t *m2) { int i; for (i = 0; i < ETHER_ADDR_LEN; i++) { if (m1[i] < m2[i]) return (-1); if (m1[i] > m2[i]) return (1); } return (0); } /* * Insert an address entry in the bridge address TAILQ starting to search * for its place from the position of the first bridge address for the bridge * interface. Update the first bridge address if necessary. */ static void bridge_addrs_insert_at(struct tp_entries *headp, struct tp_entry *ta, struct tp_entry **f_tpa) { struct tp_entry *t1; assert(f_tpa != NULL); for (t1 = *f_tpa; t1 != NULL && ta->sysindex == t1->sysindex; t1 = TAILQ_NEXT(t1, tp_e)) { if (bridge_compare_macs(ta->tp_addr, t1->tp_addr) < 0) { TAILQ_INSERT_BEFORE(t1, ta, tp_e); if (*f_tpa == t1) (*f_tpa) = ta; return; } } if (t1 == NULL) TAILQ_INSERT_TAIL(headp, ta, tp_e); else TAILQ_INSERT_BEFORE(t1, ta, tp_e); } /* * Find an address entry's position in the address list * according to bridge interface name. */ static struct tp_entry * bridge_addrs_find_pos(struct tp_entries *headp, uint32_t b_idx) { uint32_t t_idx; struct tp_entry *t1; if ((t1 = TAILQ_FIRST(headp)) == NULL || bridge_compare_sysidx(b_idx, t1->sysindex) < 0) return (NULL); t_idx = t1->sysindex; for (t1 = TAILQ_NEXT(t1, tp_e); t1 != NULL; t1 = TAILQ_NEXT(t1, tp_e)) { if (t1->sysindex != t_idx) { if (bridge_compare_sysidx(b_idx, t1->sysindex) < 0) return (TAILQ_PREV(t1, tp_entries, tp_e)); else t_idx = t1->sysindex; } } if (t1 == NULL) t1 = TAILQ_LAST(headp, tp_entries); return (t1); } /* * Insert a bridge address in the bridge addresses list. */ static void bridge_addrs_bif_insert(struct tp_entries *headp, struct tp_entry *te, struct tp_entry **f_tpa) { struct tp_entry *temp; if (*f_tpa != NULL) bridge_addrs_insert_at(headp, te, f_tpa); else { temp = bridge_addrs_find_pos(headp, te->sysindex); if (temp == NULL) TAILQ_INSERT_HEAD(headp, te, tp_e); else TAILQ_INSERT_AFTER(headp, temp, te, tp_e); *f_tpa = te; } } static struct tp_entries tp_entries = TAILQ_HEAD_INITIALIZER(tp_entries); static time_t address_list_age; void bridge_addrs_update_listage(void) { address_list_age = time(NULL); } void bridge_addrs_fini(void) { bridge_tpe_free(&tp_entries); } void bridge_addrs_free(struct bridge_if *bif) { bridge_tpe_bif_free(&tp_entries, bif); } /* * Find the first address in the list. */ static struct tp_entry * bridge_addrs_first(void) { return (TAILQ_FIRST(&tp_entries)); } /* * Find the next address in the list. */ static struct tp_entry * bridge_addrs_next(struct tp_entry *te) { return (TAILQ_NEXT(te, tp_e)); } /* * Find the first address, learnt by the specified bridge interface. */ struct tp_entry * bridge_addrs_bif_first(struct bridge_if *bif) { return (bif->f_tpa); } /* * Find the next address, learnt by the specified bridge interface. */ struct tp_entry * bridge_addrs_bif_next(struct tp_entry *te) { struct tp_entry *te_next; if ((te_next = TAILQ_NEXT(te, tp_e)) == NULL || te_next->sysindex != te->sysindex) return (NULL); return (te_next); } /* * Remove a bridge address from the list. */ void bridge_addrs_remove(struct tp_entry *te, struct bridge_if *bif) { if (bif->f_tpa == te) bif->f_tpa = bridge_addrs_bif_next(te); TAILQ_REMOVE(&tp_entries, te, tp_e); free(te); } /* * Allocate memory for a new bridge address and insert it in the list. */ struct tp_entry * bridge_new_addrs(uint8_t *mac, struct bridge_if *bif) { struct tp_entry *te; if ((te = (struct tp_entry *) malloc(sizeof(*te))) == NULL) { syslog(LOG_ERR, "bridge new address: failed: %s", strerror(errno)); return (NULL); } bzero(te, sizeof(*te)); te->sysindex = bif->sysindex; bcopy(mac, te->tp_addr, ETHER_ADDR_LEN); bridge_addrs_bif_insert(&tp_entries, te, &(bif->f_tpa)); return (te); } /* * Given a mac address, learnt on a bridge, * find the corrsponding TP entry for it. */ struct tp_entry * bridge_addrs_find(uint8_t *mac, struct bridge_if *bif) { struct tp_entry *te; for (te = bif->f_tpa; te != NULL; te = TAILQ_NEXT(te, tp_e)) { if (te->sysindex != bif->sysindex) { te = NULL; break; } if (bridge_compare_macs(te->tp_addr, mac) == 0) break; } return (te); } void bridge_addrs_dump(struct bridge_if *bif) { struct tp_entry *te; syslog(LOG_ERR, "Addresses count - %d", bif->num_addrs); for (te = bridge_addrs_bif_first(bif); te != NULL; te = bridge_addrs_bif_next(te)) { syslog(LOG_ERR, "address %x:%x:%x:%x:%x:%x on port %d.%d", te->tp_addr[0], te->tp_addr[1], te->tp_addr[2], te->tp_addr[3], te->tp_addr[4], te->tp_addr[5], te->sysindex, te->port_no); } } /* * RFC4188 specifics. */ /* - * Construct the SNMP index from the address DST Mac. + * Construct the SNMP index from the address DST Mac. */ static void bridge_addrs_index_append(struct asn_oid *oid, uint sub, const struct tp_entry *te) { int i; oid->len = sub + ETHER_ADDR_LEN + 1; oid->subs[sub] = ETHER_ADDR_LEN; for (i = 1; i <= ETHER_ADDR_LEN; i++) oid->subs[sub + i] = te->tp_addr[i - 1]; } /* * Find the address entry for the SNMP index from the default bridge only. */ static struct tp_entry * bridge_addrs_get(const struct asn_oid *oid, uint sub, struct bridge_if *bif) { int i; uint8_t tp_addr[ETHER_ADDR_LEN]; if (oid->len - sub != ETHER_ADDR_LEN + 1 || oid->subs[sub] != ETHER_ADDR_LEN) return (NULL); for (i = 0; i < ETHER_ADDR_LEN; i++) tp_addr[i] = oid->subs[sub + i + 1]; return (bridge_addrs_find(tp_addr, bif)); } /* * Find the next address entry for the SNMP index * from the default bridge only. */ static struct tp_entry * bridge_addrs_getnext(const struct asn_oid *oid, uint sub, struct bridge_if *bif) { int i; uint8_t tp_addr[ETHER_ADDR_LEN]; static struct tp_entry *te; if (oid->len - sub == 0) return (bridge_addrs_bif_first(bif)); if (oid->len - sub != ETHER_ADDR_LEN + 1 || oid->subs[sub] != ETHER_ADDR_LEN) return (NULL); for (i = 0; i < ETHER_ADDR_LEN; i++) tp_addr[i] = oid->subs[sub + i + 1]; if ((te = bridge_addrs_find(tp_addr, bif)) == NULL) return (NULL); return (bridge_addrs_bif_next(te)); } int op_dot1d_tp_fdb(struct snmp_context *c __unused, struct snmp_value *val, uint sub, uint iidx __unused, enum snmp_op op) { struct bridge_if *bif; struct tp_entry *te; if ((bif = bridge_get_default()) == NULL) return (SNMP_ERR_NOSUCHNAME); if (time(NULL) - bif->addrs_age > bridge_get_data_maxage() && bridge_update_addrs(bif) <= 0) return (SNMP_ERR_NOSUCHNAME); switch (op) { case SNMP_OP_GET: if ((te = bridge_addrs_get(&val->var, sub, bif)) == NULL) return (SNMP_ERR_NOSUCHNAME); goto get; case SNMP_OP_GETNEXT: if ((te = bridge_addrs_getnext(&val->var, sub, bif)) == NULL) return (SNMP_ERR_NOSUCHNAME); bridge_addrs_index_append(&val->var, sub, te); goto get; case SNMP_OP_SET: return (SNMP_ERR_NOT_WRITEABLE); case SNMP_OP_ROLLBACK: case SNMP_OP_COMMIT: break; } abort(); get: switch (val->var.subs[sub - 1]) { case LEAF_dot1dTpFdbAddress: return (string_get(val, te->tp_addr, ETHER_ADDR_LEN)); case LEAF_dot1dTpFdbPort : val->v.integer = te->port_no; return (SNMP_ERR_NOERROR); case LEAF_dot1dTpFdbStatus: val->v.integer = te->status; return (SNMP_ERR_NOERROR); } abort(); } /* * Private BEGEMOT-BRIDGE-MIB specifics. */ /* * Construct the SNMP index from the bridge interface name - * and the address DST Mac. + * and the address DST Mac. */ static int bridge_addrs_begemot_index_append(struct asn_oid *oid, uint sub, const struct tp_entry *te) { uint i, n_len; const char *b_name; if ((b_name = bridge_if_find_name(te->sysindex)) == NULL) return (-1); n_len = strlen(b_name); oid->len = sub++; oid->subs[oid->len++] = n_len; for (i = 1; i <= n_len; i++) oid->subs[oid->len++] = b_name[i - 1]; oid->subs[oid->len++] = ETHER_ADDR_LEN; for (i = 1 ; i <= ETHER_ADDR_LEN; i++) oid->subs[oid->len++] = te->tp_addr[i - 1]; return (0); } /* * Find a bridge address entry by the bridge interface name - * and the address DST Mac. + * and the address DST Mac. */ static struct tp_entry * bridge_addrs_begemot_get(const struct asn_oid *oid, uint sub) { uint i, n_len; uint8_t tp_addr[ETHER_ADDR_LEN]; char bif_name[IFNAMSIZ]; struct bridge_if *bif; n_len = oid->subs[sub]; if (oid->len - sub != n_len + ETHER_ADDR_LEN + 3 || n_len >= IFNAMSIZ || oid->subs[sub + n_len + 1] != ETHER_ADDR_LEN) return (NULL); for (i = 0; i < n_len; i++) bif_name[i] = oid->subs[n_len + i + 1]; bif_name[i] = '\0'; for (i = 1; i <= ETHER_ADDR_LEN; i++) tp_addr[i - 1] = oid->subs[n_len + i + 1]; if ((bif = bridge_if_find_ifname(bif_name)) == NULL) return (NULL); return (bridge_addrs_find(tp_addr, bif)); } /* * Find the next bridge address entry by the bridge interface name - * and the address DST Mac. + * and the address DST Mac. */ static struct tp_entry * bridge_addrs_begemot_getnext(const struct asn_oid *oid, uint sub) { uint i, n_len; uint8_t tp_addr[ETHER_ADDR_LEN]; char bif_name[IFNAMSIZ]; struct bridge_if *bif; struct tp_entry *tp; if (oid->len - sub == 0) return (bridge_addrs_first()); n_len = oid->subs[sub]; if (oid->len - sub != n_len + ETHER_ADDR_LEN + 2 || n_len >= IFNAMSIZ || oid->subs[sub + n_len + 1] != ETHER_ADDR_LEN) return (NULL); for (i = 1; i <= n_len; i++) bif_name[i - 1] = oid->subs[sub + i]; bif_name[i - 1] = '\0'; for (i = 1; i <= ETHER_ADDR_LEN; i++) tp_addr[i - 1] = oid->subs[sub + n_len + i + 1]; if ((bif = bridge_if_find_ifname(bif_name)) == NULL || (tp = bridge_addrs_find(tp_addr, bif)) == NULL) return (NULL); return (bridge_addrs_next(tp)); } int op_begemot_tp_fdb(struct snmp_context *c __unused, struct snmp_value *val, uint sub, uint iidx __unused, enum snmp_op op) { struct tp_entry *te; if (time(NULL) - address_list_age > bridge_get_data_maxage()) bridge_update_all_addrs(); switch (op) { case SNMP_OP_GET: if ((te = bridge_addrs_begemot_get(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); goto get; case SNMP_OP_GETNEXT: if ((te = bridge_addrs_begemot_getnext(&val->var, sub)) == NULL || bridge_addrs_begemot_index_append(&val->var, sub, te) < 0) return (SNMP_ERR_NOSUCHNAME); goto get; case SNMP_OP_SET: return (SNMP_ERR_NOT_WRITEABLE); case SNMP_OP_ROLLBACK: case SNMP_OP_COMMIT: break; } abort(); get: switch (val->var.subs[sub - 1]) { case LEAF_begemotBridgeTpFdbAddress: return (string_get(val, te->tp_addr, ETHER_ADDR_LEN)); case LEAF_begemotBridgeTpFdbPort: val->v.integer = te->port_no; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeTpFdbStatus: val->v.integer = te->status; return (SNMP_ERR_NOERROR); } abort(); } Index: head/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_if.c =================================================================== --- head/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_if.c (revision 310647) +++ head/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_if.c (revision 310648) @@ -1,1479 +1,1479 @@ /*- * Copyright (c) 2006 Shteryana Shopova * All rights reserved. * * 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 THE 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 THE 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. * * Bridge MIB implementation for SNMPd. * Bridge interface objects. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "bridge_tree.h" #include "bridge_snmp.h" #include "bridge_oid.h" static const struct asn_oid oid_newRoot = OIDX_newRoot; static const struct asn_oid oid_TopologyChange = OIDX_topologyChange; static const struct asn_oid oid_begemotBrigeName = \ OIDX_begemotBridgeBaseName; static const struct asn_oid oid_begemotNewRoot = OIDX_begemotBridgeNewRoot; static const struct asn_oid oid_begemotTopologyChange = \ OIDX_begemotBridgeTopologyChange; TAILQ_HEAD(bridge_ifs, bridge_if); /* * Free the bridge interface list. */ static void bridge_ifs_free(struct bridge_ifs *headp) { struct bridge_if *b; while ((b = TAILQ_FIRST(headp)) != NULL) { TAILQ_REMOVE(headp, b, b_if); free(b); } } /* * Insert an entry in the bridge interface TAILQ. Keep the * TAILQ sorted by the bridge's interface name. */ static void bridge_ifs_insert(struct bridge_ifs *headp, struct bridge_if *b) { struct bridge_if *temp; if ((temp = TAILQ_FIRST(headp)) == NULL || strcmp(b->bif_name, temp->bif_name) < 0) { TAILQ_INSERT_HEAD(headp, b, b_if); return; } TAILQ_FOREACH(temp, headp, b_if) if(strcmp(b->bif_name, temp->bif_name) < 0) TAILQ_INSERT_BEFORE(temp, b, b_if); TAILQ_INSERT_TAIL(headp, b, b_if); } /* The global bridge interface list. */ static struct bridge_ifs bridge_ifs = TAILQ_HEAD_INITIALIZER(bridge_ifs); static time_t bridge_list_age; /* * Free the global list. */ void bridge_ifs_fini(void) { bridge_ifs_free(&bridge_ifs); } /* * Find a bridge interface entry by the bridge interface system index. */ struct bridge_if * bridge_if_find_ifs(uint32_t sysindex) { struct bridge_if *b; TAILQ_FOREACH(b, &bridge_ifs, b_if) if (b->sysindex == sysindex) return (b); return (NULL); } /* * Find a bridge interface entry by the bridge interface name. */ struct bridge_if * bridge_if_find_ifname(const char *b_name) { struct bridge_if *b; TAILQ_FOREACH(b, &bridge_ifs, b_if) if (strcmp(b_name, b->bif_name) == 0) return (b); return (NULL); } /* * Find a bridge name by the bridge interface system index. */ const char * bridge_if_find_name(uint32_t sysindex) { struct bridge_if *b; TAILQ_FOREACH(b, &bridge_ifs, b_if) if (b->sysindex == sysindex) return (b->bif_name); return (NULL); } /* * Given two bridge interfaces' system indexes, find their * corresponding names and return the result of the name * comparison. Returns: * error : -2 * i1 < i2 : -1 * i1 > i2 : +1 * i1 = i2 : 0 */ int bridge_compare_sysidx(uint32_t i1, uint32_t i2) { int c; const char *b1, *b2; if (i1 == i2) return (0); if ((b1 = bridge_if_find_name(i1)) == NULL) { syslog(LOG_ERR, "Bridge interface %d does not exist", i1); return (-2); } if ((b2 = bridge_if_find_name(i2)) == NULL) { syslog(LOG_ERR, "Bridge interface %d does not exist", i2); return (-2); } if ((c = strcmp(b1, b2)) < 0) return (-1); else if (c > 0) return (1); return (0); } /* * Fetch the first bridge interface from the list. */ struct bridge_if * bridge_first_bif(void) { return (TAILQ_FIRST(&bridge_ifs)); } /* * Fetch the next bridge interface from the list. */ struct bridge_if * bridge_next_bif(struct bridge_if *b_pr) { return (TAILQ_NEXT(b_pr, b_if)); } /* * Create a new entry for a bridge interface and insert * it in the list. */ static struct bridge_if * bridge_new_bif(const char *bif_n, uint32_t sysindex, const u_char *physaddr) { struct bridge_if *bif; if ((bif = (struct bridge_if *) malloc(sizeof(*bif)))== NULL) { syslog(LOG_ERR, "bridge new interface failed: %s", strerror(errno)); return (NULL); } bzero(bif, sizeof(struct bridge_if)); strlcpy(bif->bif_name, bif_n, IFNAMSIZ); bcopy(physaddr, bif->br_addr.octet, ETHER_ADDR_LEN); bif->sysindex = sysindex; bif->br_type = BaseType_transparent_only; /* 1 - all bridges default hold time * 100 - centi-seconds */ bif->hold_time = 1 * 100; bif->prot_spec = dot1dStpProtocolSpecification_ieee8021d; bridge_ifs_insert(&bridge_ifs, bif); return (bif); } /* * Remove a bridge interface from the list, freeing all it's ports * and address entries. */ void bridge_remove_bif(struct bridge_if *bif) { bridge_members_free(bif); bridge_addrs_free(bif); TAILQ_REMOVE(&bridge_ifs, bif, b_if); free(bif); } /* * Prepare the variable (bridge interface name) for the private * begemot notifications. */ static struct snmp_value* bridge_basename_var(struct bridge_if *bif, struct snmp_value* b_val) { uint i; b_val->var = oid_begemotBrigeName; b_val->var.subs[b_val->var.len++] = strlen(bif->bif_name); if ((b_val->v.octetstring.octets = (u_char *) malloc(strlen(bif->bif_name))) == NULL) return (NULL); for (i = 0; i < strlen(bif->bif_name); i++) b_val->var.subs[b_val->var.len++] = bif->bif_name[i]; b_val->v.octetstring.len = strlen(bif->bif_name); bcopy(bif->bif_name, b_val->v.octetstring.octets, strlen(bif->bif_name)); b_val->syntax = SNMP_SYNTAX_OCTETSTRING; return (b_val); } /* * Compare the values of the old and the new root port and * send a new root notification, if they are not matching. */ static void bridge_new_root(struct bridge_if *bif) { struct snmp_value bif_idx; if (bridge_get_default() == bif) snmp_send_trap(&oid_newRoot, (struct snmp_value *) NULL); if (bridge_basename_var(bif, &bif_idx) == NULL) return; snmp_send_trap(&oid_begemotTopologyChange, &bif_idx, (struct snmp_value *) NULL); } /* * Compare the new and old topology change times and send a * topology change notification if necessary. */ static void bridge_top_change(struct bridge_if *bif) { struct snmp_value bif_idx; if (bridge_get_default() == bif) snmp_send_trap(&oid_TopologyChange, (struct snmp_value *) NULL); if (bridge_basename_var(bif, &bif_idx) == NULL) return; snmp_send_trap(&oid_begemotNewRoot, &bif_idx, (struct snmp_value *) NULL); } static int bridge_if_create(const char* b_name, int8_t up) { if (bridge_create(b_name) < 0) return (-1); if (up == 1 && (bridge_set_if_up(b_name, 1) < 0)) return (-1); /* * Do not create a new bridge entry here - * wait until the mibII module notifies us. */ return (0); } static int bridge_if_destroy(struct bridge_if *bif) { if (bridge_destroy(bif->bif_name) < 0) return (-1); bridge_remove_bif(bif); return (0); } /* * Calculate the timeticks since the last topology change. */ static int bridge_get_time_since_tc(struct bridge_if *bif, uint32_t *ticks) { struct timeval ct; if (gettimeofday(&ct, NULL) < 0) { syslog(LOG_ERR, "bridge get time since last TC:" "gettimeofday failed: %s", strerror(errno)); return (-1); } if (ct.tv_usec - bif->last_tc_time.tv_usec < 0) { ct.tv_sec -= 1; ct.tv_usec += 1000000; } ct.tv_sec -= bif->last_tc_time.tv_sec; ct.tv_usec -= bif->last_tc_time.tv_usec; *ticks = ct.tv_sec * 100 + ct.tv_usec/10000; return (0); } /* * Update the info we have for a single bridge interface. * Return: * 1, if successful * 0, if the interface was deleted * -1, error occurred while fetching the info from the kernel. */ static int bridge_update_bif(struct bridge_if *bif) { struct mibif *ifp; /* Walk through the mibII interface list. */ for (ifp = mib_first_if(); ifp != NULL; ifp = mib_next_if(ifp)) if (strcmp(ifp->name, bif->bif_name) == 0) break; if (ifp == NULL) { /* Ops, we do not exist anymore. */ bridge_remove_bif(bif); return (0); } if (ifp->physaddr != NULL ) bcopy(ifp->physaddr, bif->br_addr.octet, ETHER_ADDR_LEN); else bridge_get_basemac(bif->bif_name, bif->br_addr.octet, ETHER_ADDR_LEN); if (ifp->mib.ifmd_flags & IFF_RUNNING) bif->if_status = RowStatus_active; else bif->if_status = RowStatus_notInService; switch (bridge_getinfo_bif(bif)) { case 2: bridge_new_root(bif); break; case 1: bridge_top_change(bif); break; case -1: bridge_remove_bif(bif); return (-1); default: break; } /* * The number of ports is accessible via SNMP - * update the ports each time the bridge interface data * is refreshed too. */ bif->num_ports = bridge_update_memif(bif); bif->entry_age = time(NULL); return (1); } /* - * Update all bridge interfaces' ports only - + * Update all bridge interfaces' ports only - * make sure each bridge interface exists first. */ void bridge_update_all_ports(void) { struct mibif *ifp; struct bridge_if *bif, *t_bif; for (bif = bridge_first_bif(); bif != NULL; bif = t_bif) { t_bif = bridge_next_bif(bif); for (ifp = mib_first_if(); ifp != NULL; ifp = mib_next_if(ifp)) if (strcmp(ifp->name, bif->bif_name) == 0) break; if (ifp != NULL) bif->num_ports = bridge_update_memif(bif); else /* Ops, we do not exist anymore. */ bridge_remove_bif(bif); } bridge_ports_update_listage(); } /* * Update all addresses only. */ void bridge_update_all_addrs(void) { struct mibif *ifp; struct bridge_if *bif, *t_bif; for (bif = bridge_first_bif(); bif != NULL; bif = t_bif) { t_bif = bridge_next_bif(bif); for (ifp = mib_first_if(); ifp != NULL; ifp = mib_next_if(ifp)) if (strcmp(ifp->name, bif->bif_name) == 0) break; if (ifp != NULL) bif->num_addrs = bridge_update_addrs(bif); else /* Ops, we don't exist anymore. */ bridge_remove_bif(bif); } bridge_addrs_update_listage(); } /* * Update only the bridge interfaces' data - skip addresses. */ void bridge_update_all_ifs(void) { struct bridge_if *bif, *t_bif; for (bif = bridge_first_bif(); bif != NULL; bif = t_bif) { t_bif = bridge_next_bif(bif); bridge_update_bif(bif); } bridge_ports_update_listage(); bridge_list_age = time(NULL); } /* * Update all info we have for all bridges. */ void bridge_update_all(void *arg __unused) { struct bridge_if *bif, *t_bif; for (bif = bridge_first_bif(); bif != NULL; bif = t_bif) { t_bif = bridge_next_bif(bif); if (bridge_update_bif(bif) <= 0) continue; /* Update our learnt addresses. */ bif->num_addrs = bridge_update_addrs(bif); } bridge_list_age = time(NULL); bridge_ports_update_listage(); bridge_addrs_update_listage(); } /* * Callback for polling our last topology change time - * check whether we are root or whether a TC was detected once every * 30 seconds, so that we can send the newRoot and TopologyChange traps * on time. The rest of the data is polled only once every 5 min. */ void bridge_update_tc_time(void *arg __unused) { struct bridge_if *bif; struct mibif *ifp; TAILQ_FOREACH(bif, &bridge_ifs, b_if) { /* Walk through the mibII interface list. */ for (ifp = mib_first_if(); ifp != NULL; ifp = mib_next_if(ifp)) if (strcmp(ifp->name, bif->bif_name) == 0) break; if (ifp == NULL) { bridge_remove_bif(bif); continue; } switch (bridge_get_op_param(bif)) { case 2: bridge_new_root(bif); break; case 1: bridge_top_change(bif); break; } } } /* * Callback for handling new bridge interface creation. */ int bridge_attach_newif(struct mibif *ifp) { u_char mac[ETHER_ADDR_LEN]; struct bridge_if *bif; if (ifp->mib.ifmd_data.ifi_type != IFT_BRIDGE) return (0); /* Make sure it does not exist in our list. */ TAILQ_FOREACH(bif, &bridge_ifs, b_if) if(strcmp(bif->bif_name, ifp->name) == 0) { syslog(LOG_ERR, "bridge interface %s already " "in list", bif->bif_name); return (-1); } if (ifp->physaddr == NULL) { if (bridge_get_basemac(ifp->name, mac, sizeof(mac)) == NULL) { syslog(LOG_ERR, "bridge attach new %s failed - " "no bridge mac address", ifp->name); return (-1); } } else bcopy(ifp->physaddr, &mac, sizeof(mac)); if ((bif = bridge_new_bif(ifp->name, ifp->sysindex, mac)) == NULL) return (-1); if (ifp->mib.ifmd_flags & IFF_RUNNING) bif->if_status = RowStatus_active; else bif->if_status = RowStatus_notInService; /* Skip sending notifications if the interface was just created. */ if (bridge_getinfo_bif(bif) < 0 || (bif->num_ports = bridge_getinfo_bif_ports(bif)) < 0 || (bif->num_addrs = bridge_getinfo_bif_addrs(bif)) < 0) { bridge_remove_bif(bif); return (-1); } /* Check whether we are the default bridge interface. */ if (strcmp(ifp->name, bridge_get_default_name()) == 0) bridge_set_default(bif); return (0); } void bridge_ifs_dump(void) { struct bridge_if *bif; for (bif = bridge_first_bif(); bif != NULL; bif = bridge_next_bif(bif)) { syslog(LOG_ERR, "Bridge %s, index - %d", bif->bif_name, bif->sysindex); bridge_ports_dump(bif); bridge_addrs_dump(bif); } } /* * RFC4188 specifics. */ int op_dot1d_base(struct snmp_context *ctx __unused, struct snmp_value *value, uint sub, uint iidx __unused, enum snmp_op op) { struct bridge_if *bif; if ((bif = bridge_get_default()) == NULL) return (SNMP_ERR_NOSUCHNAME); if (time(NULL) - bif->entry_age > bridge_get_data_maxage() && bridge_update_bif(bif) <= 0) /* It was just deleted. */ return (SNMP_ERR_NOSUCHNAME); switch (op) { case SNMP_OP_GET: switch (value->var.subs[sub - 1]) { case LEAF_dot1dBaseBridgeAddress: return (string_get(value, bif->br_addr.octet, ETHER_ADDR_LEN)); case LEAF_dot1dBaseNumPorts: value->v.integer = bif->num_ports; return (SNMP_ERR_NOERROR); case LEAF_dot1dBaseType: value->v.integer = bif->br_type; return (SNMP_ERR_NOERROR); } abort(); case SNMP_OP_SET: return (SNMP_ERR_NOT_WRITEABLE); case SNMP_OP_GETNEXT: case SNMP_OP_ROLLBACK: case SNMP_OP_COMMIT: break; } abort(); } int op_dot1d_stp(struct snmp_context *ctx, struct snmp_value *val, uint sub, uint iidx __unused, enum snmp_op op) { struct bridge_if *bif; if ((bif = bridge_get_default()) == NULL) return (SNMP_ERR_NOSUCHNAME); if (time(NULL) - bif->entry_age > bridge_get_data_maxage() && bridge_update_bif(bif) <= 0) /* It was just deleted. */ return (SNMP_ERR_NOSUCHNAME); switch (op) { case SNMP_OP_GET: switch (val->var.subs[sub - 1]) { case LEAF_dot1dStpProtocolSpecification: val->v.integer = bif->prot_spec; return (SNMP_ERR_NOERROR); case LEAF_dot1dStpPriority: val->v.integer = bif->priority; return (SNMP_ERR_NOERROR); case LEAF_dot1dStpTimeSinceTopologyChange: if (bridge_get_time_since_tc(bif, &(val->v.uint32)) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case LEAF_dot1dStpTopChanges: val->v.uint32 = bif->top_changes; return (SNMP_ERR_NOERROR); case LEAF_dot1dStpDesignatedRoot: return (string_get(val, bif->design_root, SNMP_BRIDGE_ID_LEN)); case LEAF_dot1dStpRootCost: val->v.integer = bif->root_cost; return (SNMP_ERR_NOERROR); case LEAF_dot1dStpRootPort: val->v.integer = bif->root_port; return (SNMP_ERR_NOERROR); case LEAF_dot1dStpMaxAge: val->v.integer = bif->max_age; return (SNMP_ERR_NOERROR); case LEAF_dot1dStpHelloTime: val->v.integer = bif->hello_time; return (SNMP_ERR_NOERROR); case LEAF_dot1dStpHoldTime: val->v.integer = bif->hold_time; return (SNMP_ERR_NOERROR); case LEAF_dot1dStpForwardDelay: val->v.integer = bif->fwd_delay; return (SNMP_ERR_NOERROR); case LEAF_dot1dStpBridgeMaxAge: val->v.integer = bif->bridge_max_age; return (SNMP_ERR_NOERROR); case LEAF_dot1dStpBridgeHelloTime: val->v.integer = bif->bridge_hello_time; return (SNMP_ERR_NOERROR); case LEAF_dot1dStpBridgeForwardDelay: val->v.integer = bif->bridge_fwd_delay; return (SNMP_ERR_NOERROR); case LEAF_dot1dStpVersion: val->v.integer = bif->stp_version; return (SNMP_ERR_NOERROR); case LEAF_dot1dStpTxHoldCount: val->v.integer = bif->tx_hold_count; return (SNMP_ERR_NOERROR); } abort(); case SNMP_OP_GETNEXT: abort(); case SNMP_OP_SET: switch (val->var.subs[sub - 1]) { case LEAF_dot1dStpPriority: if (val->v.integer > SNMP_BRIDGE_MAX_PRIORITY || val->v.integer % 4096 != 0) return (SNMP_ERR_WRONG_VALUE); ctx->scratch->int1 = bif->priority; if (bridge_set_priority(bif, val->v.integer) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case LEAF_dot1dStpBridgeMaxAge: if (val->v.integer < SNMP_BRIDGE_MIN_MAGE || val->v.integer > SNMP_BRIDGE_MAX_MAGE) return (SNMP_ERR_WRONG_VALUE); ctx->scratch->int1 = bif->bridge_max_age; if (bridge_set_maxage(bif, val->v.integer) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case LEAF_dot1dStpBridgeHelloTime: if (val->v.integer < SNMP_BRIDGE_MIN_HTIME || val->v.integer > SNMP_BRIDGE_MAX_HTIME) return (SNMP_ERR_WRONG_VALUE); ctx->scratch->int1 = bif->bridge_hello_time; if (bridge_set_hello_time(bif, val->v.integer) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case LEAF_dot1dStpBridgeForwardDelay: if (val->v.integer < SNMP_BRIDGE_MIN_FDELAY || val->v.integer > SNMP_BRIDGE_MAX_FDELAY) return (SNMP_ERR_WRONG_VALUE); ctx->scratch->int1 = bif->bridge_fwd_delay; if (bridge_set_forward_delay(bif, val->v.integer) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case LEAF_dot1dStpVersion: if (val->v.integer != dot1dStpVersion_stpCompatible && val->v.integer != dot1dStpVersion_rstp) return (SNMP_ERR_WRONG_VALUE); ctx->scratch->int1 = bif->stp_version; if (bridge_set_stp_version(bif, val->v.integer) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case LEAF_dot1dStpTxHoldCount: if (val->v.integer < SNMP_BRIDGE_MIN_TXHC || val->v.integer > SNMP_BRIDGE_MAX_TXHC) return (SNMP_ERR_WRONG_VALUE); ctx->scratch->int1 = bif->tx_hold_count; if (bridge_set_tx_hold_count(bif, val->v.integer) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case LEAF_dot1dStpProtocolSpecification: case LEAF_dot1dStpTimeSinceTopologyChange: case LEAF_dot1dStpTopChanges: case LEAF_dot1dStpDesignatedRoot: case LEAF_dot1dStpRootCost: case LEAF_dot1dStpRootPort: case LEAF_dot1dStpMaxAge: case LEAF_dot1dStpHelloTime: case LEAF_dot1dStpHoldTime: case LEAF_dot1dStpForwardDelay: return (SNMP_ERR_NOT_WRITEABLE); } abort(); case SNMP_OP_ROLLBACK: switch (val->var.subs[sub - 1]) { case LEAF_dot1dStpPriority: bridge_set_priority(bif, ctx->scratch->int1); break; case LEAF_dot1dStpBridgeMaxAge: bridge_set_maxage(bif, ctx->scratch->int1); break; case LEAF_dot1dStpBridgeHelloTime: bridge_set_hello_time(bif, ctx->scratch->int1); break; case LEAF_dot1dStpBridgeForwardDelay: bridge_set_forward_delay(bif, ctx->scratch->int1); break; case LEAF_dot1dStpVersion: bridge_set_stp_version(bif, ctx->scratch->int1); break; case LEAF_dot1dStpTxHoldCount: bridge_set_tx_hold_count(bif, ctx->scratch->int1); break; } return (SNMP_ERR_NOERROR); case SNMP_OP_COMMIT: return (SNMP_ERR_NOERROR); } abort(); } int op_dot1d_tp(struct snmp_context *ctx, struct snmp_value *value, uint sub, uint iidx __unused, enum snmp_op op) { struct bridge_if *bif; if ((bif = bridge_get_default()) == NULL) return (SNMP_ERR_NOSUCHNAME); if (time(NULL) - bif->entry_age > bridge_get_data_maxage() && bridge_update_bif(bif) <= 0) /* It was just deleted. */ return (SNMP_ERR_NOSUCHNAME); switch (op) { case SNMP_OP_GET: switch (value->var.subs[sub - 1]) { case LEAF_dot1dTpLearnedEntryDiscards: value->v.uint32 = bif->lrnt_drops; return (SNMP_ERR_NOERROR); case LEAF_dot1dTpAgingTime: value->v.integer = bif->age_time; return (SNMP_ERR_NOERROR); } abort(); case SNMP_OP_GETNEXT: abort(); case SNMP_OP_SET: switch (value->var.subs[sub - 1]) { case LEAF_dot1dTpLearnedEntryDiscards: return (SNMP_ERR_NOT_WRITEABLE); case LEAF_dot1dTpAgingTime: if (value->v.integer < SNMP_BRIDGE_MIN_AGE_TIME || value->v.integer > SNMP_BRIDGE_MAX_AGE_TIME) return (SNMP_ERR_WRONG_VALUE); ctx->scratch->int1 = bif->age_time; if (bridge_set_aging_time(bif, value->v.integer) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); } abort(); case SNMP_OP_ROLLBACK: if (value->var.subs[sub - 1] == LEAF_dot1dTpAgingTime) bridge_set_aging_time(bif, ctx->scratch->int1); return (SNMP_ERR_NOERROR); case SNMP_OP_COMMIT: return (SNMP_ERR_NOERROR); } abort(); } /* * Private BEGEMOT-BRIDGE-MIB specifics. */ /* * Get the bridge name from an OID index. */ static char * bridge_name_index_get(const struct asn_oid *oid, uint sub, char *b_name) { uint i; if (oid->len - sub != oid->subs[sub] + 1 || oid->subs[sub] >= IFNAMSIZ) return (NULL); for (i = 0; i < oid->subs[sub]; i++) b_name[i] = oid->subs[sub + i + 1]; b_name[i] = '\0'; return (b_name); } static void bridge_if_index_append(struct asn_oid *oid, uint sub, const struct bridge_if *bif) { uint i; oid->len = sub + strlen(bif->bif_name) + 1; oid->subs[sub] = strlen(bif->bif_name); for (i = 1; i <= strlen(bif->bif_name); i++) oid->subs[sub + i] = bif->bif_name[i - 1]; } static struct bridge_if * bridge_if_index_get(const struct asn_oid *oid, uint sub) { uint i; char bif_name[IFNAMSIZ]; if (oid->len - sub != oid->subs[sub] + 1 || oid->subs[sub] >= IFNAMSIZ) return (NULL); for (i = 0; i < oid->subs[sub]; i++) bif_name[i] = oid->subs[sub + i + 1]; bif_name[i] = '\0'; return (bridge_if_find_ifname(bif_name)); } static struct bridge_if * bridge_if_index_getnext(const struct asn_oid *oid, uint sub) { uint i; char bif_name[IFNAMSIZ]; struct bridge_if *bif; if (oid->len - sub == 0) return (bridge_first_bif()); if (oid->len - sub != oid->subs[sub] + 1 || oid->subs[sub] >= IFNAMSIZ) return (NULL); for (i = 0; i < oid->subs[sub]; i++) bif_name[i] = oid->subs[sub + i + 1]; bif_name[i] = '\0'; if ((bif = bridge_if_find_ifname(bif_name)) == NULL) return (NULL); return (bridge_next_bif(bif)); } static int bridge_set_if_status(struct snmp_context *ctx, struct snmp_value *val, uint sub) { struct bridge_if *bif; char bif_name[IFNAMSIZ]; bif = bridge_if_index_get(&val->var, sub); switch (val->v.integer) { case RowStatus_active: if (bif == NULL) return (SNMP_ERR_INCONS_VALUE); ctx->scratch->int1 = bif->if_status; switch (bif->if_status) { case RowStatus_active: return (SNMP_ERR_NOERROR); case RowStatus_notInService: if (bridge_set_if_up(bif->bif_name, 1) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); default: break; } return (SNMP_ERR_INCONS_VALUE); case RowStatus_notInService: if (bif == NULL) return (SNMP_ERR_INCONS_VALUE); ctx->scratch->int1 = bif->if_status; switch (bif->if_status) { case RowStatus_active: if (bridge_set_if_up(bif->bif_name, 1) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case RowStatus_notInService: return (SNMP_ERR_NOERROR); default: break; } return (SNMP_ERR_INCONS_VALUE); case RowStatus_notReady: return (SNMP_ERR_INCONS_VALUE); case RowStatus_createAndGo: if (bif != NULL) return (SNMP_ERR_INCONS_VALUE); ctx->scratch->int1 = RowStatus_destroy; if (bridge_name_index_get(&val->var, sub, bif_name) == NULL) return (SNMP_ERR_BADVALUE); if (bridge_if_create(bif_name, 1) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case RowStatus_createAndWait: if (bif != NULL) return (SNMP_ERR_INCONS_VALUE); if (bridge_name_index_get(&val->var, sub, bif_name) == NULL) return (SNMP_ERR_BADVALUE); ctx->scratch->int1 = RowStatus_destroy; if (bridge_if_create(bif_name, 0) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case RowStatus_destroy: if (bif == NULL) return (SNMP_ERR_NOSUCHNAME); ctx->scratch->int1 = bif->if_status; bif->if_status = RowStatus_destroy; } return (SNMP_ERR_NOERROR); } static int bridge_rollback_if_status(struct snmp_context *ctx, struct snmp_value *val, uint sub) { struct bridge_if *bif; if ((bif = bridge_if_index_get(&val->var, sub)) == NULL) return (SNMP_ERR_GENERR); switch (ctx->scratch->int1) { case RowStatus_destroy: bridge_if_destroy(bif); return (SNMP_ERR_NOERROR); case RowStatus_notInService: if (bif->if_status != ctx->scratch->int1) bridge_set_if_up(bif->bif_name, 0); bif->if_status = RowStatus_notInService; return (SNMP_ERR_NOERROR); case RowStatus_active: if (bif->if_status != ctx->scratch->int1) bridge_set_if_up(bif->bif_name, 1); bif->if_status = RowStatus_active; return (SNMP_ERR_NOERROR); } abort(); } static int bridge_commit_if_status(struct snmp_value *val, uint sub) { struct bridge_if *bif; if ((bif = bridge_if_index_get(&val->var, sub)) == NULL) return (SNMP_ERR_GENERR); if (bif->if_status == RowStatus_destroy && bridge_if_destroy(bif) < 0) return (SNMP_ERR_COMMIT_FAILED); return (SNMP_ERR_NOERROR); } int op_begemot_base_bridge(struct snmp_context *ctx, struct snmp_value *val, uint sub, uint iidx __unused, enum snmp_op op) { struct bridge_if *bif; if (time(NULL) - bridge_list_age > bridge_get_data_maxage()) bridge_update_all_ifs(); switch (op) { case SNMP_OP_GET: if ((bif = bridge_if_index_get(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); goto get; case SNMP_OP_GETNEXT: if ((bif = bridge_if_index_getnext(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); bridge_if_index_append(&val->var, sub, bif); goto get; case SNMP_OP_SET: switch (val->var.subs[sub - 1]) { case LEAF_begemotBridgeBaseStatus: return (bridge_set_if_status(ctx, val, sub)); case LEAF_begemotBridgeBaseName: case LEAF_begemotBridgeBaseAddress: case LEAF_begemotBridgeBaseNumPorts: case LEAF_begemotBridgeBaseType: return (SNMP_ERR_NOT_WRITEABLE); } abort(); case SNMP_OP_ROLLBACK: return (bridge_rollback_if_status(ctx, val, sub)); case SNMP_OP_COMMIT: return (bridge_commit_if_status(val, sub)); } abort(); get: switch (val->var.subs[sub - 1]) { case LEAF_begemotBridgeBaseName: return (string_get(val, bif->bif_name, -1)); case LEAF_begemotBridgeBaseAddress: return (string_get(val, bif->br_addr.octet, ETHER_ADDR_LEN)); case LEAF_begemotBridgeBaseNumPorts: val->v.integer = bif->num_ports; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeBaseType: val->v.integer = bif->br_type; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeBaseStatus: val->v.integer = bif->if_status; return (SNMP_ERR_NOERROR); } abort(); } int op_begemot_stp(struct snmp_context *ctx, struct snmp_value *val, uint sub, uint iidx __unused, enum snmp_op op) { struct bridge_if *bif; if (time(NULL) - bridge_list_age > bridge_get_data_maxage()) bridge_update_all_ifs(); switch (op) { case SNMP_OP_GET: if ((bif = bridge_if_index_get(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); goto get; case SNMP_OP_GETNEXT: if ((bif = bridge_if_index_getnext(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); bridge_if_index_append(&val->var, sub, bif); goto get; case SNMP_OP_SET: if ((bif = bridge_if_index_get(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); switch (val->var.subs[sub - 1]) { case LEAF_begemotBridgeStpPriority: if (val->v.integer > SNMP_BRIDGE_MAX_PRIORITY || val->v.integer % 4096 != 0) return (SNMP_ERR_WRONG_VALUE); ctx->scratch->int1 = bif->priority; if (bridge_set_priority(bif, val->v.integer) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpBridgeMaxAge: if (val->v.integer < SNMP_BRIDGE_MIN_MAGE || val->v.integer > SNMP_BRIDGE_MAX_MAGE) return (SNMP_ERR_WRONG_VALUE); ctx->scratch->int1 = bif->bridge_max_age; if (bridge_set_maxage(bif, val->v.integer) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpBridgeHelloTime: if (val->v.integer < SNMP_BRIDGE_MIN_HTIME || val->v.integer > SNMP_BRIDGE_MAX_HTIME) return (SNMP_ERR_WRONG_VALUE); ctx->scratch->int1 = bif->bridge_hello_time; if (bridge_set_hello_time(bif, val->v.integer) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpBridgeForwardDelay: if (val->v.integer < SNMP_BRIDGE_MIN_FDELAY || val->v.integer > SNMP_BRIDGE_MAX_FDELAY) return (SNMP_ERR_WRONG_VALUE); ctx->scratch->int1 = bif->bridge_fwd_delay; if (bridge_set_forward_delay(bif, val->v.integer) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpVersion: if (val->v.integer != begemotBridgeStpVersion_stpCompatible && val->v.integer != begemotBridgeStpVersion_rstp) return (SNMP_ERR_WRONG_VALUE); ctx->scratch->int1 = bif->stp_version; if (bridge_set_stp_version(bif, val->v.integer) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpTxHoldCount: if (val->v.integer < SNMP_BRIDGE_MIN_TXHC || val->v.integer > SNMP_BRIDGE_MAX_TXHC) return (SNMP_ERR_WRONG_VALUE); ctx->scratch->int1 = bif->tx_hold_count; if (bridge_set_tx_hold_count(bif, val->v.integer) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpProtocolSpecification: case LEAF_begemotBridgeStpTimeSinceTopologyChange: case LEAF_begemotBridgeStpTopChanges: case LEAF_begemotBridgeStpDesignatedRoot: case LEAF_begemotBridgeStpRootCost: case LEAF_begemotBridgeStpRootPort: case LEAF_begemotBridgeStpMaxAge: case LEAF_begemotBridgeStpHelloTime: case LEAF_begemotBridgeStpHoldTime: case LEAF_begemotBridgeStpForwardDelay: return (SNMP_ERR_NOT_WRITEABLE); } abort(); case SNMP_OP_ROLLBACK: if ((bif = bridge_if_index_get(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); switch (val->var.subs[sub - 1]) { case LEAF_begemotBridgeStpPriority: bridge_set_priority(bif, ctx->scratch->int1); break; case LEAF_begemotBridgeStpBridgeMaxAge: bridge_set_maxage(bif, ctx->scratch->int1); break; case LEAF_begemotBridgeStpBridgeHelloTime: bridge_set_hello_time(bif, ctx->scratch->int1); break; case LEAF_begemotBridgeStpBridgeForwardDelay: bridge_set_forward_delay(bif, ctx->scratch->int1); break; case LEAF_begemotBridgeStpVersion: bridge_set_stp_version(bif, ctx->scratch->int1); break; case LEAF_begemotBridgeStpTxHoldCount: bridge_set_tx_hold_count(bif, ctx->scratch->int1); break; } return (SNMP_ERR_NOERROR); case SNMP_OP_COMMIT: return (SNMP_ERR_NOERROR); } abort(); get: switch (val->var.subs[sub - 1]) { case LEAF_begemotBridgeStpProtocolSpecification: val->v.integer = bif->prot_spec; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpPriority: val->v.integer = bif->priority; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpTimeSinceTopologyChange: if (bridge_get_time_since_tc(bif, &(val->v.uint32)) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpTopChanges: val->v.uint32 = bif->top_changes; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpDesignatedRoot: return (string_get(val, bif->design_root, SNMP_BRIDGE_ID_LEN)); case LEAF_begemotBridgeStpRootCost: val->v.integer = bif->root_cost; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpRootPort: val->v.integer = bif->root_port; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpMaxAge: val->v.integer = bif->max_age; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpHelloTime: val->v.integer = bif->hello_time; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpHoldTime: val->v.integer = bif->hold_time; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpForwardDelay: val->v.integer = bif->fwd_delay; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpBridgeMaxAge: val->v.integer = bif->bridge_max_age; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpBridgeHelloTime: val->v.integer = bif->bridge_hello_time; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpBridgeForwardDelay: val->v.integer = bif->bridge_fwd_delay; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpVersion: val->v.integer = bif->stp_version; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpTxHoldCount: val->v.integer = bif->tx_hold_count; return (SNMP_ERR_NOERROR); } abort(); } int op_begemot_tp(struct snmp_context *ctx, struct snmp_value *val, uint sub, uint iidx __unused, enum snmp_op op) { struct bridge_if *bif; if (time(NULL) - bridge_list_age > bridge_get_data_maxage()) bridge_update_all_ifs(); switch (op) { case SNMP_OP_GET: if ((bif = bridge_if_index_get(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); goto get; case SNMP_OP_GETNEXT: if ((bif = bridge_if_index_getnext(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); bridge_if_index_append(&val->var, sub, bif); goto get; case SNMP_OP_SET: if ((bif = bridge_if_index_get(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); switch (val->var.subs[sub - 1]) { case LEAF_begemotBridgeTpAgingTime: if (val->v.integer < SNMP_BRIDGE_MIN_AGE_TIME || val->v.integer > SNMP_BRIDGE_MAX_AGE_TIME) return (SNMP_ERR_WRONG_VALUE); ctx->scratch->int1 = bif->age_time; if (bridge_set_aging_time(bif, val->v.integer) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeTpMaxAddresses: ctx->scratch->int1 = bif->max_addrs; if (bridge_set_max_cache(bif, val->v.integer) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeTpLearnedEntryDiscards: return (SNMP_ERR_NOT_WRITEABLE); } abort(); case SNMP_OP_ROLLBACK: if ((bif = bridge_if_index_get(&val->var, sub)) == NULL) return (SNMP_ERR_GENERR); switch (val->var.subs[sub - 1]) { case LEAF_begemotBridgeTpAgingTime: bridge_set_aging_time(bif, ctx->scratch->int1); break; case LEAF_begemotBridgeTpMaxAddresses: bridge_set_max_cache(bif, ctx->scratch->int1); break; } return (SNMP_ERR_NOERROR); case SNMP_OP_COMMIT: return (SNMP_ERR_NOERROR); } abort(); get: switch (val->var.subs[sub - 1]) { case LEAF_begemotBridgeTpLearnedEntryDiscards: val->v.uint32 = bif->lrnt_drops; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeTpAgingTime: val->v.integer = bif->age_time; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeTpMaxAddresses: val->v.integer = bif->max_addrs; return (SNMP_ERR_NOERROR); } abort(); } Index: head/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_port.c =================================================================== --- head/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_port.c (revision 310647) +++ head/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_port.c (revision 310648) @@ -1,1513 +1,1513 @@ /*- * Copyright (c) 2006 Shteryana Shopova * All rights reserved. * * 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 THE 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 THE 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. * * Bridge MIB implementation for SNMPd. * Bridge ports. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "bridge_tree.h" #include "bridge_snmp.h" TAILQ_HEAD(bridge_ports, bridge_port); /* * Free the bridge base ports list. */ static void bridge_ports_free(struct bridge_ports *headp) { struct bridge_port *bp; while ((bp = TAILQ_FIRST(headp)) != NULL) { TAILQ_REMOVE(headp, bp, b_p); free(bp); } } /* * Free the bridge base ports from the base ports list, * members of a specified bridge interface only. */ static void bridge_port_memif_free(struct bridge_ports *headp, struct bridge_if *bif) { struct bridge_port *bp; while (bif->f_bp != NULL && bif->sysindex == bif->f_bp->sysindex) { bp = TAILQ_NEXT(bif->f_bp, b_p); TAILQ_REMOVE(headp, bif->f_bp, b_p); free(bif->f_bp); bif->f_bp = bp; } } /* * Insert a port entry in the base port TAILQ starting to search * for its place from the position of the first bridge port for the bridge * interface. Update the first bridge port if necessary. */ static void bridge_port_insert_at(struct bridge_ports *headp, struct bridge_port *bp, struct bridge_port **f_bp) { struct bridge_port *t1; assert(f_bp != NULL); for (t1 = *f_bp; t1 != NULL && bp->sysindex == t1->sysindex; t1 = TAILQ_NEXT(t1, b_p)) { if (bp->if_idx < t1->if_idx) { TAILQ_INSERT_BEFORE(t1, bp, b_p); if (*f_bp == t1) *f_bp = bp; return; } } /* * Handle the case when our first port was actually the * last element of the TAILQ. */ if (t1 == NULL) TAILQ_INSERT_TAIL(headp, bp, b_p); else TAILQ_INSERT_BEFORE(t1, bp, b_p); } /* * Find a port entry's position in the ports list according * to it's parent bridge interface name. Returns a NULL if * we should be at the TAILQ head, otherwise the entry after * which we should be inserted. */ static struct bridge_port * bridge_port_find_pos(struct bridge_ports *headp, uint32_t b_idx) { uint32_t t_idx; struct bridge_port *t1; if ((t1 = TAILQ_FIRST(headp)) == NULL || bridge_compare_sysidx(b_idx, t1->sysindex) < 0) return (NULL); t_idx = t1->sysindex; for (t1 = TAILQ_NEXT(t1, b_p); t1 != NULL; t1 = TAILQ_NEXT(t1, b_p)) { if (t1->sysindex != t_idx) { if (bridge_compare_sysidx(b_idx, t1->sysindex) < 0) return (TAILQ_PREV(t1, bridge_ports, b_p)); else t_idx = t1->sysindex; } } if (t1 == NULL) t1 = TAILQ_LAST(headp, bridge_ports); return (t1); } /* * Insert a bridge member interface in the ports TAILQ. */ static void bridge_port_memif_insert(struct bridge_ports *headp, struct bridge_port *bp, struct bridge_port **f_bp) { struct bridge_port *temp; if (*f_bp != NULL) bridge_port_insert_at(headp, bp, f_bp); else { temp = bridge_port_find_pos(headp, bp->sysindex); if (temp == NULL) TAILQ_INSERT_HEAD(headp, bp, b_p); else TAILQ_INSERT_AFTER(headp, temp, bp, b_p); *f_bp = bp; } } /* The global ports list. */ static struct bridge_ports bridge_ports = TAILQ_HEAD_INITIALIZER(bridge_ports); static time_t ports_list_age; void bridge_ports_update_listage(void) { ports_list_age = time(NULL); } void bridge_ports_fini(void) { bridge_ports_free(&bridge_ports); } void bridge_members_free(struct bridge_if *bif) { bridge_port_memif_free(&bridge_ports, bif); } /* * Find the first port in the ports list. */ static struct bridge_port * bridge_port_first(void) { return (TAILQ_FIRST(&bridge_ports)); } /* * Find the next port in the ports list. */ static struct bridge_port * bridge_port_next(struct bridge_port *bp) { return (TAILQ_NEXT(bp, b_p)); } /* * Find the first member of the specified bridge interface. */ struct bridge_port * bridge_port_bif_first(struct bridge_if *bif) { return (bif->f_bp); } /* * Find the next member of the specified bridge interface. */ struct bridge_port * bridge_port_bif_next(struct bridge_port *bp) { struct bridge_port *bp_next; if ((bp_next = TAILQ_NEXT(bp, b_p)) == NULL || bp_next->sysindex != bp->sysindex) return (NULL); return (bp_next); } /* * Remove a bridge port from the ports list. */ void bridge_port_remove(struct bridge_port *bp, struct bridge_if *bif) { if (bif->f_bp == bp) bif->f_bp = bridge_port_bif_next(bp); TAILQ_REMOVE(&bridge_ports, bp, b_p); free(bp); } /* * Allocate memory for a new bridge port and insert it * in the base ports list. Return a pointer to the port's * structure in case we want to do anything else with it. */ struct bridge_port * bridge_new_port(struct mibif *mif, struct bridge_if *bif) { struct bridge_port *bp; if ((bp = (struct bridge_port *) malloc(sizeof(*bp))) == NULL) { syslog(LOG_ERR, "bridge new member: failed: %s", strerror(errno)); return (NULL); } bzero(bp, sizeof(*bp)); bp->sysindex = bif->sysindex; bp->if_idx = mif->index; bp->port_no = mif->sysindex; strlcpy(bp->p_name, mif->name, IFNAMSIZ); bp->circuit = oid_zeroDotZero; /* * Initialize all rstpMib specific values to false/default. * These will be set to their true values later if the bridge * supports RSTP. */ bp->proto_migr = TruthValue_false; bp->admin_edge = TruthValue_false; bp->oper_edge = TruthValue_false; bp->oper_ptp = TruthValue_false; bp->admin_ptp = StpPortAdminPointToPointType_auto; bridge_port_memif_insert(&bridge_ports, bp, &(bif->f_bp)); return (bp); } /* * Update our info from the corresponding mibII interface info. */ void bridge_port_getinfo_mibif(struct mibif *m_if, struct bridge_port *bp) { bp->max_info = m_if->mib.ifmd_data.ifi_mtu; bp->in_frames = m_if->mib.ifmd_data.ifi_ipackets; bp->out_frames = m_if->mib.ifmd_data.ifi_opackets; bp->in_drops = m_if->mib.ifmd_data.ifi_iqdrops; } /* * Find a port, whose SNMP's mibII ifIndex matches one of the ports, * members of the specified bridge interface. */ struct bridge_port * bridge_port_find(int32_t if_idx, struct bridge_if *bif) { struct bridge_port *bp; for (bp = bif->f_bp; bp != NULL; bp = TAILQ_NEXT(bp, b_p)) { if (bp->sysindex != bif->sysindex) { bp = NULL; break; } if (bp->if_idx == if_idx) break; } return (bp); } void bridge_ports_dump(struct bridge_if *bif) { struct bridge_port *bp; for (bp = bridge_port_bif_first(bif); bp != NULL; bp = bridge_port_bif_next(bp)) { syslog(LOG_ERR, "memif - %s, index - %d", bp->p_name, bp->port_no); } } /* * RFC4188 specifics. */ int op_dot1d_base_port(struct snmp_context *c __unused, struct snmp_value *val, uint sub, uint iidx __unused, enum snmp_op op) { struct bridge_if *bif; struct bridge_port *bp; if ((bif = bridge_get_default()) == NULL) return (SNMP_ERR_NOSUCHNAME); if (time(NULL) - bif->ports_age > bridge_get_data_maxage() && bridge_update_memif(bif) <= 0) return (SNMP_ERR_NOSUCHNAME); switch (op) { case SNMP_OP_GET: if (val->var.len - sub != 1) return (SNMP_ERR_NOSUCHNAME); if ((bp = bridge_port_find(val->var.subs[sub], bif)) == NULL) return (SNMP_ERR_NOSUCHNAME); goto get; case SNMP_OP_GETNEXT: if (val->var.len - sub == 0) { if ((bp = bridge_port_bif_first(bif)) == NULL) return (SNMP_ERR_NOSUCHNAME); } else { if ((bp = bridge_port_find(val->var.subs[sub], bif)) == NULL || (bp = bridge_port_bif_next(bp)) == NULL) return (SNMP_ERR_NOSUCHNAME); } val->var.len = sub + 1; val->var.subs[sub] = bp->port_no; goto get; case SNMP_OP_SET: return (SNMP_ERR_NOT_WRITEABLE); case SNMP_OP_ROLLBACK: case SNMP_OP_COMMIT: break; } abort(); get: switch (val->var.subs[sub - 1]) { case LEAF_dot1dBasePort: val->v.integer = bp->port_no; return (SNMP_ERR_NOERROR); case LEAF_dot1dBasePortIfIndex: val->v.integer = bp->if_idx; return (SNMP_ERR_NOERROR); case LEAF_dot1dBasePortCircuit: val->v.oid = bp->circuit; return (SNMP_ERR_NOERROR); case LEAF_dot1dBasePortDelayExceededDiscards: val->v.uint32 = bp->dly_ex_drops; return (SNMP_ERR_NOERROR); case LEAF_dot1dBasePortMtuExceededDiscards: val->v.uint32 = bp->dly_mtu_drops; return (SNMP_ERR_NOERROR); } abort(); } int op_dot1d_stp_port(struct snmp_context *ctx, struct snmp_value *val, uint sub, uint iidx __unused, enum snmp_op op) { struct bridge_if *bif; struct bridge_port *bp; if ((bif = bridge_get_default()) == NULL) return (SNMP_ERR_NOSUCHNAME); if (time(NULL) - bif->ports_age > bridge_get_data_maxage() && bridge_update_memif(bif) <= 0) return (SNMP_ERR_NOSUCHNAME); switch (op) { case SNMP_OP_GET: if (val->var.len - sub != 1) return (SNMP_ERR_NOSUCHNAME); if ((bp = bridge_port_find(val->var.subs[sub], bif)) == NULL) return (SNMP_ERR_NOSUCHNAME); goto get; case SNMP_OP_GETNEXT: if (val->var.len - sub == 0) { if ((bp = bridge_port_bif_first(bif)) == NULL) return (SNMP_ERR_NOSUCHNAME); } else { if ((bp = bridge_port_find(val->var.subs[sub], bif)) == NULL || (bp = bridge_port_bif_next(bp)) == NULL) return (SNMP_ERR_NOSUCHNAME); } val->var.len = sub + 1; val->var.subs[sub] = bp->port_no; goto get; case SNMP_OP_SET: if (val->var.len - sub != 1) return (SNMP_ERR_NOSUCHNAME); if ((bp = bridge_port_find(val->var.subs[sub], bif)) == NULL) return (SNMP_ERR_NOSUCHNAME); switch (val->var.subs[sub - 1]) { case LEAF_dot1dStpPortPriority: if (val->v.integer < 0 || val->v.integer > 255) return (SNMP_ERR_WRONG_VALUE); ctx->scratch->int1 = bp->priority; if (bridge_port_set_priority(bif->bif_name, bp, val->v.integer) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case LEAF_dot1dStpPortEnable: if (val->v.integer != dot1dStpPortEnable_enabled && val->v.integer != dot1dStpPortEnable_disabled) return (SNMP_ERR_WRONG_VALUE); ctx->scratch->int1 = bp->enable; if (bridge_port_set_stp_enable(bif->bif_name, bp, val->v.integer) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case LEAF_dot1dStpPortPathCost: if (val->v.integer < SNMP_PORT_MIN_PATHCOST || val->v.integer > SNMP_PORT_MAX_PATHCOST) return (SNMP_ERR_WRONG_VALUE); ctx->scratch->int1 = bp->path_cost; if (bridge_port_set_path_cost(bif->bif_name, bp, val->v.integer) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case LEAF_dot1dStpPort: case LEAF_dot1dStpPortState: case LEAF_dot1dStpPortDesignatedRoot: case LEAF_dot1dStpPortDesignatedCost: case LEAF_dot1dStpPortDesignatedBridge: case LEAF_dot1dStpPortDesignatedPort: case LEAF_dot1dStpPortForwardTransitions: return (SNMP_ERR_NOT_WRITEABLE); } abort(); case SNMP_OP_ROLLBACK: if ((bp = bridge_port_find(val->var.subs[sub], bif)) == NULL) return (SNMP_ERR_GENERR); switch (val->var.subs[sub - 1]) { case LEAF_dot1dStpPortPriority: bridge_port_set_priority(bif->bif_name, bp, ctx->scratch->int1); break; case LEAF_dot1dStpPortEnable: bridge_port_set_stp_enable(bif->bif_name, bp, ctx->scratch->int1); break; case LEAF_dot1dStpPortPathCost: bridge_port_set_path_cost(bif->bif_name, bp, ctx->scratch->int1); break; } return (SNMP_ERR_NOERROR); case SNMP_OP_COMMIT: return (SNMP_ERR_NOERROR); } abort(); get: switch (val->var.subs[sub - 1]) { case LEAF_dot1dStpPort: val->v.integer = bp->port_no; return (SNMP_ERR_NOERROR); case LEAF_dot1dStpPortPriority: val->v.integer = bp->priority; return (SNMP_ERR_NOERROR); case LEAF_dot1dStpPortState: val->v.integer = bp->state; return (SNMP_ERR_NOERROR); case LEAF_dot1dStpPortEnable: val->v.integer = bp->enable; return (SNMP_ERR_NOERROR); case LEAF_dot1dStpPortPathCost: val->v.integer = bp->path_cost; return (SNMP_ERR_NOERROR); case LEAF_dot1dStpPortDesignatedRoot: return (string_get(val, bp->design_root, SNMP_BRIDGE_ID_LEN)); case LEAF_dot1dStpPortDesignatedCost: val->v.integer = bp->design_cost; return (SNMP_ERR_NOERROR); case LEAF_dot1dStpPortDesignatedBridge: return (string_get(val, bp->design_bridge, SNMP_BRIDGE_ID_LEN)); case LEAF_dot1dStpPortDesignatedPort: return (string_get(val, bp->design_port, 2)); case LEAF_dot1dStpPortForwardTransitions: val->v.uint32 = bp->fwd_trans; return (SNMP_ERR_NOERROR); } abort(); } int op_dot1d_stp_ext_port(struct snmp_context *ctx, struct snmp_value *val, uint sub, uint iidx __unused, enum snmp_op op) { struct bridge_if *bif; struct bridge_port *bp; if ((bif = bridge_get_default()) == NULL) return (SNMP_ERR_NOSUCHNAME); if (time(NULL) - bif->ports_age > bridge_get_data_maxage() && bridge_update_memif(bif) <= 0) return (SNMP_ERR_NOSUCHNAME); switch (op) { case SNMP_OP_GET: if (val->var.len - sub != 1) return (SNMP_ERR_NOSUCHNAME); if ((bp = bridge_port_find(val->var.subs[sub], bif)) == NULL) return (SNMP_ERR_NOSUCHNAME); goto get; case SNMP_OP_GETNEXT: if (val->var.len - sub == 0) { if ((bp = bridge_port_bif_first(bif)) == NULL) return (SNMP_ERR_NOSUCHNAME); } else { if ((bp = bridge_port_find(val->var.subs[sub], bif)) == NULL || (bp = bridge_port_bif_next(bp)) == NULL) return (SNMP_ERR_NOSUCHNAME); } val->var.len = sub + 1; val->var.subs[sub] = bp->port_no; goto get; case SNMP_OP_SET: if (val->var.len - sub != 1) return (SNMP_ERR_NOSUCHNAME); if ((bp = bridge_port_find(val->var.subs[sub], bif)) == NULL) return (SNMP_ERR_NOSUCHNAME); switch (val->var.subs[sub - 1]) { case LEAF_dot1dStpPortAdminEdgePort: if (val->v.integer != TruthValue_true && val->v.integer != TruthValue_false) return (SNMP_ERR_WRONG_VALUE); ctx->scratch->int1 = bp->admin_edge; if (bridge_port_set_admin_edge(bif->bif_name, bp, val->v.integer) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case LEAF_dot1dStpPortAdminPointToPoint: - if (val->v.integer < 0 || val->v.integer > + if (val->v.integer < 0 || val->v.integer > StpPortAdminPointToPointType_auto) return (SNMP_ERR_WRONG_VALUE); ctx->scratch->int1 = bp->admin_ptp; if (bridge_port_set_admin_ptp(bif->bif_name, bp, val->v.integer) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case LEAF_dot1dStpPortAdminPathCost: if (val->v.integer < SNMP_PORT_MIN_PATHCOST || val->v.integer > SNMP_PORT_MAX_PATHCOST) return (SNMP_ERR_WRONG_VALUE); ctx->scratch->int1 = bp->admin_path_cost; if (bridge_port_set_path_cost(bif->bif_name, bp, val->v.integer) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case LEAF_dot1dStpPortProtocolMigration: case LEAF_dot1dStpPortOperEdgePort: case LEAF_dot1dStpPortOperPointToPoint: return (SNMP_ERR_NOT_WRITEABLE); } abort(); case SNMP_OP_ROLLBACK: if ((bp = bridge_port_find(val->var.subs[sub], bif)) == NULL) return (SNMP_ERR_GENERR); switch (val->var.subs[sub - 1]) { case LEAF_dot1dStpPortAdminEdgePort: bridge_port_set_admin_edge(bif->bif_name, bp, ctx->scratch->int1); break; case LEAF_dot1dStpPortAdminPointToPoint: bridge_port_set_admin_ptp(bif->bif_name, bp, ctx->scratch->int1); break; case LEAF_dot1dStpPortAdminPathCost: bridge_port_set_path_cost(bif->bif_name, bp, ctx->scratch->int1); break; } return (SNMP_ERR_NOERROR); case SNMP_OP_COMMIT: return (SNMP_ERR_NOERROR); } abort(); get: switch (val->var.subs[sub - 1]) { case LEAF_dot1dStpPortProtocolMigration: val->v.integer = bp->proto_migr; return (SNMP_ERR_NOERROR); case LEAF_dot1dStpPortAdminEdgePort: val->v.integer = bp->admin_edge; return (SNMP_ERR_NOERROR); case LEAF_dot1dStpPortOperEdgePort: val->v.integer = bp->oper_edge; return (SNMP_ERR_NOERROR); case LEAF_dot1dStpPortAdminPointToPoint: val->v.integer = bp->admin_ptp; return (SNMP_ERR_NOERROR); case LEAF_dot1dStpPortOperPointToPoint: val->v.integer = bp->oper_ptp; return (SNMP_ERR_NOERROR); case LEAF_dot1dStpPortAdminPathCost: val->v.integer = bp->admin_path_cost; return (SNMP_ERR_NOERROR); } abort(); } int op_dot1d_tp_port(struct snmp_context *c __unused, struct snmp_value *val, uint sub, uint iidx __unused, enum snmp_op op) { struct bridge_if *bif; struct bridge_port *bp; if ((bif = bridge_get_default()) == NULL) return (SNMP_ERR_NOSUCHNAME); if (time(NULL) - bif->ports_age > bridge_get_data_maxage() && bridge_update_memif(bif) <= 0) return (SNMP_ERR_NOSUCHNAME); switch (op) { case SNMP_OP_GET: if (val->var.len - sub != 1) return (SNMP_ERR_NOSUCHNAME); if ((bp = bridge_port_find(val->var.subs[sub], bif)) == NULL) return (SNMP_ERR_NOSUCHNAME); goto get; case SNMP_OP_GETNEXT: if (val->var.len - sub == 0) { if ((bp = bridge_port_bif_first(bif)) == NULL) return (SNMP_ERR_NOSUCHNAME); } else { if ((bp = bridge_port_find(val->var.subs[sub], bif)) == NULL || (bp = bridge_port_bif_next(bp)) == NULL) return (SNMP_ERR_NOSUCHNAME); } val->var.len = sub + 1; val->var.subs[sub] = bp->port_no; goto get; case SNMP_OP_SET: return (SNMP_ERR_NOT_WRITEABLE); case SNMP_OP_ROLLBACK: case SNMP_OP_COMMIT: break; } abort(); get: switch (val->var.subs[sub - 1]) { case LEAF_dot1dTpPort: val->v.integer = bp->port_no; return (SNMP_ERR_NOERROR); case LEAF_dot1dTpPortMaxInfo: val->v.integer = bp->max_info; return (SNMP_ERR_NOERROR); case LEAF_dot1dTpPortInFrames: val->v.uint32 = bp->in_frames; return (SNMP_ERR_NOERROR); case LEAF_dot1dTpPortOutFrames: val->v.uint32 = bp->out_frames; return (SNMP_ERR_NOERROR); case LEAF_dot1dTpPortInDiscards: val->v.uint32 = bp->in_drops; return (SNMP_ERR_NOERROR); } abort(); } /* * Private BEGEMOT-BRIDGE-MIB specifics. */ /* * Construct a bridge port entry index. */ static int bridge_port_index_append(struct asn_oid *oid, uint sub, const struct bridge_port *bp) { uint i; const char *b_name; if ((b_name = bridge_if_find_name(bp->sysindex)) == NULL) return (-1); oid->len = sub + strlen(b_name) + 1 + 1; oid->subs[sub] = strlen(b_name); for (i = 1; i <= strlen(b_name); i++) oid->subs[sub + i] = b_name[i - 1]; oid->subs[sub + i] = bp->port_no; return (0); } /* * Get the port entry from an entry's index. */ static struct bridge_port * bridge_port_index_get(const struct asn_oid *oid, uint sub, int8_t status) { uint i; int32_t port_no; char bif_name[IFNAMSIZ]; struct bridge_if *bif; struct bridge_port *bp; if (oid->len - sub != oid->subs[sub] + 2 || oid->subs[sub] >= IFNAMSIZ) return (NULL); for (i = 0; i < oid->subs[sub]; i++) bif_name[i] = oid->subs[sub + i + 1]; bif_name[i] = '\0'; port_no = oid->subs[sub + i + 1]; if ((bif = bridge_if_find_ifname(bif_name)) == NULL) return (NULL); if ((bp = bridge_port_find(port_no, bif)) == NULL || (status == 0 && bp->status != RowStatus_active)) return (NULL); return (bp); } /* * Get the next port entry from an entry's index. */ static struct bridge_port * bridge_port_index_getnext(const struct asn_oid *oid, uint sub, int8_t status) { uint i; int32_t port_no; char bif_name[IFNAMSIZ]; struct bridge_if *bif; struct bridge_port *bp; if (oid->len - sub == 0) bp = bridge_port_first(); else { if (oid->len - sub != oid->subs[sub] + 2 || oid->subs[sub] >= IFNAMSIZ) return (NULL); for (i = 0; i < oid->subs[sub]; i++) bif_name[i] = oid->subs[sub + i + 1]; bif_name[i] = '\0'; - + port_no = oid->subs[sub + i + 1]; if ((bif = bridge_if_find_ifname(bif_name)) == NULL || (bp = bridge_port_find(port_no, bif)) == NULL) return (NULL); bp = bridge_port_next(bp); } if (status == 1) return (bp); while (bp != NULL) { if (bp->status == RowStatus_active) break; bp = bridge_port_next(bp); } return (bp); } /* * Read the bridge name and port index from a ASN OID structure. */ static int bridge_port_index_decode(const struct asn_oid *oid, uint sub, char *b_name, int32_t *idx) { uint i; if (oid->len - sub != oid->subs[sub] + 2 || oid->subs[sub] >= IFNAMSIZ) return (-1); for (i = 0; i < oid->subs[sub]; i++) b_name[i] = oid->subs[sub + i + 1]; b_name[i] = '\0'; *idx = oid->subs[sub + i + 1]; return (0); } static int bridge_port_set_status(struct snmp_context *ctx, struct snmp_value *val, uint sub) { int32_t if_idx; char b_name[IFNAMSIZ]; struct bridge_if *bif; struct bridge_port *bp; struct mibif *mif; if (bridge_port_index_decode(&val->var, sub, b_name, &if_idx) < 0) return (SNMP_ERR_INCONS_VALUE); if ((bif = bridge_if_find_ifname(b_name)) == NULL || (mif = mib_find_if(if_idx)) == NULL) return (SNMP_ERR_INCONS_VALUE); bp = bridge_port_find(if_idx, bif); switch (val->v.integer) { case RowStatus_active: if (bp == NULL) return (SNMP_ERR_INCONS_VALUE); if (bp->span_enable == 0) return (SNMP_ERR_INCONS_VALUE); ctx->scratch->int1 = bp->status; bp->status = RowStatus_active; break; - + case RowStatus_notInService: if (bp == NULL || bp->span_enable == 0 || bp->status == RowStatus_active) return (SNMP_ERR_INCONS_VALUE); ctx->scratch->int1 = bp->status; bp->status = RowStatus_notInService; case RowStatus_notReady: /* FALLTHROUGH */ case RowStatus_createAndGo: return (SNMP_ERR_INCONS_VALUE); case RowStatus_createAndWait: if (bp != NULL) return (SNMP_ERR_INCONS_VALUE); if ((bp = bridge_new_port(mif, bif)) == NULL) return (SNMP_ERR_GENERR); ctx->scratch->int1 = RowStatus_destroy; bp->status = RowStatus_notReady; break; case RowStatus_destroy: if (bp == NULL) return (SNMP_ERR_INCONS_VALUE); ctx->scratch->int1 = bp->status; bp->status = RowStatus_destroy; break; } return (SNMP_ERR_NOERROR); } static int bridge_port_rollback_status(struct snmp_context *ctx, struct snmp_value *val, uint sub) { int32_t if_idx; char b_name[IFNAMSIZ]; struct bridge_if *bif; struct bridge_port *bp; if (bridge_port_index_decode(&val->var, sub, b_name, &if_idx) < 0) return (SNMP_ERR_GENERR); if ((bif = bridge_if_find_ifname(b_name)) == NULL || (bp = bridge_port_find(if_idx, bif)) == NULL) return (SNMP_ERR_GENERR); if (ctx->scratch->int1 == RowStatus_destroy) bridge_port_remove(bp, bif); else bp->status = ctx->scratch->int1; return (SNMP_ERR_NOERROR); } static int bridge_port_commit_status(struct snmp_value *val, uint sub) { int32_t if_idx; char b_name[IFNAMSIZ]; struct bridge_if *bif; struct bridge_port *bp; if (bridge_port_index_decode(&val->var, sub, b_name, &if_idx) < 0) return (SNMP_ERR_GENERR); if ((bif = bridge_if_find_ifname(b_name)) == NULL || (bp = bridge_port_find(if_idx, bif)) == NULL) return (SNMP_ERR_GENERR); switch (bp->status) { case RowStatus_active: if (bridge_port_addm(bp, b_name) < 0) return (SNMP_ERR_COMMIT_FAILED); break; case RowStatus_destroy: if (bridge_port_delm(bp, b_name) < 0) return (SNMP_ERR_COMMIT_FAILED); bridge_port_remove(bp, bif); break; } return (SNMP_ERR_NOERROR); } static int bridge_port_set_span_enable(struct snmp_context *ctx, struct snmp_value *val, uint sub) { int32_t if_idx; char b_name[IFNAMSIZ]; struct bridge_if *bif; struct bridge_port *bp; struct mibif *mif; if (val->v.integer != begemotBridgeBaseSpanEnabled_enabled && val->v.integer != begemotBridgeBaseSpanEnabled_disabled) return (SNMP_ERR_BADVALUE); if (bridge_port_index_decode(&val->var, sub, b_name, &if_idx) < 0) return (SNMP_ERR_INCONS_VALUE); if ((bif = bridge_if_find_ifname(b_name)) == NULL) return (SNMP_ERR_INCONS_VALUE); if ((bp = bridge_port_find(if_idx, bif)) == NULL) { if ((mif = mib_find_if(if_idx)) == NULL) return (SNMP_ERR_INCONS_VALUE); if ((bp = bridge_new_port(mif, bif)) == NULL) return (SNMP_ERR_GENERR); ctx->scratch->int1 = RowStatus_destroy; } else if (bp->status == RowStatus_active) { return (SNMP_ERR_INCONS_VALUE); } else { ctx->scratch->int1 = bp->status; } bp->span_enable = val->v.integer; bp->status = RowStatus_notInService; return (SNMP_ERR_NOERROR); } int op_begemot_base_port(struct snmp_context *ctx, struct snmp_value *val, uint sub, uint iidx __unused, enum snmp_op op) { int8_t status, which; const char *bname; struct bridge_port *bp; if (time(NULL) - ports_list_age > bridge_get_data_maxage()) bridge_update_all_ports(); which = val->var.subs[sub - 1]; status = 0; switch (op) { case SNMP_OP_GET: if (which == LEAF_begemotBridgeBaseSpanEnabled || which == LEAF_begemotBridgeBasePortStatus) status = 1; if ((bp = bridge_port_index_get(&val->var, sub, status)) == NULL) return (SNMP_ERR_NOSUCHNAME); goto get; case SNMP_OP_GETNEXT: if (which == LEAF_begemotBridgeBaseSpanEnabled || which == LEAF_begemotBridgeBasePortStatus) status = 1; if ((bp = bridge_port_index_getnext(&val->var, sub, status)) == NULL || bridge_port_index_append(&val->var, sub, bp) < 0) return (SNMP_ERR_NOSUCHNAME); goto get; case SNMP_OP_SET: switch (which) { case LEAF_begemotBridgeBaseSpanEnabled: return (bridge_port_set_span_enable(ctx, val, sub)); case LEAF_begemotBridgeBasePortStatus: return (bridge_port_set_status(ctx, val, sub)); case LEAF_begemotBridgeBasePortPrivate: if ((bp = bridge_port_index_get(&val->var, sub, status)) == NULL) return (SNMP_ERR_NOSUCHNAME); if ((bname = bridge_if_find_name(bp->sysindex)) == NULL) return (SNMP_ERR_GENERR); ctx->scratch->int1 = bp->priv_set; return (bridge_port_set_private(bname, bp, val->v.integer)); case LEAF_begemotBridgeBasePort: case LEAF_begemotBridgeBasePortIfIndex: case LEAF_begemotBridgeBasePortDelayExceededDiscards: case LEAF_begemotBridgeBasePortMtuExceededDiscards: return (SNMP_ERR_NOT_WRITEABLE); } abort(); case SNMP_OP_ROLLBACK: switch (which) { case LEAF_begemotBridgeBaseSpanEnabled: /* FALLTHROUGH */ case LEAF_begemotBridgeBasePortStatus: return (bridge_port_rollback_status(ctx, val, sub)); case LEAF_begemotBridgeBasePortPrivate: if ((bp = bridge_port_index_get(&val->var, sub, status)) == NULL) return (SNMP_ERR_GENERR); if ((bname = bridge_if_find_name(bp->sysindex)) == NULL) return (SNMP_ERR_GENERR); return (bridge_port_set_private(bname, bp, ctx->scratch->int1)); } return (SNMP_ERR_NOERROR); case SNMP_OP_COMMIT: if (which == LEAF_begemotBridgeBasePortStatus) return (bridge_port_commit_status(val, sub)); return (SNMP_ERR_NOERROR); } abort(); get: switch (which) { case LEAF_begemotBridgeBasePort: val->v.integer = bp->port_no; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeBasePortIfIndex: val->v.integer = bp->if_idx; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeBaseSpanEnabled: val->v.integer = bp->span_enable; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeBasePortDelayExceededDiscards: val->v.uint32 = bp->dly_ex_drops; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeBasePortMtuExceededDiscards: val->v.uint32 = bp->dly_mtu_drops; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeBasePortStatus: val->v.integer = bp->status; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeBasePortPrivate: val->v.integer = bp->priv_set; return (SNMP_ERR_NOERROR); } abort(); } int op_begemot_stp_port(struct snmp_context *ctx, struct snmp_value *val, uint sub, uint iidx __unused, enum snmp_op op) { struct bridge_port *bp; const char *b_name; if (time(NULL) - ports_list_age > bridge_get_data_maxage()) bridge_update_all_ports(); switch (op) { case SNMP_OP_GET: if ((bp = bridge_port_index_get(&val->var, sub, 0)) == NULL) return (SNMP_ERR_NOSUCHNAME); goto get; case SNMP_OP_GETNEXT: if ((bp = bridge_port_index_getnext(&val->var, sub, 0)) == NULL || bridge_port_index_append(&val->var, sub, bp) < 0) return (SNMP_ERR_NOSUCHNAME); goto get; case SNMP_OP_SET: if ((bp = bridge_port_index_get(&val->var, sub, 0)) == NULL) return (SNMP_ERR_NOSUCHNAME); if ((b_name = bridge_if_find_name(bp->sysindex)) == NULL) return (SNMP_ERR_GENERR); switch (val->var.subs[sub - 1]) { case LEAF_begemotBridgeStpPortPriority: if (val->v.integer < 0 || val->v.integer > 255) return (SNMP_ERR_WRONG_VALUE); ctx->scratch->int1 = bp->priority; if (bridge_port_set_priority(b_name, bp, val->v.integer) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpPortEnable: if (val->v.integer != begemotBridgeStpPortEnable_enabled || val->v.integer != begemotBridgeStpPortEnable_disabled) return (SNMP_ERR_WRONG_VALUE); ctx->scratch->int1 = bp->enable; if (bridge_port_set_stp_enable(b_name, bp, val->v.integer) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpPortPathCost: if (val->v.integer < SNMP_PORT_MIN_PATHCOST || val->v.integer > SNMP_PORT_MAX_PATHCOST) return (SNMP_ERR_WRONG_VALUE); ctx->scratch->int1 = bp->path_cost; if (bridge_port_set_path_cost(b_name, bp, val->v.integer) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpPort: case LEAF_begemotBridgeStpPortState: case LEAF_begemotBridgeStpPortDesignatedRoot: case LEAF_begemotBridgeStpPortDesignatedCost: case LEAF_begemotBridgeStpPortDesignatedBridge: case LEAF_begemotBridgeStpPortDesignatedPort: case LEAF_begemotBridgeStpPortForwardTransitions: return (SNMP_ERR_NOT_WRITEABLE); } abort(); case SNMP_OP_ROLLBACK: if ((bp = bridge_port_index_get(&val->var, sub, 0)) == NULL || (b_name = bridge_if_find_name(bp->sysindex)) == NULL) return (SNMP_ERR_GENERR); switch (val->var.subs[sub - 1]) { case LEAF_begemotBridgeStpPortPriority: bridge_port_set_priority(b_name, bp, ctx->scratch->int1); break; case LEAF_begemotBridgeStpPortEnable: bridge_port_set_stp_enable(b_name, bp, ctx->scratch->int1); break; case LEAF_begemotBridgeStpPortPathCost: bridge_port_set_path_cost(b_name, bp, ctx->scratch->int1); break; } return (SNMP_ERR_NOERROR); case SNMP_OP_COMMIT: return (SNMP_ERR_NOERROR); } abort(); get: switch (val->var.subs[sub - 1]) { case LEAF_begemotBridgeStpPort: val->v.integer = bp->port_no; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpPortPriority: val->v.integer = bp->priority; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpPortState: val->v.integer = bp->state; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpPortEnable: val->v.integer = bp->enable; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpPortPathCost: val->v.integer = bp->path_cost; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpPortDesignatedRoot: return (string_get(val, bp->design_root, SNMP_BRIDGE_ID_LEN)); case LEAF_begemotBridgeStpPortDesignatedCost: val->v.integer = bp->design_cost; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpPortDesignatedBridge: return (string_get(val, bp->design_bridge, SNMP_BRIDGE_ID_LEN)); case LEAF_begemotBridgeStpPortDesignatedPort: return (string_get(val, bp->design_port, 2)); case LEAF_begemotBridgeStpPortForwardTransitions: val->v.uint32 = bp->fwd_trans; return (SNMP_ERR_NOERROR); } abort(); } int op_begemot_stp_ext_port(struct snmp_context *ctx, struct snmp_value *val, uint sub, uint iidx __unused, enum snmp_op op) { struct bridge_port *bp; const char *b_name; if (time(NULL) - ports_list_age > bridge_get_data_maxage()) bridge_update_all_ports(); switch (op) { case SNMP_OP_GET: if ((bp = bridge_port_index_get(&val->var, sub, 0)) == NULL) return (SNMP_ERR_NOSUCHNAME); goto get; case SNMP_OP_GETNEXT: if ((bp = bridge_port_index_getnext(&val->var, sub, 0)) == NULL || bridge_port_index_append(&val->var, sub, bp) < 0) return (SNMP_ERR_NOSUCHNAME); goto get; case SNMP_OP_SET: if ((bp = bridge_port_index_get(&val->var, sub, 0)) == NULL) return (SNMP_ERR_NOSUCHNAME); if ((b_name = bridge_if_find_name(bp->sysindex)) == NULL) return (SNMP_ERR_GENERR); switch (val->var.subs[sub - 1]) { case LEAF_begemotBridgeStpPortAdminEdgePort: if (val->v.integer != TruthValue_true && val->v.integer != TruthValue_false) return (SNMP_ERR_WRONG_VALUE); ctx->scratch->int1 = bp->admin_edge; if (bridge_port_set_admin_edge(b_name, bp, val->v.integer) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpPortAdminPointToPoint: if (val->v.integer < 0 || val->v.integer > StpPortAdminPointToPointType_auto) return (SNMP_ERR_WRONG_VALUE); ctx->scratch->int1 = bp->admin_ptp; if (bridge_port_set_admin_ptp(b_name, bp, val->v.integer) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpPortAdminPathCost: if (val->v.integer < SNMP_PORT_MIN_PATHCOST || val->v.integer > SNMP_PORT_MAX_PATHCOST) return (SNMP_ERR_WRONG_VALUE); ctx->scratch->int1 = bp->admin_path_cost; if (bridge_port_set_path_cost(b_name, bp, val->v.integer) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpPortProtocolMigration: case LEAF_begemotBridgeStpPortOperEdgePort: case LEAF_begemotBridgeStpPortOperPointToPoint: return (SNMP_ERR_NOT_WRITEABLE); } abort(); case SNMP_OP_ROLLBACK: if ((bp = bridge_port_index_get(&val->var, sub, 0)) == NULL || (b_name = bridge_if_find_name(bp->sysindex)) == NULL) return (SNMP_ERR_GENERR); switch (val->var.subs[sub - 1]) { case LEAF_begemotBridgeStpPortAdminEdgePort: bridge_port_set_admin_edge(b_name, bp, ctx->scratch->int1); break; case LEAF_begemotBridgeStpPortAdminPointToPoint: bridge_port_set_admin_ptp(b_name, bp, ctx->scratch->int1); break; case LEAF_begemotBridgeStpPortAdminPathCost: bridge_port_set_path_cost(b_name, bp, ctx->scratch->int1); break; } return (SNMP_ERR_NOERROR); case SNMP_OP_COMMIT: return (SNMP_ERR_NOERROR); } abort(); get: switch (val->var.subs[sub - 1]) { case LEAF_begemotBridgeStpPortProtocolMigration: val->v.integer = bp->proto_migr; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpPortAdminEdgePort: val->v.integer = bp->admin_edge; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpPortOperEdgePort: val->v.integer = bp->oper_edge; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpPortAdminPointToPoint: val->v.integer = bp->admin_ptp; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpPortOperPointToPoint: val->v.integer = bp->oper_ptp; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpPortAdminPathCost: val->v.integer = bp->admin_path_cost; return (SNMP_ERR_NOERROR); } abort(); } int op_begemot_tp_port(struct snmp_context *c __unused, struct snmp_value *val, uint sub, uint iidx __unused, enum snmp_op op) { struct bridge_port *bp; if (time(NULL) - ports_list_age > bridge_get_data_maxage()) bridge_update_all_ports(); switch (op) { case SNMP_OP_GET: if ((bp = bridge_port_index_get(&val->var, sub, 0)) == NULL) return (SNMP_ERR_NOSUCHNAME); goto get; case SNMP_OP_GETNEXT: if ((bp = bridge_port_index_getnext(&val->var, sub, 0)) == NULL || bridge_port_index_append(&val->var, sub, bp) < 0) return (SNMP_ERR_NOSUCHNAME); goto get; case SNMP_OP_SET: return (SNMP_ERR_NOT_WRITEABLE); case SNMP_OP_ROLLBACK: case SNMP_OP_COMMIT: break; } abort(); get: switch (val->var.subs[sub - 1]) { case LEAF_begemotBridgeTpPort: val->v.integer = bp->port_no; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeTpPortMaxInfo: val->v.integer = bp->max_info; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeTpPortInFrames: val->v.uint32 = bp->in_frames; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeTpPortOutFrames: val->v.uint32 = bp->out_frames; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeTpPortInDiscards: val->v.uint32 = bp->in_drops; return (SNMP_ERR_NOERROR); } abort(); } Index: head/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_sys.c =================================================================== --- head/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_sys.c (revision 310647) +++ head/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_sys.c (revision 310648) @@ -1,1503 +1,1503 @@ /*- * Copyright (c) 2006 Shteryana Shopova * All rights reserved. * * 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 THE 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 THE 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. * * Bridge MIB implementation for SNMPd. * Bridge OS specific ioctls. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "bridge_tree.h" #include "bridge_snmp.h" int sock = -1; int bridge_ioctl_init(void) { if ((sock = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { syslog(LOG_ERR, "cannot open socket : %s", strerror(errno)); return (-1); } return (0); } /* * Load the if_bridge.ko module in kernel if not already there. */ int bridge_kmod_load(void) { int fileid, modid; const char mod_name[] = "if_bridge"; struct module_stat mstat; /* Scan files in kernel. */ mstat.version = sizeof(struct module_stat); for (fileid = kldnext(0); fileid > 0; fileid = kldnext(fileid)) { /* Scan modules in file. */ for (modid = kldfirstmod(fileid); modid > 0; modid = modfnext(modid)) { if (modstat(modid, &mstat) < 0) continue; if (strcmp(mod_name, mstat.name) == 0) return (0); } } /* Not present - load it. */ if (kldload(mod_name) < 0) { syslog(LOG_ERR, "failed to load %s kernel module", mod_name); return (-1); } return (1); } /************************************************************************ * Bridge interfaces. */ /* * Convert the kernel uint64_t value for a bridge id */ static void snmp_uint64_to_bridgeid(uint64_t id, bridge_id b_id) { int i; u_char *o; o = (u_char *) &id; for (i = 0; i < SNMP_BRIDGE_ID_LEN; i++, o++) b_id[SNMP_BRIDGE_ID_LEN - i - 1] = *o; } /* * Fetch the bridge configuration parameters from the kernel excluding * it's base MAC address. */ static int bridge_get_conf_param(struct bridge_if *bif) { struct ifdrv ifd; struct ifbrparam b_param; strlcpy(ifd.ifd_name, bif->bif_name, IFNAMSIZ); ifd.ifd_len = sizeof(b_param); ifd.ifd_data = &b_param; /* Bridge priority. */ ifd.ifd_cmd = BRDGGPRI; if (ioctl(sock, SIOCGDRVSPEC, &ifd) < 0) { syslog(LOG_ERR, "update bridge: ioctl(BRDGGPRI) failed: %s", strerror(errno)); return (-1); } bif->priority = b_param.ifbrp_prio; /* Configured max age. */ ifd.ifd_cmd = BRDGGMA; if (ioctl(sock, SIOCGDRVSPEC, &ifd) < 0) { syslog(LOG_ERR, "update bridge: ioctl(BRDGGMA) failed: %s", strerror(errno)); return (-1); } /* Centi-seconds. */ bif->bridge_max_age = 100 * b_param.ifbrp_maxage; /* Configured hello time. */ ifd.ifd_cmd = BRDGGHT; if (ioctl(sock, SIOCGDRVSPEC, &ifd) < 0) { syslog(LOG_ERR, "update bridge: ioctl(BRDGGHT) failed: %s", strerror(errno)); return (-1); } bif->bridge_hello_time = 100 * b_param.ifbrp_hellotime; /* Forward delay. */ ifd.ifd_cmd = BRDGGFD; if (ioctl(sock, SIOCGDRVSPEC, &ifd) < 0) { syslog(LOG_ERR, "update bridge: ioctl(BRDGGFD) failed: %s", strerror(errno)); return (-1); } bif->bridge_fwd_delay = 100 * b_param.ifbrp_fwddelay; /* Number of dropped addresses. */ ifd.ifd_cmd = BRDGGRTE; if (ioctl(sock, SIOCGDRVSPEC, &ifd) < 0) { syslog(LOG_ERR, "update bridge: ioctl(BRDGGRTE) failed: %s", strerror(errno)); return (-1); } bif->lrnt_drops = b_param.ifbrp_cexceeded; /* Address table timeout. */ ifd.ifd_cmd = BRDGGTO; if (ioctl(sock, SIOCGDRVSPEC, &ifd) < 0) { syslog(LOG_ERR, "update bridge: ioctl(BRDGGTO) failed: %s", strerror(errno)); return (-1); } bif->age_time = b_param.ifbrp_ctime; /* Address table size. */ ifd.ifd_cmd = BRDGGCACHE; if (ioctl(sock, SIOCGDRVSPEC, &ifd) < 0) { syslog(LOG_ERR, "update bridge: ioctl(BRDGGCACHE) " "failed: %s", strerror(errno)); return (-1); } bif->max_addrs = b_param.ifbrp_csize; return (0); } /* * Fetch the current bridge STP operational parameters. * Returns: -1 - on error; * 0 - old TC time and Root Port values are same; * 1 - topologyChange notification should be sent; * 2 - newRoot notification should be sent. */ int bridge_get_op_param(struct bridge_if *bif) { int new_root_send; struct ifdrv ifd; struct ifbropreq b_req; strlcpy(ifd.ifd_name, bif->bif_name, IFNAMSIZ); ifd.ifd_len = sizeof(b_req); ifd.ifd_data = &b_req; ifd.ifd_cmd = BRDGPARAM; if (ioctl(sock, SIOCGDRVSPEC, &ifd) < 0) { syslog(LOG_ERR, "update bridge: ioctl(BRDGPARAM) failed: %s", strerror(errno)); return (-1); } bif->max_age = 100 * b_req.ifbop_maxage; bif->hello_time = 100 * b_req.ifbop_hellotime; bif->fwd_delay = 100 * b_req.ifbop_fwddelay; bif->stp_version = b_req.ifbop_protocol; bif->tx_hold_count = b_req.ifbop_holdcount; if (b_req.ifbop_root_port == 0 && bif->root_port != b_req.ifbop_root_port) new_root_send = 2; else new_root_send = 0; bif->root_port = b_req.ifbop_root_port; bif->root_cost = b_req.ifbop_root_path_cost; snmp_uint64_to_bridgeid(b_req.ifbop_designated_root, bif->design_root); if (bif->last_tc_time.tv_sec != b_req.ifbop_last_tc_time.tv_sec) { bif->top_changes++; bif->last_tc_time.tv_sec = b_req.ifbop_last_tc_time.tv_sec; bif->last_tc_time.tv_usec = b_req.ifbop_last_tc_time.tv_usec; /* * "The trap is not sent if a (begemotBridge)NewRoot * trap is sent for the same transition." */ if (new_root_send == 0) return (1); } return (new_root_send); } int bridge_getinfo_bif(struct bridge_if *bif) { if (bridge_get_conf_param(bif) < 0) return (-1); return (bridge_get_op_param(bif)); } int bridge_set_priority(struct bridge_if *bif, int32_t priority) { struct ifdrv ifd; struct ifbrparam b_param; strlcpy(ifd.ifd_name, bif->bif_name, IFNAMSIZ); ifd.ifd_len = sizeof(b_param); ifd.ifd_data = &b_param; b_param.ifbrp_prio = (uint32_t) priority; ifd.ifd_cmd = BRDGSPRI; if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) { syslog(LOG_ERR, "set bridge param: ioctl(BRDGSPRI) " "failed: %s", strerror(errno)); return (-1); } /* * Re-fetching the data from the driver after that might be a good - * idea, since changing our bridge's priority should invoke + * idea, since changing our bridge's priority should invoke * recalculation of the active spanning tree topology in the network. */ bif->priority = priority; return (0); } /* * Convert 1/100 of seconds to 1/256 of seconds. * Timeout ::= TEXTUAL-CONVENTION. * To convert a Timeout value into a value in units of * 1/256 seconds, the following algorithm should be used: * b = floor( (n * 256) / 100) * The conversion to 1/256 of a second happens in the kernel - * just make sure we correctly convert the seconds to Timout * and vice versa. */ static uint32_t snmp_timeout2_sec(int32_t secs) { return (secs / 100); } int bridge_set_maxage(struct bridge_if *bif, int32_t max_age) { struct ifdrv ifd; struct ifbrparam b_param; strlcpy(ifd.ifd_name, bif->bif_name, IFNAMSIZ); ifd.ifd_len = sizeof(b_param); ifd.ifd_data = &b_param; b_param.ifbrp_maxage = snmp_timeout2_sec(max_age); ifd.ifd_cmd = BRDGSMA; if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) { syslog(LOG_ERR, "set bridge param: ioctl(BRDGSMA) " "failed: %s", strerror(errno)); return (-1); } bif->bridge_max_age = max_age; return (0); } int bridge_set_hello_time(struct bridge_if *bif, int32_t hello_time) { struct ifdrv ifd; struct ifbrparam b_param; strlcpy(ifd.ifd_name, bif->bif_name, IFNAMSIZ); ifd.ifd_len = sizeof(b_param); ifd.ifd_data = &b_param; b_param.ifbrp_hellotime = snmp_timeout2_sec(hello_time); ifd.ifd_cmd = BRDGSHT; if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) { syslog(LOG_ERR, "set bridge param: ioctl(BRDGSHT) " "failed: %s", strerror(errno)); return (-1); } bif->bridge_hello_time = b_param.ifbrp_hellotime; return (0); } int bridge_set_forward_delay(struct bridge_if *bif, int32_t fwd_delay) { struct ifdrv ifd; struct ifbrparam b_param; strlcpy(ifd.ifd_name, bif->bif_name, IFNAMSIZ); ifd.ifd_len = sizeof(b_param); ifd.ifd_data = &b_param; b_param.ifbrp_fwddelay = snmp_timeout2_sec(fwd_delay); ifd.ifd_cmd = BRDGSFD; if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) { syslog(LOG_ERR, "set bridge param: ioctl(BRDGSFD) " "failed: %s", strerror(errno)); return (-1); } bif->bridge_fwd_delay = b_param.ifbrp_fwddelay; return (0); } int bridge_set_aging_time(struct bridge_if *bif, int32_t age_time) { struct ifdrv ifd; struct ifbrparam b_param; strlcpy(ifd.ifd_name, bif->bif_name, IFNAMSIZ); ifd.ifd_len = sizeof(b_param); ifd.ifd_data = &b_param; b_param.ifbrp_ctime = (uint32_t) age_time; ifd.ifd_cmd = BRDGSTO; if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) { syslog(LOG_ERR, "set bridge param: ioctl(BRDGSTO) " "failed: %s", strerror(errno)); return (-1); } bif->age_time = age_time; return (0); } int bridge_set_max_cache(struct bridge_if *bif, int32_t max_cache) { struct ifdrv ifd; struct ifbrparam b_param; strlcpy(ifd.ifd_name, bif->bif_name, IFNAMSIZ); ifd.ifd_len = sizeof(b_param); ifd.ifd_data = &b_param; b_param.ifbrp_csize = max_cache; ifd.ifd_cmd = BRDGSCACHE; if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) { syslog(LOG_ERR, "set bridge param: ioctl(BRDGSCACHE) " "failed: %s", strerror(errno)); return (-1); } bif->max_addrs = b_param.ifbrp_csize; return (0); } int bridge_set_tx_hold_count(struct bridge_if *bif, int32_t tx_hc) { struct ifdrv ifd; struct ifbrparam b_param; if (tx_hc < SNMP_BRIDGE_MIN_TXHC || tx_hc > SNMP_BRIDGE_MAX_TXHC) return (-1); strlcpy(ifd.ifd_name, bif->bif_name, IFNAMSIZ); ifd.ifd_len = sizeof(b_param); ifd.ifd_data = &b_param; b_param.ifbrp_txhc = tx_hc; ifd.ifd_cmd = BRDGSTXHC; if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) { syslog(LOG_ERR, "set bridge param: ioctl(BRDGSTXHC) " "failed: %s", strerror(errno)); return (-1); } bif->tx_hold_count = b_param.ifbrp_txhc; return (0); } int bridge_set_stp_version(struct bridge_if *bif, int32_t stp_proto) { struct ifdrv ifd; struct ifbrparam b_param; strlcpy(ifd.ifd_name, bif->bif_name, IFNAMSIZ); ifd.ifd_len = sizeof(b_param); ifd.ifd_data = &b_param; b_param.ifbrp_proto = stp_proto; ifd.ifd_cmd = BRDGSPROTO; if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) { syslog(LOG_ERR, "set bridge param: ioctl(BRDGSPROTO) " "failed: %s", strerror(errno)); return (-1); } bif->stp_version = b_param.ifbrp_proto; return (0); } /* * Set the bridge interface status to up/down. */ int bridge_set_if_up(const char* b_name, int8_t up) { int flags; struct ifreq ifr; bzero(&ifr, sizeof(ifr)); strcpy(ifr.ifr_name, b_name); if (ioctl(sock, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) { syslog(LOG_ERR, "set bridge up: ioctl(SIOCGIFFLAGS) " "failed: %s", strerror(errno)); return (-1); } flags = (ifr.ifr_flags & 0xffff) | (ifr.ifr_flagshigh << 16); if (up == 1) flags |= IFF_UP; else flags &= ~IFF_UP; ifr.ifr_flags = flags & 0xffff; ifr.ifr_flagshigh = flags >> 16; if (ioctl(sock, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) { syslog(LOG_ERR, "set bridge up: ioctl(SIOCSIFFLAGS) " "failed: %s", strerror(errno)); return (-1); } return (0); } int bridge_create(const char *b_name) { char *new_name; struct ifreq ifr; bzero(&ifr, sizeof(ifr)); strcpy(ifr.ifr_name, b_name); if (ioctl(sock, SIOCIFCREATE, &ifr) < 0) { syslog(LOG_ERR, "create bridge: ioctl(SIOCIFCREATE) " "failed: %s", strerror(errno)); return (-1); } if (strcmp(b_name, ifr.ifr_name) == 0) return (0); if ((new_name = strdup(b_name)) == NULL) { syslog(LOG_ERR, "create bridge: strdup() failed"); return (-1); } ifr.ifr_data = new_name; if (ioctl(sock, SIOCSIFNAME, (caddr_t) &ifr) < 0) { syslog(LOG_ERR, "create bridge: ioctl(SIOCSIFNAME) " "failed: %s", strerror(errno)); free(new_name); return (-1); } return (0); } int bridge_destroy(const char *b_name) { struct ifreq ifr; bzero(&ifr, sizeof(ifr)); strcpy(ifr.ifr_name, b_name); if (ioctl(sock, SIOCIFDESTROY, &ifr) < 0) { syslog(LOG_ERR, "destroy bridge: ioctl(SIOCIFDESTROY) " "failed: %s", strerror(errno)); return (-1); } return (0); } /* * Fetch the bridge base MAC address. Return pointer to the * buffer containing the MAC address, NULL on failure. */ u_char * bridge_get_basemac(const char *bif_name, u_char *mac, size_t mlen) { int len; char if_name[IFNAMSIZ]; struct ifaddrs *ifap, *ifa; struct sockaddr_dl sdl; if (getifaddrs(&ifap) != 0) { syslog(LOG_ERR, "bridge get mac: getifaddrs() failed - %s", strerror(errno)); return (NULL); } for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) { if (ifa->ifa_addr->sa_family != AF_LINK) continue; /* * Not just casting because of alignment constraints * on sparc64. */ bcopy(ifa->ifa_addr, &sdl, sizeof(struct sockaddr_dl)); if (sdl.sdl_alen > mlen) continue; if ((len = sdl.sdl_nlen) >= IFNAMSIZ) len = IFNAMSIZ - 1; bcopy(sdl.sdl_data, if_name, len); if_name[len] = '\0'; if (strcmp(bif_name, if_name) == 0) { bcopy(sdl.sdl_data + sdl.sdl_nlen, mac, sdl.sdl_alen); freeifaddrs(ifap); return (mac); } } freeifaddrs(ifap); return (NULL); } /************************************************************************ * Bridge ports. */ /* * Convert the kernel STP port state into * the corresopnding enumerated type from SNMP Bridge MIB. */ static int state2snmp_st(uint8_t ifbr_state) { switch (ifbr_state) { case BSTP_IFSTATE_DISABLED: return (StpPortState_disabled); case BSTP_IFSTATE_LISTENING: return (StpPortState_listening); case BSTP_IFSTATE_LEARNING: return (StpPortState_learning); case BSTP_IFSTATE_FORWARDING: return (StpPortState_forwarding); case BSTP_IFSTATE_BLOCKING: case BSTP_IFSTATE_DISCARDING: return (StpPortState_blocking); } return (StpPortState_broken); } /* * Fill in a bridge member information according to data polled from kernel. */ static void bridge_port_getinfo_conf(struct ifbreq *k_info, struct bridge_port *bp) { bp->state = state2snmp_st(k_info->ifbr_state); bp->priority = k_info->ifbr_priority; /* * RFC 4188: * "New implementations should support dot1dStpPortPathCost32. * If the port path costs exceeds the maximum value of this * object then this object should report the maximum value, * namely 65535. Applications should try to read the * dot1dStpPortPathCost32 object if this object reports * the maximum value." */ if (k_info->ifbr_ifsflags & IFBIF_BSTP_ADMCOST) bp->admin_path_cost = k_info->ifbr_path_cost; else bp->admin_path_cost = 0; bp->path_cost = k_info->ifbr_path_cost; if (k_info->ifbr_ifsflags & IFBIF_STP) bp->enable = dot1dStpPortEnable_enabled; else bp->enable = dot1dStpPortEnable_disabled; /* Begemot Bridge MIB only. */ if (k_info->ifbr_ifsflags & IFBIF_SPAN) bp->span_enable = begemotBridgeBaseSpanEnabled_enabled; else bp->span_enable = begemotBridgeBaseSpanEnabled_disabled; if (k_info->ifbr_ifsflags & IFBIF_PRIVATE) bp->priv_set = TruthValue_true; else bp->priv_set = TruthValue_false; if (k_info->ifbr_ifsflags & IFBIF_BSTP_ADMEDGE) bp->admin_edge = TruthValue_true; else bp->admin_edge = TruthValue_false; if (k_info->ifbr_ifsflags & IFBIF_BSTP_EDGE) bp->oper_edge = TruthValue_true; else bp->oper_edge = TruthValue_false; if (k_info->ifbr_ifsflags & IFBIF_BSTP_AUTOPTP) { bp->admin_ptp = StpPortAdminPointToPointType_auto; if (k_info->ifbr_ifsflags & IFBIF_BSTP_PTP) bp->oper_ptp = TruthValue_true; else bp->oper_ptp = TruthValue_false; } else if (k_info->ifbr_ifsflags & IFBIF_BSTP_PTP) { bp->admin_ptp = StpPortAdminPointToPointType_forceTrue; bp->oper_ptp = TruthValue_true; } else { bp->admin_ptp = StpPortAdminPointToPointType_forceFalse; bp->oper_ptp = TruthValue_false; } } /* * Fill in a bridge interface STP information according to * data polled from kernel. */ static void bridge_port_getinfo_opstp(struct ifbpstpreq *bp_stp, struct bridge_port *bp) { bp->enable = dot1dStpPortEnable_enabled; bp->fwd_trans = bp_stp->ifbp_fwd_trans; bp->design_cost = bp_stp->ifbp_design_cost; snmp_uint64_to_bridgeid(bp_stp->ifbp_design_root, bp->design_root); snmp_uint64_to_bridgeid(bp_stp->ifbp_design_bridge, bp->design_bridge); bcopy(&(bp_stp->ifbp_design_port), &(bp->design_port), sizeof(uint16_t)); } /* * Clear a bridge interface STP information. */ static void bridge_port_clearinfo_opstp(struct bridge_port *bp) { if (bp->enable == dot1dStpPortEnable_enabled) { bp->design_cost = 0; bzero(&(bp->design_root), sizeof(bridge_id)); bzero(&(bp->design_bridge), sizeof(bridge_id)); bzero(&(bp->design_port), sizeof(port_id)); bp->fwd_trans = 0; } bp->enable = dot1dStpPortEnable_disabled; } /* * Set a bridge member priority. */ int bridge_port_set_priority(const char *bif_name, struct bridge_port *bp, int32_t priority) { struct ifdrv ifd; struct ifbreq b_req; strlcpy(ifd.ifd_name, bif_name, sizeof(ifd.ifd_name)); ifd.ifd_len = sizeof(b_req); ifd.ifd_data = &b_req; strlcpy(b_req.ifbr_ifsname, bp->p_name, sizeof(b_req.ifbr_ifsname)); b_req.ifbr_priority = (uint8_t) priority; ifd.ifd_cmd = BRDGSIFPRIO; if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) { syslog(LOG_ERR, "set member %s param: ioctl(BRDGSIFPRIO) " "failed: %s", bp->p_name, strerror(errno)); return (-1); } bp->priority = priority; return (0); } /* * Set a bridge member STP-enabled flag. */ int bridge_port_set_stp_enable(const char *bif_name, struct bridge_port *bp, uint32_t enable) { struct ifdrv ifd; struct ifbreq b_req; if (bp->enable == enable) return (0); bzero(&b_req, sizeof(b_req)); strlcpy(ifd.ifd_name, bif_name, sizeof(ifd.ifd_name)); ifd.ifd_len = sizeof(b_req); ifd.ifd_data = &b_req; strlcpy(b_req.ifbr_ifsname, bp->p_name, sizeof(b_req.ifbr_ifsname)); ifd.ifd_cmd = BRDGGIFFLGS; if (ioctl(sock, SIOCGDRVSPEC, &ifd) < 0) { syslog(LOG_ERR, "get member %s param: ioctl(BRDGGIFFLGS) " "failed: %s", bp->p_name, strerror(errno)); return (-1); } if (enable == dot1dStpPortEnable_enabled) b_req.ifbr_ifsflags |= IFBIF_STP; else b_req.ifbr_ifsflags &= ~IFBIF_STP; ifd.ifd_cmd = BRDGSIFFLGS; if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) { syslog(LOG_ERR, "set member %s param: ioctl(BRDGSIFFLGS) " "failed: %s", bp->p_name, strerror(errno)); return (-1); } bp->enable = enable; return (0); } /* * Set a bridge member STP path cost. */ int bridge_port_set_path_cost(const char *bif_name, struct bridge_port *bp, int32_t path_cost) { struct ifdrv ifd; struct ifbreq b_req; if (path_cost < SNMP_PORT_MIN_PATHCOST || path_cost > SNMP_PORT_PATHCOST_OBSOLETE) return (-2); strlcpy(ifd.ifd_name, bif_name, sizeof(ifd.ifd_name)); ifd.ifd_len = sizeof(b_req); ifd.ifd_data = &b_req; strlcpy(b_req.ifbr_ifsname, bp->p_name, sizeof(b_req.ifbr_ifsname)); b_req.ifbr_path_cost = path_cost; ifd.ifd_cmd = BRDGSIFCOST; if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) { syslog(LOG_ERR, "set member %s param: ioctl(BRDGSIFCOST) " "failed: %s", bp->p_name, strerror(errno)); return (-1); } bp->admin_path_cost = path_cost; return (0); } /* * Set the PonitToPoint status of the link administratively. */ int bridge_port_set_admin_ptp(const char *bif_name, struct bridge_port *bp, uint32_t admin_ptp) { struct ifdrv ifd; struct ifbreq b_req; if (bp->admin_ptp == admin_ptp) return (0); bzero(&b_req, sizeof(b_req)); strlcpy(ifd.ifd_name, bif_name, sizeof(ifd.ifd_name)); ifd.ifd_len = sizeof(b_req); ifd.ifd_data = &b_req; strlcpy(b_req.ifbr_ifsname, bp->p_name, sizeof(b_req.ifbr_ifsname)); ifd.ifd_cmd = BRDGGIFFLGS; if (ioctl(sock, SIOCGDRVSPEC, &ifd) < 0) { syslog(LOG_ERR, "get member %s param: ioctl(BRDGGIFFLGS) " "failed: %s", bp->p_name, strerror(errno)); return (-1); } switch (admin_ptp) { case StpPortAdminPointToPointType_forceTrue: b_req.ifbr_ifsflags &= ~IFBIF_BSTP_AUTOPTP; b_req.ifbr_ifsflags |= IFBIF_BSTP_PTP; break; case StpPortAdminPointToPointType_forceFalse: b_req.ifbr_ifsflags &= ~IFBIF_BSTP_AUTOPTP; b_req.ifbr_ifsflags &= ~IFBIF_BSTP_PTP; break; case StpPortAdminPointToPointType_auto: b_req.ifbr_ifsflags |= IFBIF_BSTP_AUTOPTP; break; } ifd.ifd_cmd = BRDGSIFFLGS; if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) { syslog(LOG_ERR, "set member %s param: ioctl(BRDGSIFFLGS) " "failed: %s", bp->p_name, strerror(errno)); return (-1); } bp->admin_ptp = admin_ptp; return (0); } /* * Set admin edge. */ int bridge_port_set_admin_edge(const char *bif_name, struct bridge_port *bp, uint32_t enable) { struct ifdrv ifd; struct ifbreq b_req; if (bp->admin_edge == enable) return (0); bzero(&b_req, sizeof(b_req)); strlcpy(ifd.ifd_name, bif_name, sizeof(ifd.ifd_name)); ifd.ifd_len = sizeof(b_req); ifd.ifd_data = &b_req; strlcpy(b_req.ifbr_ifsname, bp->p_name, sizeof(b_req.ifbr_ifsname)); ifd.ifd_cmd = BRDGGIFFLGS; if (ioctl(sock, SIOCGDRVSPEC, &ifd) < 0) { syslog(LOG_ERR, "get member %s param: ioctl(BRDGGIFFLGS) " "failed: %s", bp->p_name, strerror(errno)); return (-1); } if (enable == TruthValue_true) { b_req.ifbr_ifsflags &= ~IFBIF_BSTP_AUTOEDGE; b_req.ifbr_ifsflags |= IFBIF_BSTP_EDGE; } else b_req.ifbr_ifsflags &= ~IFBIF_BSTP_EDGE; ifd.ifd_cmd = BRDGSIFFLGS; if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) { syslog(LOG_ERR, "set member %s param: ioctl(BRDGSIFFLGS) " "failed: %s", bp->p_name, strerror(errno)); return (-1); } bp->admin_edge = enable; return (0); } /* * Set 'private' flag. */ int bridge_port_set_private(const char *bif_name, struct bridge_port *bp, uint32_t priv_set) { struct ifdrv ifd; struct ifbreq b_req; if (bp->priv_set == priv_set) return (0); bzero(&b_req, sizeof(b_req)); strlcpy(ifd.ifd_name, bif_name, sizeof(ifd.ifd_name)); ifd.ifd_len = sizeof(b_req); ifd.ifd_data = &b_req; strlcpy(b_req.ifbr_ifsname, bp->p_name, sizeof(b_req.ifbr_ifsname)); ifd.ifd_cmd = BRDGGIFFLGS; if (ioctl(sock, SIOCGDRVSPEC, &ifd) < 0) { syslog(LOG_ERR, "get member %s param: ioctl(BRDGGIFFLGS) " "failed: %s", bp->p_name, strerror(errno)); return (-1); } if (priv_set == TruthValue_true) b_req.ifbr_ifsflags |= IFBIF_PRIVATE; else if (priv_set == TruthValue_false) b_req.ifbr_ifsflags &= ~IFBIF_PRIVATE; else return (SNMP_ERR_WRONG_VALUE); ifd.ifd_cmd = BRDGSIFFLGS; if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) { syslog(LOG_ERR, "set member %s param: ioctl(BRDGSIFFLGS) " "failed: %s", bp->p_name, strerror(errno)); return (-1); } bp->priv_set = priv_set; return (0); } /* * Add a bridge member port. */ int bridge_port_addm(struct bridge_port *bp, const char *b_name) { struct ifdrv ifd; struct ifbreq b_req; bzero(&ifd, sizeof(ifd)); bzero(&b_req, sizeof(b_req)); strlcpy(ifd.ifd_name, b_name, sizeof(ifd.ifd_name)); ifd.ifd_len = sizeof(b_req); ifd.ifd_data = &b_req; strlcpy(b_req.ifbr_ifsname, bp->p_name, sizeof(b_req.ifbr_ifsname)); if (bp->span_enable == begemotBridgeBaseSpanEnabled_enabled) ifd.ifd_cmd = BRDGADDS; else ifd.ifd_cmd = BRDGADD; if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) { syslog(LOG_ERR, "%s - add member : ioctl(%s) failed: %s", bp->p_name, (ifd.ifd_cmd == BRDGADDS ? "BRDGADDS" : "BRDGADD"), strerror(errno)); return (-1); } return (0); } /* * Delete a bridge member port. */ int bridge_port_delm(struct bridge_port *bp, const char *b_name) { struct ifdrv ifd; struct ifbreq b_req; bzero(&ifd, sizeof(ifd)); bzero(&b_req, sizeof(b_req)); strlcpy(ifd.ifd_name, b_name, sizeof(ifd.ifd_name)); ifd.ifd_len = sizeof(b_req); ifd.ifd_data = &b_req; strlcpy(b_req.ifbr_ifsname, bp->p_name, sizeof(b_req.ifbr_ifsname)); if (bp->span_enable == begemotBridgeBaseSpanEnabled_enabled) ifd.ifd_cmd = BRDGDELS; else ifd.ifd_cmd = BRDGDEL; if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) { syslog(LOG_ERR, "%s - add member : ioctl(%s) failed: %s", bp->p_name, (ifd.ifd_cmd == BRDGDELS ? "BRDGDELS" : "BRDGDEL"), strerror(errno)); return (-1); } return (0); } /* * Fetch the bridge member list from kernel. * Return -1 on error, or buffer len if successful. */ static int32_t bridge_port_get_iflist(struct bridge_if *bif, struct ifbreq **buf) { int n = 128; uint32_t len; struct ifbreq *ninbuf; struct ifbifconf ifbc; struct ifdrv ifd; *buf = NULL; strlcpy(ifd.ifd_name, bif->bif_name, IFNAMSIZ); ifd.ifd_cmd = BRDGGIFS; ifd.ifd_len = sizeof(ifbc); ifd.ifd_data = &ifbc; for ( ; ; ) { len = n * sizeof(struct ifbreq); if ((ninbuf = (struct ifbreq *)realloc(*buf, len)) == NULL) { syslog(LOG_ERR, "get bridge member list: " "realloc failed: %s", strerror(errno)); free(*buf); *buf = NULL; return (-1); } ifbc.ifbic_len = len; ifbc.ifbic_req = *buf = ninbuf; if (ioctl(sock, SIOCGDRVSPEC, &ifd) < 0) { syslog(LOG_ERR, "get bridge member list: ioctl " "(BRDGGIFS) failed: %s", strerror(errno)); free(*buf); buf = NULL; return (-1); } if ((ifbc.ifbic_len + sizeof(struct ifbreq)) < len) break; n += 64; } return (ifbc.ifbic_len); } /* * Fetch the bridge STP member list from kernel. * Return -1 on error, or buffer len if successful. */ static int32_t bridge_port_get_ifstplist(struct bridge_if *bif, struct ifbpstpreq **buf) { int n = 128; uint32_t len; struct ifbpstpreq *ninbuf; struct ifbpstpconf ifbstp; struct ifdrv ifd; *buf = NULL; strlcpy(ifd.ifd_name, bif->bif_name, IFNAMSIZ); ifd.ifd_cmd = BRDGGIFSSTP; ifd.ifd_len = sizeof(ifbstp); ifd.ifd_data = &ifbstp; for ( ; ; ) { len = n * sizeof(struct ifbpstpreq); if ((ninbuf = (struct ifbpstpreq *) realloc(*buf, len)) == NULL) { syslog(LOG_ERR, "get bridge STP ports list: " "realloc failed: %s", strerror(errno)); free(*buf); *buf = NULL; return (-1); } ifbstp.ifbpstp_len = len; ifbstp.ifbpstp_req = *buf = ninbuf; if (ioctl(sock, SIOCGDRVSPEC, &ifd) < 0) { syslog(LOG_ERR, "get bridge STP ports list: ioctl " "(BRDGGIFSSTP) failed: %s", strerror(errno)); free(*buf); buf = NULL; return (-1); } if ((ifbstp.ifbpstp_len + sizeof(struct ifbpstpreq)) < len) break; n += 64; } return (ifbstp.ifbpstp_len); } /* * Locate a bridge if STP params structure in a buffer. */ static struct ifbpstpreq * bridge_port_find_ifstplist(uint8_t port_no, struct ifbpstpreq *buf, uint32_t buf_len) { uint32_t i; struct ifbpstpreq *bstp; for (i = 0; i < buf_len / sizeof(struct ifbpstpreq); i++) { bstp = buf + i; if (bstp->ifbp_portno == port_no) return (bstp); } return (NULL); } /* * Read the initial info for all members of a bridge interface. * Returns the number of ports, 0 - if none, otherwise * -1 if some other error occurred. */ int bridge_getinfo_bif_ports(struct bridge_if *bif) { uint32_t i; int32_t buf_len; struct ifbreq *b_req_buf, *b_req; struct ifbpstpreq *bs_req_buf, *bs_req; struct bridge_port *bp; struct mibif *m_if; if ((buf_len = bridge_port_get_iflist(bif, &b_req_buf)) < 0) return (-1); for (i = 0; i < buf_len / sizeof(struct ifbreq); i++) { b_req = b_req_buf + i; if ((m_if = mib_find_if_sys(b_req->ifbr_portno)) != NULL) { /* Hopefully we will not fail here. */ if ((bp = bridge_new_port(m_if, bif)) != NULL) { bp->status = RowStatus_active; bridge_port_getinfo_conf(b_req, bp); bridge_port_getinfo_mibif(m_if, bp); } } else { syslog(LOG_ERR, "bridge member %s not present " "in mibII ifTable", b_req->ifbr_ifsname); } } free(b_req_buf); if ((buf_len = bridge_port_get_ifstplist(bif, &bs_req_buf)) < 0) return (-1); for (bp = bridge_port_bif_first(bif); bp != NULL; bp = bridge_port_bif_next(bp)) { if ((bs_req = bridge_port_find_ifstplist(bp->port_no, bs_req_buf, buf_len)) == NULL) bridge_port_clearinfo_opstp(bp); else bridge_port_getinfo_opstp(bs_req, bp); } free(bs_req_buf); return (i); } /* * Update the information for the bridge interface members. */ int bridge_update_memif(struct bridge_if *bif) { int added, updated; uint32_t i; int32_t buf_len; struct ifbreq *b_req_buf, *b_req; struct ifbpstpreq *bs_req_buf, *bs_req; struct bridge_port *bp, *bp_next; struct mibif *m_if; if ((buf_len = bridge_port_get_iflist(bif, &b_req_buf)) < 0) return (-1); added = updated = 0; #define BP_FOUND 0x01 for (i = 0; i < buf_len / sizeof(struct ifbreq); i++) { b_req = b_req_buf + i; if ((m_if = mib_find_if_sys(b_req->ifbr_portno)) == NULL) { syslog(LOG_ERR, "bridge member %s not present " "in mibII ifTable", b_req->ifbr_ifsname); continue; } if ((bp = bridge_port_find(m_if->index, bif)) == NULL && (bp = bridge_new_port(m_if, bif)) != NULL) { bp->status = RowStatus_active; added++; } if (bp != NULL) { updated++; bridge_port_getinfo_conf(b_req, bp); bridge_port_getinfo_mibif(m_if, bp); bp->flags |= BP_FOUND; } } free(b_req_buf); /* Clean up list. */ for (bp = bridge_port_bif_first(bif); bp != NULL; bp = bp_next) { bp_next = bridge_port_bif_next(bp); if ((bp->flags & BP_FOUND) == 0 && bp->status == RowStatus_active) bridge_port_remove(bp, bif); else bp->flags |= ~BP_FOUND; } #undef BP_FOUND if ((buf_len = bridge_port_get_ifstplist(bif, &bs_req_buf)) < 0) return (-1); for (bp = bridge_port_bif_first(bif); bp != NULL; bp = bridge_port_bif_next(bp)) { if ((bs_req = bridge_port_find_ifstplist(bp->port_no, bs_req_buf, buf_len)) == NULL) bridge_port_clearinfo_opstp(bp); else bridge_port_getinfo_opstp(bs_req, bp); } free(bs_req_buf); bif->ports_age = time(NULL); return (updated); } /************************************************************************ * Bridge addresses. */ /* * Update the bridge address info according to the polled data. */ static void bridge_addrs_info_ifaddrlist(struct ifbareq *ifba, struct tp_entry *tpe) { tpe->port_no = if_nametoindex(ifba->ifba_ifsname); if ((ifba->ifba_flags & IFBAF_TYPEMASK) == IFBAF_STATIC) tpe->status = TpFdbStatus_mgmt; else tpe->status = TpFdbStatus_learned; } /* * Read the bridge addresses from kernel. * Return -1 on error, or buffer len if successful. */ static int32_t bridge_addrs_getinfo_ifalist(struct bridge_if *bif, struct ifbareq **buf) { int n = 128; uint32_t len; struct ifbareq *ninbuf; struct ifbaconf bac; struct ifdrv ifd; - *buf = NULL; + *buf = NULL; strlcpy(ifd.ifd_name, bif->bif_name, IFNAMSIZ); ifd.ifd_cmd = BRDGRTS; ifd.ifd_len = sizeof(bac); ifd.ifd_data = &bac; for ( ; ; ) { len = n * sizeof(struct ifbareq); if ((ninbuf = (struct ifbareq *)realloc(*buf, len)) == NULL) { syslog(LOG_ERR, "get bridge address list: " " realloc failed: %s", strerror(errno)); free(*buf); *buf = NULL; return (-1); } bac.ifbac_len = len; bac.ifbac_req = *buf = ninbuf; if (ioctl(sock, SIOCGDRVSPEC, &ifd) < 0) { syslog(LOG_ERR, "get bridge address list: " "ioctl(BRDGRTS) failed: %s", strerror(errno)); free(*buf); buf = NULL; return (-1); } if ((bac.ifbac_len + sizeof(struct ifbareq)) < len) break; n += 64; } return (bac.ifbac_len); } /* * Read the initial info for all addresses on a bridge interface. * Returns the number of addresses, 0 - if none, otherwise * -1 if some other error occurred. */ int bridge_getinfo_bif_addrs(struct bridge_if *bif) { uint32_t i; int32_t buf_len; struct ifbareq *addr_req_buf, *addr_req; struct tp_entry *te; if ((buf_len = bridge_addrs_getinfo_ifalist(bif, &addr_req_buf)) < 0) return (-1); for (i = 0; i < buf_len / sizeof(struct ifbareq); i++) { addr_req = addr_req_buf + i; if ((te = bridge_new_addrs(addr_req->ifba_dst, bif)) != NULL) bridge_addrs_info_ifaddrlist(addr_req, te); } free(addr_req_buf); return (i); } /* * Update the addresses for the bridge interface. */ int bridge_update_addrs(struct bridge_if *bif) { int added, updated; uint32_t i; int32_t buf_len; struct tp_entry *te, *te_next; struct ifbareq *addr_req_buf, *addr_req; if ((buf_len = bridge_addrs_getinfo_ifalist(bif, &addr_req_buf)) < 0) return (-1); added = updated = 0; #define BA_FOUND 0x01 for (i = 0; i < buf_len / sizeof(struct ifbareq); i++) { addr_req = addr_req_buf + i; if ((te = bridge_addrs_find(addr_req->ifba_dst, bif)) == NULL) { added++; if ((te = bridge_new_addrs(addr_req->ifba_dst, bif)) == NULL) continue; } else updated++; bridge_addrs_info_ifaddrlist(addr_req, te); te-> flags |= BA_FOUND; } free(addr_req_buf); for (te = bridge_addrs_bif_first(bif); te != NULL; te = te_next) { te_next = bridge_addrs_bif_next(te); if ((te-> flags & BA_FOUND) == 0) bridge_addrs_remove(te, bif); else te-> flags &= ~BA_FOUND; } #undef BA_FOUND bif->addrs_age = time(NULL); return (updated + added); } /************************************************************************ * Bridge packet filtering. */ const char bridge_sysctl[] = "net.link.bridge."; static struct { int32_t val; const char *name; } bridge_pf_sysctl[] = { { 1, "pfil_bridge" }, { 1, "pfil_member" }, { 1, "pfil_onlyip" }, { 0, "ipfw" }, }; int32_t bridge_get_pfval(uint8_t which) { if (which > sizeof(bridge_pf_sysctl) / sizeof(bridge_pf_sysctl[0]) || which < 1) return (-1); return (bridge_pf_sysctl[which - 1].val); } int32_t bridge_do_pfctl(int32_t bridge_ctl, enum snmp_op op, int32_t *val) { char mib_name[100]; int32_t i, s_i; size_t len, s_len; if (bridge_ctl >= LEAF_begemotBridgeLayer2PfStatus) return (-2); if (op == SNMP_OP_SET) { s_i = *val; s_len = sizeof(s_i); } else s_len = 0; len = sizeof(i); strcpy(mib_name, bridge_sysctl); if (sysctlbyname(strcat(mib_name, bridge_pf_sysctl[bridge_ctl].name), &i, &len, (op == SNMP_OP_SET ? &s_i : NULL), s_len) == -1) { syslog(LOG_ERR, "sysctl(%s%s) failed - %s", bridge_sysctl, bridge_pf_sysctl[bridge_ctl].name, strerror(errno)); return (-1); } bridge_pf_sysctl[bridge_ctl].val = i; *val = i; return (i); } void bridge_pf_dump(void) { uint8_t i; for (i = 0; i < sizeof(bridge_pf_sysctl) / sizeof(bridge_pf_sysctl[0]); i++) { syslog(LOG_ERR, "%s%s = %d", bridge_sysctl, bridge_pf_sysctl[i].name, bridge_pf_sysctl[i].val); } } Index: head/usr.sbin/bsnmpd/modules/snmp_bridge/snmp_bridge.3 =================================================================== --- head/usr.sbin/bsnmpd/modules/snmp_bridge/snmp_bridge.3 (revision 310647) +++ head/usr.sbin/bsnmpd/modules/snmp_bridge/snmp_bridge.3 (revision 310648) @@ -1,119 +1,119 @@ .\"- .\" Copyright (C) 2006 Shteryana Shopova .\" All rights reserved. -.\" +.\" .\" 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. .\" .\" $FreeBSD$ .\" .Dd August 6, 2007 .Dt SNMP_BRIDGE 3 .Os .Sh NAME .Nm snmp_bridge .Nd "bridge module for snmpd" .Sh LIBRARY .Pq begemotSnmpdModulePath."bridge" = "/usr/lib/snmp_bridge.so" .Sh DESCRIPTION The .Nm snmp_bridge module implements the BRIDGE-MIB as standardized in RFC 4188, the RSTP-MIB standardized in RFC4318 and a private BEGEMOT-BRIDGE-MIB, which allows management of multiple bridge interfaces. Most of the objects defined in the private BEGEMOT-BRIDGE-MIB are duplicates of the original objects defined by the standard BRIDGE-MIB, but the private MIB also defines additional objects which make the functionality of .Nm similar to .Xr ifconfig 8 for configuring bridge interfaces. Therefore one should consider adding write communities or loading the .Nm module on systems where security is crucial. .Sh IMPLEMENTATION NOTES The additional objects to configure a bridge are: .Bl -tag -width "XXXXXXXXX" .It Va begemotBridgeBaseStatus Bridge interfaces can be created and destroyed via this object. SNMP SET operations with the following values are allowed: .Bl -tag -width ".It Va createAndWait" .It Va createAndWait will attempt to create a bridge interface with the name given by the table index. .It Va createAndGo will attempt to create a bridge interface with the name given by the table index and set the status of the interface to "active/up". .It Va destroy will attempt to destroy the bridge interface. .El .It Va begemotBridgeBaseSpanEnabled A SNMP SET operation on this object is only successful if the corresponding port has not been added as member of the bridge interface on the system. .It Va begemotBridgeBasePortStatus SNMP SET operations with the following values are allowed: .Bl -tag -width ".It Va createAndWait" .It Va createAndWait will create a new row for the bridge member in the SNMP .Va begemotBridgeBasePortTable but will not try to commit the information to the system. .It Va active will attempt to commit the information to the system and will be successful only if a value for .Va begemotBridgeBaseSpanEnabled has been SET already. .It Va destroy will attempt to remove the interface from the system bridge interface. .El .It Va begemotBridgeBasePortPrivate This object controls a bridge interface flag called PRIVATE where any private port can not communicate with another private port. .El .Sh RESTRICTIONS Not all information in the MIBs is currently available in FreeBSD. The following variables carry no information: .Bl -tag -width "XXXXXXXXX" .It Va dot1dBasePortCircuit .It Va dot1dBasePortDelayExceededDiscards .It Va dot1dBasePortMtuExceededDiscards .It Va begemotBridgeBasePortDelayExceededDiscards .It Va begemotBridgeBasePortMtuExceededDiscards .El .Sh FILES .Bl -tag -width "XXXXXXXXX" .It Pa /usr/share/snmp/defs/bridge_tree.def The description of the MIB tree implemented by .Nm . .It Pa /usr/share/snmp/mibs/BRIDGE-MIB.txt This is the BRIDGE-MIB that is implemented by this module. .It Pa /usr/share/snmp/mibs/RSTP-MIB.txt This is the RSTP-MIB implemented by this module. .It Pa /usr/share/snmp/mibs/BEGEMOT-BRIDGE-MIB.txt This is the private BEGEMOT-BRIDGE-MIB that is implemented by this module. .El .Sh SEE ALSO .Xr bsnmpd 1 , .Xr gensnmptree 1 , .Xr snmpmod 3 , .Xr if_bridge 4 , .Xr ifconfig 8 .Sh AUTHORS .An Shteryana Shopova Aq Mt syrinx@FreeBSD.org Index: head/usr.sbin/bsnmpd/modules/snmp_hostres/snmp_hostres.3 =================================================================== --- head/usr.sbin/bsnmpd/modules/snmp_hostres/snmp_hostres.3 (revision 310647) +++ head/usr.sbin/bsnmpd/modules/snmp_hostres/snmp_hostres.3 (revision 310648) @@ -1,88 +1,88 @@ .\" .\" Copyright (C) 2005-2006 .\" The FreeBSD Project. .\" 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. .\" .\" $FreeBSD$ .\" .Dd January 3, 2006 .Dt SNMP_HOSTRES 3 .Os .Sh NAME .Nm snmp_hostres .Nd host resources module for .Xr bsnmpd 8 .Sh LIBRARY .Pq begemotSnmpdModulePath."hostres" = "/usr/lib/snmp_hostres.so" .Sh DESCRIPTION The .Nm module implements the HOST-RESOURCES-MIB as standardized in RFC 2790. .Sh RESTRICTIONS Not all information in the MIB is meaningful in FreeBSD or is available. The following variables are not implemented or carry no information: .Bl -tag -width indent .It Va hrFSType There are several types of file systems for which no appropriate OID exists yet which are supported by .Fx . For smbfs, procfs and devfs , .Va hrFSOther is returned. In all other cases, .Va hrFSUnknown . .It Va hrFSBootable It is questionable what bootable means here. Does it mean that the BIOS is available to start a boot on that file system or does it mean that there is something bootable? In either case this information is not available so this variable returns True for the root file system (which is not necessarily correct) and False for all others. .It Va hrFSLastFullBackupDate , hrFSLastPartialBackupDate This is not available and always returns an empty string. Theoretically, this could be retrieved from .Pa /etc/dumpdates , which would hardly be correct given the different ways of doing backups. .It Va hrDiskStorageTable Floppy devices are currently not reported. Also the names of the disks are hard-coded in the module. .El .Sh FILES .Bl -tag -width indent .It Pa /usr/share/snmp/defs/hostres_tree.def The description of the MIB tree implemented by .Nm . .It Pa /usr/share/snmp/mibs/HOST-RESOURCES-TYPES.txt .It Pa /usr/share/snmp/mibs/HOST-RESOURCES-MIB.txt .It Pa /usr/share/snmp/mibs/BEGEMOT-HOSTRES-MIB.txt This is the MIB that is implemented by this module. .El .Sh SEE ALSO .Xr gensnmptree 1 , .Xr snmpmod 3 .Sh AUTHORS .An Victor Cruceru Aq Mt soc-victor@FreeBSD.org Index: head/usr.sbin/bsnmpd/modules/snmp_netgraph/snmp_netgraph.c =================================================================== --- head/usr.sbin/bsnmpd/modules/snmp_netgraph/snmp_netgraph.c (revision 310647) +++ head/usr.sbin/bsnmpd/modules/snmp_netgraph/snmp_netgraph.c (revision 310648) @@ -1,1690 +1,1690 @@ /* * Copyright (c) 2001-2003 * Fraunhofer Institute for Open Communication Systems (FhG Fokus). * 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. * * $FreeBSD$ * * Netgraph interface for SNMPd. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "snmp_netgraph.h" #include "netgraph_tree.h" #include "netgraph_oid.h" /* maximum message size */ #define RESBUFSIZ 20000 /* default node name */ #define NODENAME "NgSnmpd" /* my node Id */ ng_ID_t snmp_node; u_char *snmp_nodename; /* the Object Resource registration index */ static u_int reg_index; static const struct asn_oid oid_begemotNg = OIDX_begemotNg; /* configuration */ /* this must be smaller than int32_t because the functions in libnetgraph * falsely return an int */ static size_t resbufsiz = RESBUFSIZ; static u_int timeout = 1000; static u_int debug_level; /* number of microseconds per clock tick */ static struct clockinfo clockinfo; /* Csock buffers. Communication on the csock is asynchronuous. This means * if we wait for a specific response, we may get other messages. Put these * into a queue and execute them when we are idle. */ struct csock_buf { STAILQ_ENTRY(csock_buf) link; struct ng_mesg *mesg; char path[NG_PATHSIZ]; }; static STAILQ_HEAD(, csock_buf) csock_bufs = STAILQ_HEAD_INITIALIZER(csock_bufs); /* * We dispatch unsolicieted messages by node cookies and ids. * So we must keep a list of hook names and dispatch functions. */ struct msgreg { u_int32_t cookie; ng_ID_t id; ng_cookie_f *func; void *arg; const struct lmodule *mod; SLIST_ENTRY(msgreg) link; }; static SLIST_HEAD(, msgreg) msgreg_list = SLIST_HEAD_INITIALIZER(msgreg_list); /* * Data messages are dispatched by hook names. */ struct datareg { char hook[NG_HOOKSIZ]; ng_hook_f *func; void *arg; const struct lmodule *mod; SLIST_ENTRY(datareg) link; }; static SLIST_HEAD(, datareg) datareg_list = SLIST_HEAD_INITIALIZER(datareg_list); /* the netgraph sockets */ static int csock, dsock; static void *csock_fd, *dsock_fd; /* our module handle */ static struct lmodule *module; /* statistics */ static u_int32_t stats[LEAF_begemotNgTooLargeDatas+1]; /* netgraph type list */ struct ngtype { char name[NG_TYPESIZ]; struct asn_oid index; TAILQ_ENTRY(ngtype) link; }; TAILQ_HEAD(ngtype_list, ngtype); static struct ngtype_list ngtype_list; static uint64_t ngtype_tick; /* * Register a function to receive unsolicited messages */ void * ng_register_cookie(const struct lmodule *mod, u_int32_t cookie, ng_ID_t id, ng_cookie_f *func, void *arg) { struct msgreg *d; if ((d = malloc(sizeof(*d))) == NULL) return (NULL); d->cookie = cookie; d->id = id; d->func = func; d->arg = arg; d->mod = mod; SLIST_INSERT_HEAD(&msgreg_list, d, link); return (d); } /* * Remove a registration. */ void ng_unregister_cookie(void *dd) { struct msgreg *d = dd; SLIST_REMOVE(&msgreg_list, d, msgreg, link); free(d); } /* * Register a function for hook data. */ void * -ng_register_hook(const struct lmodule *mod, const char *hook, +ng_register_hook(const struct lmodule *mod, const char *hook, ng_hook_f *func, void *arg) { struct datareg *d; if ((d = malloc(sizeof(*d))) == NULL) return (NULL); strcpy(d->hook, hook); d->func = func; d->arg = arg; d->mod = mod; SLIST_INSERT_HEAD(&datareg_list, d, link); return (d); } /* * Unregister a hook function */ void ng_unregister_hook(void *dd) { struct datareg *d = dd; SLIST_REMOVE(&datareg_list, d, datareg, link); free(d); } /* * Unregister all hooks and cookies for that module. Note: doesn't disconnect * any hooks! */ void ng_unregister_module(const struct lmodule *mod) { struct msgreg *m, *m1; struct datareg *d, *d1; m = SLIST_FIRST(&msgreg_list); while (m != NULL) { m1 = SLIST_NEXT(m, link); if (m->mod == mod) { SLIST_REMOVE(&msgreg_list, m, msgreg, link); free(m); } m = m1; } d = SLIST_FIRST(&datareg_list); while (d != NULL) { d1 = SLIST_NEXT(d, link); if (d->mod == mod) { SLIST_REMOVE(&datareg_list, d, datareg, link); free(d); } d = d1; } } /* * Dispatch a message to the correct module and delete it. More than one * module can get a message. */ static void csock_handle(struct ng_mesg *mesg, const char *path) { struct msgreg *d, *d1; u_int id; int len; if (sscanf(path, "[%x]:%n", &id, &len) != 1 || (u_int)len != strlen(path)) { syslog(LOG_ERR, "cannot parse message path '%s'", path); id = 0; } d = SLIST_FIRST(&msgreg_list); while (d != NULL) { d1 = SLIST_NEXT(d, link); if (d->cookie == mesg->header.typecookie && (d->id == 0 || d->id == id || id == 0)) (*d->func)(mesg, path, id, d->arg); d = d1; } free(mesg); } /* * Input from the control socket. */ static struct ng_mesg * csock_read(char *path) { struct ng_mesg *mesg; int ret, err; if ((mesg = malloc(resbufsiz + 1)) == NULL) { stats[LEAF_begemotNgNoMems]++; syslog(LOG_CRIT, "out of memory"); errno = ENOMEM; return (NULL); } if ((ret = NgRecvMsg(csock, mesg, resbufsiz + 1, path)) < 0) { err = errno; free(mesg); if (errno == EWOULDBLOCK) { errno = err; return (NULL); } stats[LEAF_begemotNgMsgReadErrs]++; syslog(LOG_WARNING, "read from csock: %m"); errno = err; return (NULL); } if (ret == 0) { syslog(LOG_DEBUG, "node closed -- exiting"); exit(0); } if ((size_t)ret > resbufsiz) { stats[LEAF_begemotNgTooLargeMsgs]++; syslog(LOG_WARNING, "ng message too large"); free(mesg); errno = EFBIG; return (NULL); } return (mesg); } static void csock_input(int fd __unused, void *udata __unused) { struct ng_mesg *mesg; char path[NG_PATHSIZ]; if ((mesg = csock_read(path)) == NULL) return; csock_handle(mesg, path); } /* * Write a message to a node. */ int ng_output(const char *path, u_int cookie, u_int opcode, const void *arg, size_t arglen) { return (NgSendMsg(csock, path, (int)cookie, (int)opcode, arg, arglen)); } int ng_output_node(const char *node, u_int cookie, u_int opcode, const void *arg, size_t arglen) { char path[NG_PATHSIZ]; sprintf(path, "%s:", node); return (ng_output(path, cookie, opcode, arg, arglen)); } int ng_output_id(ng_ID_t node, u_int cookie, u_int opcode, const void *arg, size_t arglen) { char path[NG_PATHSIZ]; sprintf(path, "[%x]:", node); return (ng_output(path, cookie, opcode, arg, arglen)); } /* * Execute a synchronuous dialog with the csock. All message we receive, that * do not match our request, are queue until the next call to the IDLE function. */ struct ng_mesg * ng_dialog(const char *path, u_int cookie, u_int opcode, const void *arg, size_t arglen) { int token, err; struct ng_mesg *mesg; char rpath[NG_PATHSIZ]; struct csock_buf *b; struct timeval end, tv; if ((token = ng_output(path, cookie, opcode, arg, arglen)) < 0) return (NULL); if (csock_fd) fd_suspend(csock_fd); gettimeofday(&end, NULL); tv.tv_sec = timeout / 1000; tv.tv_usec = (timeout % 1000) * 1000; timeradd(&end, &tv, &end); for (;;) { mesg = NULL; gettimeofday(&tv, NULL); if (timercmp(&tv, &end, >=)) { block: syslog(LOG_WARNING, "no response for request %u/%u", cookie, opcode); errno = EWOULDBLOCK; break; } timersub(&end, &tv, &tv); if (tv.tv_sec == 0 && tv.tv_usec < clockinfo.tick) goto block; if (setsockopt(csock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1) syslog(LOG_WARNING, "setsockopt(SO_RCVTIMEO): %m"); if ((mesg = csock_read(rpath)) == NULL) { if (errno == EWOULDBLOCK) continue; break; } if (mesg->header.token == (u_int)token) break; if ((b = malloc(sizeof(*b))) == NULL) { stats[LEAF_begemotNgNoMems]++; syslog(LOG_ERR, "out of memory"); free(mesg); continue; } b->mesg = mesg; strcpy(b->path, rpath); STAILQ_INSERT_TAIL(&csock_bufs, b, link); } tv.tv_sec = 0; tv.tv_usec = 0; if (setsockopt(csock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1) syslog(LOG_WARNING, "setsockopt(SO_RCVTIMEO,0): %m"); if (csock_fd) { err = errno; fd_resume(csock_fd); errno = err; } return (mesg); } struct ng_mesg * ng_dialog_node(const char *node, u_int cookie, u_int opcode, const void *arg, size_t arglen) { char path[NG_PATHSIZ]; sprintf(path, "%s:", node); return (ng_dialog(path, cookie, opcode, arg, arglen)); } struct ng_mesg * ng_dialog_id(ng_ID_t id, u_int cookie, u_int opcode, const void *arg, size_t arglen) { char path[NG_PATHSIZ]; sprintf(path, "[%x]:", id); return (ng_dialog(path, cookie, opcode, arg, arglen)); } /* * Send a data message to a given hook. */ int ng_send_data(const char *hook, const void *sndbuf, size_t sndlen) { return (NgSendData(dsock, hook, sndbuf, sndlen)); } /* * Input from a data socket. Dispatch to the function for that hook. */ static void dsock_input(int fd __unused, void *udata __unused) { u_char *resbuf, embuf[100]; ssize_t len; char hook[NG_HOOKSIZ]; struct datareg *d, *d1; if ((resbuf = malloc(resbufsiz + 1)) == NULL) { stats[LEAF_begemotNgNoMems]++; syslog(LOG_CRIT, "out of memory"); (void)NgRecvData(fd, embuf, sizeof(embuf), hook); errno = ENOMEM; return; } if ((len = NgRecvData(fd, resbuf, resbufsiz + 1, hook)) == -1) { stats[LEAF_begemotNgDataReadErrs]++; syslog(LOG_ERR, "reading message: %m"); free(resbuf); return; } if (len == 0) { free(resbuf); return; } if ((size_t)len == resbufsiz + 1) { stats[LEAF_begemotNgTooLargeDatas]++; syslog(LOG_WARNING, "message too long"); free(resbuf); return; } /* * Dispatch message. Maybe dispatched to more than one function. */ d = SLIST_FIRST(&datareg_list); while (d != NULL) { d1 = SLIST_NEXT(d, link); if (strcmp(hook, d->hook) == 0) (*d->func)(hook, resbuf, len, d->arg); d = d1; } free(resbuf); } /* * The SNMP daemon is about to wait for an event. Look whether we have * netgraph messages waiting. If yes, drain the queue. */ static void ng_idle(void) { struct csock_buf *b; /* execute waiting csock_bufs */ while ((b = STAILQ_FIRST(&csock_bufs)) != NULL) { STAILQ_REMOVE_HEAD(&csock_bufs, link); csock_handle(b->mesg, b->path); free(b); } } /* * Called when the module is loaded. Returning a non-zero value means, * rejecting the initialisation. * * We make the netgraph socket. */ static int ng_init(struct lmodule *mod, int argc, char *argv[]) { int name[2]; size_t len; module = mod; if (argc == 0) { if ((snmp_nodename = malloc(strlen(NODENAME) + 1)) == NULL) return (ENOMEM); strcpy(snmp_nodename, NODENAME); } else { if ((snmp_nodename = malloc(NG_NODESIZ)) == NULL) return (ENOMEM); strlcpy(snmp_nodename, argv[0], NG_NODESIZ); } /* fetch clockinfo (for the number of microseconds per tick) */ name[0] = CTL_KERN; name[1] = KERN_CLOCKRATE; len = sizeof(clockinfo); if (sysctl(name, 2, &clockinfo, &len, NULL, 0) == -1) return (errno); TAILQ_INIT(&ngtype_list); return (0); } /* * Get the node Id/name/type of a node. */ ng_ID_t ng_node_id(const char *path) { struct ng_mesg *resp; ng_ID_t id; if ((resp = ng_dialog(path, NGM_GENERIC_COOKIE, NGM_NODEINFO, NULL, 0)) == NULL) return (0); id = ((struct nodeinfo *)(void *)resp->data)->id; free(resp); return (id); } ng_ID_t ng_node_id_node(const char *node) { struct ng_mesg *resp; ng_ID_t id; if ((resp = ng_dialog_node(node, NGM_GENERIC_COOKIE, NGM_NODEINFO, NULL, 0)) == NULL) return (0); id = ((struct nodeinfo *)(void *)resp->data)->id; free(resp); return (id); } ng_ID_t ng_node_name(ng_ID_t id, char *name) { struct ng_mesg *resp; if ((resp = ng_dialog_id(id, NGM_GENERIC_COOKIE, NGM_NODEINFO, NULL, 0)) == NULL) return (0); strcpy(name, ((struct nodeinfo *)(void *)resp->data)->name); free(resp); return (id); } ng_ID_t ng_node_type(ng_ID_t id, char *type) { struct ng_mesg *resp; if ((resp = ng_dialog_id(id, NGM_GENERIC_COOKIE, NGM_NODEINFO, NULL, 0)) == NULL) return (0); strcpy(type, ((struct nodeinfo *)(void *)resp->data)->type); free(resp); return (id); } /* * Connect our node to some other node */ int ng_connect_node(const char *node, const char *ourhook, const char *peerhook) { struct ngm_connect conn; snprintf(conn.path, NG_PATHSIZ, "%s:", node); strlcpy(conn.ourhook, ourhook, NG_HOOKSIZ); strlcpy(conn.peerhook, peerhook, NG_HOOKSIZ); return (NgSendMsg(csock, ".:", NGM_GENERIC_COOKIE, NGM_CONNECT, &conn, sizeof(conn))); } int ng_connect_id(ng_ID_t id, const char *ourhook, const char *peerhook) { struct ngm_connect conn; snprintf(conn.path, NG_PATHSIZ, "[%x]:", id); strlcpy(conn.ourhook, ourhook, NG_HOOKSIZ); strlcpy(conn.peerhook, peerhook, NG_HOOKSIZ); return (NgSendMsg(csock, ".:", NGM_GENERIC_COOKIE, NGM_CONNECT, &conn, sizeof(conn))); } int ng_connect2_id(ng_ID_t id, ng_ID_t peer, const char *ourhook, const char *peerhook) { struct ngm_connect conn; char path[NG_PATHSIZ]; snprintf(path, NG_PATHSIZ, "[%x]:", id); snprintf(conn.path, NG_PATHSIZ, "[%x]:", peer); strlcpy(conn.ourhook, ourhook, NG_HOOKSIZ); strlcpy(conn.peerhook, peerhook, NG_HOOKSIZ); return (NgSendMsg(csock, path, NGM_GENERIC_COOKIE, NGM_CONNECT, &conn, sizeof(conn))); } int ng_connect2_tee_id(ng_ID_t id, ng_ID_t peer, const char *ourhook, const char *peerhook) { struct ngm_connect conn; char path[NG_PATHSIZ]; ng_ID_t tee; if ((tee = ng_mkpeer_id(id, NULL, "tee", ourhook, "left")) == 0) return (-1); snprintf(path, NG_PATHSIZ, "[%x]:", tee); snprintf(conn.path, NG_PATHSIZ, "[%x]:", peer); strlcpy(conn.ourhook, "right", NG_HOOKSIZ); strlcpy(conn.peerhook, peerhook, NG_HOOKSIZ); return (NgSendMsg(csock, path, NGM_GENERIC_COOKIE, NGM_CONNECT, &conn, sizeof(conn))); } /* * Ensure that a node of type 'type' is connected to 'hook' of 'node' * and return its node id. tee nodes between node and the target node * are skipped. If the type is wrong, or the hook is a dead-end return 0. * If type is NULL, it is not checked. */ static ng_ID_t ng_next_node_id_internal(ng_ID_t node, const char *type, const char *hook, int skip_tee) { struct ng_mesg *resp; struct hooklist *hooklist; u_int i; if ((resp = ng_dialog_id(node, NGM_GENERIC_COOKIE, NGM_LISTHOOKS, NULL, 0)) == NULL) { syslog(LOG_ERR, "get hook list: %m"); exit(1); } hooklist = (struct hooklist *)(void *)resp->data; for (i = 0; i < hooklist->nodeinfo.hooks; i++) if (strcmp(hooklist->link[i].ourhook, hook) == 0) break; if (i == hooklist->nodeinfo.hooks) { free(resp); return (0); } node = hooklist->link[i].nodeinfo.id; if (skip_tee && strcmp(hooklist->link[i].nodeinfo.type, "tee") == 0) { if (strcmp(hooklist->link[i].peerhook, "left") == 0) node = ng_next_node_id(node, type, "right"); else if (strcmp(hooklist->link[i].peerhook, "right") == 0) node = ng_next_node_id(node, type, "left"); else if (type != NULL && strcmp(hooklist->link[i].nodeinfo.type, type) != 0) node = 0; } else if (type != NULL && strcmp(hooklist->link[i].nodeinfo.type, type) != 0) node = 0; free(resp); return (node); } /* * Ensure that a node of type 'type' is connected to 'hook' of 'node' * and return its node id. tee nodes between node and the target node * are skipped. If the type is wrong, or the hook is a dead-end return 0. * If type is NULL, it is not checked. */ ng_ID_t ng_next_node_id(ng_ID_t node, const char *type, const char *hook) { return (ng_next_node_id_internal(node, type, hook, 1)); } ng_ID_t ng_mkpeer_id(ng_ID_t id, const char *nodename, const char *type, const char *hook, const char *peerhook) { char path[NG_PATHSIZ]; struct ngm_mkpeer mkpeer; struct ngm_name name; strlcpy(mkpeer.type, type, NG_TYPESIZ); strlcpy(mkpeer.ourhook, hook, NG_HOOKSIZ); strlcpy(mkpeer.peerhook, peerhook, NG_HOOKSIZ); sprintf(path, "[%x]:", id); if (NgSendMsg(csock, path, NGM_GENERIC_COOKIE, NGM_MKPEER, &mkpeer, sizeof(mkpeer)) == -1) return (0); if ((id = ng_next_node_id_internal(id, NULL, hook, 0)) == 0) return (0); if (nodename != NULL) { strcpy(name.name, nodename); sprintf(path, "[%x]:", id); if (NgSendMsg(csock, path, NGM_GENERIC_COOKIE, NGM_NAME, &name, sizeof(name)) == -1) return (0); } return (id); } /* * SHutdown node */ int ng_shutdown_id(ng_ID_t id) { char path[NG_PATHSIZ]; snprintf(path, NG_PATHSIZ, "[%x]:", id); return (NgSendMsg(csock, path, NGM_GENERIC_COOKIE, NGM_SHUTDOWN, NULL, 0)); } /* * Disconnect one of our hooks */ int ng_rmhook(const char *ourhook) { struct ngm_rmhook rmhook; strlcpy(rmhook.ourhook, ourhook, NG_HOOKSIZ); return (NgSendMsg(csock, ".:", NGM_GENERIC_COOKIE, NGM_RMHOOK, &rmhook, sizeof(rmhook))); } /* * Disconnect a hook of a node */ int ng_rmhook_id(ng_ID_t id, const char *hook) { struct ngm_rmhook rmhook; char path[NG_PATHSIZ]; strlcpy(rmhook.ourhook, hook, NG_HOOKSIZ); snprintf(path, NG_PATHSIZ, "[%x]:", id); return (NgSendMsg(csock, path, NGM_GENERIC_COOKIE, NGM_RMHOOK, &rmhook, sizeof(rmhook))); } /* * Disconnect a hook and shutdown all tee nodes that were connected to that * hook. */ int ng_rmhook_tee_id(ng_ID_t node, const char *hook) { struct ng_mesg *resp; struct hooklist *hooklist; u_int i; int first = 1; ng_ID_t next_node; const char *next_hook; again: /* if we have just shutdown a tee node, which had no other hooks * connected, the node id may already be wrong here. */ if ((resp = ng_dialog_id(node, NGM_GENERIC_COOKIE, NGM_LISTHOOKS, NULL, 0)) == NULL) return (0); hooklist = (struct hooklist *)(void *)resp->data; for (i = 0; i < hooklist->nodeinfo.hooks; i++) if (strcmp(hooklist->link[i].ourhook, hook) == 0) break; if (i == hooklist->nodeinfo.hooks) { free(resp); return (0); } next_node = 0; next_hook = NULL; if (strcmp(hooklist->link[i].nodeinfo.type, "tee") == 0) { if (strcmp(hooklist->link[i].peerhook, "left") == 0) { next_node = hooklist->link[i].nodeinfo.id; next_hook = "right"; } else if (strcmp(hooklist->link[i].peerhook, "right") == 0) { next_node = hooklist->link[i].nodeinfo.id; next_hook = "left"; } } free(resp); if (first) { ng_rmhook_id(node, hook); first = 0; } else { ng_shutdown_id(node); } if ((node = next_node) == 0) return (0); hook = next_hook; goto again; } /* * Get the peer hook of a hook on a given node. Skip any tee nodes in between */ int ng_peer_hook_id(ng_ID_t node, const char *hook, char *peerhook) { struct ng_mesg *resp; struct hooklist *hooklist; u_int i; int ret; if ((resp = ng_dialog_id(node, NGM_GENERIC_COOKIE, NGM_LISTHOOKS, NULL, 0)) == NULL) { syslog(LOG_ERR, "get hook list: %m"); exit(1); } hooklist = (struct hooklist *)(void *)resp->data; for (i = 0; i < hooklist->nodeinfo.hooks; i++) if (strcmp(hooklist->link[i].ourhook, hook) == 0) break; if (i == hooklist->nodeinfo.hooks) { free(resp); return (-1); } node = hooklist->link[i].nodeinfo.id; ret = 0; if (strcmp(hooklist->link[i].nodeinfo.type, "tee") == 0) { if (strcmp(hooklist->link[i].peerhook, "left") == 0) ret = ng_peer_hook_id(node, "right", peerhook); else if (strcmp(hooklist->link[i].peerhook, "right") == 0) ret = ng_peer_hook_id(node, "left", peerhook); else strcpy(peerhook, hooklist->link[i].peerhook); - } else + } else strcpy(peerhook, hooklist->link[i].peerhook); free(resp); return (ret); } /* - * Now the module is started. Select on the sockets, so that we can get + * Now the module is started. Select on the sockets, so that we can get * unsolicited input. */ static void ng_start(void) { if (snmp_node == 0) { if (NgMkSockNode(snmp_nodename, &csock, &dsock) < 0) { syslog(LOG_ERR, "NgMkSockNode: %m"); exit(1); } snmp_node = ng_node_id(".:"); } if ((csock_fd = fd_select(csock, csock_input, NULL, module)) == NULL) { syslog(LOG_ERR, "fd_select failed on csock: %m"); return; } if ((dsock_fd = fd_select(dsock, dsock_input, NULL, module)) == NULL) { syslog(LOG_ERR, "fd_select failed on dsock: %m"); return; } - reg_index = or_register(&oid_begemotNg, + reg_index = or_register(&oid_begemotNg, "The MIB for the NetGraph access module for SNMP.", module); } /* * Called, when the module is to be unloaded after it was successfully loaded */ static int ng_fini(void) { struct ngtype *t; while ((t = TAILQ_FIRST(&ngtype_list)) != NULL) { TAILQ_REMOVE(&ngtype_list, t, link); free(t); } if (csock_fd != NULL) fd_deselect(csock_fd); (void)close(csock); if (dsock_fd != NULL) fd_deselect(dsock_fd); (void)close(dsock); free(snmp_nodename); or_unregister(reg_index); return (0); } const struct snmp_module config = { "This module implements access to the netgraph sub-system", ng_init, ng_fini, ng_idle, NULL, NULL, ng_start, NULL, netgraph_ctree, netgraph_CTREE_SIZE, NULL }; int op_ng_config(struct snmp_context *ctx, struct snmp_value *value, u_int sub, u_int iidx __unused, enum snmp_op op) { asn_subid_t which = value->var.subs[sub - 1]; int ret; switch (op) { case SNMP_OP_GETNEXT: abort(); case SNMP_OP_GET: /* * Come here for GET, GETNEXT and COMMIT */ switch (which) { case LEAF_begemotNgControlNodeName: return (string_get(value, snmp_nodename, -1)); case LEAF_begemotNgResBufSiz: value->v.integer = resbufsiz; break; case LEAF_begemotNgTimeout: value->v.integer = timeout; break; case LEAF_begemotNgDebugLevel: value->v.uint32 = debug_level; break; default: abort(); } return (SNMP_ERR_NOERROR); case SNMP_OP_SET: switch (which) { case LEAF_begemotNgControlNodeName: /* only at initialisation */ if (community != COMM_INITIALIZE) return (SNMP_ERR_NOT_WRITEABLE); if (snmp_node != 0) return (SNMP_ERR_NOT_WRITEABLE); if ((ret = string_save(value, ctx, -1, &snmp_nodename)) != SNMP_ERR_NOERROR) return (ret); if (NgMkSockNode(snmp_nodename, &csock, &dsock) < 0) { syslog(LOG_ERR, "NgMkSockNode: %m"); string_rollback(ctx, &snmp_nodename); return (SNMP_ERR_GENERR); } snmp_node = ng_node_id(".:"); return (SNMP_ERR_NOERROR); case LEAF_begemotNgResBufSiz: ctx->scratch->int1 = resbufsiz; if (value->v.integer < 1024 || value->v.integer > 0x10000) return (SNMP_ERR_WRONG_VALUE); resbufsiz = value->v.integer; return (SNMP_ERR_NOERROR); case LEAF_begemotNgTimeout: ctx->scratch->int1 = timeout; if (value->v.integer < 10 || value->v.integer > 10000) return (SNMP_ERR_WRONG_VALUE); timeout = value->v.integer; return (SNMP_ERR_NOERROR); case LEAF_begemotNgDebugLevel: ctx->scratch->int1 = debug_level; debug_level = value->v.uint32; NgSetDebug(debug_level); return (SNMP_ERR_NOERROR); } abort(); case SNMP_OP_ROLLBACK: switch (which) { case LEAF_begemotNgControlNodeName: string_rollback(ctx, &snmp_nodename); close(csock); close(dsock); snmp_node = 0; return (SNMP_ERR_NOERROR); case LEAF_begemotNgResBufSiz: resbufsiz = ctx->scratch->int1; return (SNMP_ERR_NOERROR); case LEAF_begemotNgTimeout: timeout = ctx->scratch->int1; return (SNMP_ERR_NOERROR); case LEAF_begemotNgDebugLevel: debug_level = ctx->scratch->int1; NgSetDebug(debug_level); return (SNMP_ERR_NOERROR); } abort(); case SNMP_OP_COMMIT: switch (which) { case LEAF_begemotNgControlNodeName: string_commit(ctx); return (SNMP_ERR_NOERROR); case LEAF_begemotNgResBufSiz: case LEAF_begemotNgTimeout: case LEAF_begemotNgDebugLevel: return (SNMP_ERR_NOERROR); } abort(); } abort(); } int op_ng_stats(struct snmp_context *ctx __unused, struct snmp_value *value, u_int sub, u_int iidx __unused, enum snmp_op op) { switch (op) { case SNMP_OP_GETNEXT: abort(); case SNMP_OP_GET: value->v.uint32 = stats[value->var.subs[sub - 1] - 1]; return (SNMP_ERR_NOERROR); case SNMP_OP_SET: return (SNMP_ERR_NOT_WRITEABLE); case SNMP_OP_ROLLBACK: case SNMP_OP_COMMIT: abort(); } abort(); } /* * Netgraph type table */ static int fetch_types(void) { struct ngtype *t; struct typelist *typelist; struct ng_mesg *resp; u_int u, i; if (this_tick <= ngtype_tick) return (0); while ((t = TAILQ_FIRST(&ngtype_list)) != NULL) { TAILQ_REMOVE(&ngtype_list, t, link); free(t); } if ((resp = ng_dialog_id(snmp_node, NGM_GENERIC_COOKIE, NGM_LISTTYPES, NULL, 0)) == NULL) return (SNMP_ERR_GENERR); typelist = (struct typelist *)(void *)resp->data; for (u = 0; u < typelist->numtypes; u++) { if ((t = malloc(sizeof(*t))) == NULL) { free(resp); return (SNMP_ERR_GENERR); } strcpy(t->name, typelist->typeinfo[u].type_name); t->index.subs[0] = strlen(t->name); t->index.len = t->index.subs[0] + 1; for (i = 0; i < t->index.subs[0]; i++) t->index.subs[i + 1] = t->name[i]; INSERT_OBJECT_OID(t, &ngtype_list); } ngtype_tick = this_tick; free(resp); return (0); } /* * Try to load the netgraph type with the given name. We assume, that * type 'type' is implemented in the kernel module 'ng_type'. */ static int ngtype_load(const u_char *name, size_t namelen) { char *mod; int ret; if ((mod = malloc(namelen + 4)) == NULL) return (-1); strcpy(mod, "ng_"); strncpy(mod + 3, name, namelen); mod[namelen + 3] = '\0'; ret = kldload(mod); free(mod); return (ret); } /* * Unload a netgraph type. */ static int ngtype_unload(const u_char *name, size_t namelen) { char *mod; int id; if ((mod = malloc(namelen + 4)) == NULL) return (-1); strcpy(mod, "ng_"); strncpy(mod + 3, name, namelen); mod[namelen + 3] = '\0'; if ((id = kldfind(mod)) == -1) { free(mod); return (-1); } free(mod); return (kldunload(id)); } int op_ng_type(struct snmp_context *ctx, struct snmp_value *value, u_int sub, u_int iidx, enum snmp_op op) { asn_subid_t which = value->var.subs[sub - 1]; struct ngtype *t; u_char *name; size_t namelen; int status = 1; int ret; switch (op) { case SNMP_OP_GETNEXT: if ((ret = fetch_types()) != 0) return (ret); if ((t = NEXT_OBJECT_OID(&ngtype_list, &value->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); index_append(&value->var, sub, &t->index); break; case SNMP_OP_GET: if ((ret = fetch_types()) != 0) return (ret); if ((t = FIND_OBJECT_OID(&ngtype_list, &value->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_SET: if (index_decode(&value->var, sub, iidx, &name, &namelen)) return (SNMP_ERR_NO_CREATION); if (namelen == 0 || namelen >= NG_TYPESIZ) { free(name); return (SNMP_ERR_NO_CREATION); } if ((ret = fetch_types()) != 0) { free(name); return (ret); } t = FIND_OBJECT_OID(&ngtype_list, &value->var, sub); if (which != LEAF_begemotNgTypeStatus) { free(name); if (t != NULL) return (SNMP_ERR_NOT_WRITEABLE); return (SNMP_ERR_NO_CREATION); } if (!TRUTH_OK(value->v.integer)) { free(name); return (SNMP_ERR_WRONG_VALUE); } ctx->scratch->int1 = TRUTH_GET(value->v.integer); ctx->scratch->int1 |= (t != NULL) << 1; ctx->scratch->ptr2 = name; ctx->scratch->int2 = namelen; if (t == NULL) { /* type not loaded */ if (ctx->scratch->int1 & 1) { /* request to load */ if (ngtype_load(name, namelen) == -1) { free(name); if (errno == ENOENT) return (SNMP_ERR_INCONS_NAME); else return (SNMP_ERR_GENERR); } } } else { /* is type loaded */ if (!(ctx->scratch->int1 & 1)) { /* request to unload */ if (ngtype_unload(name, namelen) == -1) { free(name); return (SNMP_ERR_GENERR); } } } return (SNMP_ERR_NOERROR); case SNMP_OP_ROLLBACK: ret = SNMP_ERR_NOERROR; if (!(ctx->scratch->int1 & 2)) { /* did not exist */ if (ctx->scratch->int1 & 1) { /* request to load - unload */ if (ngtype_unload(ctx->scratch->ptr2, ctx->scratch->int2) == -1) ret = SNMP_ERR_UNDO_FAILED; } } else { /* did exist */ if (!(ctx->scratch->int1 & 1)) { /* request to unload - reload */ if (ngtype_load(ctx->scratch->ptr2, ctx->scratch->int2) == -1) ret = SNMP_ERR_UNDO_FAILED; } } free(ctx->scratch->ptr2); return (ret); case SNMP_OP_COMMIT: free(ctx->scratch->ptr2); return (SNMP_ERR_NOERROR); default: abort(); } /* * Come here for GET and COMMIT */ switch (which) { case LEAF_begemotNgTypeStatus: value->v.integer = status; break; default: abort(); } return (SNMP_ERR_NOERROR); } /* * Implement the node table */ static int find_node(const struct asn_oid *oid, u_int sub, struct nodeinfo *info) { ng_ID_t id = oid->subs[sub]; struct ng_mesg *resp; if ((resp = ng_dialog_id(id, NGM_GENERIC_COOKIE, NGM_NODEINFO, NULL, 0)) == NULL) return (-1); *info = *(struct nodeinfo *)(void *)resp->data; free(resp); return (0); } static int ncmp(const void *p1, const void *p2) { const struct nodeinfo *i1 = p1; const struct nodeinfo *i2 = p2; if (i1->id < i2->id) return (-1); if (i1->id > i2->id) return (+1); return (0); } static int find_node_next(const struct asn_oid *oid, u_int sub, struct nodeinfo *info) { u_int idxlen = oid->len - sub; struct ng_mesg *resp; struct namelist *list; ng_ID_t id; u_int i; if ((resp = ng_dialog_id(snmp_node, NGM_GENERIC_COOKIE, NGM_LISTNODES, NULL, 0)) == NULL) return (-1); list = (struct namelist *)(void *)resp->data; qsort(list->nodeinfo, list->numnames, sizeof(list->nodeinfo[0]), ncmp); if (idxlen == 0) { if (list->numnames == 0) { free(resp); return (-1); } *info = list->nodeinfo[0]; free(resp); return (0); } id = oid->subs[sub]; for (i = 0; i < list->numnames; i++) if (list->nodeinfo[i].id > id) { *info = list->nodeinfo[i]; free(resp); return (0); } free(resp); return (-1); } int op_ng_node(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]; u_int idxlen = value->var.len - sub; struct nodeinfo nodeinfo; switch (op) { case SNMP_OP_GETNEXT: if (find_node_next(&value->var, sub, &nodeinfo) == -1) return (SNMP_ERR_NOSUCHNAME); value->var.len = sub + 1; value->var.subs[sub] = nodeinfo.id; break; case SNMP_OP_GET: if (idxlen != 1) return (SNMP_ERR_NOSUCHNAME); if (find_node(&value->var, sub, &nodeinfo) == -1) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_SET: if (idxlen != 1) return (SNMP_ERR_NO_CREATION); if (find_node(&value->var, sub, &nodeinfo) == -1) return (SNMP_ERR_NO_CREATION); return (SNMP_ERR_NOT_WRITEABLE); case SNMP_OP_ROLLBACK: case SNMP_OP_COMMIT: default: abort(); } /* * Come here for GET and COMMIT */ switch (which) { case LEAF_begemotNgNodeStatus: value->v.integer = 1; break; case LEAF_begemotNgNodeName: return (string_get(value, nodeinfo.name, -1)); case LEAF_begemotNgNodeType: return (string_get(value, nodeinfo.type, -1)); case LEAF_begemotNgNodeHooks: value->v.uint32 = nodeinfo.hooks; break; default: abort(); } return (SNMP_ERR_NOERROR); } /* * Implement the hook table */ static int find_hook(int32_t id, const u_char *hook, size_t hooklen, struct linkinfo *info) { struct ng_mesg *resp; struct hooklist *list; u_int i; if ((resp = ng_dialog_id(id, NGM_GENERIC_COOKIE, NGM_LISTHOOKS, NULL, 0)) == NULL) return (-1); list = (struct hooklist *)(void *)resp->data; for (i = 0; i < list->nodeinfo.hooks; i++) { if (strlen(list->link[i].ourhook) == hooklen && strncmp(list->link[i].ourhook, hook, hooklen) == 0) { *info = list->link[i]; free(resp); return (0); } } free(resp); return (-1); } static int hook_cmp(const void *p1, const void *p2) { const struct linkinfo *i1 = p1; const struct linkinfo *i2 = p2; if (strlen(i1->ourhook) < strlen(i2->ourhook)) return (-1); if (strlen(i1->ourhook) > strlen(i2->ourhook)) return (+1); return (strcmp(i1->ourhook, i2->ourhook)); } static int find_hook_next(const struct asn_oid *oid, u_int sub, struct nodeinfo *nodeinfo, struct linkinfo *linkinfo) { u_int idxlen = oid->len - sub; struct namelist *list; struct ng_mesg *resp; struct hooklist *hooks; struct ng_mesg *resp1; u_int node_index; struct asn_oid idx; u_int i, j; /* * Get and sort Node list */ if ((resp = ng_dialog_id(snmp_node, NGM_GENERIC_COOKIE, NGM_LISTNODES, NULL, 0)) == NULL) return (-1); list = (struct namelist *)(void *)resp->data; qsort(list->nodeinfo, list->numnames, sizeof(list->nodeinfo[0]), ncmp); /* * If we have no index, take the first node and return the * first hook. */ if (idxlen == 0) { node_index = 0; goto return_first_hook; } /* * Locate node */ for (node_index = 0; node_index < list->numnames; node_index++) if (list->nodeinfo[node_index].id >= oid->subs[sub]) break; /* * If we have only the node part of the index take, or * there is no node with that Id, take the first hook of that node. */ if (idxlen == 1 || node_index >= list->numnames || list->nodeinfo[node_index].id > oid->subs[sub]) goto return_first_hook; /* * We had an exact match on the node id and have (at last part) * of the hook name index. Loop through the hooks of the node * and find the next one. */ if ((resp1 = ng_dialog_id(list->nodeinfo[node_index].id, NGM_GENERIC_COOKIE, NGM_LISTHOOKS, NULL, 0)) == NULL) { free(resp); return (-1); } hooks = (struct hooklist *)(void *)resp1->data; if (hooks->nodeinfo.hooks > 0) { qsort(hooks->link, hooks->nodeinfo.hooks, sizeof(hooks->link[0]), hook_cmp); for (i = 0; i < hooks->nodeinfo.hooks; i++) { idx.len = strlen(hooks->link[i].ourhook) + 1; idx.subs[0] = idx.len - 1; for (j = 0; j < idx.len; j++) idx.subs[j + 1] = hooks->link[i].ourhook[j]; if (index_compare(oid, sub + 1, &idx) < 0) break; } if (i < hooks->nodeinfo.hooks) { *nodeinfo = hooks->nodeinfo; *linkinfo = hooks->link[i]; free(resp); free(resp1); return (0); } } /* no hook found larger than the index on the index node - take * first hook of next node */ free(resp1); node_index++; return_first_hook: while (node_index < list->numnames) { if ((resp1 = ng_dialog_id(list->nodeinfo[node_index].id, NGM_GENERIC_COOKIE, NGM_LISTHOOKS, NULL, 0)) == NULL) break; hooks = (struct hooklist *)(void *)resp1->data; if (hooks->nodeinfo.hooks > 0) { qsort(hooks->link, hooks->nodeinfo.hooks, sizeof(hooks->link[0]), hook_cmp); *nodeinfo = hooks->nodeinfo; *linkinfo = hooks->link[0]; free(resp); free(resp1); return (0); } /* if we don't have hooks, try next node */ free(resp1); node_index++; } free(resp); return (-1); } int op_ng_hook(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]; struct linkinfo linkinfo; struct nodeinfo nodeinfo; u_int32_t lid; u_char *hook; size_t hooklen; u_int i; switch (op) { case SNMP_OP_GETNEXT: if (find_hook_next(&value->var, sub, &nodeinfo, &linkinfo) == -1) return (SNMP_ERR_NOSUCHNAME); value->var.len = sub + 1 + 1 + strlen(linkinfo.ourhook); value->var.subs[sub] = nodeinfo.id; value->var.subs[sub + 1] = strlen(linkinfo.ourhook); for (i = 0; i < strlen(linkinfo.ourhook); i++) value->var.subs[sub + i + 2] = linkinfo.ourhook[i]; break; case SNMP_OP_GET: if (index_decode(&value->var, sub, iidx, &lid, &hook, &hooklen)) return (SNMP_ERR_NOSUCHNAME); if (find_hook(lid, hook, hooklen, &linkinfo) == -1) { free(hook); return (SNMP_ERR_NOSUCHNAME); } free(hook); break; case SNMP_OP_SET: if (index_decode(&value->var, sub, iidx, &lid, &hook, &hooklen)) return (SNMP_ERR_NO_CREATION); if (find_hook(lid, hook, hooklen, &linkinfo) == -1) { free(hook); return (SNMP_ERR_NO_CREATION); } free(hook); return (SNMP_ERR_NOT_WRITEABLE); case SNMP_OP_ROLLBACK: case SNMP_OP_COMMIT: default: abort(); } switch (which) { case LEAF_begemotNgHookStatus: value->v.integer = 1; break; case LEAF_begemotNgHookPeerNodeId: value->v.uint32 = linkinfo.nodeinfo.id; break; case LEAF_begemotNgHookPeerHook: return (string_get(value, linkinfo.peerhook, -1)); case LEAF_begemotNgHookPeerType: return (string_get(value, linkinfo.nodeinfo.type, -1)); default: abort(); } return (SNMP_ERR_NOERROR); } Index: head/usr.sbin/bsnmpd/modules/snmp_pf/BEGEMOT-PF-MIB.txt =================================================================== --- head/usr.sbin/bsnmpd/modules/snmp_pf/BEGEMOT-PF-MIB.txt (revision 310647) +++ head/usr.sbin/bsnmpd/modules/snmp_pf/BEGEMOT-PF-MIB.txt (revision 310648) @@ -1,1347 +1,1347 @@ --- +-- -- ---------------------------------------------------------------------------- -- "THE BEER-WARE LICENSE" (Revision 42): -- wrote this file. As long as you retain this notice you -- can do whatever you want with this stuff. If we meet some day, and you think -- this stuff is worth it, you can buy me a beer in return. -Philip Paeps -- ---------------------------------------------------------------------------- -- -- $FreeBSD$ --- +-- BEGEMOT-PF-MIB DEFINITIONS ::= BEGIN IMPORTS MODULE-IDENTITY, OBJECT-TYPE, Counter64, Integer32, TimeTicks, Unsigned32 FROM SNMPv2-SMI TruthValue FROM SNMPv2-TC InetAddress, InetAddressType, InetAddressPrefixLength FROM INET-ADDRESS-MIB begemot FROM BEGEMOT-MIB; begemotPf MODULE-IDENTITY LAST-UPDATED "201003180000Z" ORGANIZATION "NixSys BVBA" CONTACT-INFO " Philip Paeps Postal: NixSys BVBA Louizastraat 14 BE-2800 Mechelen Belgium E-Mail: philip@FreeBSD.org" DESCRIPTION "The Begemot MIB for the pf packet filter." REVISION "201003180000Z" DESCRIPTION "Modified pfTablesAddrEntry to support IPv6 addresses - added pfTablesAddrNetType column and modified type of pfTablesAddrNet to InetAddress." REVISION "200912050000Z" DESCRIPTION "Added support for retrieving counters of labeled pf filter rules via pfLabelspfLabels subtree." REVISION "200501240000Z" DESCRIPTION "Initial revision." ::= { begemot 200 } begemotPfObjects OBJECT IDENTIFIER ::= { begemotPf 1 } -- -------------------------------------------------------------------------- pfStatus OBJECT IDENTIFIER ::= { begemotPfObjects 1 } pfCounter OBJECT IDENTIFIER ::= { begemotPfObjects 2 } pfStateTable OBJECT IDENTIFIER ::= { begemotPfObjects 3 } pfSrcNodes OBJECT IDENTIFIER ::= { begemotPfObjects 4 } pfLimits OBJECT IDENTIFIER ::= { begemotPfObjects 5 } pfTimeouts OBJECT IDENTIFIER ::= { begemotPfObjects 6 } pfLogInterface OBJECT IDENTIFIER ::= { begemotPfObjects 7 } pfInterfaces OBJECT IDENTIFIER ::= { begemotPfObjects 8 } pfTables OBJECT IDENTIFIER ::= { begemotPfObjects 9 } pfAltq OBJECT IDENTIFIER ::= { begemotPfObjects 10 } pfLabels OBJECT IDENTIFIER ::= { begemotPfObjects 11 } -- -------------------------------------------------------------------------- -- -- status information -- pfStatusRunning OBJECT-TYPE SYNTAX TruthValue MAX-ACCESS read-only STATUS current DESCRIPTION "True if pf is currently enabled." ::= { pfStatus 1 } pfStatusRuntime OBJECT-TYPE SYNTAX TimeTicks UNITS "1/100th of a Second" MAX-ACCESS read-only STATUS current DESCRIPTION "Indicates how long pf has been enabled. If pf is not currently enabled, indicates how long it has been disabled. If pf has not been enabled or disabled since the system was started, the value will be 0." ::= { pfStatus 2 } pfStatusDebug OBJECT-TYPE SYNTAX INTEGER { none(0), urgent(1), misc(2), loud(3) } MAX-ACCESS read-only STATUS current DESCRIPTION "Indicates the debug level at which pf is running." ::= { pfStatus 3 } pfStatusHostId OBJECT-TYPE SYNTAX OCTET STRING MAX-ACCESS read-only STATUS current DESCRIPTION "The (unique) host identifier of the machine running pf." ::= { pfStatus 4 } -- -------------------------------------------------------------------------- -- -- counters -- pfCounterMatch OBJECT-TYPE SYNTAX Counter64 MAX-ACCESS read-only STATUS current DESCRIPTION "Number of packets that matched a filter rule." ::= { pfCounter 1 } pfCounterBadOffset OBJECT-TYPE SYNTAX Counter64 MAX-ACCESS read-only STATUS current DESCRIPTION "Number of packets with bad offset." ::= { pfCounter 2 } pfCounterFragment OBJECT-TYPE SYNTAX Counter64 MAX-ACCESS read-only STATUS current DESCRIPTION "Number of fragmented packets." ::= { pfCounter 3 } pfCounterShort OBJECT-TYPE SYNTAX Counter64 MAX-ACCESS read-only STATUS current DESCRIPTION "Number of short packets." ::= { pfCounter 4 } pfCounterNormalize OBJECT-TYPE SYNTAX Counter64 MAX-ACCESS read-only STATUS current DESCRIPTION "Number of normalized packets." ::= { pfCounter 5 } pfCounterMemDrop OBJECT-TYPE SYNTAX Counter64 MAX-ACCESS read-only STATUS current DESCRIPTION "Number of packets dropped due to memory limitations." ::= { pfCounter 6 } -- -------------------------------------------------------------------------- -- -- state table -- pfStateTableCount OBJECT-TYPE SYNTAX Unsigned32 MAX-ACCESS read-only STATUS current DESCRIPTION "Number of entries in the state table." ::= { pfStateTable 1 } pfStateTableSearches OBJECT-TYPE SYNTAX Counter64 MAX-ACCESS read-only STATUS current DESCRIPTION "Number of searches against the state table." ::= { pfStateTable 2 } pfStateTableInserts OBJECT-TYPE SYNTAX Counter64 MAX-ACCESS read-only STATUS current DESCRIPTION "Number of entries inserted into the state table." ::= { pfStateTable 3 } pfStateTableRemovals OBJECT-TYPE SYNTAX Counter64 MAX-ACCESS read-only STATUS current DESCRIPTION "Number of entries removed from the state table." ::= { pfStateTable 4 } -- -------------------------------------------------------------------------- -- -- source nodes -- pfSrcNodesCount OBJECT-TYPE SYNTAX Unsigned32 MAX-ACCESS read-only STATUS current DESCRIPTION "Number of entries in the source tracking table." ::= { pfSrcNodes 1 } pfSrcNodesSearches OBJECT-TYPE SYNTAX Counter64 MAX-ACCESS read-only STATUS current DESCRIPTION "Number of searches against the source tracking table." ::= { pfSrcNodes 2 } pfSrcNodesInserts OBJECT-TYPE SYNTAX Counter64 MAX-ACCESS read-only STATUS current DESCRIPTION "Number of entries inserted into the source tracking table." ::= { pfSrcNodes 3 } pfSrcNodesRemovals OBJECT-TYPE SYNTAX Counter64 MAX-ACCESS read-only STATUS current DESCRIPTION "Number of entries removed from the source tracking table." ::= { pfSrcNodes 4 } -- -------------------------------------------------------------------------- -- -- limits -- pfLimitsStates OBJECT-TYPE SYNTAX Unsigned32 MAX-ACCESS read-only STATUS current DESCRIPTION "Maximum number of 'keep state' rules in the ruleset." ::= { pfLimits 1 } pfLimitsSrcNodes OBJECT-TYPE SYNTAX Unsigned32 MAX-ACCESS read-only STATUS current DESCRIPTION "Maximum number of 'sticky-address' or 'source-track' rules in the ruleset." ::= { pfLimits 2 } pfLimitsFrags OBJECT-TYPE SYNTAX Unsigned32 MAX-ACCESS read-only STATUS current DESCRIPTION "Maximum number of 'scrub' rules in the ruleset." ::= { pfLimits 3 } -- -------------------------------------------------------------------------- -- -- timeouts -- pfTimeoutsTcpFirst OBJECT-TYPE SYNTAX Integer32 MAX-ACCESS read-only STATUS current DESCRIPTION "State after the first packet in a connection." ::= { pfTimeouts 1 } pfTimeoutsTcpOpening OBJECT-TYPE SYNTAX Integer32 MAX-ACCESS read-only STATUS current DESCRIPTION "State before the destination host ever sends a packet." ::= { pfTimeouts 2 } pfTimeoutsTcpEstablished OBJECT-TYPE SYNTAX Integer32 MAX-ACCESS read-only STATUS current DESCRIPTION "The fully established state." ::= { pfTimeouts 3 } pfTimeoutsTcpClosing OBJECT-TYPE SYNTAX Integer32 MAX-ACCESS read-only STATUS current DESCRIPTION "State after the first FIN has been sent." ::= { pfTimeouts 4 } pfTimeoutsTcpFinWait OBJECT-TYPE SYNTAX Integer32 MAX-ACCESS read-only STATUS current DESCRIPTION "State after both FINs have been exchanged and the connection is closed." ::= { pfTimeouts 5 } pfTimeoutsTcpClosed OBJECT-TYPE SYNTAX Integer32 MAX-ACCESS read-only STATUS current DESCRIPTION "State after one endpoint sends an RST." ::= { pfTimeouts 6 } pfTimeoutsUdpFirst OBJECT-TYPE SYNTAX Integer32 MAX-ACCESS read-only STATUS current DESCRIPTION "State after the first packet." ::= { pfTimeouts 7 } pfTimeoutsUdpSingle OBJECT-TYPE SYNTAX Integer32 MAX-ACCESS read-only STATUS current DESCRIPTION "State if the source host sends more than one packet but the destination host has never sent one back." ::= { pfTimeouts 8 } pfTimeoutsUdpMultiple OBJECT-TYPE SYNTAX Integer32 MAX-ACCESS read-only STATUS current DESCRIPTION "State if both hosts have sent packets." ::= { pfTimeouts 9 } pfTimeoutsIcmpFirst OBJECT-TYPE SYNTAX Integer32 MAX-ACCESS read-only STATUS current DESCRIPTION "State after the first packet." ::= { pfTimeouts 10 } pfTimeoutsIcmpError OBJECT-TYPE SYNTAX Integer32 MAX-ACCESS read-only STATUS current DESCRIPTION "State after an ICMP error came back in response to an ICMP packet." ::= { pfTimeouts 11 } pfTimeoutsOtherFirst OBJECT-TYPE SYNTAX Integer32 MAX-ACCESS read-only STATUS current DESCRIPTION "State after the first packet." ::= { pfTimeouts 12 } pfTimeoutsOtherSingle OBJECT-TYPE SYNTAX Integer32 MAX-ACCESS read-only STATUS current DESCRIPTION "State if the source host sends more than one packet but the destination host has never sent one back." ::= { pfTimeouts 13 } pfTimeoutsOtherMultiple OBJECT-TYPE SYNTAX Integer32 MAX-ACCESS read-only STATUS current DESCRIPTION "State if both hosts have sent packets." ::= { pfTimeouts 14 } pfTimeoutsFragment OBJECT-TYPE SYNTAX Integer32 MAX-ACCESS read-only STATUS current DESCRIPTION "Seconds before an unassembled fragment is expired." ::= { pfTimeouts 15 } pfTimeoutsInterval OBJECT-TYPE SYNTAX Integer32 MAX-ACCESS read-only STATUS current DESCRIPTION "Interval between purging expired states and fragments." ::= { pfTimeouts 16 } pfTimeoutsAdaptiveStart OBJECT-TYPE SYNTAX Integer32 MAX-ACCESS read-only STATUS current DESCRIPTION "When the number of state entries exceeds this value, adaptive scaling begins." ::= { pfTimeouts 17 } pfTimeoutsAdaptiveEnd OBJECT-TYPE SYNTAX Integer32 MAX-ACCESS read-only STATUS current DESCRIPTION "When reaching this number of state entries, all timeout values become zero, effectively purging all state entries immediately." ::= { pfTimeouts 18 } pfTimeoutsSrcNode OBJECT-TYPE SYNTAX Integer32 MAX-ACCESS read-only STATUS current DESCRIPTION "Length of time to retain a source tracking entry after the last state expires." ::= { pfTimeouts 19 } -- -------------------------------------------------------------------------- -- -- log interface -- pfLogInterfaceName OBJECT-TYPE SYNTAX OCTET STRING MAX-ACCESS read-only STATUS current DESCRIPTION "The name of the interface configured with 'set loginterface'. If no interface has been configured, the object will be empty." ::= { pfLogInterface 1 } pfLogInterfaceIp4BytesIn OBJECT-TYPE SYNTAX Counter64 MAX-ACCESS read-only STATUS current DESCRIPTION "Number of IPv4 bytes passed in on the loginterface." ::= { pfLogInterface 2 } pfLogInterfaceIp4BytesOut OBJECT-TYPE SYNTAX Counter64 MAX-ACCESS read-only STATUS current DESCRIPTION "Number of IPv4 bytes passed out on the loginterface." ::= { pfLogInterface 3 } pfLogInterfaceIp4PktsInPass OBJECT-TYPE SYNTAX Counter64 MAX-ACCESS read-only STATUS current DESCRIPTION "Number of IPv4 packets passed in on the loginterface." ::= { pfLogInterface 4 } pfLogInterfaceIp4PktsInDrop OBJECT-TYPE SYNTAX Counter64 MAX-ACCESS read-only STATUS current DESCRIPTION "Number of IPv4 packets dropped coming in on the loginterface." ::= { pfLogInterface 5 } pfLogInterfaceIp4PktsOutPass OBJECT-TYPE SYNTAX Counter64 MAX-ACCESS read-only STATUS current DESCRIPTION "Number of IPv4 packets passed out on the loginterface." ::= { pfLogInterface 6 } pfLogInterfaceIp4PktsOutDrop OBJECT-TYPE SYNTAX Counter64 MAX-ACCESS read-only STATUS current DESCRIPTION "Number of IPv4 packets dropped going out on the loginterface." ::= { pfLogInterface 7 } pfLogInterfaceIp6BytesIn OBJECT-TYPE SYNTAX Counter64 MAX-ACCESS read-only STATUS current DESCRIPTION "Number of IPv6 bytes passed in on the loginterface." ::= { pfLogInterface 8 } pfLogInterfaceIp6BytesOut OBJECT-TYPE SYNTAX Counter64 MAX-ACCESS read-only STATUS current DESCRIPTION "Number of IPv6 bytes passed out on the loginterface." ::= { pfLogInterface 9 } pfLogInterfaceIp6PktsInPass OBJECT-TYPE SYNTAX Counter64 MAX-ACCESS read-only STATUS current DESCRIPTION "Number of IPv6 packets passed in on the loginterface." ::= { pfLogInterface 10 } pfLogInterfaceIp6PktsInDrop OBJECT-TYPE SYNTAX Counter64 MAX-ACCESS read-only STATUS current DESCRIPTION "Number of IPv6 packets dropped coming in on the loginterface." ::= { pfLogInterface 11 } pfLogInterfaceIp6PktsOutPass OBJECT-TYPE SYNTAX Counter64 MAX-ACCESS read-only STATUS current DESCRIPTION "Number of IPv6 packets passed out on the loginterface." ::= { pfLogInterface 12 } pfLogInterfaceIp6PktsOutDrop OBJECT-TYPE SYNTAX Counter64 MAX-ACCESS read-only STATUS current DESCRIPTION "Number of IPv6 packets dropped going out on the loginterface." ::= { pfLogInterface 13 } -- -------------------------------------------------------------------------- -- -- interfaces -- pfInterfacesIfNumber OBJECT-TYPE SYNTAX Integer32 MAX-ACCESS read-only STATUS current DESCRIPTION "The number of network interfaces on this system." ::= { pfInterfaces 1 } pfInterfacesIfTable OBJECT-TYPE SYNTAX SEQUENCE OF PfInterfacesIfEntry MAX-ACCESS not-accessible STATUS current DESCRIPTION "Table of network interfaces, indexed on pfInterfacesIfNumber." ::= { pfInterfaces 2 } pfInterfacesIfEntry OBJECT-TYPE SYNTAX PfInterfacesIfEntry MAX-ACCESS not-accessible STATUS current DESCRIPTION "An entry in the pfInterfacesIfTable containing information about a particular network interface in the machine." INDEX { pfInterfacesIfIndex } ::= { pfInterfacesIfTable 1 } PfInterfacesIfEntry ::= SEQUENCE { pfInterfacesIfIndex Integer32, pfInterfacesIfDescr OCTET STRING, pfInterfacesIfType INTEGER, pfInterfacesIfTZero TimeTicks, pfInterfacesIfRefsState Null, pfInterfacesIfRefsRule Unsigned32, pfInterfacesIf4BytesInPass Counter64, pfInterfacesIf4BytesInBlock Counter64, pfInterfacesIf4BytesOutPass Counter64, pfInterfacesIf4BytesOutBlock Counter64, pfInterfacesIf4PktsInPass Counter64, pfInterfacesIf4PktsInBlock Counter64, pfInterfacesIf4PktsOutPass Counter64, pfInterfacesIf4PktsOutBlock Counter64, pfInterfacesIf6BytesInPass Counter64, pfInterfacesIf6BytesInBlock Counter64, pfInterfacesIf6BytesOutPass Counter64, pfInterfacesIf6BytesOutBlock Counter64, pfInterfacesIf6PktsInPass Counter64, pfInterfacesIf6PktsInBlock Counter64, pfInterfacesIf6PktsOutPass Counter64, pfInterfacesIf6PktsOutBlock Counter64 } pfInterfacesIfIndex OBJECT-TYPE SYNTAX Integer32 (1..2147483647) MAX-ACCESS not-accessible STATUS current DESCRIPTION "A unique value, greater than zero, for each interface." ::= { pfInterfacesIfEntry 1 } - + pfInterfacesIfDescr OBJECT-TYPE SYNTAX OCTET STRING MAX-ACCESS read-only STATUS current DESCRIPTION "The name of the interface." ::= { pfInterfacesIfEntry 2 } pfInterfacesIfType OBJECT-TYPE SYNTAX INTEGER { group(0), instance(1), detached(2) } MAX-ACCESS read-only STATUS current DESCRIPTION "Indicates whether the interface is a group inteface, an interface instance, or whether it has been removed or destroyed." ::= { pfInterfacesIfEntry 3 } pfInterfacesIfTZero OBJECT-TYPE SYNTAX TimeTicks UNITS "1/100th of a Second" MAX-ACCESS read-only STATUS current DESCRIPTION "Time since statistics were last reset or since the interface was loaded." ::= { pfInterfacesIfEntry 4 } pfInterfacesIfRefsState OBJECT-TYPE SYNTAX Unsigned32 MAX-ACCESS read-only STATUS current DESCRIPTION "The number of state and/or source track entries referencing this interface." ::= { pfInterfacesIfEntry 5 } pfInterfacesIfRefsRule OBJECT-TYPE SYNTAX Unsigned32 MAX-ACCESS read-only STATUS current DESCRIPTION "The number of rules referencing this interface." ::= { pfInterfacesIfEntry 6 } pfInterfacesIf4BytesInPass OBJECT-TYPE SYNTAX Counter64 MAX-ACCESS read-only STATUS current DESCRIPTION "The number of IPv4 bytes passed coming in on this interface." ::= { pfInterfacesIfEntry 7 } pfInterfacesIf4BytesInBlock OBJECT-TYPE SYNTAX Counter64 MAX-ACCESS read-only STATUS current DESCRIPTION "The number of IPv4 bytes blocked coming in on this interface." ::= { pfInterfacesIfEntry 8 } pfInterfacesIf4BytesOutPass OBJECT-TYPE SYNTAX Counter64 MAX-ACCESS read-only STATUS current DESCRIPTION "The number of IPv4 bytes passed going out on this interface." ::= { pfInterfacesIfEntry 9 } pfInterfacesIf4BytesOutBlock OBJECT-TYPE SYNTAX Counter64 MAX-ACCESS read-only STATUS current DESCRIPTION "The number of IPv4 bytes blocked going out on this interface." ::= { pfInterfacesIfEntry 10 } pfInterfacesIf4PktsInPass OBJECT-TYPE SYNTAX Counter64 MAX-ACCESS read-only STATUS current DESCRIPTION "The number of IPv4 packets passed coming in on this interface." ::= { pfInterfacesIfEntry 11 } pfInterfacesIf4PktsInBlock OBJECT-TYPE SYNTAX Counter64 MAX-ACCESS read-only STATUS current DESCRIPTION "The number of IPv4 packets blocked coming in on this interface." ::= { pfInterfacesIfEntry 12 } pfInterfacesIf4PktsOutPass OBJECT-TYPE SYNTAX Counter64 MAX-ACCESS read-only STATUS current DESCRIPTION "The number of IPv4 packets passed going out on this interface." ::= { pfInterfacesIfEntry 13 } pfInterfacesIf4PktsOutBlock OBJECT-TYPE SYNTAX Counter64 MAX-ACCESS read-only STATUS current DESCRIPTION "The number of IPv4 packets blocked going out on this interface." ::= { pfInterfacesIfEntry 14 } pfInterfacesIf6BytesInPass OBJECT-TYPE SYNTAX Counter64 MAX-ACCESS read-only STATUS current DESCRIPTION "The number of IPv6 bytes passed coming in on this interface." ::= { pfInterfacesIfEntry 15 } pfInterfacesIf6BytesInBlock OBJECT-TYPE SYNTAX Counter64 MAX-ACCESS read-only STATUS current DESCRIPTION "The number of IPv6 bytes blocked coming in on this interface." ::= { pfInterfacesIfEntry 16 } pfInterfacesIf6BytesOutPass OBJECT-TYPE SYNTAX Counter64 MAX-ACCESS read-only STATUS current DESCRIPTION "The number of IPv6 bytes passed going out on this interface." ::= { pfInterfacesIfEntry 17 } pfInterfacesIf6BytesOutBlock OBJECT-TYPE SYNTAX Counter64 MAX-ACCESS read-only STATUS current DESCRIPTION "The number of IPv6 bytes blocked going out on this interface." ::= { pfInterfacesIfEntry 18 } pfInterfacesIf6PktsInPass OBJECT-TYPE SYNTAX Counter64 MAX-ACCESS read-only STATUS current DESCRIPTION "The number of IPv6 packets passed coming in on this interface." ::= { pfInterfacesIfEntry 19 } pfInterfacesIf6PktsInBlock OBJECT-TYPE SYNTAX Counter64 MAX-ACCESS read-only STATUS current DESCRIPTION "The number of IPv6 packets blocked coming in on this interface." ::= { pfInterfacesIfEntry 20 } pfInterfacesIf6PktsOutPass OBJECT-TYPE SYNTAX Counter64 MAX-ACCESS read-only STATUS current DESCRIPTION "The number of IPv6 packets passed going out on this interface." ::= { pfInterfacesIfEntry 21 } pfInterfacesIf6PktsOutBlock OBJECT-TYPE SYNTAX Counter64 MAX-ACCESS read-only STATUS current DESCRIPTION "The number of IPv6 packets blocked going out on this interface." ::= { pfInterfacesIfEntry 22 } -- -------------------------------------------------------------------------- -- -- tables -- pfTablesTblNumber OBJECT-TYPE SYNTAX Integer32 MAX-ACCESS read-only STATUS current DESCRIPTION "The number of tables on this system." ::= { pfTables 1 } pfTablesTblTable OBJECT-TYPE SYNTAX SEQUENCE OF PfTablesTblEntry MAX-ACCESS not-accessible STATUS current DESCRIPTION "Table of tables, index on pfTablesTblIndex." ::= { pfTables 2 } pfTablesTblEntry OBJECT-TYPE SYNTAX PfTablesTblEntry MAX-ACCESS not-accessible STATUS current DESCRIPTION "Any entry in the pfTablesTblTable containing information about a particular table on the system." INDEX { pfTablesTblIndex } ::= { pfTablesTblTable 1 } PfTablesTblEntry ::= SEQUENCE { pfTablesTblIndex Integer32, pfTablesTblDescr OCTET STRING, pfTablesTblCount Integer32, pfTablesTblTZero TimeTicks, pfTablesTblRefsAnchor Integer32, pfTablesTblRefsRule Integer32, pfTablesTblEvalMatch Counter64, pfTablesTblEvalNoMatch Counter64, pfTablesTblBytesInPass Counter64, pfTablesTblBytesInBlock Counter64, pfTablesTblBytesInXPass Counter64, pfTablesTblBytesOutPass Counter64, pfTablesTblBytesOutBlock Counter64, pfTablesTblBytesOutXPass Counter64, pfTablesTblPktsInPass Counter64, pfTablesTblPktsInBlock Counter64, pfTablesTblPktsInXPass Counter64, pfTablesTblPktsOutPass Counter64, pfTablesTblPktsOutBlock Counter64, pfTablesTblPktsOutXPass Counter64 } pfTablesTblIndex OBJECT-TYPE SYNTAX Integer32 (1..2147483647) MAX-ACCESS not-accessible STATUS current DESCRIPTION "A unique value, greater than zero, for each table." ::= { pfTablesTblEntry 1 } pfTablesTblDescr OBJECT-TYPE SYNTAX OCTET STRING MAX-ACCESS read-only STATUS current DESCRIPTION "The name of the table." ::= { pfTablesTblEntry 2 } pfTablesTblCount OBJECT-TYPE SYNTAX Integer32 MAX-ACCESS read-only STATUS current DESCRIPTION "The number of addresses in the table." ::= { pfTablesTblEntry 3 } pfTablesTblTZero OBJECT-TYPE SYNTAX TimeTicks UNITS "1/100th of a Second" MAX-ACCESS read-only STATUS current DESCRIPTION "The time passed since the statistics of this table were last cleared or the time since this table was loaded, whichever is sooner." ::= { pfTablesTblEntry 4 } - + pfTablesTblRefsAnchor OBJECT-TYPE SYNTAX Integer32 MAX-ACCESS read-only STATUS current DESCRIPTION "The number of anchors referencing this table." ::= { pfTablesTblEntry 5 } pfTablesTblRefsRule OBJECT-TYPE SYNTAX Integer32 MAX-ACCESS read-only STATUS current DESCRIPTION "The number of rules referencing this table." ::= { pfTablesTblEntry 6 } pfTablesTblEvalMatch OBJECT-TYPE SYNTAX Counter64 MAX-ACCESS read-only STATUS current DESCRIPTION "The number of evaluations returning a match." ::= { pfTablesTblEntry 7 } pfTablesTblEvalNoMatch OBJECT-TYPE SYNTAX Counter64 MAX-ACCESS read-only STATUS current DESCRIPTION "The number of evaluations not returning a match." ::= { pfTablesTblEntry 8 } pfTablesTblBytesInPass OBJECT-TYPE SYNTAX Counter64 MAX-ACCESS read-only STATUS current DESCRIPTION "The number of bytes passed in matching the table." ::= { pfTablesTblEntry 9 } pfTablesTblBytesInBlock OBJECT-TYPE SYNTAX Counter64 MAX-ACCESS read-only STATUS current DESCRIPTION "The number of bytes blocked coming in matching the table." ::= { pfTablesTblEntry 10 } pfTablesTblBytesInXPass OBJECT-TYPE SYNTAX Counter64 MAX-ACCESS read-only STATUS current DESCRIPTION "The number of bytes statefully passed in where the state entry refers to the table, but the table no longer contains the address in question." ::= { pfTablesTblEntry 11 } pfTablesTblBytesOutPass OBJECT-TYPE SYNTAX Counter64 MAX-ACCESS read-only STATUS current DESCRIPTION "The number of bytes passed out matching the table." ::= { pfTablesTblEntry 12 } pfTablesTblBytesOutBlock OBJECT-TYPE SYNTAX Counter64 MAX-ACCESS read-only STATUS current DESCRIPTION "The number of bytes blocked going out matching the table." ::= { pfTablesTblEntry 13 } pfTablesTblBytesOutXPass OBJECT-TYPE SYNTAX Counter64 MAX-ACCESS read-only STATUS current DESCRIPTION "The number of bytes statefully passed out where the state entry refers to the table, but the table no longer contains the address in question." ::= { pfTablesTblEntry 14 } pfTablesTblPktsInPass OBJECT-TYPE SYNTAX Counter64 MAX-ACCESS read-only STATUS current DESCRIPTION "The number of packets passed in matching the table." ::= { pfTablesTblEntry 15 } pfTablesTblPktsInBlock OBJECT-TYPE SYNTAX Counter64 MAX-ACCESS read-only STATUS current DESCRIPTION "The number of packets blocked coming in matching the table." ::= { pfTablesTblEntry 16 } pfTablesTblPktsInXPass OBJECT-TYPE SYNTAX Counter64 MAX-ACCESS read-only STATUS current DESCRIPTION "The number of packets statefully passed in where the state entry refers to the table, but the table no longer contains the address in question." ::= { pfTablesTblEntry 17 } pfTablesTblPktsOutPass OBJECT-TYPE SYNTAX Counter64 MAX-ACCESS read-only STATUS current DESCRIPTION "The number of packets passed out matching the table." ::= { pfTablesTblEntry 18 } pfTablesTblPktsOutBlock OBJECT-TYPE SYNTAX Counter64 MAX-ACCESS read-only STATUS current DESCRIPTION "The number of packets blocked going out matching the table." ::= { pfTablesTblEntry 19 } pfTablesTblPktsOutXPass OBJECT-TYPE SYNTAX Counter64 MAX-ACCESS read-only STATUS current DESCRIPTION "The number of packets statefully passed out where the state entry refers to the table, but the table no longer contains the address in question." ::= { pfTablesTblEntry 20 } pfTablesAddrTable OBJECT-TYPE SYNTAX SEQUENCE OF PfTablesAddrEntry MAX-ACCESS not-accessible STATUS current DESCRIPTION "Table of addresses from every table on the system." ::= { pfTables 3 } pfTablesAddrEntry OBJECT-TYPE SYNTAX PfTablesAddrEntry MAX-ACCESS not-accessible STATUS current DESCRIPTION "An entry in the pfTablesAddrTable containing information about a particular entry in a table." INDEX { pfTablesAddrIndex } ::= { pfTablesAddrTable 1 } PfTablesAddrEntry ::= SEQUENCE { pfTablesAddrIndex Integer32, pfTablesAddrNetType InetAddressType, pfTablesAddrNet InetAddress, pfTablesAddrPrefix InetAddressPrefixLength, pfTablesAddrTZero TimeTicks, pfTablesAddrBytesInPass Counter64, pfTablesAddrBytesInBlock Counter64, pfTablesAddrBytesOutPass Counter64, pfTablesAddrBytesOutBlock Counter64, pfTablesAddrPktsInPass Counter64, pfTablesAddrPktsInBlock Counter64, pfTablesAddrPktsOutPass Counter64, pfTablesAddrPktsOutBlock Counter64 } pfTablesAddrIndex OBJECT-TYPE SYNTAX Integer32 (1..2147483647) MAX-ACCESS not-accessible STATUS current DESCRIPTION "A unique value, greater than zero, for each address." ::= { pfTablesAddrEntry 1 } pfTablesAddrNetType OBJECT-TYPE SYNTAX InetAddressType MAX-ACCESS read-only STATUS current DESCRIPTION "The type of address in the corresponding pfTablesAddrNet object." ::= { pfTablesAddrEntry 2 } pfTablesAddrNet OBJECT-TYPE SYNTAX InetAddress MAX-ACCESS read-only STATUS current DESCRIPTION "The IP address of this particular table entry." ::= { pfTablesAddrEntry 3 } pfTablesAddrPrefix OBJECT-TYPE SYNTAX InetAddressPrefixLength MAX-ACCESS read-only STATUS current DESCRIPTION "The CIDR netmask of this particular table entry." ::= { pfTablesAddrEntry 4 } pfTablesAddrTZero OBJECT-TYPE SYNTAX TimeTicks UNITS "1/100th of a Second" MAX-ACCESS read-only STATUS current DESCRIPTION "The time passed since this entry's statistics were last cleared, or the time passed since this entry was loaded into the table, whichever is sooner." ::= { pfTablesAddrEntry 5 } pfTablesAddrBytesInPass OBJECT-TYPE SYNTAX Counter64 MAX-ACCESS read-only STATUS current DESCRIPTION "The number of inbound bytes passed as a result of this entry." ::= { pfTablesAddrEntry 6 } pfTablesAddrBytesInBlock OBJECT-TYPE SYNTAX Counter64 MAX-ACCESS read-only STATUS current DESCRIPTION "The number of inbound bytes blocked as a result of this entry." ::= { pfTablesAddrEntry 7 } pfTablesAddrBytesOutPass OBJECT-TYPE SYNTAX Counter64 MAX-ACCESS read-only STATUS current DESCRIPTION "The number of outbound bytes passed as a result of this entry." ::= { pfTablesAddrEntry 8 } pfTablesAddrBytesOutBlock OBJECT-TYPE SYNTAX Counter64 MAX-ACCESS read-only STATUS current DESCRIPTION "The number of outbound bytes blocked as a result of this entry." ::= { pfTablesAddrEntry 9 } pfTablesAddrPktsInPass OBJECT-TYPE SYNTAX Counter64 MAX-ACCESS read-only STATUS current DESCRIPTION "The number of inbound packets passed as a result of this entry." ::= { pfTablesAddrEntry 10 } pfTablesAddrPktsInBlock OBJECT-TYPE SYNTAX Counter64 MAX-ACCESS read-only STATUS current DESCRIPTION "The number of inbound packets blocked as a result of this entry." ::= { pfTablesAddrEntry 11 } pfTablesAddrPktsOutPass OBJECT-TYPE SYNTAX Counter64 MAX-ACCESS read-only STATUS current DESCRIPTION "The number of outbound packets passed as a result of this entry." ::= { pfTablesAddrEntry 12 } pfTablesAddrPktsOutBlock OBJECT-TYPE SYNTAX Counter64 MAX-ACCESS read-only STATUS current DESCRIPTION "The number of outbound packets blocked as a result of this entry." ::= { pfTablesAddrEntry 13 } -- -------------------------------------------------------------------------- -- -- Altq information -- pfAltqQueueNumber OBJECT-TYPE SYNTAX Unsigned32 MAX-ACCESS read-only STATUS current DESCRIPTION "The number of queues in the active set." ::= { pfAltq 1 } pfAltqQueueTable OBJECT-TYPE SYNTAX SEQUENCE OF PfAltqQueueEntry MAX-ACCESS not-accessible STATUS current DESCRIPTION "Table containing the rules that are active on this system." ::= { pfAltq 2 } pfAltqQueueEntry OBJECT-TYPE SYNTAX PfAltqQueueEntry MAX-ACCESS not-accessible STATUS current DESCRIPTION "An entry in the pfAltqQueueTable table." INDEX { pfAltqQueueIndex } ::= { pfAltqQueueTable 1 } PfAltqQueueEntry ::= SEQUENCE { pfAltqQueueIndex Integer32, pfAltqQueueDescr OCTET STRING, pfAltqQueueParent OCTET STRING, pfAltqQueueScheduler INTEGER, pfAltqQueueBandwidth Unsigned32, pfAltqQueuePriority Integer32, pfAltqQueueLimit Integer32 } pfAltqQueueIndex OBJECT-TYPE SYNTAX Integer32 (1..2147483647) MAX-ACCESS not-accessible STATUS current DESCRIPTION "A unique value, greater than zero, for each queue." ::= { pfAltqQueueEntry 1 } pfAltqQueueDescr OBJECT-TYPE SYNTAX OCTET STRING MAX-ACCESS read-only STATUS current DESCRIPTION "The name of the queue." ::= { pfAltqQueueEntry 2 } pfAltqQueueParent OBJECT-TYPE SYNTAX OCTET STRING MAX-ACCESS read-only STATUS current DESCRIPTION "Name of the queue's parent if it has one." ::= { pfAltqQueueEntry 3 } pfAltqQueueScheduler OBJECT-TYPE SYNTAX INTEGER { cbq(1), hfsc(8), priq(11) } MAX-ACCESS read-only STATUS current DESCRIPTION "Scheduler algorithm implemented by this queue." ::= { pfAltqQueueEntry 4 } pfAltqQueueBandwidth OBJECT-TYPE SYNTAX Unsigned32 MAX-ACCESS read-only STATUS current DESCRIPTION "Bandwitch assigned to this queue." ::= { pfAltqQueueEntry 5 } pfAltqQueuePriority OBJECT-TYPE SYNTAX Integer32 MAX-ACCESS read-only STATUS current DESCRIPTION "Priority level of the queue." ::= { pfAltqQueueEntry 6 } pfAltqQueueLimit OBJECT-TYPE SYNTAX Integer32 MAX-ACCESS read-only STATUS current DESCRIPTION "Maximum number of packets in the queue." ::= { pfAltqQueueEntry 7 } pfLabelsLblNumber OBJECT-TYPE SYNTAX Integer32 MAX-ACCESS read-only STATUS current DESCRIPTION "The number of labeled filter rules on this system." ::= { pfLabels 1 } pfLabelsLblTable OBJECT-TYPE SYNTAX SEQUENCE OF PfLabelsLblEntry MAX-ACCESS not-accessible STATUS current DESCRIPTION "Table of filter rules, index on pfLabelsLblIndex." ::= { pfLabels 2 } pfLabelsLblEntry OBJECT-TYPE SYNTAX PfLabelsLblEntry MAX-ACCESS not-accessible STATUS current DESCRIPTION "Any entry in the pfLabelsLblTable containing information about a particular filter rule on the system." INDEX { pfLabelsLblIndex } ::= { pfLabelsLblTable 1 } PfLabelsLblEntry ::= SEQUENCE { pfLabelsLblIndex Integer32, pfLabelsLblName OCTET STRING, pfLabelsLblEvals Counter64, pfLabelsLblBytesIn Counter64, pfLabelsLblBytesOut Counter64, pfLabelsLblPktsIn Counter64, pfLabelsLblPktsOut Counter64 } pfLabelsLblIndex OBJECT-TYPE SYNTAX Integer32 (1..2147483647) MAX-ACCESS not-accessible STATUS current DESCRIPTION "A unique value, greater than zero, for each label." - ::= { pfLabelsLblEntry 1 } + ::= { pfLabelsLblEntry 1 } pfLabelsLblName OBJECT-TYPE SYNTAX OCTET STRING MAX-ACCESS read-only STATUS current DESCRIPTION "The name of the rule label." ::= { pfLabelsLblEntry 2 } pfLabelsLblEvals OBJECT-TYPE SYNTAX Counter64 MAX-ACCESS read-only STATUS current DESCRIPTION "The number of rule evaluations." ::= { pfLabelsLblEntry 3 } pfLabelsLblBytesIn OBJECT-TYPE SYNTAX Counter64 MAX-ACCESS read-only STATUS current DESCRIPTION "The number of incoming bytes matched by the rule." ::= { pfLabelsLblEntry 4 } pfLabelsLblBytesOut OBJECT-TYPE SYNTAX Counter64 MAX-ACCESS read-only STATUS current DESCRIPTION "The number of outgoing bytes matched by the rule." ::= { pfLabelsLblEntry 5 } pfLabelsLblPktsIn OBJECT-TYPE SYNTAX Counter64 MAX-ACCESS read-only STATUS current DESCRIPTION "The number of incoming packets matched by the rule." ::= { pfLabelsLblEntry 6 } pfLabelsLblPktsOut OBJECT-TYPE SYNTAX Counter64 MAX-ACCESS read-only STATUS current DESCRIPTION "The number of outgoing packets matched by the rule." ::= { pfLabelsLblEntry 7 } END Index: head/usr.sbin/bsnmpd/modules/snmp_pf/Makefile =================================================================== --- head/usr.sbin/bsnmpd/modules/snmp_pf/Makefile (revision 310647) +++ head/usr.sbin/bsnmpd/modules/snmp_pf/Makefile (revision 310648) @@ -1,13 +1,13 @@ # $FreeBSD$ -# +# # Author: Philip Paeps MOD= pf SRCS= pf_snmp.c CFLAGS+= -DSNMPTREE_TYPES XSYM= begemotPf DEFS= ${MOD}_tree.def BMIBS= BEGEMOT-PF-MIB.txt .include Index: head/usr.sbin/bsnmpd/modules/snmp_pf/pf_snmp.c =================================================================== --- head/usr.sbin/bsnmpd/modules/snmp_pf/pf_snmp.c (revision 310647) +++ head/usr.sbin/bsnmpd/modules/snmp_pf/pf_snmp.c (revision 310648) @@ -1,1797 +1,1797 @@ /*- * Copyright (c) 2005 Philip Paeps * All rights reserved. * * 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 THE 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 THE 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. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include "pf_oid.h" #include "pf_tree.h" struct lmodule *module; static int dev = -1; static int started; static uint64_t pf_tick; static struct pf_status pfs; enum { IN, OUT }; enum { IPV4, IPV6 }; enum { PASS, BLOCK }; #define PFI_IFTYPE_GROUP 0 #define PFI_IFTYPE_INSTANCE 1 #define PFI_IFTYPE_DETACHED 2 struct pfi_entry { struct pfi_kif pfi; u_int index; TAILQ_ENTRY(pfi_entry) link; }; TAILQ_HEAD(pfi_table, pfi_entry); static struct pfi_table pfi_table; static time_t pfi_table_age; static int pfi_table_count; #define PFI_TABLE_MAXAGE 5 struct pft_entry { struct pfr_tstats pft; u_int index; TAILQ_ENTRY(pft_entry) link; }; TAILQ_HEAD(pft_table, pft_entry); static struct pft_table pft_table; static time_t pft_table_age; static int pft_table_count; #define PFT_TABLE_MAXAGE 5 struct pfa_entry { struct pfr_astats pfas; u_int index; TAILQ_ENTRY(pfa_entry) link; }; TAILQ_HEAD(pfa_table, pfa_entry); static struct pfa_table pfa_table; static time_t pfa_table_age; static int pfa_table_count; #define PFA_TABLE_MAXAGE 5 struct pfq_entry { struct pf_altq altq; u_int index; TAILQ_ENTRY(pfq_entry) link; }; TAILQ_HEAD(pfq_table, pfq_entry); static struct pfq_table pfq_table; static time_t pfq_table_age; static int pfq_table_count; static int altq_enabled = 0; #define PFQ_TABLE_MAXAGE 5 struct pfl_entry { char name[MAXPATHLEN + PF_RULE_LABEL_SIZE]; u_int64_t evals; u_int64_t bytes[2]; u_int64_t pkts[2]; u_int index; TAILQ_ENTRY(pfl_entry) link; }; TAILQ_HEAD(pfl_table, pfl_entry); static struct pfl_table pfl_table; static time_t pfl_table_age; static int pfl_table_count; #define PFL_TABLE_MAXAGE 5 /* Forward declarations */ static int pfi_refresh(void); static int pfq_refresh(void); static int pfs_refresh(void); static int pft_refresh(void); static int pfa_refresh(void); static int pfl_refresh(void); static struct pfi_entry * pfi_table_find(u_int idx); static struct pfq_entry * pfq_table_find(u_int idx); static struct pft_entry * pft_table_find(u_int idx); static struct pfa_entry * pfa_table_find(u_int idx); static struct pfl_entry * pfl_table_find(u_int idx); static int altq_is_enabled(int pfdevice); int pf_status(struct snmp_context __unused *ctx, struct snmp_value *val, u_int sub, u_int __unused vindex, enum snmp_op op) { asn_subid_t which = val->var.subs[sub - 1]; time_t runtime; unsigned char str[128]; if (op == SNMP_OP_SET) return (SNMP_ERR_NOT_WRITEABLE); if (op == SNMP_OP_GET) { if (pfs_refresh() == -1) return (SNMP_ERR_GENERR); switch (which) { case LEAF_pfStatusRunning: val->v.uint32 = pfs.running; break; case LEAF_pfStatusRuntime: runtime = (pfs.since > 0) ? time(NULL) - pfs.since : 0; val->v.uint32 = runtime * 100; break; case LEAF_pfStatusDebug: val->v.uint32 = pfs.debug; break; case LEAF_pfStatusHostId: sprintf(str, "0x%08x", ntohl(pfs.hostid)); return (string_get(val, str, strlen(str))); default: return (SNMP_ERR_NOSUCHNAME); } return (SNMP_ERR_NOERROR); } abort(); } int pf_counter(struct snmp_context __unused *ctx, struct snmp_value *val, u_int sub, u_int __unused vindex, enum snmp_op op) { asn_subid_t which = val->var.subs[sub - 1]; if (op == SNMP_OP_SET) return (SNMP_ERR_NOT_WRITEABLE); if (op == SNMP_OP_GET) { if (pfs_refresh() == -1) return (SNMP_ERR_GENERR); switch (which) { case LEAF_pfCounterMatch: val->v.counter64 = pfs.counters[PFRES_MATCH]; break; case LEAF_pfCounterBadOffset: val->v.counter64 = pfs.counters[PFRES_BADOFF]; break; case LEAF_pfCounterFragment: val->v.counter64 = pfs.counters[PFRES_FRAG]; break; case LEAF_pfCounterShort: val->v.counter64 = pfs.counters[PFRES_SHORT]; break; case LEAF_pfCounterNormalize: val->v.counter64 = pfs.counters[PFRES_NORM]; break; case LEAF_pfCounterMemDrop: val->v.counter64 = pfs.counters[PFRES_MEMORY]; break; default: return (SNMP_ERR_NOSUCHNAME); } return (SNMP_ERR_NOERROR); } abort(); } int pf_statetable(struct snmp_context __unused *ctx, struct snmp_value *val, u_int sub, u_int __unused vindex, enum snmp_op op) { asn_subid_t which = val->var.subs[sub - 1]; if (op == SNMP_OP_SET) return (SNMP_ERR_NOT_WRITEABLE); if (op == SNMP_OP_GET) { if (pfs_refresh() == -1) return (SNMP_ERR_GENERR); switch (which) { case LEAF_pfStateTableCount: val->v.uint32 = pfs.states; break; case LEAF_pfStateTableSearches: val->v.counter64 = pfs.fcounters[FCNT_STATE_SEARCH]; break; case LEAF_pfStateTableInserts: val->v.counter64 = pfs.fcounters[FCNT_STATE_INSERT]; break; case LEAF_pfStateTableRemovals: val->v.counter64 = pfs.fcounters[FCNT_STATE_REMOVALS]; break; default: return (SNMP_ERR_NOSUCHNAME); } return (SNMP_ERR_NOERROR); } abort(); } int pf_srcnodes(struct snmp_context __unused *ctx, struct snmp_value *val, u_int sub, u_int __unused vindex, enum snmp_op op) { asn_subid_t which = val->var.subs[sub - 1]; if (op == SNMP_OP_SET) return (SNMP_ERR_NOT_WRITEABLE); if (op == SNMP_OP_GET) { if (pfs_refresh() == -1) return (SNMP_ERR_GENERR); switch (which) { case LEAF_pfSrcNodesCount: val->v.uint32 = pfs.src_nodes; break; case LEAF_pfSrcNodesSearches: val->v.counter64 = pfs.scounters[SCNT_SRC_NODE_SEARCH]; break; case LEAF_pfSrcNodesInserts: val->v.counter64 = pfs.scounters[SCNT_SRC_NODE_INSERT]; break; case LEAF_pfSrcNodesRemovals: val->v.counter64 = pfs.scounters[SCNT_SRC_NODE_REMOVALS]; break; default: return (SNMP_ERR_NOSUCHNAME); } return (SNMP_ERR_NOERROR); } abort(); } int pf_limits(struct snmp_context __unused *ctx, struct snmp_value *val, u_int sub, u_int __unused vindex, enum snmp_op op) { asn_subid_t which = val->var.subs[sub - 1]; struct pfioc_limit pl; if (op == SNMP_OP_SET) return (SNMP_ERR_NOT_WRITEABLE); if (op == SNMP_OP_GET) { bzero(&pl, sizeof(struct pfioc_limit)); switch (which) { case LEAF_pfLimitsStates: pl.index = PF_LIMIT_STATES; break; case LEAF_pfLimitsSrcNodes: pl.index = PF_LIMIT_SRC_NODES; break; case LEAF_pfLimitsFrags: pl.index = PF_LIMIT_FRAGS; break; default: return (SNMP_ERR_NOSUCHNAME); } if (ioctl(dev, DIOCGETLIMIT, &pl)) { syslog(LOG_ERR, "pf_limits(): ioctl(): %s", strerror(errno)); return (SNMP_ERR_GENERR); } val->v.uint32 = pl.limit; return (SNMP_ERR_NOERROR); } abort(); } int pf_timeouts(struct snmp_context __unused *ctx, struct snmp_value *val, u_int sub, u_int __unused vindex, enum snmp_op op) { asn_subid_t which = val->var.subs[sub - 1]; struct pfioc_tm pt; if (op == SNMP_OP_SET) return (SNMP_ERR_NOT_WRITEABLE); if (op == SNMP_OP_GET) { bzero(&pt, sizeof(struct pfioc_tm)); switch (which) { case LEAF_pfTimeoutsTcpFirst: pt.timeout = PFTM_TCP_FIRST_PACKET; break; case LEAF_pfTimeoutsTcpOpening: pt.timeout = PFTM_TCP_OPENING; break; case LEAF_pfTimeoutsTcpEstablished: pt.timeout = PFTM_TCP_ESTABLISHED; break; case LEAF_pfTimeoutsTcpClosing: pt.timeout = PFTM_TCP_CLOSING; break; case LEAF_pfTimeoutsTcpFinWait: pt.timeout = PFTM_TCP_FIN_WAIT; break; case LEAF_pfTimeoutsTcpClosed: pt.timeout = PFTM_TCP_CLOSED; break; case LEAF_pfTimeoutsUdpFirst: pt.timeout = PFTM_UDP_FIRST_PACKET; break; case LEAF_pfTimeoutsUdpSingle: pt.timeout = PFTM_UDP_SINGLE; break; case LEAF_pfTimeoutsUdpMultiple: pt.timeout = PFTM_UDP_MULTIPLE; break; case LEAF_pfTimeoutsIcmpFirst: pt.timeout = PFTM_ICMP_FIRST_PACKET; break; case LEAF_pfTimeoutsIcmpError: pt.timeout = PFTM_ICMP_ERROR_REPLY; break; case LEAF_pfTimeoutsOtherFirst: pt.timeout = PFTM_OTHER_FIRST_PACKET; break; case LEAF_pfTimeoutsOtherSingle: pt.timeout = PFTM_OTHER_SINGLE; break; case LEAF_pfTimeoutsOtherMultiple: pt.timeout = PFTM_OTHER_MULTIPLE; break; case LEAF_pfTimeoutsFragment: pt.timeout = PFTM_FRAG; break; case LEAF_pfTimeoutsInterval: pt.timeout = PFTM_INTERVAL; break; case LEAF_pfTimeoutsAdaptiveStart: pt.timeout = PFTM_ADAPTIVE_START; break; case LEAF_pfTimeoutsAdaptiveEnd: pt.timeout = PFTM_ADAPTIVE_END; break; case LEAF_pfTimeoutsSrcNode: pt.timeout = PFTM_SRC_NODE; break; default: return (SNMP_ERR_NOSUCHNAME); } if (ioctl(dev, DIOCGETTIMEOUT, &pt)) { syslog(LOG_ERR, "pf_timeouts(): ioctl(): %s", strerror(errno)); return (SNMP_ERR_GENERR); } val->v.integer = pt.seconds; return (SNMP_ERR_NOERROR); } abort(); } int pf_logif(struct snmp_context __unused *ctx, struct snmp_value *val, u_int sub, u_int __unused vindex, enum snmp_op op) { asn_subid_t which = val->var.subs[sub - 1]; unsigned char str[IFNAMSIZ]; if (op == SNMP_OP_SET) return (SNMP_ERR_NOT_WRITEABLE); if (op == SNMP_OP_GET) { if (pfs_refresh() == -1) return (SNMP_ERR_GENERR); switch (which) { case LEAF_pfLogInterfaceName: strlcpy(str, pfs.ifname, sizeof str); return (string_get(val, str, strlen(str))); case LEAF_pfLogInterfaceIp4BytesIn: val->v.counter64 = pfs.bcounters[IPV4][IN]; break; case LEAF_pfLogInterfaceIp4BytesOut: val->v.counter64 = pfs.bcounters[IPV4][OUT]; break; case LEAF_pfLogInterfaceIp4PktsInPass: val->v.counter64 = pfs.pcounters[IPV4][IN][PF_PASS]; break; case LEAF_pfLogInterfaceIp4PktsInDrop: val->v.counter64 = pfs.pcounters[IPV4][IN][PF_DROP]; break; case LEAF_pfLogInterfaceIp4PktsOutPass: val->v.counter64 = pfs.pcounters[IPV4][OUT][PF_PASS]; break; case LEAF_pfLogInterfaceIp4PktsOutDrop: val->v.counter64 = pfs.pcounters[IPV4][OUT][PF_DROP]; break; case LEAF_pfLogInterfaceIp6BytesIn: val->v.counter64 = pfs.bcounters[IPV6][IN]; break; case LEAF_pfLogInterfaceIp6BytesOut: val->v.counter64 = pfs.bcounters[IPV6][OUT]; break; case LEAF_pfLogInterfaceIp6PktsInPass: val->v.counter64 = pfs.pcounters[IPV6][IN][PF_PASS]; break; case LEAF_pfLogInterfaceIp6PktsInDrop: val->v.counter64 = pfs.pcounters[IPV6][IN][PF_DROP]; break; case LEAF_pfLogInterfaceIp6PktsOutPass: val->v.counter64 = pfs.pcounters[IPV6][OUT][PF_PASS]; break; case LEAF_pfLogInterfaceIp6PktsOutDrop: val->v.counter64 = pfs.pcounters[IPV6][OUT][PF_DROP]; break; default: return (SNMP_ERR_NOSUCHNAME); } return (SNMP_ERR_NOERROR); } abort(); } int pf_interfaces(struct snmp_context __unused *ctx, struct snmp_value *val, u_int sub, u_int __unused vindex, enum snmp_op op) { asn_subid_t which = val->var.subs[sub - 1]; if (op == SNMP_OP_SET) return (SNMP_ERR_NOT_WRITEABLE); if (op == SNMP_OP_GET) { if ((time(NULL) - pfi_table_age) > PFI_TABLE_MAXAGE) if (pfi_refresh() == -1) return (SNMP_ERR_GENERR); switch (which) { case LEAF_pfInterfacesIfNumber: val->v.uint32 = pfi_table_count; break; default: return (SNMP_ERR_NOSUCHNAME); } return (SNMP_ERR_NOERROR); } abort(); } int pf_iftable(struct snmp_context __unused *ctx, struct snmp_value *val, u_int sub, u_int __unused vindex, enum snmp_op op) { asn_subid_t which = val->var.subs[sub - 1]; struct pfi_entry *e = NULL; if ((time(NULL) - pfi_table_age) > PFI_TABLE_MAXAGE) pfi_refresh(); switch (op) { case SNMP_OP_SET: return (SNMP_ERR_NOT_WRITEABLE); case SNMP_OP_GETNEXT: if ((e = NEXT_OBJECT_INT(&pfi_table, &val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); val->var.len = sub + 1; val->var.subs[sub] = e->index; break; case SNMP_OP_GET: if (val->var.len - sub != 1) return (SNMP_ERR_NOSUCHNAME); if ((e = pfi_table_find(val->var.subs[sub])) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_COMMIT: case SNMP_OP_ROLLBACK: default: abort(); } switch (which) { case LEAF_pfInterfacesIfDescr: return (string_get(val, e->pfi.pfik_name, -1)); case LEAF_pfInterfacesIfType: val->v.integer = PFI_IFTYPE_INSTANCE; break; case LEAF_pfInterfacesIfTZero: val->v.uint32 = (time(NULL) - e->pfi.pfik_tzero) * 100; break; case LEAF_pfInterfacesIfRefsRule: val->v.uint32 = e->pfi.pfik_rulerefs; break; case LEAF_pfInterfacesIf4BytesInPass: val->v.counter64 = e->pfi.pfik_bytes[IPV4][IN][PASS]; break; case LEAF_pfInterfacesIf4BytesInBlock: val->v.counter64 = e->pfi.pfik_bytes[IPV4][IN][BLOCK]; break; case LEAF_pfInterfacesIf4BytesOutPass: val->v.counter64 = e->pfi.pfik_bytes[IPV4][OUT][PASS]; break; case LEAF_pfInterfacesIf4BytesOutBlock: val->v.counter64 = e->pfi.pfik_bytes[IPV4][OUT][BLOCK]; break; case LEAF_pfInterfacesIf4PktsInPass: val->v.counter64 = e->pfi.pfik_packets[IPV4][IN][PASS]; break; case LEAF_pfInterfacesIf4PktsInBlock: val->v.counter64 = e->pfi.pfik_packets[IPV4][IN][BLOCK]; break; case LEAF_pfInterfacesIf4PktsOutPass: val->v.counter64 = e->pfi.pfik_packets[IPV4][OUT][PASS]; break; case LEAF_pfInterfacesIf4PktsOutBlock: val->v.counter64 = e->pfi.pfik_packets[IPV4][OUT][BLOCK]; break; case LEAF_pfInterfacesIf6BytesInPass: val->v.counter64 = e->pfi.pfik_bytes[IPV6][IN][PASS]; break; case LEAF_pfInterfacesIf6BytesInBlock: val->v.counter64 = e->pfi.pfik_bytes[IPV6][IN][BLOCK]; break; case LEAF_pfInterfacesIf6BytesOutPass: val->v.counter64 = e->pfi.pfik_bytes[IPV6][OUT][PASS]; break; case LEAF_pfInterfacesIf6BytesOutBlock: val->v.counter64 = e->pfi.pfik_bytes[IPV6][OUT][BLOCK]; break; case LEAF_pfInterfacesIf6PktsInPass: val->v.counter64 = e->pfi.pfik_packets[IPV6][IN][PASS]; break; case LEAF_pfInterfacesIf6PktsInBlock: val->v.counter64 = e->pfi.pfik_packets[IPV6][IN][BLOCK]; break; case LEAF_pfInterfacesIf6PktsOutPass: val->v.counter64 = e->pfi.pfik_packets[IPV6][OUT][PASS]; break; case LEAF_pfInterfacesIf6PktsOutBlock: - val->v.counter64 = + val->v.counter64 = e->pfi.pfik_packets[IPV6][OUT][BLOCK]; break; default: return (SNMP_ERR_NOSUCHNAME); } return (SNMP_ERR_NOERROR); } int pf_tables(struct snmp_context __unused *ctx, struct snmp_value *val, u_int sub, u_int __unused vindex, enum snmp_op op) { asn_subid_t which = val->var.subs[sub - 1]; if (op == SNMP_OP_SET) return (SNMP_ERR_NOT_WRITEABLE); if (op == SNMP_OP_GET) { if ((time(NULL) - pft_table_age) > PFT_TABLE_MAXAGE) if (pft_refresh() == -1) return (SNMP_ERR_GENERR); switch (which) { case LEAF_pfTablesTblNumber: val->v.uint32 = pft_table_count; break; default: return (SNMP_ERR_NOSUCHNAME); } return (SNMP_ERR_NOERROR); } abort(); } int pf_tbltable(struct snmp_context __unused *ctx, struct snmp_value *val, u_int sub, u_int __unused vindex, enum snmp_op op) { asn_subid_t which = val->var.subs[sub - 1]; struct pft_entry *e = NULL; if ((time(NULL) - pft_table_age) > PFT_TABLE_MAXAGE) pft_refresh(); switch (op) { case SNMP_OP_SET: return (SNMP_ERR_NOT_WRITEABLE); case SNMP_OP_GETNEXT: if ((e = NEXT_OBJECT_INT(&pft_table, &val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); val->var.len = sub + 1; val->var.subs[sub] = e->index; break; case SNMP_OP_GET: if (val->var.len - sub != 1) return (SNMP_ERR_NOSUCHNAME); if ((e = pft_table_find(val->var.subs[sub])) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_COMMIT: case SNMP_OP_ROLLBACK: default: abort(); } switch (which) { case LEAF_pfTablesTblDescr: return (string_get(val, e->pft.pfrts_name, -1)); case LEAF_pfTablesTblCount: val->v.integer = e->pft.pfrts_cnt; break; case LEAF_pfTablesTblTZero: val->v.uint32 = (time(NULL) - e->pft.pfrts_tzero) * 100; break; case LEAF_pfTablesTblRefsAnchor: val->v.integer = e->pft.pfrts_refcnt[PFR_REFCNT_ANCHOR]; break; case LEAF_pfTablesTblRefsRule: val->v.integer = e->pft.pfrts_refcnt[PFR_REFCNT_RULE]; break; case LEAF_pfTablesTblEvalMatch: val->v.counter64 = e->pft.pfrts_match; break; case LEAF_pfTablesTblEvalNoMatch: val->v.counter64 = e->pft.pfrts_nomatch; break; case LEAF_pfTablesTblBytesInPass: val->v.counter64 = e->pft.pfrts_bytes[PFR_DIR_IN][PFR_OP_PASS]; break; case LEAF_pfTablesTblBytesInBlock: val->v.counter64 = e->pft.pfrts_bytes[PFR_DIR_IN][PFR_OP_BLOCK]; break; case LEAF_pfTablesTblBytesInXPass: val->v.counter64 = e->pft.pfrts_bytes[PFR_DIR_IN][PFR_OP_XPASS]; break; case LEAF_pfTablesTblBytesOutPass: val->v.counter64 = e->pft.pfrts_bytes[PFR_DIR_OUT][PFR_OP_PASS]; break; case LEAF_pfTablesTblBytesOutBlock: val->v.counter64 = e->pft.pfrts_bytes[PFR_DIR_OUT][PFR_OP_BLOCK]; break; case LEAF_pfTablesTblBytesOutXPass: val->v.counter64 = e->pft.pfrts_bytes[PFR_DIR_OUT][PFR_OP_XPASS]; break; case LEAF_pfTablesTblPktsInPass: val->v.counter64 = e->pft.pfrts_packets[PFR_DIR_IN][PFR_OP_PASS]; break; case LEAF_pfTablesTblPktsInBlock: val->v.counter64 = e->pft.pfrts_packets[PFR_DIR_IN][PFR_OP_BLOCK]; break; case LEAF_pfTablesTblPktsInXPass: val->v.counter64 = e->pft.pfrts_packets[PFR_DIR_IN][PFR_OP_XPASS]; break; case LEAF_pfTablesTblPktsOutPass: val->v.counter64 = e->pft.pfrts_packets[PFR_DIR_OUT][PFR_OP_PASS]; break; case LEAF_pfTablesTblPktsOutBlock: val->v.counter64 = e->pft.pfrts_packets[PFR_DIR_OUT][PFR_OP_BLOCK]; break; case LEAF_pfTablesTblPktsOutXPass: val->v.counter64 = e->pft.pfrts_packets[PFR_DIR_OUT][PFR_OP_XPASS]; break; default: return (SNMP_ERR_NOSUCHNAME); } return (SNMP_ERR_NOERROR); } int pf_tbladdr(struct snmp_context __unused *ctx, struct snmp_value __unused *val, u_int __unused sub, u_int __unused vindex, enum snmp_op __unused op) { asn_subid_t which = val->var.subs[sub - 1]; struct pfa_entry *e = NULL; if ((time(NULL) - pfa_table_age) > PFA_TABLE_MAXAGE) pfa_refresh(); switch (op) { case SNMP_OP_SET: return (SNMP_ERR_NOT_WRITEABLE); case SNMP_OP_GETNEXT: if ((e = NEXT_OBJECT_INT(&pfa_table, &val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); val->var.len = sub + 1; val->var.subs[sub] = e->index; break; case SNMP_OP_GET: if (val->var.len - sub != 1) return (SNMP_ERR_NOSUCHNAME); if ((e = pfa_table_find(val->var.subs[sub])) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_COMMIT: case SNMP_OP_ROLLBACK: default: abort(); } switch (which) { case LEAF_pfTablesAddrNetType: if (e->pfas.pfras_a.pfra_af == AF_INET) val->v.integer = pfTablesAddrNetType_ipv4; else if (e->pfas.pfras_a.pfra_af == AF_INET6) val->v.integer = pfTablesAddrNetType_ipv6; else return (SNMP_ERR_GENERR); break; case LEAF_pfTablesAddrNet: if (e->pfas.pfras_a.pfra_af == AF_INET) { return (string_get(val, (u_char *)&e->pfas.pfras_a.pfra_ip4addr, 4)); } else if (e->pfas.pfras_a.pfra_af == AF_INET6) return (string_get(val, (u_char *)&e->pfas.pfras_a.pfra_ip6addr, 16)); else return (SNMP_ERR_GENERR); break; case LEAF_pfTablesAddrPrefix: val->v.integer = (int32_t) e->pfas.pfras_a.pfra_net; break; case LEAF_pfTablesAddrTZero: val->v.uint32 = (time(NULL) - e->pfas.pfras_tzero) * 100; break; case LEAF_pfTablesAddrBytesInPass: val->v.counter64 = e->pfas.pfras_bytes[PFR_DIR_IN][PFR_OP_PASS]; break; case LEAF_pfTablesAddrBytesInBlock: val->v.counter64 = e->pfas.pfras_bytes[PFR_DIR_IN][PFR_OP_BLOCK]; break; case LEAF_pfTablesAddrBytesOutPass: val->v.counter64 = e->pfas.pfras_bytes[PFR_DIR_OUT][PFR_OP_PASS]; break; case LEAF_pfTablesAddrBytesOutBlock: val->v.counter64 = e->pfas.pfras_bytes[PFR_DIR_OUT][PFR_OP_BLOCK]; break; case LEAF_pfTablesAddrPktsInPass: val->v.counter64 = e->pfas.pfras_packets[PFR_DIR_IN][PFR_OP_PASS]; break; case LEAF_pfTablesAddrPktsInBlock: val->v.counter64 = e->pfas.pfras_packets[PFR_DIR_IN][PFR_OP_BLOCK]; break; case LEAF_pfTablesAddrPktsOutPass: val->v.counter64 = e->pfas.pfras_packets[PFR_DIR_OUT][PFR_OP_PASS]; break; case LEAF_pfTablesAddrPktsOutBlock: val->v.counter64 = e->pfas.pfras_packets[PFR_DIR_OUT][PFR_OP_BLOCK]; break; default: return (SNMP_ERR_NOSUCHNAME); } return (SNMP_ERR_NOERROR); } int pf_altq(struct snmp_context __unused *ctx, struct snmp_value *val, u_int sub, u_int __unused vindex, enum snmp_op op) { asn_subid_t which = val->var.subs[sub - 1]; if (!altq_enabled) return (SNMP_ERR_NOSUCHNAME); if (op == SNMP_OP_SET) return (SNMP_ERR_NOT_WRITEABLE); if (op == SNMP_OP_GET) { if ((time(NULL) - pfq_table_age) > PFQ_TABLE_MAXAGE) if (pfq_refresh() == -1) return (SNMP_ERR_GENERR); switch (which) { case LEAF_pfAltqQueueNumber: val->v.uint32 = pfq_table_count; break; default: return (SNMP_ERR_NOSUCHNAME); } return (SNMP_ERR_NOERROR); } abort(); return (SNMP_ERR_GENERR); -} +} int pf_altqq(struct snmp_context __unused *ctx, struct snmp_value *val, u_int sub, u_int __unused vindex, enum snmp_op op) { asn_subid_t which = val->var.subs[sub - 1]; struct pfq_entry *e = NULL; if (!altq_enabled) return (SNMP_ERR_NOSUCHNAME); if ((time(NULL) - pfq_table_age) > PFQ_TABLE_MAXAGE) pfq_refresh(); switch (op) { case SNMP_OP_SET: return (SNMP_ERR_NOT_WRITEABLE); case SNMP_OP_GETNEXT: if ((e = NEXT_OBJECT_INT(&pfq_table, &val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); val->var.len = sub + 1; val->var.subs[sub] = e->index; break; case SNMP_OP_GET: if (val->var.len - sub != 1) return (SNMP_ERR_NOSUCHNAME); if ((e = pfq_table_find(val->var.subs[sub])) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_COMMIT: case SNMP_OP_ROLLBACK: default: abort(); } switch (which) { case LEAF_pfAltqQueueDescr: return (string_get(val, e->altq.qname, -1)); case LEAF_pfAltqQueueParent: return (string_get(val, e->altq.parent, -1)); case LEAF_pfAltqQueueScheduler: val->v.integer = e->altq.scheduler; break; case LEAF_pfAltqQueueBandwidth: val->v.uint32 = e->altq.bandwidth; break; case LEAF_pfAltqQueuePriority: val->v.integer = e->altq.priority; break; case LEAF_pfAltqQueueLimit: val->v.integer = e->altq.qlimit; break; - + default: return (SNMP_ERR_NOSUCHNAME); } return (SNMP_ERR_NOERROR); } int pf_labels(struct snmp_context __unused *ctx, struct snmp_value *val, u_int sub, u_int __unused vindex, enum snmp_op op) { asn_subid_t which = val->var.subs[sub - 1]; if (op == SNMP_OP_SET) return (SNMP_ERR_NOT_WRITEABLE); if (op == SNMP_OP_GET) { if ((time(NULL) - pfl_table_age) > PFL_TABLE_MAXAGE) if (pfl_refresh() == -1) return (SNMP_ERR_GENERR); switch (which) { case LEAF_pfLabelsLblNumber: val->v.uint32 = pfl_table_count; break; default: return (SNMP_ERR_NOSUCHNAME); } return (SNMP_ERR_NOERROR); } abort(); return (SNMP_ERR_GENERR); } int pf_lbltable(struct snmp_context __unused *ctx, struct snmp_value *val, u_int sub, u_int __unused vindex, enum snmp_op op) { asn_subid_t which = val->var.subs[sub - 1]; struct pfl_entry *e = NULL; if ((time(NULL) - pfl_table_age) > PFL_TABLE_MAXAGE) pfl_refresh(); switch (op) { case SNMP_OP_SET: return (SNMP_ERR_NOT_WRITEABLE); case SNMP_OP_GETNEXT: if ((e = NEXT_OBJECT_INT(&pfl_table, &val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); val->var.len = sub + 1; val->var.subs[sub] = e->index; break; case SNMP_OP_GET: if (val->var.len - sub != 1) return (SNMP_ERR_NOSUCHNAME); if ((e = pfl_table_find(val->var.subs[sub])) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_COMMIT: case SNMP_OP_ROLLBACK: default: abort(); } switch (which) { case LEAF_pfLabelsLblName: return (string_get(val, e->name, -1)); case LEAF_pfLabelsLblEvals: val->v.counter64 = e->evals; break; case LEAF_pfLabelsLblBytesIn: val->v.counter64 = e->bytes[IN]; break; case LEAF_pfLabelsLblBytesOut: val->v.counter64 = e->bytes[OUT]; break; case LEAF_pfLabelsLblPktsIn: val->v.counter64 = e->pkts[IN]; break; case LEAF_pfLabelsLblPktsOut: val->v.counter64 = e->pkts[OUT]; break; default: return (SNMP_ERR_NOSUCHNAME); } return (SNMP_ERR_NOERROR); } static struct pfi_entry * pfi_table_find(u_int idx) { struct pfi_entry *e; TAILQ_FOREACH(e, &pfi_table, link) if (e->index == idx) return (e); return (NULL); } static struct pfq_entry * pfq_table_find(u_int idx) { struct pfq_entry *e; TAILQ_FOREACH(e, &pfq_table, link) if (e->index == idx) return (e); return (NULL); } static struct pft_entry * pft_table_find(u_int idx) { struct pft_entry *e; TAILQ_FOREACH(e, &pft_table, link) if (e->index == idx) return (e); return (NULL); } static struct pfa_entry * pfa_table_find(u_int idx) { struct pfa_entry *e; TAILQ_FOREACH(e, &pfa_table, link) if (e->index == idx) return (e); return (NULL); } static struct pfl_entry * pfl_table_find(u_int idx) { struct pfl_entry *e; TAILQ_FOREACH(e, &pfl_table, link) if (e->index == idx) return (e); return (NULL); } static int pfi_refresh(void) { struct pfioc_iface io; struct pfi_kif *p = NULL; struct pfi_entry *e; int i, numifs = 1; if (started && this_tick <= pf_tick) return (0); while (!TAILQ_EMPTY(&pfi_table)) { e = TAILQ_FIRST(&pfi_table); TAILQ_REMOVE(&pfi_table, e, link); free(e); } bzero(&io, sizeof(io)); io.pfiio_esize = sizeof(struct pfi_kif); for (;;) { p = reallocf(p, numifs * sizeof(struct pfi_kif)); if (p == NULL) { syslog(LOG_ERR, "pfi_refresh(): reallocf() numifs=%d: %s", numifs, strerror(errno)); goto err2; } io.pfiio_size = numifs; io.pfiio_buffer = p; if (ioctl(dev, DIOCIGETIFACES, &io)) { syslog(LOG_ERR, "pfi_refresh(): ioctl(): %s", strerror(errno)); goto err2; } if (numifs >= io.pfiio_size) break; numifs = io.pfiio_size; } for (i = 0; i < numifs; i++) { e = malloc(sizeof(struct pfi_entry)); if (e == NULL) goto err1; e->index = i + 1; memcpy(&e->pfi, p+i, sizeof(struct pfi_kif)); TAILQ_INSERT_TAIL(&pfi_table, e, link); } pfi_table_age = time(NULL); pfi_table_count = numifs; pf_tick = this_tick; free(p); return (0); err1: while (!TAILQ_EMPTY(&pfi_table)) { e = TAILQ_FIRST(&pfi_table); TAILQ_REMOVE(&pfi_table, e, link); free(e); } err2: free(p); return(-1); } static int pfq_refresh(void) { struct pfioc_altq pa; struct pfq_entry *e; int i, numqs, ticket; if (started && this_tick <= pf_tick) return (0); while (!TAILQ_EMPTY(&pfq_table)) { e = TAILQ_FIRST(&pfq_table); TAILQ_REMOVE(&pfq_table, e, link); free(e); } bzero(&pa, sizeof(pa)); - + if (ioctl(dev, DIOCGETALTQS, &pa)) { syslog(LOG_ERR, "pfq_refresh: ioctl(DIOCGETALTQS): %s", strerror(errno)); return (-1); } numqs = pa.nr; ticket = pa.ticket; for (i = 0; i < numqs; i++) { e = malloc(sizeof(struct pfq_entry)); if (e == NULL) { syslog(LOG_ERR, "pfq_refresh(): " "malloc(): %s", strerror(errno)); goto err; } pa.ticket = ticket; pa.nr = i; if (ioctl(dev, DIOCGETALTQ, &pa)) { syslog(LOG_ERR, "pfq_refresh(): " "ioctl(DIOCGETALTQ): %s", strerror(errno)); goto err; } if (pa.altq.qid > 0) { memcpy(&e->altq, &pa.altq, sizeof(struct pf_altq)); e->index = pa.altq.qid; pfq_table_count = i; INSERT_OBJECT_INT_LINK_INDEX(e, &pfq_table, link, index); } } - + pfq_table_age = time(NULL); pf_tick = this_tick; return (0); err: free(e); while (!TAILQ_EMPTY(&pfq_table)) { e = TAILQ_FIRST(&pfq_table); TAILQ_REMOVE(&pfq_table, e, link); free(e); } return(-1); } static int pfs_refresh(void) { if (started && this_tick <= pf_tick) return (0); bzero(&pfs, sizeof(struct pf_status)); if (ioctl(dev, DIOCGETSTATUS, &pfs)) { syslog(LOG_ERR, "pfs_refresh(): ioctl(): %s", strerror(errno)); return (-1); } pf_tick = this_tick; return (0); } static int pft_refresh(void) { struct pfioc_table io; struct pfr_tstats *t = NULL; struct pft_entry *e; int i, numtbls = 1; if (started && this_tick <= pf_tick) return (0); while (!TAILQ_EMPTY(&pft_table)) { e = TAILQ_FIRST(&pft_table); TAILQ_REMOVE(&pft_table, e, link); free(e); } bzero(&io, sizeof(io)); io.pfrio_esize = sizeof(struct pfr_tstats); for (;;) { t = reallocf(t, numtbls * sizeof(struct pfr_tstats)); if (t == NULL) { syslog(LOG_ERR, "pft_refresh(): reallocf() numtbls=%d: %s", numtbls, strerror(errno)); goto err2; } io.pfrio_size = numtbls; io.pfrio_buffer = t; if (ioctl(dev, DIOCRGETTSTATS, &io)) { syslog(LOG_ERR, "pft_refresh(): ioctl(): %s", strerror(errno)); goto err2; } if (numtbls >= io.pfrio_size) break; numtbls = io.pfrio_size; } for (i = 0; i < numtbls; i++) { e = malloc(sizeof(struct pft_entry)); if (e == NULL) goto err1; e->index = i + 1; memcpy(&e->pft, t+i, sizeof(struct pfr_tstats)); TAILQ_INSERT_TAIL(&pft_table, e, link); } pft_table_age = time(NULL); pft_table_count = numtbls; pf_tick = this_tick; free(t); return (0); err1: while (!TAILQ_EMPTY(&pft_table)) { e = TAILQ_FIRST(&pft_table); TAILQ_REMOVE(&pft_table, e, link); free(e); } err2: free(t); return(-1); } static int pfa_table_addrs(u_int sidx, struct pfr_table *pt) { struct pfioc_table io; struct pfr_astats *t = NULL; struct pfa_entry *e; int i, numaddrs = 1; if (pt == NULL) return (-1); memset(&io, 0, sizeof(io)); strlcpy(io.pfrio_table.pfrt_name, pt->pfrt_name, sizeof(io.pfrio_table.pfrt_name)); for (;;) { t = reallocf(t, numaddrs * sizeof(struct pfr_astats)); if (t == NULL) { syslog(LOG_ERR, "pfa_table_addrs(): reallocf(): %s", strerror(errno)); numaddrs = -1; goto error; } memset(t, 0, sizeof(*t)); io.pfrio_size = numaddrs; io.pfrio_buffer = t; io.pfrio_esize = sizeof(struct pfr_astats); if (ioctl(dev, DIOCRGETASTATS, &io)) { syslog(LOG_ERR, "pfa_table_addrs(): ioctl() on %s: %s", pt->pfrt_name, strerror(errno)); numaddrs = -1; break; } if (numaddrs >= io.pfrio_size) break; numaddrs = io.pfrio_size; } for (i = 0; i < numaddrs; i++) { if ((t + i)->pfras_a.pfra_af != AF_INET && (t + i)->pfras_a.pfra_af != AF_INET6) { numaddrs = i; break; } e = (struct pfa_entry *)malloc(sizeof(struct pfa_entry)); if (e == NULL) { syslog(LOG_ERR, "pfa_table_addrs(): malloc(): %s", strerror(errno)); numaddrs = -1; break; } e->index = sidx + i; memcpy(&e->pfas, t + i, sizeof(struct pfr_astats)); TAILQ_INSERT_TAIL(&pfa_table, e, link); } free(t); error: return (numaddrs); } static int pfa_refresh(void) { struct pfioc_table io; struct pfr_table *pt = NULL, *it = NULL; struct pfa_entry *e; int i, numtbls = 1, cidx, naddrs; if (started && this_tick <= pf_tick) return (0); while (!TAILQ_EMPTY(&pfa_table)) { e = TAILQ_FIRST(&pfa_table); TAILQ_REMOVE(&pfa_table, e, link); free(e); } memset(&io, 0, sizeof(io)); io.pfrio_esize = sizeof(struct pfr_table); for (;;) { pt = reallocf(pt, numtbls * sizeof(struct pfr_table)); if (pt == NULL) { syslog(LOG_ERR, "pfa_refresh(): reallocf() %s", strerror(errno)); return (-1); } memset(pt, 0, sizeof(*pt)); io.pfrio_size = numtbls; io.pfrio_buffer = pt; if (ioctl(dev, DIOCRGETTABLES, &io)) { syslog(LOG_ERR, "pfa_refresh(): ioctl(): %s", strerror(errno)); goto err2; } if (numtbls >= io.pfrio_size) break; numtbls = io.pfrio_size; } cidx = 1; for (it = pt, i = 0; i < numtbls; it++, i++) { /* * Skip the table if not active - ioctl(DIOCRGETASTATS) will * return ESRCH for this entry anyway. */ if (!(it->pfrt_flags & PFR_TFLAG_ACTIVE)) continue; if ((naddrs = pfa_table_addrs(cidx, it)) < 0) goto err1; cidx += naddrs; } pfa_table_age = time(NULL); pfa_table_count = cidx; pf_tick = this_tick; free(pt); return (0); err1: while (!TAILQ_EMPTY(&pfa_table)) { e = TAILQ_FIRST(&pfa_table); TAILQ_REMOVE(&pfa_table, e, link); free(e); } err2: free(pt); return (-1); } static int pfl_scan_ruleset(const char *path) { struct pfioc_rule pr; struct pfl_entry *e; u_int32_t nr, i; bzero(&pr, sizeof(pr)); strlcpy(pr.anchor, path, sizeof(pr.anchor)); pr.rule.action = PF_PASS; if (ioctl(dev, DIOCGETRULES, &pr)) { syslog(LOG_ERR, "pfl_scan_ruleset: ioctl(DIOCGETRULES): %s", strerror(errno)); goto err; } for (nr = pr.nr, i = 0; i < nr; i++) { pr.nr = i; if (ioctl(dev, DIOCGETRULE, &pr)) { syslog(LOG_ERR, "pfl_scan_ruleset: ioctl(DIOCGETRULE):" " %s", strerror(errno)); goto err; } if (pr.rule.label[0]) { e = (struct pfl_entry *)malloc(sizeof(*e)); if (e == NULL) goto err; strlcpy(e->name, path, sizeof(e->name)); if (path[0]) strlcat(e->name, "/", sizeof(e->name)); strlcat(e->name, pr.rule.label, sizeof(e->name)); e->evals = pr.rule.evaluations; e->bytes[IN] = pr.rule.bytes[IN]; e->bytes[OUT] = pr.rule.bytes[OUT]; e->pkts[IN] = pr.rule.packets[IN]; e->pkts[OUT] = pr.rule.packets[OUT]; e->index = ++pfl_table_count; TAILQ_INSERT_TAIL(&pfl_table, e, link); } } return (0); err: return (-1); } static int pfl_walk_rulesets(const char *path) { struct pfioc_ruleset prs; char newpath[MAXPATHLEN]; u_int32_t nr, i; if (pfl_scan_ruleset(path)) goto err; bzero(&prs, sizeof(prs)); strlcpy(prs.path, path, sizeof(prs.path)); if (ioctl(dev, DIOCGETRULESETS, &prs)) { syslog(LOG_ERR, "pfl_walk_rulesets: ioctl(DIOCGETRULESETS): %s", strerror(errno)); goto err; } for (nr = prs.nr, i = 0; i < nr; i++) { prs.nr = i; if (ioctl(dev, DIOCGETRULESET, &prs)) { syslog(LOG_ERR, "pfl_walk_rulesets: ioctl(DIOCGETRULESET):" " %s", strerror(errno)); goto err; } if (strcmp(prs.name, PF_RESERVED_ANCHOR) == 0) continue; strlcpy(newpath, path, sizeof(newpath)); if (path[0]) strlcat(newpath, "/", sizeof(newpath)); strlcat(newpath, prs.name, sizeof(newpath)); if (pfl_walk_rulesets(newpath)) goto err; } return (0); err: return (-1); } static int pfl_refresh(void) { struct pfl_entry *e; if (started && this_tick <= pf_tick) return (0); while (!TAILQ_EMPTY(&pfl_table)) { e = TAILQ_FIRST(&pfl_table); TAILQ_REMOVE(&pfl_table, e, link); free(e); } pfl_table_count = 0; if (pfl_walk_rulesets("")) goto err; pfl_table_age = time(NULL); pf_tick = this_tick; return (0); err: while (!TAILQ_EMPTY(&pfl_table)) { e = TAILQ_FIRST(&pfl_table); TAILQ_REMOVE(&pfl_table, e, link); free(e); } pfl_table_count = 0; return (-1); } /* * check whether altq support is enabled in kernel */ static int altq_is_enabled(int pfdev) { struct pfioc_altq pa; errno = 0; if (ioctl(pfdev, DIOCGETALTQS, &pa)) { if (errno == ENODEV) { syslog(LOG_INFO, "No ALTQ support in kernel\n" "ALTQ related functions disabled\n"); return (0); - } else + } else syslog(LOG_ERR, "DIOCGETALTQS returned an error: %s", strerror(errno)); return (-1); } return (1); } /* * Implement the bsnmpd module interface */ static int pf_init(struct lmodule *mod, int __unused argc, char __unused *argv[]) { module = mod; if ((dev = open("/dev/pf", O_RDONLY)) == -1) { syslog(LOG_ERR, "pf_init(): open(): %s\n", strerror(errno)); return (-1); } if ((altq_enabled = altq_is_enabled(dev)) == -1) { syslog(LOG_ERR, "pf_init(): altq test failed"); return (-1); } - + /* Prepare internal state */ TAILQ_INIT(&pfi_table); TAILQ_INIT(&pfq_table); TAILQ_INIT(&pft_table); TAILQ_INIT(&pfa_table); TAILQ_INIT(&pfl_table); pfi_refresh(); if (altq_enabled) { pfq_refresh(); } pfs_refresh(); pft_refresh(); pfa_refresh(); pfl_refresh(); started = 1; return (0); } static int pf_fini(void) { struct pfi_entry *i1, *i2; struct pfq_entry *q1, *q2; struct pft_entry *t1, *t2; struct pfa_entry *a1, *a2; struct pfl_entry *l1, *l2; /* Empty the list of interfaces */ i1 = TAILQ_FIRST(&pfi_table); while (i1 != NULL) { i2 = TAILQ_NEXT(i1, link); free(i1); i1 = i2; } /* List of queues */ q1 = TAILQ_FIRST(&pfq_table); while (q1 != NULL) { q2 = TAILQ_NEXT(q1, link); free(q1); q1 = q2; } /* List of tables */ t1 = TAILQ_FIRST(&pft_table); while (t1 != NULL) { t2 = TAILQ_NEXT(t1, link); free(t1); t1 = t2; } /* List of table addresses */ a1 = TAILQ_FIRST(&pfa_table); while (a1 != NULL) { a2 = TAILQ_NEXT(a1, link); free(a1); a1 = a2; } /* And the list of labeled filter rules */ l1 = TAILQ_FIRST(&pfl_table); while (l1 != NULL) { l2 = TAILQ_NEXT(l1, link); free(l1); l1 = l2; } close(dev); return (0); } static void pf_dump(void) { pfi_refresh(); if (altq_enabled) { pfq_refresh(); } pft_refresh(); pfa_refresh(); pfl_refresh(); syslog(LOG_ERR, "Dump: pfi_table_age = %jd", (intmax_t)pfi_table_age); syslog(LOG_ERR, "Dump: pfi_table_count = %d", pfi_table_count); - + syslog(LOG_ERR, "Dump: pfq_table_age = %jd", (intmax_t)pfq_table_age); syslog(LOG_ERR, "Dump: pfq_table_count = %d", pfq_table_count); syslog(LOG_ERR, "Dump: pft_table_age = %jd", (intmax_t)pft_table_age); syslog(LOG_ERR, "Dump: pft_table_count = %d", pft_table_count); syslog(LOG_ERR, "Dump: pfa_table_age = %jd", (intmax_t)pfa_table_age); syslog(LOG_ERR, "Dump: pfa_table_count = %d", pfa_table_count); syslog(LOG_ERR, "Dump: pfl_table_age = %jd", (intmax_t)pfl_table_age); syslog(LOG_ERR, "Dump: pfl_table_count = %d", pfl_table_count); } const struct snmp_module config = { .comment = "This module implements a MIB for the pf packet filter.", .init = pf_init, .fini = pf_fini, .tree = pf_ctree, .dump = pf_dump, .tree_size = pf_CTREE_SIZE, }; Index: head/usr.sbin/bsnmpd/modules/snmp_target/Makefile =================================================================== --- head/usr.sbin/bsnmpd/modules/snmp_target/Makefile (revision 310647) +++ head/usr.sbin/bsnmpd/modules/snmp_target/Makefile (revision 310648) @@ -1,20 +1,20 @@ # $FreeBSD$ # # Author: Shteryana Shopova CONTRIB= ${.CURDIR}/../../../../contrib/bsnmp .PATH: ${CONTRIB}/snmp_target MOD= target SRCS= target_snmp.c XSYM= snmpTargetMIB snmpNotificationMIB snmpUDPDomain MAN= snmp_target.3 CFLAGS+= -I${CONTRIB}/lib -I${CONTRIB}/snmpd -DSNMPTREE_TYPES CFLAGS+= -DHAVE_ERR_H -DHAVE_GETADDRINFO -DHAVE_STRLCPY -DHAVE_SYS_TREE_H DEFS= ${MOD}_tree.def -BMIBS= +BMIBS= .include Index: head/usr.sbin/bsnmpd/modules/snmp_usm/Makefile =================================================================== --- head/usr.sbin/bsnmpd/modules/snmp_usm/Makefile (revision 310647) +++ head/usr.sbin/bsnmpd/modules/snmp_usm/Makefile (revision 310648) @@ -1,22 +1,22 @@ # $FreeBSD$ # # Author: Shteryana Shopova CONTRIB= ${.CURDIR}/../../../../contrib/bsnmp .PATH: ${CONTRIB}/snmp_usm MOD= usm SRCS= usm_snmp.c XSYM= snmpUsmMIB usmNoAuthProtocol usmHMACMD5AuthProtocol \ usmHMACSHAAuthProtocol usmNoPrivProtocol usmDESPrivProtocol \ usmAesCfb128Protocol usmUserSecurityName MAN= snmp_usm.3 CFLAGS+= -I${CONTRIB}/lib -I${CONTRIB}/snmpd -DSNMPTREE_TYPES CFLAGS+= -DHAVE_ERR_H -DHAVE_GETADDRINFO -DHAVE_STRLCPY -DHAVE_SYS_TREE_H DEFS= ${MOD}_tree.def -BMIBS= +BMIBS= .include Index: head/usr.sbin/bsnmpd/modules/snmp_vacm/Makefile =================================================================== --- head/usr.sbin/bsnmpd/modules/snmp_vacm/Makefile (revision 310647) +++ head/usr.sbin/bsnmpd/modules/snmp_vacm/Makefile (revision 310648) @@ -1,20 +1,20 @@ # $FreeBSD$ # # Author: Shteryana Shopova CONTRIB= ${.CURDIR}/../../../../contrib/bsnmp .PATH: ${CONTRIB}/snmp_vacm MOD= vacm SRCS= vacm_snmp.c XSYM= snmpVacmMIB MAN= snmp_vacm.3 CFLAGS+= -I${CONTRIB}/lib -I${CONTRIB}/snmpd -DSNMPTREE_TYPES CFLAGS+= -DHAVE_ERR_H -DHAVE_GETADDRINFO -DHAVE_STRLCPY -DHAVE_SYS_TREE_H DEFS= ${MOD}_tree.def -BMIBS= +BMIBS= .include Index: head/usr.sbin/bsnmpd/modules/snmp_wlan/wlan_snmp.c =================================================================== --- head/usr.sbin/bsnmpd/modules/snmp_wlan/wlan_snmp.c (revision 310647) +++ head/usr.sbin/bsnmpd/modules/snmp_wlan/wlan_snmp.c (revision 310648) @@ -1,4513 +1,4513 @@ /*- * Copyright (c) 2010 The FreeBSD Foundation * All rights reserved. * * This software was developed by Shteryana Sotirova Shopova under * sponsorship from the FreeBSD Foundation. * * 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 THE 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 THE 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. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "wlan_tree.h" #include "wlan_snmp.h" #include "wlan_oid.h" static struct lmodule *wlan_module; /* For the registration. */ static const struct asn_oid oid_wlan = OIDX_begemotWlan; /* The registration. */ static uint reg_wlan; /* Periodic timer for polling the module's data. */ static void *wlan_data_timer; /* * Poll data from kernel every 15 minutes unless explicitly requested by an * SNMP client. * XXX: make that configurable. */ static int wlan_poll_ticks = (15 * 60) * 100; /* The age of each table. */ #define WLAN_LIST_MAXAGE 5 static time_t wlan_iflist_age; static time_t wlan_peerlist_age; static time_t wlan_chanlist_age; static time_t wlan_roamlist_age; static time_t wlan_tx_paramlist_age; static time_t wlan_scanlist_age; static time_t wlan_maclist_age; static time_t wlan_mrlist_age; /* * The list of all virtual wireless interfaces - sorted by name. */ SLIST_HEAD(wlan_ifaces, wlan_iface); static struct wlan_ifaces wlan_ifaces = SLIST_HEAD_INITIALIZER(wlan_ifaces); static struct wlan_config wlan_config; /* Forward declarations */ static int bits_get(struct snmp_value *, const u_char *, ssize_t); static int wlan_add_wif(struct wlan_iface *); static void wlan_delete_wif(struct wlan_iface *); static int wlan_attach_newif(struct mibif *); static int wlan_iface_create(struct wlan_iface *); static int wlan_iface_destroy(struct wlan_iface *); static struct wlan_iface * wlan_new_wif(char *); static void wlan_free_interface(struct wlan_iface *); static void wlan_free_iflist(void); static void wlan_free_peerlist(struct wlan_iface *); static void wlan_scan_free_results(struct wlan_iface *); static void wlan_mac_free_maclist(struct wlan_iface *); static void wlan_mesh_free_routes(struct wlan_iface *); static int wlan_update_interface(struct wlan_iface *); static void wlan_update_interface_list(void); static void wlan_update_peers(void); static void wlan_update_channels(void); static void wlan_update_roam_params(void); static void wlan_update_tx_params(void); static void wlan_scan_update_results(void); static void wlan_mac_update_aclmacs(void); static void wlan_mesh_update_routes(void); static struct wlan_iface * wlan_find_interface(const char *); static struct wlan_peer * wlan_find_peer(struct wlan_iface *, uint8_t *); static struct ieee80211_channel* wlan_find_channel(struct wlan_iface *, uint32_t); static struct wlan_scan_result * wlan_scan_find_result(struct wlan_iface *, uint8_t *, uint8_t *); static struct wlan_mac_mac * wlan_mac_find_mac(struct wlan_iface *, uint8_t *); static struct wlan_mesh_route * wlan_mesh_find_route(struct wlan_iface *, uint8_t *); static struct wlan_iface * wlan_first_interface(void); static struct wlan_iface * wlan_next_interface(struct wlan_iface *); static struct wlan_iface * wlan_mesh_first_interface(void); static struct wlan_iface * wlan_mesh_next_interface(struct wlan_iface *); static struct wlan_iface * wlan_get_interface(const struct asn_oid *, uint); static struct wlan_iface * wlan_get_snmp_interface(const struct asn_oid *, uint); static struct wlan_peer * wlan_get_peer(const struct asn_oid *, uint, struct wlan_iface **); static struct ieee80211_channel *wlan_get_channel(const struct asn_oid *, uint, struct wlan_iface **); static struct ieee80211_roamparam *wlan_get_roam_param(const struct asn_oid *, uint, struct wlan_iface **); static struct ieee80211_txparam *wlan_get_tx_param(const struct asn_oid *, uint, struct wlan_iface **, uint32_t *); static struct wlan_scan_result *wlan_get_scanr(const struct asn_oid *, uint, struct wlan_iface **); static struct wlan_mac_mac * wlan_get_acl_mac(const struct asn_oid *, uint, struct wlan_iface **); static struct wlan_iface * wlan_mesh_get_iface(const struct asn_oid *, uint); static struct wlan_peer * wlan_mesh_get_peer(const struct asn_oid *, uint, struct wlan_iface **); static struct wlan_mesh_route * wlan_mesh_get_route(const struct asn_oid *, uint, struct wlan_iface **); static struct wlan_iface * wlan_get_next_interface(const struct asn_oid *, uint); static struct wlan_iface * wlan_get_next_snmp_interface(const struct asn_oid *, uint); static struct wlan_peer * wlan_get_next_peer(const struct asn_oid *, uint, struct wlan_iface **); static struct ieee80211_channel *wlan_get_next_channel(const struct asn_oid *, uint, struct wlan_iface **); static struct ieee80211_roamparam *wlan_get_next_roam_param(const struct asn_oid *, uint sub, struct wlan_iface **, uint32_t *); static struct ieee80211_txparam *wlan_get_next_tx_param(const struct asn_oid *, uint, struct wlan_iface **, uint32_t *); static struct wlan_scan_result *wlan_get_next_scanr(const struct asn_oid *, uint , struct wlan_iface **); static struct wlan_mac_mac * wlan_get_next_acl_mac(const struct asn_oid *, uint, struct wlan_iface **); static struct wlan_iface * wlan_mesh_get_next_iface(const struct asn_oid *, uint); static struct wlan_peer * wlan_mesh_get_next_peer(const struct asn_oid *, uint, struct wlan_iface **); static struct wlan_mesh_route * wlan_mesh_get_next_route(const struct asn_oid *, uint sub, struct wlan_iface **); static uint8_t *wlan_get_ifname(const struct asn_oid *, uint, uint8_t *); static int wlan_mac_index_decode(const struct asn_oid *, uint, char *, uint8_t *); static int wlan_channel_index_decode(const struct asn_oid *, uint, char *, uint32_t *); static int wlan_phy_index_decode(const struct asn_oid *, uint, char *, uint32_t *); static int wlan_scanr_index_decode(const struct asn_oid *oid, uint sub, char *wname, uint8_t *ssid, uint8_t *bssid); static void wlan_append_ifindex(struct asn_oid *, uint, const struct wlan_iface *); static void wlan_append_mac_index(struct asn_oid *, uint, char *, uint8_t *); static void wlan_append_channel_index(struct asn_oid *, uint, const struct wlan_iface *, const struct ieee80211_channel *); static void wlan_append_phy_index(struct asn_oid *, uint, char *, uint32_t); static void wlan_append_scanr_index(struct asn_oid *, uint, char *, uint8_t *, uint8_t *); static int wlan_acl_mac_set_status(struct snmp_context *, struct snmp_value *, uint); static int wlan_mesh_route_set_status(struct snmp_context *, struct snmp_value *, uint); static int32_t wlan_get_channel_type(struct ieee80211_channel *); static int wlan_scan_compare_result(struct wlan_scan_result *, struct wlan_scan_result *); static int wlan_mac_delete_mac(struct wlan_iface *, struct wlan_mac_mac *); static int wlan_mesh_delete_route(struct wlan_iface *, struct wlan_mesh_route *); /* * The module's GET/SET data hooks per each table or group of objects as * required by bsnmpd(1). */ int op_wlan_iface(struct snmp_context *ctx, struct snmp_value *val, uint32_t sub, uint32_t iidx __unused, enum snmp_op op) { int rc; char wname[IFNAMSIZ]; struct wlan_iface *wif; wlan_update_interface_list(); switch (op) { case SNMP_OP_GET: if ((wif = wlan_get_snmp_interface(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_GETNEXT: if ((wif = wlan_get_next_snmp_interface(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); wlan_append_ifindex(&val->var, sub, wif); break; case SNMP_OP_SET: if ((wif = wlan_get_snmp_interface(&val->var, sub)) == NULL) { if (val->var.subs[sub - 1] != LEAF_wlanIfaceName) return (SNMP_ERR_NOSUCHNAME); if (wlan_get_ifname(&val->var, sub, wname) == NULL) return (SNMP_ERR_INCONS_VALUE); if ((wif = wlan_new_wif(wname)) == NULL) return (SNMP_ERR_GENERR); wif->internal = 1; } if (wif->status == RowStatus_active && val->var.subs[sub - 1] != LEAF_wlanIfaceStatus && val->var.subs[sub - 1] != LEAF_wlanIfaceState) return (SNMP_ERR_INCONS_VALUE); switch (val->var.subs[sub - 1]) { case LEAF_wlanIfaceIndex: return (SNMP_ERR_NOT_WRITEABLE); case LEAF_wlanIfaceName: if (val->v.octetstring.len >= IFNAMSIZ) return (SNMP_ERR_INCONS_VALUE); if ((ctx->scratch->ptr1 = malloc(IFNAMSIZ)) == NULL) return (SNMP_ERR_GENERR); strlcpy(ctx->scratch->ptr1, wif->wname, IFNAMSIZ); memcpy(wif->wname, val->v.octetstring.octets, val->v.octetstring.len); wif->wname[val->v.octetstring.len] = '\0'; return (SNMP_ERR_NOERROR); case LEAF_wlanParentIfName: if (val->v.octetstring.len >= IFNAMSIZ) return (SNMP_ERR_INCONS_VALUE); if ((ctx->scratch->ptr1 = malloc(IFNAMSIZ)) == NULL) return (SNMP_ERR_GENERR); strlcpy(ctx->scratch->ptr1, wif->pname, IFNAMSIZ); memcpy(wif->pname, val->v.octetstring.octets, val->v.octetstring.len); wif->pname[val->v.octetstring.len] = '\0'; return (SNMP_ERR_NOERROR); case LEAF_wlanIfaceOperatingMode: ctx->scratch->int1 = wif->mode; wif->mode = val->v.integer; return (SNMP_ERR_NOERROR); case LEAF_wlanIfaceFlags: if (val->v.octetstring.len > sizeof(wif->flags)) return (SNMP_ERR_INCONS_VALUE); ctx->scratch->ptr1 = malloc(sizeof(wif->flags)); if (ctx->scratch->ptr1 == NULL) return (SNMP_ERR_GENERR); memcpy(ctx->scratch->ptr1, (uint8_t *)&wif->flags, sizeof(wif->flags)); memcpy((uint8_t *)&wif->flags, val->v.octetstring.octets, sizeof(wif->flags)); return (SNMP_ERR_NOERROR); case LEAF_wlanIfaceBssid: if (val->v.octetstring.len != IEEE80211_ADDR_LEN) return (SNMP_ERR_INCONS_VALUE); ctx->scratch->ptr1 = malloc(IEEE80211_ADDR_LEN); if (ctx->scratch->ptr1 == NULL) return (SNMP_ERR_GENERR); memcpy(ctx->scratch->ptr1, wif->dbssid, IEEE80211_ADDR_LEN); memcpy(wif->dbssid, val->v.octetstring.octets, IEEE80211_ADDR_LEN); return (SNMP_ERR_NOERROR); case LEAF_wlanIfaceLocalAddress: if (val->v.octetstring.len != IEEE80211_ADDR_LEN) return (SNMP_ERR_INCONS_VALUE); ctx->scratch->ptr1 = malloc(IEEE80211_ADDR_LEN); if (ctx->scratch->ptr1 == NULL) return (SNMP_ERR_GENERR); memcpy(ctx->scratch->ptr1, wif->dlmac, IEEE80211_ADDR_LEN); memcpy(wif->dlmac, val->v.octetstring.octets, IEEE80211_ADDR_LEN); return (SNMP_ERR_NOERROR); case LEAF_wlanIfaceStatus: ctx->scratch->int1 = wif->status; wif->status = val->v.integer; if (wif->status == RowStatus_active) { rc = wlan_iface_create(wif); /* XXX */ if (rc != SNMP_ERR_NOERROR) { wif->status = ctx->scratch->int1; return (rc); } } else if (wif->status == RowStatus_destroy) return (wlan_iface_destroy(wif)); else wif->status = RowStatus_notReady; return (SNMP_ERR_NOERROR); case LEAF_wlanIfaceState: ctx->scratch->int1 = wif->state; wif->state = val->v.integer; if (wif->status == RowStatus_active) if (wlan_config_state(wif, 1) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); } abort(); case SNMP_OP_ROLLBACK: if ((wif = wlan_get_snmp_interface(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); switch (val->var.subs[sub - 1]) { case LEAF_wlanIfaceName: strlcpy(wif->wname, ctx->scratch->ptr1, IFNAMSIZ); free(ctx->scratch->ptr1); break; case LEAF_wlanParentIfName: strlcpy(wif->pname, ctx->scratch->ptr1, IFNAMSIZ); free(ctx->scratch->ptr1); break; case LEAF_wlanIfaceOperatingMode: wif->mode = ctx->scratch->int1; break; case LEAF_wlanIfaceFlags: memcpy((uint8_t *)&wif->flags, ctx->scratch->ptr1, sizeof(wif->flags)); free(ctx->scratch->ptr1); break; case LEAF_wlanIfaceBssid: memcpy(wif->dbssid, ctx->scratch->ptr1, IEEE80211_ADDR_LEN); free(ctx->scratch->ptr1); break; case LEAF_wlanIfaceLocalAddress: memcpy(wif->dlmac, ctx->scratch->ptr1, IEEE80211_ADDR_LEN); free(ctx->scratch->ptr1); break; case LEAF_wlanIfaceStatus: wif->status = ctx->scratch->int1; if (ctx->scratch->int1 == RowStatus_active) return (SNMP_ERR_GENERR); /* XXX: FIXME */ else if (wif->internal != 0) return (wlan_iface_destroy(wif)); break; case LEAF_wlanIfaceState: wif->state = ctx->scratch->int1; if (wif->status == RowStatus_active) if (wlan_config_state(wif, 1) < 0) return (SNMP_ERR_GENERR); break; } return (SNMP_ERR_NOERROR); case SNMP_OP_COMMIT: switch (val->var.subs[sub - 1]) { case LEAF_wlanIfaceName: case LEAF_wlanParentIfName: case LEAF_wlanIfaceFlags: case LEAF_wlanIfaceBssid: case LEAF_wlanIfaceLocalAddress: free(ctx->scratch->ptr1); /* FALLTHROUGH */ default: return (SNMP_ERR_NOERROR); } default: abort(); } switch (val->var.subs[sub - 1]) { case LEAF_wlanIfaceIndex: val->v.integer = wif->index; return (SNMP_ERR_NOERROR); case LEAF_wlanIfaceName: return (string_get(val, wif->wname, -1)); case LEAF_wlanParentIfName: return (string_get(val, wif->pname, -1)); case LEAF_wlanIfaceOperatingMode: val->v.integer = wif->mode; return (SNMP_ERR_NOERROR); case LEAF_wlanIfaceFlags: return (bits_get(val, (uint8_t *)&wif->flags, sizeof(wif->flags))); case LEAF_wlanIfaceBssid: return (string_get(val, wif->dbssid, IEEE80211_ADDR_LEN)); case LEAF_wlanIfaceLocalAddress: return (string_get(val, wif->dlmac, IEEE80211_ADDR_LEN)); case LEAF_wlanIfaceStatus: val->v.integer = wif->status; return (SNMP_ERR_NOERROR); case LEAF_wlanIfaceState: val->v.integer = wif->state; return (SNMP_ERR_NOERROR); } abort(); } int op_wlan_if_parent(struct snmp_context *ctx __unused, struct snmp_value *val, uint32_t sub, uint32_t iidx __unused, enum snmp_op op) { struct wlan_iface *wif; wlan_update_interface_list(); switch (op) { case SNMP_OP_GET: if ((wif = wlan_get_interface(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_GETNEXT: if ((wif = wlan_get_next_interface(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); wlan_append_ifindex(&val->var, sub, wif); break; case SNMP_OP_SET: return (SNMP_ERR_NOT_WRITEABLE); case SNMP_OP_COMMIT: /* FALLTHROUGH */ case SNMP_OP_ROLLBACK: /* FALLTHROUGH */ default: abort(); } switch (val->var.subs[sub - 1]) { case LEAF_wlanIfParentDriverCapabilities: return (bits_get(val, (uint8_t *)&wif->drivercaps, sizeof(wif->drivercaps))); case LEAF_wlanIfParentCryptoCapabilities: return (bits_get(val, (uint8_t *)&wif->cryptocaps, sizeof(wif->cryptocaps))); case LEAF_wlanIfParentHTCapabilities: return (bits_get(val, (uint8_t *)&wif->htcaps, sizeof(wif->htcaps))); } abort(); } int op_wlan_iface_config(struct snmp_context *ctx, struct snmp_value *val, uint32_t sub, uint32_t iidx __unused, enum snmp_op op) { int intval, vlen, rc; char *strval; struct wlan_iface *wif; wlan_update_interface_list(); switch (op) { case SNMP_OP_GET: if ((wif = wlan_get_interface(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); goto get_config; case SNMP_OP_GETNEXT: if ((wif = wlan_get_next_interface(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); wlan_append_ifindex(&val->var, sub, wif); goto get_config; case SNMP_OP_SET: if ((wif = wlan_get_interface(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); intval = val->v.integer; strval = NULL; vlen = 0; /* Simple sanity checks & save old data. */ switch (val->var.subs[sub - 1]) { case LEAF_wlanIfaceCountryCode: if (val->v.octetstring.len != WLAN_COUNTRY_CODE_SIZE) return (SNMP_ERR_INCONS_VALUE); break; case LEAF_wlanIfaceDesiredSsid: if (val->v.octetstring.len > IEEE80211_NWID_LEN) return (SNMP_ERR_INCONS_VALUE); break; case LEAF_wlanIfaceDesiredBssid: if (val->v.octetstring.len != IEEE80211_ADDR_LEN) return (SNMP_ERR_INCONS_VALUE); break; case LEAF_wlanIfacePacketBurst: ctx->scratch->int1 = wif->packet_burst; break; case LEAF_wlanIfaceRegDomain: ctx->scratch->int1 = wif->reg_domain; break; case LEAF_wlanIfaceDesiredChannel: ctx->scratch->int1 = wif->desired_channel; break; case LEAF_wlanIfaceDynamicFreqSelection: ctx->scratch->int1 = wif->dyn_frequency; break; case LEAF_wlanIfaceFastFrames: ctx->scratch->int1 = wif->fast_frames; break; case LEAF_wlanIfaceDturbo: ctx->scratch->int1 = wif->dturbo; break; case LEAF_wlanIfaceTxPower: ctx->scratch->int1 = wif->tx_power; break; case LEAF_wlanIfaceFragmentThreshold: ctx->scratch->int1 = wif->frag_threshold; break; case LEAF_wlanIfaceRTSThreshold: ctx->scratch->int1 = wif->rts_threshold; break; case LEAF_wlanIfaceWlanPrivacySubscribe: ctx->scratch->int1 = wif->priv_subscribe; break; case LEAF_wlanIfaceBgScan: ctx->scratch->int1 = wif->bg_scan; break; case LEAF_wlanIfaceBgScanIdle: ctx->scratch->int1 = wif->bg_scan_idle; break; case LEAF_wlanIfaceBgScanInterval: ctx->scratch->int1 = wif->bg_scan_interval; break; case LEAF_wlanIfaceBeaconMissedThreshold: ctx->scratch->int1 = wif->beacons_missed; break; case LEAF_wlanIfaceRoamingMode: ctx->scratch->int1 = wif->roam_mode; break; case LEAF_wlanIfaceDot11d: ctx->scratch->int1 = wif->dot11d; break; case LEAF_wlanIfaceDot11h: ctx->scratch->int1 = wif->dot11h; break; case LEAF_wlanIfaceDynamicWds: ctx->scratch->int1 = wif->dynamic_wds; break; case LEAF_wlanIfacePowerSave: ctx->scratch->int1 = wif->power_save; break; case LEAF_wlanIfaceApBridge: ctx->scratch->int1 = wif->ap_bridge; break; case LEAF_wlanIfaceBeaconInterval: ctx->scratch->int1 = wif->beacon_interval; break; case LEAF_wlanIfaceDtimPeriod: ctx->scratch->int1 = wif->dtim_period; break; case LEAF_wlanIfaceHideSsid: ctx->scratch->int1 = wif->hide_ssid; break; case LEAF_wlanIfaceInactivityProccess: ctx->scratch->int1 = wif->inact_process; break; case LEAF_wlanIfaceDot11gProtMode: ctx->scratch->int1 = wif->do11g_protect; break; case LEAF_wlanIfaceDot11gPureMode: ctx->scratch->int1 = wif->dot11g_pure; break; case LEAF_wlanIfaceDot11nPureMode: ctx->scratch->int1 = wif->dot11n_pure; break; case LEAF_wlanIfaceDot11nAmpdu: ctx->scratch->int1 = wif->ampdu; break; case LEAF_wlanIfaceDot11nAmpduDensity: ctx->scratch->int1 = wif->ampdu_density; break; case LEAF_wlanIfaceDot11nAmpduLimit: ctx->scratch->int1 = wif->ampdu_limit; break; case LEAF_wlanIfaceDot11nAmsdu: ctx->scratch->int1 = wif->amsdu; break; case LEAF_wlanIfaceDot11nAmsduLimit: ctx->scratch->int1 = wif->amsdu_limit; break; case LEAF_wlanIfaceDot11nHighThroughput: ctx->scratch->int1 = wif->ht_enabled; break; case LEAF_wlanIfaceDot11nHTCompatible: ctx->scratch->int1 = wif->ht_compatible; break; case LEAF_wlanIfaceDot11nHTProtMode: ctx->scratch->int1 = wif->ht_prot_mode; break; case LEAF_wlanIfaceDot11nRIFS: ctx->scratch->int1 = wif->rifs; break; case LEAF_wlanIfaceDot11nShortGI: ctx->scratch->int1 = wif->short_gi; break; case LEAF_wlanIfaceDot11nSMPSMode: ctx->scratch->int1 = wif->smps_mode; break; case LEAF_wlanIfaceTdmaSlot: ctx->scratch->int1 = wif->tdma_slot; break; case LEAF_wlanIfaceTdmaSlotCount: ctx->scratch->int1 = wif->tdma_slot_count; break; case LEAF_wlanIfaceTdmaSlotLength: ctx->scratch->int1 = wif->tdma_slot_length; break; case LEAF_wlanIfaceTdmaBeaconInterval: ctx->scratch->int1 = wif->tdma_binterval; break; default: abort(); } if (val->syntax != SNMP_SYNTAX_OCTETSTRING) goto set_config; ctx->scratch->int1 = val->v.octetstring.len; ctx->scratch->ptr1 = malloc(val->v.octetstring.len + 1); if (ctx->scratch->ptr1 == NULL) return (SNMP_ERR_GENERR); /* XXX */ if (val->var.subs[sub - 1] == LEAF_wlanIfaceDesiredSsid) strlcpy(ctx->scratch->ptr1, val->v.octetstring.octets, val->v.octetstring.len + 1); else memcpy(ctx->scratch->ptr1, val->v.octetstring.octets, val->v.octetstring.len); strval = val->v.octetstring.octets; vlen = val->v.octetstring.len; goto set_config; case SNMP_OP_ROLLBACK: intval = ctx->scratch->int1; strval = NULL; vlen = 0; if ((wif = wlan_get_interface(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); switch (val->var.subs[sub - 1]) { case LEAF_wlanIfaceCountryCode: case LEAF_wlanIfaceDesiredSsid: case LEAF_wlanIfaceDesiredBssid: strval = ctx->scratch->ptr1; vlen = ctx->scratch->int1; break; default: break; } goto set_config; case SNMP_OP_COMMIT: switch (val->var.subs[sub - 1]) { case LEAF_wlanIfaceCountryCode: case LEAF_wlanIfaceDesiredSsid: case LEAF_wlanIfaceDesiredBssid: free(ctx->scratch->ptr1); /* FALLTHROUGH */ default: return (SNMP_ERR_NOERROR); } } abort(); get_config: if (wlan_config_get_ioctl(wif, val->var.subs[sub - 1]) < 0) return (SNMP_ERR_GENERR); switch (val->var.subs[sub - 1]) { case LEAF_wlanIfacePacketBurst: val->v.integer = wif->packet_burst; break; case LEAF_wlanIfaceCountryCode: return (string_get(val, wif->country_code, WLAN_COUNTRY_CODE_SIZE)); case LEAF_wlanIfaceRegDomain: val->v.integer = wif->reg_domain; break; case LEAF_wlanIfaceDesiredSsid: return (string_get(val, wif->desired_ssid, -1)); case LEAF_wlanIfaceDesiredChannel: val->v.integer = wif->desired_channel; break; case LEAF_wlanIfaceDynamicFreqSelection: val->v.integer = wif->dyn_frequency; break; case LEAF_wlanIfaceFastFrames: val->v.integer = wif->fast_frames; break; case LEAF_wlanIfaceDturbo: val->v.integer = wif->dturbo; break; case LEAF_wlanIfaceTxPower: val->v.integer = wif->tx_power; break; case LEAF_wlanIfaceFragmentThreshold: val->v.integer = wif->frag_threshold; break; case LEAF_wlanIfaceRTSThreshold: val->v.integer = wif->rts_threshold; break; case LEAF_wlanIfaceWlanPrivacySubscribe: val->v.integer = wif->priv_subscribe; break; case LEAF_wlanIfaceBgScan: val->v.integer = wif->bg_scan; break; case LEAF_wlanIfaceBgScanIdle: val->v.integer = wif->bg_scan_idle; break; case LEAF_wlanIfaceBgScanInterval: val->v.integer = wif->bg_scan_interval; break; case LEAF_wlanIfaceBeaconMissedThreshold: val->v.integer = wif->beacons_missed; break; case LEAF_wlanIfaceDesiredBssid: return (string_get(val, wif->desired_bssid, IEEE80211_ADDR_LEN)); case LEAF_wlanIfaceRoamingMode: val->v.integer = wif->roam_mode; break; case LEAF_wlanIfaceDot11d: val->v.integer = wif->dot11d; break; case LEAF_wlanIfaceDot11h: val->v.integer = wif->dot11h; break; case LEAF_wlanIfaceDynamicWds: val->v.integer = wif->dynamic_wds; break; case LEAF_wlanIfacePowerSave: val->v.integer = wif->power_save; break; case LEAF_wlanIfaceApBridge: val->v.integer = wif->ap_bridge; break; case LEAF_wlanIfaceBeaconInterval: val->v.integer = wif->beacon_interval; break; case LEAF_wlanIfaceDtimPeriod: val->v.integer = wif->dtim_period; break; case LEAF_wlanIfaceHideSsid: val->v.integer = wif->hide_ssid; break; case LEAF_wlanIfaceInactivityProccess: val->v.integer = wif->inact_process; break; case LEAF_wlanIfaceDot11gProtMode: val->v.integer = wif->do11g_protect; break; case LEAF_wlanIfaceDot11gPureMode: val->v.integer = wif->dot11g_pure; break; case LEAF_wlanIfaceDot11nPureMode: val->v.integer = wif->dot11n_pure; break; case LEAF_wlanIfaceDot11nAmpdu: val->v.integer = wif->ampdu; break; case LEAF_wlanIfaceDot11nAmpduDensity: val->v.integer = wif->ampdu_density; break; case LEAF_wlanIfaceDot11nAmpduLimit: val->v.integer = wif->ampdu_limit; break; case LEAF_wlanIfaceDot11nAmsdu: val->v.integer = wif->amsdu; break; case LEAF_wlanIfaceDot11nAmsduLimit: val->v.integer = wif->amsdu_limit; break; case LEAF_wlanIfaceDot11nHighThroughput: val->v.integer = wif->ht_enabled; break; case LEAF_wlanIfaceDot11nHTCompatible: val->v.integer = wif->ht_compatible; break; case LEAF_wlanIfaceDot11nHTProtMode: val->v.integer = wif->ht_prot_mode; break; case LEAF_wlanIfaceDot11nRIFS: val->v.integer = wif->rifs; break; case LEAF_wlanIfaceDot11nShortGI: val->v.integer = wif->short_gi; break; case LEAF_wlanIfaceDot11nSMPSMode: val->v.integer = wif->smps_mode; break; case LEAF_wlanIfaceTdmaSlot: val->v.integer = wif->tdma_slot; break; case LEAF_wlanIfaceTdmaSlotCount: val->v.integer = wif->tdma_slot_count; break; case LEAF_wlanIfaceTdmaSlotLength: val->v.integer = wif->tdma_slot_length; break; case LEAF_wlanIfaceTdmaBeaconInterval: val->v.integer = wif->tdma_binterval; break; } return (SNMP_ERR_NOERROR); set_config: rc = wlan_config_set_ioctl(wif, val->var.subs[sub - 1], intval, strval, vlen); if (op == SNMP_OP_ROLLBACK) { switch (val->var.subs[sub - 1]) { case LEAF_wlanIfaceCountryCode: case LEAF_wlanIfaceDesiredSsid: case LEAF_wlanIfaceDesiredBssid: free(ctx->scratch->ptr1); /* FALLTHROUGH */ default: break; } } if (rc < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); } int op_wlan_if_peer(struct snmp_context *ctx, struct snmp_value *val, uint32_t sub, uint32_t iidx __unused, enum snmp_op op) { struct wlan_peer *wip; struct wlan_iface *wif; wlan_update_interface_list(); wlan_update_peers(); switch (op) { case SNMP_OP_GET: if ((wip = wlan_get_peer(&val->var, sub, &wif)) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_GETNEXT: if ((wip = wlan_get_next_peer(&val->var, sub, &wif)) == NULL) return (SNMP_ERR_NOSUCHNAME); wlan_append_mac_index(&val->var, sub, wif->wname, wip->pmac); break; case SNMP_OP_SET: if ((wip = wlan_get_peer(&val->var, sub, &wif)) == NULL) return (SNMP_ERR_NOSUCHNAME); if (val->var.subs[sub - 1] != LEAF_wlanIfacePeerVlanTag) return (SNMP_ERR_GENERR); ctx->scratch->int1 = wip->vlan; if (wlan_peer_set_vlan(wif, wip, val->v.integer) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case SNMP_OP_COMMIT: return (SNMP_ERR_NOERROR); case SNMP_OP_ROLLBACK: if ((wip = wlan_get_peer(&val->var, sub, &wif)) == NULL) return (SNMP_ERR_NOSUCHNAME); if (val->var.subs[sub - 1] != LEAF_wlanIfacePeerVlanTag) return (SNMP_ERR_GENERR); if (wlan_peer_set_vlan(wif, wip, ctx->scratch->int1) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); default: abort(); } switch (val->var.subs[sub - 1]) { case LEAF_wlanIfacePeerAddress: return (string_get(val, wip->pmac, IEEE80211_ADDR_LEN)); case LEAF_wlanIfacePeerAssociationId: val->v.integer = wip->associd; break; case LEAF_wlanIfacePeerVlanTag: val->v.integer = wip->vlan; break; case LEAF_wlanIfacePeerFrequency: val->v.integer = wip->frequency; break; case LEAF_wlanIfacePeerCurrentTXRate: val->v.integer = wip->txrate; break; case LEAF_wlanIfacePeerRxSignalStrength: val->v.integer = wip->rssi; break; case LEAF_wlanIfacePeerIdleTimer: val->v.integer = wip->idle; break; case LEAF_wlanIfacePeerTxSequenceNo: val->v.integer = wip->txseqs; break; case LEAF_wlanIfacePeerRxSequenceNo: val->v.integer = wip->rxseqs; break; case LEAF_wlanIfacePeerTxPower: val->v.integer = wip->txpower; break; case LEAF_wlanIfacePeerCapabilities: return (bits_get(val, (uint8_t *)&wip->capinfo, sizeof(wip->capinfo))); case LEAF_wlanIfacePeerFlags: return (bits_get(val, (uint8_t *)&wip->state, sizeof(wip->state))); default: abort(); } return (SNMP_ERR_NOERROR); } int op_wlan_channels(struct snmp_context *ctx __unused, struct snmp_value *val, uint32_t sub, uint32_t iidx __unused, enum snmp_op op) { int32_t bits; struct ieee80211_channel *channel; struct wlan_iface *wif; wlan_update_interface_list(); wlan_update_channels(); switch (op) { case SNMP_OP_GET: if ((channel = wlan_get_channel(&val->var, sub, &wif)) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_GETNEXT: channel = wlan_get_next_channel(&val->var, sub, &wif); if (channel == NULL || wif == NULL) return (SNMP_ERR_NOSUCHNAME); wlan_append_channel_index(&val->var, sub, wif, channel); break; case SNMP_OP_SET: return (SNMP_ERR_NOT_WRITEABLE); case SNMP_OP_COMMIT: /* FALLTHROUGH */ case SNMP_OP_ROLLBACK: /* FALLTHROUGH */ default: abort(); } switch (val->var.subs[sub - 1]) { case LEAF_wlanIfaceChannelIeeeId: val->v.integer = channel->ic_ieee; break; case LEAF_wlanIfaceChannelType: val->v.integer = wlan_get_channel_type(channel); break; case LEAF_wlanIfaceChannelFlags: bits = wlan_channel_flags_to_snmp(channel->ic_flags); return (bits_get(val, (uint8_t *)&bits, sizeof(bits))); case LEAF_wlanIfaceChannelFrequency: val->v.integer = channel->ic_freq; break; case LEAF_wlanIfaceChannelMaxRegPower: val->v.integer = channel->ic_maxregpower; break; case LEAF_wlanIfaceChannelMaxTxPower: val->v.integer = channel->ic_maxpower; break; case LEAF_wlanIfaceChannelMinTxPower: val->v.integer = channel->ic_minpower; break; case LEAF_wlanIfaceChannelState: bits = wlan_channel_state_to_snmp(channel->ic_state); return (bits_get(val, (uint8_t *)&bits, sizeof(bits))); case LEAF_wlanIfaceChannelHTExtension: val->v.integer = channel->ic_extieee; break; case LEAF_wlanIfaceChannelMaxAntennaGain: val->v.integer = channel->ic_maxantgain; break; } return (SNMP_ERR_NOERROR); } int op_wlan_roam_params(struct snmp_context *ctx __unused, struct snmp_value *val, uint32_t sub, uint32_t iidx __unused, enum snmp_op op) { uint32_t phy; struct ieee80211_roamparam *rparam; struct wlan_iface *wif; wlan_update_interface_list(); wlan_update_roam_params(); switch (op) { case SNMP_OP_GET: rparam = wlan_get_roam_param(&val->var, sub, &wif); if (rparam == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_GETNEXT: rparam = wlan_get_next_roam_param(&val->var, sub, &wif, &phy); if (rparam == NULL || wif == NULL) return (SNMP_ERR_NOSUCHNAME); wlan_append_phy_index(&val->var, sub, wif->wname, phy); break; case SNMP_OP_SET: return (SNMP_ERR_NOT_WRITEABLE); case SNMP_OP_COMMIT: /* FALLTHROUGH */ case SNMP_OP_ROLLBACK: /* FALLTHROUGH */ default: abort(); } switch (val->var.subs[sub - 1]) { case LEAF_wlanIfRoamRxSignalStrength: val->v.integer = rparam->rssi/2; break; case LEAF_wlanIfRoamTxRateThreshold: val->v.integer = rparam->rate/2; break; default: abort(); } return (SNMP_ERR_NOERROR); } int op_wlan_tx_params(struct snmp_context *ctx, struct snmp_value *val, uint32_t sub, uint32_t iidx __unused, enum snmp_op op) { uint32_t phy; struct ieee80211_txparam *txparam; struct wlan_iface *wif; wlan_update_interface_list(); wlan_update_tx_params(); switch (op) { case SNMP_OP_GET: txparam = wlan_get_tx_param(&val->var, sub, &wif, &phy); if (txparam == NULL) return (SNMP_ERR_NOSUCHNAME); goto get_txparams; case SNMP_OP_GETNEXT: txparam = wlan_get_next_tx_param(&val->var, sub, &wif, &phy); if (txparam == NULL || wif == NULL) return (SNMP_ERR_NOSUCHNAME); wlan_append_phy_index(&val->var, sub, wif->wname, phy); goto get_txparams; case SNMP_OP_SET: txparam = wlan_get_tx_param(&val->var, sub, &wif, &phy); if (txparam == NULL || wif == NULL) return (SNMP_ERR_NOSUCHNAME); switch (val->var.subs[sub - 1]) { case LEAF_wlanIfTxUnicastRate: ctx->scratch->int1 = txparam->ucastrate; txparam->ucastrate = val->v.integer * 2; break; case LEAF_wlanIfTxMcastRate: ctx->scratch->int1 = txparam->mcastrate; txparam->mcastrate = val->v.integer * 2; break; case LEAF_wlanIfTxMgmtRate: ctx->scratch->int1 = txparam->mgmtrate; txparam->mgmtrate = val->v.integer * 2; break; case LEAF_wlanIfTxMaxRetryCount: ctx->scratch->int1 = txparam->maxretry; txparam->maxretry = val->v.integer; break; default: abort(); } if (wlan_set_tx_params(wif, phy) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case SNMP_OP_COMMIT: return (SNMP_ERR_NOERROR); case SNMP_OP_ROLLBACK: txparam = wlan_get_tx_param(&val->var, sub, &wif, &phy); if (txparam == NULL || wif == NULL) return (SNMP_ERR_NOSUCHNAME); switch (val->var.subs[sub - 1]) { case LEAF_wlanIfTxUnicastRate: txparam->ucastrate = ctx->scratch->int1; break; case LEAF_wlanIfTxMcastRate: txparam->mcastrate = ctx->scratch->int1; break; case LEAF_wlanIfTxMgmtRate: txparam->mgmtrate = ctx->scratch->int1; break; case LEAF_wlanIfTxMaxRetryCount: txparam->maxretry = ctx->scratch->int1; break; default: abort(); } if (wlan_set_tx_params(wif, phy) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); default: abort(); } get_txparams: switch (val->var.subs[sub - 1]) { case LEAF_wlanIfTxUnicastRate: val->v.integer = txparam->ucastrate / 2; break; case LEAF_wlanIfTxMcastRate: val->v.integer = txparam->mcastrate / 2; break; case LEAF_wlanIfTxMgmtRate: val->v.integer = txparam->mgmtrate / 2; break; case LEAF_wlanIfTxMaxRetryCount: val->v.integer = txparam->maxretry; break; default: abort(); } return (SNMP_ERR_NOERROR); } int op_wlan_scan_config(struct snmp_context *ctx, struct snmp_value *val, uint32_t sub, uint32_t iidx __unused, enum snmp_op op) { struct wlan_iface *wif; wlan_update_interface_list(); switch (op) { case SNMP_OP_GET: if ((wif = wlan_get_interface(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_GETNEXT: if ((wif = wlan_get_next_interface(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); wlan_append_ifindex(&val->var, sub, wif); break; case SNMP_OP_SET: if ((wif = wlan_get_interface(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); if (wif->scan_status == wlanScanConfigStatus_running && val->var.subs[sub - 1] != LEAF_wlanScanConfigStatus) return (SNMP_ERR_INCONS_VALUE); switch (val->var.subs[sub - 1]) { case LEAF_wlanScanFlags: ctx->scratch->int1 = wif->scan_flags; wif->scan_flags = val->v.integer; break; case LEAF_wlanScanDuration: ctx->scratch->int1 = wif->scan_duration; wif->scan_duration = val->v.integer; break; case LEAF_wlanScanMinChannelDwellTime: ctx->scratch->int1 = wif->scan_mindwell; wif->scan_mindwell = val->v.integer; break; case LEAF_wlanScanMaxChannelDwellTime: ctx->scratch->int1 = wif->scan_maxdwell; wif->scan_maxdwell = val->v.integer; break; case LEAF_wlanScanConfigStatus: if (val->v.integer == wlanScanConfigStatus_running || val->v.integer == wlanScanConfigStatus_cancel) { ctx->scratch->int1 = wif->scan_status; wif->scan_status = val->v.integer; break; } return (SNMP_ERR_INCONS_VALUE); } return (SNMP_ERR_NOERROR); case SNMP_OP_COMMIT: if ((wif = wlan_get_interface(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); if (val->var.subs[sub - 1] == LEAF_wlanScanConfigStatus) if (wif->scan_status == wlanScanConfigStatus_running) (void)wlan_set_scan_config(wif); /* XXX */ return (SNMP_ERR_NOERROR); case SNMP_OP_ROLLBACK: if ((wif = wlan_get_interface(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); switch (val->var.subs[sub - 1]) { case LEAF_wlanScanFlags: wif->scan_flags = ctx->scratch->int1; break; case LEAF_wlanScanDuration: wif->scan_duration = ctx->scratch->int1; break; case LEAF_wlanScanMinChannelDwellTime: wif->scan_mindwell = ctx->scratch->int1; break; case LEAF_wlanScanMaxChannelDwellTime: wif->scan_maxdwell = ctx->scratch->int1; break; case LEAF_wlanScanConfigStatus: wif->scan_status = ctx->scratch->int1; break; } return (SNMP_ERR_NOERROR); default: abort(); } switch (val->var.subs[sub - 1]) { case LEAF_wlanScanFlags: val->v.integer = wif->scan_flags; break; case LEAF_wlanScanDuration: val->v.integer = wif->scan_duration; break; case LEAF_wlanScanMinChannelDwellTime: val->v.integer = wif->scan_mindwell; break; case LEAF_wlanScanMaxChannelDwellTime: val->v.integer = wif->scan_maxdwell; break; case LEAF_wlanScanConfigStatus: val->v.integer = wif->scan_status; break; } return (SNMP_ERR_NOERROR); } int op_wlan_scan_results(struct snmp_context *ctx __unused, struct snmp_value *val, uint32_t sub, uint32_t iidx __unused, enum snmp_op op) { struct wlan_scan_result *sr; struct wlan_iface *wif; wlan_update_interface_list(); wlan_scan_update_results(); switch (op) { case SNMP_OP_GET: if ((sr = wlan_get_scanr(&val->var, sub, &wif)) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_GETNEXT: if ((sr = wlan_get_next_scanr(&val->var, sub, &wif)) == NULL) return (SNMP_ERR_NOSUCHNAME); wlan_append_scanr_index(&val->var, sub, wif->wname, sr->ssid, sr->bssid); break; case SNMP_OP_SET: return (SNMP_ERR_NOT_WRITEABLE); case SNMP_OP_COMMIT: /* FALLTHROUGH */ case SNMP_OP_ROLLBACK: /* FALLTHROUGH */ default: abort(); } switch (val->var.subs[sub - 1]) { case LEAF_wlanScanResultID: return (string_get(val, sr->ssid, -1)); case LEAF_wlanScanResultBssid: return (string_get(val, sr->bssid, IEEE80211_ADDR_LEN)); case LEAF_wlanScanResultChannel: val->v.integer = sr->opchannel; /* XXX */ break; case LEAF_wlanScanResultRate: val->v.integer = sr->rssi; break; case LEAF_wlanScanResultNoise: val->v.integer = sr->noise; break; case LEAF_wlanScanResultBeaconInterval: val->v.integer = sr->bintval; break; case LEAF_wlanScanResultCapabilities: return (bits_get(val, &sr->capinfo, sizeof(sr->capinfo))); default: abort(); } return (SNMP_ERR_NOERROR); } int op_wlan_iface_stats(struct snmp_context *ctx __unused, struct snmp_value *val, uint32_t sub, uint32_t iidx __unused, enum snmp_op op) { struct wlan_iface *wif; wlan_update_interface_list(); switch (op) { case SNMP_OP_GET: if ((wif = wlan_get_interface(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_GETNEXT: if ((wif = wlan_get_next_interface(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); wlan_append_ifindex(&val->var, sub, wif); break; case SNMP_OP_SET: /* XXX: LEAF_wlanStatsReset */ return (SNMP_ERR_NOT_WRITEABLE); case SNMP_OP_COMMIT: /* FALLTHROUGH */ case SNMP_OP_ROLLBACK: /* FALLTHROUGH */ default: abort(); } if (wlan_get_stats(wif) < 0) return (SNMP_ERR_GENERR); switch (val->var.subs[sub - 1]) { case LEAF_wlanStatsRxBadVersion: val->v.uint32 = wif->stats.is_rx_badversion; break; case LEAF_wlanStatsRxTooShort: val->v.uint32 = wif->stats.is_rx_tooshort; break; case LEAF_wlanStatsRxWrongBssid: val->v.uint32 = wif->stats.is_rx_wrongbss; break; case LEAF_wlanStatsRxDiscardedDups: val->v.uint32 = wif->stats.is_rx_dup; break; case LEAF_wlanStatsRxWrongDir: val->v.uint32 = wif->stats.is_rx_wrongdir; break; case LEAF_wlanStatsRxDiscardMcastEcho: val->v.uint32 = wif->stats.is_rx_mcastecho; break; case LEAF_wlanStatsRxDiscardNoAssoc: val->v.uint32 = wif->stats.is_rx_notassoc; break; case LEAF_wlanStatsRxWepNoPrivacy: val->v.uint32 = wif->stats.is_rx_noprivacy; break; case LEAF_wlanStatsRxWepUnencrypted: val->v.uint32 = wif->stats.is_rx_unencrypted; break; case LEAF_wlanStatsRxWepFailed: val->v.uint32 = wif->stats.is_rx_wepfail; break; case LEAF_wlanStatsRxDecapsulationFailed: val->v.uint32 = wif->stats.is_rx_decap; break; case LEAF_wlanStatsRxDiscardMgmt: val->v.uint32 = wif->stats.is_rx_mgtdiscard; break; case LEAF_wlanStatsRxControl: val->v.uint32 = wif->stats.is_rx_ctl; break; case LEAF_wlanStatsRxBeacon: val->v.uint32 = wif->stats.is_rx_beacon; break; case LEAF_wlanStatsRxRateSetTooBig: val->v.uint32 = wif->stats.is_rx_rstoobig; break; case LEAF_wlanStatsRxElemMissing: val->v.uint32 = wif->stats.is_rx_elem_missing; break; case LEAF_wlanStatsRxElemTooBig: val->v.uint32 = wif->stats.is_rx_elem_toobig; break; case LEAF_wlanStatsRxElemTooSmall: val->v.uint32 = wif->stats.is_rx_elem_toosmall; break; case LEAF_wlanStatsRxElemUnknown: val->v.uint32 = wif->stats.is_rx_elem_unknown; break; case LEAF_wlanStatsRxChannelMismatch: val->v.uint32 = wif->stats.is_rx_chanmismatch; break; case LEAF_wlanStatsRxDropped: val->v.uint32 = wif->stats.is_rx_nodealloc; break; case LEAF_wlanStatsRxSsidMismatch: val->v.uint32 = wif->stats.is_rx_ssidmismatch; break; case LEAF_wlanStatsRxAuthNotSupported: val->v.uint32 = wif->stats.is_rx_auth_unsupported; break; case LEAF_wlanStatsRxAuthFailed: val->v.uint32 = wif->stats.is_rx_auth_fail; break; case LEAF_wlanStatsRxAuthCM: val->v.uint32 = wif->stats.is_rx_auth_countermeasures; break; case LEAF_wlanStatsRxAssocWrongBssid: val->v.uint32 = wif->stats.is_rx_assoc_bss; break; case LEAF_wlanStatsRxAssocNoAuth: val->v.uint32 = wif->stats.is_rx_assoc_notauth; break; case LEAF_wlanStatsRxAssocCapMismatch: val->v.uint32 = wif->stats.is_rx_assoc_capmismatch; break; case LEAF_wlanStatsRxAssocNoRateMatch: val->v.uint32 = wif->stats.is_rx_assoc_norate; break; case LEAF_wlanStatsRxBadWpaIE: val->v.uint32 = wif->stats.is_rx_assoc_badwpaie; break; case LEAF_wlanStatsRxDeauthenticate: val->v.uint32 = wif->stats.is_rx_deauth; break; case LEAF_wlanStatsRxDisassociate: val->v.uint32 = wif->stats.is_rx_disassoc; break; case LEAF_wlanStatsRxUnknownSubtype: val->v.uint32 = wif->stats.is_rx_badsubtype; break; case LEAF_wlanStatsRxFailedNoBuf: val->v.uint32 = wif->stats.is_rx_nobuf; break; case LEAF_wlanStatsRxBadAuthRequest: val->v.uint32 = wif->stats.is_rx_bad_auth; break; case LEAF_wlanStatsRxUnAuthorized: val->v.uint32 = wif->stats.is_rx_unauth; break; case LEAF_wlanStatsRxBadKeyId: val->v.uint32 = wif->stats.is_rx_badkeyid; break; case LEAF_wlanStatsRxCCMPSeqViolation: val->v.uint32 = wif->stats.is_rx_ccmpreplay; break; case LEAF_wlanStatsRxCCMPBadFormat: val->v.uint32 = wif->stats.is_rx_ccmpformat; break; case LEAF_wlanStatsRxCCMPFailedMIC: val->v.uint32 = wif->stats.is_rx_ccmpmic; break; case LEAF_wlanStatsRxTKIPSeqViolation: val->v.uint32 = wif->stats.is_rx_tkipreplay; break; case LEAF_wlanStatsRxTKIPBadFormat: val->v.uint32 = wif->stats.is_rx_tkipformat; break; case LEAF_wlanStatsRxTKIPFailedMIC: val->v.uint32 = wif->stats.is_rx_tkipmic; break; case LEAF_wlanStatsRxTKIPFailedICV: val->v.uint32 = wif->stats.is_rx_tkipicv; break; case LEAF_wlanStatsRxDiscardACL: val->v.uint32 = wif->stats.is_rx_acl; break; case LEAF_wlanStatsTxFailedNoBuf: val->v.uint32 = wif->stats.is_tx_nobuf; break; case LEAF_wlanStatsTxFailedNoNode: val->v.uint32 = wif->stats.is_tx_nonode; break; case LEAF_wlanStatsTxUnknownMgmt: val->v.uint32 = wif->stats.is_tx_unknownmgt; break; case LEAF_wlanStatsTxBadCipher: val->v.uint32 = wif->stats.is_tx_badcipher; break; case LEAF_wlanStatsTxNoDefKey: val->v.uint32 = wif->stats.is_tx_nodefkey; break; case LEAF_wlanStatsTxFragmented: val->v.uint32 = wif->stats.is_tx_fragframes; break; case LEAF_wlanStatsTxFragmentsCreated: val->v.uint32 = wif->stats.is_tx_frags; break; case LEAF_wlanStatsActiveScans: val->v.uint32 = wif->stats.is_scan_active; break; case LEAF_wlanStatsPassiveScans: val->v.uint32 = wif->stats.is_scan_passive; break; case LEAF_wlanStatsTimeoutInactivity: val->v.uint32 = wif->stats.is_node_timeout; break; case LEAF_wlanStatsCryptoNoMem: val->v.uint32 = wif->stats.is_crypto_nomem; break; case LEAF_wlanStatsSwCryptoTKIP: val->v.uint32 = wif->stats.is_crypto_tkip; break; case LEAF_wlanStatsSwCryptoTKIPEnMIC: val->v.uint32 = wif->stats.is_crypto_tkipenmic; break; case LEAF_wlanStatsSwCryptoTKIPDeMIC: val->v.uint32 = wif->stats.is_crypto_tkipdemic; break; case LEAF_wlanStatsCryptoTKIPCM: val->v.uint32 = wif->stats.is_crypto_tkipcm; break; case LEAF_wlanStatsSwCryptoCCMP: val->v.uint32 = wif->stats.is_crypto_ccmp; break; case LEAF_wlanStatsSwCryptoWEP: val->v.uint32 = wif->stats.is_crypto_wep; break; case LEAF_wlanStatsCryptoCipherKeyRejected: val->v.uint32 = wif->stats.is_crypto_setkey_cipher; break; case LEAF_wlanStatsCryptoNoKey: val->v.uint32 = wif->stats.is_crypto_setkey_nokey; break; case LEAF_wlanStatsCryptoDeleteKeyFailed: val->v.uint32 = wif->stats.is_crypto_delkey; break; case LEAF_wlanStatsCryptoUnknownCipher: val->v.uint32 = wif->stats.is_crypto_badcipher; break; case LEAF_wlanStatsCryptoAttachFailed: val->v.uint32 = wif->stats.is_crypto_attachfail; break; case LEAF_wlanStatsCryptoKeyFailed: val->v.uint32 = wif->stats.is_crypto_keyfail; break; case LEAF_wlanStatsCryptoEnMICFailed: val->v.uint32 = wif->stats.is_crypto_enmicfail; break; case LEAF_wlanStatsIBSSCapMismatch: val->v.uint32 = wif->stats.is_ibss_capmismatch; break; case LEAF_wlanStatsUnassocStaPSPoll: val->v.uint32 = wif->stats.is_ps_unassoc; break; case LEAF_wlanStatsBadAidPSPoll: val->v.uint32 = wif->stats.is_ps_badaid; break; case LEAF_wlanStatsEmptyPSPoll: val->v.uint32 = wif->stats.is_ps_qempty; break; case LEAF_wlanStatsRxFFBadHdr: val->v.uint32 = wif->stats.is_ff_badhdr; break; case LEAF_wlanStatsRxFFTooShort: val->v.uint32 = wif->stats.is_ff_tooshort; break; case LEAF_wlanStatsRxFFSplitError: val->v.uint32 = wif->stats.is_ff_split; break; case LEAF_wlanStatsRxFFDecap: val->v.uint32 = wif->stats.is_ff_decap; break; case LEAF_wlanStatsTxFFEncap: val->v.uint32 = wif->stats.is_ff_encap; break; case LEAF_wlanStatsRxBadBintval: val->v.uint32 = wif->stats.is_rx_badbintval; break; case LEAF_wlanStatsRxDemicFailed: val->v.uint32 = wif->stats.is_rx_demicfail; break; case LEAF_wlanStatsRxDefragFailed: val->v.uint32 = wif->stats.is_rx_defrag; break; case LEAF_wlanStatsRxMgmt: val->v.uint32 = wif->stats.is_rx_mgmt; break; case LEAF_wlanStatsRxActionMgmt: val->v.uint32 = wif->stats.is_rx_action; break; case LEAF_wlanStatsRxAMSDUTooShort: val->v.uint32 = wif->stats.is_amsdu_tooshort; break; case LEAF_wlanStatsRxAMSDUSplitError: val->v.uint32 = wif->stats.is_amsdu_split; break; case LEAF_wlanStatsRxAMSDUDecap: val->v.uint32 = wif->stats.is_amsdu_decap; break; case LEAF_wlanStatsTxAMSDUEncap: val->v.uint32 = wif->stats.is_amsdu_encap; break; case LEAF_wlanStatsAMPDUBadBAR: val->v.uint32 = wif->stats.is_ampdu_bar_bad; break; case LEAF_wlanStatsAMPDUOowBar: val->v.uint32 = wif->stats.is_ampdu_bar_oow; break; case LEAF_wlanStatsAMPDUMovedBAR: val->v.uint32 = wif->stats.is_ampdu_bar_move; break; case LEAF_wlanStatsAMPDURxBAR: val->v.uint32 = wif->stats.is_ampdu_bar_rx; break; case LEAF_wlanStatsAMPDURxOor: val->v.uint32 = wif->stats.is_ampdu_rx_oor; break; case LEAF_wlanStatsAMPDURxCopied: val->v.uint32 = wif->stats.is_ampdu_rx_copy; break; case LEAF_wlanStatsAMPDURxDropped: val->v.uint32 = wif->stats.is_ampdu_rx_drop; break; case LEAF_wlanStatsTxDiscardBadState: val->v.uint32 = wif->stats.is_tx_badstate; break; case LEAF_wlanStatsTxFailedNoAssoc: val->v.uint32 = wif->stats.is_tx_notassoc; break; case LEAF_wlanStatsTxClassifyFailed: val->v.uint32 = wif->stats.is_tx_classify; break; case LEAF_wlanStatsDwdsMcastDiscard: val->v.uint32 = wif->stats.is_dwds_mcast; break; case LEAF_wlanStatsHTAssocRejectNoHT: val->v.uint32 = wif->stats.is_ht_assoc_nohtcap; break; case LEAF_wlanStatsHTAssocDowngrade: val->v.uint32 = wif->stats.is_ht_assoc_downgrade; break; case LEAF_wlanStatsHTAssocRateMismatch: val->v.uint32 = wif->stats.is_ht_assoc_norate; break; case LEAF_wlanStatsAMPDURxAge: val->v.uint32 = wif->stats.is_ampdu_rx_age; break; case LEAF_wlanStatsAMPDUMoved: val->v.uint32 = wif->stats.is_ampdu_rx_move; break; case LEAF_wlanStatsADDBADisabledReject: val->v.uint32 = wif->stats.is_addba_reject; break; case LEAF_wlanStatsADDBANoRequest: val->v.uint32 = wif->stats.is_addba_norequest; break; case LEAF_wlanStatsADDBABadToken: val->v.uint32 = wif->stats.is_addba_badtoken; break; case LEAF_wlanStatsADDBABadPolicy: val->v.uint32 = wif->stats.is_addba_badpolicy; break; case LEAF_wlanStatsAMPDUStopped: val->v.uint32 = wif->stats.is_ampdu_stop; break; case LEAF_wlanStatsAMPDUStopFailed: val->v.uint32 = wif->stats.is_ampdu_stop_failed; break; case LEAF_wlanStatsAMPDURxReorder: val->v.uint32 = wif->stats.is_ampdu_rx_reorder; break; case LEAF_wlanStatsScansBackground: val->v.uint32 = wif->stats.is_scan_bg; break; case LEAF_wlanLastDeauthReason: val->v.uint32 = wif->stats.is_rx_deauth_code; break; case LEAF_wlanLastDissasocReason: val->v.uint32 = wif->stats.is_rx_disassoc_code; break; case LEAF_wlanLastAuthFailReason: val->v.uint32 = wif->stats.is_rx_authfail_code; break; case LEAF_wlanStatsBeaconMissedEvents: val->v.uint32 = wif->stats.is_beacon_miss; break; case LEAF_wlanStatsRxDiscardBadStates: val->v.uint32 = wif->stats.is_rx_badstate; break; case LEAF_wlanStatsFFFlushed: val->v.uint32 = wif->stats.is_ff_flush; break; case LEAF_wlanStatsTxControlFrames: val->v.uint32 = wif->stats.is_tx_ctl; break; case LEAF_wlanStatsAMPDURexmt: val->v.uint32 = wif->stats.is_ampdu_rexmt; break; case LEAF_wlanStatsAMPDURexmtFailed: val->v.uint32 = wif->stats.is_ampdu_rexmt_fail; break; case LEAF_wlanStatsReset: val->v.uint32 = wlanStatsReset_no_op; break; default: abort(); } return (SNMP_ERR_NOERROR); } int op_wlan_wep_iface(struct snmp_context *ctx, struct snmp_value *val, uint32_t sub, uint32_t iidx __unused, enum snmp_op op) { struct wlan_iface *wif; wlan_update_interface_list(); switch (op) { case SNMP_OP_GET: if ((wif = wlan_get_interface(&val->var, sub)) == NULL || !wif->wepsupported) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_GETNEXT: /* XXX: filter wif->wepsupported */ if ((wif = wlan_get_next_interface(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); wlan_append_ifindex(&val->var, sub, wif); break; case SNMP_OP_SET: if ((wif = wlan_get_interface(&val->var, sub)) == NULL || !wif->wepsupported) return (SNMP_ERR_NOSUCHNAME); switch (val->var.subs[sub - 1]) { case LEAF_wlanWepMode: if (val->v.integer < wlanWepMode_off || val->v.integer > wlanWepMode_mixed) return (SNMP_ERR_INCONS_VALUE); ctx->scratch->int1 = wif->wepmode; wif->wepmode = val->v.integer; if (wlan_set_wepmode(wif) < 0) { wif->wepmode = ctx->scratch->int1; return (SNMP_ERR_GENERR); } break; case LEAF_wlanWepDefTxKey: if (val->v.integer < 0 || val->v.integer > IEEE80211_WEP_NKID) return (SNMP_ERR_INCONS_VALUE); ctx->scratch->int1 = wif->weptxkey; wif->weptxkey = val->v.integer; if (wlan_set_weptxkey(wif) < 0) { wif->weptxkey = ctx->scratch->int1; return (SNMP_ERR_GENERR); } break; default: abort(); } return (SNMP_ERR_NOERROR); case SNMP_OP_COMMIT: return (SNMP_ERR_NOERROR); case SNMP_OP_ROLLBACK: if ((wif = wlan_get_interface(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); switch (val->var.subs[sub - 1]) { case LEAF_wlanWepMode: wif->wepmode = ctx->scratch->int1; if (wlan_set_wepmode(wif) < 0) return (SNMP_ERR_GENERR); break; case LEAF_wlanWepDefTxKey: wif->weptxkey = ctx->scratch->int1; if (wlan_set_weptxkey(wif) < 0) return (SNMP_ERR_GENERR); break; default: abort(); } return (SNMP_ERR_NOERROR); default: abort(); } switch (val->var.subs[sub - 1]) { case LEAF_wlanWepMode: if (wlan_get_wepmode(wif) < 0) return (SNMP_ERR_GENERR); val->v.integer = wif->wepmode; break; case LEAF_wlanWepDefTxKey: if (wlan_get_weptxkey(wif) < 0) return (SNMP_ERR_GENERR); val->v.integer = wif->weptxkey; break; default: abort(); } - + return (SNMP_ERR_NOERROR); } int op_wlan_wep_key(struct snmp_context *ctx __unused, struct snmp_value *val __unused, uint32_t sub __unused, uint32_t iidx __unused, enum snmp_op op __unused) { return (SNMP_ERR_NOSUCHNAME); } int op_wlan_mac_access_control(struct snmp_context *ctx, struct snmp_value *val, uint32_t sub, uint32_t iidx __unused, enum snmp_op op) { struct wlan_iface *wif; wlan_update_interface_list(); switch (op) { case SNMP_OP_GET: if ((wif = wlan_get_interface(&val->var, sub)) == NULL || !wif->macsupported) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_GETNEXT: /* XXX: filter wif->macsupported */ if ((wif = wlan_get_next_interface(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); wlan_append_ifindex(&val->var, sub, wif); break; case SNMP_OP_SET: if ((wif = wlan_get_interface(&val->var, sub)) == NULL || !wif->macsupported) return (SNMP_ERR_NOSUCHNAME); switch (val->var.subs[sub - 1]) { case LEAF_wlanMACAccessControlPolicy: ctx->scratch->int1 = wif->mac_policy; wif->mac_policy = val->v.integer; break; case LEAF_wlanMACAccessControlNacl: return (SNMP_ERR_NOT_WRITEABLE); case LEAF_wlanMACAccessControlFlush: break; default: abort(); } return (SNMP_ERR_NOERROR); case SNMP_OP_COMMIT: if ((wif = wlan_get_interface(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); switch (val->var.subs[sub - 1]) { case LEAF_wlanMACAccessControlPolicy: if (wlan_set_mac_policy(wif) < 0) { wif->mac_policy = ctx->scratch->int1; return (SNMP_ERR_GENERR); } break; case LEAF_wlanMACAccessControlFlush: if (wlan_flush_mac_mac(wif) < 0) return (SNMP_ERR_GENERR); break; default: abort(); } return (SNMP_ERR_NOERROR); case SNMP_OP_ROLLBACK: if ((wif = wlan_get_interface(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); if (val->var.subs[sub - 1] == LEAF_wlanMACAccessControlPolicy) wif->mac_policy = ctx->scratch->int1; return (SNMP_ERR_NOERROR); default: abort(); } if (wlan_get_mac_policy(wif) < 0) return (SNMP_ERR_GENERR); switch (val->var.subs[sub - 1]) { case LEAF_wlanMACAccessControlPolicy: val->v.integer = wif->mac_policy; break; case LEAF_wlanMACAccessControlNacl: val->v.integer = wif->mac_nacls; break; case LEAF_wlanMACAccessControlFlush: val->v.integer = wlanMACAccessControlFlush_no_op; break; default: abort(); } return (SNMP_ERR_NOERROR); } int op_wlan_mac_acl_mac(struct snmp_context *ctx, struct snmp_value *val, uint32_t sub, uint32_t iidx __unused, enum snmp_op op) { struct wlan_iface *wif; struct wlan_mac_mac *macl; wlan_update_interface_list(); wlan_mac_update_aclmacs(); switch (op) { case SNMP_OP_GET: if ((macl = wlan_get_acl_mac(&val->var, sub, &wif)) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_GETNEXT: if ((macl = wlan_get_next_acl_mac(&val->var, sub, &wif)) == NULL) return (SNMP_ERR_NOSUCHNAME); wlan_append_mac_index(&val->var, sub, wif->wname, macl->mac); break; case SNMP_OP_SET: switch (val->var.subs[sub - 1]) { case LEAF_wlanMACAccessControlMAC: return (SNMP_ERR_INCONS_NAME); case LEAF_wlanMACAccessControlMACStatus: return(wlan_acl_mac_set_status(ctx, val, sub)); default: abort(); } case SNMP_OP_COMMIT: if ((macl = wlan_get_acl_mac(&val->var, sub, &wif)) == NULL) return (SNMP_ERR_NOSUCHNAME); if (val->v.integer == RowStatus_destroy && wlan_mac_delete_mac(wif, macl) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case SNMP_OP_ROLLBACK: if ((macl = wlan_get_acl_mac(&val->var, sub, &wif)) == NULL) return (SNMP_ERR_NOSUCHNAME); if (ctx->scratch->int1 == RowStatus_destroy && wlan_mac_delete_mac(wif, macl) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); default: abort(); } switch (val->var.subs[sub - 1]) { case LEAF_wlanMACAccessControlMAC: return (string_get(val, macl->mac, IEEE80211_ADDR_LEN)); case LEAF_wlanMACAccessControlMACStatus: val->v.integer = macl->mac_status; break; default: abort(); } return (SNMP_ERR_NOERROR); } int op_wlan_mesh_config(struct snmp_context *ctx, struct snmp_value *val, uint32_t sub, uint32_t iidx __unused, enum snmp_op op) { int which; switch (val->var.subs[sub - 1]) { case LEAF_wlanMeshMaxRetries: which = WLAN_MESH_MAX_RETRIES; break; case LEAF_wlanMeshHoldingTimeout: which = WLAN_MESH_HOLDING_TO; break; case LEAF_wlanMeshConfirmTimeout: which = WLAN_MESH_CONFIRM_TO; break; case LEAF_wlanMeshRetryTimeout: which = WLAN_MESH_RETRY_TO; break; default: abort(); } switch (op) { case SNMP_OP_GET: if (wlan_do_sysctl(&wlan_config, which, 0) < 0) return (SNMP_ERR_GENERR); break; case SNMP_OP_GETNEXT: abort(); case SNMP_OP_SET: switch (val->var.subs[sub - 1]) { case LEAF_wlanMeshRetryTimeout : ctx->scratch->int1 = wlan_config.mesh_retryto; wlan_config.mesh_retryto = val->v.integer; break; case LEAF_wlanMeshHoldingTimeout: ctx->scratch->int1 = wlan_config.mesh_holdingto; wlan_config.mesh_holdingto = val->v.integer; break; case LEAF_wlanMeshConfirmTimeout: ctx->scratch->int1 = wlan_config.mesh_confirmto; wlan_config.mesh_confirmto = val->v.integer; break; case LEAF_wlanMeshMaxRetries: ctx->scratch->int1 = wlan_config.mesh_maxretries; wlan_config.mesh_maxretries = val->v.integer; break; } if (wlan_do_sysctl(&wlan_config, which, 1) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case SNMP_OP_COMMIT: return (SNMP_ERR_NOERROR); case SNMP_OP_ROLLBACK: switch (val->var.subs[sub - 1]) { case LEAF_wlanMeshRetryTimeout: wlan_config.mesh_retryto = ctx->scratch->int1; break; case LEAF_wlanMeshConfirmTimeout: wlan_config.mesh_confirmto = ctx->scratch->int1; break; case LEAF_wlanMeshHoldingTimeout: wlan_config.mesh_holdingto= ctx->scratch->int1; break; case LEAF_wlanMeshMaxRetries: wlan_config.mesh_maxretries = ctx->scratch->int1; break; } if (wlan_do_sysctl(&wlan_config, which, 1) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); default: abort(); } switch (val->var.subs[sub - 1]) { case LEAF_wlanMeshRetryTimeout: val->v.integer = wlan_config.mesh_retryto; break; case LEAF_wlanMeshHoldingTimeout: val->v.integer = wlan_config.mesh_holdingto; break; case LEAF_wlanMeshConfirmTimeout: val->v.integer = wlan_config.mesh_confirmto; break; case LEAF_wlanMeshMaxRetries: val->v.integer = wlan_config.mesh_maxretries; break; } return (SNMP_ERR_NOERROR); } int op_wlan_mesh_iface(struct snmp_context *ctx, struct snmp_value *val, uint32_t sub, uint32_t iidx __unused, enum snmp_op op) { int rc; struct wlan_iface *wif; wlan_update_interface_list(); switch (op) { case SNMP_OP_GET: if ((wif = wlan_mesh_get_iface(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_GETNEXT: if ((wif = wlan_mesh_get_next_iface(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); wlan_append_ifindex(&val->var, sub, wif); break; case SNMP_OP_SET: if ((wif = wlan_mesh_get_iface(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); switch (val->var.subs[sub - 1]) { case LEAF_wlanMeshId: if (val->v.octetstring.len > IEEE80211_NWID_LEN) return (SNMP_ERR_INCONS_VALUE); ctx->scratch->ptr1 = malloc(val->v.octetstring.len + 1); if (ctx->scratch->ptr1 == NULL) return (SNMP_ERR_GENERR); strlcpy(ctx->scratch->ptr1, wif->desired_ssid, val->v.octetstring.len + 1); ctx->scratch->int1 = strlen(wif->desired_ssid); memcpy(wif->desired_ssid, val->v.octetstring.octets, val->v.octetstring.len); wif->desired_ssid[val->v.octetstring.len] = '\0'; break; case LEAF_wlanMeshTTL: ctx->scratch->int1 = wif->mesh_ttl; wif->mesh_ttl = val->v.integer; break; case LEAF_wlanMeshPeeringEnabled: ctx->scratch->int1 = wif->mesh_peering; wif->mesh_peering = val->v.integer; break; case LEAF_wlanMeshForwardingEnabled: ctx->scratch->int1 = wif->mesh_forwarding; wif->mesh_forwarding = val->v.integer; break; case LEAF_wlanMeshMetric: ctx->scratch->int1 = wif->mesh_metric; wif->mesh_metric = val->v.integer; break; case LEAF_wlanMeshPath: ctx->scratch->int1 = wif->mesh_path; wif->mesh_path = val->v.integer; break; case LEAF_wlanMeshRoutesFlush: if (val->v.integer != wlanMeshRoutesFlush_flush) return (SNMP_ERR_INCONS_VALUE); return (SNMP_ERR_NOERROR); default: abort(); } if (val->var.subs[sub - 1] == LEAF_wlanMeshId) rc = wlan_config_set_dssid(wif, val->v.octetstring.octets, val->v.octetstring.len); else rc = wlan_mesh_config_set(wif, val->var.subs[sub - 1]); if (rc < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case SNMP_OP_COMMIT: if ((wif = wlan_mesh_get_iface(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); if (val->var.subs[sub - 1] == LEAF_wlanMeshRoutesFlush && wlan_mesh_flush_routes(wif) < 0) return (SNMP_ERR_GENERR); if (val->var.subs[sub - 1] == LEAF_wlanMeshId) free(ctx->scratch->ptr1); return (SNMP_ERR_NOERROR); case SNMP_OP_ROLLBACK: if ((wif = wlan_mesh_get_iface(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); switch (val->var.subs[sub - 1]) { case LEAF_wlanMeshId: strlcpy(wif->desired_ssid, ctx->scratch->ptr1, IEEE80211_NWID_LEN); free(ctx->scratch->ptr1); break; case LEAF_wlanMeshTTL: wif->mesh_ttl = ctx->scratch->int1; break; case LEAF_wlanMeshPeeringEnabled: wif->mesh_peering = ctx->scratch->int1; break; case LEAF_wlanMeshForwardingEnabled: wif->mesh_forwarding = ctx->scratch->int1; break; case LEAF_wlanMeshMetric: wif->mesh_metric = ctx->scratch->int1; break; case LEAF_wlanMeshPath: wif->mesh_path = ctx->scratch->int1; break; case LEAF_wlanMeshRoutesFlush: return (SNMP_ERR_NOERROR); default: abort(); } if (val->var.subs[sub - 1] == LEAF_wlanMeshId) rc = wlan_config_set_dssid(wif, wif->desired_ssid, strlen(wif->desired_ssid)); else rc = wlan_mesh_config_set(wif, val->var.subs[sub - 1]); if (rc < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); default: abort(); } if (val->var.subs[sub - 1] == LEAF_wlanMeshId) rc = wlan_config_get_dssid(wif); else rc = wlan_mesh_config_get(wif, val->var.subs[sub - 1]); if (rc < 0) return (SNMP_ERR_GENERR); switch (val->var.subs[sub - 1]) { case LEAF_wlanMeshId: return (string_get(val, wif->desired_ssid, -1)); case LEAF_wlanMeshTTL: val->v.integer = wif->mesh_ttl; break; case LEAF_wlanMeshPeeringEnabled: val->v.integer = wif->mesh_peering; break; case LEAF_wlanMeshForwardingEnabled: val->v.integer = wif->mesh_forwarding; break; case LEAF_wlanMeshMetric: val->v.integer = wif->mesh_metric; break; case LEAF_wlanMeshPath: val->v.integer = wif->mesh_path; break; case LEAF_wlanMeshRoutesFlush: val->v.integer = wlanMeshRoutesFlush_no_op; break; default: abort(); } return (SNMP_ERR_NOERROR); } int op_wlan_mesh_neighbor(struct snmp_context *ctx __unused, struct snmp_value *val, uint32_t sub, uint32_t iidx __unused, enum snmp_op op) { struct wlan_peer *wip; struct wlan_iface *wif; wlan_update_interface_list(); wlan_update_peers(); switch (op) { case SNMP_OP_GET: if ((wip = wlan_mesh_get_peer(&val->var, sub, &wif)) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_GETNEXT: wip = wlan_mesh_get_next_peer(&val->var, sub, &wif); if (wip == NULL) return (SNMP_ERR_NOSUCHNAME); wlan_append_mac_index(&val->var, sub, wif->wname, wip->pmac); break; case SNMP_OP_SET: return (SNMP_ERR_NOT_WRITEABLE); case SNMP_OP_COMMIT: /* FALLTHROUGH */ case SNMP_OP_ROLLBACK: /* FALLTHROUGH */ default: abort(); } switch (val->var.subs[sub - 1]) { case LEAF_wlanMeshNeighborAddress: return (string_get(val, wip->pmac, IEEE80211_ADDR_LEN)); case LEAF_wlanMeshNeighborFrequency: val->v.integer = wip->frequency; break; case LEAF_wlanMeshNeighborLocalId: val->v.integer = wip->local_id; break; case LEAF_wlanMeshNeighborPeerId: val->v.integer = wip->peer_id; break; case LEAF_wlanMeshNeighborPeerState: return (bits_get(val, (uint8_t *)&wip->state, sizeof(wip->state))); case LEAF_wlanMeshNeighborCurrentTXRate: val->v.integer = wip->txrate; break; case LEAF_wlanMeshNeighborRxSignalStrength: val->v.integer = wip->rssi; break; case LEAF_wlanMeshNeighborIdleTimer: val->v.integer = wip->idle; break; case LEAF_wlanMeshNeighborTxSequenceNo: val->v.integer = wip->txseqs; break; case LEAF_wlanMeshNeighborRxSequenceNo: val->v.integer = wip->rxseqs; break; default: abort(); } return (SNMP_ERR_NOERROR); } int op_wlan_mesh_route(struct snmp_context *ctx, struct snmp_value *val, uint32_t sub, uint32_t iidx __unused, enum snmp_op op) { struct wlan_mesh_route *wmr; struct wlan_iface *wif; wlan_update_interface_list(); wlan_mesh_update_routes(); switch (op) { case SNMP_OP_GET: if ((wmr = wlan_mesh_get_route(&val->var, sub, &wif)) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_GETNEXT: wmr = wlan_mesh_get_next_route(&val->var, sub, &wif); if (wmr == NULL) return (SNMP_ERR_NOSUCHNAME); wlan_append_mac_index(&val->var, sub, wif->wname, wmr->imroute.imr_dest); break; case SNMP_OP_SET: switch (val->var.subs[sub - 1]) { case LEAF_wlanMeshRouteDestination: return (SNMP_ERR_INCONS_NAME); case LEAF_wlanMeshRouteStatus: return(wlan_mesh_route_set_status(ctx, val, sub)); default: return (SNMP_ERR_NOT_WRITEABLE); } abort(); case SNMP_OP_COMMIT: if ((wmr = wlan_mesh_get_route(&val->var, sub, &wif)) == NULL) return (SNMP_ERR_NOSUCHNAME); if (val->v.integer == RowStatus_destroy && wlan_mesh_delete_route(wif, wmr) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case SNMP_OP_ROLLBACK: if ((wmr = wlan_mesh_get_route(&val->var, sub, &wif)) == NULL) return (SNMP_ERR_NOSUCHNAME); if (ctx->scratch->int1 == RowStatus_destroy && wlan_mesh_delete_route(wif, wmr) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); default: abort(); } switch (val->var.subs[sub - 1]) { case LEAF_wlanMeshRouteDestination: return (string_get(val, wmr->imroute.imr_dest, IEEE80211_ADDR_LEN)); case LEAF_wlanMeshRouteNextHop: return (string_get(val, wmr->imroute.imr_nexthop, IEEE80211_ADDR_LEN)); case LEAF_wlanMeshRouteHops: val->v.integer = wmr->imroute.imr_nhops; break; case LEAF_wlanMeshRouteMetric: val->v.integer = wmr->imroute.imr_metric; break; case LEAF_wlanMeshRouteLifeTime: val->v.integer = wmr->imroute.imr_lifetime; break; case LEAF_wlanMeshRouteLastMseq: val->v.integer = wmr->imroute.imr_lastmseq; break; case LEAF_wlanMeshRouteFlags: val->v.integer = 0; if ((wmr->imroute.imr_flags & IEEE80211_MESHRT_FLAGS_VALID) != 0) val->v.integer |= (0x1 << wlanMeshRouteFlags_valid); if ((wmr->imroute.imr_flags & IEEE80211_MESHRT_FLAGS_PROXY) != 0) val->v.integer |= (0x1 << wlanMeshRouteFlags_proxy); return (bits_get(val, (uint8_t *)&val->v.integer, sizeof(val->v.integer))); case LEAF_wlanMeshRouteStatus: val->v.integer = wmr->mroute_status; break; } return (SNMP_ERR_NOERROR); } int op_wlan_mesh_stats(struct snmp_context *ctx __unused, struct snmp_value *val, uint32_t sub, uint32_t iidx __unused, enum snmp_op op) { struct wlan_iface *wif; wlan_update_interface_list(); switch (op) { case SNMP_OP_GET: if ((wif = wlan_mesh_get_iface(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_GETNEXT: if ((wif = wlan_mesh_get_next_iface(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); wlan_append_ifindex(&val->var, sub, wif); break; case SNMP_OP_SET: return (SNMP_ERR_NOT_WRITEABLE); case SNMP_OP_COMMIT: /* FALLTHROUGH */ case SNMP_OP_ROLLBACK: /* FALLTHROUGH */ default: abort(); } if (wlan_get_stats(wif) < 0) return (SNMP_ERR_GENERR); switch (val->var.subs[sub - 1]) { case LEAF_wlanMeshDroppedBadSta: val->v.uint32 = wif->stats.is_mesh_wrongmesh; break; case LEAF_wlanMeshDroppedNoLink: val->v.uint32 = wif->stats.is_mesh_nolink; break; case LEAF_wlanMeshNoFwdTtl: val->v.uint32 = wif->stats.is_mesh_fwd_ttl; break; case LEAF_wlanMeshNoFwdBuf: val->v.uint32 = wif->stats.is_mesh_fwd_nobuf; break; case LEAF_wlanMeshNoFwdTooShort: val->v.uint32 = wif->stats.is_mesh_fwd_tooshort; break; case LEAF_wlanMeshNoFwdDisabled: val->v.uint32 = wif->stats.is_mesh_fwd_disabled; break; case LEAF_wlanMeshNoFwdPathUnknown: val->v.uint32 = wif->stats.is_mesh_fwd_nopath; break; case LEAF_wlanMeshDroppedBadAE: val->v.uint32 = wif->stats.is_mesh_badae; break; case LEAF_wlanMeshRouteAddFailed: val->v.uint32 = wif->stats.is_mesh_rtaddfailed; break; case LEAF_wlanMeshDroppedNoProxy: val->v.uint32 = wif->stats.is_mesh_notproxy; break; case LEAF_wlanMeshDroppedMisaligned: val->v.uint32 = wif->stats.is_rx_badalign; break; default: abort(); } return (SNMP_ERR_NOERROR); } int op_wlan_hwmp_config(struct snmp_context *ctx, struct snmp_value *val, uint32_t sub, uint32_t iidx __unused, enum snmp_op op) { int which; switch (val->var.subs[sub - 1]) { case LEAF_wlanHWMPRouteInactiveTimeout: which = WLAN_HWMP_INACTIVITY_TO; break; case LEAF_wlanHWMPRootAnnounceInterval: which = WLAN_HWMP_RANN_INT; break; case LEAF_wlanHWMPRootInterval: which = WLAN_HWMP_ROOT_INT; break; case LEAF_wlanHWMPRootTimeout: which = WLAN_HWMP_ROOT_TO; break; case LEAF_wlanHWMPPathLifetime: which = WLAN_HWMP_PATH_LIFETIME; break; case LEAF_wlanHWMPReplyForwardBit: which = WLAN_HWMP_REPLY_FORWARD; break; case LEAF_wlanHWMPTargetOnlyBit: which = WLAN_HWMP_TARGET_ONLY; break; default: abort(); } switch (op) { case SNMP_OP_GET: if (wlan_do_sysctl(&wlan_config, which, 0) < 0) return (SNMP_ERR_GENERR); break; case SNMP_OP_GETNEXT: abort(); case SNMP_OP_SET: switch (val->var.subs[sub - 1]) { case LEAF_wlanHWMPRouteInactiveTimeout: ctx->scratch->int1 = wlan_config.hwmp_inact; wlan_config.hwmp_inact = val->v.integer; break; case LEAF_wlanHWMPRootAnnounceInterval: ctx->scratch->int1 = wlan_config.hwmp_rannint; wlan_config.hwmp_rannint = val->v.integer; break; case LEAF_wlanHWMPRootInterval: ctx->scratch->int1 = wlan_config.hwmp_rootint; wlan_config.hwmp_rootint = val->v.integer; break; case LEAF_wlanHWMPRootTimeout: ctx->scratch->int1 = wlan_config.hwmp_roottimeout; wlan_config.hwmp_roottimeout = val->v.integer; break; case LEAF_wlanHWMPPathLifetime: ctx->scratch->int1 = wlan_config.hwmp_pathlifetime; wlan_config.hwmp_pathlifetime = val->v.integer; break; case LEAF_wlanHWMPReplyForwardBit: ctx->scratch->int1 = wlan_config.hwmp_replyforward; wlan_config.hwmp_replyforward = val->v.integer; break; case LEAF_wlanHWMPTargetOnlyBit: ctx->scratch->int1 = wlan_config.hwmp_targetonly; wlan_config.hwmp_targetonly = val->v.integer; break; } if (wlan_do_sysctl(&wlan_config, which, 1) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case SNMP_OP_COMMIT: return (SNMP_ERR_NOERROR); case SNMP_OP_ROLLBACK: switch (val->var.subs[sub - 1]) { case LEAF_wlanHWMPRouteInactiveTimeout: wlan_config.hwmp_inact = ctx->scratch->int1; break; case LEAF_wlanHWMPRootAnnounceInterval: wlan_config.hwmp_rannint = ctx->scratch->int1; break; case LEAF_wlanHWMPRootInterval: wlan_config.hwmp_rootint = ctx->scratch->int1; break; case LEAF_wlanHWMPRootTimeout: wlan_config.hwmp_roottimeout = ctx->scratch->int1; break; case LEAF_wlanHWMPPathLifetime: wlan_config.hwmp_pathlifetime = ctx->scratch->int1; break; case LEAF_wlanHWMPReplyForwardBit: wlan_config.hwmp_replyforward = ctx->scratch->int1; break; case LEAF_wlanHWMPTargetOnlyBit: wlan_config.hwmp_targetonly = ctx->scratch->int1; break; } if (wlan_do_sysctl(&wlan_config, which, 1) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); default: abort(); } switch (val->var.subs[sub - 1]) { case LEAF_wlanHWMPRouteInactiveTimeout: val->v.integer = wlan_config.hwmp_inact; break; case LEAF_wlanHWMPRootAnnounceInterval: val->v.integer = wlan_config.hwmp_rannint; break; case LEAF_wlanHWMPRootInterval: val->v.integer = wlan_config.hwmp_rootint; break; case LEAF_wlanHWMPRootTimeout: val->v.integer = wlan_config.hwmp_roottimeout; break; case LEAF_wlanHWMPPathLifetime: val->v.integer = wlan_config.hwmp_pathlifetime; break; case LEAF_wlanHWMPReplyForwardBit: val->v.integer = wlan_config.hwmp_replyforward; break; case LEAF_wlanHWMPTargetOnlyBit: val->v.integer = wlan_config.hwmp_targetonly; break; } return (SNMP_ERR_NOERROR); } int op_wlan_hwmp_iface(struct snmp_context *ctx, struct snmp_value *val, uint32_t sub, uint32_t iidx __unused, enum snmp_op op) { struct wlan_iface *wif; wlan_update_interface_list(); switch (op) { case SNMP_OP_GET: if ((wif = wlan_mesh_get_iface(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_GETNEXT: if ((wif = wlan_mesh_get_next_iface(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); wlan_append_ifindex(&val->var, sub, wif); break; case SNMP_OP_SET: if ((wif = wlan_mesh_get_iface(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); switch (val->var.subs[sub - 1]) { case LEAF_wlanHWMPRootMode: ctx->scratch->int1 = wif->hwmp_root_mode; wif->hwmp_root_mode = val->v.integer; break; case LEAF_wlanHWMPMaxHops: ctx->scratch->int1 = wif->hwmp_max_hops; wif->hwmp_max_hops = val->v.integer; break; default: abort(); } if (wlan_hwmp_config_set(wif, val->var.subs[sub - 1]) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case SNMP_OP_COMMIT: return (SNMP_ERR_NOERROR); case SNMP_OP_ROLLBACK: if ((wif = wlan_mesh_get_iface(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); switch (val->var.subs[sub - 1]) { case LEAF_wlanHWMPRootMode: wif->hwmp_root_mode = ctx->scratch->int1; break; case LEAF_wlanHWMPMaxHops: wif->hwmp_max_hops = ctx->scratch->int1; break; default: abort(); } if (wlan_hwmp_config_set(wif, val->var.subs[sub - 1]) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); default: abort(); } if (wlan_hwmp_config_get(wif, val->var.subs[sub - 1]) < 0) return (SNMP_ERR_GENERR); switch (val->var.subs[sub - 1]) { case LEAF_wlanHWMPRootMode: val->v.integer = wif->hwmp_root_mode; break; case LEAF_wlanHWMPMaxHops: val->v.integer = wif->hwmp_max_hops; break; default: abort(); } return (SNMP_ERR_NOERROR); } int op_wlan_hwmp_stats(struct snmp_context *ctx __unused, struct snmp_value *val, uint32_t sub, uint32_t iidx __unused, enum snmp_op op) { struct wlan_iface *wif; wlan_update_interface_list(); switch (op) { case SNMP_OP_GET: if ((wif = wlan_mesh_get_iface(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_GETNEXT: if ((wif = wlan_mesh_get_next_iface(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); wlan_append_ifindex(&val->var, sub, wif); break; case SNMP_OP_SET: return (SNMP_ERR_NOT_WRITEABLE); case SNMP_OP_COMMIT: /* FALLTHROUGH */ case SNMP_OP_ROLLBACK: /* FALLTHROUGH */ default: abort(); } if (wlan_get_stats(wif) < 0) return (SNMP_ERR_GENERR); switch (val->var.subs[sub - 1]) { case LEAF_wlanMeshHWMPWrongSeqNo: val->v.uint32 = wif->stats.is_hwmp_wrongseq; break; case LEAF_wlanMeshHWMPTxRootPREQ: val->v.uint32 = wif->stats.is_hwmp_rootreqs; break; case LEAF_wlanMeshHWMPTxRootRANN: val->v.uint32 = wif->stats.is_hwmp_rootrann; break; case LEAF_wlanMeshHWMPProxy: val->v.uint32 = wif->stats.is_hwmp_proxy; break; default: abort(); } return (SNMP_ERR_NOERROR); } /* * Encode BITS type for a response packet - XXX: this belongs to the snmp lib. */ static int bits_get(struct snmp_value *value, const u_char *ptr, ssize_t len) { int size; if (ptr == NULL) { value->v.octetstring.len = 0; value->v.octetstring.octets = NULL; return (SNMP_ERR_NOERROR); } /* Determine length - up to 8 octets supported so far. */ for (size = len; size > 0; size--) if (ptr[size - 1] != 0) break; if (size == 0) size = 1; value->v.octetstring.len = (u_long)size; if ((value->v.octetstring.octets = malloc((size_t)size)) == NULL) return (SNMP_ERR_RES_UNAVAIL); memcpy(value->v.octetstring.octets, ptr, (size_t)size); return (SNMP_ERR_NOERROR); } /* * Calls for adding/updating/freeing/etc of wireless interfaces. */ static void wlan_free_interface(struct wlan_iface *wif) { wlan_free_peerlist(wif); free(wif->chanlist); wlan_scan_free_results(wif); wlan_mac_free_maclist(wif); wlan_mesh_free_routes(wif); free(wif); } static void wlan_free_iflist(void) { struct wlan_iface *w; while ((w = SLIST_FIRST(&wlan_ifaces)) != NULL) { SLIST_REMOVE_HEAD(&wlan_ifaces, w_if); wlan_free_interface(w); } } static struct wlan_iface * wlan_find_interface(const char *wname) { struct wlan_iface *wif; SLIST_FOREACH(wif, &wlan_ifaces, w_if) if (strcmp(wif->wname, wname) == 0) { if (wif->status != RowStatus_active) return (NULL); break; } return (wif); } static struct wlan_iface * wlan_first_interface(void) { return (SLIST_FIRST(&wlan_ifaces)); } static struct wlan_iface * wlan_next_interface(struct wlan_iface *wif) { if (wif == NULL) return (NULL); return (SLIST_NEXT(wif, w_if)); } /* * Add a new interface to the list - sorted by name. */ static int wlan_add_wif(struct wlan_iface *wif) { int cmp; struct wlan_iface *temp, *prev; if ((prev = SLIST_FIRST(&wlan_ifaces)) == NULL || strcmp(wif->wname, prev->wname) < 0) { SLIST_INSERT_HEAD(&wlan_ifaces, wif, w_if); return (0); } SLIST_FOREACH(temp, &wlan_ifaces, w_if) { if ((cmp = strcmp(wif->wname, temp->wname)) <= 0) break; prev = temp; } if (temp == NULL) SLIST_INSERT_AFTER(prev, wif, w_if); else if (cmp > 0) SLIST_INSERT_AFTER(temp, wif, w_if); else { syslog(LOG_ERR, "Wlan iface %s already in list", wif->wname); return (-1); } return (0); } static struct wlan_iface * wlan_new_wif(char *wname) { struct wlan_iface *wif; /* Make sure it's not in the list. */ for (wif = wlan_first_interface(); wif != NULL; wif = wlan_next_interface(wif)) if (strcmp(wname, wif->wname) == 0) { wif->internal = 0; return (wif); } if ((wif = (struct wlan_iface *)malloc(sizeof(*wif))) == NULL) return (NULL); memset(wif, 0, sizeof(struct wlan_iface)); strlcpy(wif->wname, wname, IFNAMSIZ); wif->status = RowStatus_notReady; wif->state = wlanIfaceState_down; wif->mode = WlanIfaceOperatingModeType_station; if (wlan_add_wif(wif) < 0) { free(wif); return (NULL); } return (wif); } static void wlan_delete_wif(struct wlan_iface *wif) { SLIST_REMOVE(&wlan_ifaces, wif, wlan_iface, w_if); wlan_free_interface(wif); } static int wlan_attach_newif(struct mibif *mif) { struct wlan_iface *wif; if (mif->mib.ifmd_data.ifi_type != IFT_ETHER || wlan_check_media(mif->name) != IFM_IEEE80211) return (0); if ((wif = wlan_new_wif(mif->name)) == NULL) return (-1); (void)wlan_get_opmode(wif); wif->index = mif->index; wif->status = RowStatus_active; (void)wlan_update_interface(wif); return (0); } static int wlan_iface_create(struct wlan_iface *wif) { int rc; if ((rc = wlan_clone_create(wif)) == SNMP_ERR_NOERROR) { /* * The rest of the info will be updated once the * snmp_mibII module notifies us of the interface. */ wif->status = RowStatus_active; if (wif->state == wlanIfaceState_up) (void)wlan_config_state(wif, 1); } return (rc); } static int wlan_iface_destroy(struct wlan_iface *wif) { int rc = SNMP_ERR_NOERROR; if (wif->internal == 0) rc = wlan_clone_destroy(wif); if (rc == SNMP_ERR_NOERROR) wlan_delete_wif(wif); return (rc); } static int wlan_update_interface(struct wlan_iface *wif) { int i; (void)wlan_config_state(wif, 0); (void)wlan_get_driver_caps(wif); for (i = LEAF_wlanIfacePacketBurst; i <= LEAF_wlanIfaceTdmaBeaconInterval; i++) (void)wlan_config_get_ioctl(wif, i); (void)wlan_get_stats(wif); /* * XXX: wlan_get_channel_list() not needed - * fetched with wlan_get_driver_caps() */ (void)wlan_get_channel_list(wif); (void)wlan_get_roam_params(wif); (void)wlan_get_tx_params(wif); (void)wlan_get_scan_results(wif); (void)wlan_get_wepmode(wif); (void)wlan_get_weptxkey(wif); (void)wlan_get_mac_policy(wif); (void)wlan_get_mac_acl_macs(wif); (void)wlan_get_peerinfo(wif); if (wif->mode == WlanIfaceOperatingModeType_meshPoint) { for (i = LEAF_wlanMeshTTL; i <= LEAF_wlanMeshPath; i++) (void)wlan_mesh_config_get(wif, i); (void)wlan_mesh_get_routelist(wif); for (i = LEAF_wlanHWMPRootMode; i <= LEAF_wlanHWMPMaxHops; i++) (void)wlan_hwmp_config_get(wif, i); } return (0); } static void wlan_update_interface_list(void) { struct wlan_iface *wif, *twif; if ((time(NULL) - wlan_iflist_age) <= WLAN_LIST_MAXAGE) return; /* * The snmp_mibII module would have notified us for new interfaces, * so only check if any have been deleted. */ SLIST_FOREACH_SAFE(wif, &wlan_ifaces, w_if, twif) if (wif->status == RowStatus_active && wlan_get_opmode(wif) < 0) wlan_delete_wif(wif); wlan_iflist_age = time(NULL); } static void wlan_append_ifindex(struct asn_oid *oid, uint sub, const struct wlan_iface *w) { uint32_t i; oid->len = sub + strlen(w->wname) + 1; oid->subs[sub] = strlen(w->wname); for (i = 1; i <= strlen(w->wname); i++) oid->subs[sub + i] = w->wname[i - 1]; } static uint8_t * wlan_get_ifname(const struct asn_oid *oid, uint sub, uint8_t *wname) { uint32_t i; memset(wname, 0, IFNAMSIZ); if (oid->len - sub != oid->subs[sub] + 1 || oid->subs[sub] >= IFNAMSIZ) return (NULL); for (i = 0; i < oid->subs[sub]; i++) wname[i] = oid->subs[sub + i + 1]; wname[i] = '\0'; return (wname); } static struct wlan_iface * wlan_get_interface(const struct asn_oid *oid, uint sub) { uint8_t wname[IFNAMSIZ]; if (wlan_get_ifname(oid, sub, wname) == NULL) return (NULL); return (wlan_find_interface(wname)); } static struct wlan_iface * wlan_get_next_interface(const struct asn_oid *oid, uint sub) { uint32_t i; uint8_t wname[IFNAMSIZ]; struct wlan_iface *wif; if (oid->len - sub == 0) { for (wif = wlan_first_interface(); wif != NULL; wif = wlan_next_interface(wif)) if (wif->status == RowStatus_active) break; return (wif); } if (oid->len - sub != oid->subs[sub] + 1 || oid->subs[sub] >= IFNAMSIZ) return (NULL); memset(wname, 0, IFNAMSIZ); for (i = 0; i < oid->subs[sub]; i++) wname[i] = oid->subs[sub + i + 1]; wname[i] = '\0'; if ((wif = wlan_find_interface(wname)) == NULL) return (NULL); while ((wif = wlan_next_interface(wif)) != NULL) if (wif->status == RowStatus_active) break; return (wif); } static struct wlan_iface * wlan_get_snmp_interface(const struct asn_oid *oid, uint sub) { uint8_t wname[IFNAMSIZ]; struct wlan_iface *wif; if (wlan_get_ifname(oid, sub, wname) == NULL) return (NULL); for (wif = wlan_first_interface(); wif != NULL; wif = wlan_next_interface(wif)) if (strcmp(wif->wname, wname) == 0) break; return (wif); } static struct wlan_iface * wlan_get_next_snmp_interface(const struct asn_oid *oid, uint sub) { uint32_t i; uint8_t wname[IFNAMSIZ]; struct wlan_iface *wif; if (oid->len - sub == 0) return (wlan_first_interface()); if (oid->len - sub != oid->subs[sub] + 1 || oid->subs[sub] >= IFNAMSIZ) return (NULL); memset(wname, 0, IFNAMSIZ); for (i = 0; i < oid->subs[sub]; i++) wname[i] = oid->subs[sub + i + 1]; wname[i] = '\0'; for (wif = wlan_first_interface(); wif != NULL; wif = wlan_next_interface(wif)) if (strcmp(wif->wname, wname) == 0) break; return (wlan_next_interface(wif)); } /* * Decode/Append an index for tables indexed by the wireless interface * name and a MAC address - ACL MACs and Mesh Routes. */ static int wlan_mac_index_decode(const struct asn_oid *oid, uint sub, char *wname, uint8_t *mac) { uint32_t i; int mac_off; if (oid->len - sub != oid->subs[sub] + 2 + IEEE80211_ADDR_LEN || oid->subs[sub] >= IFNAMSIZ) return (-1); for (i = 0; i < oid->subs[sub]; i++) wname[i] = oid->subs[sub + i + 1]; wname[i] = '\0'; mac_off = sub + oid->subs[sub] + 1; if (oid->subs[mac_off] != IEEE80211_ADDR_LEN) return (-1); for (i = 0; i < IEEE80211_ADDR_LEN; i++) mac[i] = oid->subs[mac_off + i + 1]; return (0); } static void wlan_append_mac_index(struct asn_oid *oid, uint sub, char *wname, uint8_t *mac) { uint32_t i; oid->len = sub + strlen(wname) + IEEE80211_ADDR_LEN + 2; oid->subs[sub] = strlen(wname); for (i = 1; i <= strlen(wname); i++) oid->subs[sub + i] = wname[i - 1]; sub += strlen(wname) + 1; oid->subs[sub] = IEEE80211_ADDR_LEN; for (i = 1; i <= IEEE80211_ADDR_LEN; i++) oid->subs[sub + i] = mac[i - 1]; } /* * Decode/Append an index for tables indexed by the wireless interface * name and the PHY mode - Roam and TX params. */ static int wlan_phy_index_decode(const struct asn_oid *oid, uint sub, char *wname, uint32_t *phy) { uint32_t i; if (oid->len - sub != oid->subs[sub] + 2 || oid->subs[sub] >= IFNAMSIZ) return (-1); for (i = 0; i < oid->subs[sub]; i++) wname[i] = oid->subs[sub + i + 1]; wname[i] = '\0'; *phy = oid->subs[sub + oid->subs[sub] + 1]; return (0); } static void wlan_append_phy_index(struct asn_oid *oid, uint sub, char *wname, uint32_t phy) { uint32_t i; oid->len = sub + strlen(wname) + 2; oid->subs[sub] = strlen(wname); for (i = 1; i <= strlen(wname); i++) oid->subs[sub + i] = wname[i - 1]; oid->subs[sub + strlen(wname) + 1] = phy; } /* * Calls for manipulating the peerlist of a wireless interface. */ static void wlan_free_peerlist(struct wlan_iface *wif) { struct wlan_peer *wip; while ((wip = SLIST_FIRST(&wif->peerlist)) != NULL) { SLIST_REMOVE_HEAD(&wif->peerlist, wp); free(wip); } SLIST_INIT(&wif->peerlist); } static struct wlan_peer * wlan_find_peer(struct wlan_iface *wif, uint8_t *peermac) { struct wlan_peer *wip; SLIST_FOREACH(wip, &wif->peerlist, wp) if (memcmp(wip->pmac, peermac, IEEE80211_ADDR_LEN) == 0) break; return (wip); } struct wlan_peer * wlan_new_peer(const uint8_t *pmac) { struct wlan_peer *wip; if ((wip = (struct wlan_peer *)malloc(sizeof(*wip))) == NULL) return (NULL); memset(wip, 0, sizeof(struct wlan_peer)); memcpy(wip->pmac, pmac, IEEE80211_ADDR_LEN); return (wip); } void wlan_free_peer(struct wlan_peer *wip) { free(wip); } int wlan_add_peer(struct wlan_iface *wif, struct wlan_peer *wip) { struct wlan_peer *temp, *prev; SLIST_FOREACH(temp, &wif->peerlist, wp) if (memcmp(temp->pmac, wip->pmac, IEEE80211_ADDR_LEN) == 0) return (-1); if ((prev = SLIST_FIRST(&wif->peerlist)) == NULL || memcmp(wip->pmac, prev->pmac, IEEE80211_ADDR_LEN) < 0) { SLIST_INSERT_HEAD(&wif->peerlist, wip, wp); return (0); } SLIST_FOREACH(temp, &wif->peerlist, wp) { if (memcmp(wip->pmac, temp->pmac, IEEE80211_ADDR_LEN) < 0) break; prev = temp; } SLIST_INSERT_AFTER(prev, wip, wp); return (0); } static void wlan_update_peers(void) { struct wlan_iface *wif; if ((time(NULL) - wlan_peerlist_age) <= WLAN_LIST_MAXAGE) return; for (wif = wlan_first_interface(); wif != NULL; wif = wlan_next_interface(wif)) { if (wif->status != RowStatus_active) continue; wlan_free_peerlist(wif); (void)wlan_get_peerinfo(wif); } wlan_peerlist_age = time(NULL); } static struct wlan_peer * wlan_get_peer(const struct asn_oid *oid, uint sub, struct wlan_iface **wif) { char wname[IFNAMSIZ]; uint8_t pmac[IEEE80211_ADDR_LEN]; if (wlan_mac_index_decode(oid, sub, wname, pmac) < 0) return (NULL); if ((*wif = wlan_find_interface(wname)) == NULL) return (NULL); return (wlan_find_peer(*wif, pmac)); } static struct wlan_peer * wlan_get_next_peer(const struct asn_oid *oid, uint sub, struct wlan_iface **wif) { char wname[IFNAMSIZ]; char pmac[IEEE80211_ADDR_LEN]; struct wlan_peer *wip; if (oid->len - sub == 0) { for (*wif = wlan_first_interface(); *wif != NULL; *wif = wlan_next_interface(*wif)) { if ((*wif)->mode == WlanIfaceOperatingModeType_meshPoint) continue; wip = SLIST_FIRST(&(*wif)->peerlist); if (wip != NULL) return (wip); } return (NULL); } if (wlan_mac_index_decode(oid, sub, wname, pmac) < 0 || (*wif = wlan_find_interface(wname)) == NULL || (wip = wlan_find_peer(*wif, pmac)) == NULL) return (NULL); if ((wip = SLIST_NEXT(wip, wp)) != NULL) return (wip); while ((*wif = wlan_next_interface(*wif)) != NULL) { if ((*wif)->mode == WlanIfaceOperatingModeType_meshPoint) continue; if ((wip = SLIST_FIRST(&(*wif)->peerlist)) != NULL) break; } return (wip); } /* * Calls for manipulating the active channel list of a wireless interface. */ static void wlan_update_channels(void) { struct wlan_iface *wif; if ((time(NULL) - wlan_chanlist_age) <= WLAN_LIST_MAXAGE) return; for (wif = wlan_first_interface(); wif != NULL; wif = wlan_next_interface(wif)) { if (wif->status != RowStatus_active) continue; (void)wlan_get_channel_list(wif); } wlan_chanlist_age = time(NULL); } static int wlan_channel_index_decode(const struct asn_oid *oid, uint sub, char *wname, uint32_t *cindex) { uint32_t i; if (oid->len - sub != oid->subs[sub] + 2 || oid->subs[sub] >= IFNAMSIZ) return (-1); for (i = 0; i < oid->subs[sub]; i++) wname[i] = oid->subs[sub + i + 1]; wname[i] = '\0'; *cindex = oid->subs[sub + oid->subs[sub] + 1]; return (0); } static void wlan_append_channel_index(struct asn_oid *oid, uint sub, const struct wlan_iface *wif, const struct ieee80211_channel *channel) { uint32_t i; oid->len = sub + strlen(wif->wname) + 2; oid->subs[sub] = strlen(wif->wname); for (i = 1; i <= strlen(wif->wname); i++) oid->subs[sub + i] = wif->wname[i - 1]; oid->subs[sub + strlen(wif->wname) + 1] = (channel - wif->chanlist) + 1; } static int32_t wlan_get_channel_type(struct ieee80211_channel *c) { if (IEEE80211_IS_CHAN_FHSS(c)) return (WlanChannelType_fhss); if (IEEE80211_IS_CHAN_A(c)) return (WlanChannelType_dot11a); if (IEEE80211_IS_CHAN_B(c)) return (WlanChannelType_dot11b); if (IEEE80211_IS_CHAN_ANYG(c)) return (WlanChannelType_dot11g); if (IEEE80211_IS_CHAN_HALF(c)) return (WlanChannelType_tenMHz); if (IEEE80211_IS_CHAN_QUARTER(c)) return (WlanChannelType_fiveMHz); if (IEEE80211_IS_CHAN_TURBO(c)) return (WlanChannelType_turbo); if (IEEE80211_IS_CHAN_HT(c)) return (WlanChannelType_ht); - + return (-1); } static struct ieee80211_channel * wlan_find_channel(struct wlan_iface *wif, uint32_t cindex) { if (wif->chanlist == NULL || cindex > wif->nchannels) return (NULL); return (wif->chanlist + cindex - 1); } static struct ieee80211_channel * wlan_get_channel(const struct asn_oid *oid, uint sub, struct wlan_iface **wif) { uint32_t cindex; char wname[IFNAMSIZ]; if (wlan_channel_index_decode(oid, sub, wname, &cindex) < 0) return (NULL); if ((*wif = wlan_find_interface(wname)) == NULL) return (NULL); return (wlan_find_channel(*wif, cindex)); } static struct ieee80211_channel * wlan_get_next_channel(const struct asn_oid *oid, uint sub, struct wlan_iface **wif) { uint32_t cindex; char wname[IFNAMSIZ]; if (oid->len - sub == 0) { for (*wif = wlan_first_interface(); *wif != NULL; *wif = wlan_next_interface(*wif)) { if ((*wif)->status != RowStatus_active) continue; if ((*wif)->nchannels != 0 && (*wif)->chanlist != NULL) return ((*wif)->chanlist); } return (NULL); } if (wlan_channel_index_decode(oid, sub, wname, &cindex) < 0) return (NULL); if ((*wif = wlan_find_interface(wname)) == NULL) return (NULL); if (cindex < (*wif)->nchannels) return ((*wif)->chanlist + cindex); while ((*wif = wlan_next_interface(*wif)) != NULL) if ((*wif)->status == RowStatus_active) if ((*wif)->nchannels != 0 && (*wif)->chanlist != NULL) return ((*wif)->chanlist); return (NULL); } /* * Calls for manipulating the roam params of a wireless interface. */ static void wlan_update_roam_params(void) { struct wlan_iface *wif; if ((time(NULL) - wlan_roamlist_age) <= WLAN_LIST_MAXAGE) return; for (wif = wlan_first_interface(); wif != NULL; wif = wlan_next_interface(wif)) { if (wif->status != RowStatus_active) continue; (void)wlan_get_roam_params(wif); } wlan_roamlist_age = time(NULL); } static struct ieee80211_roamparam * wlan_get_roam_param(const struct asn_oid *oid, uint sub, struct wlan_iface **wif) { uint32_t phy; char wname[IFNAMSIZ]; if (wlan_phy_index_decode(oid, sub, wname, &phy) < 0) return (NULL); if ((*wif = wlan_find_interface(wname)) == NULL) return (NULL); if (phy == 0 || phy > IEEE80211_MODE_MAX) return (NULL); return ((*wif)->roamparams.params + phy - 1); } static struct ieee80211_roamparam * wlan_get_next_roam_param(const struct asn_oid *oid, uint sub, struct wlan_iface **wif, uint32_t *phy) { char wname[IFNAMSIZ]; if (oid->len - sub == 0) { for (*wif = wlan_first_interface(); *wif != NULL; *wif = wlan_next_interface(*wif)) { if ((*wif)->status != RowStatus_active) continue; *phy = 1; return ((*wif)->roamparams.params); } return (NULL); } if (wlan_phy_index_decode(oid, sub, wname, phy) < 0) return (NULL); if (*phy == 0 || (*wif = wlan_find_interface(wname)) == NULL) return (NULL); if (++(*phy) <= IEEE80211_MODE_MAX) return ((*wif)->roamparams.params + *phy - 1); *phy = 1; while ((*wif = wlan_next_interface(*wif)) != NULL) if ((*wif)->status == RowStatus_active) return ((*wif)->roamparams.params); return (NULL); } /* * Calls for manipulating the tx params of a wireless interface. */ static void wlan_update_tx_params(void) { struct wlan_iface *wif; if ((time(NULL) - wlan_tx_paramlist_age) <= WLAN_LIST_MAXAGE) return; for (wif = wlan_first_interface(); wif != NULL; wif = wlan_next_interface(wif)) { if (wif->status != RowStatus_active) continue; (void)wlan_get_tx_params(wif); } wlan_tx_paramlist_age = time(NULL); } static struct ieee80211_txparam * wlan_get_tx_param(const struct asn_oid *oid, uint sub, struct wlan_iface **wif, uint32_t *phy) { char wname[IFNAMSIZ]; if (wlan_phy_index_decode(oid, sub, wname, phy) < 0) return (NULL); if ((*wif = wlan_find_interface(wname)) == NULL) return (NULL); if (*phy == 0 || *phy > IEEE80211_MODE_MAX) return (NULL); return ((*wif)->txparams.params + *phy - 1); } static struct ieee80211_txparam * wlan_get_next_tx_param(const struct asn_oid *oid, uint sub, struct wlan_iface **wif, uint32_t *phy) { char wname[IFNAMSIZ]; if (oid->len - sub == 0) { for (*wif = wlan_first_interface(); *wif != NULL; *wif = wlan_next_interface(*wif)) { if ((*wif)->status != RowStatus_active) continue; *phy = 1; return ((*wif)->txparams.params); } return (NULL); } if (wlan_phy_index_decode(oid, sub, wname, phy) < 0) return (NULL); if (*phy == 0 || (*wif = wlan_find_interface(wname)) == NULL) return (NULL); if (++(*phy) <= IEEE80211_MODE_MAX) return ((*wif)->txparams.params + *phy - 1); *phy = 1; while ((*wif = wlan_next_interface(*wif)) != NULL) if ((*wif)->status == RowStatus_active) return ((*wif)->txparams.params); return (NULL); } /* * Calls for manipulating the scan results for a wireless interface. */ static void wlan_scan_free_results(struct wlan_iface *wif) { struct wlan_scan_result *sr; while ((sr = SLIST_FIRST(&wif->scanlist)) != NULL) { SLIST_REMOVE_HEAD(&wif->scanlist, wsr); free(sr); } SLIST_INIT(&wif->scanlist); } static struct wlan_scan_result * wlan_scan_find_result(struct wlan_iface *wif, uint8_t *ssid, uint8_t *bssid) { struct wlan_scan_result *sr; SLIST_FOREACH(sr, &wif->scanlist, wsr) if (strlen(ssid) == strlen(sr->ssid) && strcmp(sr->ssid, ssid) == 0 && memcmp(sr->bssid, bssid, IEEE80211_ADDR_LEN) == 0) break; return (sr); } struct wlan_scan_result * wlan_scan_new_result(const uint8_t *ssid, const uint8_t *bssid) { struct wlan_scan_result *sr; sr = (struct wlan_scan_result *)malloc(sizeof(*sr)); if (sr == NULL) return (NULL); memset(sr, 0, sizeof(*sr)); if (ssid[0] != '\0') strlcpy(sr->ssid, ssid, IEEE80211_NWID_LEN + 1); memcpy(sr->bssid, bssid, IEEE80211_ADDR_LEN); return (sr); } void wlan_scan_free_result(struct wlan_scan_result *sr) { free(sr); } static int wlan_scan_compare_result(struct wlan_scan_result *sr1, struct wlan_scan_result *sr2) { uint32_t i; if (strlen(sr1->ssid) < strlen(sr2->ssid)) return (-1); if (strlen(sr1->ssid) > strlen(sr2->ssid)) return (1); for (i = 0; i < strlen(sr1->ssid) && i < strlen(sr2->ssid); i++) { if (sr1->ssid[i] < sr2->ssid[i]) return (-1); if (sr1->ssid[i] > sr2->ssid[i]) return (1); } for (i = 0; i < IEEE80211_ADDR_LEN; i++) { if (sr1->bssid[i] < sr2->bssid[i]) return (-1); if (sr1->bssid[i] > sr2->bssid[i]) return (1); } return (0); } int wlan_scan_add_result(struct wlan_iface *wif, struct wlan_scan_result *sr) { struct wlan_scan_result *prev, *temp; SLIST_FOREACH(temp, &wif->scanlist, wsr) if (strlen(temp->ssid) == strlen(sr->ssid) && strcmp(sr->ssid, temp->ssid) == 0 && memcmp(sr->bssid, temp->bssid, IEEE80211_ADDR_LEN) == 0) return (-1); if ((prev = SLIST_FIRST(&wif->scanlist)) == NULL || wlan_scan_compare_result(sr, prev) < 0) { SLIST_INSERT_HEAD(&wif->scanlist, sr, wsr); return (0); } - + SLIST_FOREACH(temp, &wif->scanlist, wsr) { if (wlan_scan_compare_result(sr, temp) < 0) break; prev = temp; } SLIST_INSERT_AFTER(prev, sr, wsr); return (0); } static void wlan_scan_update_results(void) { struct wlan_iface *wif; if ((time(NULL) - wlan_scanlist_age) <= WLAN_LIST_MAXAGE) return; for (wif = wlan_first_interface(); wif != NULL; wif = wlan_next_interface(wif)) { if (wif->status != RowStatus_active) continue; wlan_scan_free_results(wif); (void)wlan_get_scan_results(wif); } wlan_scanlist_age = time(NULL); } static int wlan_scanr_index_decode(const struct asn_oid *oid, uint sub, char *wname, uint8_t *ssid, uint8_t *bssid) { uint32_t i; int offset; if (oid->subs[sub] >= IFNAMSIZ) return (-1); for (i = 0; i < oid->subs[sub]; i++) wname[i] = oid->subs[sub + i + 1]; wname[oid->subs[sub]] = '\0'; offset = sub + oid->subs[sub] + 1; if (oid->subs[offset] > IEEE80211_NWID_LEN) return (-1); for (i = 0; i < oid->subs[offset]; i++) ssid[i] = oid->subs[offset + i + 1]; ssid[i] = '\0'; offset = sub + oid->subs[sub] + oid->subs[offset] + 2; if (oid->subs[offset] != IEEE80211_ADDR_LEN) return (-1); for (i = 0; i < IEEE80211_ADDR_LEN; i++) bssid[i] = oid->subs[offset + i + 1]; return (0); } static void wlan_append_scanr_index(struct asn_oid *oid, uint sub, char *wname, uint8_t *ssid, uint8_t *bssid) { uint32_t i; oid->len = sub + strlen(wname) + strlen(ssid) + IEEE80211_ADDR_LEN + 3; oid->subs[sub] = strlen(wname); for (i = 1; i <= strlen(wname); i++) oid->subs[sub + i] = wname[i - 1]; sub += strlen(wname) + 1; oid->subs[sub] = strlen(ssid); for (i = 1; i <= strlen(ssid); i++) oid->subs[sub + i] = ssid[i - 1]; sub += strlen(ssid) + 1; oid->subs[sub] = IEEE80211_ADDR_LEN; for (i = 1; i <= IEEE80211_ADDR_LEN; i++) oid->subs[sub + i] = bssid[i - 1]; } static struct wlan_scan_result * wlan_get_scanr(const struct asn_oid *oid, uint sub, struct wlan_iface **wif) { char wname[IFNAMSIZ]; uint8_t ssid[IEEE80211_NWID_LEN + 1]; uint8_t bssid[IEEE80211_ADDR_LEN]; if (wlan_scanr_index_decode(oid, sub, wname, ssid, bssid) < 0) return (NULL); if ((*wif = wlan_find_interface(wname)) == NULL) return (NULL); return (wlan_scan_find_result(*wif, ssid, bssid)); } static struct wlan_scan_result * wlan_get_next_scanr(const struct asn_oid *oid, uint sub, struct wlan_iface **wif) { char wname[IFNAMSIZ]; uint8_t ssid[IEEE80211_NWID_LEN + 1]; uint8_t bssid[IEEE80211_ADDR_LEN]; struct wlan_scan_result *sr; if (oid->len - sub == 0) { for (*wif = wlan_first_interface(); *wif != NULL; *wif = wlan_next_interface(*wif)) { sr = SLIST_FIRST(&(*wif)->scanlist); if (sr != NULL) return (sr); } return (NULL); } if (wlan_scanr_index_decode(oid, sub, wname, ssid, bssid) < 0 || (*wif = wlan_find_interface(wname)) == NULL || (sr = wlan_scan_find_result(*wif, ssid, bssid)) == NULL) return (NULL); if ((sr = SLIST_NEXT(sr, wsr)) != NULL) return (sr); while ((*wif = wlan_next_interface(*wif)) != NULL) if ((sr = SLIST_FIRST(&(*wif)->scanlist)) != NULL) break; return (sr); } /* * MAC Access Control. */ static void wlan_mac_free_maclist(struct wlan_iface *wif) { struct wlan_mac_mac *wmm; while ((wmm = SLIST_FIRST(&wif->mac_maclist)) != NULL) { SLIST_REMOVE_HEAD(&wif->mac_maclist, wm); free(wmm); } SLIST_INIT(&wif->mac_maclist); } static struct wlan_mac_mac * wlan_mac_find_mac(struct wlan_iface *wif, uint8_t *mac) { struct wlan_mac_mac *wmm; SLIST_FOREACH(wmm, &wif->mac_maclist, wm) if (memcmp(wmm->mac, mac, IEEE80211_ADDR_LEN) == 0) break; return (wmm); } struct wlan_mac_mac * wlan_mac_new_mac(const uint8_t *mac) { struct wlan_mac_mac *wmm; if ((wmm = (struct wlan_mac_mac *)malloc(sizeof(*wmm))) == NULL) return (NULL); memset(wmm, 0, sizeof(*wmm)); memcpy(wmm->mac, mac, IEEE80211_ADDR_LEN); wmm->mac_status = RowStatus_notReady; return (wmm); } void wlan_mac_free_mac(struct wlan_mac_mac *wmm) { free(wmm); } int wlan_mac_add_mac(struct wlan_iface *wif, struct wlan_mac_mac *wmm) { struct wlan_mac_mac *temp, *prev; SLIST_FOREACH(temp, &wif->mac_maclist, wm) if (memcmp(temp->mac, wmm->mac, IEEE80211_ADDR_LEN) == 0) return (-1); if ((prev = SLIST_FIRST(&wif->mac_maclist)) == NULL || memcmp(wmm->mac, prev->mac,IEEE80211_ADDR_LEN) < 0) { SLIST_INSERT_HEAD(&wif->mac_maclist, wmm, wm); return (0); } SLIST_FOREACH(temp, &wif->mac_maclist, wm) { if (memcmp(wmm->mac, temp->mac, IEEE80211_ADDR_LEN) < 0) break; prev = temp; } SLIST_INSERT_AFTER(prev, wmm, wm); return (0); } static int wlan_mac_delete_mac(struct wlan_iface *wif, struct wlan_mac_mac *wmm) { if (wmm->mac_status == RowStatus_active && wlan_del_mac_acl_mac(wif, wmm) < 0) return (-1); SLIST_REMOVE(&wif->mac_maclist, wmm, wlan_mac_mac, wm); free(wmm); return (0); } static void wlan_mac_update_aclmacs(void) { struct wlan_iface *wif; struct wlan_mac_mac *wmm, *twmm; if ((time(NULL) - wlan_maclist_age) <= WLAN_LIST_MAXAGE) return; for (wif = wlan_first_interface(); wif != NULL; wif = wlan_next_interface(wif)) { if (wif->status != RowStatus_active) continue; /* * Nuke old entries - XXX - they are likely not to * change often - reconsider. */ SLIST_FOREACH_SAFE(wmm, &wif->mac_maclist, wm, twmm) if (wmm->mac_status == RowStatus_active) { SLIST_REMOVE(&wif->mac_maclist, wmm, wlan_mac_mac, wm); wlan_mac_free_mac(wmm); } (void)wlan_get_mac_acl_macs(wif); } wlan_maclist_age = time(NULL); } static struct wlan_mac_mac * wlan_get_acl_mac(const struct asn_oid *oid, uint sub, struct wlan_iface **wif) { char wname[IFNAMSIZ]; char mac[IEEE80211_ADDR_LEN]; if (wlan_mac_index_decode(oid, sub, wname, mac) < 0) return (NULL); if ((*wif = wlan_find_interface(wname)) == NULL) return (NULL); return (wlan_mac_find_mac(*wif, mac)); } static struct wlan_mac_mac * wlan_get_next_acl_mac(const struct asn_oid *oid, uint sub, struct wlan_iface **wif) { char wname[IFNAMSIZ]; char mac[IEEE80211_ADDR_LEN]; struct wlan_mac_mac *wmm; if (oid->len - sub == 0) { for (*wif = wlan_first_interface(); *wif != NULL; *wif = wlan_next_interface(*wif)) { wmm = SLIST_FIRST(&(*wif)->mac_maclist); if (wmm != NULL) return (wmm); } return (NULL); } if (wlan_mac_index_decode(oid, sub, wname, mac) < 0 || (*wif = wlan_find_interface(wname)) == NULL || (wmm = wlan_mac_find_mac(*wif, mac)) == NULL) return (NULL); if ((wmm = SLIST_NEXT(wmm, wm)) != NULL) return (wmm); while ((*wif = wlan_next_interface(*wif)) != NULL) if ((wmm = SLIST_FIRST(&(*wif)->mac_maclist)) != NULL) break; return (wmm); } static int wlan_acl_mac_set_status(struct snmp_context *ctx, struct snmp_value *val, uint sub) { char wname[IFNAMSIZ]; uint8_t mac[IEEE80211_ADDR_LEN]; struct wlan_iface *wif; struct wlan_mac_mac *macl; if (wlan_mac_index_decode(&val->var, sub, wname, mac) < 0) return (SNMP_ERR_GENERR); macl = wlan_get_acl_mac(&val->var, sub, &wif); switch (val->v.integer) { case RowStatus_createAndGo: if (macl != NULL) return (SNMP_ERR_INCONS_NAME); break; case RowStatus_destroy: if (macl == NULL) return (SNMP_ERR_NOSUCHNAME); ctx->scratch->int1 = RowStatus_active; return (SNMP_ERR_NOERROR); default: return (SNMP_ERR_INCONS_VALUE); } if (wif == NULL || !wif->macsupported) return (SNMP_ERR_INCONS_VALUE); if ((macl = wlan_mac_new_mac((const uint8_t *)mac)) == NULL) return (SNMP_ERR_GENERR); ctx->scratch->int1 = RowStatus_destroy; if (wlan_mac_add_mac(wif, macl) < 0) { wlan_mac_free_mac(macl); return (SNMP_ERR_GENERR); } ctx->scratch->int1 = RowStatus_destroy; if (wlan_add_mac_acl_mac(wif, macl) < 0) { (void)wlan_mac_delete_mac(wif, macl); return (SNMP_ERR_GENERR); } return (SNMP_ERR_NOERROR); } /* * Wireless interfaces operating as mesh points. */ static struct wlan_iface * wlan_mesh_first_interface(void) { struct wlan_iface *wif; SLIST_FOREACH(wif, &wlan_ifaces, w_if) if (wif->mode == WlanIfaceOperatingModeType_meshPoint && wif->status == RowStatus_active) break; return (wif); } static struct wlan_iface * wlan_mesh_next_interface(struct wlan_iface *wif) { struct wlan_iface *nwif; while ((nwif = wlan_next_interface(wif)) != NULL) { if (nwif->mode == WlanIfaceOperatingModeType_meshPoint && nwif->status == RowStatus_active) break; wif = nwif; } return (nwif); } static struct wlan_iface * wlan_mesh_get_iface(const struct asn_oid *oid, uint sub) { struct wlan_iface *wif; if ((wif = wlan_get_interface(oid, sub)) == NULL) return (NULL); if (wif->mode != WlanIfaceOperatingModeType_meshPoint) return (NULL); return (wif); } static struct wlan_iface * wlan_mesh_get_next_iface(const struct asn_oid *oid, uint sub) { uint32_t i; uint8_t wname[IFNAMSIZ]; struct wlan_iface *wif; if (oid->len - sub == 0) return (wlan_mesh_first_interface()); if (oid->len - sub != oid->subs[sub] + 1 || oid->subs[sub] >= IFNAMSIZ) return (NULL); memset(wname, 0, IFNAMSIZ); for (i = 0; i < oid->subs[sub]; i++) wname[i] = oid->subs[sub + i + 1]; wname[i] = '\0'; if ((wif = wlan_find_interface(wname)) == NULL) return (NULL); return (wlan_mesh_next_interface(wif)); } /* * The neighbors of wireless interfaces operating as mesh points. */ static struct wlan_peer * wlan_mesh_get_peer(const struct asn_oid *oid, uint sub, struct wlan_iface **wif) { char wname[IFNAMSIZ]; uint8_t pmac[IEEE80211_ADDR_LEN]; if (wlan_mac_index_decode(oid, sub, wname, pmac) < 0) return (NULL); if ((*wif = wlan_find_interface(wname)) == NULL || (*wif)->mode != WlanIfaceOperatingModeType_meshPoint) return (NULL); return (wlan_find_peer(*wif, pmac)); } static struct wlan_peer * wlan_mesh_get_next_peer(const struct asn_oid *oid, uint sub, struct wlan_iface **wif) { char wname[IFNAMSIZ]; char pmac[IEEE80211_ADDR_LEN]; struct wlan_peer *wip; if (oid->len - sub == 0) { for (*wif = wlan_mesh_first_interface(); *wif != NULL; *wif = wlan_mesh_next_interface(*wif)) { wip = SLIST_FIRST(&(*wif)->peerlist); if (wip != NULL) return (wip); } return (NULL); } if (wlan_mac_index_decode(oid, sub, wname, pmac) < 0 || (*wif = wlan_find_interface(wname)) == NULL || (*wif)->mode != WlanIfaceOperatingModeType_meshPoint || (wip = wlan_find_peer(*wif, pmac)) == NULL) return (NULL); if ((wip = SLIST_NEXT(wip, wp)) != NULL) return (wip); while ((*wif = wlan_mesh_next_interface(*wif)) != NULL) if ((wip = SLIST_FIRST(&(*wif)->peerlist)) != NULL) break; return (wip); } /* * Mesh routing table. */ static void wlan_mesh_free_routes(struct wlan_iface *wif) { struct wlan_mesh_route *wmr; while ((wmr = SLIST_FIRST(&wif->mesh_routelist)) != NULL) { SLIST_REMOVE_HEAD(&wif->mesh_routelist, wr); free(wmr); } SLIST_INIT(&wif->mesh_routelist); } static struct wlan_mesh_route * wlan_mesh_find_route(struct wlan_iface *wif, uint8_t *dstmac) { struct wlan_mesh_route *wmr; if (wif->mode != WlanIfaceOperatingModeType_meshPoint) return (NULL); SLIST_FOREACH(wmr, &wif->mesh_routelist, wr) if (memcmp(wmr->imroute.imr_dest, dstmac, IEEE80211_ADDR_LEN) == 0) break; return (wmr); } struct wlan_mesh_route * wlan_mesh_new_route(const uint8_t *dstmac) { struct wlan_mesh_route *wmr; if ((wmr = (struct wlan_mesh_route *)malloc(sizeof(*wmr))) == NULL) return (NULL); memset(wmr, 0, sizeof(*wmr)); memcpy(wmr->imroute.imr_dest, dstmac, IEEE80211_ADDR_LEN); wmr->mroute_status = RowStatus_notReady; return (wmr); } void wlan_mesh_free_route(struct wlan_mesh_route *wmr) { free(wmr); } int wlan_mesh_add_rtentry(struct wlan_iface *wif, struct wlan_mesh_route *wmr) { struct wlan_mesh_route *temp, *prev; SLIST_FOREACH(temp, &wif->mesh_routelist, wr) if (memcmp(temp->imroute.imr_dest, wmr->imroute.imr_dest, IEEE80211_ADDR_LEN) == 0) return (-1); if ((prev = SLIST_FIRST(&wif->mesh_routelist)) == NULL || memcmp(wmr->imroute.imr_dest, prev->imroute.imr_dest, IEEE80211_ADDR_LEN) < 0) { SLIST_INSERT_HEAD(&wif->mesh_routelist, wmr, wr); return (0); } SLIST_FOREACH(temp, &wif->mesh_routelist, wr) { if (memcmp(wmr->imroute.imr_dest, temp->imroute.imr_dest, IEEE80211_ADDR_LEN) < 0) break; prev = temp; } SLIST_INSERT_AFTER(prev, wmr, wr); return (0); } static int wlan_mesh_delete_route(struct wlan_iface *wif, struct wlan_mesh_route *wmr) { if (wmr->mroute_status == RowStatus_active && wlan_mesh_del_route(wif, wmr) < 0) return (-1); SLIST_REMOVE(&wif->mesh_routelist, wmr, wlan_mesh_route, wr); free(wmr); return (0); } static void wlan_mesh_update_routes(void) { struct wlan_iface *wif; struct wlan_mesh_route *wmr, *twmr; if ((time(NULL) - wlan_mrlist_age) <= WLAN_LIST_MAXAGE) return; for (wif = wlan_mesh_first_interface(); wif != NULL; wif = wlan_mesh_next_interface(wif)) { /* * Nuke old entries - XXX - they are likely not to * change often - reconsider. */ SLIST_FOREACH_SAFE(wmr, &wif->mesh_routelist, wr, twmr) if (wmr->mroute_status == RowStatus_active) { SLIST_REMOVE(&wif->mesh_routelist, wmr, wlan_mesh_route, wr); wlan_mesh_free_route(wmr); } (void)wlan_mesh_get_routelist(wif); } wlan_mrlist_age = time(NULL); } static struct wlan_mesh_route * wlan_mesh_get_route(const struct asn_oid *oid, uint sub, struct wlan_iface **wif) { char wname[IFNAMSIZ]; char dstmac[IEEE80211_ADDR_LEN]; if (wlan_mac_index_decode(oid, sub, wname, dstmac) < 0) return (NULL); if ((*wif = wlan_find_interface(wname)) == NULL) return (NULL); return (wlan_mesh_find_route(*wif, dstmac)); } static struct wlan_mesh_route * wlan_mesh_get_next_route(const struct asn_oid *oid, uint sub, struct wlan_iface **wif) { char wname[IFNAMSIZ]; char dstmac[IEEE80211_ADDR_LEN]; struct wlan_mesh_route *wmr; if (oid->len - sub == 0) { for (*wif = wlan_mesh_first_interface(); *wif != NULL; *wif = wlan_mesh_next_interface(*wif)) { wmr = SLIST_FIRST(&(*wif)->mesh_routelist); if (wmr != NULL) return (wmr); } return (NULL); } if (wlan_mac_index_decode(oid, sub, wname, dstmac) < 0 || (*wif = wlan_find_interface(wname)) == NULL || (wmr = wlan_mesh_find_route(*wif, dstmac)) == NULL) return (NULL); if ((wmr = SLIST_NEXT(wmr, wr)) != NULL) return (wmr); while ((*wif = wlan_mesh_next_interface(*wif)) != NULL) if ((wmr = SLIST_FIRST(&(*wif)->mesh_routelist)) != NULL) break; return (wmr); } static int wlan_mesh_route_set_status(struct snmp_context *ctx, struct snmp_value *val, uint sub) { char wname[IFNAMSIZ]; char mac[IEEE80211_ADDR_LEN]; struct wlan_mesh_route *wmr; struct wlan_iface *wif; if (wlan_mac_index_decode(&val->var, sub, wname, mac) < 0) return (SNMP_ERR_GENERR); wmr = wlan_mesh_get_route(&val->var, sub, &wif); switch (val->v.integer) { case RowStatus_createAndGo: if (wmr != NULL) return (SNMP_ERR_INCONS_NAME); break; case RowStatus_destroy: if (wmr == NULL) return (SNMP_ERR_NOSUCHNAME); ctx->scratch->int1 = RowStatus_active; return (SNMP_ERR_NOERROR); default: return (SNMP_ERR_INCONS_VALUE); } if ((wif = wlan_find_interface(wname)) == NULL) return (SNMP_ERR_INCONS_NAME); if ((wmr = wlan_mesh_new_route(mac)) == NULL) return (SNMP_ERR_GENERR); if (wlan_mesh_add_rtentry(wif, wmr) < 0) { wlan_mesh_free_route(wmr); return (SNMP_ERR_GENERR); } ctx->scratch->int1 = RowStatus_destroy; if (wlan_mesh_add_route(wif, wmr) < 0) { (void)wlan_mesh_delete_route(wif, wmr); return (SNMP_ERR_GENERR); } return (SNMP_ERR_NOERROR); } /* * Wlan snmp module initialization hook. * Returns 0 on success, < 0 on error. */ static int wlan_init(struct lmodule * mod __unused, int argc __unused, char *argv[] __unused) { if (wlan_kmodules_load() < 0) return (-1); if (wlan_ioctl_init() < 0) return (-1); /* Register for new interface creation notifications. */ if (mib_register_newif(wlan_attach_newif, wlan_module)) { syslog(LOG_ERR, "Cannot register newif function: %s", strerror(errno)); return (-1); } return (0); } /* * Wlan snmp module finalization hook. */ static int wlan_fini(void) { mib_unregister_newif(wlan_module); or_unregister(reg_wlan); /* XXX: Cleanup! */ wlan_free_iflist(); return (0); } /* * Refetch all available data from the kernel. */ static void wlan_update_data(void *arg __unused) { } /* * Wlan snmp module start operation. */ static void wlan_start(void) { struct mibif *ifp; reg_wlan = or_register(&oid_wlan, "The MIB module for managing wireless networking.", wlan_module); /* Add the existing wlan interfaces. */ for (ifp = mib_first_if(); ifp != NULL; ifp = mib_next_if(ifp)) wlan_attach_newif(ifp); wlan_data_timer = timer_start_repeat(wlan_poll_ticks, wlan_poll_ticks, wlan_update_data, NULL, wlan_module); } /* * Dump the Wlan snmp module data on SIGUSR1. */ static void wlan_dump(void) { /* XXX: Print some debug info to syslog. */ struct wlan_iface *wif; for (wif = wlan_first_interface(); wif != NULL; wif = wlan_next_interface(wif)) syslog(LOG_ERR, "wlan iface %s", wif->wname); } const char wlan_comment[] = \ "This module implements the BEGEMOT MIB for wireless networking."; const struct snmp_module config = { .comment = wlan_comment, .init = wlan_init, .fini = wlan_fini, .start = wlan_start, .tree = wlan_ctree, .dump = wlan_dump, .tree_size = wlan_CTREE_SIZE, }; Index: head/usr.sbin/bsnmpd/modules/snmp_wlan/wlan_snmp.h =================================================================== --- head/usr.sbin/bsnmpd/modules/snmp_wlan/wlan_snmp.h (revision 310647) +++ head/usr.sbin/bsnmpd/modules/snmp_wlan/wlan_snmp.h (revision 310648) @@ -1,286 +1,286 @@ /*- * Copyright (c) 2010 The FreeBSD Foundation * All rights reserved. * * This software was developed by Shteryana Sotirova Shopova under * sponsorship from the FreeBSD Foundation. * * 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 THE 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 THE 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. * * $FreeBSD$ */ #define WLAN_IFMODE_MAX WlanIfaceOperatingModeType_tdma #define WLAN_COUNTRY_CODE_SIZE 3 #define WLAN_BGSCAN_IDLE_MIN 100 /* XXX */ #define WLAN_SCAN_VALID_MIN 10 /* XXX */ #define WLAN_TDMA_MAXSLOTS 2 /* XXX */ struct wlan_iface; struct wlan_peer { uint8_t pmac[IEEE80211_ADDR_LEN]; /* key */ uint16_t associd; uint16_t vlan; uint16_t frequency; uint32_t fflags; uint8_t txrate; int8_t rssi; uint16_t idle; uint16_t txseqs; uint16_t rxseqs; uint16_t txpower; uint8_t capinfo; uint32_t state; uint16_t local_id; uint16_t peer_id; SLIST_ENTRY(wlan_peer) wp; }; SLIST_HEAD(wlan_peerlist, wlan_peer); struct wlan_scan_result { uint8_t ssid[IEEE80211_NWID_LEN + 1]; uint8_t bssid[IEEE80211_ADDR_LEN]; uint8_t opchannel; int8_t rssi; uint16_t frequency; int8_t noise; uint16_t bintval; uint8_t capinfo; struct wlan_iface *pwif; SLIST_ENTRY(wlan_scan_result) wsr; }; SLIST_HEAD(wlan_scanlist, wlan_scan_result); struct wlan_mac_mac { uint8_t mac[IEEE80211_ADDR_LEN]; enum RowStatus mac_status; SLIST_ENTRY(wlan_mac_mac) wm; }; SLIST_HEAD(wlan_maclist, wlan_mac_mac); struct wlan_mesh_route { struct ieee80211req_mesh_route imroute; enum RowStatus mroute_status; SLIST_ENTRY(wlan_mesh_route) wr; }; SLIST_HEAD(wlan_mesh_routes, wlan_mesh_route); struct wlan_iface { char wname[IFNAMSIZ]; uint32_t index; char pname[IFNAMSIZ]; enum WlanIfaceOperatingModeType mode; uint32_t flags; uint8_t dbssid[IEEE80211_ADDR_LEN]; uint8_t dlmac[IEEE80211_ADDR_LEN]; enum RowStatus status; enum wlanIfaceState state; uint8_t internal; uint32_t drivercaps; uint32_t cryptocaps; uint32_t htcaps; uint32_t packet_burst; uint8_t country_code[WLAN_COUNTRY_CODE_SIZE]; enum WlanRegDomainCode reg_domain; uint8_t desired_ssid[IEEE80211_NWID_LEN + 1]; uint32_t desired_channel; enum TruthValue dyn_frequency; enum TruthValue fast_frames; enum TruthValue dturbo; int32_t tx_power; int32_t frag_threshold; int32_t rts_threshold; enum TruthValue priv_subscribe; enum TruthValue bg_scan; int32_t bg_scan_idle; int32_t bg_scan_interval; int32_t beacons_missed; uint8_t desired_bssid[IEEE80211_ADDR_LEN]; enum wlanIfaceRoamingMode roam_mode; enum TruthValue dot11d; enum TruthValue dot11h; enum TruthValue dynamic_wds; enum TruthValue power_save; enum TruthValue ap_bridge; int32_t beacon_interval; int32_t dtim_period; enum TruthValue hide_ssid; enum TruthValue inact_process; enum wlanIfaceDot11gProtMode do11g_protect; enum TruthValue dot11g_pure; enum TruthValue dot11n_pure; enum WlanIfaceDot11nPduType ampdu; int32_t ampdu_density; int32_t ampdu_limit; enum WlanIfaceDot11nPduType amsdu; int32_t amsdu_limit; enum TruthValue ht_enabled; enum TruthValue ht_compatible; enum wlanIfaceDot11nHTProtMode ht_prot_mode; enum TruthValue rifs; enum TruthValue short_gi; enum wlanIfaceDot11nSMPSMode smps_mode; int32_t tdma_slot; int32_t tdma_slot_count; int32_t tdma_slot_length; int32_t tdma_binterval; struct wlan_peerlist peerlist; struct ieee80211_stats stats; uint32_t nchannels; struct ieee80211_channel *chanlist; struct ieee80211_roamparams_req roamparams; struct ieee80211_txparams_req txparams; uint32_t scan_flags; uint32_t scan_duration; uint32_t scan_mindwell; uint32_t scan_maxdwell; enum wlanScanConfigStatus scan_status; struct wlan_scanlist scanlist; uint8_t wepsupported; enum wlanWepMode wepmode; int32_t weptxkey; uint8_t macsupported; enum wlanMACAccessControlPolicy mac_policy; uint32_t mac_nacls; struct wlan_maclist mac_maclist; uint32_t mesh_ttl; enum wlanMeshPeeringEnabled mesh_peering; enum wlanMeshForwardingEnabled mesh_forwarding; enum wlanMeshMetric mesh_metric; enum wlanMeshPath mesh_path; enum wlanHWMPRootMode hwmp_root_mode; uint32_t hwmp_max_hops; struct wlan_mesh_routes mesh_routelist; - + SLIST_ENTRY(wlan_iface) w_if; }; enum wlan_syscl { WLAN_MESH_RETRY_TO = 0, WLAN_MESH_HOLDING_TO, WLAN_MESH_CONFIRM_TO, WLAN_MESH_MAX_RETRIES, WLAN_HWMP_TARGET_ONLY, WLAN_HWMP_REPLY_FORWARD, WLAN_HWMP_PATH_LIFETIME, WLAN_HWMP_ROOT_TO, WLAN_HWMP_ROOT_INT, WLAN_HWMP_RANN_INT, WLAN_HWMP_INACTIVITY_TO, WLAN_SYSCTL_MAX }; struct wlan_config { int32_t mesh_retryto; int32_t mesh_holdingto; int32_t mesh_confirmto; int32_t mesh_maxretries; int32_t hwmp_targetonly; int32_t hwmp_replyforward; int32_t hwmp_pathlifetime; int32_t hwmp_roottimeout; int32_t hwmp_rootint; int32_t hwmp_rannint; int32_t hwmp_inact; }; int wlan_ioctl_init(void); int wlan_kmodules_load(void); int wlan_check_media(char *); int wlan_config_state(struct wlan_iface *, uint8_t); int wlan_get_opmode(struct wlan_iface *wif); int wlan_get_local_addr(struct wlan_iface *wif); int wlan_get_parent(struct wlan_iface *wif); int wlan_get_driver_caps(struct wlan_iface *wif); uint8_t wlan_channel_state_to_snmp(uint8_t cstate); uint32_t wlan_channel_flags_to_snmp(uint32_t cflags); int wlan_get_channel_list(struct wlan_iface *wif); int wlan_get_roam_params(struct wlan_iface *wif); int wlan_get_tx_params(struct wlan_iface *wif); int wlan_set_tx_params(struct wlan_iface *wif, int32_t pmode); int wlan_clone_create(struct wlan_iface *); int wlan_clone_destroy(struct wlan_iface *wif); int wlan_config_get_dssid(struct wlan_iface *wif); int wlan_config_set_dssid(struct wlan_iface *wif, char *ssid, int slen); int wlan_config_get_ioctl(struct wlan_iface *wif, int which); int wlan_config_set_ioctl(struct wlan_iface *wif, int which, int val, char *strval, int len); int wlan_set_scan_config(struct wlan_iface *wif); int wlan_get_scan_results(struct wlan_iface *wif); int wlan_get_stats(struct wlan_iface *wif); int wlan_get_wepmode(struct wlan_iface *wif); int wlan_set_wepmode(struct wlan_iface *wif); int wlan_get_weptxkey(struct wlan_iface *wif); int wlan_set_weptxkey(struct wlan_iface *wif); int wlan_get_wepkeys(struct wlan_iface *wif); int wlan_set_wepkeys(struct wlan_iface *wif); int wlan_get_mac_policy(struct wlan_iface *wif); int wlan_set_mac_policy(struct wlan_iface *wif); int wlan_flush_mac_mac(struct wlan_iface *wif); int wlan_get_mac_acl_macs(struct wlan_iface *wif); int wlan_add_mac_acl_mac(struct wlan_iface *wif, struct wlan_mac_mac *mmac); int wlan_del_mac_acl_mac(struct wlan_iface *wif, struct wlan_mac_mac *mmac); int32_t wlan_do_sysctl(struct wlan_config *cfg, enum wlan_syscl which, int set); int wlan_mesh_config_get(struct wlan_iface *wif, int which); int wlan_mesh_config_set(struct wlan_iface *wif, int which); int wlan_mesh_flush_routes(struct wlan_iface *wif); int wlan_mesh_add_route(struct wlan_iface *wif, struct wlan_mesh_route *wmr); int wlan_mesh_del_route(struct wlan_iface *wif, struct wlan_mesh_route *wmr); int wlan_mesh_get_routelist(struct wlan_iface *wif); int wlan_hwmp_config_get(struct wlan_iface *wif, int which); int wlan_hwmp_config_set(struct wlan_iface *wif, int which); /* XXX: static */ int wlan_peer_set_vlan(struct wlan_iface *wif, struct wlan_peer *wip, int vlan); int wlan_get_peerinfo(struct wlan_iface *wif); /* XXX*/ struct wlan_peer *wlan_new_peer(const uint8_t *pmac); void wlan_free_peer(struct wlan_peer *wip); int wlan_add_peer(struct wlan_iface *wif, struct wlan_peer *wip); struct wlan_scan_result * wlan_scan_new_result(const uint8_t *ssid, const uint8_t *bssid); void wlan_scan_free_result(struct wlan_scan_result *sr); int wlan_scan_add_result(struct wlan_iface *wif, struct wlan_scan_result *sr); struct wlan_mac_mac *wlan_mac_new_mac(const uint8_t *mac); void wlan_mac_free_mac(struct wlan_mac_mac *wmm); int wlan_mac_add_mac(struct wlan_iface *wif, struct wlan_mac_mac *wmm); struct wlan_mesh_route *wlan_mesh_new_route(const uint8_t *dstmac); int wlan_mesh_add_rtentry(struct wlan_iface *wif, struct wlan_mesh_route *wmr); void wlan_mesh_free_route(struct wlan_mesh_route *wmr); Index: head/usr.sbin/bsnmpd/modules/snmp_wlan/wlan_sys.c =================================================================== --- head/usr.sbin/bsnmpd/modules/snmp_wlan/wlan_sys.c (revision 310647) +++ head/usr.sbin/bsnmpd/modules/snmp_wlan/wlan_sys.c (revision 310648) @@ -1,3145 +1,3145 @@ /*- * Copyright (c) 2010 The FreeBSD Foundation * All rights reserved. - + * This software was developed by Shteryana Sotirova Shopova under * sponsorship from the FreeBSD Foundation. * * 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 THE 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 THE 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. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "wlan_tree.h" #include "wlan_snmp.h" static int sock = -1; static int wlan_ioctl(char *, uint16_t, int *, void *, size_t *, int); static int wlan_kmod_load(const char *); static uint32_t wlan_drivercaps_to_snmp(uint32_t); static uint32_t wlan_cryptocaps_to_snmp(uint32_t); static uint32_t wlan_htcaps_to_snmp(uint32_t); static uint32_t wlan_peerstate_to_snmp(uint32_t); static uint32_t wlan_peercaps_to_snmp(uint32_t ); static uint32_t wlan_channel_flags_to_snmp_phy(uint32_t); static uint32_t wlan_regdomain_to_snmp(int); static uint32_t wlan_snmp_to_scan_flags(int); static int wlan_config_snmp2ioctl(int); static int wlan_snmp_to_regdomain(enum WlanRegDomainCode); static int wlan_config_get_country(struct wlan_iface *); static int wlan_config_set_country(struct wlan_iface *, char *, int); static int wlan_config_get_dchannel(struct wlan_iface *wif); static int wlan_config_set_dchannel(struct wlan_iface *wif, uint32_t); static int wlan_config_get_bssid(struct wlan_iface *); static int wlan_config_set_bssid(struct wlan_iface *, uint8_t *); static void wlan_config_set_snmp_intval(struct wlan_iface *, int, int); static int wlan_config_snmp2value(int, int, int *); static int wlan_config_check(struct wlan_iface *, int); static int wlan_config_get_intval(struct wlan_iface *, int); static int wlan_config_set_intval(struct wlan_iface *, int, int); static int wlan_add_new_scan_result(struct wlan_iface *, const struct ieee80211req_scan_result *, uint8_t *); static int wlan_add_mac_macinfo(struct wlan_iface *, const struct ieee80211req_maclist *); static struct wlan_peer *wlan_add_peerinfo(const struct ieee80211req_sta_info *); int wlan_ioctl_init(void) { if ((sock = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { syslog(LOG_ERR, "cannot open socket : %s", strerror(errno)); return (-1); } return (0); } /* * Load the needed modules in kernel if not already there. */ enum wlan_kmodules { WLAN_KMOD = 0, WLAN_KMOD_ACL, WLAN_KMOD_WEP, WLAN_KMODS_MAX }; static const char *wmod_names[] = { "wlan", "wlan_wlan_acl", "wlan_wep", NULL }; static int wlan_kmod_load(const char *modname) { int fileid, modid; struct module_stat mstat; mstat.version = sizeof(struct module_stat); for (fileid = kldnext(0); fileid > 0; fileid = kldnext(fileid)) { for (modid = kldfirstmod(fileid); modid > 0; modid = modfnext(modid)) { if (modstat(modid, &mstat) < 0) continue; if (strcmp(modname, mstat.name) == 0) return (0); } } /* Not present - load it. */ if (kldload(modname) < 0) { syslog(LOG_ERR, "failed to load %s kernel module - %s", modname, strerror(errno)); return (-1); } return (1); } int wlan_kmodules_load(void) { if (wlan_kmod_load(wmod_names[WLAN_KMOD]) < 0) return (-1); if (wlan_kmod_load(wmod_names[WLAN_KMOD_ACL]) > 0) syslog(LOG_NOTICE, "SNMP wlan loaded %s module", wmod_names[WLAN_KMOD_ACL]); if (wlan_kmod_load(wmod_names[WLAN_KMOD_WEP]) > 0) syslog(LOG_NOTICE, "SNMP wlan loaded %s module", wmod_names[WLAN_KMOD_WEP]); - return (0); + return (0); } /* XXX: FIXME */ static int wlan_ioctl(char *wif_name, uint16_t req_type, int *val, void *arg, size_t *argsize, int set) { struct ieee80211req ireq; memset(&ireq, 0, sizeof(struct ieee80211req)); strlcpy(ireq.i_name, wif_name, IFNAMSIZ); ireq.i_type = req_type; ireq.i_val = *val; ireq.i_len = *argsize; ireq.i_data = arg; if (ioctl(sock, set ? SIOCS80211 : SIOCG80211, &ireq) < 0) { syslog(LOG_ERR, "iface %s - %s param: ioctl(%d) " "failed: %s", wif_name, set ? "set" : "get", req_type, strerror(errno)); return (-1); } *argsize = ireq.i_len; *val = ireq.i_val; return (0); } int wlan_check_media(char *ifname) { struct ifmediareq ifmr; memset(&ifmr, 0, sizeof(struct ifmediareq)); strlcpy(ifmr.ifm_name, ifname, sizeof(ifmr.ifm_name)); if (ioctl(sock, SIOCGIFMEDIA, &ifmr) < 0 || ifmr.ifm_count == 0) return (0); /* Interface doesn't support SIOCGIFMEDIA. */ if ((ifmr.ifm_status & IFM_AVALID) == 0) return (0); return (IFM_TYPE(ifmr.ifm_active)); } int wlan_get_opmode(struct wlan_iface *wif) { struct ifmediareq ifmr; memset(&ifmr, 0, sizeof(struct ifmediareq)); strlcpy(ifmr.ifm_name, wif->wname, sizeof(ifmr.ifm_name)); if (ioctl(sock, SIOCGIFMEDIA, &ifmr) < 0) { if (errno == ENXIO) return (-1); wif->mode = WlanIfaceOperatingModeType_station; return (0); } if (ifmr.ifm_current & IFM_IEEE80211_ADHOC) { if (ifmr.ifm_current & IFM_FLAG0) wif->mode = WlanIfaceOperatingModeType_adhocDemo; else wif->mode = WlanIfaceOperatingModeType_ibss; } else if (ifmr.ifm_current & IFM_IEEE80211_HOSTAP) wif->mode = WlanIfaceOperatingModeType_hostAp; else if (ifmr.ifm_current & IFM_IEEE80211_MONITOR) wif->mode = WlanIfaceOperatingModeType_monitor; else if (ifmr.ifm_current & IFM_IEEE80211_MBSS) wif->mode = WlanIfaceOperatingModeType_meshPoint; else if (ifmr.ifm_current & IFM_IEEE80211_WDS) wif->mode = WlanIfaceOperatingModeType_wds; return (0); } int wlan_config_state(struct wlan_iface *wif, uint8_t set) { int flags; struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); strcpy(ifr.ifr_name, wif->wname); if (ioctl(sock, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) { syslog(LOG_ERR, "set %s status: ioctl(SIOCGIFFLAGS) " "failed: %s", wif->wname, strerror(errno)); return (-1); } if (set == 0) { if ((ifr.ifr_flags & IFF_UP) != 0) wif->state = wlanIfaceState_up; else wif->state = wlanIfaceState_down; return (0); } flags = (ifr.ifr_flags & 0xffff) | (ifr.ifr_flagshigh << 16); if (wif->state == wlanIfaceState_up) flags |= IFF_UP; else flags &= ~IFF_UP; ifr.ifr_flags = flags & 0xffff; ifr.ifr_flagshigh = flags >> 16; if (ioctl(sock, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) { syslog(LOG_ERR, "set %s %s: ioctl(SIOCSIFFLAGS) failed: %s", wif->wname, wif->state == wlanIfaceState_up?"up":"down", strerror(errno)); return (-1); } return (0); } int wlan_get_local_addr(struct wlan_iface *wif) { int len; char ifname[IFNAMSIZ]; struct ifaddrs *ifap, *ifa; struct sockaddr_dl sdl; if (getifaddrs(&ifap) != 0) { syslog(LOG_ERR, "wlan get mac: getifaddrs() failed - %s", strerror(errno)); return (-1); } for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) { if (ifa->ifa_addr->sa_family != AF_LINK) continue; memcpy(&sdl, ifa->ifa_addr, sizeof(struct sockaddr_dl)); if (sdl.sdl_alen > IEEE80211_ADDR_LEN) continue; if ((len = sdl.sdl_nlen) >= IFNAMSIZ) len = IFNAMSIZ - 1; memcpy(ifname, sdl.sdl_data, len); ifname[len] = '\0'; if (strcmp(wif->wname, ifname) == 0) break; } freeifaddrs(ifap); return (0); } int wlan_get_parent(struct wlan_iface *wif __unused) { /* XXX: There's no way to fetch this from the kernel. */ return (0); } /* XXX */ #define IEEE80211_C_STA 0x00000001 /* CAPABILITY: STA available */ #define IEEE80211_C_8023ENCAP 0x00000002 /* CAPABILITY: 802.3 encap */ #define IEEE80211_C_FF 0x00000040 /* CAPABILITY: ATH FF avail */ #define IEEE80211_C_TURBOP 0x00000080 /* CAPABILITY: ATH Turbo avail*/ #define IEEE80211_C_IBSS 0x00000100 /* CAPABILITY: IBSS available */ #define IEEE80211_C_PMGT 0x00000200 /* CAPABILITY: Power mgmt */ #define IEEE80211_C_HOSTAP 0x00000400 /* CAPABILITY: HOSTAP avail */ #define IEEE80211_C_AHDEMO 0x00000800 /* CAPABILITY: Old Adhoc Demo */ #define IEEE80211_C_SWRETRY 0x00001000 /* CAPABILITY: sw tx retry */ #define IEEE80211_C_TXPMGT 0x00002000 /* CAPABILITY: tx power mgmt */ #define IEEE80211_C_SHSLOT 0x00004000 /* CAPABILITY: short slottime */ #define IEEE80211_C_SHPREAMBLE 0x00008000 /* CAPABILITY: short preamble */ #define IEEE80211_C_MONITOR 0x00010000 /* CAPABILITY: monitor mode */ #define IEEE80211_C_DFS 0x00020000 /* CAPABILITY: DFS/radar avail*/ #define IEEE80211_C_MBSS 0x00040000 /* CAPABILITY: MBSS available */ /* 0x7c0000 available */ #define IEEE80211_C_WPA1 0x00800000 /* CAPABILITY: WPA1 avail */ #define IEEE80211_C_WPA2 0x01000000 /* CAPABILITY: WPA2 avail */ #define IEEE80211_C_WPA 0x01800000 /* CAPABILITY: WPA1+WPA2 avail*/ #define IEEE80211_C_BURST 0x02000000 /* CAPABILITY: frame bursting */ #define IEEE80211_C_WME 0x04000000 /* CAPABILITY: WME avail */ #define IEEE80211_C_WDS 0x08000000 /* CAPABILITY: 4-addr support */ /* 0x10000000 reserved */ #define IEEE80211_C_BGSCAN 0x20000000 /* CAPABILITY: bg scanning */ #define IEEE80211_C_TXFRAG 0x40000000 /* CAPABILITY: tx fragments */ #define IEEE80211_C_TDMA 0x80000000 /* CAPABILITY: TDMA avail */ static uint32_t wlan_drivercaps_to_snmp(uint32_t dcaps) { uint32_t scaps = 0; if ((dcaps & IEEE80211_C_STA) != 0) scaps |= (0x1 << WlanDriverCaps_station); if ((dcaps & IEEE80211_C_8023ENCAP) != 0) scaps |= (0x1 << WlanDriverCaps_ieee8023encap); if ((dcaps & IEEE80211_C_FF) != 0) scaps |= (0x1 << WlanDriverCaps_athFastFrames); if ((dcaps & IEEE80211_C_TURBOP) != 0) scaps |= (0x1 << WlanDriverCaps_athTurbo); if ((dcaps & IEEE80211_C_IBSS) != 0) scaps |= (0x1 << WlanDriverCaps_ibss); if ((dcaps & IEEE80211_C_PMGT) != 0) scaps |= (0x1 << WlanDriverCaps_pmgt); if ((dcaps & IEEE80211_C_HOSTAP) != 0) scaps |= (0x1 << WlanDriverCaps_hostAp); if ((dcaps & IEEE80211_C_AHDEMO) != 0) scaps |= (0x1 << WlanDriverCaps_ahDemo); if ((dcaps & IEEE80211_C_SWRETRY) != 0) scaps |= (0x1 << WlanDriverCaps_swRetry); if ((dcaps & IEEE80211_C_TXPMGT) != 0) scaps |= (0x1 << WlanDriverCaps_txPmgt); if ((dcaps & IEEE80211_C_SHSLOT) != 0) scaps |= (0x1 << WlanDriverCaps_shortSlot); if ((dcaps & IEEE80211_C_SHPREAMBLE) != 0) scaps |= (0x1 << WlanDriverCaps_shortPreamble); if ((dcaps & IEEE80211_C_MONITOR) != 0) scaps |= (0x1 << WlanDriverCaps_monitor); if ((dcaps & IEEE80211_C_DFS) != 0) scaps |= (0x1 << WlanDriverCaps_dfs); if ((dcaps & IEEE80211_C_MBSS) != 0) scaps |= (0x1 << WlanDriverCaps_mbss); if ((dcaps & IEEE80211_C_WPA1) != 0) scaps |= (0x1 << WlanDriverCaps_wpa1); if ((dcaps & IEEE80211_C_WPA2) != 0) scaps |= (0x1 << WlanDriverCaps_wpa2); if ((dcaps & IEEE80211_C_BURST) != 0) scaps |= (0x1 << WlanDriverCaps_burst); if ((dcaps & IEEE80211_C_WME) != 0) scaps |= (0x1 << WlanDriverCaps_wme); if ((dcaps & IEEE80211_C_WDS) != 0) scaps |= (0x1 << WlanDriverCaps_wds); if ((dcaps & IEEE80211_C_BGSCAN) != 0) scaps |= (0x1 << WlanDriverCaps_bgScan); if ((dcaps & IEEE80211_C_TXFRAG) != 0) scaps |= (0x1 << WlanDriverCaps_txFrag); if ((dcaps & IEEE80211_C_TDMA) != 0) scaps |= (0x1 << WlanDriverCaps_tdma); return (scaps); } static uint32_t wlan_cryptocaps_to_snmp(uint32_t ccaps) { uint32_t scaps = 0; #if NOT_YET if ((ccaps & IEEE80211_CRYPTO_WEP) != 0) scaps |= (0x1 << wlanCryptoCaps_wep); if ((ccaps & IEEE80211_CRYPTO_TKIP) != 0) scaps |= (0x1 << wlanCryptoCaps_tkip); if ((ccaps & IEEE80211_CRYPTO_AES_OCB) != 0) scaps |= (0x1 << wlanCryptoCaps_aes); if ((ccaps & IEEE80211_CRYPTO_AES_CCM) != 0) scaps |= (0x1 << wlanCryptoCaps_aesCcm); if ((ccaps & IEEE80211_CRYPTO_TKIPMIC) != 0) scaps |= (0x1 << wlanCryptoCaps_tkipMic); if ((ccaps & IEEE80211_CRYPTO_CKIP) != 0) scaps |= (0x1 << wlanCryptoCaps_ckip); #else /* !NOT_YET */ scaps = ccaps; #endif return (scaps); } #define IEEE80211_HTC_AMPDU 0x00010000 /* CAPABILITY: A-MPDU tx */ #define IEEE80211_HTC_AMSDU 0x00020000 /* CAPABILITY: A-MSDU tx */ /* NB: HT40 is implied by IEEE80211_HTCAP_CHWIDTH40 */ #define IEEE80211_HTC_HT 0x00040000 /* CAPABILITY: HT operation */ #define IEEE80211_HTC_SMPS 0x00080000 /* CAPABILITY: MIMO power save*/ #define IEEE80211_HTC_RIFS 0x00100000 /* CAPABILITY: RIFS support */ static uint32_t wlan_htcaps_to_snmp(uint32_t hcaps) { uint32_t scaps = 0; if ((hcaps & IEEE80211_HTCAP_LDPC) != 0) scaps |= (0x1 << WlanHTCaps_ldpc); if ((hcaps & IEEE80211_HTCAP_CHWIDTH40) != 0) scaps |= (0x1 << WlanHTCaps_chwidth40); if ((hcaps & IEEE80211_HTCAP_GREENFIELD) != 0) scaps |= (0x1 << WlanHTCaps_greenField); if ((hcaps & IEEE80211_HTCAP_SHORTGI20) != 0) scaps |= (0x1 << WlanHTCaps_shortGi20); if ((hcaps & IEEE80211_HTCAP_SHORTGI40) != 0) scaps |= (0x1 << WlanHTCaps_shortGi40); if ((hcaps & IEEE80211_HTCAP_TXSTBC) != 0) scaps |= (0x1 << WlanHTCaps_txStbc); if ((hcaps & IEEE80211_HTCAP_DELBA) != 0) scaps |= (0x1 << WlanHTCaps_delba); if ((hcaps & IEEE80211_HTCAP_MAXAMSDU_7935) != 0) scaps |= (0x1 << WlanHTCaps_amsdu7935); if ((hcaps & IEEE80211_HTCAP_DSSSCCK40) != 0) scaps |= (0x1 << WlanHTCaps_dssscck40); if ((hcaps & IEEE80211_HTCAP_PSMP) != 0) scaps |= (0x1 << WlanHTCaps_psmp); if ((hcaps & IEEE80211_HTCAP_40INTOLERANT) != 0) scaps |= (0x1 << WlanHTCaps_fortyMHzIntolerant); if ((hcaps & IEEE80211_HTCAP_LSIGTXOPPROT) != 0) scaps |= (0x1 << WlanHTCaps_lsigTxOpProt); if ((hcaps & IEEE80211_HTC_AMPDU) != 0) scaps |= (0x1 << WlanHTCaps_htcAmpdu); if ((hcaps & IEEE80211_HTC_AMSDU) != 0) scaps |= (0x1 << WlanHTCaps_htcAmsdu); if ((hcaps & IEEE80211_HTC_HT) != 0) scaps |= (0x1 << WlanHTCaps_htcHt); if ((hcaps & IEEE80211_HTC_SMPS) != 0) scaps |= (0x1 << WlanHTCaps_htcSmps); if ((hcaps & IEEE80211_HTC_RIFS) != 0) scaps |= (0x1 << WlanHTCaps_htcRifs); return (scaps); } /* XXX: Not here? */ #define WLAN_SET_TDMA_OPMODE(w) do { \ if ((w)->mode == WlanIfaceOperatingModeType_adhocDemo && \ ((w)->drivercaps & WlanDriverCaps_tdma) != 0) \ (w)->mode = WlanIfaceOperatingModeType_tdma; \ } while (0) int wlan_get_driver_caps(struct wlan_iface *wif) { int val = 0; size_t argsize; struct ieee80211_devcaps_req dc; memset(&dc, 0, sizeof(struct ieee80211_devcaps_req)); argsize = sizeof(struct ieee80211_devcaps_req); if (wlan_ioctl(wif->wname, IEEE80211_IOC_DEVCAPS, &val, &dc, &argsize, 0) < 0) return (-1); wif->drivercaps = wlan_drivercaps_to_snmp(dc.dc_drivercaps); wif->cryptocaps = wlan_cryptocaps_to_snmp(dc.dc_cryptocaps); wif->htcaps = wlan_htcaps_to_snmp(dc.dc_htcaps); WLAN_SET_TDMA_OPMODE(wif); argsize = dc.dc_chaninfo.ic_nchans * sizeof(struct ieee80211_channel); wif->chanlist = (struct ieee80211_channel *)malloc(argsize); if (wif->chanlist == NULL) return (0); memcpy(wif->chanlist, dc.dc_chaninfo.ic_chans, argsize); wif->nchannels = dc.dc_chaninfo.ic_nchans; return (0); } uint8_t wlan_channel_state_to_snmp(uint8_t cstate) { uint8_t cs = 0; if ((cstate & IEEE80211_CHANSTATE_RADAR) != 0) cs |= (0x1 << WlanIfaceChannelStateType_radar); if ((cstate & IEEE80211_CHANSTATE_CACDONE) != 0) cs |= (0x1 << WlanIfaceChannelStateType_cacDone); if ((cstate & IEEE80211_CHANSTATE_CWINT) != 0) cs |= (0x1 << WlanIfaceChannelStateType_interferenceDetected); if ((cstate & IEEE80211_CHANSTATE_NORADAR) != 0) cs |= (0x1 << WlanIfaceChannelStateType_radarClear); return (cs); } uint32_t wlan_channel_flags_to_snmp(uint32_t cflags) { uint32_t cf = 0; if ((cflags & IEEE80211_CHAN_TURBO) != 0) cf |= (0x1 << WlanIfaceChannelFlagsType_turbo); if ((cflags & IEEE80211_CHAN_CCK) != 0) cf |= (0x1 << WlanIfaceChannelFlagsType_cck); if ((cflags & IEEE80211_CHAN_OFDM) != 0) cf |= (0x1 << WlanIfaceChannelFlagsType_ofdm); if ((cflags & IEEE80211_CHAN_2GHZ) != 0) cf |= (0x1 << WlanIfaceChannelFlagsType_spectrum2Ghz); if ((cflags & IEEE80211_CHAN_5GHZ) != 0) cf |= (0x1 << WlanIfaceChannelFlagsType_spectrum5Ghz); if ((cflags & IEEE80211_CHAN_PASSIVE) != 0) cf |= (0x1 << WlanIfaceChannelFlagsType_passiveScan); if ((cflags & IEEE80211_CHAN_DYN) != 0) cf |= (0x1 << WlanIfaceChannelFlagsType_dynamicCckOfdm); if ((cflags & IEEE80211_CHAN_GFSK) != 0) cf |= (0x1 << WlanIfaceChannelFlagsType_gfsk); if ((cflags & IEEE80211_CHAN_GSM) != 0) cf |= (0x1 << WlanIfaceChannelFlagsType_spectrum900Mhz); if ((cflags & IEEE80211_CHAN_STURBO) != 0) cf |= (0x1 << WlanIfaceChannelFlagsType_dot11aStaticTurbo); if ((cflags & IEEE80211_CHAN_HALF) != 0) cf |= (0x1 << WlanIfaceChannelFlagsType_halfRate); if ((cflags & IEEE80211_CHAN_QUARTER) != 0) cf |= (0x1 << WlanIfaceChannelFlagsType_quarterRate); if ((cflags & IEEE80211_CHAN_HT20) != 0) cf |= (0x1 << WlanIfaceChannelFlagsType_ht20); if ((cflags & IEEE80211_CHAN_HT40U) != 0) cf |= (0x1 << WlanIfaceChannelFlagsType_ht40u); if ((cflags & IEEE80211_CHAN_HT40D) != 0) cf |= (0x1 << WlanIfaceChannelFlagsType_ht40d); if ((cflags & IEEE80211_CHAN_DFS) != 0) cf |= (0x1 << WlanIfaceChannelFlagsType_dfs); if ((cflags & IEEE80211_CHAN_4MSXMIT) != 0) cf |= (0x1 << WlanIfaceChannelFlagsType_xmit4ms); if ((cflags & IEEE80211_CHAN_NOADHOC) != 0) cf |= (0x1 << WlanIfaceChannelFlagsType_noAdhoc); if ((cflags & IEEE80211_CHAN_NOHOSTAP) != 0) cf |= (0x1 << WlanIfaceChannelFlagsType_noHostAp); if ((cflags & IEEE80211_CHAN_11D) != 0) cf |= (0x1 << WlanIfaceChannelFlagsType_dot11d); return (cf); } /* XXX: */ #define WLAN_SNMP_MAX_CHANS 256 int wlan_get_channel_list(struct wlan_iface *wif) { int val = 0; uint32_t i, nchans; size_t argsize; struct ieee80211req_chaninfo *chaninfo; struct ieee80211req_chanlist active; const struct ieee80211_channel *c; argsize = sizeof(struct ieee80211req_chaninfo) + sizeof(struct ieee80211_channel) * WLAN_SNMP_MAX_CHANS; chaninfo = (struct ieee80211req_chaninfo *)malloc(argsize); if (chaninfo == NULL) return (-1); if (wlan_ioctl(wif->wname, IEEE80211_IOC_CHANINFO, &val, chaninfo, &argsize, 0) < 0) return (-1); argsize = sizeof(active); if (wlan_ioctl(wif->wname, IEEE80211_IOC_CHANLIST, &val, &active, &argsize, 0) < 0) goto error; for (i = 0, nchans = 0; i < chaninfo->ic_nchans; i++) { c = &chaninfo->ic_chans[i]; if (!isset(active.ic_channels, c->ic_ieee)) continue; nchans++; } wif->chanlist = (struct ieee80211_channel *)reallocf(wif->chanlist, nchans * sizeof(*c)); if (wif->chanlist == NULL) goto error; wif->nchannels = nchans; for (i = 0, nchans = 0; i < chaninfo->ic_nchans; i++) { c = &chaninfo->ic_chans[i]; if (!isset(active.ic_channels, c->ic_ieee)) continue; memcpy(wif->chanlist + nchans, c, sizeof (*c)); nchans++; } free(chaninfo); return (0); error: wif->nchannels = 0; free(chaninfo); return (-1); } static enum WlanIfPhyMode wlan_channel_flags_to_snmp_phy(uint32_t cflags) { /* XXX: recheck */ if ((cflags & IEEE80211_CHAN_A) != 0) return (WlanIfPhyMode_dot11a); if ((cflags & IEEE80211_CHAN_B) != 0) return (WlanIfPhyMode_dot11b); if ((cflags & IEEE80211_CHAN_G) != 0 || (cflags & IEEE80211_CHAN_PUREG) != 0) return (WlanIfPhyMode_dot11g); if ((cflags & IEEE80211_CHAN_FHSS) != 0) return (WlanIfPhyMode_fh); if ((cflags & IEEE80211_CHAN_TURBO) != 0 && (cflags & IEEE80211_CHAN_A) != 0) return (WlanIfPhyMode_turboA); if ((cflags & IEEE80211_CHAN_TURBO) != 0 && (cflags & IEEE80211_CHAN_G) != 0) return (WlanIfPhyMode_turboG); if ((cflags & IEEE80211_CHAN_STURBO) != 0) return (WlanIfPhyMode_sturboA); if ((cflags & IEEE80211_CHAN_HALF) != 0) return (WlanIfPhyMode_ofdmHalf); if ((cflags & IEEE80211_CHAN_QUARTER) != 0) return (WlanIfPhyMode_ofdmQuarter); return (WlanIfPhyMode_auto); } int wlan_get_roam_params(struct wlan_iface *wif) { int val = 0; size_t argsize; argsize = sizeof(struct ieee80211_roamparams_req); if (wlan_ioctl(wif->wname, IEEE80211_IOC_ROAM, &val, &wif->roamparams, &argsize, 0) < 0) return (-1); return (0); } int wlan_get_tx_params(struct wlan_iface *wif) { int val = 0; size_t argsize; /* * XXX: Reset IEEE80211_RATE_MCS bit on IEEE80211_MODE_11NA * and IEEE80211_MODE_11NG modes. */ argsize = sizeof(struct ieee80211_txparams_req); if (wlan_ioctl(wif->wname, IEEE80211_IOC_TXPARAMS, &val, &wif->txparams, &argsize, 0) < 0) return (-1); return (0); } int wlan_set_tx_params(struct wlan_iface *wif, int32_t pmode __unused) { int val = 0; size_t argsize; /* * XXX: Set IEEE80211_RATE_MCS bit on IEEE80211_MODE_11NA * and IEEE80211_MODE_11NG modes. */ argsize = sizeof(struct ieee80211_txparams_req); if (wlan_ioctl(wif->wname, IEEE80211_IOC_TXPARAMS, &val, &wif->txparams, &argsize, 1) < 0) return (-1); return (0); } int wlan_clone_create(struct wlan_iface *wif) { struct ifreq ifr; struct ieee80211_clone_params wcp; static const uint8_t zerobssid[IEEE80211_ADDR_LEN]; memset(&wcp, 0, sizeof(wcp)); memset(&ifr, 0, sizeof(ifr)); /* Sanity checks. */ if (wif == NULL || wif->pname[0] == '\0' || wif->mode > WLAN_IFMODE_MAX) return (SNMP_ERR_INCONS_VALUE); if (wif->mode == WlanIfaceOperatingModeType_wds && memcmp(wif->dbssid, zerobssid, IEEE80211_ADDR_LEN) == 0) return (SNMP_ERR_INCONS_VALUE); strlcpy(wcp.icp_parent, wif->pname, IFNAMSIZ); if ((wif->flags & WlanIfaceFlagsType_uniqueBssid) != 0) wcp.icp_flags |= IEEE80211_CLONE_BSSID; if ((wif->flags & WlanIfaceFlagsType_noBeacons) != 0) wcp.icp_flags |= IEEE80211_CLONE_NOBEACONS; if (wif->mode == WlanIfaceOperatingModeType_wds && (wif->flags & WlanIfaceFlagsType_wdsLegacy) != 0) wcp.icp_flags |= IEEE80211_CLONE_WDSLEGACY; switch (wif->mode) { case WlanIfaceOperatingModeType_ibss: wcp.icp_opmode = IEEE80211_M_IBSS; break; case WlanIfaceOperatingModeType_station: wcp.icp_opmode = IEEE80211_M_STA; break; case WlanIfaceOperatingModeType_wds: wcp.icp_opmode = IEEE80211_M_WDS; break; case WlanIfaceOperatingModeType_adhocDemo: wcp.icp_opmode = IEEE80211_M_AHDEMO; break; case WlanIfaceOperatingModeType_hostAp: wcp.icp_opmode = IEEE80211_M_HOSTAP; break; case WlanIfaceOperatingModeType_monitor: wcp.icp_opmode = IEEE80211_M_MONITOR; break; case WlanIfaceOperatingModeType_meshPoint: wcp.icp_opmode = IEEE80211_M_MBSS; break; case WlanIfaceOperatingModeType_tdma: wcp.icp_opmode = IEEE80211_M_AHDEMO; wcp.icp_flags |= IEEE80211_CLONE_TDMA; break; } memcpy(wcp.icp_bssid, wif->dbssid, IEEE80211_ADDR_LEN); if (memcmp(wif->dlmac, zerobssid, IEEE80211_ADDR_LEN) != 0) { memcpy(wcp.icp_macaddr, wif->dlmac, IEEE80211_ADDR_LEN); wcp.icp_flags |= IEEE80211_CLONE_MACADDR; } strlcpy(ifr.ifr_name, wif->wname, IFNAMSIZ); ifr.ifr_data = (caddr_t) &wcp; if (ioctl(sock, SIOCIFCREATE2, (caddr_t) &ifr) < 0) { syslog(LOG_ERR, "wlan clone create: ioctl(SIOCIFCREATE2) " "failed: %s", strerror(errno)); return (SNMP_ERR_GENERR); } return (SNMP_ERR_NOERROR); } int wlan_clone_destroy(struct wlan_iface *wif) { struct ifreq ifr; if (wif == NULL) return (SNMP_ERR_INCONS_VALUE); memset(&ifr, 0, sizeof(ifr)); strcpy(ifr.ifr_name, wif->wname); if (ioctl(sock, SIOCIFDESTROY, &ifr) < 0) { syslog(LOG_ERR, "wlan clone destroy: ioctl(SIOCIFDESTROY) " "failed: %s", strerror(errno)); return (SNMP_ERR_GENERR); } return (SNMP_ERR_NOERROR); } static int wlan_config_snmp2ioctl(int which) { int op; switch (which) { case LEAF_wlanIfacePacketBurst: op = IEEE80211_IOC_BURST; break; case LEAF_wlanIfaceCountryCode: op = IEEE80211_IOC_REGDOMAIN; break; case LEAF_wlanIfaceRegDomain: op = IEEE80211_IOC_REGDOMAIN; break; case LEAF_wlanIfaceDesiredSsid: op = IEEE80211_IOC_SSID; break; case LEAF_wlanIfaceDesiredChannel: op = IEEE80211_IOC_CURCHAN; break; case LEAF_wlanIfaceDynamicFreqSelection: op = IEEE80211_IOC_DFS; break; case LEAF_wlanIfaceFastFrames: op = IEEE80211_IOC_FF; break; case LEAF_wlanIfaceDturbo: op = IEEE80211_IOC_TURBOP; break; case LEAF_wlanIfaceTxPower: op = IEEE80211_IOC_TXPOWER; break; case LEAF_wlanIfaceFragmentThreshold: op = IEEE80211_IOC_FRAGTHRESHOLD; break; case LEAF_wlanIfaceRTSThreshold: op = IEEE80211_IOC_RTSTHRESHOLD; break; case LEAF_wlanIfaceWlanPrivacySubscribe: op = IEEE80211_IOC_WPS; break; case LEAF_wlanIfaceBgScan: op = IEEE80211_IOC_BGSCAN; break; case LEAF_wlanIfaceBgScanIdle: op = IEEE80211_IOC_BGSCAN_IDLE; break; case LEAF_wlanIfaceBgScanInterval: op = IEEE80211_IOC_BGSCAN_INTERVAL; break; case LEAF_wlanIfaceBeaconMissedThreshold: op = IEEE80211_IOC_BMISSTHRESHOLD; break; case LEAF_wlanIfaceDesiredBssid: op = IEEE80211_IOC_BSSID; break; case LEAF_wlanIfaceRoamingMode: op = IEEE80211_IOC_ROAMING; break; case LEAF_wlanIfaceDot11d: op = IEEE80211_IOC_DOTD; break; case LEAF_wlanIfaceDot11h: op = IEEE80211_IOC_DOTH; break; case LEAF_wlanIfaceDynamicWds: op = IEEE80211_IOC_DWDS; break; case LEAF_wlanIfacePowerSave: op = IEEE80211_IOC_POWERSAVE; break; case LEAF_wlanIfaceApBridge: op = IEEE80211_IOC_APBRIDGE; break; case LEAF_wlanIfaceBeaconInterval: op = IEEE80211_IOC_BEACON_INTERVAL; break; case LEAF_wlanIfaceDtimPeriod: op = IEEE80211_IOC_DTIM_PERIOD; break; case LEAF_wlanIfaceHideSsid: op = IEEE80211_IOC_HIDESSID; break; case LEAF_wlanIfaceInactivityProccess: op = IEEE80211_IOC_INACTIVITY; break; case LEAF_wlanIfaceDot11gProtMode: op = IEEE80211_IOC_PROTMODE; break; case LEAF_wlanIfaceDot11gPureMode: op = IEEE80211_IOC_PUREG; break; case LEAF_wlanIfaceDot11nPureMode: op = IEEE80211_IOC_PUREN; break; case LEAF_wlanIfaceDot11nAmpdu: op = IEEE80211_IOC_AMPDU; break; case LEAF_wlanIfaceDot11nAmpduDensity: op = IEEE80211_IOC_AMPDU_DENSITY; break; case LEAF_wlanIfaceDot11nAmpduLimit: op = IEEE80211_IOC_AMPDU_LIMIT; break; case LEAF_wlanIfaceDot11nAmsdu: op = IEEE80211_IOC_AMSDU; break; case LEAF_wlanIfaceDot11nAmsduLimit: op = IEEE80211_IOC_AMSDU_LIMIT; break; case LEAF_wlanIfaceDot11nHighThroughput: op = IEEE80211_IOC_HTCONF; break; case LEAF_wlanIfaceDot11nHTCompatible: op = IEEE80211_IOC_HTCOMPAT; break; case LEAF_wlanIfaceDot11nHTProtMode: op = IEEE80211_IOC_HTPROTMODE; break; case LEAF_wlanIfaceDot11nRIFS: op = IEEE80211_IOC_RIFS; break; case LEAF_wlanIfaceDot11nShortGI: op = IEEE80211_IOC_SHORTGI; break; case LEAF_wlanIfaceDot11nSMPSMode: op = IEEE80211_IOC_SMPS; break; case LEAF_wlanIfaceTdmaSlot: op = IEEE80211_IOC_TDMA_SLOT; break; case LEAF_wlanIfaceTdmaSlotCount: op = IEEE80211_IOC_TDMA_SLOTCNT; break; case LEAF_wlanIfaceTdmaSlotLength: op = IEEE80211_IOC_TDMA_SLOTLEN; break; case LEAF_wlanIfaceTdmaBeaconInterval: op = IEEE80211_IOC_TDMA_BINTERVAL; break; default: op = -1; } return (op); } static enum WlanRegDomainCode wlan_regdomain_to_snmp(int which) { enum WlanRegDomainCode reg_domain; switch (which) { case SKU_FCC: reg_domain = WlanRegDomainCode_fcc; break; case SKU_CA: reg_domain = WlanRegDomainCode_ca; break; case SKU_ETSI: reg_domain = WlanRegDomainCode_etsi; break; case SKU_ETSI2: reg_domain = WlanRegDomainCode_etsi2; break; case SKU_ETSI3: reg_domain = WlanRegDomainCode_etsi3; break; case SKU_FCC3: reg_domain = WlanRegDomainCode_fcc3; break; case SKU_JAPAN: reg_domain = WlanRegDomainCode_japan; break; case SKU_KOREA: reg_domain = WlanRegDomainCode_korea; break; case SKU_APAC: reg_domain = WlanRegDomainCode_apac; break; case SKU_APAC2: reg_domain = WlanRegDomainCode_apac2; break; case SKU_APAC3: reg_domain = WlanRegDomainCode_apac3; break; case SKU_ROW: reg_domain = WlanRegDomainCode_row; break; case SKU_NONE: reg_domain = WlanRegDomainCode_none; break; case SKU_DEBUG: reg_domain = WlanRegDomainCode_debug; break; case SKU_SR9: reg_domain = WlanRegDomainCode_sr9; break; case SKU_XR9: reg_domain = WlanRegDomainCode_xr9; break; case SKU_GZ901: reg_domain = WlanRegDomainCode_gz901; break; case 0: reg_domain = WlanRegDomainCode_none; break; default: syslog(LOG_ERR, "unknown regdomain (0x%x) ", which); reg_domain = WlanRegDomainCode_none; break; } return (reg_domain); } static int wlan_snmp_to_regdomain(enum WlanRegDomainCode regdomain) { int which; switch (regdomain) { case WlanRegDomainCode_fcc: which = SKU_FCC; break; case WlanRegDomainCode_ca: which = SKU_CA; break; case WlanRegDomainCode_etsi: which = SKU_ETSI; break; case WlanRegDomainCode_etsi2: which = SKU_ETSI2; break; case WlanRegDomainCode_etsi3: which = SKU_ETSI3; break; case WlanRegDomainCode_fcc3: which = SKU_FCC3; break; case WlanRegDomainCode_japan: which = SKU_JAPAN; break; case WlanRegDomainCode_korea: which = SKU_KOREA; break; case WlanRegDomainCode_apac: which = SKU_APAC; break; case WlanRegDomainCode_apac2: which = SKU_APAC2; break; case WlanRegDomainCode_apac3: which = SKU_APAC3; break; case WlanRegDomainCode_row: which = SKU_ROW; break; case WlanRegDomainCode_none: which = SKU_NONE; break; case WlanRegDomainCode_debug: which = SKU_DEBUG; break; case WlanRegDomainCode_sr9: which = SKU_SR9; break; case WlanRegDomainCode_xr9: which = SKU_XR9; break; case WlanRegDomainCode_gz901: which = SKU_GZ901; break; default: syslog(LOG_ERR, "unknown snmp regdomain (0x%x) ", regdomain); which = SKU_NONE; break; } return (which); } static int wlan_config_get_country(struct wlan_iface *wif) { int val = 0; size_t argsize; struct ieee80211_regdomain regdomain; memset(®domain, 0, sizeof(regdomain)); argsize = sizeof(regdomain); if (wlan_ioctl(wif->wname, IEEE80211_IOC_REGDOMAIN, &val, ®domain, &argsize, 0) < 0) return (-1); wif->reg_domain = wlan_regdomain_to_snmp(regdomain.regdomain); wif->country_code[0] = regdomain.isocc[0]; wif->country_code[1] = regdomain.isocc[1]; wif->country_code[2] = regdomain.location; return (0); } static int wlan_config_set_country(struct wlan_iface *wif, char *ccode, int rdomain) { int val = 0, txpowermax; uint32_t i; size_t argsize = 0; struct ieee80211_regdomain_req *regdomain; if (wlan_get_channel_list(wif) < 0) return (-1); if (wif->nchannels == 0) { syslog(LOG_ERR, "iface %s - set regdomain failed", wif->wname); return (-1); } if (wlan_ioctl(wif->wname, IEEE80211_IOC_TXPOWMAX, &txpowermax, 0, &argsize, 0) < 0) return (-1); regdomain = malloc(IEEE80211_REGDOMAIN_SIZE(wif->nchannels)); if (regdomain == NULL) return (-1); memset(regdomain, 0, IEEE80211_REGDOMAIN_SIZE(wif->nchannels)); argsize = IEEE80211_REGDOMAIN_SIZE(wif->nchannels); /* XXX: recheck with how this is done by ifconfig(8) */ regdomain->rd.regdomain = wlan_snmp_to_regdomain(rdomain); regdomain->rd.isocc[0] = ccode[0]; regdomain->rd.isocc[1] = ccode[1]; regdomain->rd.location = ccode[2]; /* XXX: fill the channel list properly */ regdomain->chaninfo.ic_nchans = wif->nchannels; memcpy(regdomain->chaninfo.ic_chans, wif->chanlist, wif->nchannels * sizeof(struct ieee80211_channel)); for (i = 0; i < wif->nchannels; i++) regdomain->chaninfo.ic_chans[i].ic_maxregpower = txpowermax; wif->state = wlanIfaceState_down; if (wlan_config_state(wif, 1) < 0 || wlan_ioctl(wif->wname, IEEE80211_IOC_REGDOMAIN, &val, regdomain, &argsize, 1) < 0) { free(regdomain); return (-1); } wif->state = wlanIfaceState_up; (void)wlan_config_state(wif, 1); wif->reg_domain = wlan_regdomain_to_snmp(regdomain->rd.regdomain); wif->country_code[0] = regdomain->rd.isocc[0]; wif->country_code[1] = regdomain->rd.isocc[1]; wif->country_code[2] = regdomain->rd.location; free(regdomain); return (0); } int wlan_config_get_dssid(struct wlan_iface *wif) { int val = -1; size_t argsize = IEEE80211_NWID_LEN + 1; char ssid[IEEE80211_NWID_LEN + 1]; memset(ssid, 0, IEEE80211_NWID_LEN + 1); if (wlan_ioctl(wif->wname, (wif->mode == WlanIfaceOperatingModeType_meshPoint) ? IEEE80211_IOC_MESH_ID : IEEE80211_IOC_SSID, &val, ssid, &argsize, 0) < 0) return (-1); if (argsize > IEEE80211_NWID_LEN) argsize = IEEE80211_NWID_LEN; memcpy(wif->desired_ssid, ssid, argsize); wif->desired_ssid[argsize] = '\0'; return (0); } int wlan_config_set_dssid(struct wlan_iface *wif, char *ssid, int slen) { int val = 0; size_t argsize = slen; if (wlan_ioctl(wif->wname, (wif->mode == WlanIfaceOperatingModeType_meshPoint) ? IEEE80211_IOC_MESH_ID : IEEE80211_IOC_SSID, &val, ssid, &argsize, 1) < 0) return (-1); if (argsize > IEEE80211_NWID_LEN) argsize = IEEE80211_NWID_LEN; memcpy(wif->desired_ssid, ssid, argsize); wif->desired_ssid[argsize] = '\0'; return (0); } static int wlan_config_get_dchannel(struct wlan_iface *wif) { uint32_t i = 0; int val = 0; size_t argsize = sizeof(struct ieee80211_channel); struct ieee80211_channel chan; if (wlan_get_channel_list(wif) < 0) return (-1); memset(&chan, 0, sizeof(chan)); if (wlan_ioctl(wif->wname, IEEE80211_IOC_CURCHAN, &val, &chan, &argsize, 0) < 0) return (-1); for (i = 0; i < wif->nchannels; i++) if (chan.ic_ieee == wif->chanlist[i].ic_ieee && chan.ic_flags == wif->chanlist[i].ic_flags) { wif->desired_channel = i + 1; break; } return (0); } static int wlan_config_set_dchannel(struct wlan_iface *wif, uint32_t dchannel) { int val = 0; size_t argsize = sizeof(struct ieee80211_channel); struct ieee80211_channel chan; if (wlan_get_channel_list(wif) < 0) return (-1); if (dchannel > wif->nchannels) return (-1); memcpy(&chan, wif->chanlist + dchannel - 1, sizeof(chan)); if (wlan_ioctl(wif->wname, IEEE80211_IOC_CURCHAN, &val, &chan, &argsize, 1) < 0) return (-1); wif->desired_channel = dchannel; return (0); } static int wlan_config_get_bssid(struct wlan_iface *wif) { int val = 0; size_t argsize = IEEE80211_ADDR_LEN; char bssid[IEEE80211_ADDR_LEN]; memset(bssid, 0, IEEE80211_ADDR_LEN); if (wlan_ioctl(wif->wname, IEEE80211_IOC_BSSID, &val, bssid, &argsize, 0) < 0 || argsize != IEEE80211_ADDR_LEN) return (-1); memcpy(wif->desired_bssid, bssid, IEEE80211_ADDR_LEN); return (0); } static int wlan_config_set_bssid(struct wlan_iface *wif, uint8_t *bssid) { int val = 0; size_t argsize = IEEE80211_ADDR_LEN; if (wlan_ioctl(wif->wname, IEEE80211_IOC_BSSID, &val, bssid, &argsize, 1) < 0 || argsize != IEEE80211_ADDR_LEN) return (-1); memcpy(wif->desired_bssid, bssid, IEEE80211_ADDR_LEN); return (0); } /* * Convert the value returned by the kernel to the appropriate SNMP * representation and set the corresponding interface member accordingly. */ static void wlan_config_set_snmp_intval(struct wlan_iface *wif, int op, int val) { switch (op) { case IEEE80211_IOC_BURST: if (val == 0) wif->packet_burst = TruthValue_false; else wif->packet_burst = TruthValue_true; break; case IEEE80211_IOC_DFS: if (val == 0) wif->dyn_frequency = TruthValue_false; else wif->dyn_frequency = TruthValue_true; break; case IEEE80211_IOC_FF: if (val == 0) wif->fast_frames = TruthValue_false; else wif->fast_frames = TruthValue_true; break; case IEEE80211_IOC_TURBOP: if (val == 0) wif->dturbo = TruthValue_false; else wif->dturbo = TruthValue_true; break; case IEEE80211_IOC_TXPOWER: wif->tx_power = val / 2; break; case IEEE80211_IOC_FRAGTHRESHOLD: wif->frag_threshold = val; break; case IEEE80211_IOC_RTSTHRESHOLD: wif->rts_threshold = val; break; case IEEE80211_IOC_WPS: if (val == 0) wif->priv_subscribe = TruthValue_false; else wif->priv_subscribe = TruthValue_true; break; case IEEE80211_IOC_BGSCAN: if (val == 0) wif->bg_scan = TruthValue_false; else wif->bg_scan = TruthValue_true; break; case IEEE80211_IOC_BGSCAN_IDLE: wif->bg_scan_idle = val; break; case IEEE80211_IOC_BGSCAN_INTERVAL: wif->bg_scan_interval = val; break; case IEEE80211_IOC_BMISSTHRESHOLD: wif->beacons_missed = val; break; case IEEE80211_IOC_ROAMING: switch (val) { case IEEE80211_ROAMING_DEVICE: wif->roam_mode = wlanIfaceRoamingMode_device; break; case IEEE80211_ROAMING_MANUAL: wif->roam_mode = wlanIfaceRoamingMode_manual; break; case IEEE80211_ROAMING_AUTO: /* FALTHROUGH */ default: wif->roam_mode = wlanIfaceRoamingMode_auto; break; } break; case IEEE80211_IOC_DOTD: if (val == 0) wif->dot11d = TruthValue_false; else wif->dot11d = TruthValue_true; break; case IEEE80211_IOC_DOTH: if (val == 0) wif->dot11h = TruthValue_false; else wif->dot11h = TruthValue_true; break; case IEEE80211_IOC_DWDS: if (val == 0) wif->dynamic_wds = TruthValue_false; else wif->dynamic_wds = TruthValue_true; break; case IEEE80211_IOC_POWERSAVE: if (val == 0) wif->power_save = TruthValue_false; else wif->power_save = TruthValue_true; break; case IEEE80211_IOC_APBRIDGE: if (val == 0) wif->ap_bridge = TruthValue_false; else wif->ap_bridge = TruthValue_true; break; case IEEE80211_IOC_BEACON_INTERVAL: wif->beacon_interval = val; break; case IEEE80211_IOC_DTIM_PERIOD: wif->dtim_period = val; break; case IEEE80211_IOC_HIDESSID: if (val == 0) wif->hide_ssid = TruthValue_false; else wif->hide_ssid = TruthValue_true; break; case IEEE80211_IOC_INACTIVITY: if (val == 0) wif->inact_process = TruthValue_false; else wif->inact_process = TruthValue_true; break; case IEEE80211_IOC_PROTMODE: switch (val) { case IEEE80211_PROTMODE_CTS: wif->do11g_protect = wlanIfaceDot11gProtMode_cts; break; case IEEE80211_PROTMODE_RTSCTS: wif->do11g_protect = wlanIfaceDot11gProtMode_rtscts; break; case IEEE80211_PROTMODE_OFF: /* FALLTHROUGH */ default: wif->do11g_protect = wlanIfaceDot11gProtMode_off; break; } break; case IEEE80211_IOC_PUREG: if (val == 0) wif->dot11g_pure = TruthValue_false; else wif->dot11g_pure = TruthValue_true; break; case IEEE80211_IOC_PUREN: if (val == 0) wif->dot11n_pure = TruthValue_false; else wif->dot11n_pure = TruthValue_true; break; case IEEE80211_IOC_AMPDU: switch (val) { case 0: wif->ampdu = WlanIfaceDot11nPduType_disabled; break; case 1: wif->ampdu = WlanIfaceDot11nPduType_txOnly; break; case 2: wif->ampdu = WlanIfaceDot11nPduType_rxOnly; break; case 3: /* FALLTHROUGH */ default: wif->ampdu = WlanIfaceDot11nPduType_txAndRx; break; } break; case IEEE80211_IOC_AMPDU_DENSITY: switch (val) { case IEEE80211_HTCAP_MPDUDENSITY_025: wif->ampdu_density = 25; break; case IEEE80211_HTCAP_MPDUDENSITY_05: wif->ampdu_density = 50; break; case IEEE80211_HTCAP_MPDUDENSITY_1: wif->ampdu_density = 100; break; case IEEE80211_HTCAP_MPDUDENSITY_2: wif->ampdu_density = 200; break; case IEEE80211_HTCAP_MPDUDENSITY_4: wif->ampdu_density = 400; break; case IEEE80211_HTCAP_MPDUDENSITY_8: wif->ampdu_density = 800; break; case IEEE80211_HTCAP_MPDUDENSITY_16: wif->ampdu_density = 1600; break; case IEEE80211_HTCAP_MPDUDENSITY_NA: default: wif->ampdu_density = 0; break; } break; case IEEE80211_IOC_AMPDU_LIMIT: switch (val) { case IEEE80211_HTCAP_MAXRXAMPDU_8K: wif->ampdu_limit = 8192; break; case IEEE80211_HTCAP_MAXRXAMPDU_16K: wif->ampdu_limit = 16384; break; case IEEE80211_HTCAP_MAXRXAMPDU_32K: wif->ampdu_limit = 32768; break; case IEEE80211_HTCAP_MAXRXAMPDU_64K: default: wif->ampdu_limit = 65536; break; } break; case IEEE80211_IOC_AMSDU: switch (val) { case 0: wif->amsdu = WlanIfaceDot11nPduType_disabled; break; case 1: wif->amsdu = WlanIfaceDot11nPduType_txOnly; break; case 3: wif->amsdu = WlanIfaceDot11nPduType_txAndRx; break; case 2: default: /* FALLTHROUGH */ wif->amsdu = WlanIfaceDot11nPduType_rxOnly; break; } break; case IEEE80211_IOC_AMSDU_LIMIT: wif->amsdu_limit = val; break; case IEEE80211_IOC_HTCONF: if (val == 0) /* XXX */ wif->ht_enabled = TruthValue_false; else wif->ht_enabled = TruthValue_true; break; case IEEE80211_IOC_HTCOMPAT: if (val == 0) wif->ht_compatible = TruthValue_false; else wif->ht_compatible = TruthValue_true; break; case IEEE80211_IOC_HTPROTMODE: if (val == IEEE80211_PROTMODE_RTSCTS) wif->ht_prot_mode = wlanIfaceDot11nHTProtMode_rts; else wif->ht_prot_mode = wlanIfaceDot11nHTProtMode_off; break; case IEEE80211_IOC_RIFS: if (val == 0) wif->rifs = TruthValue_false; else wif->rifs = TruthValue_true; break; case IEEE80211_IOC_SHORTGI: if (val == 0) wif->short_gi = TruthValue_false; else wif->short_gi = TruthValue_true; break; case IEEE80211_IOC_SMPS: switch (val) { case IEEE80211_HTCAP_SMPS_DYNAMIC: wif->smps_mode = wlanIfaceDot11nSMPSMode_dynamic; break; case IEEE80211_HTCAP_SMPS_ENA: wif->smps_mode = wlanIfaceDot11nSMPSMode_static; break; case IEEE80211_HTCAP_SMPS_OFF: /* FALLTHROUGH */ default: wif->smps_mode = wlanIfaceDot11nSMPSMode_disabled; break; } break; case IEEE80211_IOC_TDMA_SLOT: wif->tdma_slot = val; break; case IEEE80211_IOC_TDMA_SLOTCNT: wif->tdma_slot_count = val; break; case IEEE80211_IOC_TDMA_SLOTLEN: wif->tdma_slot_length = val; break; case IEEE80211_IOC_TDMA_BINTERVAL: wif->tdma_binterval = val; break; default: break; } } /* * Convert an SNMP value to the kernel equivalent and also do sanity check * for each specific type. */ static int wlan_config_snmp2value(int which, int sval, int *value) { *value = 0; switch (which) { case IEEE80211_IOC_BURST: case IEEE80211_IOC_DFS: case IEEE80211_IOC_FF: case IEEE80211_IOC_TURBOP: case IEEE80211_IOC_WPS: case IEEE80211_IOC_BGSCAN: case IEEE80211_IOC_DOTD: case IEEE80211_IOC_DOTH: case IEEE80211_IOC_DWDS: case IEEE80211_IOC_POWERSAVE: case IEEE80211_IOC_APBRIDGE: case IEEE80211_IOC_HIDESSID: case IEEE80211_IOC_INACTIVITY: case IEEE80211_IOC_PUREG: case IEEE80211_IOC_PUREN: case IEEE80211_IOC_HTCONF: case IEEE80211_IOC_HTCOMPAT: case IEEE80211_IOC_RIFS: if (sval == TruthValue_true) *value = 1; else if (sval != TruthValue_false) return (SNMP_ERR_INCONS_VALUE); break; case IEEE80211_IOC_REGDOMAIN: break; case IEEE80211_IOC_SSID: break; case IEEE80211_IOC_CURCHAN: break; case IEEE80211_IOC_TXPOWER: *value = sval * 2; break; case IEEE80211_IOC_FRAGTHRESHOLD: if (sval < IEEE80211_FRAG_MIN || sval > IEEE80211_FRAG_MAX) return (SNMP_ERR_INCONS_VALUE); *value = sval; break; case IEEE80211_IOC_RTSTHRESHOLD: if (sval < IEEE80211_RTS_MIN || sval > IEEE80211_RTS_MAX) return (SNMP_ERR_INCONS_VALUE); *value = sval; break; case IEEE80211_IOC_BGSCAN_IDLE: if (sval < WLAN_BGSCAN_IDLE_MIN) return (SNMP_ERR_INCONS_VALUE); *value = sval; break; case IEEE80211_IOC_BGSCAN_INTERVAL: if (sval < WLAN_SCAN_VALID_MIN) return (SNMP_ERR_INCONS_VALUE); *value = sval; break; case IEEE80211_IOC_BMISSTHRESHOLD: if (sval < IEEE80211_HWBMISS_MIN || sval > IEEE80211_HWBMISS_MAX) return (SNMP_ERR_INCONS_VALUE); *value = sval; break; case IEEE80211_IOC_BSSID: break; case IEEE80211_IOC_ROAMING: switch (sval) { case wlanIfaceRoamingMode_device: *value = IEEE80211_ROAMING_DEVICE; break; case wlanIfaceRoamingMode_manual: *value = IEEE80211_ROAMING_MANUAL; break; case wlanIfaceRoamingMode_auto: *value = IEEE80211_ROAMING_AUTO; break; default: return (SNMP_ERR_INCONS_VALUE); } break; case IEEE80211_IOC_BEACON_INTERVAL: if (sval < IEEE80211_BINTVAL_MIN || sval > IEEE80211_BINTVAL_MAX) return (SNMP_ERR_INCONS_VALUE); *value = sval; break; case IEEE80211_IOC_DTIM_PERIOD: if (sval < IEEE80211_DTIM_MIN || sval > IEEE80211_DTIM_MAX) return (SNMP_ERR_INCONS_VALUE); *value = sval; break; case IEEE80211_IOC_PROTMODE: switch (sval) { case wlanIfaceDot11gProtMode_cts: *value = IEEE80211_PROTMODE_CTS; break; case wlanIfaceDot11gProtMode_rtscts: *value = IEEE80211_PROTMODE_RTSCTS; break; case wlanIfaceDot11gProtMode_off: *value = IEEE80211_PROTMODE_OFF; break; default: return (SNMP_ERR_INCONS_VALUE); } break; case IEEE80211_IOC_AMPDU: switch (sval) { case WlanIfaceDot11nPduType_disabled: break; case WlanIfaceDot11nPduType_txOnly: *value = 1; break; case WlanIfaceDot11nPduType_rxOnly: *value = 2; break; case WlanIfaceDot11nPduType_txAndRx: *value = 3; break; default: return (SNMP_ERR_INCONS_VALUE); } break; case IEEE80211_IOC_AMPDU_DENSITY: switch (sval) { case 0: *value = IEEE80211_HTCAP_MPDUDENSITY_NA; break; case 25: *value = IEEE80211_HTCAP_MPDUDENSITY_025; break; case 50: *value = IEEE80211_HTCAP_MPDUDENSITY_05; break; case 100: *value = IEEE80211_HTCAP_MPDUDENSITY_1; break; case 200: *value = IEEE80211_HTCAP_MPDUDENSITY_2; break; case 400: *value = IEEE80211_HTCAP_MPDUDENSITY_4; break; case 800: *value = IEEE80211_HTCAP_MPDUDENSITY_8; break; case 1600: *value = IEEE80211_HTCAP_MPDUDENSITY_16; break; default: return (SNMP_ERR_INCONS_VALUE); } break; case IEEE80211_IOC_AMPDU_LIMIT: switch (sval) { case 8192: *value = IEEE80211_HTCAP_MAXRXAMPDU_8K; break; case 16384: *value = IEEE80211_HTCAP_MAXRXAMPDU_16K; break; case 32768: *value = IEEE80211_HTCAP_MAXRXAMPDU_32K; break; case 65536: *value = IEEE80211_HTCAP_MAXRXAMPDU_64K; break; default: return (SNMP_ERR_INCONS_VALUE); } break; case IEEE80211_IOC_AMSDU: switch (sval) { case WlanIfaceDot11nPduType_disabled: break; case WlanIfaceDot11nPduType_txOnly: *value = 1; break; case WlanIfaceDot11nPduType_rxOnly: *value = 2; break; case WlanIfaceDot11nPduType_txAndRx: *value = 3; break; default: return (SNMP_ERR_INCONS_VALUE); } break; case IEEE80211_IOC_AMSDU_LIMIT: if (sval == 3839 || sval == 0) *value = IEEE80211_HTCAP_MAXAMSDU_3839; else if (sval == 7935) *value = IEEE80211_HTCAP_MAXAMSDU_7935; else return (SNMP_ERR_INCONS_VALUE); break; case IEEE80211_IOC_HTPROTMODE: switch (sval) { case wlanIfaceDot11nHTProtMode_rts: *value = IEEE80211_PROTMODE_RTSCTS; break; case wlanIfaceDot11nHTProtMode_off: break; default: return (SNMP_ERR_INCONS_VALUE); } break; case IEEE80211_IOC_SHORTGI: if (sval == TruthValue_true) *value = IEEE80211_HTCAP_SHORTGI20 | IEEE80211_HTCAP_SHORTGI40; else if (sval != TruthValue_false) return (SNMP_ERR_INCONS_VALUE); break; case IEEE80211_IOC_SMPS: switch (sval) { case wlanIfaceDot11nSMPSMode_disabled: *value = IEEE80211_HTCAP_SMPS_OFF; break; case wlanIfaceDot11nSMPSMode_static: *value = IEEE80211_HTCAP_SMPS_ENA; break; case wlanIfaceDot11nSMPSMode_dynamic: *value = IEEE80211_HTCAP_SMPS_DYNAMIC; break; default: return (SNMP_ERR_INCONS_VALUE); } break; case IEEE80211_IOC_TDMA_SLOT: if (sval < 0 || sval > WLAN_TDMA_MAXSLOTS) /* XXX */ return (SNMP_ERR_INCONS_VALUE); *value = sval; break; case IEEE80211_IOC_TDMA_SLOTCNT: if (sval < 0 || sval > WLAN_TDMA_MAXSLOTS) /* XXX */ return (SNMP_ERR_INCONS_VALUE); *value = sval; break; case IEEE80211_IOC_TDMA_SLOTLEN: if (sval < 2*100 || sval > 0xfffff) /* XXX */ return (SNMP_ERR_INCONS_VALUE); *value = sval; break; case IEEE80211_IOC_TDMA_BINTERVAL: if (sval < 1) /* XXX */ return (SNMP_ERR_INCONS_VALUE); *value = sval; break; default: return (SNMP_ERR_INCONS_VALUE); } return (SNMP_ERR_NOERROR); } /* * Sanity checks for the wlanIfaceConfigTable. */ static int wlan_config_check(struct wlan_iface *wif, int op) { switch (op) { case IEEE80211_IOC_BURST: if ((wif->drivercaps & (0x1 << WlanDriverCaps_burst)) == 0) { wif->packet_burst = TruthValue_false; return (-1); } break; case IEEE80211_IOC_DFS: if ((wif->drivercaps & (0x1 << WlanDriverCaps_dfs)) == 0) { wif->dyn_frequency = TruthValue_false; return (-1); } break; case IEEE80211_IOC_FF: if ((wif->drivercaps & (0x1 << WlanDriverCaps_athFastFrames)) == 0) { wif->fast_frames = TruthValue_false; return (-1); } break; case IEEE80211_IOC_TURBOP: if ((wif->drivercaps & (0x1 << WlanDriverCaps_athTurbo)) == 0) { wif->dturbo = TruthValue_false; return (-1); } break; case IEEE80211_IOC_TXPOWER: if ((wif->drivercaps & (0x1 << WlanDriverCaps_txPmgt)) == 0) { wif->tx_power = 0; return (-1); } break; case IEEE80211_IOC_FRAGTHRESHOLD: if ((wif->drivercaps & (0x1 << WlanDriverCaps_txFrag)) == 0) { wif->frag_threshold = IEEE80211_FRAG_MAX; return (-1); } break; case IEEE80211_IOC_DWDS: if ((wif->drivercaps & (0x1 << WlanDriverCaps_wds)) == 0) { wif->dynamic_wds = TruthValue_false; return (-1); } break; case IEEE80211_IOC_POWERSAVE: if ((wif->drivercaps & (0x1 << WlanDriverCaps_pmgt)) == 0) { wif->power_save = TruthValue_false; return (-1); } break; case IEEE80211_IOC_BEACON_INTERVAL: if (wif->mode != WlanIfaceOperatingModeType_hostAp && wif->mode != WlanIfaceOperatingModeType_meshPoint && wif->mode != WlanIfaceOperatingModeType_ibss) { wif->beacon_interval = 100; /* XXX */ return (-1); } break; case IEEE80211_IOC_DTIM_PERIOD: if (wif->mode != WlanIfaceOperatingModeType_hostAp && wif->mode != WlanIfaceOperatingModeType_meshPoint && wif->mode != WlanIfaceOperatingModeType_ibss) { wif->dtim_period = 1; /* XXX */ return (-1); } break; case IEEE80211_IOC_PUREN: if ((wif->htcaps & (0x1 << WlanHTCaps_htcHt)) == 0) { wif->dot11n_pure = TruthValue_false; return (-1); } break; case IEEE80211_IOC_AMPDU: if ((wif->htcaps & (0x1 << WlanHTCaps_htcAmpdu)) == 0) { wif->ampdu = WlanIfaceDot11nPduType_disabled; return (-1); } break; case IEEE80211_IOC_AMSDU: if ((wif->htcaps & (0x1 << WlanHTCaps_htcAmsdu)) == 0) { wif->amsdu = WlanIfaceDot11nPduType_disabled; return (-1); } break; case IEEE80211_IOC_RIFS: if ((wif->htcaps & (0x1 << WlanHTCaps_htcRifs)) == 0) { wif->rifs = TruthValue_false; return (-1); } break; case IEEE80211_IOC_SHORTGI: if ((wif->htcaps & (0x1 << WlanHTCaps_shortGi20 | 0x1 << WlanHTCaps_shortGi40)) == 0) { wif->short_gi = TruthValue_false; return (-1); } break; case IEEE80211_IOC_SMPS: if ((wif->htcaps & (0x1 << WlanHTCaps_htcSmps)) == 0) { wif->smps_mode = wlanIfaceDot11nSMPSMode_disabled; return (-1); } break; case IEEE80211_IOC_TDMA_SLOT: if ((wif->drivercaps & (0x1 << WlanDriverCaps_tdma)) == 0) { wif->tdma_slot = 0; return (-1); } break; case IEEE80211_IOC_TDMA_SLOTCNT: if ((wif->drivercaps & (0x1 << WlanDriverCaps_tdma)) == 0) { wif->tdma_slot_count = 0; return (-1); } break; case IEEE80211_IOC_TDMA_SLOTLEN: if ((wif->drivercaps & (0x1 << WlanDriverCaps_tdma)) == 0) { wif->tdma_slot_length = 0; return (-1); } break; case IEEE80211_IOC_TDMA_BINTERVAL: if ((wif->drivercaps & (0x1 << WlanDriverCaps_tdma)) == 0) { wif->tdma_binterval = 0; return (-1); } break; default: break; } return (0); } static int wlan_config_get_intval(struct wlan_iface *wif, int op) { int val = 0; size_t argsize = 0; if (wlan_config_check(wif, op) < 0) return (0); if (wlan_ioctl(wif->wname, op, &val, NULL, &argsize, 0) < 0) return (-1); wlan_config_set_snmp_intval(wif, op, val); return (0); } static int wlan_config_set_intval(struct wlan_iface *wif, int op, int sval) { size_t argsize = 0; int val; if (wlan_config_check(wif, op) < 0) return (-1); if (wlan_config_snmp2value(op, sval, &val) != SNMP_ERR_NOERROR) return (-1); if (wlan_ioctl(wif->wname, op, &val, NULL, &argsize, 1) < 0) return (-1); wlan_config_set_snmp_intval(wif, op, val); return (0); } int wlan_config_get_ioctl(struct wlan_iface *wif, int which) { int op; switch (which) { case LEAF_wlanIfaceCountryCode: /* FALLTHROUGH */ case LEAF_wlanIfaceRegDomain: return (wlan_config_get_country(wif)); case LEAF_wlanIfaceDesiredSsid: return (wlan_config_get_dssid(wif)); case LEAF_wlanIfaceDesiredChannel: return (wlan_config_get_dchannel(wif)); case LEAF_wlanIfaceDesiredBssid: return (wlan_config_get_bssid(wif)); default: op = wlan_config_snmp2ioctl(which); return (wlan_config_get_intval(wif, op)); } return (-1); } int wlan_config_set_ioctl(struct wlan_iface *wif, int which, int val, char *strval, int len) { int op; switch (which) { case LEAF_wlanIfaceCountryCode: return (wlan_config_set_country(wif, strval, wif->reg_domain)); case LEAF_wlanIfaceRegDomain: return (wlan_config_set_country(wif, wif->country_code, val)); case LEAF_wlanIfaceDesiredSsid: return (wlan_config_set_dssid(wif, strval, len)); case LEAF_wlanIfaceDesiredChannel: return (wlan_config_set_dchannel(wif, val)); case LEAF_wlanIfaceDesiredBssid: return (wlan_config_set_bssid(wif, strval)); default: op = wlan_config_snmp2ioctl(which); return (wlan_config_set_intval(wif, op, val)); } return (-1); } static uint32_t wlan_snmp_to_scan_flags(int flags) { int sr_flags = 0; if ((flags & (0x1 << WlanScanFlagsType_noSelection)) != 0) sr_flags |= IEEE80211_IOC_SCAN_NOPICK; if ((flags & (0x1 << WlanScanFlagsType_activeScan)) != 0) sr_flags |= IEEE80211_IOC_SCAN_ACTIVE; if ((flags & (0x1 << WlanScanFlagsType_pickFirst)) != 0) sr_flags |= IEEE80211_IOC_SCAN_PICK1ST; if ((flags & (0x1 << WlanScanFlagsType_backgroundScan)) != 0) sr_flags |= IEEE80211_IOC_SCAN_BGSCAN; if ((flags & (0x1 << WlanScanFlagsType_once)) != 0) sr_flags |= IEEE80211_IOC_SCAN_ONCE; if ((flags & (0x1 << WlanScanFlagsType_noBroadcast)) != 0) sr_flags |= IEEE80211_IOC_SCAN_NOBCAST; if ((flags & (0x1 << WlanScanFlagsType_noAutoSequencing)) != 0) sr_flags |= IEEE80211_IOC_SCAN_NOJOIN; if ((flags & (0x1 << WlanScanFlagsType_flushCashe)) != 0) sr_flags |= IEEE80211_IOC_SCAN_FLUSH; if ((flags & (0x1 << WlanScanFlagsType_chechCashe)) != 0) sr_flags |= IEEE80211_IOC_SCAN_CHECK; return (sr_flags); } int wlan_set_scan_config(struct wlan_iface *wif) { int val = 0; size_t argsize; struct ieee80211_scan_req sr; memset(&sr, 0, sizeof(sr)); argsize = sizeof(struct ieee80211_scan_req); sr.sr_flags = wlan_snmp_to_scan_flags(wif->scan_flags); sr.sr_flags |= IEEE80211_IOC_SCAN_BGSCAN; sr.sr_duration = wif->scan_duration; sr.sr_mindwell = wif->scan_mindwell; sr.sr_maxdwell = wif->scan_maxdwell; sr.sr_nssid = 0; if (wlan_ioctl(wif->wname, IEEE80211_IOC_SCAN_REQ, &val, &sr, &argsize, 1) < 0) return (-1); wif->scan_status = wlanScanConfigStatus_running; return (0); } static uint32_t wlan_peercaps_to_snmp(uint32_t pcaps) { uint32_t scaps = 0; if ((pcaps & IEEE80211_CAPINFO_ESS) != 0) scaps |= (0x1 << WlanPeerCapabilityFlags_ess); if ((pcaps & IEEE80211_CAPINFO_IBSS) != 0) scaps |= (0x1 << WlanPeerCapabilityFlags_ibss); if ((pcaps & IEEE80211_CAPINFO_CF_POLLABLE) != 0) scaps |= (0x1 << WlanPeerCapabilityFlags_cfPollable); if ((pcaps & IEEE80211_CAPINFO_CF_POLLREQ) != 0) scaps |= (0x1 << WlanPeerCapabilityFlags_cfPollRequest); if ((pcaps & IEEE80211_CAPINFO_PRIVACY) != 0) scaps |= (0x1 << WlanPeerCapabilityFlags_privacy); if ((pcaps & IEEE80211_CAPINFO_SHORT_PREAMBLE) != 0) scaps |= (0x1 << WlanPeerCapabilityFlags_shortPreamble); if ((pcaps & IEEE80211_CAPINFO_PBCC) != 0) scaps |= (0x1 << WlanPeerCapabilityFlags_pbcc); if ((pcaps & IEEE80211_CAPINFO_CHNL_AGILITY) != 0) scaps |= (0x1 << WlanPeerCapabilityFlags_channelAgility); if ((pcaps & IEEE80211_CAPINFO_SHORT_SLOTTIME) != 0) scaps |= (0x1 << WlanPeerCapabilityFlags_shortSlotTime); if ((pcaps & IEEE80211_CAPINFO_RSN) != 0) scaps |= (0x1 << WlanPeerCapabilityFlags_rsn); if ((pcaps & IEEE80211_CAPINFO_DSSSOFDM) != 0) scaps |= (0x1 << WlanPeerCapabilityFlags_dsssofdm); return (scaps); } static int wlan_add_new_scan_result(struct wlan_iface *wif, const struct ieee80211req_scan_result *isr, uint8_t *ssid) { struct wlan_scan_result *sr; if ((sr = wlan_scan_new_result(ssid, isr->isr_bssid)) == NULL) return (-1); sr->opchannel = wlan_channel_flags_to_snmp_phy(isr->isr_flags); sr->rssi = isr->isr_rssi; sr->frequency = isr->isr_freq; sr->noise = isr->isr_noise; sr->bintval = isr->isr_intval; sr->capinfo = wlan_peercaps_to_snmp(isr->isr_capinfo); if (wlan_scan_add_result(wif, sr) < 0) { wlan_scan_free_result(sr); return (-1); } return (0); } int wlan_get_scan_results(struct wlan_iface *wif) { int ssidlen, val = 0; uint8_t buf[24 * 1024]; size_t argsize; const uint8_t *cp, *idp; uint8_t ssid[IEEE80211_NWID_LEN + 1]; struct ieee80211req_scan_result isr; argsize = sizeof(buf); if (wlan_ioctl(wif->wname, IEEE80211_IOC_SCAN_RESULTS, &val, &buf, &argsize, 0) < 0) return (-1); if (argsize < sizeof(struct ieee80211req_scan_result)) return (0); cp = buf; do { memcpy(&isr, cp, sizeof(struct ieee80211req_scan_result)); memset(ssid, 0, IEEE80211_NWID_LEN + 1); if (isr.isr_meshid_len) { idp = cp + isr.isr_ie_off + isr.isr_ssid_len; ssidlen = isr.isr_meshid_len; } else { idp = cp + isr.isr_ie_off; ssidlen = isr.isr_ssid_len; } if (ssidlen > IEEE80211_NWID_LEN) ssidlen = IEEE80211_NWID_LEN; memcpy(ssid, idp, ssidlen); ssid[IEEE80211_NWID_LEN] = '\0'; (void)wlan_add_new_scan_result(wif, &isr, ssid); cp += isr.isr_len; argsize -= isr.isr_len; } while (argsize >= sizeof(struct ieee80211req_scan_result)); return (0); } int wlan_get_stats(struct wlan_iface *wif) { struct ifreq ifr; memset(&ifr, 0, sizeof(struct ifreq)); strlcpy(ifr.ifr_name, wif->wname, IFNAMSIZ); ifr.ifr_data = (caddr_t) &wif->stats; if (ioctl(sock, SIOCG80211STATS, &ifr) < 0) { syslog(LOG_ERR, "iface %s - ioctl(SIOCG80211STATS) failed: %s", wif->wname, strerror(errno)); return (-1); } return (0); } int wlan_get_wepmode(struct wlan_iface *wif) { int val = 0; size_t argsize = 0; if (wlan_ioctl(wif->wname, IEEE80211_IOC_WEP, &val, NULL, &argsize, 0) < 0 || val == IEEE80211_WEP_NOSUP) { wif->wepsupported = 0; /* XXX */ wif->wepmode = wlanWepMode_off; wif->weptxkey = 0; return (-1); } wif->wepsupported = 1; switch (val) { case IEEE80211_WEP_ON: wif->wepmode = wlanWepMode_on; break; case IEEE80211_WEP_MIXED: wif->wepmode = wlanWepMode_mixed; break; case IEEE80211_WEP_OFF: /* FALLTHROUGH */ default: wif->wepmode = wlanWepMode_off; break; } return (0); } int wlan_set_wepmode(struct wlan_iface *wif) { int val; size_t argsize = 0; if (!wif->wepsupported) return (-1); switch (wif->wepmode) { case wlanWepMode_off: val = IEEE80211_WEP_OFF; break; case wlanWepMode_on: val = IEEE80211_WEP_ON; break; case wlanWepMode_mixed: val = IEEE80211_WEP_MIXED; break; default: return (-1); } if (wlan_ioctl(wif->wname, IEEE80211_IOC_WEP, &val, NULL, &argsize, 1) < 0) return (-1); return (0); } int wlan_get_weptxkey(struct wlan_iface *wif) { int val; size_t argsize = 0; if (!wif->wepsupported) return (0); if (wlan_ioctl(wif->wname, IEEE80211_IOC_WEPTXKEY, &val, NULL, &argsize, 0) < 0) return (-1); if (val == IEEE80211_KEYIX_NONE) wif->weptxkey = 0; else wif->weptxkey = val + 1; return (0); } int wlan_set_weptxkey(struct wlan_iface *wif) { int val; size_t argsize = 0; if (!wif->wepsupported) return (0); if (wif->weptxkey >= IEEE80211_WEP_NKID) return (-1); if (wif->weptxkey == 0) val = IEEE80211_KEYIX_NONE; else val = wif->weptxkey - 1; if (wlan_ioctl(wif->wname, IEEE80211_IOC_WEPTXKEY, &val, NULL, &argsize, 1) < 0) return (-1); return (0); } int wlan_get_wepkeys(struct wlan_iface *wif __unused) { /* XXX: should they be visible via SNMP */ return (0); } int wlan_set_wepkeys(struct wlan_iface *wif __unused) { /* XXX: should they be configurable via SNMP */ return (0); } int wlan_get_mac_policy(struct wlan_iface *wif) { int val = IEEE80211_MACCMD_POLICY; size_t argsize = 0; struct ieee80211req ireq; memset(&ireq, 0, sizeof(struct ieee80211req)); strlcpy(ireq.i_name, wif->wname, IFNAMSIZ); ireq.i_type = IEEE80211_IOC_MACCMD; ireq.i_val = IEEE80211_MACCMD_POLICY; if (ioctl(sock, SIOCG80211, &ireq) < 0) { if (errno != EINVAL) { syslog(LOG_ERR, "iface %s - get param: ioctl(%d) " "failed: %s", wif->wname, ireq.i_type, strerror(errno)); wif->macsupported = 0; return (-1); } else { wif->macsupported = 1; wif->mac_policy = wlanMACAccessControlPolicy_open; return (0); } - + } wif->macsupported = 1; switch (val) { case IEEE80211_MACCMD_POLICY_ALLOW: wif->mac_policy = wlanMACAccessControlPolicy_allow; break; case IEEE80211_MACCMD_POLICY_DENY: wif->mac_policy = wlanMACAccessControlPolicy_deny; break; case IEEE80211_MACCMD_POLICY_RADIUS: wif->mac_policy = wlanMACAccessControlPolicy_radius; break; case IEEE80211_MACCMD_POLICY_OPEN: /* FALLTHROUGH */ default: wif->mac_policy = wlanMACAccessControlPolicy_open; break; } argsize = 0; val = IEEE80211_MACCMD_LIST; if (wlan_ioctl(wif->wname, IEEE80211_IOC_MACCMD, &val, NULL, &argsize, 0) < 0) return (-1); wif->mac_nacls = argsize / sizeof(struct ieee80211req_maclist *); return (0); } int wlan_set_mac_policy(struct wlan_iface *wif) { int val; size_t argsize = 0; if (!wif->macsupported) return (-1); switch (wif->mac_policy) { case wlanMACAccessControlPolicy_allow: val = IEEE80211_MACCMD_POLICY_ALLOW; break; case wlanMACAccessControlPolicy_deny: val = IEEE80211_MACCMD_POLICY_DENY; break; case wlanMACAccessControlPolicy_radius: val = IEEE80211_MACCMD_POLICY_RADIUS; break; case wlanMACAccessControlPolicy_open: val = IEEE80211_MACCMD_POLICY_OPEN; break; default: return (-1); } if (wlan_ioctl(wif->wname, IEEE80211_IOC_MACCMD, &val, NULL, &argsize, 1) < 0) return (-1); return (0); } int wlan_flush_mac_mac(struct wlan_iface *wif) { int val = IEEE80211_MACCMD_FLUSH; size_t argsize = 0; if (wlan_ioctl(wif->wname, IEEE80211_IOC_MACCMD, &val, NULL, &argsize, 1) < 0) return (-1); return (0); } static int wlan_add_mac_macinfo(struct wlan_iface *wif, const struct ieee80211req_maclist *ml) { struct wlan_mac_mac *mmac; if ((mmac = wlan_mac_new_mac(ml->ml_macaddr)) == NULL) return (-1); mmac->mac_status = RowStatus_active; if (wlan_mac_add_mac(wif, mmac) < 0) { wlan_mac_free_mac(mmac); return (-1); } return (0); } int wlan_get_mac_acl_macs(struct wlan_iface *wif) { int i, nacls, val = IEEE80211_MACCMD_LIST; size_t argsize = 0; uint8_t *data; struct ieee80211req ireq; const struct ieee80211req_maclist *acllist; if (wif->mac_policy == wlanMACAccessControlPolicy_radius) { wif->mac_nacls = 0; return (0); } memset(&ireq, 0, sizeof(struct ieee80211req)); strlcpy(ireq.i_name, wif->wname, IFNAMSIZ); ireq.i_type = IEEE80211_IOC_MACCMD; ireq.i_val = IEEE80211_MACCMD_LIST; if (ioctl(sock, SIOCG80211, &ireq) < 0) { if (errno != EINVAL) { syslog(LOG_ERR, "iface %s - get param: ioctl(%d) " "failed: %s", wif->wname, ireq.i_type, strerror(errno)); wif->macsupported = 0; return (-1); } } if (argsize == 0) { wif->mac_nacls = 0; return (0); } if ((data = (uint8_t *)malloc(argsize)) == NULL) return (-1); if (wlan_ioctl(wif->wname, IEEE80211_IOC_MACCMD, &val, data, &argsize, 0) < 0) return (-1); nacls = argsize / sizeof(*acllist); acllist = (struct ieee80211req_maclist *) data; for (i = 0; i < nacls; i++) (void)wlan_add_mac_macinfo(wif, acllist + i); wif->mac_nacls = nacls; return (0); } int wlan_add_mac_acl_mac(struct wlan_iface *wif, struct wlan_mac_mac *mmac) { int val = 0; size_t argsize = IEEE80211_ADDR_LEN; struct ieee80211req_mlme mlme; if (wlan_ioctl(wif->wname, IEEE80211_IOC_ADDMAC, &val, mmac->mac, &argsize, 1) < 0) return (-1); mmac->mac_status = RowStatus_active; /* If policy is deny, try to kick the station just in case. */ if (wif->mac_policy != wlanMACAccessControlPolicy_deny) return (0); memset(&mlme, 0, sizeof(mlme)); mlme.im_op = IEEE80211_MLME_DEAUTH; mlme.im_reason = IEEE80211_REASON_AUTH_EXPIRE; memcpy(mlme.im_macaddr, mmac->mac, IEEE80211_ADDR_LEN); argsize = sizeof(struct ieee80211req_mlme); if (wlan_ioctl(wif->wname, IEEE80211_IOC_MLME, &val, &mlme, &argsize, 1) < 0 && errno != ENOENT) return (-1); return (0); } int wlan_del_mac_acl_mac(struct wlan_iface *wif, struct wlan_mac_mac *mmac) { int val = 0; size_t argsize = IEEE80211_ADDR_LEN; struct ieee80211req_mlme mlme; if (wlan_ioctl(wif->wname, IEEE80211_IOC_DELMAC, &val, mmac->mac, &argsize, 1) < 0) return (-1); mmac->mac_status = RowStatus_active; /* If policy is allow, try to kick the station just in case. */ if (wif->mac_policy != wlanMACAccessControlPolicy_allow) return (0); memset(&mlme, 0, sizeof(mlme)); mlme.im_op = IEEE80211_MLME_DEAUTH; mlme.im_reason = IEEE80211_REASON_AUTH_EXPIRE; memcpy(mlme.im_macaddr, mmac->mac, IEEE80211_ADDR_LEN); argsize = sizeof(struct ieee80211req_mlme); if (wlan_ioctl(wif->wname, IEEE80211_IOC_MLME, &val, &mlme, &argsize, 1) < 0 && errno != ENOENT) return (-1); return (0); } int wlan_peer_set_vlan(struct wlan_iface *wif, struct wlan_peer *wip, int vlan) { int val = 0; size_t argsize; struct ieee80211req_sta_vlan vreq; memcpy(vreq.sv_macaddr, wip->pmac, IEEE80211_ADDR_LEN); vreq.sv_vlan = vlan; argsize = sizeof(struct ieee80211req_sta_vlan); if (wlan_ioctl(wif->wname, IEEE80211_IOC_STA_VLAN, &val, &vreq, &argsize, 1) < 0) return (-1); wip->vlan = vlan; return (0); } /* XXX */ #ifndef IEEE80211_NODE_AUTH #define IEEE80211_NODE_AUTH 0x000001 /* authorized for data */ #define IEEE80211_NODE_QOS 0x000002 /* QoS enabled */ #define IEEE80211_NODE_ERP 0x000004 /* ERP enabled */ #define IEEE80211_NODE_PWR_MGT 0x000010 /* power save mode enabled */ #define IEEE80211_NODE_AREF 0x000020 /* authentication ref held */ #define IEEE80211_NODE_HT 0x000040 /* HT enabled */ #define IEEE80211_NODE_HTCOMPAT 0x000080 /* HT setup w/ vendor OUI's */ #define IEEE80211_NODE_WPS 0x000100 /* WPS association */ #define IEEE80211_NODE_TSN 0x000200 /* TSN association */ #define IEEE80211_NODE_AMPDU_RX 0x000400 /* AMPDU rx enabled */ #define IEEE80211_NODE_AMPDU_TX 0x000800 /* AMPDU tx enabled */ #define IEEE80211_NODE_MIMO_PS 0x001000 /* MIMO power save enabled */ #define IEEE80211_NODE_MIMO_RTS 0x002000 /* send RTS in MIMO PS */ #define IEEE80211_NODE_RIFS 0x004000 /* RIFS enabled */ #define IEEE80211_NODE_SGI20 0x008000 /* Short GI in HT20 enabled */ #define IEEE80211_NODE_SGI40 0x010000 /* Short GI in HT40 enabled */ #define IEEE80211_NODE_ASSOCID 0x020000 /* xmit requires associd */ #define IEEE80211_NODE_AMSDU_RX 0x040000 /* AMSDU rx enabled */ #define IEEE80211_NODE_AMSDU_TX 0x080000 /* AMSDU tx enabled */ #endif static uint32_t wlan_peerstate_to_snmp(uint32_t pstate) { uint32_t sstate = 0; if ((pstate & IEEE80211_NODE_AUTH) != 0) sstate |= (0x1 << WlanIfacePeerFlagsType_authorizedForData); if ((pstate & IEEE80211_NODE_QOS) != 0) sstate |= (0x1 << WlanIfacePeerFlagsType_qosEnabled); if ((pstate & IEEE80211_NODE_ERP) != 0) sstate |= (0x1 << WlanIfacePeerFlagsType_erpEnabled); if ((pstate & IEEE80211_NODE_PWR_MGT) != 0) sstate |= (0x1 << WlanIfacePeerFlagsType_powerSaveMode); if ((pstate & IEEE80211_NODE_AREF) != 0) sstate |= (0x1 << WlanIfacePeerFlagsType_authRefHeld); if ((pstate & IEEE80211_NODE_HT) != 0) sstate |= (0x1 << WlanIfacePeerFlagsType_htEnabled); if ((pstate & IEEE80211_NODE_HTCOMPAT) != 0) sstate |= (0x1 << WlanIfacePeerFlagsType_htCompat); if ((pstate & IEEE80211_NODE_WPS) != 0) sstate |= (0x1 << WlanIfacePeerFlagsType_wpsAssoc); if ((pstate & IEEE80211_NODE_TSN) != 0) sstate |= (0x1 << WlanIfacePeerFlagsType_tsnAssoc); if ((pstate & IEEE80211_NODE_AMPDU_RX) != 0) sstate |= (0x1 << WlanIfacePeerFlagsType_ampduRx); if ((pstate & IEEE80211_NODE_AMPDU_TX) != 0) sstate |= (0x1 << WlanIfacePeerFlagsType_ampduTx); if ((pstate & IEEE80211_NODE_MIMO_PS) != 0) sstate |= (0x1 << WlanIfacePeerFlagsType_mimoPowerSave); if ((pstate & IEEE80211_NODE_MIMO_RTS) != 0) sstate |= (0x1 << WlanIfacePeerFlagsType_sendRts); if ((pstate & IEEE80211_NODE_RIFS) != 0) sstate |= (0x1 << WlanIfacePeerFlagsType_rifs); if ((pstate & IEEE80211_NODE_SGI20) != 0) sstate |= (0x1 << WlanIfacePeerFlagsType_shortGiHT20); if ((pstate & IEEE80211_NODE_SGI40) != 0) sstate |= (0x1 << WlanIfacePeerFlagsType_shortGiHT40); if ((pstate & IEEE80211_NODE_AMSDU_RX) != 0) sstate |= (0x1 << WlanIfacePeerFlagsType_amsduRx); if ((pstate & IEEE80211_NODE_AMSDU_TX) != 0) sstate |= (0x1 << WlanIfacePeerFlagsType_amsduTx); return (sstate); } static struct wlan_peer * wlan_add_peerinfo(const struct ieee80211req_sta_info *si) { struct wlan_peer *wip; if ((wip = wlan_new_peer(si->isi_macaddr))== NULL) return (NULL); wip->associd = IEEE80211_AID(si->isi_associd); wip->vlan = si->isi_vlan; wip->frequency = si->isi_freq; wip->fflags = si->isi_flags; wip->txrate = si->isi_txrate; wip->rssi = si->isi_rssi; wip->idle = si->isi_inact; wip->txseqs = si->isi_txseqs[0]; /* XXX */ wip->rxseqs = si->isi_rxseqs[0]; /* XXX */ wip->txpower = si->isi_txpower; wip->capinfo = wlan_peercaps_to_snmp(si->isi_capinfo); wip->state = wlan_peerstate_to_snmp(si->isi_state); wip->local_id = si->isi_localid; wip->peer_id = si->isi_peerid; return (wip); } int wlan_get_peerinfo(struct wlan_iface *wif) { union { struct ieee80211req_sta_req req; uint8_t buf[24 * 1024]; } u; const uint8_t *cp; int val = 0; size_t len; struct ieee80211req_sta_info si; struct wlan_peer *wip; /* Get all stations - broadcast address */ (void) memset(u.req.is_u.macaddr, 0xff, IEEE80211_ADDR_LEN); len = sizeof(u); if (wlan_ioctl(wif->wname, IEEE80211_IOC_STA_INFO, & val, &u, &len, 0) < 0) return (-1); if (len < sizeof(struct ieee80211req_sta_info)) return (-1); cp = (const uint8_t *) u.req.info; do { memcpy(&si, cp, sizeof(struct ieee80211req_sta_info)); if ((wip = wlan_add_peerinfo(&si)) != NULL && wlan_add_peer(wif, wip) < 0) wlan_free_peer(wip); cp += si.isi_len, len -= si.isi_len; } while (len >= sizeof(struct ieee80211req_sta_info)); return (0); } /************************************************************************ * Wireless MESH & HWMP sysctl config. */ const char wlan_sysctl_name[] = "net.wlan."; static const char *wlan_sysctl[] = { "mesh.retrytimeout", "mesh.holdingtimeout", "mesh.confirmtimeout", "mesh.maxretries", "hwmp.targetonly", "hwmp.replyforward", "hwmp.pathlifetime", "hwmp.roottimeout", "hwmp.rootint", "hwmp.rannint", "hwmp.inact", }; int32_t wlan_do_sysctl(struct wlan_config *cfg, enum wlan_syscl which, int set) { char mib_name[100]; int val, sval; size_t len, vlen; if (set) { vlen = sizeof(sval); switch (which) { case WLAN_MESH_RETRY_TO: sval = cfg->mesh_retryto; break; case WLAN_MESH_HOLDING_TO: sval = cfg->mesh_holdingto; break; case WLAN_MESH_CONFIRM_TO: sval = cfg->mesh_confirmto; break; case WLAN_MESH_MAX_RETRIES: sval = cfg->mesh_maxretries; break; case WLAN_HWMP_TARGET_ONLY: sval = cfg->hwmp_targetonly; break; case WLAN_HWMP_REPLY_FORWARD: sval = cfg->hwmp_replyforward; break; case WLAN_HWMP_PATH_LIFETIME: sval = cfg->hwmp_pathlifetime; break; case WLAN_HWMP_ROOT_TO: sval = cfg->hwmp_roottimeout; break; case WLAN_HWMP_ROOT_INT: sval = cfg->hwmp_rootint; break; case WLAN_HWMP_RANN_INT: sval = cfg->hwmp_rannint; break; case WLAN_HWMP_INACTIVITY_TO: sval = cfg->hwmp_inact; break; default: return (-1); } } else { if (which >= WLAN_SYSCTL_MAX) return (-1); vlen = 0; } strlcpy(mib_name, wlan_sysctl_name, sizeof(mib_name)); strlcat(mib_name, wlan_sysctl[which], sizeof(mib_name)); len = sizeof (val); if (sysctlbyname(mib_name, &val, &len, (set? &sval : NULL), vlen) < 0) { syslog(LOG_ERR, "sysctl(%s) failed - %s", mib_name, strerror(errno)); return (-1); } switch (which) { case WLAN_MESH_RETRY_TO: cfg->mesh_retryto = val; break; case WLAN_MESH_HOLDING_TO: cfg->mesh_holdingto = val; break; case WLAN_MESH_CONFIRM_TO: cfg->mesh_confirmto = val; break; case WLAN_MESH_MAX_RETRIES: cfg->mesh_maxretries = val; break; case WLAN_HWMP_TARGET_ONLY: cfg->hwmp_targetonly = val; break; case WLAN_HWMP_REPLY_FORWARD: cfg->hwmp_replyforward = val; break; case WLAN_HWMP_PATH_LIFETIME: cfg->hwmp_pathlifetime = val; break; case WLAN_HWMP_ROOT_TO: cfg->hwmp_roottimeout = val; break; case WLAN_HWMP_ROOT_INT: cfg->hwmp_rootint = val; break; case WLAN_HWMP_RANN_INT: cfg->hwmp_rannint = val; break; case WLAN_HWMP_INACTIVITY_TO: cfg->hwmp_inact = val; break; default: /* NOTREACHED */ abort(); } return (0); } int wlan_mesh_config_get(struct wlan_iface *wif, int which) { int op, val = 0; size_t argsize = 0; uint8_t data[32], *pd = NULL; switch (which) { case LEAF_wlanMeshTTL: op = IEEE80211_IOC_MESH_TTL; break; case LEAF_wlanMeshPeeringEnabled: op = IEEE80211_IOC_MESH_AP; break; case LEAF_wlanMeshForwardingEnabled: op = IEEE80211_IOC_MESH_FWRD; break; case LEAF_wlanMeshMetric: op = IEEE80211_IOC_MESH_PR_METRIC; pd = data; argsize = sizeof(data); break; case LEAF_wlanMeshPath: op = IEEE80211_IOC_MESH_PR_PATH; pd = data; argsize = sizeof(data); break; case LEAF_wlanMeshRoutesFlush: return (0); default: return (-1); } if (wlan_ioctl(wif->wname, op, &val, pd, &argsize, 0) < 0) return (-1); switch (which) { case LEAF_wlanMeshTTL: wif->mesh_ttl = val; break; case LEAF_wlanMeshPeeringEnabled: if (val) wif->mesh_peering = wlanMeshPeeringEnabled_true; else wif->mesh_peering = wlanMeshPeeringEnabled_false; break; case LEAF_wlanMeshForwardingEnabled: if (val) wif->mesh_forwarding = wlanMeshForwardingEnabled_true; else wif->mesh_forwarding = wlanMeshForwardingEnabled_false; break; case LEAF_wlanMeshMetric: data[argsize] = '\0'; if (strcmp(data, "AIRTIME") == 0) wif->mesh_metric = wlanMeshMetric_airtime; else wif->mesh_metric = wlanMeshMetric_unknown; break; case LEAF_wlanMeshPath: data[argsize] = '\0'; if (strcmp(data, "HWMP") == 0) wif->mesh_path = wlanMeshPath_hwmp; else wif->mesh_path = wlanMeshPath_unknown; } return (0); } int wlan_mesh_config_set(struct wlan_iface *wif, int which) { int op, val = 0; size_t argsize = 0; uint8_t data[32], *pd = NULL; switch (which) { case LEAF_wlanMeshTTL: op = IEEE80211_IOC_MESH_TTL; val = wif->mesh_ttl; break; case LEAF_wlanMeshPeeringEnabled: op = IEEE80211_IOC_MESH_AP; if (wif->mesh_peering == wlanMeshPeeringEnabled_true) val = 1; break; case LEAF_wlanMeshForwardingEnabled: if (wif->mesh_forwarding == wlanMeshForwardingEnabled_true) val = 1; op = IEEE80211_IOC_MESH_FWRD; break; case LEAF_wlanMeshMetric: op = IEEE80211_IOC_MESH_PR_METRIC; if (wif->mesh_metric == wlanMeshMetric_airtime) strcpy(data, "AIRTIME"); else return (-1); pd = data; argsize = sizeof(data); break; case LEAF_wlanMeshPath: op = IEEE80211_IOC_MESH_PR_PATH; if (wif->mesh_path == wlanMeshPath_hwmp) strcpy(data, "HWMP"); else return (-1); pd = data; argsize = sizeof(data); break; default: return (-1); } if (wlan_ioctl(wif->wname, op, &val, pd, &argsize, 1) < 0) return (-1); return(0); } int wlan_mesh_flush_routes(struct wlan_iface *wif) { int val = IEEE80211_MESH_RTCMD_FLUSH; size_t argsize = 0; if (wlan_ioctl(wif->wname, IEEE80211_IOC_MESH_RTCMD, &val, NULL, &argsize, 1) < 0) return (-1); return (0); } int wlan_mesh_add_route(struct wlan_iface *wif, struct wlan_mesh_route *wmr) { int val = IEEE80211_MESH_RTCMD_ADD; size_t argsize = IEEE80211_ADDR_LEN; if (wlan_ioctl(wif->wname, IEEE80211_IOC_MESH_RTCMD, &val, wmr->imroute.imr_dest, &argsize, 1) < 0) return (-1); wmr->mroute_status = RowStatus_active; return (0); } int wlan_mesh_del_route(struct wlan_iface *wif, struct wlan_mesh_route *wmr) { int val = IEEE80211_MESH_RTCMD_DELETE; size_t argsize = IEEE80211_ADDR_LEN; if (wlan_ioctl(wif->wname, IEEE80211_IOC_MESH_RTCMD, &val, wmr->imroute.imr_dest, &argsize, 1) < 0) return (-1); wmr->mroute_status = RowStatus_destroy; return (0); } int wlan_mesh_get_routelist(struct wlan_iface *wif) { int i, nroutes, val = IEEE80211_MESH_RTCMD_LIST; size_t argsize; struct ieee80211req_mesh_route routes[128]; struct ieee80211req_mesh_route *rt; struct wlan_mesh_route *wmr; argsize = sizeof(routes); if (wlan_ioctl(wif->wname, IEEE80211_IOC_MESH_RTCMD, &val, routes, &argsize, 0) < 0) /* XXX: ENOMEM? */ return (-1); nroutes = argsize / sizeof(*rt); for (i = 0; i < nroutes; i++) { rt = routes + i; if ((wmr = wlan_mesh_new_route(rt->imr_dest)) == NULL) return (-1); memcpy(&wmr->imroute, rt, sizeof(*rt)); wmr->mroute_status = RowStatus_active; if (wlan_mesh_add_rtentry(wif, wmr) < 0) wlan_mesh_free_route(wmr); } return (0); } int wlan_hwmp_config_get(struct wlan_iface *wif, int which) { int op, val = 0; size_t argsize = 0; switch (which) { case LEAF_wlanHWMPRootMode: op = IEEE80211_IOC_HWMP_ROOTMODE; break; case LEAF_wlanHWMPMaxHops: op = IEEE80211_IOC_HWMP_MAXHOPS; break; default: return (-1); } if (wlan_ioctl(wif->wname, op, &val, NULL, &argsize, 0) < 0) return (-1); switch (which) { case LEAF_wlanHWMPRootMode: switch (val) { case IEEE80211_HWMP_ROOTMODE_NORMAL: wif->hwmp_root_mode = wlanHWMPRootMode_normal; break; case IEEE80211_HWMP_ROOTMODE_PROACTIVE: wif->hwmp_root_mode = wlanHWMPRootMode_proactive; break; case IEEE80211_HWMP_ROOTMODE_RANN: wif->hwmp_root_mode = wlanHWMPRootMode_rann; break; case IEEE80211_HWMP_ROOTMODE_DISABLED: default: wif->hwmp_root_mode = wlanHWMPRootMode_disabled; break; } break; case LEAF_wlanHWMPMaxHops: wif->hwmp_max_hops = val; break; } return (0); } int wlan_hwmp_config_set(struct wlan_iface *wif, int which) { int op, val = 0; size_t argsize = 0; switch (which) { case LEAF_wlanHWMPRootMode: op = IEEE80211_IOC_HWMP_ROOTMODE; switch (wif->hwmp_root_mode) { case wlanHWMPRootMode_disabled: val = IEEE80211_HWMP_ROOTMODE_DISABLED; break; case wlanHWMPRootMode_normal: val = IEEE80211_HWMP_ROOTMODE_NORMAL; break; case wlanHWMPRootMode_proactive: val = IEEE80211_HWMP_ROOTMODE_PROACTIVE; break; case wlanHWMPRootMode_rann: val = IEEE80211_HWMP_ROOTMODE_RANN; break; default: return (-1); } break; case LEAF_wlanHWMPMaxHops: op = IEEE80211_IOC_HWMP_MAXHOPS; val = wif->hwmp_max_hops; break; default: return (-1); } if (wlan_ioctl(wif->wname, op, &val, NULL, &argsize, 1) < 0) return (-1); return (0); } Index: head/usr.sbin/bsnmpd/tools/bsnmptools/bsnmpget.c =================================================================== --- head/usr.sbin/bsnmpd/tools/bsnmptools/bsnmpget.c (revision 310647) +++ head/usr.sbin/bsnmpd/tools/bsnmptools/bsnmpget.c (revision 310648) @@ -1,1288 +1,1288 @@ /*- * Copyright (c) 2005-2006 The FreeBSD Project * All rights reserved. * * Author: Shteryana Shopova * * 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 IS PROVIDED BY THE 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 THE 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. * * Bsnmpget and bsnmpwalk are simple tools for querying SNMP agents, * bsnmpset can be used to set MIB objects in an agent. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "bsnmptc.h" #include "bsnmptools.h" static const char *program_name = NULL; static enum program_e { BSNMPGET, BSNMPWALK, BSNMPSET } program; /* ***************************************************************************** * Common bsnmptools functions. */ static void usage(void) { fprintf(stderr, "Usage:\n" "%s %s [-A options] [-b buffersize] [-C options] [-I options]\n" "\t[-i filelist] [-l filename]%s [-o output] [-P options]\n" "\t%s[-r retries] [-s [trans::][community@][server][:port]]\n" "\t[-t timeout] [-U options] [-v version]%s\n", program_name, (program == BSNMPGET) ? "[-aDdehnK]" : (program == BSNMPWALK) ? "[-dhnK]" : (program == BSNMPSET) ? "[-adehnK]" : "", (program == BSNMPGET || program == BSNMPWALK) ? " [-M max-repetitions] [-N non-repeaters]" : "", (program == BSNMPGET || program == BSNMPWALK) ? "[-p pdu] " : "", (program == BSNMPGET) ? " OID [OID ...]" : (program == BSNMPWALK || program == BSNMPSET) ? " [OID ...]" : "" ); } static int32_t parse_max_repetitions(struct snmp_toolinfo *snmptoolctx, char *opt_arg) { uint32_t v; assert(opt_arg != NULL); v = strtoul(opt_arg, (void *) NULL, 10); if (v > SNMP_MAX_BINDINGS) { warnx("Max repetitions value greater than %d maximum allowed.", SNMP_MAX_BINDINGS); return (-1); } SET_MAXREP(snmptoolctx, v); return (2); } static int32_t parse_non_repeaters(struct snmp_toolinfo *snmptoolctx, char *opt_arg) { uint32_t v; assert(opt_arg != NULL); v = strtoul(opt_arg, (void *) NULL, 10); if (v > SNMP_MAX_BINDINGS) { warnx("Non repeaters value greater than %d maximum allowed.", SNMP_MAX_BINDINGS); return (-1); } SET_NONREP(snmptoolctx, v); return (2); } static int32_t parse_pdu_type(struct snmp_toolinfo *snmptoolctx, char *opt_arg) { assert(opt_arg != NULL); if (strcasecmp(opt_arg, "getbulk") == 0) SET_PDUTYPE(snmptoolctx, SNMP_PDU_GETBULK); else if (strcasecmp(opt_arg, "getnext") == 0) SET_PDUTYPE(snmptoolctx, SNMP_PDU_GETNEXT); else if (strcasecmp(opt_arg, "get") == 0) SET_PDUTYPE(snmptoolctx, SNMP_PDU_GET); else { warnx("PDU type '%s' not supported.", opt_arg); return (-1); } return (2); } static int32_t snmptool_parse_options(struct snmp_toolinfo *snmptoolctx, int argc, char **argv) { int32_t count, optnum = 0; int ch; const char *opts; switch (program) { case BSNMPWALK: opts = "dhnKA:b:C:I:i:l:M:N:o:P:p:r:s:t:U:v:"; break; case BSNMPGET: opts = "aDdehnKA:b:C:I:i:l:M:N:o:P:p:r:s:t:U:v:"; break; case BSNMPSET: opts = "adehnKA:b:C:I:i:l:o:P:r:s:t:U:v:"; break; default: return (-1); } while ((ch = getopt(argc, argv, opts)) != EOF) { switch (ch) { case 'A': count = parse_authentication(snmptoolctx, optarg); break; case 'a': count = parse_skip_access(snmptoolctx); break; case 'b': count = parse_buflen(optarg); break; case 'D': count = parse_discovery(snmptoolctx); break; case 'd': count = parse_debug(); break; case 'e': count = parse_errors(snmptoolctx); break; case 'h': usage(); return (-2); case 'C': count = parse_context(snmptoolctx, optarg); break; case 'I': count = parse_include(snmptoolctx, optarg); break; case 'i': count = parse_file(snmptoolctx, optarg); break; case 'K': count = parse_local_key(snmptoolctx); break; case 'l': count = parse_local_path(optarg); break; case 'M': count = parse_max_repetitions(snmptoolctx, optarg); break; case 'N': count = parse_non_repeaters(snmptoolctx, optarg); break; case 'n': count = parse_num_oids(snmptoolctx); break; case 'o': count = parse_output(snmptoolctx, optarg); break; case 'P': count = parse_privacy(snmptoolctx, optarg); break; case 'p': count = parse_pdu_type(snmptoolctx, optarg); break; case 'r': count = parse_retry(optarg); break; case 's': count = parse_server(optarg); break; case 't': count = parse_timeout(optarg); break; case 'U': count = parse_user_security(snmptoolctx, optarg); break; case 'v': count = parse_version(optarg); break; case '?': default: usage(); return (-1); } if (count < 0) return (-1); optnum += count; } return (optnum); } /* * Read user input OID - one of following formats: * 1) 1.2.1.1.2.1.0 - that is if option numeric was given; * 2) string - in such case append .0 to the asn_oid subs; * 3) string.1 - no additional processing required in such case. */ static char * snmptools_parse_stroid(struct snmp_toolinfo *snmptoolctx, struct snmp_object *obj, char *argv) { char string[MAXSTR], *str; int32_t i = 0; struct asn_oid in_oid; str = argv; if (*str == '.') str++; while (isalpha(*str) || *str == '_' || (i != 0 && isdigit(*str))) { str++; i++; } if (i <= 0 || i >= MAXSTR) return (NULL); memset(&in_oid, 0, sizeof(struct asn_oid)); if ((str = snmp_parse_suboid((argv + i), &in_oid)) == NULL) { warnx("Invalid OID - %s", argv); return (NULL); } strlcpy(string, argv, i + 1); if (snmp_lookup_oidall(snmptoolctx, obj, string) < 0) { warnx("No entry for %s in mapping lists", string); return (NULL); } /* If OID given on command line append it. */ if (in_oid.len > 0) asn_append_oid(&(obj->val.var), &in_oid); else if (*str == '[') { if ((str = snmp_parse_index(snmptoolctx, str + 1, obj)) == NULL) return (NULL); } else if (obj->val.syntax > 0 && GET_PDUTYPE(snmptoolctx) == SNMP_PDU_GET) { if (snmp_suboid_append(&(obj->val.var), (asn_subid_t) 0) < 0) return (NULL); } return (str); } static int32_t snmptools_parse_oid(struct snmp_toolinfo *snmptoolctx, struct snmp_object *obj, char *argv) { if (argv == NULL) return (-1); if (ISSET_NUMERIC(snmptoolctx)) { if (snmp_parse_numoid(argv, &(obj->val.var)) < 0) return (-1); } else { if (snmptools_parse_stroid(snmptoolctx, obj, argv) == NULL && snmp_parse_numoid(argv, &(obj->val.var)) < 0) return (-1); } return (1); } static int32_t snmptool_add_vbind(struct snmp_pdu *pdu, struct snmp_object *obj) { if (obj->error > 0) return (0); asn_append_oid(&(pdu->bindings[pdu->nbindings].var), &(obj->val.var)); pdu->nbindings++; return (pdu->nbindings); } /* ***************************************************************************** * bsnmpget private functions. */ static int32_t snmpget_verify_vbind(struct snmp_toolinfo *snmptoolctx, struct snmp_pdu *pdu, struct snmp_object *obj) { if (pdu->version == SNMP_V1 && obj->val.syntax == SNMP_SYNTAX_COUNTER64) { warnx("64-bit counters are not supported in SNMPv1 PDU"); return (-1); } if (ISSET_NUMERIC(snmptoolctx) || pdu->type == SNMP_PDU_GETNEXT || pdu->type == SNMP_PDU_GETBULK) return (1); if (pdu->type == SNMP_PDU_GET && obj->val.syntax == SNMP_SYNTAX_NULL) { warnx("Only leaf object values can be added to GET PDU"); return (-1); } return (1); } /* * In case of a getbulk PDU, the error_status and error_index fields are used by * libbsnmp to hold the values of the non-repeaters and max-repetitions fields * that are present only in the getbulk - so before sending the PDU make sure * these have correct values as well. */ static void snmpget_fix_getbulk(struct snmp_pdu *pdu, uint32_t max_rep, uint32_t non_rep) { assert(pdu != NULL); if (pdu->nbindings < non_rep) pdu->error_status = pdu->nbindings; else pdu->error_status = non_rep; if (max_rep > 0) pdu->error_index = max_rep; else pdu->error_index = 1; } static int snmptool_get(struct snmp_toolinfo *snmptoolctx) { struct snmp_pdu req, resp; snmp_pdu_create(&req, GET_PDUTYPE(snmptoolctx)); while ((snmp_pdu_add_bindings(snmptoolctx, snmpget_verify_vbind, snmptool_add_vbind, &req, SNMP_MAX_BINDINGS)) > 0) { if (GET_PDUTYPE(snmptoolctx) == SNMP_PDU_GETBULK) snmpget_fix_getbulk(&req, GET_MAXREP(snmptoolctx), GET_NONREP(snmptoolctx)); if (snmp_dialog(&req, &resp) == -1) { warn("Snmp dialog"); break; } if (snmp_parse_resp(&resp, &req) >= 0) { snmp_output_resp(snmptoolctx, &resp, NULL); break; } snmp_output_err_resp(snmptoolctx, &resp); if (GET_PDUTYPE(snmptoolctx) == SNMP_PDU_GETBULK || !ISSET_RETRY(snmptoolctx)) break; /* * Loop through the object list and set object->error to the * varbinding that caused the error. */ if (snmp_object_seterror(snmptoolctx, &(resp.bindings[resp.error_index - 1]), resp.error_status) <= 0) break; fprintf(stderr, "Retrying...\n"); snmp_pdu_free(&resp); snmp_pdu_create(&req, GET_PDUTYPE(snmptoolctx)); } snmp_pdu_free(&resp); return (0); } /* ***************************************************************************** * bsnmpwalk private functions. */ /* The default tree to walk. */ static const struct asn_oid snmp_mibII_OID = { 6 , { 1, 3, 6, 1, 2, 1 } }; static int32_t snmpwalk_add_default(struct snmp_toolinfo *snmptoolctx __unused, struct snmp_object *obj, char *string __unused) { asn_append_oid(&(obj->val.var), &snmp_mibII_OID); return (1); } /* * Prepare the next GetNext/Get PDU to send. */ static void snmpwalk_nextpdu_create(uint32_t op, struct asn_oid *var, struct snmp_pdu *pdu) { snmp_pdu_create(pdu, op); asn_append_oid(&(pdu->bindings[0].var), var); pdu->nbindings = 1; } static int snmptool_walk(struct snmp_toolinfo *snmptoolctx) { struct snmp_pdu req, resp; struct asn_oid root; /* Keep the initial oid. */ int32_t outputs, rc; uint32_t op; if (GET_PDUTYPE(snmptoolctx) == SNMP_PDU_GETBULK) op = SNMP_PDU_GETBULK; else op = SNMP_PDU_GETNEXT; snmp_pdu_create(&req, op); while ((rc = snmp_pdu_add_bindings(snmptoolctx, NULL, snmptool_add_vbind, &req, 1)) > 0) { /* Remember the root where the walk started from. */ memset(&root, 0, sizeof(struct asn_oid)); asn_append_oid(&root, &(req.bindings[0].var)); if (op == SNMP_PDU_GETBULK) snmpget_fix_getbulk(&req, GET_MAXREP(snmptoolctx), GET_NONREP(snmptoolctx)); outputs = 0; while (snmp_dialog(&req, &resp) >= 0) { if ((snmp_parse_resp(&resp, &req)) < 0) { snmp_output_err_resp(snmptoolctx, &resp); snmp_pdu_free(&resp); outputs = -1; break; } rc = snmp_output_resp(snmptoolctx, &resp, &root); if (rc < 0) { snmp_pdu_free(&resp); outputs = -1; break; } outputs += rc; snmp_pdu_free(&resp); if ((u_int)rc < resp.nbindings) break; snmpwalk_nextpdu_create(op, &(resp.bindings[resp.nbindings - 1].var), &req); if (op == SNMP_PDU_GETBULK) snmpget_fix_getbulk(&req, GET_MAXREP(snmptoolctx), GET_NONREP(snmptoolctx)); } /* Just in case our root was a leaf. */ if (outputs == 0) { snmpwalk_nextpdu_create(SNMP_PDU_GET, &root, &req); if (snmp_dialog(&req, &resp) == SNMP_CODE_OK) { if (snmp_parse_resp(&resp,&req) < 0) snmp_output_err_resp(snmptoolctx, &resp); else snmp_output_resp(snmptoolctx, &(resp), NULL); snmp_pdu_free(&resp); } else warn("Snmp dialog"); } if (snmp_object_remove(snmptoolctx, &root) < 0) { warnx("snmp_object_remove"); break; } snmp_pdu_create(&req, op); } if (rc == 0) return (0); else return (1); } /* ***************************************************************************** * bsnmpset private functions. */ static int32_t parse_oid_numeric(struct snmp_value *value, char *val) { char *endptr; int32_t saved_errno; asn_subid_t suboid; do { saved_errno = errno; errno = 0; suboid = strtoul(val, &endptr, 10); if (errno != 0) { warn("Value %s not supported", val); errno = saved_errno; return (-1); } errno = saved_errno; if ((asn_subid_t) suboid > ASN_MAXID) { warnx("Suboid %u > ASN_MAXID", suboid); return (-1); } if (snmp_suboid_append(&(value->v.oid), suboid) < 0) return (-1); val = endptr + 1; } while (*endptr == '.'); if (*endptr != '\0') warnx("OID value %s not supported", val); value->syntax = SNMP_SYNTAX_OID; return (0); } /* * Allow OID leaf in both forms: * 1) 1.3.6.1.2... -> in such case call directly the function reading raw OIDs; * 2) begemotSnmpdAgentFreeBSD -> lookup the ASN OID corresponding to that. */ static int32_t parse_oid_string(struct snmp_toolinfo *snmptoolctx, struct snmp_value *value, char *string) { struct snmp_object obj; if (isdigit(string[0])) return (parse_oid_numeric(value, string)); memset(&obj, 0, sizeof(struct snmp_object)); if (snmp_lookup_enumoid(snmptoolctx, &obj, string) < 0) { warnx("Unknown OID enum string - %s", string); return (-1); } asn_append_oid(&(value->v.oid), &(obj.val.var)); return (1); } static int32_t parse_ip(struct snmp_value * value, char * val) { char *endptr, *str; int32_t i; uint32_t v; str = val; for (i = 0; i < 4; i++) { v = strtoul(str, &endptr, 10); if (v > 0xff) return (-1); if (*endptr != '.' && *endptr != '\0' && i != 3) break; str = endptr + 1; value->v.ipaddress[i] = (uint8_t) v; } value->syntax = SNMP_SYNTAX_IPADDRESS; return (0); } static int32_t parse_int(struct snmp_value *value, char *val) { char *endptr; int32_t v, saved_errno; saved_errno = errno; errno = 0; v = strtol(val, &endptr, 10); if (errno != 0) { warn("Value %s not supported", val); errno = saved_errno; return (-1); } value->syntax = SNMP_SYNTAX_INTEGER; value->v.integer = v; errno = saved_errno; return (0); } static int32_t parse_int_string(struct snmp_object *object, char *val) { int32_t v; if (isdigit(val[0])) return ((parse_int(&(object->val), val))); if (object->info == NULL) { warnx("Unknown enumerated integer type - %s", val); return (-1); } if ((v = enum_number_lookup(object->info->snmp_enum, val)) < 0) warnx("Unknown enumerated integer type - %s", val); object->val.v.integer = v; return (1); } /* * Here syntax may be one of SNMP_SYNTAX_COUNTER, SNMP_SYNTAX_GAUGE, * SNMP_SYNTAX_TIMETICKS. */ static int32_t parse_uint(struct snmp_value *value, char *val) { char *endptr; uint32_t v = 0; int32_t saved_errno; saved_errno = errno; errno = 0; v = strtoul(val, &endptr, 10); if (errno != 0) { warn("Value %s not supported", val); errno = saved_errno; return (-1); } value->v.uint32 = v; errno = saved_errno; return (0); } static int32_t parse_ticks(struct snmp_value *value, char *val) { if (parse_uint(value, val) < 0) return (-1); value->syntax = SNMP_SYNTAX_TIMETICKS; return (0); } static int32_t parse_gauge(struct snmp_value *value, char *val) { if (parse_uint(value, val) < 0) return (-1); value->syntax = SNMP_SYNTAX_GAUGE; return (0); } static int32_t parse_counter(struct snmp_value *value, char *val) { if (parse_uint(value, val) < 0) return (-1); value->syntax = SNMP_SYNTAX_COUNTER; return (0); } static int32_t parse_uint64(struct snmp_value *value, char *val) { char *endptr; int32_t saved_errno; uint64_t v; saved_errno = errno; errno = 0; v = strtoull(val, &endptr, 10); if (errno != 0) { warnx("Value %s not supported", val); errno = saved_errno; return (-1); } value->syntax = SNMP_SYNTAX_COUNTER64; value->v.counter64 = v; errno = saved_errno; return (0); } static int32_t parse_syntax_val(struct snmp_value *value, enum snmp_syntax syntax, char *val) { switch (syntax) { case SNMP_SYNTAX_INTEGER: return (parse_int(value, val)); case SNMP_SYNTAX_IPADDRESS: return (parse_ip(value, val)); case SNMP_SYNTAX_COUNTER: return (parse_counter(value, val)); case SNMP_SYNTAX_GAUGE: return (parse_gauge(value, val)); case SNMP_SYNTAX_TIMETICKS: return (parse_ticks(value, val)); case SNMP_SYNTAX_COUNTER64: return (parse_uint64(value, val)); case SNMP_SYNTAX_OCTETSTRING: return (snmp_tc2oct(SNMP_STRING, value, val)); case SNMP_SYNTAX_OID: return (parse_oid_numeric(value, val)); default: /* NOTREACHED */ break; } return (-1); } /* - * Parse a command line argument of type OID=syntax:value and fill in whatever + * Parse a command line argument of type OID=syntax:value and fill in whatever * fields can be derived from the input into snmp_value structure. Reads numeric * OIDs. */ static int32_t parse_pair_numoid_val(char *str, struct snmp_value *snmp_val) { int32_t cnt; char *ptr; enum snmp_syntax syntax; char oid_str[ASN_OIDSTRLEN]; ptr = str; for (cnt = 0; cnt < ASN_OIDSTRLEN; cnt++) if (ptr[cnt] == '=') break; if (cnt >= ASN_OIDSTRLEN) { warnx("OID too long - %s", str); return (-1); } strlcpy(oid_str, ptr, (size_t) (cnt + 1)); ptr = str + cnt + 1; for (cnt = 0; cnt < MAX_CMD_SYNTAX_LEN; cnt++) if(ptr[cnt] == ':') break; if (cnt >= MAX_CMD_SYNTAX_LEN) { warnx("Unknown syntax in OID - %s", str); return (-1); } if ((syntax = parse_syntax(ptr)) <= SNMP_SYNTAX_NULL) { warnx("Unknown syntax in OID - %s", ptr); return (-1); } ptr = ptr + cnt + 1; for (cnt = 0; cnt < MAX_OCTSTRING_LEN; cnt++) if (ptr[cnt] == '\0') break; if (ptr[cnt] != '\0') { warnx("Value string too long - %s", ptr); return (-1); } /* * Here try parsing the OIDs and syntaxes and then check values - have * to know syntax to check value boundaries. */ if (snmp_parse_numoid(oid_str, &(snmp_val->var)) < 0) { warnx("Error parsing OID %s", oid_str); return (-1); } if (parse_syntax_val(snmp_val, syntax, ptr) < 0) return (-1); return (1); } /* XXX-BZ aruments should be swapped. */ static int32_t parse_syntax_strval(struct snmp_toolinfo *snmptoolctx, char *str, struct snmp_object *object) { uint32_t len; enum snmp_syntax syn; /* * Syntax string here not required - still may be present. */ if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE) { for (len = 0 ; *(str + len) != ':'; len++) { if (*(str + len) == '\0') { warnx("Syntax missing in value - %s", str); return (-1); } } if ((syn = parse_syntax(str)) <= SNMP_SYNTAX_NULL) { warnx("Unknown syntax in - %s", str); return (-1); } if (syn != object->val.syntax) { if (!ISSET_ERRIGNORE(snmptoolctx)) { warnx("Bad syntax in - %s", str); return (-1); } else object->val.syntax = syn; } len++; } else len = 0; switch (object->val.syntax) { case SNMP_SYNTAX_INTEGER: return (parse_int_string(object, str + len)); case SNMP_SYNTAX_IPADDRESS: return (parse_ip(&(object->val), str + len)); case SNMP_SYNTAX_COUNTER: return (parse_counter(&(object->val), str + len)); case SNMP_SYNTAX_GAUGE: return (parse_gauge(&(object->val), str + len)); case SNMP_SYNTAX_TIMETICKS: return (parse_ticks(&(object->val), str + len)); case SNMP_SYNTAX_COUNTER64: return (parse_uint64(&(object->val), str + len)); case SNMP_SYNTAX_OCTETSTRING: return (snmp_tc2oct(object->info->tc, &(object->val), str + len)); case SNMP_SYNTAX_OID: return (parse_oid_string(snmptoolctx, &(object->val), str + len)); default: /* NOTREACHED */ break; } return (-1); } static int32_t parse_pair_stroid_val(struct snmp_toolinfo *snmptoolctx, struct snmp_object *obj, char *argv) { char *ptr; if ((ptr = snmptools_parse_stroid(snmptoolctx, obj, argv)) == NULL) return (-1); - + if (*ptr != '=') { warnx("Value to set expected after OID"); return (-1); } if (parse_syntax_strval(snmptoolctx, ptr + 1, obj) < 0) return (-1); return (1); } static int32_t snmpset_parse_oid(struct snmp_toolinfo *snmptoolctx, struct snmp_object *obj, char *argv) { if (argv == NULL) return (-1); if (ISSET_NUMERIC(snmptoolctx)) { if (parse_pair_numoid_val(argv, &(obj->val)) < 0) return (-1); } else { if (parse_pair_stroid_val(snmptoolctx, obj, argv) < 0) return (-1); } return (1); } static int32_t add_ip_syntax(struct snmp_value *dst, struct snmp_value *src) { int8_t i; dst->syntax = SNMP_SYNTAX_IPADDRESS; for (i = 0; i < 4; i++) dst->v.ipaddress[i] = src->v.ipaddress[i]; return (1); } static int32_t add_octstring_syntax(struct snmp_value *dst, struct snmp_value *src) { if (src->v.octetstring.len > ASN_MAXOCTETSTRING) { warnx("OctetString len too big - %u", src->v.octetstring.len); return (-1); } if ((dst->v.octetstring.octets = malloc(src->v.octetstring.len)) == NULL) { syslog(LOG_ERR, "malloc() failed - %s", strerror(errno)); return (-1); } memcpy(dst->v.octetstring.octets, src->v.octetstring.octets, src->v.octetstring.len); dst->syntax = SNMP_SYNTAX_OCTETSTRING; dst->v.octetstring.len = src->v.octetstring.len; return(0); } static int32_t add_oid_syntax(struct snmp_value *dst, struct snmp_value *src) { asn_append_oid(&(dst->v.oid), &(src->v.oid)); dst->syntax = SNMP_SYNTAX_OID; return (0); } /* * Check syntax - if one of SNMP_SYNTAX_NULL, SNMP_SYNTAX_NOSUCHOBJECT, * SNMP_SYNTAX_NOSUCHINSTANCE, SNMP_SYNTAX_ENDOFMIBVIEW or anything not known - * return error. */ static int32_t snmpset_add_value(struct snmp_value *dst, struct snmp_value *src) { if (dst == NULL || src == NULL) return (-1); switch (src->syntax) { case SNMP_SYNTAX_INTEGER: dst->v.integer = src->v.integer; dst->syntax = SNMP_SYNTAX_INTEGER; break; case SNMP_SYNTAX_TIMETICKS: dst->v.uint32 = src->v.uint32; dst->syntax = SNMP_SYNTAX_TIMETICKS; break; case SNMP_SYNTAX_GAUGE: dst->v.uint32 = src->v.uint32; dst->syntax = SNMP_SYNTAX_GAUGE; break; case SNMP_SYNTAX_COUNTER: dst->v.uint32 = src->v.uint32; dst->syntax = SNMP_SYNTAX_COUNTER; break; case SNMP_SYNTAX_COUNTER64: dst->v.counter64 = src->v.counter64; dst->syntax = SNMP_SYNTAX_COUNTER64; break; case SNMP_SYNTAX_IPADDRESS: add_ip_syntax(dst, src); break; case SNMP_SYNTAX_OCTETSTRING: add_octstring_syntax(dst, src); break; case SNMP_SYNTAX_OID: add_oid_syntax(dst, src); break; default: warnx("Unknown syntax %d", src->syntax); return (-1); } return (0); } static int32_t snmpset_verify_vbind(struct snmp_toolinfo *snmptoolctx, struct snmp_pdu *pdu, struct snmp_object *obj) { if (pdu->version == SNMP_V1 && obj->val.syntax == SNMP_SYNTAX_COUNTER64) { warnx("64-bit counters are not supported in SNMPv1 PDU"); return (-1); } if (ISSET_NUMERIC(snmptoolctx) || ISSET_ERRIGNORE(snmptoolctx)) return (1); if (obj->info->access < SNMP_ACCESS_SET) { warnx("Object %s not accessible for set - try 'bsnmpset -a'", obj->info->string); return (-1); } return (1); } static int32_t snmpset_add_vbind(struct snmp_pdu *pdu, struct snmp_object *obj) { if (pdu->nbindings > SNMP_MAX_BINDINGS) { warnx("Too many OIDs for one PDU"); return (-1); } if (obj->error > 0) return (0); if (snmpset_add_value(&(pdu->bindings[pdu->nbindings]), &(obj->val)) < 0) return (-1); asn_append_oid(&(pdu->bindings[pdu->nbindings].var), &(obj->val.var)); pdu->nbindings++; return (pdu->nbindings); } static int snmptool_set(struct snmp_toolinfo *snmptoolctx) { struct snmp_pdu req, resp; snmp_pdu_create(&req, SNMP_PDU_SET); while ((snmp_pdu_add_bindings(snmptoolctx, snmpset_verify_vbind, snmpset_add_vbind, &req, SNMP_MAX_BINDINGS)) > 0) { if (snmp_dialog(&req, &resp)) { warn("Snmp dialog"); break; } if (snmp_pdu_check(&req, &resp) > 0) { if (GET_OUTPUT(snmptoolctx) != OUTPUT_QUIET) snmp_output_resp(snmptoolctx, &resp, NULL); break; } snmp_output_err_resp(snmptoolctx, &resp); if (!ISSET_RETRY(snmptoolctx)) break; if (snmp_object_seterror(snmptoolctx, &(resp.bindings[resp.error_index - 1]), resp.error_status) <= 0) break; fprintf(stderr, "Retrying...\n"); snmp_pdu_free(&req); snmp_pdu_free(&resp); snmp_pdu_create(&req, SNMP_PDU_SET); } snmp_pdu_free(&resp); return (0); } /* ***************************************************************************** * main */ /* * According to command line options prepare SNMP Get | GetNext | GetBulk PDU. * Wait for a response and print it. */ /* * Do a 'snmp walk' - according to command line options request for values * lexicographically subsequent and subrooted at a common node. Send a GetNext * PDU requesting the value for each next variable and print the response. Stop * when a Response PDU is received that contains the value of a variable not * subrooted at the variable the walk started. */ int main(int argc, char ** argv) { struct snmp_toolinfo snmptoolctx; int32_t oid_cnt, last_oid, opt_num; int rc = 0; /* Make sure program_name is set and valid. */ if (*argv == NULL) program_name = "snmptool"; else { program_name = strrchr(*argv, '/'); if (program_name != NULL) program_name++; else program_name = *argv; } if (program_name == NULL) { fprintf(stderr, "Error: No program name?\n"); exit (1); } else if (strcmp(program_name, "bsnmpget") == 0) program = BSNMPGET; else if (strcmp(program_name, "bsnmpwalk") == 0) program = BSNMPWALK; else if (strcmp(program_name, "bsnmpset") == 0) program = BSNMPSET; else { fprintf(stderr, "Unknown snmp tool name '%s'.\n", program_name); exit (1); } /* Initialize. */ if (snmptool_init(&snmptoolctx) < 0) exit (1); if ((opt_num = snmptool_parse_options(&snmptoolctx, argc, argv)) < 0) { snmp_tool_freeall(&snmptoolctx); /* On -h (help) exit without error. */ if (opt_num == -2) exit(0); else exit(1); } oid_cnt = argc - opt_num - 1; if (oid_cnt == 0) { switch (program) { case BSNMPGET: if (!ISSET_EDISCOVER(&snmptoolctx) && !ISSET_LOCALKEY(&snmptoolctx)) { fprintf(stderr, "No OID given.\n"); usage(); snmp_tool_freeall(&snmptoolctx); exit(1); } break; case BSNMPWALK: if (snmp_object_add(&snmptoolctx, snmpwalk_add_default, NULL) < 0) { fprintf(stderr, "Error setting default subtree.\n"); snmp_tool_freeall(&snmptoolctx); exit(1); } break; case BSNMPSET: fprintf(stderr, "No OID given.\n"); usage(); snmp_tool_freeall(&snmptoolctx); exit(1); } } if (snmp_import_all(&snmptoolctx) < 0) { snmp_tool_freeall(&snmptoolctx); exit(1); } /* A simple sanity check - can not send GETBULK when using SNMPv1. */ if (program == BSNMPGET && snmp_client.version == SNMP_V1 && GET_PDUTYPE(&snmptoolctx) == SNMP_PDU_GETBULK) { fprintf(stderr, "Cannot send GETBULK PDU with SNMPv1.\n"); snmp_tool_freeall(&snmptoolctx); exit(1); } for (last_oid = argc - 1; oid_cnt > 0; last_oid--, oid_cnt--) { if ((snmp_object_add(&snmptoolctx, (program == BSNMPSET) ? snmpset_parse_oid : snmptools_parse_oid, argv[last_oid])) < 0) { fprintf(stderr, "Error parsing OID string '%s'.\n", argv[last_oid]); snmp_tool_freeall(&snmptoolctx); exit(1); } } if (snmp_open(NULL, NULL, NULL, NULL)) { warn("Failed to open snmp session"); snmp_tool_freeall(&snmptoolctx); exit(1); } if (snmp_client.version == SNMP_V3 && snmp_client.engine.engine_len == 0) SET_EDISCOVER(&snmptoolctx); if (ISSET_EDISCOVER(&snmptoolctx) && snmp_discover_engine(snmptoolctx.passwd) < 0) { warn("Unknown SNMP Engine ID"); rc = 1; goto cleanup; } if (GET_OUTPUT(&snmptoolctx) == OUTPUT_VERBOSE || ISSET_EDISCOVER(&snmptoolctx)) snmp_output_engine(); if (snmp_client.version == SNMP_V3 && ISSET_LOCALKEY(&snmptoolctx) && !ISSET_EDISCOVER(&snmptoolctx)) { if (snmp_passwd_to_keys(&snmp_client.user, snmptoolctx.passwd) != SNMP_CODE_OK || snmp_get_local_keys(&snmp_client.user, snmp_client.engine.engine_id, snmp_client.engine.engine_len) != SNMP_CODE_OK) { warn("Failed to get keys"); rc = 1; goto cleanup; } } if (GET_OUTPUT(&snmptoolctx) == OUTPUT_VERBOSE || ISSET_EDISCOVER(&snmptoolctx)) snmp_output_keys(); if (ISSET_EDISCOVER(&snmptoolctx) && snmptoolctx.objects == 0) goto cleanup; switch (program) { case BSNMPGET: rc = snmptool_get(&snmptoolctx); break; case BSNMPWALK: rc = snmptool_walk(&snmptoolctx); break; case BSNMPSET: rc = snmptool_set(&snmptoolctx); break; } cleanup: snmp_tool_freeall(&snmptoolctx); snmp_close(); exit(rc); } Index: head/usr.sbin/bsnmpd/tools/libbsnmptools/bsnmpimport.c =================================================================== --- head/usr.sbin/bsnmpd/tools/libbsnmptools/bsnmpimport.c (revision 310647) +++ head/usr.sbin/bsnmpd/tools/libbsnmptools/bsnmpimport.c (revision 310648) @@ -1,968 +1,968 @@ /*- * Copyright (c) 2006 The FreeBSD Project * All rights reserved. * * Author: Shteryana Shopova * * 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 IS PROVIDED BY THE 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 THE 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. * * $FreeBSD$ */ /* * Read file containing table description - reuse magic from gensnmptree.c. * Hopefully one day most of the code here will be part of libbsnmp and * this duplication won't be necessary. * * Syntax is: * --------- * file := top | top file * * top := tree | typedef | include * * tree := head elements ')' * * entry := head ':' index STRING elements ')' * * leaf := head type STRING ACCESS ')' * * column := head type ACCESS ')' * * type := BASETYPE | BASETYPE '|' subtype | enum | bits * * subtype := STRING * * enum := ENUM '(' value ')' - * + * * bits := BITS '(' value ')' * * value := INT STRING | INT STRING value * * head := '(' INT STRING * * elements := EMPTY | elements element * * element := tree | leaf | column * * index := type | index type * * typedef := 'typedef' STRING type * * include := 'include' filespec * * filespec := '"' STRING '"' | '<' STRING '>' */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* SNMP_INDEXES_MAX */ #include "bsnmptc.h" #include "bsnmptools.h" enum snmp_tbl_entry { ENTRY_NONE = 0, ENTRY_INDEX, ENTRY_DATA }; enum { FL_GET = 0x01, FL_SET = 0x02, }; /************************************************************ * * 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); } static char * savestr(const char *s) { if (s == NULL) return (NULL); return (strcpy(xalloc(strlen(s) + 1), s)); } /************************************************************ * * Input stack */ struct input { FILE *fp; uint32_t lno; char *fname; char *path; LIST_ENTRY(input) link; }; static LIST_HEAD(, input) inputs = LIST_HEAD_INITIALIZER(inputs); static struct input *input = NULL; static int32_t pbchar = -1; #define MAX_PATHS 100 static const char *paths[MAX_PATHS + 1] = { "/usr/share/snmp/defs", "/usr/local/share/snmp/defs", NULL }; static void input_new(FILE *fp, const char *path, const char *fname) { struct input *ip; ip = xalloc(sizeof(*ip)); ip->fp = fp; ip->lno = 1; ip->fname = savestr(fname); ip->path = savestr(path); LIST_INSERT_HEAD(&inputs, ip, link); input = ip; } static void input_close(void) { if (input == NULL) return; fclose(input->fp); free(input->fname); free(input->path); LIST_REMOVE(input, link); free(input); input = LIST_FIRST(&inputs); } static FILE * tryopen(const char *path, const char *fname) { char *fn; FILE *fp; if (path == NULL) fn = savestr(fname); else { fn = xalloc(strlen(path) + strlen(fname) + 2); sprintf(fn, "%s/%s", path, fname); } fp = fopen(fn, "r"); free(fn); return (fp); } static int32_t input_fopen(const char *fname) { FILE *fp; u_int p; if (fname[0] == '/' || fname[0] == '.' || fname[0] == '~') { if ((fp = tryopen(NULL, fname)) != NULL) { input_new(fp, NULL, fname); return (0); } } else { for (p = 0; paths[p] != NULL; p++) if ((fp = tryopen(paths[p], fname)) != NULL) { input_new(fp, paths[p], fname); return (0); } } warnx("cannot open '%s'", fname); return (-1); } static int32_t tgetc(void) { int c; if (pbchar != -1) { c = pbchar; pbchar = -1; return (c); } for (;;) { if (input == NULL) return (EOF); if ((c = getc(input->fp)) != EOF) return (c); input_close(); } } static int32_t tungetc(int c) { if (pbchar != -1) return (-1); pbchar = c; return (1); } /************************************************************ * * 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 */ TOK_ENUM, /* enum token (kind of a type) */ TOK_TYPEDEF, /* typedef directive */ TOK_DEFTYPE, /* defined type */ TOK_INCLUDE, /* include directive */ TOK_FILENAME, /* filename ("foo.bar" or ) */ TOK_BITS, /* bits token (kind of a type) */ TOK_ERR /* unexpected char - exit */ }; static const struct { const char *str; enum tok tok; uint32_t 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 }, { "ENUM", TOK_ENUM, SNMP_SYNTAX_INTEGER }, { "BITS", TOK_BITS, SNMP_SYNTAX_OCTETSTRING }, { "typedef", TOK_TYPEDEF, 0 }, { "include", TOK_INCLUDE, 0 }, { NULL, 0, 0 } }; static struct { /* Current OID type, regarding table membership. */ enum snmp_tbl_entry tbl_type; /* A pointer to a structure in table list to add to its members. */ struct snmp_index_entry *table_idx; } table_data; static struct asn_oid current_oid; static char nexttok[MAXSTR]; static u_long val; /* integer values */ static int32_t all_cond; /* all conditions are true */ static int32_t saved_token = -1; /* Prepare the global data before parsing a new file. */ static void snmp_import_init(struct asn_oid *append) { memset(&table_data, 0, sizeof(table_data)); memset(¤t_oid, 0, sizeof(struct asn_oid)); memset(nexttok, 0, MAXSTR); if (append != NULL) asn_append_oid(¤t_oid, append); all_cond = 0; val = 0; saved_token = -1; } static int32_t gettoken(struct snmp_toolinfo *snmptoolctx) { int c; struct enum_type *t; if (saved_token != -1) { c = saved_token; saved_token = -1; return (c); } again: /* * Skip any whitespace before the next token. */ while ((c = tgetc()) != EOF) { if (c == '\n') input->lno++; if (!isspace(c)) break; } if (c == EOF) return (TOK_EOF); if (!isascii(c)) { warnx("unexpected character %#2x", (u_int) c); return (TOK_ERR); } /* * Skip comments. */ if (c == '#') { while ((c = tgetc()) != EOF) { if (c == '\n') { input->lno++; goto again; } } warnx("unexpected EOF in comment"); return (TOK_ERR); } /* * Single character tokens. */ if (strchr("():|", c) != NULL) return (c); if (c == '"' || c == '<') { int32_t end = c; size_t n = 0; val = 1; if (c == '<') { val = 0; end = '>'; } while ((c = tgetc()) != EOF) { if (c == end) break; if (n == sizeof(nexttok) - 1) { nexttok[n++] = '\0'; warnx("filename too long '%s...'", nexttok); return (TOK_ERR); } nexttok[n++] = c; } nexttok[n++] = '\0'; return (TOK_FILENAME); } /* * Sort out numbers. */ if (isdigit(c)) { size_t n = 0; nexttok[n++] = c; while ((c = tgetc()) != EOF) { if (!isdigit(c)) { if (tungetc(c) < 0) return (TOK_ERR); break; } if (n == sizeof(nexttok) - 1) { nexttok[n++] = '\0'; warnx("number too long '%s...'", nexttok); return (TOK_ERR); } nexttok[n++] = c; } nexttok[n++] = '\0'; sscanf(nexttok, "%lu", &val); return (TOK_NUM); } /* * So that has to be a string. */ if (isalpha(c) || c == '_' || c == '-') { size_t n = 0; nexttok[n++] = c; while ((c = tgetc()) != EOF) { if (!isalnum(c) && c != '_' && c != '-') { if (tungetc (c) < 0) return (TOK_ERR); break; } if (n == sizeof(nexttok) - 1) { nexttok[n++] = '\0'; warnx("string too long '%s...'", nexttok); return (TOK_ERR); } nexttok[n++] = c; } nexttok[n++] = '\0'; /* * Keywords. */ for (c = 0; keywords[c].str != NULL; c++) if (strcmp(keywords[c].str, nexttok) == 0) { val = keywords[c].val; return (keywords[c].tok); } if ((t = snmp_enumtc_lookup(snmptoolctx, nexttok)) != NULL) { val = t->syntax; return (TOK_DEFTYPE); } return (TOK_STR); } if (isprint(c)) warnx("%u: unexpected character '%c'", input->lno, c); else warnx("%u: unexpected character 0x%02x", input->lno, (u_int) c); return (TOK_ERR); } /* * Update table information. */ static struct snmp_index_entry * snmp_import_update_table(enum snmp_tbl_entry te, struct snmp_index_entry *tbl) { switch (te) { case ENTRY_NONE: if (table_data.tbl_type == ENTRY_NONE) - return (NULL); + return (NULL); if (table_data.tbl_type == ENTRY_INDEX) table_data.table_idx = NULL; table_data.tbl_type--; return (NULL); case ENTRY_INDEX: if (tbl == NULL) warnx("No table_index to add!!!"); table_data.table_idx = tbl; table_data.tbl_type = ENTRY_INDEX; return (tbl); case ENTRY_DATA: if (table_data.tbl_type == ENTRY_INDEX) { table_data.tbl_type = ENTRY_DATA; return (table_data.table_idx); } return (NULL); default: /* NOTREACHED */ warnx("Unknown table entry type!!!"); break; } return (NULL); } static int32_t parse_enum(struct snmp_toolinfo *snmptoolctx, int32_t *tok, struct enum_pairs *enums) { while ((*tok = gettoken(snmptoolctx)) == TOK_STR) { if (enum_pair_insert(enums, val, nexttok) < 0) return (-1); if ((*tok = gettoken(snmptoolctx)) != TOK_NUM) break; } if (*tok != ')') { warnx("')' at end of enums"); return (-1); } return (1); } static int32_t parse_subtype(struct snmp_toolinfo *snmptoolctx, int32_t *tok, enum snmp_tc *tc) { if ((*tok = gettoken(snmptoolctx)) != TOK_STR) { warnx("subtype expected after '|'"); return (-1); } - + *tc = snmp_get_tc(nexttok); *tok = gettoken(snmptoolctx); return (1); } static int32_t parse_type(struct snmp_toolinfo *snmptoolctx, int32_t *tok, enum snmp_tc *tc, struct enum_pairs **snmp_enum) { int32_t syntax, mem; syntax = val; *tc = 0; if (*tok == TOK_ENUM || *tok == TOK_BITS) { if (*snmp_enum == NULL) { if ((*snmp_enum = enum_pairs_init()) == NULL) return (-1); mem = 1; *tc = SNMP_TC_OWN; } else mem = 0; if (gettoken(snmptoolctx) != '(') { warnx("'(' expected after ENUM/BITS"); return (-1); } if ((*tok = gettoken(snmptoolctx)) != TOK_NUM) { warnx("need value for ENUM//BITS"); if (mem == 1) { free(*snmp_enum); *snmp_enum = NULL; } return (-1); } if (parse_enum(snmptoolctx, tok, *snmp_enum) < 0) { enum_pairs_free(*snmp_enum); *snmp_enum = NULL; return (-1); } *tok = gettoken(snmptoolctx); } else if (*tok == TOK_DEFTYPE) { struct enum_type *t; *tc = 0; t = snmp_enumtc_lookup(snmptoolctx, nexttok); if (t != NULL) *snmp_enum = t->snmp_enum; *tok = gettoken(snmptoolctx); } else { if ((*tok = gettoken(snmptoolctx)) == '|') { if (parse_subtype(snmptoolctx, tok, tc) < 0) return (-1); } } return (syntax); } static int32_t snmp_import_head(struct snmp_toolinfo *snmptoolctx) { enum tok tok; if ((tok = gettoken(snmptoolctx)) == '(') tok = gettoken(snmptoolctx); if (tok != TOK_NUM || val > ASN_MAXID ) { warnx("Suboid expected - line %d", input->lno); return (-1); } if (gettoken(snmptoolctx) != TOK_STR) { warnx("Node name expected at line %d", input->lno); return (-1); } return (1); } static int32_t snmp_import_table(struct snmp_toolinfo *snmptoolctx, struct snmp_oid2str *obj) { int32_t i, tok; enum snmp_tc tc; struct snmp_index_entry *entry; if ((entry = calloc(1, sizeof(struct snmp_index_entry))) == NULL) { syslog(LOG_ERR, "malloc() failed: %s", strerror(errno)); return (-1); } STAILQ_INIT(&(entry->index_list)); for (i = 0, tok = gettoken(snmptoolctx); i < SNMP_INDEXES_MAX; i++) { int32_t syntax; struct enum_pairs *enums = NULL; if (tok != TOK_TYPE && tok != TOK_DEFTYPE && tok != TOK_ENUM && tok != TOK_BITS) break; if ((syntax = parse_type(snmptoolctx, &tok, &tc, &enums)) < 0) { enum_pairs_free(enums); snmp_index_listfree(&(entry->index_list)); free(entry); return (-1); } if (snmp_syntax_insert(&(entry->index_list), enums, syntax, tc) < 0) { snmp_index_listfree(&(entry->index_list)); enum_pairs_free(enums); free(entry); return (-1); } } if (i == 0 || i > SNMP_INDEXES_MAX) { warnx("Bad number of indexes at line %d", input->lno); snmp_index_listfree(&(entry->index_list)); free(entry); return (-1); } if (tok != TOK_STR) { warnx("String expected after indexes at line %d", input->lno); snmp_index_listfree(&(entry->index_list)); free(entry); return (-1); } entry->string = obj->string; entry->strlen = obj->strlen; asn_append_oid(&(entry->var), &(obj->var)); if ((i = snmp_table_insert(snmptoolctx, entry)) < 0) { snmp_index_listfree(&(entry->index_list)); free(entry); return (-1); } else if (i == 0) { /* Same entry already present in lists. */ free(entry->string); free(entry); return (0); } (void) snmp_import_update_table(ENTRY_INDEX, entry); return (1); } /* * Read everything after the syntax type that is certainly a leaf OID info. */ static int32_t snmp_import_leaf(struct snmp_toolinfo *snmptoolctx, int32_t *tok, struct snmp_oid2str *oid2str) { int32_t i, syntax; if ((syntax = parse_type(snmptoolctx, tok, &(oid2str->tc), &(oid2str->snmp_enum))) < 0) return(-1); oid2str->syntax = syntax; /* * That is the name of the function, corresponding to the entry. * It is used by bsnmpd, but is not interesting for us. */ if (*tok == TOK_STR) *tok = gettoken(snmptoolctx); for (i = 0; i < SNMP_ACCESS_GETSET && *tok == TOK_ACCESS; i++) { oid2str->access |= (uint32_t) val; *tok = gettoken(snmptoolctx); } if (*tok != ')') { warnx("')' expected at end of line %d", input->lno); return (-1); } oid2str->table_idx = snmp_import_update_table(ENTRY_DATA, NULL); if ((i = snmp_leaf_insert(snmptoolctx, oid2str)) < 0) { warnx("Error adding leaf %s to list", oid2str->string); return (-1); } /* * Same entry is already present in the mapping lists and * the new one was not inserted. */ if (i == 0) { free(oid2str->string); free(oid2str); } (void) snmp_import_update_table(ENTRY_NONE, NULL); return (1); } static int32_t snmp_import_object(struct snmp_toolinfo *snmptoolctx) { char *string; int i; int32_t tok; struct snmp_oid2str *oid2str; if (snmp_import_head(snmptoolctx) < 0) return (-1); if ((oid2str = calloc(1, sizeof(struct snmp_oid2str))) == NULL) { syslog(LOG_ERR, "calloc() failed: %s", strerror(errno)); return (-1); } if ((string = strdup(nexttok)) == NULL) { syslog(LOG_ERR, "strdup() failed: %s", strerror(errno)); free(oid2str); return (-1); } oid2str->string = string; oid2str->strlen = strlen(nexttok); asn_append_oid(&(oid2str->var), &(current_oid)); if (snmp_suboid_append(&(oid2str->var), (asn_subid_t) val) < 0) goto error; /* * Prepared the entry - now figure out where to insert it. * After the object we have following options: * 1) new line, blank, ) - then it is an enum oid -> snmp_enumlist; * 2) new line , ( - nonleaf oid -> snmp_nodelist; * 2) ':' - table entry - a variable length SYNTAX_TYPE (one or more) * may follow and second string must end line -> snmp_tablelist; * 3) OID , string ) - this is a trap entry or a leaf -> snmp_oidlist; * 4) SYNTAX_TYPE, string (not always), get/set modifier - always last * and )- this is definitely a leaf. */ switch (tok = gettoken(snmptoolctx)) { case ')': if ((i = snmp_enum_insert(snmptoolctx, oid2str)) < 0) goto error; if (i == 0) { free(oid2str->string); free(oid2str); } return (1); case '(': if (snmp_suboid_append(¤t_oid, (asn_subid_t) val) < 0) goto error; - /* + /* * Ignore the error for nodes since the .def files currently * contain different strings for 1.3.6.1.2.1 - mibII. Only make * sure the memory is freed and don't complain. */ if ((i = snmp_node_insert(snmptoolctx, oid2str)) <= 0) { free(string); free(oid2str); } return (snmp_import_object(snmptoolctx)); case ':': if (snmp_suboid_append(¤t_oid, (asn_subid_t) val) < 0) goto error; if (snmp_import_table(snmptoolctx, oid2str) < 0) goto error; /* * A different table entry type was malloced and the data is * contained there. */ free(oid2str); return (1); case TOK_TYPE: /* FALLTHROUGH */ case TOK_DEFTYPE: /* FALLTHROUGH */ case TOK_ENUM: /* FALLTHROUGH */ case TOK_BITS: if (snmp_import_leaf(snmptoolctx, &tok, oid2str) < 0) goto error; return (1); default: warnx("Unexpected token at line %d - %s", input->lno, input->fname); break; } error: snmp_mapping_entryfree(oid2str); return (-1); } static int32_t snmp_import_tree(struct snmp_toolinfo *snmptoolctx, int32_t *tok) { while (*tok != TOK_EOF) { switch (*tok) { case TOK_ERR: return (-1); case '(': if (snmp_import_object(snmptoolctx) < 0) return (-1); break; case ')': if (snmp_suboid_pop(¤t_oid) < 0) return (-1); (void) snmp_import_update_table(ENTRY_NONE, NULL); break; default: /* Anything else here would be illegal. */ return (-1); } *tok = gettoken(snmptoolctx); } return (0); } static int32_t snmp_import_top(struct snmp_toolinfo *snmptoolctx, int32_t *tok) { enum snmp_tc tc; struct enum_type *t; if (*tok == '(') return (snmp_import_tree(snmptoolctx, tok)); if (*tok == TOK_TYPEDEF) { if ((*tok = gettoken(snmptoolctx)) != TOK_STR) { warnx("type name expected after typedef - %s", input->fname); return (-1); } t = snmp_enumtc_init(nexttok); *tok = gettoken(snmptoolctx); t->is_enum = (*tok == TOK_ENUM); t->is_bits = (*tok == TOK_BITS); t->syntax = parse_type(snmptoolctx, tok, &tc, &(t->snmp_enum)); snmp_enumtc_insert(snmptoolctx, t); return (1); } if (*tok == TOK_INCLUDE) { int i; *tok = gettoken(snmptoolctx); if (*tok != TOK_FILENAME) { warnx("filename expected in include directive - %s", nexttok); return (-1); } if (( i = add_filename(snmptoolctx, nexttok, NULL, 1)) == 0) { *tok = gettoken(snmptoolctx); return (1); } if (i == -1) return (-1); input_fopen(nexttok); *tok = gettoken(snmptoolctx); return (1); } warnx("'(' or 'typedef' expected - %s", nexttok); return (-1); } static int32_t snmp_import(struct snmp_toolinfo *snmptoolctx) { int i; int32_t tok; tok = gettoken(snmptoolctx); do i = snmp_import_top(snmptoolctx, &tok); while (i > 0); return (i); } /* * Read a .def file and import oid<->string mapping. * Mappings are inserted into a global structure containing list for each OID * syntax type. */ int32_t snmp_import_file(struct snmp_toolinfo *snmptoolctx, struct fname *file) { int idx; snmp_import_init(&(file->cut)); input_fopen(file->name); if ((idx = snmp_import(snmptoolctx)) < 0) warnx("Failed to read mappings from file %s", file->name); input_close(); return (idx); } Index: head/usr.sbin/bsnmpd/tools/libbsnmptools/bsnmpmap.c =================================================================== --- head/usr.sbin/bsnmpd/tools/libbsnmptools/bsnmpmap.c (revision 310647) +++ head/usr.sbin/bsnmpd/tools/libbsnmptools/bsnmpmap.c (revision 310648) @@ -1,1009 +1,1009 @@ /*- * Copyright (c) 2006 The FreeBSD Project * All rights reserved. * * Author: Shteryana Shopova * * 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 IS PROVIDED BY THE 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 THE 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. * * $FreeBSD$ */ -#include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include "bsnmptc.h" #include "bsnmptools.h" #define DEBUG if (_bsnmptools_debug) fprintf /* Allocate memory and initialize list. */ struct snmp_mappings * snmp_mapping_init(void) { struct snmp_mappings *m; if ((m = calloc(1, sizeof(struct snmp_mappings))) == NULL) { syslog(LOG_ERR, "malloc() failed: %s", strerror(errno)); return (NULL); } return (m); } #define snmp_nodelist mappings->nodelist #define snmp_intlist mappings->intlist #define snmp_octlist mappings->octlist #define snmp_oidlist mappings->oidlist #define snmp_iplist mappings->iplist #define snmp_ticklist mappings->ticklist #define snmp_cntlist mappings->cntlist #define snmp_gaugelist mappings->gaugelist #define snmp_cnt64list mappings->cnt64list #define snmp_enumlist mappings->enumlist #define snmp_tablelist mappings->tablelist #define snmp_tclist mappings->tclist void enum_pairs_free(struct enum_pairs *headp) { struct enum_pair *e; if (headp == NULL) return; while ((e = STAILQ_FIRST(headp)) != NULL) { STAILQ_REMOVE_HEAD(headp, link); if (e->enum_str) free(e->enum_str); free(e); } free(headp); } void snmp_mapping_entryfree(struct snmp_oid2str *entry) { if (entry->string) free(entry->string); if (entry->tc == SNMP_TC_OWN) enum_pairs_free(entry->snmp_enum); free(entry); } static void snmp_mapping_listfree(struct snmp_mapping *headp) { struct snmp_oid2str *p; while ((p = SLIST_FIRST(headp)) != NULL) { SLIST_REMOVE_HEAD(headp, link); if (p->string) free(p->string); if (p->tc == SNMP_TC_OWN) enum_pairs_free(p->snmp_enum); free(p); } SLIST_INIT(headp); } void snmp_index_listfree(struct snmp_idxlist *headp) { struct index *i; while ((i = STAILQ_FIRST(headp)) != NULL) { STAILQ_REMOVE_HEAD(headp, link); if (i->tc == SNMP_TC_OWN) enum_pairs_free(i->snmp_enum); free(i); } STAILQ_INIT(headp); } static void snmp_mapping_table_listfree(struct snmp_table_index *headp) { struct snmp_index_entry *t; while ((t = SLIST_FIRST(headp)) != NULL) { SLIST_REMOVE_HEAD(headp, link); if (t->string) free(t->string); snmp_index_listfree(&(t->index_list)); free(t); } } static void snmp_enumtc_listfree(struct snmp_enum_tc *headp) { struct enum_type *t; while ((t = SLIST_FIRST(headp)) != NULL) { SLIST_REMOVE_HEAD(headp, link); if (t->name) free(t->name); enum_pairs_free(t->snmp_enum); free(t); } } int snmp_mapping_free(struct snmp_toolinfo *snmptoolctx) { if (snmptoolctx == NULL || snmptoolctx->mappings == NULL) return (-1); snmp_mapping_listfree(&snmptoolctx->snmp_nodelist); snmp_mapping_listfree(&snmptoolctx->snmp_intlist); snmp_mapping_listfree(&snmptoolctx->snmp_octlist); snmp_mapping_listfree(&snmptoolctx->snmp_oidlist); snmp_mapping_listfree(&snmptoolctx->snmp_iplist); snmp_mapping_listfree(&snmptoolctx->snmp_ticklist); snmp_mapping_listfree(&snmptoolctx->snmp_cntlist); snmp_mapping_listfree(&snmptoolctx->snmp_gaugelist); snmp_mapping_listfree(&snmptoolctx->snmp_cnt64list); snmp_mapping_listfree(&snmptoolctx->snmp_enumlist); snmp_mapping_table_listfree(&snmptoolctx->snmp_tablelist); snmp_enumtc_listfree(&snmptoolctx->snmp_tclist); free(snmptoolctx->mappings); return (0); } static void snmp_dump_enumpairs(struct enum_pairs *headp) { struct enum_pair *entry; if (headp == NULL) return; fprintf(stderr,"enums: "); STAILQ_FOREACH(entry, headp, link) fprintf(stderr,"%d - %s, ", entry->enum_val, (entry->enum_str == NULL)?"NULL":entry->enum_str); fprintf(stderr,"; "); } void snmp_dump_oid2str(struct snmp_oid2str *entry) { char buf[ASN_OIDSTRLEN]; if (entry != NULL) { memset(buf, 0, sizeof(buf)); asn_oid2str_r(&(entry->var), buf); DEBUG(stderr, "%s - %s - %d - %d - %d", buf, entry->string, entry->syntax, entry->access, entry->strlen); snmp_dump_enumpairs(entry->snmp_enum); DEBUG(stderr,"%s \n", (entry->table_idx == NULL)?"No table": entry->table_idx->string); } } static void snmp_dump_indexlist(struct snmp_idxlist *headp) { struct index *entry; if (headp == NULL) return; STAILQ_FOREACH(entry, headp, link) { fprintf(stderr,"%d, ", entry->syntax); snmp_dump_enumpairs(entry->snmp_enum); } fprintf(stderr,"\n"); } /* Initialize the enum pairs list of a oid2str entry. */ struct enum_pairs * enum_pairs_init(void) { struct enum_pairs *snmp_enum; if ((snmp_enum = malloc(sizeof(struct enum_pairs))) == NULL) { syslog(LOG_ERR, "malloc() failed: %s", strerror(errno)); return (NULL); } STAILQ_INIT(snmp_enum); return (snmp_enum); } /* * Given a number and string, allocate memory for a (int, string) pair and add * it to the given oid2str mapping entry's enum pairs list. */ int32_t enum_pair_insert(struct enum_pairs *headp, int32_t enum_val, char *enum_str) { struct enum_pair *e_new; if ((e_new = calloc(1, sizeof(struct enum_pair))) == NULL) { syslog(LOG_ERR, "calloc() failed: %s", strerror(errno)); return (-1); } if ((e_new->enum_str = strdup(enum_str)) == NULL) { syslog(LOG_ERR, "strdup() failed: %s", strerror(errno)); free(e_new); return (-1); } e_new->enum_val = enum_val; STAILQ_INSERT_TAIL(headp, e_new, link); return (1); } /* * Insert an entry in a list - entries are lexicographicaly order by asn_oid. * Returns 1 on success, -1 if list is not initialized, 0 if a matching oid already * exists. Error cheking is left to calling function. */ static int snmp_mapping_insert(struct snmp_mapping *headp, struct snmp_oid2str *entry) { int32_t rc; struct snmp_oid2str *temp, *prev; if (entry == NULL) return(-1); if ((prev = SLIST_FIRST(headp)) == NULL || asn_compare_oid(&(entry->var), &(prev->var)) < 0) { SLIST_INSERT_HEAD(headp, entry, link); return (1); } else rc = -1; /* Make the compiler happy. */ SLIST_FOREACH(temp, headp, link) { if ((rc = asn_compare_oid(&(entry->var), &(temp->var))) <= 0) break; prev = temp; rc = -1; } switch (rc) { case 0: /* Ops, matching OIDs - hope the rest info also matches. */ if (strncmp(temp->string, entry->string, entry->strlen)) { syslog(LOG_INFO, "Matching OIDs with different string " "mappings: old - %s, new - %s", temp->string, entry->string); return (-1); } /* * Ok, we have that already. * As long as the strings match - don't complain. */ return (0); case 1: SLIST_INSERT_AFTER(temp, entry, link); break; case -1: SLIST_INSERT_AFTER(prev, entry, link); break; default: /* NOTREACHED */ return (-1); } return (1); } int32_t snmp_node_insert(struct snmp_toolinfo *snmptoolctx, struct snmp_oid2str *entry) { if (snmptoolctx != NULL && snmptoolctx->mappings) return (snmp_mapping_insert(&snmptoolctx->snmp_nodelist,entry)); return (-1); } static int32_t snmp_int_insert(struct snmp_toolinfo *snmptoolctx, struct snmp_oid2str *entry) { if (snmptoolctx != NULL && snmptoolctx->mappings) return (snmp_mapping_insert(&snmptoolctx->snmp_intlist,entry)); return (-1); } static int32_t snmp_oct_insert(struct snmp_toolinfo *snmptoolctx, struct snmp_oid2str *entry) { if (snmptoolctx != NULL && snmptoolctx->mappings) return (snmp_mapping_insert(&snmptoolctx->snmp_octlist,entry)); return (-1); } static int32_t snmp_oid_insert(struct snmp_toolinfo *snmptoolctx, struct snmp_oid2str *entry) { if (snmptoolctx != NULL && snmptoolctx->mappings) return (snmp_mapping_insert(&snmptoolctx->snmp_oidlist,entry)); return (-1); } static int32_t snmp_ip_insert(struct snmp_toolinfo *snmptoolctx, struct snmp_oid2str *entry) { if (snmptoolctx != NULL && snmptoolctx->mappings) return (snmp_mapping_insert(&snmptoolctx->snmp_iplist,entry)); return (-1); } static int32_t snmp_tick_insert(struct snmp_toolinfo *snmptoolctx, struct snmp_oid2str *entry) { if (snmptoolctx != NULL && snmptoolctx->mappings) return (snmp_mapping_insert(&snmptoolctx->snmp_ticklist,entry)); return (-1); } static int32_t snmp_cnt_insert(struct snmp_toolinfo *snmptoolctx, struct snmp_oid2str *entry) { if (snmptoolctx != NULL && snmptoolctx->mappings) return (snmp_mapping_insert(&snmptoolctx->snmp_cntlist,entry)); return (-1); } static int32_t snmp_gauge_insert(struct snmp_toolinfo *snmptoolctx, struct snmp_oid2str *entry) { if (snmptoolctx != NULL && snmptoolctx->mappings) return (snmp_mapping_insert(&snmptoolctx->snmp_gaugelist,entry)); return (-1); } static int32_t snmp_cnt64_insert(struct snmp_toolinfo *snmptoolctx, struct snmp_oid2str *entry) { if (snmptoolctx != NULL && snmptoolctx->mappings) return (snmp_mapping_insert(&snmptoolctx->snmp_cnt64list,entry)); return (-1); } int32_t snmp_enum_insert(struct snmp_toolinfo *snmptoolctx, struct snmp_oid2str *entry) { if (snmptoolctx != NULL && snmptoolctx->mappings) return (snmp_mapping_insert(&snmptoolctx->snmp_enumlist,entry)); return (-1); } int32_t snmp_leaf_insert(struct snmp_toolinfo *snmptoolctx, struct snmp_oid2str *entry) { switch (entry->syntax) { case SNMP_SYNTAX_INTEGER: return (snmp_int_insert(snmptoolctx, entry)); case SNMP_SYNTAX_OCTETSTRING: return (snmp_oct_insert(snmptoolctx, entry)); case SNMP_SYNTAX_OID: return (snmp_oid_insert(snmptoolctx, entry)); case SNMP_SYNTAX_IPADDRESS: return (snmp_ip_insert(snmptoolctx, entry)); case SNMP_SYNTAX_COUNTER: return (snmp_cnt_insert(snmptoolctx, entry)); case SNMP_SYNTAX_GAUGE: return (snmp_gauge_insert(snmptoolctx, entry)); case SNMP_SYNTAX_TIMETICKS: return (snmp_tick_insert(snmptoolctx, entry)); case SNMP_SYNTAX_COUNTER64: return (snmp_cnt64_insert(snmptoolctx, entry)); default: break; } return (-1); } static int32_t snmp_index_insert(struct snmp_idxlist *headp, struct index *idx) { if (headp == NULL || idx == NULL) return (-1); STAILQ_INSERT_TAIL(headp, idx, link); return (1); } int32_t snmp_syntax_insert(struct snmp_idxlist *headp, struct enum_pairs *enums, enum snmp_syntax syntax, enum snmp_tc tc) { struct index *idx; if ((idx = calloc(1, sizeof(struct index))) == NULL) { syslog(LOG_ERR, "malloc() failed: %s", strerror(errno)); return (-1); } if (snmp_index_insert(headp, idx) < 0) { free(idx); return (-1); } idx->syntax = syntax; idx->snmp_enum = enums; idx->tc = tc; return (1); } int32_t snmp_table_insert(struct snmp_toolinfo *snmptoolctx, struct snmp_index_entry *entry) { int32_t rc; struct snmp_index_entry *temp, *prev; if (snmptoolctx == NULL || snmptoolctx->mappings == NULL || entry == NULL) return(-1); if ((prev = SLIST_FIRST(&snmptoolctx->snmp_tablelist)) == NULL || asn_compare_oid(&(entry->var), &(prev->var)) < 0) { SLIST_INSERT_HEAD(&snmptoolctx->snmp_tablelist, entry, link); return (1); } else rc = -1; /* Make the compiler happy. */ SLIST_FOREACH(temp, &snmptoolctx->snmp_tablelist, link) { if ((rc = asn_compare_oid(&(entry->var), &(temp->var))) <= 0) break; prev = temp; rc = -1; } switch (rc) { case 0: /* Ops, matching OIDs - hope the rest info also matches. */ if (strncmp(temp->string, entry->string, entry->strlen)) { syslog(LOG_INFO, "Matching OIDs with different string " "mapping - old - %s, new - %s", temp->string, entry->string); return (-1); } return(0); case 1: SLIST_INSERT_AFTER(temp, entry, link); break; case -1: SLIST_INSERT_AFTER(prev, entry, link); break; default: /* NOTREACHED */ return (-1); } return (1); } struct enum_type * snmp_enumtc_init(char *name) { struct enum_type *enum_tc; if ((enum_tc = calloc(1, sizeof(struct enum_type))) == NULL) { syslog(LOG_ERR, "malloc() failed: %s", strerror(errno)); return (NULL); } if ((enum_tc->name = strdup(name)) == NULL) { syslog(LOG_ERR, "malloc() failed: %s", strerror(errno)); free(enum_tc); return (NULL); } return (enum_tc); } void snmp_enumtc_free(struct enum_type *tc) { if (tc->name) free(tc->name); if (tc->snmp_enum) enum_pairs_free(tc->snmp_enum); free(tc); } void snmp_enumtc_insert(struct snmp_toolinfo *snmptoolctx, struct enum_type *entry) { if (snmptoolctx == NULL || snmptoolctx->mappings == NULL) return; /* XXX no error handling? */ SLIST_INSERT_HEAD(&snmptoolctx->snmp_tclist, entry, link); } struct enum_type * snmp_enumtc_lookup(struct snmp_toolinfo *snmptoolctx, char *name) { struct enum_type *temp; if (snmptoolctx == NULL || snmptoolctx->mappings == NULL) return (NULL); SLIST_FOREACH(temp, &snmptoolctx->snmp_tclist, link) { if (strcmp(temp->name, name) == 0) return (temp); } return (NULL); } static void snmp_mapping_dumplist(struct snmp_mapping *headp) { char buf[ASN_OIDSTRLEN]; struct snmp_oid2str *entry; if (headp == NULL) return; SLIST_FOREACH(entry,headp,link) { memset(buf, 0, sizeof(buf)); asn_oid2str_r(&(entry->var), buf); fprintf(stderr, "%s - %s - %d - %d - %d", buf, entry->string, entry->syntax, entry->access ,entry->strlen); fprintf(stderr," - %s \n", (entry->table_idx == NULL)? "No table":entry->table_idx->string); } } static void snmp_mapping_dumptable(struct snmp_table_index *headp) { char buf[ASN_OIDSTRLEN]; struct snmp_index_entry *entry; if (headp == NULL) return; SLIST_FOREACH(entry, headp, link) { memset(buf, 0, sizeof(buf)); asn_oid2str_r(&(entry->var), buf); fprintf(stderr,"%s - %s - %d - ", buf, entry->string, entry->strlen); snmp_dump_indexlist(&(entry->index_list)); } } -void +void snmp_mapping_dump(struct snmp_toolinfo *snmptoolctx /* int bits */) { if (!_bsnmptools_debug) return; if (snmptoolctx == NULL) { fprintf(stderr,"No snmptool context!\n"); return; } if (snmptoolctx->mappings == NULL) { fprintf(stderr,"No mappings!\n"); return; } fprintf(stderr,"snmp_nodelist:\n"); snmp_mapping_dumplist(&snmptoolctx->snmp_nodelist); fprintf(stderr,"snmp_intlist:\n"); snmp_mapping_dumplist(&snmptoolctx->snmp_intlist); fprintf(stderr,"snmp_octlist:\n"); snmp_mapping_dumplist(&snmptoolctx->snmp_octlist); fprintf(stderr,"snmp_oidlist:\n"); snmp_mapping_dumplist(&snmptoolctx->snmp_oidlist); fprintf(stderr,"snmp_iplist:\n"); snmp_mapping_dumplist(&snmptoolctx->snmp_iplist); fprintf(stderr,"snmp_ticklist:\n"); snmp_mapping_dumplist(&snmptoolctx->snmp_ticklist); fprintf(stderr,"snmp_cntlist:\n"); snmp_mapping_dumplist(&snmptoolctx->snmp_cntlist); fprintf(stderr,"snmp_gaugelist:\n"); snmp_mapping_dumplist(&snmptoolctx->snmp_gaugelist); fprintf(stderr,"snmp_cnt64list:\n"); snmp_mapping_dumplist(&snmptoolctx->snmp_cnt64list); fprintf(stderr,"snmp_enumlist:\n"); snmp_mapping_dumplist(&snmptoolctx->snmp_enumlist); fprintf(stderr,"snmp_tablelist:\n"); snmp_mapping_dumptable(&snmptoolctx->snmp_tablelist); } char * enum_string_lookup(struct enum_pairs *headp, int32_t enum_val) { struct enum_pair *temp; if (headp == NULL) return (NULL); STAILQ_FOREACH(temp, headp, link) { if (temp->enum_val == enum_val) return (temp->enum_str); } return (NULL); } int32_t enum_number_lookup(struct enum_pairs *headp, char *e_str) { struct enum_pair *tmp; if (headp == NULL) return (-1); STAILQ_FOREACH(tmp, headp, link) if (strncmp(tmp->enum_str, e_str, strlen(tmp->enum_str)) == 0) return (tmp->enum_val); return (-1); } static int32_t snmp_lookuplist_string(struct snmp_mapping *headp, struct snmp_object *s) { struct snmp_oid2str *temp; if (headp == NULL) return (-1); SLIST_FOREACH(temp, headp, link) if (asn_compare_oid(&(temp->var), &(s->val.var)) == 0) break; if ((s->info = temp) == NULL) return (-1); return (1); } /* provided an asn_oid find the corresponding string for it */ static int32_t snmp_lookup_leaf(struct snmp_mapping *headp, struct snmp_object *s) { struct snmp_oid2str *temp; if (headp == NULL) return (-1); SLIST_FOREACH(temp,headp,link) { if ((asn_compare_oid(&(temp->var), &(s->val.var)) == 0) || (asn_is_suboid(&(temp->var), &(s->val.var)))) { s->info = temp; return (1); } } return (-1); } int32_t snmp_lookup_leafstring(struct snmp_toolinfo *snmptoolctx, struct snmp_object *s) { if (snmptoolctx == NULL || snmptoolctx->mappings == NULL || s == NULL) return (-1); switch (s->val.syntax) { case SNMP_SYNTAX_INTEGER: return (snmp_lookup_leaf(&snmptoolctx->snmp_intlist, s)); case SNMP_SYNTAX_OCTETSTRING: return (snmp_lookup_leaf(&snmptoolctx->snmp_octlist, s)); case SNMP_SYNTAX_OID: return (snmp_lookup_leaf(&snmptoolctx->snmp_oidlist, s)); case SNMP_SYNTAX_IPADDRESS: return (snmp_lookup_leaf(&snmptoolctx->snmp_iplist, s)); case SNMP_SYNTAX_COUNTER: return (snmp_lookup_leaf(&snmptoolctx->snmp_cntlist, s)); case SNMP_SYNTAX_GAUGE: return (snmp_lookup_leaf( &snmptoolctx->snmp_gaugelist, s)); case SNMP_SYNTAX_TIMETICKS: return (snmp_lookup_leaf( &snmptoolctx->snmp_ticklist, s)); case SNMP_SYNTAX_COUNTER64: return (snmp_lookup_leaf( &snmptoolctx->snmp_cnt64list, s)); case SNMP_SYNTAX_NOSUCHOBJECT: /* FALLTHROUGH */ case SNMP_SYNTAX_NOSUCHINSTANCE: /* FALLTHROUGH */ case SNMP_SYNTAX_ENDOFMIBVIEW: return (snmp_lookup_allstring(snmptoolctx, s)); default: warnx("Unknown syntax - %d", s->val.syntax); break; } return (-1); } int32_t snmp_lookup_enumstring(struct snmp_toolinfo *snmptoolctx, struct snmp_object *s) { if (snmptoolctx == NULL || snmptoolctx->mappings == NULL || s == NULL) return (-1); return (snmp_lookuplist_string(&snmptoolctx->snmp_enumlist, s)); } int32_t snmp_lookup_oidstring(struct snmp_toolinfo *snmptoolctx, struct snmp_object *s) { if (snmptoolctx == NULL || snmptoolctx->mappings == NULL || s == NULL) return (-1); return (snmp_lookuplist_string(&snmptoolctx->snmp_oidlist, s)); } int32_t snmp_lookup_nodestring(struct snmp_toolinfo *snmptoolctx, struct snmp_object *s) { if (snmptoolctx == NULL || snmptoolctx->mappings == NULL || s == NULL) return (-1); return (snmp_lookuplist_string(&snmptoolctx->snmp_nodelist, s)); } int32_t snmp_lookup_allstring(struct snmp_toolinfo *snmptoolctx, struct snmp_object *s) { if (snmptoolctx == NULL || snmptoolctx->mappings == NULL) return (-1); if (snmp_lookup_leaf(&snmptoolctx->snmp_intlist, s) > 0) return (1); if (snmp_lookup_leaf(&snmptoolctx->snmp_octlist, s) > 0) return (1); if (snmp_lookup_leaf(&snmptoolctx->snmp_oidlist, s) > 0) return (1); if (snmp_lookup_leaf(&snmptoolctx->snmp_iplist, s) > 0) return (1); if (snmp_lookup_leaf(&snmptoolctx->snmp_cntlist, s) > 0) return (1); if (snmp_lookup_leaf(&snmptoolctx->snmp_gaugelist, s) > 0) return (1); if (snmp_lookup_leaf(&snmptoolctx->snmp_ticklist, s) > 0) return (1); if (snmp_lookup_leaf(&snmptoolctx->snmp_cnt64list, s) > 0) return (1); if (snmp_lookuplist_string(&snmptoolctx->snmp_enumlist, s) > 0) return (1); if (snmp_lookuplist_string(&snmptoolctx->snmp_nodelist, s) > 0) return (1); return (-1); } int32_t snmp_lookup_nonleaf_string(struct snmp_toolinfo *snmptoolctx, struct snmp_object *s) { if (snmptoolctx == NULL) return (-1); if (snmp_lookuplist_string(&snmptoolctx->snmp_nodelist, s) > 0) return (1); if (snmp_lookuplist_string(&snmptoolctx->snmp_enumlist, s) > 0) return (1); return (-1); } static int32_t snmp_lookup_oidlist(struct snmp_mapping *hp, struct snmp_object *s, char *oid) { struct snmp_oid2str *temp; if (hp == NULL) return (-1); SLIST_FOREACH(temp, hp, link) { if (temp->strlen != strlen(oid)) continue; if (strncmp(temp->string, oid, temp->strlen)) continue; s->val.syntax = temp->syntax; s->info = temp; asn_append_oid(&(s->val.var), &(temp->var)); return (1); } return (-1); } static int32_t snmp_lookup_tablelist(struct snmp_toolinfo *snmptoolctx, struct snmp_table_index *headp, struct snmp_object *s, char *oid) { struct snmp_index_entry *temp; if (snmptoolctx == NULL || headp == NULL) return (-1); SLIST_FOREACH(temp, headp, link) { if (temp->strlen != strlen(oid)) continue; if (strncmp(temp->string, oid, temp->strlen)) continue; /* * Another hack here - if we were given a table name * return the corresponding pointer to it's entry. * That should not change the reponce we'll get. */ s->val.syntax = SNMP_SYNTAX_NULL; asn_append_oid(&(s->val.var), &(temp->var)); if (snmp_lookup_leaf(&snmptoolctx->snmp_nodelist, s) > 0) return (1); else return (-1); } return (-1); } int32_t snmp_lookup_oidall(struct snmp_toolinfo *snmptoolctx, struct snmp_object *s, char *oid) { if (snmptoolctx == NULL || s == NULL || oid == NULL) return (-1); if (snmp_lookup_oidlist(&snmptoolctx->snmp_intlist, s, oid) > 0) return (1); if (snmp_lookup_oidlist(&snmptoolctx->snmp_octlist, s, oid) > 0) return (1); if (snmp_lookup_oidlist(&snmptoolctx->snmp_oidlist, s, oid) > 0) return (1); if (snmp_lookup_oidlist(&snmptoolctx->snmp_iplist, s, oid) > 0) return (1); if (snmp_lookup_oidlist(&snmptoolctx->snmp_ticklist, s, oid) > 0) return (1); if (snmp_lookup_oidlist(&snmptoolctx->snmp_cntlist, s, oid) > 0) return (1); if (snmp_lookup_oidlist(&snmptoolctx->snmp_gaugelist, s, oid) > 0) return (1); if (snmp_lookup_oidlist(&snmptoolctx->snmp_cnt64list, s, oid) > 0) return (1); if (snmp_lookup_oidlist(&snmptoolctx->snmp_nodelist, s, oid) > 0) return (1); if (snmp_lookup_tablelist(snmptoolctx, &snmptoolctx->snmp_tablelist, s, oid) > 0) return (1); return (-1); } int32_t snmp_lookup_enumoid(struct snmp_toolinfo *snmptoolctx, struct snmp_object *s, char *oid) { if (snmptoolctx == NULL || s == NULL) return (-1); return (snmp_lookup_oidlist(&snmptoolctx->snmp_enumlist, s, oid)); } int32_t snmp_lookup_oid(struct snmp_toolinfo *snmptoolctx, struct snmp_object *s, char *oid) { if (snmptoolctx == NULL || s == NULL) return (-1); switch (s->val.syntax) { case SNMP_SYNTAX_INTEGER: return (snmp_lookup_oidlist(&snmptoolctx->snmp_intlist, s, oid)); case SNMP_SYNTAX_OCTETSTRING: return (snmp_lookup_oidlist(&snmptoolctx->snmp_octlist, s, oid)); case SNMP_SYNTAX_OID: return (snmp_lookup_oidlist(&snmptoolctx->snmp_oidlist, s, oid)); case SNMP_SYNTAX_IPADDRESS: return (snmp_lookup_oidlist(&snmptoolctx->snmp_iplist, s, oid)); case SNMP_SYNTAX_COUNTER: return (snmp_lookup_oidlist(&snmptoolctx->snmp_cntlist, s, oid)); case SNMP_SYNTAX_GAUGE: return (snmp_lookup_oidlist(&snmptoolctx->snmp_gaugelist, s, oid)); case SNMP_SYNTAX_TIMETICKS: return (snmp_lookup_oidlist(&snmptoolctx->snmp_ticklist, s, oid)); case SNMP_SYNTAX_COUNTER64: return (snmp_lookup_oidlist(&snmptoolctx->snmp_cnt64list, s, oid)); case SNMP_SYNTAX_NULL: return (snmp_lookup_oidlist(&snmptoolctx->snmp_nodelist, s, oid)); default: warnx("Unknown syntax - %d", s->val.syntax); break; } return (-1); } Index: head/usr.sbin/bsnmpd/tools/libbsnmptools/bsnmptc.c =================================================================== --- head/usr.sbin/bsnmpd/tools/libbsnmptools/bsnmptc.c (revision 310647) +++ head/usr.sbin/bsnmpd/tools/libbsnmptools/bsnmptc.c (revision 310648) @@ -1,1299 +1,1299 @@ /*- * Copyright (c) 2006 The FreeBSD Project * All rights reserved. * * Author: Shteryana Shopova * * 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 IS PROVIDED BY THE 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 THE 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. * * Textual conventions for OctetStrings * * $FreeBSD$ */ -#include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "bsnmptc.h" #include "bsnmptools.h" /* OctetString, DisplayString */ static char *snmp_oct2str(uint32_t, char *, char *); static char *snmp_str2asn_oid(char *, struct asn_oid *); static int parse_octetstring(struct snmp_value *, char *); /* DateAndTime */ static char *snmp_octstr2date(uint32_t, char *, char *); static char *snmp_date2asn_oid(char * , struct asn_oid *); static int parse_dateandtime(struct snmp_value *, char *); /* PhysAddress */ static char *snmp_oct2physAddr(uint32_t, char *, char *); static char *snmp_addr2asn_oid(char *, struct asn_oid *); static int parse_physaddress(struct snmp_value *, char *); /* NTPTimeStamp */ static char *snmp_oct2ntp_ts(uint32_t, char *, char *); static char *snmp_ntp_ts2asn_oid(char *, struct asn_oid *); static int parse_ntp_ts(struct snmp_value *, char *); /* BridgeId */ static char *snmp_oct2bridgeid(uint32_t, char *, char *); static char *snmp_bridgeid2oct(char *, struct asn_oid *); static int parse_bridge_id(struct snmp_value *, char *); /* BridgePortId */ static char *snmp_oct2bport_id(uint32_t, char *, char *); static char *snmp_bport_id2oct(char *, struct asn_oid *); static int parse_bport_id(struct snmp_value *, char *); /* InetAddress */ static char *snmp_oct2inetaddr(uint32_t len, char *octets, char *buf); static char *snmp_inetaddr2oct(char *str, struct asn_oid *oid); static int32_t parse_inetaddr(struct snmp_value *value, char *string); static char *snmp_oct2bits(uint32_t len, char *octets, char *buf); static char *snmp_bits2oct(char *str, struct asn_oid *oid); static int32_t parse_bits(struct snmp_value *value, char *string); static struct snmp_text_conv { enum snmp_tc tc; const char *tc_str; int32_t len; snmp_oct2tc_f oct2tc; snmp_tc2oid_f tc2oid; snmp_tc2oct_f tc2oct; } text_convs[] = { { SNMP_STRING, "OctetString", SNMP_VAR_STRSZ, snmp_oct2str, snmp_str2asn_oid, parse_octetstring }, { SNMP_DISPLAYSTRING, "DisplayString" , SNMP_VAR_STRSZ, snmp_oct2str, snmp_str2asn_oid, parse_octetstring }, { SNMP_DATEANDTIME, "DateAndTime", SNMP_DATETIME_STRSZ, snmp_octstr2date, snmp_date2asn_oid, parse_dateandtime }, { SNMP_PHYSADDR, "PhysAddress", SNMP_PHYSADDR_STRSZ, snmp_oct2physAddr, snmp_addr2asn_oid, parse_physaddress }, { SNMP_ATMESI, "AtmESI", SNMP_PHYSADDR_STRSZ, snmp_oct2physAddr, snmp_addr2asn_oid, parse_physaddress }, { SNMP_NTP_TIMESTAMP, "NTPTimeStamp", SNMP_NTP_TS_STRSZ, snmp_oct2ntp_ts, snmp_ntp_ts2asn_oid, parse_ntp_ts }, { SNMP_MACADDRESS, "MacAddress", SNMP_PHYSADDR_STRSZ, snmp_oct2physAddr, snmp_addr2asn_oid, parse_physaddress }, { SNMP_BRIDGE_ID, "BridgeId", SNMP_BRIDGEID_STRSZ, snmp_oct2bridgeid, snmp_bridgeid2oct, parse_bridge_id }, { SNMP_BPORT_ID, "BridgePortId", SNMP_BPORT_STRSZ, snmp_oct2bport_id, snmp_bport_id2oct, parse_bport_id }, { SNMP_INETADDRESS, "InetAddress", SNMP_INADDRS_STRSZ, snmp_oct2inetaddr, snmp_inetaddr2oct, parse_inetaddr }, { SNMP_TC_OWN, "BITS", SNMP_VAR_STRSZ, snmp_oct2bits, snmp_bits2oct, parse_bits }, { SNMP_UNKNOWN, "Unknown", SNMP_VAR_STRSZ, snmp_oct2str, snmp_str2asn_oid, parse_octetstring } /* keep last */ }; /* Common API */ enum snmp_tc snmp_get_tc(char *str) { int i; for (i = 0; i < SNMP_UNKNOWN; i++) { if (!strncmp(text_convs[i].tc_str, str, strlen(text_convs[i].tc_str))) return (text_convs[i].tc); } return (SNMP_STRING); } char * snmp_oct2tc(enum snmp_tc tc, uint32_t len, char *octets) { uint32_t tc_len; char * buf; if (tc > SNMP_UNKNOWN) tc = SNMP_UNKNOWN; if (text_convs[tc].len > 0) tc_len = text_convs[tc].len; else tc_len = 2 * len + 3; if ((buf = malloc(tc_len)) == NULL ) { syslog(LOG_ERR, "malloc failed - %s", strerror(errno)); return (NULL); } memset(buf, 0, tc_len); if (text_convs[tc].oct2tc(len, octets, buf) == NULL) { free(buf); return (NULL); } return (buf); } char * snmp_tc2oid(enum snmp_tc tc, char *str, struct asn_oid *oid) { if (tc > SNMP_UNKNOWN) tc = SNMP_UNKNOWN; return (text_convs[tc].tc2oid(str, oid)); } int32_t snmp_tc2oct(enum snmp_tc tc, struct snmp_value *value, char *string) { if (tc > SNMP_UNKNOWN) tc = SNMP_UNKNOWN; return (text_convs[tc].tc2oct(value, string)); } /***************************************************** * Basic OctetString type. */ static char * snmp_oct2str(uint32_t len, char *octets, char *buf) { uint8_t binary = 0; uint32_t i; char *ptr; if (len > MAX_OCTSTRING_LEN || octets == NULL || buf == NULL) return (NULL); for (ptr = buf, i = 0; i < len; i++) if (!isprint(octets[i])) { binary = 1; buf += sprintf(buf, "0x"); break; } for (ptr = buf, i = 0; i < len; i++) if (!binary) ptr += sprintf(ptr, "%c", octets[i]); else ptr += sprintf(ptr, "%2.2x", (u_char)octets[i]); return (buf); } static char * snmp_str2asn_oid(char *str, struct asn_oid *oid) { uint32_t i, len = 0; /* * OctetStrings are allowed max length of ASN_MAXOCTETSTRING, * but trying to index an entry with such a long OctetString * will fail anyway. */ for (len = 0; len < ASN_MAXOIDLEN; len++) { if (strchr(",]", *(str + len)) != NULL) break; } if (len >= ASN_MAXOIDLEN) return (NULL); if (snmp_suboid_append(oid, (asn_subid_t) len) < 0) return (NULL); for (i = 0; i < len; i++) if (snmp_suboid_append(oid, (asn_subid_t) *(str + i)) < 0) return (NULL); return (str + len); } static int32_t parse_octetstring(struct snmp_value *value, char *val) { size_t len; if ((len = strlen(val)) >= MAX_OCTSTRING_LEN) { warnx("Octetstring too long - %d is max allowed", MAX_OCTSTRING_LEN - 1); return (-1); } value->v.octetstring.len = len; if((value->v.octetstring.octets = malloc(len)) == NULL) { syslog(LOG_ERR, "malloc failed: %s", strerror(errno)); return (-1); } memcpy(value->v.octetstring.octets, val, len); value->syntax = SNMP_SYNTAX_OCTETSTRING; return (0); } /************************************************************* * DateAndTime ************************************************************* * rfc 2579 specification: * DateAndTime ::= TEXTUAL-CONVENTION * DISPLAY-HINT "2d-1d-1d,1d:1d:1d.1d,1a1d:1d" * STATUS current * DESCRIPTION * "A date-time specification. * * field octets contents range * ----- ------ -------- ----- * 1 1-2 year* 0..65536 * 2 3 month 1..12 * 3 4 day 1..31 * 4 5 hour 0..23 * 5 6 minutes 0..59 * 6 7 seconds 0..60 * (use 60 for leap-second) * 7 8 deci-seconds 0..9 * 8 9 direction from UTC '+' / '-' * 9 10 hours from UTC* 0..13 * 10 11 minutes from UTC 0..59 * * * Notes: * - the value of year is in network-byte order * - daylight saving time in New Zealand is +13 * * For example, Tuesday May 26, 1992 at 1:30:15 PM EDT would be * displayed as: * * 1992-5-26,13:30:15.0,-4:0 */ static char * snmp_octstr2date(uint32_t len, char *octets, char *buf) { int year; char *ptr; if (len != SNMP_DATETIME_OCTETS || octets == NULL || buf == NULL) return (NULL); buf[0]= '\0'; year = (octets[0] << 8); year += (octets[1]); ptr = buf; ptr += sprintf(ptr, "%4.4d-%.2d-%.2d, ", year, octets[2],octets[3]); ptr += sprintf(ptr, "%2.2d:%2.2d:%2.2d.%.2d, ", octets[4],octets[5], octets[6],octets[7]); ptr += sprintf(ptr, "%c%.2d:%.2d", octets[8],octets[9],octets[10]); return (buf); } static char * snmp_date2asn_oid(char *str, struct asn_oid *oid) { char *endptr, *ptr; static const char UTC[3] = "UTC"; int32_t saved_errno; uint32_t v; if (snmp_suboid_append(oid, (asn_subid_t) SNMP_DATETIME_OCTETS) < 0) return (NULL); /* Read 'YYYY-' and write it in two subs. */ ptr = str; saved_errno = errno; errno = 0; v = strtoul(ptr, &endptr, 10); if (v > 0xffff) goto error; else errno = saved_errno; if (*endptr != '-') goto error1; if (snmp_suboid_append(oid, (asn_subid_t) ((v & 0xff00) >> 8)) < 0) return (NULL); if (snmp_suboid_append(oid, (asn_subid_t) (v & 0xff)) < 0) return (NULL); /* 'MM-' */ ptr = endptr + 1; saved_errno = errno; errno = 0; v = strtoul(ptr, &endptr, 10); if (errno != 0) goto error; else errno = saved_errno; if (*endptr != '-') goto error1; if (snmp_suboid_append(oid, (asn_subid_t) v) < 0) return (NULL); /* 'DD,' */ ptr = endptr + 1; saved_errno = errno; errno = 0; v = strtoul(ptr, &endptr, 10); if (errno != 0) goto error; else errno = saved_errno; if (*endptr != '-') goto error1; if (snmp_suboid_append(oid, (asn_subid_t) v) < 0) return (NULL); /* 'HH:' */ ptr = endptr + 1; saved_errno = errno; errno = 0; v = strtoul(ptr, &endptr, 10); if (errno != 0) goto error; else errno = saved_errno; if (*endptr != ':') goto error1; if (snmp_suboid_append(oid, (asn_subid_t) v) < 0) return (NULL); /* 'MM:' */ ptr = endptr + 1; saved_errno = errno; errno = 0; v = strtoul(ptr, &endptr, 10); if (errno != 0) goto error; else errno = saved_errno; if (*endptr != ':') goto error1; if (snmp_suboid_append(oid, (asn_subid_t) v) < 0) return (NULL); /* 'SS.' */ ptr = endptr + 1; saved_errno = errno; errno = 0; v = strtoul(ptr, &endptr, 10); if (errno != 0) goto error; else errno = saved_errno; if (*endptr != '.') goto error1; if (snmp_suboid_append(oid, (asn_subid_t) v) < 0) return (NULL); /* 'M(mseconds),' */ ptr = endptr + 1; saved_errno = errno; errno = 0; v = strtoul(ptr, &endptr, 10); if (errno != 0) goto error; else errno = saved_errno; if (*endptr != ',') goto error1; if (snmp_suboid_append(oid, (asn_subid_t) v) < 0) return (NULL); /* 'UTC' - optional */ ptr = endptr + 1; if (strncmp(ptr, UTC, sizeof(UTC)) == 0) ptr += sizeof(UTC); /* '+/-' */ if (*ptr == '-' || *ptr == '+') { if (snmp_suboid_append(oid, (asn_subid_t) (*ptr)) < 0) return (NULL); } else goto error1; /* 'HH:' */ ptr = endptr + 1; saved_errno = errno; errno = 0; v = strtoul(ptr, &endptr, 10); if (errno != 0) goto error; else errno = saved_errno; if (*endptr != ':') goto error1; if (snmp_suboid_append(oid, (asn_subid_t) v) < 0) return (NULL); /* 'MM' - last one - ignore endptr here. */ ptr = endptr + 1; saved_errno = errno; errno = 0; v = strtoul(ptr, &endptr, 10); if (errno != 0) goto error; else errno = saved_errno; if (snmp_suboid_append(oid, (asn_subid_t) v) < 0) return (NULL); return (endptr); error: errno = saved_errno; error1: warnx("Date value %s not supported", str); return (NULL); } /* Read a DateAndTime string eg. 1992-5-26,13:30:15.0,-4:0. */ static int32_t parse_dateandtime(struct snmp_value *sv, char *val) { char *endptr; uint32_t v; uint8_t date[SNMP_DATETIME_OCTETS]; /* 'YYYY-' */ v = strtoul(val, &endptr, 10); if (v > 0xffff || *endptr != '-') goto error; date[0] = ((v & 0xff00) >> 8); date[1] = (v & 0xff); val = endptr + 1; /* 'MM-' */ v = strtoul(val, &endptr, 10); if (v == 0 || v > 12 || *endptr != '-') goto error; date[2] = v; val = endptr + 1; /* 'DD,' */ v = strtoul(val, &endptr, 10); if (v == 0 || v > 31 || *endptr != ',') goto error; date[3] = v; val = endptr + 1; /* 'HH:' */ v = strtoul(val, &endptr, 10); if (v > 23 || *endptr != ':') goto error; date[4] = v; val = endptr + 1; /* 'MM:' */ v = strtoul(val, &endptr, 10); if (v > 59 || *endptr != ':') goto error; date[5] = v; val = endptr + 1; /* 'SS.' */ v = strtoul(val, &endptr, 10); if (v > 60 || *endptr != '.') goto error; date[6] = v; val = endptr + 1; /* '(deci-)s,' */ v = strtoul(val, &endptr, 10); if (v > 9 || *endptr != ',') goto error; date[7] = v; val = endptr + 1; /* offset - '+/-' */ if (*val != '-' && *val != '+') goto error; date[8] = (uint8_t) *val; val = endptr + 1; /* 'HH:' - offset from UTC */ v = strtoul(val, &endptr, 10); if (v > 13 || *endptr != ':') goto error; date[9] = v; val = endptr + 1; /* 'MM'\0'' offset from UTC */ v = strtoul(val, &endptr, 10); if (v > 59 || *endptr != '\0') goto error; date[10] = v; if ((sv->v.octetstring.octets = malloc(SNMP_DATETIME_OCTETS)) == NULL) { warn("malloc() failed"); return (-1); } sv->v.octetstring.len = SNMP_DATETIME_OCTETS; memcpy(sv->v.octetstring.octets, date, SNMP_DATETIME_OCTETS); sv->syntax = SNMP_SYNTAX_OCTETSTRING; return (1); error: warnx("Date value %s not supported", val); return (-1); } /************************************************************** * PhysAddress */ static char * snmp_oct2physAddr(uint32_t len, char *octets, char *buf) { char *ptr; uint32_t i; if (len != SNMP_PHYSADDR_OCTETS || octets == NULL || buf == NULL) return (NULL); buf[0]= '\0'; ptr = buf; ptr += sprintf(ptr, "%2.2x", octets[0]); for (i = 1; i < 6; i++) ptr += sprintf(ptr, ":%2.2x", octets[i]); return (buf); } static char * snmp_addr2asn_oid(char *str, struct asn_oid *oid) { char *endptr, *ptr; uint32_t v, i; int saved_errno; if (snmp_suboid_append(oid, (asn_subid_t) SNMP_PHYSADDR_OCTETS) < 0) return (NULL); ptr = str; for (i = 0; i < 5; i++) { saved_errno = errno; v = strtoul(ptr, &endptr, 16); errno = saved_errno; if (v > 0xff) { warnx("Integer value %s not supported", str); return (NULL); } if (*endptr != ':') { warnx("Failed adding oid - %s", str); return (NULL); } if (snmp_suboid_append(oid, (asn_subid_t) v) < 0) return (NULL); ptr = endptr + 1; } /* The last one - don't check the ending char here. */ saved_errno = errno; v = strtoul(ptr, &endptr, 16); errno = saved_errno; if (v > 0xff) { warnx("Integer value %s not supported", str); return (NULL); } if (snmp_suboid_append(oid, (asn_subid_t) v) < 0) return (NULL); return (endptr); } static int32_t parse_physaddress(struct snmp_value *sv, char *val) { char *endptr; int32_t i; uint32_t v; uint8_t phys_addr[SNMP_PHYSADDR_OCTETS]; for (i = 0; i < 5; i++) { v = strtoul(val, &endptr, 16); if (v > 0xff) { warnx("Integer value %s not supported", val); return (-1); } if(*endptr != ':') { warnx("Failed reading octet - %s", val); return (-1); } phys_addr[i] = v; val = endptr + 1; } /* The last one - don't check the ending char here. */ v = strtoul(val, &endptr, 16); if (v > 0xff) { warnx("Integer value %s not supported", val); return (-1); } phys_addr[5] = v; if ((sv->v.octetstring.octets = malloc(SNMP_PHYSADDR_OCTETS)) == NULL) { syslog(LOG_ERR, "malloc failed: %s", strerror(errno)); return (-1); } sv->v.octetstring.len = SNMP_PHYSADDR_OCTETS; memcpy(sv->v.octetstring.octets, phys_addr, SNMP_PHYSADDR_OCTETS); sv->syntax = SNMP_SYNTAX_OCTETSTRING; return (1); } /************************************************************** * NTPTimeStamp ************************************************************** * NTP MIB, Revision 0.2, 7/25/97: * NTPTimeStamp ::= TEXTUAL-CONVENTION * DISPLAY-HINT "4x.4x" * STATUS current * DESCRIPTION * "" * SYNTAX OCTET STRING (SIZE(8)) */ static char * snmp_oct2ntp_ts(uint32_t len, char *octets, char *buf) { char *ptr; uint32_t i; if (len != SNMP_NTP_TS_OCTETS || octets == NULL || buf == NULL) return (NULL); buf[0]= '\0'; ptr = buf; i = octets[0] * 1000 + octets[1] * 100 + octets[2] * 10 + octets[3]; ptr += sprintf(ptr, "%4.4d", i); i = octets[4] * 1000 + octets[5] * 100 + octets[6] * 10 + octets[7]; ptr += sprintf(ptr, ".%4.4d", i); return (buf); } static char * snmp_ntp_ts2asn_oid(char *str, struct asn_oid *oid) { char *endptr, *ptr; uint32_t v, i, d; struct asn_oid suboid; int saved_errno; if (snmp_suboid_append(oid, (asn_subid_t) SNMP_NTP_TS_OCTETS) < 0) return (NULL); ptr = str; saved_errno = errno; errno = 0; v = strtoul(ptr, &endptr, 10); if (errno != 0 || (v / 1000) > 9) { warnx("Integer value %s not supported", str); errno = saved_errno; return (NULL); } else errno = saved_errno; if (*endptr != '.') { warnx("Failed adding oid - %s", str); return (NULL); } memset(&suboid, 0, sizeof(struct asn_oid)); suboid.len = SNMP_NTP_TS_OCTETS; for (i = 0, d = 1000; i < 4; i++) { suboid.subs[i] = v / d; v = v % d; d = d / 10; } ptr = endptr + 1; saved_errno = errno; errno = 0; v = strtoul(ptr, &endptr, 10); if (errno != 0 || (v / 1000) > 9) { warnx("Integer value %s not supported", str); errno = saved_errno; return (NULL); } else errno = saved_errno; for (i = 0, d = 1000; i < 4; i++) { suboid.subs[i + 4] = v / d; v = v % d; d = d / 10; } asn_append_oid(oid, &suboid); return (endptr); } static int32_t parse_ntp_ts(struct snmp_value *sv, char *val) { char *endptr; int32_t i, d, saved_errno; uint32_t v; uint8_t ntp_ts[SNMP_NTP_TS_OCTETS]; saved_errno = errno; errno = 0; v = strtoul(val, &endptr, 10); if (errno != 0 || (v / 1000) > 9) { errno = saved_errno; warnx("Integer value %s not supported", val); return (-1); } else errno = saved_errno; if (*endptr != '.') { warnx("Failed reading octet - %s", val); return (-1); } for (i = 0, d = 1000; i < 4; i++) { ntp_ts[i] = v / d; v = v % d; d = d / 10; } val = endptr + 1; saved_errno = errno; errno = 0; v = strtoul(val, &endptr, 10); if (errno != 0 || (v / 1000) > 9) { errno = saved_errno; warnx("Integer value %s not supported", val); return (-1); } else errno = saved_errno; for (i = 0, d = 1000; i < 4; i++) { ntp_ts[i + 4] = v / d; v = v % d; d = d / 10; } if ((sv->v.octetstring.octets = malloc(SNMP_NTP_TS_OCTETS)) == NULL) { syslog(LOG_ERR, "malloc failed: %s", strerror(errno)); return (-1); } sv->v.octetstring.len = SNMP_NTP_TS_OCTETS; memcpy(sv->v.octetstring.octets, ntp_ts, SNMP_NTP_TS_OCTETS); sv->syntax = SNMP_SYNTAX_OCTETSTRING; return (1); } /************************************************************** * BridgeId ************************************************************** * BRIDGE-MIB, REVISION "200509190000Z" * BridgeId ::= TEXTUAL-CONVENTION * STATUS current * DESCRIPTION * "The Bridge-Identifier, as used in the Spanning Tree * Protocol, to uniquely identify a bridge. Its first two * octets (in network byte order) contain a priority value, * and its last 6 octets contain the MAC address used to * refer to a bridge in a unique fashion (typically, the * numerically smallest MAC address of all ports on the * bridge)." * SYNTAX OCTET STRING (SIZE (8)) */ static char * snmp_oct2bridgeid(uint32_t len, char *octets, char *buf) { char *ptr; uint32_t i, priority; if (len != SNMP_BRIDGEID_OCTETS || octets == NULL || buf == NULL) return (NULL); buf[0]= '\0'; ptr = buf; priority = octets[0] << 8; priority += octets[1]; if (priority > SNMP_MAX_BRIDGE_PRIORITY) { warnx("Invalid bridge priority %d", priority); return (NULL); } else ptr += sprintf(ptr, "%d.", octets[0]); ptr += sprintf(ptr, "%2.2x", octets[2]); for (i = 1; i < 6; i++) ptr += sprintf(ptr, ":%2.2x", octets[i + 2]); return (buf); } static char * snmp_bridgeid2oct(char *str, struct asn_oid *oid) { char *endptr, *ptr; uint32_t v, i; int32_t saved_errno; if (snmp_suboid_append(oid, (asn_subid_t) SNMP_BRIDGEID_OCTETS) < 0) return (NULL); ptr = str; /* Read the priority. */ saved_errno = errno; errno = 0; v = strtoul(ptr, &endptr, 10); if (v > SNMP_MAX_BRIDGE_PRIORITY || errno != 0 || *endptr != '.') { errno = saved_errno; warnx("Bad bridge priority value %d", v); return (NULL); } if (snmp_suboid_append(oid, (asn_subid_t) (v & 0xff00)) < 0) return (NULL); if (snmp_suboid_append(oid, (asn_subid_t) (v & 0xff)) < 0) return (NULL); ptr = endptr + 1; for (i = 0; i < 5; i++) { saved_errno = errno; errno = 0; v = strtoul(ptr, &endptr, 16); errno = saved_errno; if (v > 0xff) { warnx("Integer value %s not supported", str); return (NULL); } if (*endptr != ':') { warnx("Failed adding oid - %s",str); return (NULL); } if (snmp_suboid_append(oid, (asn_subid_t) v) < 0) return (NULL); ptr = endptr + 1; } /* The last one - don't check the ending char here. */ saved_errno = errno; errno = 0; v = strtoul(ptr, &endptr, 16); errno = saved_errno; if (v > 0xff) { warnx("Integer value %s not supported", str); return (NULL); } if (snmp_suboid_append(oid, (asn_subid_t) v) < 0) return (NULL); return (endptr); } static int32_t parse_bridge_id(struct snmp_value *sv, char *string) { char *endptr; int32_t i, saved_errno; uint32_t v; uint8_t bridge_id[SNMP_BRIDGEID_OCTETS]; /* Read the priority. */ saved_errno = errno; errno = 0; v = strtoul(string, &endptr, 10); if (v > SNMP_MAX_BRIDGE_PRIORITY || errno != 0 || *endptr != '.') { errno = saved_errno; warnx("Bad bridge priority value %d", v); return (-1); } bridge_id[0] = (v & 0xff00); bridge_id[1] = (v & 0xff); string = endptr + 1; for (i = 0; i < 5; i++) { v = strtoul(string, &endptr, 16); if (v > 0xff) { warnx("Integer value %s not supported", string); return (-1); } if(*endptr != ':') { warnx("Failed reading octet - %s", string); return (-1); } bridge_id[i + 2] = v; string = endptr + 1; } /* The last one - don't check the ending char here. */ v = strtoul(string, &endptr, 16); if (v > 0xff) { warnx("Integer value %s not supported", string); return (-1); } bridge_id[7] = v; if ((sv->v.octetstring.octets = malloc(SNMP_BRIDGEID_OCTETS)) == NULL) { syslog(LOG_ERR, "malloc failed: %s", strerror(errno)); return (-1); } sv->v.octetstring.len = SNMP_BRIDGEID_OCTETS; memcpy(sv->v.octetstring.octets, bridge_id, SNMP_BRIDGEID_OCTETS); sv->syntax = SNMP_SYNTAX_OCTETSTRING; return (1); } /************************************************************** * BridgePortId ************************************************************** * BEGEMOT-BRIDGE-MIB, LAST-UPDATED "200608100000Z" * BridgePortId ::= TEXTUAL-CONVENTION * DISPLAY-HINT "1x.1x" * STATUS current * DESCRIPTION * "A port identifier that contains a bridge port's STP priority * in the first octet and the port number in the second octet." * SYNTAX OCTET STRING (SIZE(2)) */ static char * snmp_oct2bport_id(uint32_t len, char *octets, char *buf) { char *ptr; if (len != SNMP_BPORT_OCTETS || octets == NULL || buf == NULL) return (NULL); buf[0]= '\0'; ptr = buf; ptr += sprintf(ptr, "%d.", octets[0]); ptr += sprintf(ptr, "%d", octets[1]); return (buf); } static char * snmp_bport_id2oct(char *str, struct asn_oid *oid) { char *endptr, *ptr; uint32_t v; int saved_errno; if (snmp_suboid_append(oid, (asn_subid_t) SNMP_BPORT_OCTETS) < 0) return (NULL); ptr = str; /* Read the priority. */ saved_errno = errno; errno = 0; v = strtoul(ptr, &endptr, 10); if (v > SNMP_MAX_BPORT_PRIORITY || errno != 0 || *endptr != '.') { errno = saved_errno; warnx("Bad bridge port priority value %d", v); return (NULL); } if (snmp_suboid_append(oid, (asn_subid_t) v) < 0) return (NULL); saved_errno = errno; errno = 0; v = strtoul(ptr, &endptr, 16); errno = saved_errno; if (v > 0xff) { warnx("Bad port number - %d", v); return (NULL); } if (snmp_suboid_append(oid, (asn_subid_t) v) < 0) return (NULL); return (endptr); } static int32_t parse_bport_id(struct snmp_value *value, char *string) { char *endptr; int saved_errno; uint32_t v; uint8_t bport_id[SNMP_BPORT_OCTETS]; /* Read the priority. */ saved_errno = errno; errno = 0; v = strtoul(string, &endptr, 10); if (v > SNMP_MAX_BPORT_PRIORITY || errno != 0 || *endptr != '.') { errno = saved_errno; warnx("Bad bridge port priority value %d", v); return (-1); } bport_id[0] = v; string = endptr + 1; v = strtoul(string, &endptr, 16); if (v > 0xff) { warnx("Bad port number - %d", v); return (-1); } bport_id[1] = v; if ((value->v.octetstring.octets = malloc(SNMP_BPORT_OCTETS)) == NULL) { syslog(LOG_ERR, "malloc failed: %s", strerror(errno)); return (-1); } value->v.octetstring.len = SNMP_BPORT_OCTETS; memcpy(value->v.octetstring.octets, bport_id, SNMP_BPORT_OCTETS); value->syntax = SNMP_SYNTAX_OCTETSTRING; return (1); } /************************************************************** * InetAddress ************************************************************** * INET-ADDRESS-MIB, REVISION "200502040000Z" * InetAddress ::= TEXTUAL-CONVENTION * STATUS current * DESCRIPTION * "Denotes a generic Internet address. * * An InetAddress value is always interpreted within the context * of an InetAddressType value. Every usage of the InetAddress * textual convention is required to specify the InetAddressType * object that provides the context. It is suggested that the * InetAddressType object be logically registered before the * object(s) that use the InetAddress textual convention, if * they appear in the same logical row. * * The value of an InetAddress object must always be * consistent with the value of the associated InetAddressType * object. Attempts to set an InetAddress object to a value * inconsistent with the associated InetAddressType * must fail with an inconsistentValue error. * * When this textual convention is used as the syntax of an * index object, there may be issues with the limit of 128 * sub-identifiers specified in SMIv2, STD 58. In this case, * the object definition MUST include a 'SIZE' clause to * limit the number of potential instance sub-identifiers; * otherwise the applicable constraints MUST be stated in * the appropriate conceptual row DESCRIPTION clauses, or * in the surrounding documentation if there is no single * DESCRIPTION clause that is appropriate." * SYNTAX OCTET STRING (SIZE (0..255)) ************************************************************** * TODO: FIXME!!! syrinx: Since we do not support checking the * consistency of a varbinding based on the value of a previous - * one, try to guess the type of address based on the + * one, try to guess the type of address based on the * OctetString SIZE - 4 for IPv4, 16 for IPv6, others currently * not supported. */ static char * snmp_oct2inetaddr(uint32_t len, char *octets, char *buf) { int af; void *ip; struct in_addr ipv4; struct in6_addr ipv6; if (len > MAX_OCTSTRING_LEN || octets == NULL || buf == NULL) return (NULL); switch (len) { /* XXX: FIXME - IPv4*/ case 4: memcpy(&ipv4.s_addr, octets, sizeof(ipv4.s_addr)); af = AF_INET; ip = &ipv4; break; /* XXX: FIXME - IPv4*/ case 16: memcpy(ipv6.s6_addr, octets, sizeof(ipv6.s6_addr)); af = AF_INET6; ip = &ipv6; break; default: return (NULL); } if (inet_ntop(af, ip, buf, SNMP_INADDRS_STRSZ) == NULL) { warn("inet_ntop failed"); return (NULL); } return (buf); } static char * snmp_inetaddr2oct(char *str __unused, struct asn_oid *oid __unused) { return (NULL); } static int32_t parse_inetaddr(struct snmp_value *value __unused, char *string __unused) { return (-1); } /************************************************************** * SNMP BITS type - XXX: FIXME **************************************************************/ static char * snmp_oct2bits(uint32_t len, char *octets, char *buf) { int i, bits; uint64_t value; if (len > sizeof(value) || octets == NULL || buf == NULL) return (NULL); for (i = len, value = 0, bits = 0; i > 0; i--, bits += 8) value += octets[i] << bits; buf[0]= '\0'; sprintf(buf, "0x%llx.",(long long unsigned) value); return (buf); } static char * snmp_bits2oct(char *str, struct asn_oid *oid) { char *endptr; int i, size, bits, saved_errno; uint64_t v, mask = 0xFF00000000000000; saved_errno = errno; errno = 0; v = strtoull(str, &endptr, 16); if (errno != 0) { warn("Bad BITS value %s", str); errno = saved_errno; return (NULL); } bits = 8; /* Determine length - up to 8 octets supported so far. */ for (size = sizeof(v); size > 0; size--) { if ((v & mask) != 0) break; mask = mask >> bits; } if (size == 0) size = 1; if (snmp_suboid_append(oid, (asn_subid_t) size) < 0) return (NULL); for (i = 0, bits = 0; i < size; i++, bits += 8) if (snmp_suboid_append(oid, (asn_subid_t)((v & mask) >> bits)) < 0) return (NULL); return (endptr); } static int32_t parse_bits(struct snmp_value *value, char *string) { char *endptr; int i, size, bits, saved_errno; uint64_t v, mask = 0xFF00000000000000; saved_errno = errno; errno = 0; v = strtoull(string, &endptr, 16); if (errno != 0) { warn("Bad BITS value %s", string); errno = saved_errno; return (-1); } bits = 8; /* Determine length - up to 8 octets supported so far. */ for (size = sizeof(v); size > 0; size--) { if ((v & mask) != 0) break; mask = mask >> bits; } if (size == 0) size = 1; if ((value->v.octetstring.octets = malloc(size)) == NULL) { syslog(LOG_ERR, "malloc failed: %s", strerror(errno)); return (-1); } value->v.octetstring.len = size; for (i = 0, bits = 0; i < size; i++, bits += 8) value->v.octetstring.octets[i] = (v & mask) >> bits; value->syntax = SNMP_SYNTAX_OCTETSTRING; return (1); } Index: head/usr.sbin/bsnmpd/tools/libbsnmptools/bsnmptools.c =================================================================== --- head/usr.sbin/bsnmpd/tools/libbsnmptools/bsnmptools.c (revision 310647) +++ head/usr.sbin/bsnmpd/tools/libbsnmptools/bsnmptools.c (revision 310648) @@ -1,2122 +1,2122 @@ /*- * Copyright (c) 2005-2006 The FreeBSD Project * All rights reserved. * * Author: Shteryana Shopova * * 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 IS PROVIDED BY THE 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 THE 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. * * Helper functions for snmp client tools * * $FreeBSD$ */ -#include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "bsnmptc.h" #include "bsnmptools.h" /* Internal varibale to turn on library debugging for testing and to * find bugs. It is not exported via the header file. * XXX should we cover it by some #ifdef BSNMPTOOLS_DEBUG? */ int _bsnmptools_debug = 0; /* Default files to import mapping from if none explicitly provided. */ #define bsnmpd_defs "/usr/share/snmp/defs/tree.def" #define mibII_defs "/usr/share/snmp/defs/mibII_tree.def" /* * The .iso.org.dod oid that has to be prepended to every OID when requesting * a value. */ const struct asn_oid IsoOrgDod_OID = { 3, { 1, 3, 6 } }; #define SNMP_ERR_UNKNOWN 0 /* * An array of error strings corresponding to error definitions from libbsnmp. */ static const struct { const char *str; int32_t error; } error_strings[] = { { "Unknown", SNMP_ERR_UNKNOWN }, { "Too big ", SNMP_ERR_TOOBIG }, { "No such Name", SNMP_ERR_NOSUCHNAME }, { "Bad Value", SNMP_ERR_BADVALUE }, { "Readonly", SNMP_ERR_READONLY }, { "General error", SNMP_ERR_GENERR }, { "No access", SNMP_ERR_NO_ACCESS }, { "Wrong type", SNMP_ERR_WRONG_TYPE }, { "Wrong length", SNMP_ERR_WRONG_LENGTH }, { "Wrong encoding", SNMP_ERR_WRONG_ENCODING }, { "Wrong value", SNMP_ERR_WRONG_VALUE }, { "No creation", SNMP_ERR_NO_CREATION }, { "Inconsistent value", SNMP_ERR_INCONS_VALUE }, { "Resource unavailable", SNMP_ERR_RES_UNAVAIL }, { "Commit failed", SNMP_ERR_COMMIT_FAILED }, { "Undo failed", SNMP_ERR_UNDO_FAILED }, { "Authorization error", SNMP_ERR_AUTH_ERR }, { "Not writable", SNMP_ERR_NOT_WRITEABLE }, { "Inconsistent name", SNMP_ERR_INCONS_NAME }, { NULL, 0 } }; /* This one and any following are exceptions. */ #define SNMP_SYNTAX_UNKNOWN SNMP_SYNTAX_NOSUCHOBJECT static const struct { const char *str; enum snmp_syntax stx; } syntax_strings[] = { { "Null", SNMP_SYNTAX_NULL }, { "Integer", SNMP_SYNTAX_INTEGER }, { "OctetString", SNMP_SYNTAX_OCTETSTRING }, { "OID", SNMP_SYNTAX_OID }, { "IpAddress", SNMP_SYNTAX_IPADDRESS }, { "Counter32", SNMP_SYNTAX_COUNTER }, { "Gauge", SNMP_SYNTAX_GAUGE }, { "TimeTicks", SNMP_SYNTAX_TIMETICKS }, { "Counter64", SNMP_SYNTAX_COUNTER64 }, - { "Unknown", SNMP_SYNTAX_UNKNOWN }, + { "Unknown", SNMP_SYNTAX_UNKNOWN }, }; int snmptool_init(struct snmp_toolinfo *snmptoolctx) { char *str; size_t slen; memset(snmptoolctx, 0, sizeof(struct snmp_toolinfo)); snmptoolctx->objects = 0; snmptoolctx->mappings = NULL; snmptoolctx->flags = SNMP_PDU_GET; /* XXX */ SLIST_INIT(&snmptoolctx->filelist); snmp_client_init(&snmp_client); SET_MAXREP(snmptoolctx, SNMP_MAX_REPETITIONS); if (add_filename(snmptoolctx, bsnmpd_defs, &IsoOrgDod_OID, 0) < 0) warnx("Error adding file %s to list", bsnmpd_defs); if (add_filename(snmptoolctx, mibII_defs, &IsoOrgDod_OID, 0) < 0) warnx("Error adding file %s to list", mibII_defs); /* Read the environment */ if ((str = getenv("SNMPAUTH")) != NULL) { slen = strlen(str); if (slen == strlen("md5") && strcasecmp(str, "md5") == 0) snmp_client.user.auth_proto = SNMP_AUTH_HMAC_MD5; else if (slen == strlen("sha")&& strcasecmp(str, "sha") == 0) snmp_client.user.auth_proto = SNMP_AUTH_HMAC_SHA; else if (slen != 0) warnx("Bad authentication type - %s in SNMPAUTH", str); } if ((str = getenv("SNMPPRIV")) != NULL) { slen = strlen(str); if (slen == strlen("des") && strcasecmp(str, "des") == 0) snmp_client.user.priv_proto = SNMP_PRIV_DES; else if (slen == strlen("aes")&& strcasecmp(str, "aes") == 0) snmp_client.user.priv_proto = SNMP_PRIV_AES; else if (slen != 0) warnx("Bad privacy type - %s in SNMPPRIV", str); } if ((str = getenv("SNMPUSER")) != NULL) { if ((slen = strlen(str)) > sizeof(snmp_client.user.sec_name)) { warnx("Username too long - %s in SNMPUSER", str); return (-1); } if (slen > 0) { strlcpy(snmp_client.user.sec_name, str, sizeof(snmp_client.user.sec_name)); snmp_client.version = SNMP_V3; } } if ((str = getenv("SNMPPASSWD")) != NULL) { if ((slen = strlen(str)) > MAXSTR) slen = MAXSTR - 1; if ((snmptoolctx->passwd = malloc(slen + 1)) == NULL) { warn("malloc() failed"); return (-1); } if (slen > 0) strlcpy(snmptoolctx->passwd, str, slen + 1); } return (0); } #define OBJECT_IDX_LIST(o) o->info->table_idx->index_list -/* +/* * Walk through the file list and import string<->oid mappings from each file. */ int32_t snmp_import_all(struct snmp_toolinfo *snmptoolctx) { int32_t fc; struct fname *tmp; if (snmptoolctx == NULL) return (-1); if (ISSET_NUMERIC(snmptoolctx)) return (0); if ((snmptoolctx->mappings = snmp_mapping_init()) == NULL) return (-1); fc = 0; if (SLIST_EMPTY(&snmptoolctx->filelist)) { warnx("No files to read OID <-> string conversions from"); return (-1); } else { SLIST_FOREACH(tmp, &snmptoolctx->filelist, link) { if (tmp->done) continue; if (snmp_import_file(snmptoolctx, tmp) < 0) { fc = -1; break; } fc++; } } snmp_mapping_dump(snmptoolctx); return (fc); } /* * Add a filename to the file list - the initial idea of keeping a list with all * files to read OIDs from was that an application might want to have loaded in * memory the OIDs from a single file only and when done with them read the OIDs * from another file. This is not used yet but might be a good idea at some * point. Size argument is number of bytes in string including trailing '\0', * not string length. */ int32_t add_filename(struct snmp_toolinfo *snmptoolctx, const char *filename, const struct asn_oid *cut, int32_t done) { char *fstring; struct fname *entry; if (snmptoolctx == NULL) return (-1); /* Make sure file was not in list. */ SLIST_FOREACH(entry, &snmptoolctx->filelist, link) { if (strncmp(entry->name, filename, strlen(entry->name)) == 0) return (0); } if ((fstring = strdup(filename)) == NULL) { warn("strdup() failed"); return (-1); } if ((entry = calloc(1, sizeof(struct fname))) == NULL) { warn("calloc() failed"); free(fstring); return (-1); } if (cut != NULL) asn_append_oid(&(entry->cut), cut); entry->name = fstring; entry->done = done; SLIST_INSERT_HEAD(&snmptoolctx->filelist, entry, link); return (1); } void free_filelist(struct snmp_toolinfo *snmptoolctx) { struct fname *f; if (snmptoolctx == NULL) return; /* XXX error handling */ while ((f = SLIST_FIRST(&snmptoolctx->filelist)) != NULL) { SLIST_REMOVE_HEAD(&snmptoolctx->filelist, link); if (f->name) free(f->name); free(f); } } -static char +static char isvalid_fchar(char c, int pos) { if (isalpha(c)|| c == '/'|| c == '_' || c == '.' || c == '~' || (pos != 0 && isdigit(c))){ return (c); } if (c == '\0') return (0); if (!isascii(c) || !isprint(c)) warnx("Unexpected character %#2x", (u_int) c); else warnx("Illegal character '%c'", c); return (-1); } /* * Re-implement getsubopt from scratch, because the second argument is broken * and will not compile with WARNS=5. * Copied from src/contrib/bsnmp/snmpd/main.c. */ 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); } static int32_t parse_path(char *value) { int32_t i, len; if (value == NULL) return (-1); for (len = 0; len < MAXPATHLEN; len++) { i = isvalid_fchar(*(value + len), len) ; if (i == 0) break; else if (i < 0) return (-1); } if (len >= MAXPATHLEN || value[len] != '\0') { warnx("Bad pathname - '%s'", value); return (-1); } return (len); } static int32_t parse_flist(struct snmp_toolinfo *snmptoolctx, char *value, char *path, const struct asn_oid *cut) { int32_t namelen; char filename[MAXPATHLEN + 1]; if (value == NULL) return (-1); do { memset(filename, 0, MAXPATHLEN + 1); if (isalpha(*value) && (path == NULL || path[0] == '\0')) { strlcpy(filename, SNMP_DEFS_DIR, MAXPATHLEN + 1); namelen = strlen(SNMP_DEFS_DIR); } else if (path != NULL){ strlcpy(filename, path, MAXPATHLEN + 1); namelen = strlen(path); } else namelen = 0; for ( ; namelen < MAXPATHLEN; value++) { if (isvalid_fchar(*value, namelen) > 0) { filename[namelen++] = *value; continue; } if (*value == ',' ) value++; else if (*value == '\0') ; else { if (!isascii(*value) || !isprint(*value)) warnx("Unexpected character %#2x in" " filename", (u_int) *value); else warnx("Illegal character '%c' in" " filename", *value); return (-1); } filename[namelen]='\0'; break; } if ((namelen == MAXPATHLEN) && (filename[MAXPATHLEN] != '\0')) { warnx("Filename %s too long", filename); return (-1); } if (add_filename(snmptoolctx, filename, cut, 0) < 0) { warnx("Error adding file %s to list", filename); return (-1); } } while (*value != '\0'); return(1); } static int32_t parse_ascii(char *ascii, uint8_t *binstr, size_t binlen) { char dptr[3]; size_t count; int32_t alen, i, saved_errno; uint32_t val; /* Filter 0x at the beginning */ if ((alen = strlen(ascii)) > 2 && ascii[0] == '0' && ascii[1] == 'x') i = 2; else i = 0; saved_errno = errno; errno = 0; for (count = 0; i < alen; i += 2) { /* XXX: consider strlen(ascii) % 2 != 0 */ dptr[0] = ascii[i]; dptr[1] = ascii[i + 1]; dptr[2] = '\0'; if ((val = strtoul(dptr, NULL, 16)) > 0xFF || errno != 0) { errno = saved_errno; return (-1); } binstr[count] = (uint8_t) val; if (++count >= binlen) { warnx("Key %s too long - truncating to %zu octets", ascii, binlen); break; } } return (count); } /* * Functions to parse common input options for client tools and fill in the * snmp_client structure. */ int32_t parse_authentication(struct snmp_toolinfo *snmptoolctx __unused, char *opt_arg) { int32_t count, subopt; char *val, *option; const char *const subopts[] = { "proto", "key", NULL }; assert(opt_arg != NULL); count = 1; while ((subopt = getsubopt1(&opt_arg, subopts, &val, &option)) != EOF) { switch (subopt) { case 0: if (val == NULL) { warnx("Suboption 'proto' requires an argument"); return (-1); } if (strlen(val) != 3) { warnx("Unknown auth protocol - %s", val); return (-1); } if (strncasecmp("md5", val, strlen("md5")) == 0) snmp_client.user.auth_proto = SNMP_AUTH_HMAC_MD5; else if (strncasecmp("sha", val, strlen("sha")) == 0) snmp_client.user.auth_proto = SNMP_AUTH_HMAC_SHA; else { warnx("Unknown auth protocol - %s", val); return (-1); } break; case 1: if (val == NULL) { warnx("Suboption 'key' requires an argument"); return (-1); } if (parse_ascii(val, snmp_client.user.auth_key, SNMP_AUTH_KEY_SIZ) < 0) { warnx("Bad authentication key- %s", val); return (-1); } break; default: warnx("Unknown suboption - '%s'", suboptarg); return (-1); } count += 1; } return (2/* count */); } int32_t parse_privacy(struct snmp_toolinfo *snmptoolctx __unused, char *opt_arg) { int32_t count, subopt; char *val, *option; const char *const subopts[] = { "proto", "key", NULL }; assert(opt_arg != NULL); count = 1; while ((subopt = getsubopt1(&opt_arg, subopts, &val, &option)) != EOF) { switch (subopt) { case 0: if (val == NULL) { warnx("Suboption 'proto' requires an argument"); return (-1); } if (strlen(val) != 3) { warnx("Unknown privacy protocol - %s", val); return (-1); } if (strncasecmp("aes", val, strlen("aes")) == 0) snmp_client.user.priv_proto = SNMP_PRIV_AES; else if (strncasecmp("des", val, strlen("des")) == 0) snmp_client.user.priv_proto = SNMP_PRIV_DES; else { warnx("Unknown privacy protocol - %s", val); return (-1); } break; case 1: if (val == NULL) { warnx("Suboption 'key' requires an argument"); return (-1); } if (parse_ascii(val, snmp_client.user.priv_key, SNMP_PRIV_KEY_SIZ) < 0) { warnx("Bad privacy key- %s", val); return (-1); } break; default: warnx("Unknown suboption - '%s'", suboptarg); return (-1); } count += 1; } return (2/* count */); } int32_t parse_context(struct snmp_toolinfo *snmptoolctx __unused, char *opt_arg) { int32_t count, subopt; char *val, *option; const char *const subopts[] = { "context", "context-engine", NULL }; assert(opt_arg != NULL); count = 1; while ((subopt = getsubopt1(&opt_arg, subopts, &val, &option)) != EOF) { switch (subopt) { case 0: if (val == NULL) { warnx("Suboption 'context' - no argument"); return (-1); } strlcpy(snmp_client.cname, val, SNMP_CONTEXT_NAME_SIZ); break; case 1: if (val == NULL) { warnx("Suboption 'context-engine' - no argument"); return (-1); } if ((int32_t)(snmp_client.clen = parse_ascii(val, snmp_client.cengine, SNMP_ENGINE_ID_SIZ)) == -1) { warnx("Bad EngineID - %s", val); return (-1); } break; default: warnx("Unknown suboption - '%s'", suboptarg); return (-1); } count += 1; } return (2/* count */); } int32_t parse_user_security(struct snmp_toolinfo *snmptoolctx __unused, char *opt_arg) { int32_t count, subopt, saved_errno; char *val, *option; const char *const subopts[] = { "engine", "engine-boots", "engine-time", "name", NULL }; assert(opt_arg != NULL); count = 1; while ((subopt = getsubopt1(&opt_arg, subopts, &val, &option)) != EOF) { switch (subopt) { case 0: if (val == NULL) { warnx("Suboption 'engine' - no argument"); return (-1); } - snmp_client.engine.engine_len = parse_ascii(val, + snmp_client.engine.engine_len = parse_ascii(val, snmp_client.engine.engine_id, SNMP_ENGINE_ID_SIZ); if ((int32_t)snmp_client.engine.engine_len == -1) { warnx("Bad EngineID - %s", val); return (-1); } break; case 1: if (val == NULL) { warnx("Suboption 'engine-boots' - no argument"); return (-1); } saved_errno = errno; errno = 0; snmp_client.engine.engine_boots = strtoul(val, NULL, 10); if (errno != 0) { warn("Bad 'engine-boots' value %s", val); errno = saved_errno; return (-1); } errno = saved_errno; break; case 2: if (val == NULL) { warnx("Suboption 'engine-time' - no argument"); return (-1); } saved_errno = errno; errno = 0; snmp_client.engine.engine_time = strtoul(val, NULL, 10); if (errno != 0) { warn("Bad 'engine-time' value %s", val); errno = saved_errno; return (-1); } errno = saved_errno; break; case 3: strlcpy(snmp_client.user.sec_name, val, SNMP_ADM_STR32_SIZ); break; default: warnx("Unknown suboption - '%s'", suboptarg); return (-1); } count += 1; } return (2/* count */); } int32_t parse_file(struct snmp_toolinfo *snmptoolctx, char *opt_arg) { assert(opt_arg != NULL); if (parse_flist(snmptoolctx, opt_arg, NULL, &IsoOrgDod_OID) < 0) return (-1); return (2); } int32_t parse_include(struct snmp_toolinfo *snmptoolctx, char *opt_arg) { char path[MAXPATHLEN + 1]; int32_t cut_dflt, len, subopt; struct asn_oid cut; char *val, *option; const char *const subopts[] = { "cut", "path", "file", NULL }; #define INC_CUT 0 #define INC_PATH 1 #define INC_LIST 2 assert(opt_arg != NULL); /* if (opt == 'i') free_filelist(snmptoolctx, ); */ /* * This function should be called only after getopt(3) - otherwise if * no previous validation of opt_arg strlen() may not return what is * expected. */ path[0] = '\0'; memset(&cut, 0, sizeof(struct asn_oid)); cut_dflt = -1; while ((subopt = getsubopt1(&opt_arg, subopts, &val, &option)) != EOF) { switch (subopt) { case INC_CUT: if (val == NULL) { warnx("Suboption 'cut' requires an argument"); return (-1); } else { if (snmp_parse_numoid(val, &cut) < 0) return (-1); } cut_dflt = 1; break; case INC_PATH: if ((len = parse_path(val)) < 0) return (-1); strlcpy(path, val, len + 1); break; case INC_LIST: if (val == NULL) return (-1); if (cut_dflt == -1) len = parse_flist(snmptoolctx, val, path, &IsoOrgDod_OID); else len = parse_flist(snmptoolctx, val, path, &cut); if (len < 0) return (-1); break; default: warnx("Unknown suboption - '%s'", suboptarg); return (-1); } } /* XXX: Fix me - returning two is wrong here */ return (2); } int32_t parse_server(char *opt_arg) { assert(opt_arg != NULL); if (snmp_parse_server(&snmp_client, opt_arg) < 0) return (-1); if (snmp_client.trans > SNMP_TRANS_UDP && snmp_client.chost == NULL) { if ((snmp_client.chost = malloc(strlen(SNMP_DEFAULT_LOCAL) + 1)) == NULL) { syslog(LOG_ERR, "malloc() failed: %s", strerror(errno)); return (-1); } strcpy(snmp_client.chost, SNMP_DEFAULT_LOCAL); } return (2); } int32_t parse_timeout(char *opt_arg) { int32_t v, saved_errno; assert(opt_arg != NULL); saved_errno = errno; errno = 0; v = strtol(opt_arg, NULL, 10); if (errno != 0) { warn("Error parsing timeout value"); errno = saved_errno; return (-1); } snmp_client.timeout.tv_sec = v; errno = saved_errno; return (2); } int32_t parse_retry(char *opt_arg) { uint32_t v; int32_t saved_errno; assert(opt_arg != NULL); saved_errno = errno; errno = 0; v = strtoul(opt_arg, NULL, 10); if (errno != 0) { warn("Error parsing retries count"); errno = saved_errno; return (-1); } snmp_client.retries = v; errno = saved_errno; return (2); } int32_t parse_version(char *opt_arg) { uint32_t v; int32_t saved_errno; assert(opt_arg != NULL); saved_errno = errno; errno = 0; v = strtoul(opt_arg, NULL, 10); if (errno != 0) { warn("Error parsing version"); errno = saved_errno; return (-1); } switch (v) { case 1: snmp_client.version = SNMP_V1; break; case 2: snmp_client.version = SNMP_V2c; break; case 3: snmp_client.version = SNMP_V3; break; default: warnx("Unsupported SNMP version - %u", v); errno = saved_errno; return (-1); } errno = saved_errno; return (2); } int32_t parse_local_path(char *opt_arg) { assert(opt_arg != NULL); if (sizeof(opt_arg) > sizeof(SNMP_LOCAL_PATH)) { warnx("Filename too long - %s", opt_arg); return (-1); } strlcpy(snmp_client.local_path, opt_arg, sizeof(SNMP_LOCAL_PATH)); return (2); } int32_t parse_buflen(char *opt_arg) { uint32_t size; int32_t saved_errno; assert(opt_arg != NULL); saved_errno = errno; errno = 0; size = strtoul(opt_arg, NULL, 10); if (errno != 0) { warn("Error parsing buffer size"); errno = saved_errno; return (-1); } if (size > MAX_BUFF_SIZE) { warnx("Buffer size too big - %d max allowed", MAX_BUFF_SIZE); errno = saved_errno; return (-1); } snmp_client.txbuflen = snmp_client.rxbuflen = size; errno = saved_errno; return (2); } int32_t parse_debug(void) { snmp_client.dump_pdus = 1; return (1); } int32_t parse_discovery(struct snmp_toolinfo *snmptoolctx) { SET_EDISCOVER(snmptoolctx); snmp_client.version = SNMP_V3; return (1); } int32_t parse_local_key(struct snmp_toolinfo *snmptoolctx) { SET_LOCALKEY(snmptoolctx); snmp_client.version = SNMP_V3; return (1); } int32_t parse_num_oids(struct snmp_toolinfo *snmptoolctx) { SET_NUMERIC(snmptoolctx); return (1); } int32_t parse_output(struct snmp_toolinfo *snmptoolctx, char *opt_arg) { assert(opt_arg != NULL); if (strlen(opt_arg) > strlen("verbose")) { warnx( "Invalid output option - %s",opt_arg); return (-1); } if (strncasecmp(opt_arg, "short", strlen(opt_arg)) == 0) SET_OUTPUT(snmptoolctx, OUTPUT_SHORT); else if (strncasecmp(opt_arg, "verbose", strlen(opt_arg)) == 0) SET_OUTPUT(snmptoolctx, OUTPUT_VERBOSE); else if (strncasecmp(opt_arg,"tabular", strlen(opt_arg)) == 0) SET_OUTPUT(snmptoolctx, OUTPUT_TABULAR); else if (strncasecmp(opt_arg, "quiet", strlen(opt_arg)) == 0) SET_OUTPUT(snmptoolctx, OUTPUT_QUIET); else { warnx( "Invalid output option - %s", opt_arg); return (-1); } return (2); } int32_t parse_errors(struct snmp_toolinfo *snmptoolctx) { SET_RETRY(snmptoolctx); return (1); } int32_t parse_skip_access(struct snmp_toolinfo *snmptoolctx) { SET_ERRIGNORE(snmptoolctx); return (1); } char * snmp_parse_suboid(char *str, struct asn_oid *oid) { char *endptr; asn_subid_t suboid; if (*str == '.') str++; if (*str < '0' || *str > '9') return (str); do { suboid = strtoul(str, &endptr, 10); if ((asn_subid_t) suboid > ASN_MAXID) { warnx("Suboid %u > ASN_MAXID", suboid); return (NULL); } if (snmp_suboid_append(oid, suboid) < 0) return (NULL); str = endptr + 1; } while (*endptr == '.'); return (endptr); } static char * snmp_int2asn_oid(char *str, struct asn_oid *oid) { char *endptr; int32_t v, saved_errno; saved_errno = errno; errno = 0; v = strtol(str, &endptr, 10); if (errno != 0) { warn("Integer value %s not supported", str); errno = saved_errno; return (NULL); } errno = saved_errno; if (snmp_suboid_append(oid, (asn_subid_t) v) < 0) return (NULL); return (endptr); } /* It is a bit weird to have a table indexed by OID but still... */ static char * snmp_oid2asn_oid(struct snmp_toolinfo *snmptoolctx, char *str, struct asn_oid *oid) { int32_t i; char string[MAXSTR + 1], *endptr; struct snmp_object obj; for (i = 0; i < MAXSTR; i++) if (isalpha (*(str + i)) == 0) break; endptr = str + i; memset(&obj, 0, sizeof(struct snmp_object)); if (i == 0) { if ((endptr = snmp_parse_suboid(str, &(obj.val.var))) == NULL) return (NULL); if (snmp_suboid_append(oid, (asn_subid_t) obj.val.var.len) < 0) return (NULL); } else { strlcpy(string, str, i + 1); if (snmp_lookup_enumoid(snmptoolctx, &obj, string) < 0) { warnx("Unknown string - %s", string); return (NULL); } } asn_append_oid(oid, &(obj.val.var)); return (endptr); } static char * snmp_ip2asn_oid(char *str, struct asn_oid *oid) { uint32_t v; int32_t i; char *endptr, *ptr; ptr = str; for (i = 0; i < 4; i++) { v = strtoul(ptr, &endptr, 10); if (v > 0xff) return (NULL); if (*endptr != '.' && strchr("],\0", *endptr) == NULL && i != 3) return (NULL); if (snmp_suboid_append(oid, (asn_subid_t) v) < 0) return (NULL); ptr = endptr + 1; } return (endptr); } /* 32-bit counter, gauge, timeticks. */ static char * snmp_uint2asn_oid(char *str, struct asn_oid *oid) { char *endptr; uint32_t v; int32_t saved_errno; saved_errno = errno; errno = 0; v = strtoul(str, &endptr, 10); if (errno != 0) { warn("Integer value %s not supported", str); errno = saved_errno; return (NULL); } errno = saved_errno; if (snmp_suboid_append(oid, (asn_subid_t) v) < 0) return (NULL); return (endptr); } static char * snmp_cnt64_2asn_oid(char *str, struct asn_oid *oid) { char *endptr; uint64_t v; int32_t saved_errno; saved_errno = errno; errno = 0; v = strtoull(str, &endptr, 10); if (errno != 0) { warn("Integer value %s not supported", str); errno = saved_errno; return (NULL); } errno = saved_errno; if (snmp_suboid_append(oid, (asn_subid_t) (v & 0xffffffff)) < 0) return (NULL); if (snmp_suboid_append(oid, (asn_subid_t) (v >> 32)) < 0) return (NULL); return (endptr); } enum snmp_syntax parse_syntax(char *str) { int32_t i; for (i = 0; i < SNMP_SYNTAX_UNKNOWN; i++) { if (strncmp(syntax_strings[i].str, str, strlen(syntax_strings[i].str)) == 0) return (syntax_strings[i].stx); } return (SNMP_SYNTAX_NULL); } static char * snmp_parse_subindex(struct snmp_toolinfo *snmptoolctx, char *str, struct index *idx, struct snmp_object *object) { char *ptr; int32_t i; enum snmp_syntax stx; char syntax[MAX_CMD_SYNTAX_LEN]; ptr = str; if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE) { for (i = 0; i < MAX_CMD_SYNTAX_LEN ; i++) { if (*(ptr + i) == ':') break; } if (i >= MAX_CMD_SYNTAX_LEN) { warnx("Unknown syntax in OID - %s", str); return (NULL); } /* Expect a syntax string here. */ if ((stx = parse_syntax(str)) <= SNMP_SYNTAX_NULL) { warnx("Invalid syntax - %s",syntax); return (NULL); } if (stx != idx->syntax && !ISSET_ERRIGNORE(snmptoolctx)) { warnx("Syntax mismatch - %d expected, %d given", idx->syntax, stx); return (NULL); } /* * That is where the suboid started + the syntax length + one * character for ':'. */ ptr = str + i + 1; } else stx = idx->syntax; switch (stx) { case SNMP_SYNTAX_INTEGER: return (snmp_int2asn_oid(ptr, &(object->val.var))); case SNMP_SYNTAX_OID: return (snmp_oid2asn_oid(snmptoolctx, ptr, &(object->val.var))); case SNMP_SYNTAX_IPADDRESS: return (snmp_ip2asn_oid(ptr, &(object->val.var))); case SNMP_SYNTAX_COUNTER: /* FALLTHROUGH */ case SNMP_SYNTAX_GAUGE: /* FALLTHROUGH */ case SNMP_SYNTAX_TIMETICKS: return (snmp_uint2asn_oid(ptr, &(object->val.var))); case SNMP_SYNTAX_COUNTER64: return (snmp_cnt64_2asn_oid(ptr, &(object->val.var))); case SNMP_SYNTAX_OCTETSTRING: return (snmp_tc2oid(idx->tc, ptr, &(object->val.var))); default: /* NOTREACHED */ break; } return (NULL); } char * snmp_parse_index(struct snmp_toolinfo *snmptoolctx, char *str, struct snmp_object *object) { char *ptr; struct index *temp; if (object->info->table_idx == NULL) return (NULL); ptr = NULL; STAILQ_FOREACH(temp, &(OBJECT_IDX_LIST(object)), link) { if ((ptr = snmp_parse_subindex(snmptoolctx, str, temp, object)) == NULL) return (NULL); if (*ptr != ',' && *ptr != ']') return (NULL); str = ptr + 1; } if (ptr == NULL || *ptr != ']') { warnx("Mismatching index - %s", str); return (NULL); } return (ptr + 1); } /* * Fill in the struct asn_oid member of snmp_value with suboids from input. * If an error occurs - print message on stderr and return (-1). * If all is ok - return the length of the oid. */ int32_t snmp_parse_numoid(char *argv, struct asn_oid *var) { char *endptr, *str; asn_subid_t suboid; str = argv; if (*str == '.') str++; do { if (var->len == ASN_MAXOIDLEN) { warnx("Oid too long - %u", var->len); return (-1); } suboid = strtoul(str, &endptr, 10); if (suboid > ASN_MAXID) { warnx("Oid too long - %u", var->len); return (-1); } var->subs[var->len++] = suboid; str = endptr + 1; } while ( *endptr == '.'); if (*endptr != '\0') { warnx("Invalid oid string - %s", argv); return (-1); } return (var->len); } /* Append a length 1 suboid to an asn_oid structure. */ int32_t snmp_suboid_append(struct asn_oid *var, asn_subid_t suboid) { if (var == NULL) return (-1); if (var->len >= ASN_MAXOIDLEN) { warnx("Oid too long - %u", var->len); return (-1); } var->subs[var->len++] = suboid; return (1); } /* Pop the last suboid from an asn_oid structure. */ int32_t snmp_suboid_pop(struct asn_oid *var) { asn_subid_t suboid; if (var == NULL) return (-1); if (var->len < 1) return (-1); suboid = var->subs[--(var->len)]; var->subs[var->len] = 0; return (suboid); } /* * Parse the command-line provided string into an OID - alocate memory for a new * snmp object, fill in its fields and insert it in the object list. A * (snmp_verify_inoid_f) function must be provided to validate the input string. */ int32_t snmp_object_add(struct snmp_toolinfo *snmptoolctx, snmp_verify_inoid_f func, char *string) { struct snmp_object *obj; if (snmptoolctx == NULL) return (-1); /* XXX-BZ does that chack make sense? */ if (snmptoolctx->objects >= SNMP_MAX_BINDINGS) { warnx("Too many bindings in PDU - %u", snmptoolctx->objects + 1); return (-1); } if ((obj = calloc(1, sizeof(struct snmp_object))) == NULL) { syslog(LOG_ERR, "malloc() failed: %s", strerror(errno)); return (-1); } if (func(snmptoolctx, obj, string) < 0) { warnx("Invalid OID - %s", string); free(obj); return (-1); } snmptoolctx->objects++; SLIST_INSERT_HEAD(&snmptoolctx->snmp_objectlist, obj, link); return (1); } /* Given an OID, find it in the object list and remove it. */ int32_t snmp_object_remove(struct snmp_toolinfo *snmptoolctx, struct asn_oid *oid) { struct snmp_object *temp; if (SLIST_EMPTY(&snmptoolctx->snmp_objectlist)) { warnx("Object list already empty"); return (-1); } SLIST_FOREACH(temp, &snmptoolctx->snmp_objectlist, link) if (asn_compare_oid(&(temp->val.var), oid) == 0) break; if (temp == NULL) { warnx("No such object in list"); return (-1); } SLIST_REMOVE(&snmptoolctx->snmp_objectlist, temp, snmp_object, link); if (temp->val.syntax == SNMP_SYNTAX_OCTETSTRING && temp->val.v.octetstring.octets != NULL) free(temp->val.v.octetstring.octets); free(temp); return (1); } static void snmp_object_freeall(struct snmp_toolinfo *snmptoolctx) { struct snmp_object *o; while ((o = SLIST_FIRST(&snmptoolctx->snmp_objectlist)) != NULL) { SLIST_REMOVE_HEAD(&snmptoolctx->snmp_objectlist, link); if (o->val.syntax == SNMP_SYNTAX_OCTETSTRING && o->val.v.octetstring.octets != NULL) free(o->val.v.octetstring.octets); free(o); } } /* Do all possible memory release before exit. */ void snmp_tool_freeall(struct snmp_toolinfo *snmptoolctx) { if (snmp_client.chost != NULL) { free(snmp_client.chost); snmp_client.chost = NULL; } if (snmp_client.cport != NULL) { free(snmp_client.cport); snmp_client.cport = NULL; } snmp_mapping_free(snmptoolctx); free_filelist(snmptoolctx); snmp_object_freeall(snmptoolctx); if (snmptoolctx->passwd != NULL) { free(snmptoolctx->passwd); snmptoolctx->passwd = NULL; } } /* * Fill all variables from the object list into a PDU. (snmp_verify_vbind_f) * function should check whether the variable is consistent in this PDU * (e.g do not add non-leaf OIDs to a GET PDU, or OIDs with read access only to * a SET PDU) - might be NULL though. (snmp_add_vbind_f) function is the * function actually adds the variable to the PDU and must not be NULL. */ int32_t snmp_pdu_add_bindings(struct snmp_toolinfo *snmptoolctx, snmp_verify_vbind_f vfunc, snmp_add_vbind_f afunc, struct snmp_pdu *pdu, int32_t maxcount) { int32_t nbindings, abind; struct snmp_object *obj; if (pdu == NULL || afunc == NULL) return (-1); /* Return 0 in case of no more work todo. */ if (SLIST_EMPTY(&snmptoolctx->snmp_objectlist)) return (0); - + if (maxcount < 0 || maxcount > SNMP_MAX_BINDINGS) { warnx("maxcount out of range: <0 || >SNMP_MAX_BINDINGS"); return (-1); } nbindings = 0; SLIST_FOREACH(obj, &snmptoolctx->snmp_objectlist, link) { if ((vfunc != NULL) && (vfunc(snmptoolctx, pdu, obj) < 0)) { nbindings = -1; break; } if ((abind = afunc(pdu, obj)) < 0) { nbindings = -1; break; } if (abind > 0) { /* Do not put more varbindings than requested. */ if (++nbindings >= maxcount) break; } } return (nbindings); } /* * Locate an object in the object list and set a corresponding error status. */ int32_t snmp_object_seterror(struct snmp_toolinfo *snmptoolctx, struct snmp_value *err_value, int32_t error_status) { struct snmp_object *obj; if (SLIST_EMPTY(&snmptoolctx->snmp_objectlist) || err_value == NULL) return (-1); SLIST_FOREACH(obj, &snmptoolctx->snmp_objectlist, link) if (asn_compare_oid(&(err_value->var), &(obj->val.var)) == 0) { obj->error = error_status; return (1); } return (0); } /* * Check a PDU received in response to a SNMP_PDU_GET/SNMP_PDU_GETBULK request * but don't compare syntaxes - when sending a request PDU they must be null. * This is a (almost) complete copy of snmp_pdu_check() - with matching syntaxes * checks and some other checks skipped. */ int32_t snmp_parse_get_resp(struct snmp_pdu *resp, struct snmp_pdu *req) { uint32_t i; for (i = 0; i < req->nbindings; i++) { if (asn_compare_oid(&req->bindings[i].var, &resp->bindings[i].var) != 0) { warnx("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); } return (1); } int32_t snmp_parse_getbulk_resp(struct snmp_pdu *resp, struct snmp_pdu *req) { int32_t N, R, M, r; if (req->error_status > (int32_t) resp->nbindings) { warnx("Bad number of bindings in response"); return (-1); } for (N = 0; N < req->error_status; N++) { if (asn_is_suboid(&req->bindings[N].var, &resp->bindings[N].var) == 0) return (0); if (resp->bindings[N].syntax == SNMP_SYNTAX_ENDOFMIBVIEW) return (0); } for (R = N , r = N; R < (int32_t) req->nbindings; R++) { for (M = 0; M < req->error_index && (r + M) < (int32_t) resp->nbindings; M++) { if (asn_is_suboid(&req->bindings[R].var, &resp->bindings[r + M].var) == 0) return (0); if (resp->bindings[r + M].syntax == SNMP_SYNTAX_ENDOFMIBVIEW) { M++; break; } } r += M; } return (0); } int32_t snmp_parse_getnext_resp(struct snmp_pdu *resp, struct snmp_pdu *req) { uint32_t i; for (i = 0; i < req->nbindings; i++) { if (asn_is_suboid(&req->bindings[i].var, &resp->bindings[i].var) == 0) return (0); if (resp->version != SNMP_V1 && resp->bindings[i].syntax == SNMP_SYNTAX_ENDOFMIBVIEW) return (0); } return (1); } /* * Should be called to check a response to get/getnext/getbulk. */ int32_t snmp_parse_resp(struct snmp_pdu *resp, struct snmp_pdu *req) { if (resp == NULL || req == NULL) return (-2); if (resp->version != req->version) { warnx("Response has wrong version"); return (-1); } if (resp->error_status == SNMP_ERR_NOSUCHNAME) { warnx("Error - No Such Name"); return (0); } if (resp->error_status != SNMP_ERR_NOERROR) { warnx("Error %d in response", resp->error_status); return (-1); } if (resp->nbindings != req->nbindings && req->type != SNMP_PDU_GETBULK){ warnx("Bad number of bindings in response"); return (-1); } switch (req->type) { case SNMP_PDU_GET: return (snmp_parse_get_resp(resp,req)); case SNMP_PDU_GETBULK: return (snmp_parse_getbulk_resp(resp,req)); case SNMP_PDU_GETNEXT: return (snmp_parse_getnext_resp(resp,req)); default: /* NOTREACHED */ break; } return (-2); } static void snmp_output_octetstring(struct snmp_toolinfo *snmptoolctx, enum snmp_tc tc, uint32_t len, uint8_t *octets) { char *buf; if (len == 0 || octets == NULL) return; if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE) fprintf(stdout, "%s : ", syntax_strings[SNMP_SYNTAX_OCTETSTRING].str); if ((buf = snmp_oct2tc(tc, len, (char *) octets)) != NULL) { fprintf(stdout, "%s", buf); free(buf); } } static void snmp_output_octetindex(struct snmp_toolinfo *snmptoolctx, enum snmp_tc tc, struct asn_oid *oid) { uint32_t i; uint8_t *s; if ((s = malloc(oid->subs[0] + 1)) == NULL) syslog(LOG_ERR, "malloc failed - %s", strerror(errno)); else { for (i = 0; i < oid->subs[0]; i++) s[i] = (u_char) (oid->subs[i + 1]); snmp_output_octetstring(snmptoolctx, tc, oid->subs[0], s); free(s); } } /* * Check and output syntax type and value. */ static void snmp_output_oid_value(struct snmp_toolinfo *snmptoolctx, struct asn_oid *oid) { char oid_string[ASN_OIDSTRLEN]; struct snmp_object obj; if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE) fprintf(stdout, "%s : ", syntax_strings[SNMP_SYNTAX_OID].str); if(!ISSET_NUMERIC(snmptoolctx)) { memset(&obj, 0, sizeof(struct snmp_object)); asn_append_oid(&(obj.val.var), oid); if (snmp_lookup_enumstring(snmptoolctx, &obj) > 0) fprintf(stdout, "%s" , obj.info->string); else if (snmp_lookup_oidstring(snmptoolctx, &obj) > 0) fprintf(stdout, "%s" , obj.info->string); else if (snmp_lookup_nodestring(snmptoolctx, &obj) > 0) fprintf(stdout, "%s" , obj.info->string); else { (void) asn_oid2str_r(oid, oid_string); fprintf(stdout, "%s", oid_string); } } else { (void) asn_oid2str_r(oid, oid_string); fprintf(stdout, "%s", oid_string); } } static void snmp_output_int(struct snmp_toolinfo *snmptoolctx, struct enum_pairs *enums, int32_t int_val) { char *string; if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE) fprintf(stdout, "%s : ", syntax_strings[SNMP_SYNTAX_INTEGER].str); if (enums != NULL && (string = enum_string_lookup(enums, int_val)) != NULL) fprintf(stdout, "%s", string); else fprintf(stdout, "%d", int_val); } static void snmp_output_ipaddress(struct snmp_toolinfo *snmptoolctx, uint8_t *ip) { if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE) fprintf(stdout, "%s : ", syntax_strings[SNMP_SYNTAX_IPADDRESS].str); fprintf(stdout, "%u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]); } static void snmp_output_counter(struct snmp_toolinfo *snmptoolctx, uint32_t counter) { if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE) fprintf(stdout, "%s : ", syntax_strings[SNMP_SYNTAX_COUNTER].str); fprintf(stdout, "%u", counter); } static void snmp_output_gauge(struct snmp_toolinfo *snmptoolctx, uint32_t gauge) { if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE) fprintf(stdout, "%s : ", syntax_strings[SNMP_SYNTAX_GAUGE].str); fprintf(stdout, "%u", gauge); } static void snmp_output_ticks(struct snmp_toolinfo *snmptoolctx, uint32_t ticks) { if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE) fprintf(stdout, "%s : ", syntax_strings[SNMP_SYNTAX_TIMETICKS].str); fprintf(stdout, "%u", ticks); } static void snmp_output_counter64(struct snmp_toolinfo *snmptoolctx, uint64_t counter64) { if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE) fprintf(stdout, "%s : ", syntax_strings[SNMP_SYNTAX_COUNTER64].str); fprintf(stdout,"%ju", counter64); } int32_t snmp_output_numval(struct snmp_toolinfo *snmptoolctx, struct snmp_value *val, struct snmp_oid2str *entry) { if (val == NULL) return (-1); if (GET_OUTPUT(snmptoolctx) != OUTPUT_QUIET) fprintf(stdout, " = "); switch (val->syntax) { case SNMP_SYNTAX_INTEGER: if (entry != NULL) snmp_output_int(snmptoolctx, entry->snmp_enum, val->v.integer); else snmp_output_int(snmptoolctx, NULL, val->v.integer); break; case SNMP_SYNTAX_OCTETSTRING: if (entry != NULL) snmp_output_octetstring(snmptoolctx, entry->tc, val->v.octetstring.len, val->v.octetstring.octets); else snmp_output_octetstring(snmptoolctx, SNMP_STRING, val->v.octetstring.len, val->v.octetstring.octets); break; case SNMP_SYNTAX_OID: snmp_output_oid_value(snmptoolctx, &(val->v.oid)); break; case SNMP_SYNTAX_IPADDRESS: snmp_output_ipaddress(snmptoolctx, val->v.ipaddress); break; case SNMP_SYNTAX_COUNTER: snmp_output_counter(snmptoolctx, val->v.uint32); break; case SNMP_SYNTAX_GAUGE: snmp_output_gauge(snmptoolctx, val->v.uint32); break; case SNMP_SYNTAX_TIMETICKS: snmp_output_ticks(snmptoolctx, val->v.uint32); break; case SNMP_SYNTAX_COUNTER64: snmp_output_counter64(snmptoolctx, val->v.counter64); break; case SNMP_SYNTAX_NOSUCHOBJECT: fprintf(stdout, "No Such Object\n"); return (val->syntax); case SNMP_SYNTAX_NOSUCHINSTANCE: fprintf(stdout, "No Such Instance\n"); return (val->syntax); case SNMP_SYNTAX_ENDOFMIBVIEW: fprintf(stdout, "End of Mib View\n"); return (val->syntax); case SNMP_SYNTAX_NULL: /* NOTREACHED */ fprintf(stdout, "agent returned NULL Syntax\n"); return (val->syntax); default: /* NOTREACHED - If here - then all went completely wrong. */ fprintf(stdout, "agent returned unknown syntax\n"); return (-1); } fprintf(stdout, "\n"); return (0); } static int32_t snmp_fill_object(struct snmp_toolinfo *snmptoolctx, struct snmp_object *obj, struct snmp_value *val) { int32_t rc; asn_subid_t suboid; if (obj == NULL || val == NULL) return (-1); if ((suboid = snmp_suboid_pop(&(val->var))) > ASN_MAXID) return (-1); memset(obj, 0, sizeof(struct snmp_object)); asn_append_oid(&(obj->val.var), &(val->var)); obj->val.syntax = val->syntax; if (obj->val.syntax > 0) rc = snmp_lookup_leafstring(snmptoolctx, obj); else rc = snmp_lookup_nonleaf_string(snmptoolctx, obj); (void) snmp_suboid_append(&(val->var), suboid); (void) snmp_suboid_append(&(obj->val.var), suboid); return (rc); } static int32_t snmp_output_index(struct snmp_toolinfo *snmptoolctx, struct index *stx, struct asn_oid *oid) { uint8_t ip[4]; uint32_t bytes = 1; uint64_t cnt64; struct asn_oid temp, out; if (oid->len < bytes) return (-1); memset(&temp, 0, sizeof(struct asn_oid)); asn_append_oid(&temp, oid); switch (stx->syntax) { case SNMP_SYNTAX_INTEGER: snmp_output_int(snmptoolctx, stx->snmp_enum, temp.subs[0]); break; case SNMP_SYNTAX_OCTETSTRING: if ((temp.subs[0] > temp.len -1 ) || (temp.subs[0] > ASN_MAXOCTETSTRING)) return (-1); snmp_output_octetindex(snmptoolctx, stx->tc, &temp); bytes += temp.subs[0]; break; case SNMP_SYNTAX_OID: if ((temp.subs[0] > temp.len -1) || (temp.subs[0] > ASN_MAXOIDLEN)) return (-1); bytes += temp.subs[0]; memset(&out, 0, sizeof(struct asn_oid)); asn_slice_oid(&out, &temp, 1, bytes); snmp_output_oid_value(snmptoolctx, &out); break; case SNMP_SYNTAX_IPADDRESS: if (temp.len < 4) return (-1); for (bytes = 0; bytes < 4; bytes++) ip[bytes] = temp.subs[bytes]; snmp_output_ipaddress(snmptoolctx, ip); bytes = 4; break; case SNMP_SYNTAX_COUNTER: snmp_output_counter(snmptoolctx, temp.subs[0]); break; case SNMP_SYNTAX_GAUGE: snmp_output_gauge(snmptoolctx, temp.subs[0]); break; case SNMP_SYNTAX_TIMETICKS: snmp_output_ticks(snmptoolctx, temp.subs[0]); break; case SNMP_SYNTAX_COUNTER64: if (oid->len < 2) return (-1); bytes = 2; memcpy(&cnt64, temp.subs, bytes); snmp_output_counter64(snmptoolctx, cnt64); break; default: return (-1); } return (bytes); } static int32_t snmp_output_object(struct snmp_toolinfo *snmptoolctx, struct snmp_object *o) { int32_t i, first, len; struct asn_oid oid; struct index *temp; if (ISSET_NUMERIC(snmptoolctx)) return (-1); if (o->info->table_idx == NULL) { fprintf(stdout,"%s.%d", o->info->string, o->val.var.subs[o->val.var.len - 1]); return (1); } fprintf(stdout,"%s[", o->info->string); memset(&oid, 0, sizeof(struct asn_oid)); len = 1; asn_slice_oid(&oid, &(o->val.var), (o->info->table_idx->var.len + len), o->val.var.len); first = 1; STAILQ_FOREACH(temp, &(OBJECT_IDX_LIST(o)), link) { if(first) first = 0; else fprintf(stdout, ", "); if ((i = snmp_output_index(snmptoolctx, temp, &oid)) < 0) break; len += i; memset(&oid, 0, sizeof(struct asn_oid)); asn_slice_oid(&oid, &(o->val.var), (o->info->table_idx->var.len + len), o->val.var.len + 1); } fprintf(stdout,"]"); return (1); } void snmp_output_err_resp(struct snmp_toolinfo *snmptoolctx, struct snmp_pdu *pdu) { char buf[ASN_OIDSTRLEN]; struct snmp_object object; if (pdu == NULL || (pdu->error_index > (int32_t) pdu->nbindings)) { fprintf(stdout,"Invalid error index in PDU\n"); return; } fprintf(stdout, "Agent %s:%s returned error \n", snmp_client.chost, snmp_client.cport); if (!ISSET_NUMERIC(snmptoolctx) && (snmp_fill_object(snmptoolctx, &object, &(pdu->bindings[pdu->error_index - 1])) > 0)) snmp_output_object(snmptoolctx, &object); else { asn_oid2str_r(&(pdu->bindings[pdu->error_index - 1].var), buf); fprintf(stdout,"%s", buf); } fprintf(stdout," caused error - "); if ((pdu->error_status > 0) && (pdu->error_status <= SNMP_ERR_INCONS_NAME)) fprintf(stdout, "%s\n", error_strings[pdu->error_status].str); else fprintf(stdout,"%s\n", error_strings[SNMP_ERR_UNKNOWN].str); } int32_t snmp_output_resp(struct snmp_toolinfo *snmptoolctx, struct snmp_pdu *pdu, struct asn_oid *root) { int32_t error; char p[ASN_OIDSTRLEN]; uint32_t i; struct snmp_object object; i = error = 0; while (i < pdu->nbindings) { if (root != NULL && !(asn_is_suboid(root, &(pdu->bindings[i].var)))) break; if (GET_OUTPUT(snmptoolctx) != OUTPUT_QUIET) { if (!ISSET_NUMERIC(snmptoolctx) && (snmp_fill_object(snmptoolctx, &object, &(pdu->bindings[i])) > 0)) snmp_output_object(snmptoolctx, &object); else { asn_oid2str_r(&(pdu->bindings[i].var), p); fprintf(stdout, "%s", p); } } error |= snmp_output_numval(snmptoolctx, &(pdu->bindings[i]), object.info); i++; } if (error) return (-1); return (i); } void snmp_output_engine(void) { uint32_t i; char *cptr, engine[2 * SNMP_ENGINE_ID_SIZ + 2]; cptr = engine; for (i = 0; i < snmp_client.engine.engine_len; i++) cptr += sprintf(cptr, "%.2x", snmp_client.engine.engine_id[i]); *cptr++ = '\0'; fprintf(stdout, "Engine ID 0x%s\n", engine); fprintf(stdout, "Boots : %u\t\tTime : %d\n", snmp_client.engine.engine_boots, snmp_client.engine.engine_time); } void snmp_output_keys(void) { uint32_t i, keylen = 0; char *cptr, extkey[2 * SNMP_AUTH_KEY_SIZ + 2]; fprintf(stdout, "Localized keys for %s\n", snmp_client.user.sec_name); if (snmp_client.user.auth_proto == SNMP_AUTH_HMAC_MD5) { fprintf(stdout, "MD5 : 0x"); keylen = SNMP_AUTH_HMACMD5_KEY_SIZ; } else if (snmp_client.user.auth_proto == SNMP_AUTH_HMAC_SHA) { fprintf(stdout, "SHA : 0x"); keylen = SNMP_AUTH_HMACSHA_KEY_SIZ; } if (snmp_client.user.auth_proto != SNMP_AUTH_NOAUTH) { cptr = extkey; for (i = 0; i < keylen; i++) cptr += sprintf(cptr, "%.2x", snmp_client.user.auth_key[i]); *cptr++ = '\0'; fprintf(stdout, "%s\n", extkey); } if (snmp_client.user.priv_proto == SNMP_PRIV_DES) { fprintf(stdout, "DES : 0x"); keylen = SNMP_PRIV_DES_KEY_SIZ; } else if (snmp_client.user.priv_proto == SNMP_PRIV_AES) { fprintf(stdout, "AES : 0x"); keylen = SNMP_PRIV_AES_KEY_SIZ; } if (snmp_client.user.priv_proto != SNMP_PRIV_NOPRIV) { cptr = extkey; for (i = 0; i < keylen; i++) cptr += sprintf(cptr, "%.2x", snmp_client.user.priv_key[i]); *cptr++ = '\0'; fprintf(stdout, "%s\n", extkey); } }