Index: head/contrib/bsnmp/gensnmptree/gensnmptree.1 =================================================================== --- head/contrib/bsnmp/gensnmptree/gensnmptree.1 (revision 345796) +++ head/contrib/bsnmp/gensnmptree/gensnmptree.1 (revision 345797) @@ -1,296 +1,252 @@ .\" .\" Copyright (c) 2001-2005 .\" Fraunhofer Institute for Open Communication Systems (FhG Fokus). .\" All rights reserved. .\" Copyright (c) 2006,2018 .\" 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 June 29, 2018 +.Dd April 2, 2019 .Dt GENSNMPTREE 1 .Os .Sh NAME .Nm gensnmptree .Nd "generate C and header files from a MIB description file" .Sh SYNOPSIS .Nm .Op Fl dEeFfhlt .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 F -Together with -.Fl E -causes -.Nm -instead of the generation of enum definitions the generation of -functions for checking a value to be one of the enumeration variants and -for conversion between strings and the enum. The file is sent to standard -output and is meant to be included into a C-file for compilation. +emit definitions for C-functions includeable in a C-file that do some basic +stuff on enums like value checking and conversion between value and strings. .It Fl f -This flag can be used together with -.Fl E -or when generating the tree files. It causes -.Nm -to emit static inline functions for checking a value to be one of the -enumeration values and for conversion between strings and the enum. -If used when generating the tree files, the preprocessor symbol -.Ar SNMPTREE_TYPES -must be defined when including the tree header file for these definitions -to become visible. +emit definitions for inline C-functions that do some basic +stuff on enums like value checking and conversion between value and strings. .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 -.Pp -The following functions are generated by -.Fl f -or -.Fl F : -.Pp -.Ft static inline int -.Fn isok_EnumName "enum EnumName" ; -.Pp -.Ft static inline const char * -.Fn tostr_EnumName "enum EnumName" ; -.Pp -.Ft static inline int -.Fn fromstr_EnumName "const char *" "enum EnumName *" ; -.Pp -The -.Fa EnumName -is replaced with the enumeration name. -.Fn isok_EnumName -returns 1 if the argument is one of the valid enum values and 0 otherwise. -.Fn tostr_EnumName -returns a string representation of the enumeration value. -If the values is not one of the legal values -.Ar EnumName??? -is returned. -.Fn fromstr_EnumName -returns 1 if the string represents one of the legal enumeration values and -0 otherwise. -If 1 is return the variable pointed to by the second argument is set to -the enumeration value. .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 345796) +++ head/contrib/bsnmp/gensnmptree/gensnmptree.c (revision 345797) @@ -1,1740 +1,1818 @@ /* * Copyright (c) 2001-2003 * Fraunhofer Institute for Open Communication Systems (FhG Fokus). * All rights reserved. * * Copyright (c) 2004-2006,2018 * 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\ -$Id$\n\ usage: gensnmptree [-dEeFfhlt] [-I directory] [-i infile] [-p prefix]\n\ [name]...\n\ options:\n\ -d debug mode\n\ -E extract the named or all enums and bits only\n\ -e extract the named oids or enums\n\ -F generate functions for -E into a .c file\n\ -f generate functions for -E into the header\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 generate a .def file\n\ "; +/** + * Program operation. + */ +enum op { + /** generate the tree */ + OP_GEN, + + /** extract OIDs */ + OP_EXTRACT, + + /** print the parsed tree */ + OP_TREE, + + /** extract enums */ + OP_ENUMS, +}; + +/** + * Which functions to create. + */ +enum gen_funcs { + /** none */ + GEN_FUNCS_NONE, + + /** functions for header files */ + GEN_FUNCS_H, + + /** functions for C files */ + GEN_FUNCS_C, +}; + /* * 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; + char *subtypes[SNMP_INDEXES_MAX]; } entry; struct leaf { enum snmp_syntax syntax; /* syntax for this leaf */ char *func; /* function name */ + char *subtype; /* subtype */ } leaf; struct column { enum snmp_syntax syntax; /* syntax for this column */ + char *subtype; /* subtype */ } 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) + if ((ptr = calloc(1, 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 static char str[MAXSTR]; static u_long val; /* integer values */ static 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) +parse_type(enum tok *tok, struct type *t, const char *vname, char **subtype) { u_int syntax; struct enums *e; syntax = val; + if (subtype != NULL) + *subtype = NULL; 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 '|'"); + if (subtype != NULL) + *subtype = savetok(); *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); + char *subtype; + u_int syntax = parse_type(&tok, NULL, node->name, &subtype); if (tok == TOK_STR) { /* LEAF */ node->type = NODE_LEAF; node->u.leaf.func = savetok(); node->u.leaf.syntax = syntax; + node->u.leaf.subtype = subtype; tok = gettoken(); } else { /* COLUMN */ node->type = NODE_COLUMN; node->u.column.syntax = syntax; + node->u.column.subtype = subtype; } 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) + char *subtype; + u_int syntax = parse_type(&tok, NULL, node->name, + &subtype); + if (index_count == SNMP_INDEXES_MAX) report("too many table indexes"); + node->u.entry.subtypes[index_count++] = subtype; 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); + + t->syntax = parse_type(&tok, t, NULL, 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, +gen_node(FILE *fp, const 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) +gen_header(FILE *fp, const 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) +gen_table(FILE *fp, const struct node *node) { struct asn_oid oid; fprintf(fp, "#include \n"); fprintf(fp, "#include \n"); #ifdef HAVE_STDINT_H fprintf(fp, "#include \n"); #endif - fprintf(fp, "#include \n"); if (localincs) { fprintf(fp, "#include \"asn1.h\"\n"); fprintf(fp, "#include \"snmp.h\"\n"); fprintf(fp, "#include \"snmpagent.h\"\n"); } else { fprintf(fp, "#include \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); + if (np->u.leaf.subtype != NULL) + printf(" | %s", np->u.leaf.subtype); 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++) + for (i = 0; i < SNMP_INDEX_COUNT(np->u.entry.index); i++) { print_syntax(SNMP_INDEX(np->u.entry.index, i)); + if (np->u.entry.subtypes[i] != NULL) + printf(" | %s", np->u.entry.subtypes[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); + if (np->u.column.subtype != NULL) + printf(" | %s", np->u.column.subtype); 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); } -/** - * Extract the named OID. - * - * \param fp file to extract to - * \param root root of the tree - * \param object name of the object to extract - * - * \return 0 on success, -1 if the object was not found - */ 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++; } } /** - * Generate a definition for the enum packed into a guard against multiple - * definitions. - * - * \param fp file to write definition to - * \param t type - */ -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); -} - -/** * Generate helper functions for an enum. * * We always generate a switch statement for the isok function. The compiler * optimizes this into range checks if possible. * * \param fp file to write to * \param t type * \param ccode generate externally visible non-inline functions */ static void gen_enum_funcs(FILE *fp, const struct type *t, int ccode) { fprintf(fp, "\n"); if (!ccode) fprintf(fp, "static inline "); fprintf(fp, "int\n"); fprintf(fp, "isok_%s(enum %s s)\n", t->name, t->name); fprintf(fp, "{\n"); fprintf(fp, " switch (s) {\n"); const struct enums *e; TAILQ_FOREACH(e, &t->enums, link) { fprintf(fp, "\t case %s_", t->name); unminus(fp, e->name); fprintf(fp, ":\n"); } fprintf(fp, " return (1);\n"); fprintf(fp, " }\n"); fprintf(fp, " return (0);\n"); fprintf(fp, "}\n\n"); if (!ccode) fprintf(fp, "static inline "); fprintf(fp, "const char *\n"); fprintf(fp, "tostr_%s(enum %s s)\n", t->name, t->name); fprintf(fp, "{\n"); fprintf(fp, " static const char *vals[] = { STRING_%s };\n", t->name); fprintf(fp, "\n"); fprintf(fp, " if (isok_%s(s))\n", t->name); fprintf(fp, " return (vals[(int)s - STROFF_%s]);\n", t->name); fprintf(fp, " return (\"%s???\");\n", t->name); fprintf(fp, "}\n\n"); if (!ccode) fprintf(fp, "static inline "); fprintf(fp, "int\n"); fprintf(fp, "fromstr_%s(const char *str, enum %s *s)\n", t->name, t->name); fprintf(fp, "{\n"); fprintf(fp, " static const char *vals[] = { STRING_%s };\n", t->name); fprintf(fp, "\n"); fprintf(fp, " for (size_t i = 0; i < sizeof(vals)/sizeof(vals[0]); i++) {\n"); fprintf(fp, " if (vals[i] != NULL && strcmp(vals[i], str) == 0) {\n"); fprintf(fp, " *s = i + STROFF_%s;\n", t->name); fprintf(fp, " return (1);\n"); fprintf(fp, " }\n"); fprintf(fp, " }\n"); fprintf(fp, " return (0);\n"); fprintf(fp, "}\n"); } /** + * Generate a definition for the enum packed into a guard against multiple + * definitions. + * + * \param fp file to write definition to + * \param t type + * \param dof generate functions too + */ +static void +gen_enum(FILE *fp, const struct type *t, int dof) +{ + 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"); + if (dof) { + fprintf(fp, "#ifdef SNMPENUM_FUNCS\n"); + fprintf(fp, "\n"); + gen_enum_funcs(fp, t, 0); + fprintf(fp, "\n"); + fprintf(fp, "#endif\n"); + fprintf(fp, "\n"); + } + fprintf(fp, "#endif /* %s_defined__ */\n", t->name); +} + +/** * Generate helper functions for an enum. This generates code for a c file. * * \param fp file to write to * \param name enum name */ static int gen_enum_funcs_str(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_funcs(fp, t, 1); return (0); } return (-1); } /** * Generate helper functions for all enums. * * \param fp file to write to * \param ccode generate externally visible non-inline functions */ static void gen_all_enum_funcs(FILE *fp, int ccode) { const struct type *t; LIST_FOREACH(t, &types, link) if (t->is_enum || t->is_bits) gen_enum_funcs(fp, t, ccode); } +static void +gen_enums(FILE *fp, int dof) +{ + const struct type *t; + + LIST_FOREACH(t, &types, link) + if (t->is_enum || t->is_bits) + gen_enum(fp, t, dof); +} + /** * Extract a given enum to the specified file and optionally generate static * inline helper functions for them. * * \param fp file to print on * \param name name of the enum * \param gen_funcs generate the functions too * * \return 0 if found, -1 otherwise */ static int extract_enum(FILE *fp, const char *name, int gen_funcs) { 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); - if (gen_funcs) - gen_enum_funcs(fp, t, 0); + gen_enum(fp, t, gen_funcs); return (0); } return (-1); } /** * Extract all enums to the given file and optionally generate static inline * helper functions for them. * * \param fp file to print on * \param gen_funcs generate the functions too */ static void extract_all_enums(FILE *fp, int gen_funcs) { const struct type *t; LIST_FOREACH(t, &types, link) - if (t->is_enum || t->is_bits) { - gen_enum(fp, t); - if (gen_funcs) - gen_enum_funcs(fp, t, 0); - } + if (t->is_enum || t->is_bits) + gen_enum(fp, t, gen_funcs); } /** * Extract enums and optionally generate some helper functions for them. * * \param argc number of arguments * \param argv arguments (enum names) - * \param gen_funcs_h generate functions into the header file - * \param gen_funcs_c generate a .c file with functions + * \param gen_funcs which functions to generate */ static void -make_enums(int argc, char *argv[], int gen_funcs_h, int gen_funcs_c) +make_enums(int argc, char *argv[], enum gen_funcs gen_funcs) { - if (gen_funcs_c) { + if (gen_funcs == GEN_FUNCS_C) { if (argc == 0) gen_all_enum_funcs(stdout, 1); else { for (int i = 0; i < argc; i++) if (gen_enum_funcs_str(stdout, argv[i])) errx(1, "enum not found: %s", argv[i]); } } else { if (argc == 0) - extract_all_enums(stdout, gen_funcs_h); + extract_all_enums(stdout, gen_funcs == GEN_FUNCS_H); else { for (int i = 0; i < argc; i++) - if (extract_enum(stdout, argv[i], gen_funcs_h)) + if (extract_enum(stdout, argv[i], + gen_funcs == GEN_FUNCS_H)) errx(1, "enum not found: %s", argv[i]); } } } +/** + * Produce the operation tables for the daemon or a module. + * + * \param root tree root + * \param gen_funcs generate enum funcs + */ +static void +make_table(const struct node *root, int gen_funcs) +{ + FILE *fp; + + char fname[MAXPATHLEN + 1]; + 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, gen_funcs); + 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); +} + int main(int argc, char *argv[]) { - int do_extract = 0; - int do_tree = 0; - int do_enums = 0; - int gen_funcs_h = 0; - int gen_funcs_c = 0; - int opt; - struct node *root; - char fname[MAXPATHLEN + 1]; - int tok; - FILE *fp; + enum op op = OP_GEN; + enum gen_funcs gen_funcs = GEN_FUNCS_NONE; + char *infile = NULL; + int opt; while ((opt = getopt(argc, argv, "dEeFfhI:i:lp:t")) != EOF) switch (opt) { case 'd': debug = 1; break; case 'E': - do_enums = 1; + if (op != OP_GEN && op != OP_ENUMS) + errx(1, "-E conflicts with earlier options"); + op = OP_ENUMS; break; case 'e': - do_extract = 1; + if (op != OP_GEN && op != OP_EXTRACT) + errx(1, "-e conflicts with earlier options"); + op = OP_EXTRACT; break; case 'F': - gen_funcs_c = 1; + if (gen_funcs != GEN_FUNCS_NONE && + gen_funcs != GEN_FUNCS_C) + errx(1, "-F conflicts with -f"); + gen_funcs = GEN_FUNCS_C; break; case 'f': - gen_funcs_h = 1; + if (gen_funcs != GEN_FUNCS_NONE && + gen_funcs != GEN_FUNCS_H) + errx(1, "-f conflicts with -F"); + gen_funcs = GEN_FUNCS_H; break; case 'h': fprintf(stderr, "%s", usgtxt); exit(0); 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; + if (op != OP_GEN && op != OP_TREE) + errx(1, "-t conflicts with earlier options"); + op = OP_TREE; 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 && argc == optind) - errx(1, "no objects specified"); + argc -= optind; + argv += optind; - if ((gen_funcs_h || gen_funcs_c) && (do_extract || do_tree)) - errx(1, "-f and -F not allowed with -e or -t"); - if (gen_funcs_c && !do_enums) - errx(1, "-F requires -E"); - if (gen_funcs_h && gen_funcs_c) - errx(1, "-f and -F are mutually exclusive"); - + /* open input */ if (infile == NULL) { input_new(stdin, NULL, ""); } else { + FILE *fp; if ((fp = fopen(infile, "r")) == NULL) err(1, "%s", infile); input_new(fp, NULL, infile); } - root = parse_top(gettoken()); + /* parse and check input */ + struct node *root = parse_top(gettoken()); + + int tok; while ((tok = gettoken()) != TOK_EOF) merge(&root, parse_top(tok)); if (root) 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++; - } + /* do what the user has requested */ + switch (op) { + + case OP_EXTRACT: + if (argc == 0) + errx(1, "-e requires arguments"); + + for (int i = 0; i < argc; i++) + if (gen_extract(stdout, root, argv[i])) + errx(1, "object not found: %s", argv[i]); return (0); - } - if (do_enums) { - make_enums(argc - optind, argv + optind, - gen_funcs_h, gen_funcs_c); + + case OP_ENUMS: + make_enums(argc, argv, gen_funcs); return (0); - } - if (do_tree) { + + case OP_TREE: + if (argc != 0) + errx(1, "-t allows no arguments"); 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"); - extract_all_enums(fp, gen_funcs_h); - 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); + case OP_GEN: + if (argc != 0) + errx(1, "tree generation allows no arguments"); + make_table(root, gen_funcs == GEN_FUNCS_H); + return (0); + } } Index: head/contrib/bsnmp/lib/snmpclient.c =================================================================== --- head/contrib/bsnmp/lib/snmpclient.c (revision 345796) +++ head/contrib/bsnmp/lib/snmpclient.c (revision 345797) @@ -1,1964 +1,2283 @@ /* - * Copyright (c) 2004-2005 + * Copyright (c) 2004-2005,2018 * 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 #include #include #include #ifdef HAVE_STDINT_H #include #elif defined(HAVE_INTTYPES_H) #include #endif #include #ifdef HAVE_ERR_H #include #endif +#include + #include "support.h" #include "asn1.h" #include "snmp.h" #include "snmpclient.h" #include "snmppriv.h" +#define DEBUG_PARSE 0 + /* global context */ struct snmp_client snmp_client; /* List of all outstanding requests */ struct sent_pdu { int reqid; struct snmp_pdu *pdu; struct timeval time; u_int retrycount; snmp_send_cb_f callback; void *arg; void *timeout_id; LIST_ENTRY(sent_pdu) entries; }; LIST_HEAD(sent_pdu_list, sent_pdu); static struct sent_pdu_list sent_pdus; /* * Prototype table entry. All C-structure produced by the table function must * start with these two fields. This relies on the fact, that all TAILQ_ENTRY * are compatible with each other in the sense implied by ANSI-C. */ struct entry { TAILQ_ENTRY(entry) link; uint64_t found; }; TAILQ_HEAD(table, entry); /* * working list entry. This list is used to hold the Index part of the * table row's. The entry list and the work list parallel each other. */ struct work { TAILQ_ENTRY(work) link; struct asn_oid index; }; TAILQ_HEAD(worklist, work); /* * Table working data */ struct tabwork { const struct snmp_table *descr; struct table *table; struct worklist worklist; uint32_t last_change; int first; u_int iter; snmp_table_cb_f callback; void *arg; struct snmp_pdu pdu; }; /* * Set the error string */ static void seterr(struct snmp_client *sc, const char *fmt, ...) { va_list ap; va_start(ap, fmt); vsnprintf(sc->error, sizeof(sc->error), fmt, ap); va_end(ap); } /* * Free the entire table and work list. If table is NULL only the worklist * is freed. */ static void table_free(struct tabwork *work, int all) { struct work *w; struct entry *e; const struct snmp_table_entry *d; u_int i; while ((w = TAILQ_FIRST(&work->worklist)) != NULL) { TAILQ_REMOVE(&work->worklist, w, link); free(w); } if (all == 0) return; while ((e = TAILQ_FIRST(work->table)) != NULL) { for (i = 0; work->descr->entries[i].syntax != SNMP_SYNTAX_NULL; i++) { d = &work->descr->entries[i]; if (d->syntax == SNMP_SYNTAX_OCTETSTRING && (e->found & ((uint64_t)1 << i))) free(*(void **)(void *) ((u_char *)e + d->offset)); } TAILQ_REMOVE(work->table, e, link); free(e); } } /* * Find the correct table entry for the given variable. If non exists, * create one. */ static struct entry * table_find(struct tabwork *work, const struct asn_oid *var) { struct entry *e, *e1; struct work *w, *w1; u_int i, p, j; size_t len; u_char *ptr; struct asn_oid oid; /* get index */ asn_slice_oid(&oid, var, work->descr->table.len + 2, var->len); e = TAILQ_FIRST(work->table); w = TAILQ_FIRST(&work->worklist); while (e != NULL) { if (asn_compare_oid(&w->index, &oid) == 0) return (e); e = TAILQ_NEXT(e, link); w = TAILQ_NEXT(w, link); } /* Not found create new one */ if ((e = malloc(work->descr->entry_size)) == NULL) { seterr(&snmp_client, "no memory for table entry"); return (NULL); } if ((w = malloc(sizeof(*w))) == NULL) { seterr(&snmp_client, "no memory for table entry"); free(e); return (NULL); } w->index = oid; memset(e, 0, work->descr->entry_size); /* decode index */ p = work->descr->table.len + 2; for (i = 0; i < work->descr->index_size; i++) { switch (work->descr->entries[i].syntax) { case SNMP_SYNTAX_INTEGER: if (var->len < p + 1) { seterr(&snmp_client, "bad index: need integer"); goto err; } if (var->subs[p] > INT32_MAX) { seterr(&snmp_client, "bad index: integer too large"); goto err; } *(int32_t *)(void *)((u_char *)e + work->descr->entries[i].offset) = var->subs[p++]; break; case SNMP_SYNTAX_OCTETSTRING: if (var->len < p + 1) { seterr(&snmp_client, "bad index: need string length"); goto err; } len = var->subs[p++]; if (var->len < p + len) { seterr(&snmp_client, "bad index: string too short"); goto err; } if ((ptr = malloc(len + 1)) == NULL) { seterr(&snmp_client, "no memory for index string"); goto err; } for (j = 0; j < len; j++) { if (var->subs[p] > UCHAR_MAX) { seterr(&snmp_client, "bad index: char too large"); free(ptr); goto err; } ptr[j] = var->subs[p++]; } ptr[j] = '\0'; *(u_char **)(void *)((u_char *)e + work->descr->entries[i].offset) = ptr; *(size_t *)(void *)((u_char *)e + work->descr->entries[i].offset + sizeof(u_char *)) = len; break; case SNMP_SYNTAX_OID: if (var->len < p + 1) { seterr(&snmp_client, "bad index: need oid length"); goto err; } oid.len = var->subs[p++]; if (var->len < p + oid.len) { seterr(&snmp_client, "bad index: oid too short"); goto err; } for (j = 0; j < oid.len; j++) oid.subs[j] = var->subs[p++]; *(struct asn_oid *)(void *)((u_char *)e + work->descr->entries[i].offset) = oid; break; case SNMP_SYNTAX_IPADDRESS: if (var->len < p + 4) { seterr(&snmp_client, "bad index: need ip-address"); goto err; } for (j = 0; j < 4; j++) { if (var->subs[p] > 0xff) { seterr(&snmp_client, "bad index: ipaddress too large"); goto err; } ((u_char *)e + work->descr->entries[i].offset)[j] = var->subs[p++]; } break; case SNMP_SYNTAX_GAUGE: if (var->len < p + 1) { seterr(&snmp_client, "bad index: need unsigned"); goto err; } if (var->subs[p] > UINT32_MAX) { seterr(&snmp_client, "bad index: unsigned too large"); goto err; } *(uint32_t *)(void *)((u_char *)e + work->descr->entries[i].offset) = var->subs[p++]; break; case SNMP_SYNTAX_COUNTER: case SNMP_SYNTAX_TIMETICKS: case SNMP_SYNTAX_COUNTER64: case SNMP_SYNTAX_NULL: case SNMP_SYNTAX_NOSUCHOBJECT: case SNMP_SYNTAX_NOSUCHINSTANCE: case SNMP_SYNTAX_ENDOFMIBVIEW: abort(); } e->found |= (uint64_t)1 << i; } /* link into the correct place */ e1 = TAILQ_FIRST(work->table); w1 = TAILQ_FIRST(&work->worklist); while (e1 != NULL) { if (asn_compare_oid(&w1->index, &w->index) > 0) break; e1 = TAILQ_NEXT(e1, link); w1 = TAILQ_NEXT(w1, link); } if (e1 == NULL) { TAILQ_INSERT_TAIL(work->table, e, link); TAILQ_INSERT_TAIL(&work->worklist, w, link); } else { TAILQ_INSERT_BEFORE(e1, e, link); TAILQ_INSERT_BEFORE(w1, w, link); } return (e); err: /* * Error happend. Free all octet string index parts and the entry * itself. */ for (i = 0; i < work->descr->index_size; i++) { if (work->descr->entries[i].syntax == SNMP_SYNTAX_OCTETSTRING && (e->found & ((uint64_t)1 << i))) free(*(void **)(void *)((u_char *)e + work->descr->entries[i].offset)); } free(e); free(w); return (NULL); } /* * Assign the value */ static int table_value(const struct snmp_table *descr, struct entry *e, const struct snmp_value *b) { u_int i; u_char *ptr; for (i = descr->index_size; descr->entries[i].syntax != SNMP_SYNTAX_NULL; i++) if (descr->entries[i].subid == b->var.subs[descr->table.len + 1]) break; if (descr->entries[i].syntax == SNMP_SYNTAX_NULL) return (0); /* check syntax */ if (b->syntax != descr->entries[i].syntax) { seterr(&snmp_client, "bad syntax (%u instead of %u)", b->syntax, descr->entries[i].syntax); return (-1); } switch (b->syntax) { case SNMP_SYNTAX_INTEGER: *(int32_t *)(void *)((u_char *)e + descr->entries[i].offset) = b->v.integer; break; case SNMP_SYNTAX_OCTETSTRING: if ((ptr = malloc(b->v.octetstring.len + 1)) == NULL) { seterr(&snmp_client, "no memory for string"); return (-1); } memcpy(ptr, b->v.octetstring.octets, b->v.octetstring.len); ptr[b->v.octetstring.len] = '\0'; *(u_char **)(void *)((u_char *)e + descr->entries[i].offset) = ptr; *(size_t *)(void *)((u_char *)e + descr->entries[i].offset + sizeof(u_char *)) = b->v.octetstring.len; break; case SNMP_SYNTAX_OID: *(struct asn_oid *)(void *)((u_char *)e + descr->entries[i].offset) = b->v.oid; break; case SNMP_SYNTAX_IPADDRESS: memcpy((u_char *)e + descr->entries[i].offset, b->v.ipaddress, 4); break; case SNMP_SYNTAX_COUNTER: case SNMP_SYNTAX_GAUGE: case SNMP_SYNTAX_TIMETICKS: *(uint32_t *)(void *)((u_char *)e + descr->entries[i].offset) = b->v.uint32; break; case SNMP_SYNTAX_COUNTER64: *(uint64_t *)(void *)((u_char *)e + descr->entries[i].offset) = b->v.counter64; break; case SNMP_SYNTAX_NULL: case SNMP_SYNTAX_NOSUCHOBJECT: case SNMP_SYNTAX_NOSUCHINSTANCE: case SNMP_SYNTAX_ENDOFMIBVIEW: abort(); } e->found |= (uint64_t)1 << i; return (0); } /* * Initialize the first PDU to send */ static void table_init_pdu(const struct snmp_table *descr, struct snmp_pdu *pdu) { if (snmp_client.version == SNMP_V1) snmp_pdu_create(pdu, SNMP_PDU_GETNEXT); else { snmp_pdu_create(pdu, SNMP_PDU_GETBULK); pdu->error_index = 10; } if (descr->last_change.len != 0) { pdu->bindings[pdu->nbindings].syntax = SNMP_SYNTAX_NULL; pdu->bindings[pdu->nbindings].var = descr->last_change; pdu->nbindings++; if (pdu->version != SNMP_V1) pdu->error_status++; } pdu->bindings[pdu->nbindings].var = descr->table; pdu->bindings[pdu->nbindings].syntax = SNMP_SYNTAX_NULL; pdu->nbindings++; } /* * Return code: * 0 - End Of Table * -1 - Error * -2 - Last change changed - again * +1 - ok, continue */ static int table_check_response(struct tabwork *work, const struct snmp_pdu *resp) { const struct snmp_value *b; struct entry *e; if (resp->error_status != SNMP_ERR_NOERROR) { if (snmp_client.version == SNMP_V1 && resp->error_status == SNMP_ERR_NOSUCHNAME && resp->error_index == (work->descr->last_change.len == 0) ? 1 : 2) /* EOT */ return (0); /* Error */ seterr(&snmp_client, "error fetching table: status=%d index=%d", resp->error_status, resp->error_index); return (-1); } for (b = resp->bindings; b < resp->bindings + resp->nbindings; b++) { if (work->descr->last_change.len != 0 && b == resp->bindings) { if (!asn_is_suboid(&work->descr->last_change, &b->var) || b->var.len != work->descr->last_change.len + 1 || b->var.subs[work->descr->last_change.len] != 0) { seterr(&snmp_client, "last_change: bad response"); return (-1); } if (b->syntax != SNMP_SYNTAX_TIMETICKS) { seterr(&snmp_client, "last_change: bad syntax %u", b->syntax); return (-1); } if (work->first) { work->last_change = b->v.uint32; work->first = 0; } else if (work->last_change != b->v.uint32) { if (++work->iter >= work->descr->max_iter) { seterr(&snmp_client, "max iteration count exceeded"); return (-1); } table_free(work, 1); return (-2); } continue; } if (!asn_is_suboid(&work->descr->table, &b->var) || b->syntax == SNMP_SYNTAX_ENDOFMIBVIEW) return (0); if ((e = table_find(work, &b->var)) == NULL) return (-1); if (table_value(work->descr, e, b)) return (-1); } return (+1); } /* * Check table consistency */ static int table_check_cons(struct tabwork *work) { struct entry *e; TAILQ_FOREACH(e, work->table, link) if ((e->found & work->descr->req_mask) != work->descr->req_mask) { if (work->descr->last_change.len == 0) { if (++work->iter >= work->descr->max_iter) { seterr(&snmp_client, "max iteration count exceeded"); return (-1); } return (-2); } seterr(&snmp_client, "inconsistency detected %llx %llx", e->found, work->descr->req_mask); return (-1); } return (0); } /* * Fetch a table. Returns 0 if ok, -1 on errors. * This is the synchronous variant. */ int snmp_table_fetch(const struct snmp_table *descr, void *list) { struct snmp_pdu resp; struct tabwork work; int ret; work.descr = descr; work.table = (struct table *)list; work.iter = 0; TAILQ_INIT(work.table); TAILQ_INIT(&work.worklist); work.callback = NULL; work.arg = NULL; again: /* * We come to this label when the code detects that the table * has changed while fetching it. */ work.first = 1; work.last_change = 0; table_init_pdu(descr, &work.pdu); for (;;) { if (snmp_dialog(&work.pdu, &resp)) { table_free(&work, 1); return (-1); } if ((ret = table_check_response(&work, &resp)) == 0) { snmp_pdu_free(&resp); break; } if (ret == -1) { snmp_pdu_free(&resp); table_free(&work, 1); return (-1); } if (ret == -2) { snmp_pdu_free(&resp); goto again; } work.pdu.bindings[work.pdu.nbindings - 1].var = resp.bindings[resp.nbindings - 1].var; snmp_pdu_free(&resp); } if ((ret = table_check_cons(&work)) == -1) { table_free(&work, 1); return (-1); } if (ret == -2) { table_free(&work, 1); goto again; } /* * Free index list */ table_free(&work, 0); return (0); } /* * Callback for table */ static void table_cb(struct snmp_pdu *req __unused, struct snmp_pdu *resp, void *arg) { struct tabwork *work = arg; int ret; if (resp == NULL) { /* timeout */ seterr(&snmp_client, "no response to fetch table request"); table_free(work, 1); work->callback(work->table, work->arg, -1); free(work); return; } if ((ret = table_check_response(work, resp)) == 0) { /* EOT */ snmp_pdu_free(resp); if ((ret = table_check_cons(work)) == -1) { /* error happend */ table_free(work, 1); work->callback(work->table, work->arg, -1); free(work); return; } if (ret == -2) { /* restart */ again: table_free(work, 1); work->first = 1; work->last_change = 0; table_init_pdu(work->descr, &work->pdu); if (snmp_pdu_send(&work->pdu, table_cb, work) == -1) { work->callback(work->table, work->arg, -1); free(work); return; } return; } /* * Free index list */ table_free(work, 0); work->callback(work->table, work->arg, 0); free(work); return; } if (ret == -1) { /* error */ snmp_pdu_free(resp); table_free(work, 1); work->callback(work->table, work->arg, -1); free(work); return; } if (ret == -2) { /* again */ snmp_pdu_free(resp); goto again; } /* next part */ work->pdu.bindings[work->pdu.nbindings - 1].var = resp->bindings[resp->nbindings - 1].var; snmp_pdu_free(resp); if (snmp_pdu_send(&work->pdu, table_cb, work) == -1) { table_free(work, 1); work->callback(work->table, work->arg, -1); free(work); return; } } int snmp_table_fetch_async(const struct snmp_table *descr, void *list, snmp_table_cb_f func, void *arg) { struct tabwork *work; if ((work = malloc(sizeof(*work))) == NULL) { seterr(&snmp_client, "%s", strerror(errno)); return (-1); } work->descr = descr; work->table = (struct table *)list; work->iter = 0; TAILQ_INIT(work->table); TAILQ_INIT(&work->worklist); work->callback = func; work->arg = arg; /* * Start by sending the first PDU */ work->first = 1; work->last_change = 0; table_init_pdu(descr, &work->pdu); if (snmp_pdu_send(&work->pdu, table_cb, work) == -1) { free(work); work = NULL; return (-1); } return (0); } /* * Append an index to an oid */ int snmp_oid_append(struct asn_oid *oid, const char *fmt, ...) { va_list va; int size; char *nextptr; const u_char *str; size_t len; struct in_addr ina; int ret; va_start(va, fmt); size = 0; ret = 0; while (*fmt != '\0') { switch (*fmt++) { case 'i': /* just an integer more */ if (oid->len + 1 > ASN_MAXOIDLEN) { warnx("%s: OID too long for integer", __func__); ret = -1; break; } oid->subs[oid->len++] = va_arg(va, asn_subid_t); break; case 'a': /* append an IP address */ if (oid->len + 4 > ASN_MAXOIDLEN) { warnx("%s: OID too long for ip-addr", __func__); ret = -1; break; } ina = va_arg(va, struct in_addr); ina.s_addr = ntohl(ina.s_addr); oid->subs[oid->len++] = (ina.s_addr >> 24) & 0xff; oid->subs[oid->len++] = (ina.s_addr >> 16) & 0xff; oid->subs[oid->len++] = (ina.s_addr >> 8) & 0xff; oid->subs[oid->len++] = (ina.s_addr >> 0) & 0xff; break; case 's': /* append a null-terminated string, * length is computed */ str = (const u_char *)va_arg(va, const char *); len = strlen((const char *)str); if (oid->len + len + 1 > ASN_MAXOIDLEN) { warnx("%s: OID too long for string", __func__); ret = -1; break; } oid->subs[oid->len++] = len; while (len--) oid->subs[oid->len++] = *str++; break; case '(': /* the integer value between ( and ) is stored * in size */ size = strtol(fmt, &nextptr, 10); if (*nextptr != ')') abort(); fmt = ++nextptr; break; case 'b': /* append `size` characters */ str = (const u_char *)va_arg(va, const char *); if (oid->len + size > ASN_MAXOIDLEN) { warnx("%s: OID too long for string", __func__); ret = -1; break; } while (size--) oid->subs[oid->len++] = *str++; break; case 'c': /* get size and the octets from the arguments */ size = va_arg(va, size_t); str = va_arg(va, const u_char *); if (oid->len + size + 1 > ASN_MAXOIDLEN) { warnx("%s: OID too long for string", __func__); ret = -1; break; } oid->subs[oid->len++] = size; while (size--) oid->subs[oid->len++] = *str++; break; default: abort(); } } va_end(va); return (ret); } /* * Initialize a client structure */ void snmp_client_init(struct snmp_client *c) { memset(c, 0, sizeof(*c)); c->version = SNMP_V2c; c->trans = SNMP_TRANS_UDP; c->chost = NULL; c->cport = NULL; strcpy(c->read_community, "public"); strcpy(c->write_community, "private"); c->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_family = snmp_client.trans == SNMP_TRANS_UDP ? AF_INET: + AF_INET6; 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); (void)close(snmp_client.fd); snmp_client.fd = -1; 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: + case SNMP_TRANS_UDP6: if (open_client_udp(host, port) != 0) return (-1); break; case SNMP_TRANS_LOC_DGRAM: case SNMP_TRANS_LOC_STREAM: if (open_client_local(host) != 0) 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 = calloc(1, 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 = calloc(1, 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_free(&req); 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; snmp_pdu_free(&req); snmp_pdu_free(&resp); 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 +/** + * Try to get a transport identifier which is a leading alphanumeric string + * (starting with '_' or a letter and including also '_') terminated by + * a double colon. The string may not be empty. The transport identifier + * is optional. * - * [trans::][community@][server][:port] + * \param sc client struct to set errors + * \param strp possible start of transport; updated to point to + * the next character to parse + * + * \return end of transport; equals *strp if there is none; NULL if there + * was an error */ -int -snmp_parse_server(struct snmp_client *sc, const char *str) +static inline const char * +get_transp(struct snmp_client *sc, const char **strp) { - const char *p, *s = str; + const char *p = *strp; - /* look for a double colon */ - for (p = s; *p != '\0'; p++) { - if (*p == '\\' && p[1] != '\0') { + if (isascii(*p) && (isalpha(*p) || *p == '_')) { + p++; + while (isascii(*p) && (isalnum(*p) || *p == '_')) p++; - continue; + if (p[0] == ':' && p[1] == ':') { + *strp = p + 2; + return (p); } - 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; + if (p[0] == ':' && p[1] == ':') { + seterr(sc, "empty transport specifier"); + return (NULL); } + return (*strp); +} - /* look for a @ */ - for (p = s; *p != '\0'; p++) { - if (*p == '\\' && p[1] != '\0') { - p++; - continue; - } - if (*p == '@') - break; +/** + * Try to get community string. Eat everything up to the last @ (if there is + * any) but only if it is not longer than SNMP_COMMUNITY_MAXLEN. Empty + * community strings are legal. + * + * \param sc client struct to set errors + * \param strp possible start of community; updated to the point to + * the next character to parse + * + * \return end of community; equals *strp if there is none; NULL if there + * was an error + */ +static inline const char * +get_comm(struct snmp_client *sc, const char **strp) +{ + const char *p = strrchr(*strp, '@'); + + if (p == NULL) + /* no community string */ + return (*strp); + + if (p - *strp > SNMP_COMMUNITY_MAXLEN) { + seterr(sc, "community string too long '%.*s'", + p - *strp, *strp); + return (NULL); } - if (*p != '\0') { - if (p - s > SNMP_COMMUNITY_MAXLEN) { - seterr(sc, "community string too long"); - return (-1); + *strp = p + 1; + return (p); +} + +/** + * Try to get an IPv6 address. This starts with an [ and should end with an ] + * and everything between should be not longer than INET6_ADDRSTRLEN and + * parseable by inet_pton(). + * + * \param sc client struct to set errors + * \param strp possible start of IPv6 address (the '['); updated to point to + * the next character to parse (the one after the closing ']') + * + * \return end of address (equals *strp + 1 if there is none) or NULL + * on errors + */ +static inline const char * +get_ipv6(struct snmp_client *sc, const char **strp) +{ + char str[INET6_ADDRSTRLEN + IF_NAMESIZE]; + struct addrinfo hints, *res; + int error; + + if (**strp != '[') + return (*strp + 1); + + const char *p = *strp + 1; + while (*p != ']' ) { + if (*p == '\0') { + seterr(sc, "unterminated IPv6 address '%.*s'", + p - *strp, *strp); + return (NULL); } - 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; + p++; } - /* look for a colon */ - for (p = s; *p != '\0'; p++) { - if (*p == '\\' && p[1] != '\0') { - p++; - continue; - } - if (*p == ':') - break; + if (p - *strp > INET6_ADDRSTRLEN + IF_NAMESIZE) { + seterr(sc, "IPv6 address too long '%.*s'", p - *strp, *strp); + return (NULL); } - 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'; + strncpy(str, *strp + 1, p - (*strp + 1)); + str[p - (*strp + 1)] = '\0'; + + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_CANONNAME | AI_NUMERICHOST; + hints.ai_family = AF_INET6; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_protocol = IPPROTO_UDP; + error = getaddrinfo(str, NULL, &hints, &res); + if (error != 0) { + seterr(sc, "%s: %s", str, gai_strerror(error)); + return (NULL); + } + freeaddrinfo(res); + *strp = p + 1; + return (p); +} + +/** + * Try to get an IPv4 address. This starts with a digit and consists of digits + * and dots, is not longer INET_ADDRSTRLEN and must be parseable by + * inet_aton(). + * + * \param sc client struct to set errors + * \param strp possible start of IPv4 address; updated to point to the + * next character to parse + * + * \return end of address (equals *strp if there is none) or NULL + * on errors + */ +static inline const char * +get_ipv4(struct snmp_client *sc, const char **strp) +{ + const char *p = *strp; + + while (isascii(*p) && (isdigit(*p) || *p == '.')) + p++; + + if (p - *strp > INET_ADDRSTRLEN) { + seterr(sc, "IPv4 address too long '%.*s'", p - *strp, *strp); + return (NULL); + } + if (*strp == p) + return *strp; + + char str[INET_ADDRSTRLEN + 1]; + strncpy(str, *strp, p - *strp); + str[p - *strp] = '\0'; + + struct in_addr addr; + if (inet_aton(str, &addr) != 1) { + seterr(sc, "illegal IPv4 address '%s'", str); + return (NULL); + } + + *strp = p; + return (p); +} + +/** + * Try to get a hostname. This includes everything up to but not including + * the last colon (if any). There is no length restriction. + * + * \param sc client struct to set errors + * \param strp possible start of hostname; updated to point to the next + * character to parse (the trailing NUL character or the last + * colon) + * + * \return end of address (equals *strp if there is none) + */ +static inline const char * +get_host(struct snmp_client *sc __unused, const char **strp) +{ + const char *p = strrchr(*strp, ':'); + + if (p == NULL) { + *strp += strlen(*strp); + return (*strp); + } + + *strp = p; + return (p); +} + +/** + * Try to get a port number. This start with a colon and extends to the end + * of string. The port number must not be empty. + * + * \param sc client struct to set errors + * \param strp possible start of port specification; if this points to a + * colon there is a port specification + * + * \return end of port number (equals *strp if there is none); NULL + * if there is no port number + */ +static inline const char * +get_port(struct snmp_client *sc, const char **strp) +{ + if (**strp != ':') + return (*strp + 1); + + if ((*strp)[1] == '\0') { + seterr(sc, "empty port name"); + return (NULL); + } + + *strp += strlen(*strp); + return (*strp); +} + +/** + * Save the string in the range given by two pointers. + * + * \param sc client struct to set errors + * \param s begin and end pointers + * + * \return freshly allocated copy of the string between s[0] and s[1] + */ +static inline char * +save_str(struct snmp_client *sc, const char *const s[2]) +{ + char *m; + + if ((m = malloc(s[1] - s[0] + 1)) == NULL) { + seterr(sc, "%s: %s", __func__, strerror(errno)); + return (NULL); + } + strncpy(m, s[0], s[1] - s[0]); + m[s[1] - s[0]] = '\0'; + + return (m); +} + +/** + * Parse a server specification. All parts are optional: + * + * [::][@][][:] + * + * The transport string consists of letters, digits or '_' and starts with + * a letter or digit. It is terminated by two colons and may not be empty. + * + * The community string is terminated by the last '@' and does not exceed + * SNMP_COMMUNITY_MAXLEN. It may be empty. + * + * The host or ip is either an IPv4 address (as parsed by inet_pton()), an + * IPv6 address in '[' and ']' and parseable by inet_aton() or a hostname + * terminated by the last colon or by the NUL character. + * + * The port number may be specified numerically or symbolically and starts + * with the last colon. + * + * The functions sets the chost, cport, trans, read_community and + * write_community fields on success and the error field on errors. + * The chost and cport fields are allocated by malloc(3), their previous + * content is deallocated by free(3). + * + * The function explicitly allows mismatches between the transport and + * the address type in order to support IPv4 in IPv6 addresses. + * + * \param sc client struct to fill + * \param str string to parse + * + * \return 0 on success and -1 on errors + */ +int +snmp_parse_server(struct snmp_client *sc, const char *str) +{ +#if DEBUG_PARSE + const char *const orig = str; +#endif + + const char *const trans_list[] = { + [SNMP_TRANS_UDP] = "udp", + [SNMP_TRANS_LOC_DGRAM] = "dgram", + [SNMP_TRANS_LOC_STREAM] = "stream", + [SNMP_TRANS_UDP6] = "udp6", + }; + + /* parse input */ + const char *const transp[2] = { + str, + get_transp(sc, &str), + }; + if (transp[1] == NULL) + return (-1); + + const char *const comm[2] = { + str, + get_comm(sc, &str), + }; + if (comm[1] == NULL) + return (-1); + + const char *const ipv6[2] = { + str + 1, + get_ipv6(sc, &str), + }; + if (ipv6[1] == NULL) + return (-1); + + const char *ipv4[2] = { + str, + str, + }; + + const char *host[2] = { + str, + str, + }; + + if (ipv6[0] == ipv6[1]) { + ipv4[1] = get_ipv4(sc, &str); + + if (ipv4[0] == ipv4[1]) + host[1] = get_host(sc, &str); + } + + const char *port[2] = { + str + 1, + get_port(sc, &str), + }; + if (port[1] == NULL) + return (-1); + + if (*str != '\0') { + seterr(sc, "junk at end of server specification '%s'", str); + return (-1); + } + +#if DEBUG_PARSE + printf("transp: %zu %zu\n", transp[0] - orig, transp[1] - orig); + printf("comm: %zu %zu\n", comm[0] - orig, comm[1] - orig); + printf("ipv6: %zu %zu\n", ipv6[0] - orig, ipv6[1] - orig); + printf("ipv4: %zu %zu\n", ipv4[0] - orig, ipv4[1] - orig); + printf("host: %zu %zu\n", host[0] - orig, host[1] - orig); + printf("port: %zu %zu\n", port[0] - orig, port[1] - orig); +#endif + + /* analyse and allocate */ + int i = -1; + if (transp[0] != transp[1]) { + for (i = 0; i < (int)nitems(trans_list); i++) { + if (trans_list[i] != NULL && + strlen(trans_list[i]) == (size_t)(transp[1] - + transp[0]) && !strncmp(trans_list[i], transp[0], + transp[1] - transp[0])) + break; } - /* port */ - free(sc->cport); - if ((sc->cport = strdup(p + 1)) == NULL) { - seterr(sc, "%s", strerror(errno)); + + if (i == (int)nitems(trans_list)) { + seterr(sc, "unknown transport specifier '%.*s'", + transp[1] - transp[0], transp[0]); return (-1); } + } - } else if (p > s) { - /* host */ - free(sc->chost); - if ((sc->chost = strdup(s)) == NULL) { - seterr(sc, "%s", strerror(errno)); + char *chost; + + if (ipv6[0] != ipv6[1]) { + if ((chost = save_str(sc, ipv6)) == NULL) return (-1); + if (i == -1) + i = SNMP_TRANS_UDP6; + } else if (ipv4[0] != ipv4[1]) { + if ((chost = save_str(sc, ipv4)) == NULL) + return (-1); + if (i == -1) + i = SNMP_TRANS_UDP; + } else { + if ((chost = save_str(sc, host)) == NULL) + return (-1); + + if (i == -1) { + /* Default transport is UDP unless the host contains + * a slash in which case we default to DGRAM. */ + i = SNMP_TRANS_UDP; + for (const char *p = host[0]; p < host[1]; p++) + if (*p == '/') { + i = SNMP_TRANS_LOC_DGRAM; + break; + } } } + + char *cport = save_str(sc, port); + if (cport == NULL) { + free(chost); + return (-1); + } + + /* commit */ + sc->trans = i; + + strncpy(sc->read_community, comm[0], comm[1] - comm[0]); + sc->read_community[comm[1] - comm[0]] = '\0'; + strncpy(sc->write_community, comm[0], comm[1] - comm[0]); + sc->write_community[comm[1] - comm[0]] = '\0'; + + free(sc->chost); + sc->chost = chost; + free(sc->cport); + sc->cport = cport; + return (0); } Index: head/contrib/bsnmp/lib/snmpclient.h =================================================================== --- head/contrib/bsnmp/lib/snmpclient.h (revision 345796) +++ head/contrib/bsnmp/lib/snmpclient.h (revision 345797) @@ -1,201 +1,202 @@ /* * 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 +#define SNMP_TRANS_UDP6 3 /* type of callback function for responses * this callback function is responsible for free() any memory associated with * any of the PDUs. Therefor it may call snmp_pdu_free() */ typedef void (*snmp_send_cb_f)(struct snmp_pdu *, struct snmp_pdu *, void *); /* type of callback function for timeouts */ typedef void (*snmp_timeout_cb_f)(void * ); /* timeout start function */ typedef void *(*snmp_timeout_start_f)(struct timeval *timeout, snmp_timeout_cb_f callback, void *); /* timeout stop function */ typedef void (*snmp_timeout_stop_f)(void *timeout_id); /* * Client context. */ struct snmp_client { enum snmp_version version; int 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/tc.def =================================================================== --- head/contrib/bsnmp/lib/tc.def (revision 345796) +++ head/contrib/bsnmp/lib/tc.def (revision 345797) @@ -1,48 +1,56 @@ #- # 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 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$ # typedef RowStatus ENUM ( 1 active 2 notInService 3 notReady 4 createAndGo 5 createAndWait 6 destroy ) typedef StorageType ENUM ( 1 other 2 volatile 3 nonVolatile 4 permanent 5 readOnly ) +typedef InetAddressType ENUM ( + 0 unknown + 1 ipv4 + 2 ipv6 + 3 ipv4z + 4 ipv6z + 16 dns +) Index: head/contrib/bsnmp/snmpd/BEGEMOT-SNMPD.txt =================================================================== --- head/contrib/bsnmp/snmpd/BEGEMOT-SNMPD.txt (revision 345796) +++ head/contrib/bsnmp/snmpd/BEGEMOT-SNMPD.txt (revision 345797) @@ -1,579 +1,691 @@ -- -- Copyright (c) 2001-2003 -- Fraunhofer Institute for Open Communication Systems (FhG Fokus). -- All rights reserved. -- +-- Copyright (c) 2018 +-- 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/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 + Integer32, Unsigned32, IpAddress FROM SNMPv2-SMI TEXTUAL-CONVENTION, TruthValue, RowStatus FROM SNMPv2-TC - MODULE-COMPLIANCE, OBJECT-GROUP - FROM SNMPv2-CONF + InetAddressType, InetAddress, InetPortNumber + FROM INET-ADDRESS-MIB begemot FROM BEGEMOT-MIB; begemotSnmpd MODULE-IDENTITY - LAST-UPDATED "201801190000Z" + LAST-UPDATED "201808080000Z" 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." + REVISION "201808080000Z" + DESCRIPTION + "Add the begemotSnmpdTransInetTable." ::= { 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) + SYNTAX Integer32 (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) + SYNTAX Integer32 (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) + SYNTAX Integer32 (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 + STATUS deprecated 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 + STATUS deprecated 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 + STATUS deprecated DESCRIPTION "The IP address to bind to." ::= { begemotSnmpdPortEntry 1 } begemotSnmpdPortPort OBJECT-TYPE - SYNTAX INTEGER (1..65535) + SYNTAX Integer32 (1..65535) MAX-ACCESS not-accessible - STATUS current + STATUS deprecated 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 + STATUS deprecated 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, - begemotSnmpdCommunityPermission INTEGER + begemotSnmpdCommunityPermission Unsigned32 } 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 } begemotSnmpdCommunityPermission OBJECT-TYPE - SYNTAX INTEGER (1..4294967295) + SYNTAX Unsigned32 (1..4294967295) MAX-ACCESS not-accessible STATUS current DESCRIPTION "The numerical value of access rights granted to the community." ::= { begemotSnmpdCommunityEntry 5 } -- -- 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) + SYNTAX Integer32 (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 } +-- ---------------------------------------------------------------------- -- +-- Internet port table. +-- +BegemotSnmpdTransportProto ::= TEXTUAL-CONVENTION + STATUS current + DESCRIPTION + "A value that represents the type of protocol to be used for + listening on a socket. The following protocols are currently + used: + + udp(1) Use UDP for IPv4 and IPv6 sockets." + SYNTAX INTEGER { + udp(1) + } + +begemotSnmpdTransInetTable OBJECT-TYPE + SYNTAX SEQUENCE OF BegemotSnmpdTransInetEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "This table contains all the ports the daemon should listen on. + Entries can be created at initialization time via the config + file or at run time via a SET. One row can map to several open + sockets in the case of InetAddressType::dns rows. These rows + open one socket for each address returned by getaddrinfo(3). + for SNMP messages." + ::= { begemotSnmpdObjects 11 } + +begemotSnmpdTransInetEntry OBJECT-TYPE + SYNTAX BegemotSnmpdTransInetEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A row of the internet port table. Each row may map to one or + more listening sockets." + INDEX { + begemotSnmpdTransInetAddressType, + begemotSnmpdTransInetAddress, + begemotSnmpdTransInetPort, + begemotSnmpdTransInetProto + } + ::= { begemotSnmpdTransInetTable 1 } + +BegemotSnmpdTransInetEntry ::= SEQUENCE { + begemotSnmpdTransInetAddressType InetAddressType, + begemotSnmpdTransInetAddress InetAddress, + begemotSnmpdTransInetPort InetPortNumber, + begemotSnmpdTransInetProto BegemotSnmpdTransportProto, + begemotSnmpdTransInetStatus RowStatus +} + +begemotSnmpdTransInetAddressType OBJECT-TYPE + SYNTAX InetAddressType + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The type of the address. Only ipv4, ipv6, ipv6z and dns are + supported." + ::= { begemotSnmpdTransInetEntry 1 } + +begemotSnmpdTransInetAddress OBJECT-TYPE + SYNTAX InetAddress (SIZE (0..64)) + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The address. For ipv4 addresses the length must be 4, ipv6 + addresses have a length of 16 and ipv6z addresses a length of + 20 where the last four bytes are the interface index in big + endian format. dns addresses may be of zero-length in which case + getaddrinfo() generates INADDR_ANY and its IPv6 equivalent. dns + addresses will open a socket for all addresses returned by + getaddrinfo()." + ::= { begemotSnmpdTransInetEntry 2 } + +begemotSnmpdTransInetPort OBJECT-TYPE + SYNTAX InetPortNumber (1..65535) + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The port to listen on for SNMP messages." + ::= { begemotSnmpdTransInetEntry 3 } + +begemotSnmpdTransInetProto OBJECT-TYPE + SYNTAX BegemotSnmpdTransportProto + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The protocol to use. Currently only the value udp(1) is supported." + ::= { begemotSnmpdTransInetEntry 4 } + +begemotSnmpdTransInetStatus OBJECT-TYPE + SYNTAX RowStatus + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The status of the conceptual row. A row may be created using + createAndGo(4) or createAndWait(5). An inactive row can be + activated writing active(1) and an active row can be inactivated + by writing notInService(2). Finally active or inactive rows can be + deleted by writing the value destroy(6). The value of this field + will never read as notReady(3)." + ::= { begemotSnmpdTransInetEntry 5 } + +-- -- XXX These should go into their own MIB -- begemotSnmpdTransUdp OBJECT IDENTIFIER ::= { begemotSnmpdTransportMappings 2 } begemotSnmpdTransLsock OBJECT IDENTIFIER ::= { begemotSnmpdTransportMappings 3 } +begemotSnmpdTransInet OBJECT IDENTIFIER ::= { begemotSnmpdTransportMappings 4 } END Index: head/contrib/bsnmp/snmpd/main.c =================================================================== --- head/contrib/bsnmp/snmpd/main.c (revision 345796) +++ head/contrib/bsnmp/snmpd/main.c (revision 345797) @@ -1,3111 +1,3144 @@ /* * 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" +#include "trans_inet.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; ret = SNMPD_INPUT_OK; /* 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); 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; port->transport = t; TAILQ_FOREACH(p, &t->table, link) { if (asn_compare_oid(&p->index, &port->index) > 0) { TAILQ_INSERT_BEFORE(p, port, link); return; } } 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, RPOLL_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]; + char client[INET6_ADDRSTRLEN]; #endif ret = tport->transport->vtab->recv(tport, 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) || (comm != NULL && comm->private != COMM_WRITE && (pdu.type == SNMP_PDU_SET || comm->private != COMM_READ)))) { snmpd_stats.inBadCommunityUses++; snmp_pdu_free(&pdu); snmp_input_consume(pi); if (snmpd.auth_traps) snmp_send_trap(&oid_authenticationFailure, (struct snmp_value *)NULL); return (0); } /* * Execute it. */ if ((sndbuf = buf_alloc(1)) == NULL) { snmpd_stats.silentDrops++; snmp_pdu_free(&pdu); snmp_input_consume(pi); return (0); } ferr = snmp_input_finish(&pdu, pi->buf, pi->length, sndbuf, &sndlen, "SNMP", ierr, vi, NULL); if (ferr == SNMPD_INPUT_OK) { - slen = tport->transport->vtab->send(tport, sndbuf, sndlen, - pi->peer, pi->peerlen); + if (tport->transport->vtab->send != NULL) + slen = tport->transport->vtab->send(tport, sndbuf, + sndlen, pi->peer, pi->peerlen); + else + slen = tport->transport->vtab->send2(tport, sndbuf, + sndlen, pi); if (slen == -1) syslog(LOG_ERR, "send*: %m"); else if ((size_t)slen != sndlen) syslog(LOG_ERR, "send*: 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 + * Send a PDU to a given port. If this is a multi-socket port, use the + * first socket. */ 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 (trans->vtab->send != NULL) + len = trans->vtab->send(tp, sndbuf, sndlen, addr, addrlen); + else + len = trans->vtab->send2(tp, sndbuf, sndlen, NULL); 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 + * + * \param pi input instance */ void snmpd_input_close(struct port_input *pi) { - if (pi->id != NULL) + if (pi->id != NULL) { fd_deselect(pi->id); - if (pi->fd >= 0) + pi->id = NULL; + } + if (pi->fd >= 0) { (void)close(pi->fd); - if (pi->buf != NULL) + pi->fd = -1; + } + if (pi->buf != NULL) { free(pi->buf); + pi->buf = NULL; + } } /* + * Initialize an input source. + * + * \param pi input instance + */ +void +snmpd_input_init(struct port_input *pi) +{ + pi->id = NULL; + pi->fd = -1; + pi->buf = NULL; +} + +/* * 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 */ comm_define(COMM_READ, "SNMP read", NULL, NULL); comm_define(COMM_WRITE, "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"); + if (inet_trans.start() != SNMP_ERR_NOERROR) + syslog(LOG_WARNING, "cannot start INET 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 */ struct community* comm_define_ordered(u_int priv, const char *descr, struct asn_oid *idx, struct lmodule *owner, const char *str) { struct community *c, *p; u_int ncomm; ncomm = idx->subs[idx->len - 1]; /* check that community doesn't already exist */ TAILQ_FOREACH(c, &community_list, link) if (c->value == ncomm) return (c); if ((c = malloc(sizeof(struct community))) == NULL) { syslog(LOG_ERR, "%s: %m", __func__); return (NULL); } 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 (NULL); } strcpy(c->string, str); } /* * Insert ordered */ c->index = *idx; 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); } u_int comm_define(u_int priv, const char *descr, struct lmodule *owner, const char *str) { struct asn_oid idx, *p; struct community *c; 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); /* make index */ if (owner != NULL) p = &owner->index; else { p = &idx; p->len = 1; p->subs[0] = 0; } p->subs[p->len++] = ncomm; c = comm_define_ordered(priv, descr, p, owner, str); if (c == NULL) return (0); 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; strlcpy(m->section, section, sizeof(m->section)); if ((m->path = strdup(path)) == NULL) { syslog(LOG_ERR, "lm_load: %m"); goto err; } /* * 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 345796) +++ head/contrib/bsnmp/snmpd/snmpd.config (revision 345797) @@ -1,104 +1,114 @@ # # 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) # begemotSnmpdCommunityString.0.3 = "otherPublic" begemotSnmpdCommunityDisable = 1 # open standard SNMP ports -begemotSnmpdPortStatus.[$(host)].161 = 1 -begemotSnmpdPortStatus.127.0.0.1.161 = 1 +# begemotSnmpdPortStatus.[$(host)].161 = 1 +# begemotSnmpdPortStatus.127.0.0.1.161 = 1 + +# UDP over IPv4: 127.0.0.1:161 +begemotSnmpdTransInetStatus.1.4.127.0.0.1.161.1 = 4 + +# UDP over IPv6: ::1:161 +begemotSnmpdTransInetStatus.2.16.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.1.161.1 = 4 + +# Use domain name and IPv6 link-local address with scope zone id as address +# begemotSnmpdTransInetStatus.16."localhost".161.1 = 4 +# begemotSnmpdTransInetStatus.16."fe80::1%em0".161.1 = 4 # 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 345796) +++ head/contrib/bsnmp/snmpd/snmpd.h (revision 345797) @@ -1,342 +1,346 @@ /* * 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 *); +void snmpd_input_init(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 tport *, struct port_input *); + + /** send via a multi-socket port */ + ssize_t (*send2)(struct tport *, const u_char *, size_t, + 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/snmpmod.h =================================================================== --- head/contrib/bsnmp/snmpd/snmpmod.h (revision 345796) +++ head/contrib/bsnmp/snmpd/snmpmod.h (revision 345797) @@ -1,627 +1,669 @@ /* * 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. */ + +/* + * First set of macros is used when the link is embedded into sub-struct + * and links these sub-structs. The sub-struct must be the first field. + * + * The list is a list of the subfield types. + */ +#define INSERT_OBJECT_OID_LINK_INDEX_TYPE(PTR, LIST, LINK, INDEX, SUBF) do {\ + typedef __typeof ((PTR)->SUBF) _subf_type; \ + _subf_type *_lelem; \ + \ + TAILQ_FOREACH(_lelem, (LIST), LINK) \ + if (asn_compare_oid(&_lelem->INDEX, &(PTR)->SUBF.INDEX) > 0)\ + break; \ + if (_lelem == NULL) \ + TAILQ_INSERT_TAIL((LIST), &(PTR)->SUBF, LINK); \ + else \ + TAILQ_INSERT_BEFORE(_lelem, &(PTR)->SUBF, LINK); \ + } while (0) + +#define NEXT_OBJECT_OID_LINK_INDEX_TYPE(LIST, OID, SUB, LINK, INDEX, TYPE) ({\ + __typeof (TAILQ_FIRST((LIST))) _lelem; \ + \ + TAILQ_FOREACH(_lelem, (LIST), LINK) \ + if (index_compare(OID, SUB, &_lelem->INDEX) < 0) \ + break; \ + (TYPE *)(_lelem); \ + }) + +#define FIND_OBJECT_OID_LINK_INDEX_TYPE(LIST, OID, SUB, LINK, INDEX, TYPE) ({\ + __typeof (TAILQ_FIRST((LIST))) _lelem; \ + \ + TAILQ_FOREACH(_lelem, (LIST), LINK) \ + if (index_compare(OID, SUB, &_lelem->INDEX) == 0) \ + break; \ + (TYPE *)(_lelem); \ + }) + +/* + * This set of macros allows specification of the link and index name. + * The index is an OID. + */ #define INSERT_OBJECT_OID_LINK_INDEX(PTR, LIST, LINK, INDEX) do { \ __typeof (PTR) _lelem; \ \ TAILQ_FOREACH(_lelem, (LIST), LINK) \ if (asn_compare_oid(&_lelem->INDEX, &(PTR)->INDEX) > 0) \ break; \ if (_lelem == NULL) \ TAILQ_INSERT_TAIL((LIST), (PTR), LINK); \ else \ TAILQ_INSERT_BEFORE(_lelem, (PTR), LINK); \ } while (0) #define INSERT_OBJECT_INT_LINK_INDEX(PTR, LIST, LINK, INDEX) do { \ __typeof (PTR) _lelem; \ \ TAILQ_FOREACH(_lelem, (LIST), LINK) \ if ((asn_subid_t)_lelem->INDEX > (asn_subid_t)(PTR)->INDEX)\ break; \ if (_lelem == NULL) \ TAILQ_INSERT_TAIL((LIST), (PTR), LINK); \ else \ TAILQ_INSERT_BEFORE(_lelem, (PTR), LINK); \ } while (0) #define 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; uint32_t services; uint32_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); struct community *comm_define_ordered(u_int priv, const char *descr, struct asn_oid *index, struct lmodule *owner, 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_inet.c =================================================================== --- head/contrib/bsnmp/snmpd/trans_inet.c (nonexistent) +++ head/contrib/bsnmp/snmpd/trans_inet.c (revision 345797) @@ -0,0 +1,1342 @@ +/* + * Copyright (c) 2018 + * 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/snmpd/trans_udp.c,v 1.5 2005/10/04 08:46:56 brandt_h Exp $ + * + * Internet transport + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include "asn1.h" +#include "snmp.h" +#include "snmpmod.h" + +#include "snmpd.h" + +#define SNMPTREE_TYPES +#define SNMPENUM_FUNCS +#include "tree.h" +#include "oid.h" + +extern const struct transport_def inet_trans; + +struct inet_port; +struct inet_port_params; +struct port_sock; + +typedef int create_func(struct inet_port *, struct inet_port_params *); +typedef void input_func(int, void *); +typedef int activate_func(struct inet_port *); +typedef void deactivate_func(struct inet_port *); +typedef void parse_ctrl_func(struct port_sock *, const struct msghdr *); +typedef void setsrc_func(struct port_sock *, struct msghdr *); + +static create_func ipv4_create; +static input_func ipv4_input; +static activate_func ipv4_activate; +static deactivate_func ipv4_deactivate; +static parse_ctrl_func ipv4_parse_ctrl; +static setsrc_func ipv4_setsrc; + +static create_func ipv6_create; +static input_func ipv6_input; +static activate_func ipv6_activate; +static deactivate_func ipv6_deactivate; +static parse_ctrl_func ipv6_parse_ctrl; +static setsrc_func ipv6_setsrc; + +static create_func ipv6z_create; + +static create_func dns_create; +static activate_func dns_activate; +static deactivate_func dns_deactivate; + +struct port_sock { + /* common input stuff; must be first */ + struct port_input input; + + /** link field */ + TAILQ_ENTRY(port_sock) link; + + /** pointer to parent */ + struct inet_port *port; + + /** bind address */ + struct sockaddr_storage bind_addr; + + /** reply destination */ + struct sockaddr_storage ret_dest; + + /** need to set source address in reply; set for INADDR_ANY */ + bool set_ret_source; + + /** address of the receive interface */ + union { + /** IPv4 case */ + struct in_addr a4; + + /** IPv6 case */ + struct in6_pktinfo a6; + } ret_source; + + /** parse control message */ + parse_ctrl_func *parse_ctrl; + + /** set source address for a send() */ + setsrc_func *setsrc; +}; +static_assert(offsetof(struct port_sock, input) == 0, + "input not first in port_sock"); + +/** + * Table row for the inet ports. + * + * When actived each row can have one or several open sockets. For ipv6, + * ipv4 and ipv6z addresses it is always one, for dns addresses more than + * one socket can be open. + */ +struct inet_port { + /** common i/o port stuff (must be first) */ + struct tport tport; + + /** transport protocol */ + enum BegemotSnmpdTransportProto proto; + + /** row status */ + enum RowStatus row_status; + + /** socket list */ + TAILQ_HEAD(, port_sock) socks; + + /** value for InetAddressType::dns */ + char *dns_addr; + + /** port number in dns case; network byte order */ + uint16_t dns_port; + + /** create a port */ + create_func *create; + + /** activate a port */ + activate_func *activate; + + /** deactivate port */ + deactivate_func *deactivate; +}; +static_assert(offsetof(struct inet_port, tport) == 0, + "tport not first in inet_port"); + +/** to be used in bind_addr field */ +#define AF_DNS AF_VENDOR00 + +/** registered transport */ +static struct transport *my_trans; + +/** set operation */ +enum { + SET_CREATED, + SET_ACTIVATED, + SET_DEACTIVATE, + SET_DESTROY, +}; + +/** length of the control data buffer */ +static const size_t RECV_CBUF_SIZE = + MAX(CMSG_SPACE(SOCKCREDSIZE(CMGROUP_MAX)) + + CMSG_SPACE(sizeof(struct in_addr)), + CMSG_SPACE(SOCKCREDSIZE(CMGROUP_MAX)) + + CMSG_SPACE(sizeof(struct in6_pktinfo))); + +/** length of the control data buffer */ +static const size_t XMIT_CBUF_SIZE = + MAX(CMSG_SPACE(sizeof(struct in_addr)), + CMSG_SPACE(sizeof(struct in6_pktinfo))); + +/** + * Start the transport. This registers the transport with the + * transport table. + * + * \return SNMP error code + */ +static int +inet_start(void) +{ + return (trans_register(&inet_trans, &my_trans)); +} + +/** + * Stop the transport. This tries to unregister the transport which + * in turn fails if the list of ports is not empty. + * + * \return SNMP error code + */ +static int +inet_stop(int force __unused) +{ + if (my_trans != NULL) + if (trans_unregister(my_trans) != 0) + return (SNMP_ERR_GENERR); + return (SNMP_ERR_NOERROR); +} + +/** + * Deactivate SNMP port. + * + * \param tp port to close + */ +static void +deactivate_port(struct inet_port *p) +{ + p->deactivate(p); +} + +/* + * This function activates a port. For ports opened via the config files + * this is called just before entering the event loop. For ports create + * during runtime this is called when the RowStatus is set to Active or + * as second step for CreateAndGo. + * + * \param tp transport port + * + * \return SNMP error code + */ +static int +inet_activate(struct tport *tp) +{ + struct inet_port *port = (struct inet_port *)tp; + + return (port->activate(port)); +} + +/* + * Close the SNMP port if it is open and destroy it. + * + * \param tp port to close + */ +static void +inet_destroy_port(struct tport *tp) +{ + struct inet_port *port = (struct inet_port *)tp; + + deactivate_port(port); + + trans_remove_port(tp); + + free(port->dns_addr); + free(port); +} + +/** + * If the input struct has no buffer allocated yet, do it now. If allocation + * fails, read the data into a local buffer and drop it. + * + * \param pi input struct + * + * \return -1 if allocation fails, 0 otherwise + */ +static int +inet_alloc_buf(struct port_input *pi) +{ + char drop_buf[2000]; + + if (pi->buf == NULL) { + if ((pi->buf = buf_alloc(0)) == NULL) { + (void)recvfrom(pi->fd, drop_buf, sizeof(drop_buf), + 0, NULL, NULL); + return (-1); + } + pi->buflen = buf_size(0); + } + return (0); +} + +/** + * Read message into input buffer. Get also the source address and any + * control data that is available. If the message is truncated, increment + * corresponding statistics. + * + * \param pi input object + * \param msg message object to fill + * \param cbuf control data buffer + * + * \return -1 when something goes wrong, 0 othersise + */ +static int +inet_read_msg(struct port_input *pi, struct msghdr *msg, char *cbuf) +{ + struct iovec iov[1]; + + iov[0].iov_base = pi->buf; + iov[0].iov_len = pi->buflen; + + msg->msg_name = pi->peer; + msg->msg_namelen = pi->peerlen; + msg->msg_iov = iov; + msg->msg_iovlen = 1; + msg->msg_control = cbuf; + msg->msg_controllen = RECV_CBUF_SIZE; + msg->msg_flags = 0; + + memset(cbuf, 0, RECV_CBUF_SIZE); + + const ssize_t 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; + + return (0); +} + +/* + * Input available on socket. + * + * \param tp transport port + * \param pi input struct + * + * \return number of bytes received + */ +static ssize_t +inet_recv(struct tport *tp, struct port_input *pi) +{ + struct inet_port *port = __containerof(tp, struct inet_port, tport); + struct port_sock *sock = __containerof(pi, struct port_sock, input); + + assert(port->proto == BegemotSnmpdTransportProto_udp); + + if (inet_alloc_buf(pi) == -1) + return (-1); + + char cbuf[RECV_CBUF_SIZE]; + struct msghdr msg; + + if (inet_read_msg(pi, &msg, cbuf) == -1) + return (-1); + + sock->parse_ctrl(sock, &msg); + + return (0); +} + +/* + * Send message. + * + * \param tp port + * \param buf data to send + * \param len number of bytes to send + * \param addr destination address + * \param addlen destination address length + * + * \return number of bytes sent + */ +static ssize_t +inet_send2(struct tport *tp, const u_char *buf, size_t len, + struct port_input *pi) +{ + struct inet_port *p = __containerof(tp, struct inet_port, tport); + struct port_sock *s = (pi == NULL) ? TAILQ_FIRST(&p->socks) : + __containerof(pi, struct port_sock, input); + + struct iovec iov; + + iov.iov_base = __DECONST(void*, buf); + iov.iov_len = len; + + struct msghdr msg; + + msg.msg_flags = 0; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_name = (void *)pi->peer; + msg.msg_namelen = pi->peerlen; + + msg.msg_control = NULL; + msg.msg_controllen = 0; + + char cbuf[XMIT_CBUF_SIZE]; + if (s->set_ret_source) { + msg.msg_control = cbuf; + s->setsrc(s, &msg); + } + + return (sendmsg(s->input.fd, &msg, 0)); +} + +/** exported to daemon */ +const struct transport_def inet_trans = { + "inet", + OIDX_begemotSnmpdTransInet, + inet_start, + inet_stop, + inet_destroy_port, + inet_activate, + NULL, + inet_recv, + inet_send2, +}; + +struct inet_port_params { + /** index oid */ + struct asn_oid index; + + /** internet address type */ + enum InetAddressType type; + + /** internet address */ + u_char *addr; + + /** length of address */ + size_t addr_len; + + /** port number */ + uint32_t port; + + /** protocol */ + enum BegemotSnmpdTransportProto proto; +}; + +/** + * IPv4 creation stuff. Parse the index, fill socket address and creates + * a port_sock. + * + * \param port the port to create + * \param params parameters from the SNMP SET + * + * \return SNMP error + */ +static int +ipv4_create(struct inet_port *port, struct inet_port_params *params) +{ + uint32_t ip; + + if (params->addr_len != 4) + return (SNMP_ERR_INCONS_VALUE); + + memcpy(&ip, params->addr, 4); + struct port_sock *sock = calloc(1, sizeof(struct port_sock)); + if (sock == NULL) + return (SNMP_ERR_GENERR); + + snmpd_input_init(&sock->input); + + TAILQ_INSERT_HEAD(&port->socks, sock, link); + + struct sockaddr_in *sin = + (struct sockaddr_in *)&sock->bind_addr; + + sin->sin_len = sizeof(struct sockaddr_in); + sin->sin_family = AF_INET; + sin->sin_addr.s_addr = htonl(ip); + sin->sin_port = htons(params->port); + + sock->port = port; + + return (SNMP_ERR_NOERROR); +} + +/* + * An IPv4 inet port is ready. Delegate to the generic code to read the data + * and react. + * + * \param fd file descriptor that is ready + * \param udata inet_port pointer + */ +static void +ipv4_input(int fd __unused, void *udata) +{ + struct port_sock *sock = udata; + + sock->input.peerlen = sizeof(struct sockaddr_in); + snmpd_input(&sock->input, &sock->port->tport); +} + +/** + * Activate an IPv4 socket. + * + * \param sock thhe socket to activate + * + * \return error code + */ +static int +ipv4_activate_sock(struct port_sock *sock) +{ + if ((sock->input.fd = socket(PF_INET, SOCK_DGRAM, 0)) == -1) { + syslog(LOG_ERR, "creating UDP4 socket: %m"); + return (SNMP_ERR_RES_UNAVAIL); + } + + const struct sockaddr_in *sin = + (const struct sockaddr_in *)&sock->bind_addr; + + if (sin->sin_addr.s_addr == INADDR_ANY) { + /* need to know from which address to return */ + static const int on = 1; + + if (setsockopt(sock->input.fd, IPPROTO_IP, IP_RECVDSTADDR, &on, + sizeof(on)) == -1) { + syslog(LOG_ERR, "setsockopt(IP_RECVDSTADDR): %m"); + (void)close(sock->input.fd); + sock->input.fd = -1; + return (SNMP_ERR_GENERR); + } + sock->set_ret_source = true; + } + + if (bind(sock->input.fd, (const struct sockaddr *)sin, sizeof(*sin))) { + if (errno == EADDRNOTAVAIL) { + (void)close(sock->input.fd); + sock->input.fd = -1; + return (SNMP_ERR_INCONS_NAME); + } + syslog(LOG_ERR, "bind: %s:%u %m", inet_ntoa(sin->sin_addr), + ntohs(sin->sin_port)); + (void)close(sock->input.fd); + sock->input.fd = -1; + return (SNMP_ERR_GENERR); + } + + if ((sock->input.id = fd_select(sock->input.fd, ipv4_input, + sock, NULL)) == NULL) { + (void)close(sock->input.fd); + sock->input.fd = -1; + return (SNMP_ERR_GENERR); + } + sock->input.peer = (struct sockaddr *)&sock->ret_dest; + + sock->parse_ctrl = ipv4_parse_ctrl; + sock->setsrc = ipv4_setsrc; + + return (SNMP_ERR_NOERROR); +} + +/** + * Open an IPv4 socket. Make the socket, bind it and put it on the select + * list. The socket struct has already been created during creation. + * + * \param p inet port + * + * \return SNMP error code + */ +static int +ipv4_activate(struct inet_port *p) +{ + struct port_sock *sock = TAILQ_FIRST(&p->socks); + assert(sock); + assert(!TAILQ_NEXT(sock, link)); + + const int ret = ipv4_activate_sock(sock); + if (ret == SNMP_ERR_NOERROR) + p->row_status = RowStatus_active; + + return (ret); +} + +/** + * Close an IPv4 socket. Keep the sock object. + * + * \param p inet port + */ +static void +ipv4_deactivate(struct inet_port *p) +{ + struct port_sock *sock = TAILQ_FIRST(&p->socks); + assert(sock); + assert(!TAILQ_NEXT(sock, link)); + + snmpd_input_close(&sock->input); + + p->row_status = RowStatus_notInService; +} + +/** + * Parse the control data received with a UDPv4 packet. This may contain + * credentials (for a local connection) and the address of the interface + * the message was received on. If there are credentials set the priv flag + * if the effective UID is zero. + * + * \param sock the sock object + * \param msg the received message + */ +static void +ipv4_parse_ctrl(struct port_sock *sock, const struct msghdr *msg) +{ + struct sockcred *cred = NULL; + + for (struct cmsghdr *cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL; + cmsg = CMSG_NXTHDR(msg, cmsg)) { + + if (cmsg->cmsg_level == IPPROTO_IP && + cmsg->cmsg_type == IP_RECVDSTADDR) { + memcpy(&sock->ret_source.a4, CMSG_DATA(cmsg), + sizeof(struct in_addr)); + + } else if (cmsg->cmsg_level == SOL_SOCKET && + cmsg->cmsg_type == SCM_CREDS) { + cred = (struct sockcred *)(void *)CMSG_DATA(cmsg); + } + } + + sock->input.priv = 0; + if (sock->input.cred && cred) + /* remote end has sent credentials */ + sock->input.priv = (cred->sc_euid == 0); +} + +/** + * Set the source address option for IPv4 sockets. + * + * \param sock socket object + * \param msg message + */ +static void +ipv4_setsrc(struct port_sock *sock, struct msghdr *msg) +{ + struct cmsghdr *cmsg = CMSG_FIRSTHDR(msg); + + /* select outgoing interface by setting source address */ + cmsg->cmsg_level = IPPROTO_IP; + cmsg->cmsg_type = IP_SENDSRCADDR; + cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_addr)); + memcpy(CMSG_DATA(cmsg), &sock->ret_source.a4, + sizeof(struct in_addr)); + + msg->msg_controllen = CMSG_SPACE(sizeof(struct in_addr)); +} + +/** + * Common part of IPv6 creation. This is used by both ipv6_create() and + * ipv6z_create(). + * + * \param port the table row + * \param params creation parameters + * \param scope_id scope_id (0 or from index) + * + * \return SNMP_ERR_NOERROR if port has been created, error code otherwise + */ +static int +ipv6_create_common(struct inet_port *port, struct inet_port_params *params, + u_int scope_id) +{ + struct port_sock *sock = calloc(1, sizeof(struct port_sock)); + + if (sock == NULL) + return (SNMP_ERR_GENERR); + + struct sockaddr_in6 *sin = (struct sockaddr_in6 *)&sock->bind_addr; + + sin->sin6_len = sizeof(struct sockaddr_in6); + sin->sin6_family = AF_INET6; + sin->sin6_port = htons(params->port); + sin->sin6_flowinfo = 0; + sin->sin6_scope_id = scope_id; + + memcpy(sin->sin6_addr.s6_addr, params->addr, 16); + + if (IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr) && scope_id == 0) { + char buf[INET6_ADDRSTRLEN]; + syslog(LOG_INFO, "%s: link local address used without scope " + "index: %s", __func__, inet_ntop(AF_INET6, + &sin->sin6_addr, buf, sizeof(buf))); + free(sock); + return (SNMP_ERR_NO_CREATION); + } + + sock->port = port; + + snmpd_input_init(&sock->input); + TAILQ_INSERT_HEAD(&port->socks, sock, link); + + return (SNMP_ERR_NOERROR); +} + +/** + * IPv6 creation stuff. Parse the index, fill socket address and creates + * a port_sock. + * + * \param port the port to create + * \param params parameters from the SNMP SET + * + * \return SNMP error + */ +static int +ipv6_create(struct inet_port *port, struct inet_port_params *params) +{ + if (params->addr_len != 16) + return (SNMP_ERR_INCONS_VALUE); + + const int ret = ipv6_create_common(port, params, 0); + if (ret != SNMP_ERR_NOERROR) + return (ret); + + return (SNMP_ERR_NOERROR); +} + +/* + * An IPv6 inet port is ready. Delegate to the generic code to read the data + * and react. + * + * \param fd file descriptor that is ready + * \param udata inet_port pointer + */ +static void +ipv6_input(int fd __unused, void *udata) +{ + struct port_sock *sock = udata; + + sock->input.peerlen = sizeof(struct sockaddr_in6); + snmpd_input(&sock->input, &sock->port->tport); +} + +/** + * Activate an IPv6 socket. + * + * \param sock thhe socket to activate + * + * \return error code + */ +static int +ipv6_activate_sock(struct port_sock *sock) +{ + if ((sock->input.fd = socket(PF_INET6, SOCK_DGRAM, 0)) == -1) { + syslog(LOG_ERR, "creating UDP6 socket: %m"); + return (SNMP_ERR_RES_UNAVAIL); + } + + const struct sockaddr_in6 *sin = + (const struct sockaddr_in6 *)&sock->bind_addr; + + if (memcmp(&sin->sin6_addr, &in6addr_any, sizeof(in6addr_any)) == 0) { + /* need to know from which address to return */ + static const int on = 1; + + if (setsockopt(sock->input.fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, + &on, sizeof(on)) == -1) { + syslog(LOG_ERR, "setsockopt(IP6_PKTINFO): %m"); + (void)close(sock->input.fd); + sock->input.fd = -1; + return (SNMP_ERR_GENERR); + } + sock->set_ret_source = true; + } + + if (bind(sock->input.fd, (const struct sockaddr *)sin, sizeof(*sin))) { + if (community != COMM_INITIALIZE && errno == EADDRNOTAVAIL) { + (void)close(sock->input.fd); + sock->input.fd = -1; + return (SNMP_ERR_INCONS_NAME); + } + char buf[INET6_ADDRSTRLEN]; + syslog(LOG_ERR, "bind: %s:%u:%u %m", inet_ntop(AF_INET6, + &sin->sin6_addr, buf, sizeof(buf)), sin->sin6_scope_id, + ntohs(sin->sin6_port)); + (void)close(sock->input.fd); + sock->input.fd = -1; + return (SNMP_ERR_GENERR); + } + if ((sock->input.id = fd_select(sock->input.fd, ipv6_input, + sock, NULL)) == NULL) { + (void)close(sock->input.fd); + sock->input.fd = -1; + return (SNMP_ERR_GENERR); + } + sock->input.peer = (struct sockaddr *)&sock->ret_dest; + + sock->parse_ctrl = ipv6_parse_ctrl; + sock->setsrc = ipv6_setsrc; + + return (SNMP_ERR_NOERROR); +} + +/** + * Open an IPv6 socket. + * + * \param port inet port + * + * \return SNMP error code + */ +static int +ipv6_activate(struct inet_port *p) +{ + struct port_sock *sock = TAILQ_FIRST(&p->socks); + assert(sock); + + const int ret = ipv6_activate_sock(sock); + + if (ret == SNMP_ERR_NOERROR) + p->row_status = RowStatus_active; + return (ret); +} + +/** + * Close an IPv6 socket. Keep the sock object. + * + * \param p inet port + */ +static void +ipv6_deactivate(struct inet_port *p) +{ + struct port_sock *sock = TAILQ_FIRST(&p->socks); + assert(sock); + assert(!TAILQ_NEXT(sock, link)); + + snmpd_input_close(&sock->input); + + p->row_status = RowStatus_notInService; +} + +/** + * IPv6 specific part of message processing. The control data may contain + * credentials and packet info that contains the destination address of + * the packet. + * + * \param sock the sock object + * \param msg the received message + */ +static void +ipv6_parse_ctrl(struct port_sock *sock, const struct msghdr *msg) +{ + struct sockcred *cred = NULL; + + for (struct cmsghdr *cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL; + cmsg = CMSG_NXTHDR(msg, cmsg)) { + + if (cmsg->cmsg_level == IPPROTO_IPV6 && + cmsg->cmsg_type == IPV6_PKTINFO) { + const struct in6_pktinfo *info = + (const struct in6_pktinfo *)(const void *) + CMSG_DATA(cmsg); + sock->ret_source.a6.ipi6_addr = info->ipi6_addr; + sock->ret_source.a6.ipi6_ifindex = + !IN6_IS_ADDR_LINKLOCAL(&info->ipi6_addr) ? 0: + info->ipi6_ifindex; + } else if (cmsg->cmsg_level == SOL_SOCKET && + cmsg->cmsg_type == SCM_CREDS) { + cred = (struct sockcred *)(void *)CMSG_DATA(cmsg); + } + } + + sock->input.priv = 0; + if (sock->input.cred && cred) + /* remote end has sent credentials */ + sock->input.priv = (cred->sc_euid == 0); +} + +/** + * Set the source address option for IPv4 sockets. + * + * \param sock socket object + * \param msg message + */ +static void +ipv6_setsrc(struct port_sock *sock, struct msghdr *msg) +{ + struct cmsghdr *cmsg = CMSG_FIRSTHDR(msg); + + /* select outgoing interface by setting source address */ + cmsg->cmsg_level = IPPROTO_IPV6; + cmsg->cmsg_type = IPV6_PKTINFO; + cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); + memcpy(CMSG_DATA(cmsg), &sock->ret_source.a6, + sizeof(struct in6_pktinfo)); + + msg->msg_controllen = CMSG_SPACE(sizeof(struct in6_pktinfo)); +} + +/** + * IPv6z creation stuff. Parse the index, fill socket address and creates + * a port_sock. + * + * \param port the port to create + * \param params parameters from the SNMP SET + * + * \return SNMP error + */ +static int +ipv6z_create(struct inet_port *port, struct inet_port_params *params) +{ + if (params->addr_len != 20) + return (SNMP_ERR_INCONS_VALUE); + + u_int scope_id = 0; + for (u_int i = 16; i < 20; i++) { + scope_id <<= 8; + scope_id |= params->addr[i]; + } + + const int ret = ipv6_create_common(port, params, scope_id); + if (ret != SNMP_ERR_NOERROR) + return (ret); + + return (SNMP_ERR_NOERROR); +} + +/** + * DNS name creation stuff. Parse the index and save the string. + * This does not create a socket struct. + * + * \param port the port to create + * \param params parameters from the SNMP SET + * + * \return SNMP error + */ +static int +dns_create(struct inet_port *port, struct inet_port_params *params) +{ + if (params->addr_len > 64) + return (SNMP_ERR_INCONS_VALUE); + + if (strnlen(params->addr, params->addr_len) != + params->addr_len) + return (SNMP_ERR_INCONS_VALUE); + + if ((port->dns_addr = realloc(params->addr, + params->addr_len + 1)) == NULL) + return (SNMP_ERR_GENERR); + + port->dns_addr[params->addr_len] = '\0'; + params->addr = NULL; + + port->dns_port = htons(params->port); + + return (SNMP_ERR_NOERROR); +} + +/** + * Open sockets. This loops through all the addresses returned by getaddrinfo + * and opens a socket for each of them. + * + * \param port inet port + * + * \return SNMP error code + */ +static int +dns_activate(struct inet_port *port) +{ + struct addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_DGRAM; // XXX udp-only + hints.ai_flags = AI_ADDRCONFIG | AI_PASSIVE | AI_NUMERICSERV; + + char portbuf[8]; + sprintf(portbuf, "%hu", ntohs(port->dns_port)); + + struct addrinfo *res0; + int error = getaddrinfo(port->dns_addr[0] == '\0' + ? NULL : port->dns_addr, + portbuf, &hints, &res0); + + if (error) { + syslog(LOG_ERR, "cannot resolve address '%s': %s", + port->dns_addr, gai_strerror(error)); + return (SNMP_ERR_GENERR); + } + + for (struct addrinfo *res = res0; res != NULL; res = res->ai_next) { + if (res->ai_family != AF_INET && res->ai_family != AF_INET6) + continue; + + struct port_sock *sock = calloc(1, sizeof(struct port_sock)); + if (sock == NULL) + return (SNMP_ERR_GENERR); + + snmpd_input_init(&sock->input); + sock->port = port; + + int ret = SNMP_ERR_NOERROR; + + if (res->ai_family == AF_INET) { + *(struct sockaddr_in *)&sock->bind_addr = + *(struct sockaddr_in *)(void *)res->ai_addr; + ret = ipv4_activate_sock(sock); + } else { + *(struct sockaddr_in6 *)&sock->bind_addr = + *(struct sockaddr_in6 *)(void *)res->ai_addr; + ret = ipv6_activate_sock(sock); + } + + if (ret != SNMP_ERR_NOERROR) + free(sock); + else + TAILQ_INSERT_HEAD(&port->socks, sock, link); + } + + if (!TAILQ_EMPTY(&port->socks)) + port->row_status = RowStatus_active; + + freeaddrinfo(res0); + return (SNMP_ERR_GENERR); +} + +/** + * Deactive the socket. Close all open sockets and delete all sock objects. + * + * \param port inet port + */ +static void +dns_deactivate(struct inet_port *port) +{ + while (!TAILQ_EMPTY(&port->socks)) { + struct port_sock *sock = TAILQ_FIRST(&port->socks); + TAILQ_REMOVE(&port->socks, sock, link); + snmpd_input_close(&sock->input); + free(sock); + } + port->row_status = RowStatus_notInService; +} + +static int +inet_create(struct inet_port_params *params, struct inet_port **pp) +{ + int err = SNMP_ERR_NOERROR; + struct inet_port *port = NULL; + + if (params->port > 0xffff) { + err = SNMP_ERR_NO_CREATION; + goto fail; + } + + if ((port = malloc(sizeof(*port))) == NULL) { + err = SNMP_ERR_GENERR; + goto fail; + } + memset(port, 0, sizeof(*port)); + TAILQ_INIT(&port->socks); + + port->proto = params->proto; + port->tport.index = params->index; + + switch (params->type) { + + case InetAddressType_ipv4: + port->create = ipv4_create; + port->activate = ipv4_activate; + port->deactivate = ipv4_deactivate; + break; + + case InetAddressType_ipv6: + port->create = ipv6_create; + port->activate = ipv6_activate; + port->deactivate = ipv6_deactivate; + break; + + case InetAddressType_ipv6z: + port->create = ipv6z_create; + port->activate = ipv6_activate; + port->deactivate = ipv6_deactivate; + break; + + case InetAddressType_dns: + port->create = dns_create; + port->activate = dns_activate; + port->deactivate = dns_deactivate; + break; + + default: + err = SNMP_ERR_NO_CREATION; + goto fail; + } + + if ((err = port->create(port, params)) != SNMP_ERR_NOERROR) + goto fail; + + *pp = port; + trans_insert_port(my_trans, &port->tport); + return (err); + +fail: + free(port->dns_addr); + free(port); + return (err); +} + +static int +create_and_go(struct snmp_context *ctx, struct inet_port_params *params) +{ + int err; + struct inet_port *port; + + if ((err = inet_create(params, &port)) != SNMP_ERR_NOERROR) + return (err); + + port->row_status = RowStatus_createAndGo; + ctx->scratch->ptr1 = port; + + if (community == COMM_INITIALIZE) + return (err); + + return (inet_activate(&port->tport)); +} + +static int +create_and_wait(struct snmp_context *ctx, struct inet_port_params *params) +{ + int err; + struct inet_port *port; + + if ((err = inet_create(params, &port)) != SNMP_ERR_NOERROR) + return (err); + + port->row_status = RowStatus_createAndWait; + ctx->scratch->ptr1 = port; + + return (err); +} + +/** + * This is called to set a RowStatus value in the port table during + * SET processing. + * + * When this is called during initialization time and the RowStatus is set + * to CreateAndGo, the port is actually not activated. This is done when + * the main code calls the init() for all ports later. + */ +static int +inet_port_set(struct snmp_context *ctx, struct inet_port *port, + struct inet_port_params *params, enum RowStatus nstatus) +{ + switch (nstatus) { + + case RowStatus_createAndGo: + if (port != NULL) + return (SNMP_ERR_INCONS_VALUE); + ctx->scratch->int1 = SET_CREATED; + return (create_and_go(ctx, params)); + + case RowStatus_createAndWait: + if (port != NULL) + return (SNMP_ERR_INCONS_VALUE); + ctx->scratch->int1 = SET_CREATED; + return (create_and_wait(ctx, params)); + + case RowStatus_active: + if (port == NULL) + return (SNMP_ERR_INCONS_VALUE); + + switch (port->row_status) { + + case RowStatus_notReady: + /* this can not happend */ + abort(); + + case RowStatus_notInService: + ctx->scratch->int1 = SET_ACTIVATED; + return (inet_activate(&port->tport)); + + case RowStatus_active: + return (SNMP_ERR_NOERROR); + + case RowStatus_createAndGo: + case RowStatus_createAndWait: + case RowStatus_destroy: + abort(); + } + break; + + case RowStatus_notInService: + if (port == NULL) + return (SNMP_ERR_INCONS_VALUE); + + switch (port->row_status) { + + case RowStatus_notReady: + /* this can not happend */ + abort(); + + case RowStatus_notInService: + return (SNMP_ERR_NOERROR); + + case RowStatus_active: + /* this is done during commit */ + ctx->scratch->int1 = SET_DEACTIVATE; + return (SNMP_ERR_NOERROR); + + case RowStatus_createAndGo: + case RowStatus_createAndWait: + case RowStatus_destroy: + abort(); + } + break; + + case RowStatus_destroy: + /* this is done during commit */ + ctx->scratch->int1 = SET_DESTROY; + return (SNMP_ERR_NOERROR); + + case RowStatus_notReady: + return (SNMP_ERR_WRONG_VALUE); + } + abort(); +} + +/* + * Port table + */ +int +op_snmp_trans_inet(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 inet_port *port; + struct inet_port_params params; + int ret; + + switch (op) { + + case SNMP_OP_GETNEXT: + if ((port = (struct inet_port *)trans_next_port(my_trans, + &value->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + index_append(&value->var, sub, &port->tport.index); + goto fetch; + + case SNMP_OP_GET: + if ((port = (struct inet_port *)trans_find_port(my_trans, + &value->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + goto fetch; + + case SNMP_OP_SET: + port = (struct inet_port *)trans_find_port(my_trans, + &value->var, sub); + + if (which != LEAF_begemotSnmpdTransInetStatus) + abort(); + if (!isok_RowStatus(value->v.integer)) + return (SNMP_ERR_WRONG_VALUE); + + if (index_decode(&value->var, sub, iidx, ¶ms.type, + ¶ms.addr, ¶ms.addr_len, ¶ms.port, + ¶ms.proto)) + return (SNMP_ERR_NO_CREATION); + + asn_slice_oid(¶ms.index, &value->var, sub, value->var.len); + + ret = inet_port_set(ctx, port, ¶ms, + (enum RowStatus)value->v.integer); + + free(params.addr); + return (ret); + + case SNMP_OP_ROLLBACK: + if ((port = (struct inet_port *)trans_find_port(my_trans, + &value->var, sub)) == NULL) + /* cannot happen */ + abort(); + + switch (ctx->scratch->int1) { + + case SET_CREATED: + /* newly created */ + assert(port != NULL); + inet_destroy_port(&port->tport); + return (SNMP_ERR_NOERROR); + + case SET_DESTROY: + /* do it now */ + assert(port != NULL); + return (SNMP_ERR_NOERROR); + + case SET_ACTIVATED: + deactivate_port(port); + return (SNMP_ERR_NOERROR); + + case SET_DEACTIVATE: + return (SNMP_ERR_NOERROR); + } + abort(); + + case SNMP_OP_COMMIT: + if ((port = (struct inet_port *)trans_find_port(my_trans, + &value->var, sub)) == NULL) + /* cannot happen */ + abort(); + + switch (ctx->scratch->int1) { + + case SET_CREATED: + /* newly created */ + assert(port != NULL); + return (SNMP_ERR_NOERROR); + + case SET_DESTROY: + /* do it now */ + assert(port != NULL); + inet_destroy_port(&port->tport); + return (SNMP_ERR_NOERROR); + + case SET_ACTIVATED: + return (SNMP_ERR_NOERROR); + + case SET_DEACTIVATE: + deactivate_port(port); + return (SNMP_ERR_NOERROR); + } + abort(); + } + abort(); + + fetch: + switch (which) { + + case LEAF_begemotSnmpdTransInetStatus: + value->v.integer = port->row_status; + break; + + default: + abort(); + } + + return (SNMP_ERR_NOERROR); +} Index: head/contrib/bsnmp/snmpd/trans_inet.h =================================================================== --- head/contrib/bsnmp/snmpd/trans_inet.h (nonexistent) +++ head/contrib/bsnmp/snmpd/trans_inet.h (revision 345797) @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2018 + * 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. + * + * $Id$ + */ + +#ifndef trans_inet_h_1530971397 +#define trans_inet_h_1530971397 + +/* transport declaration */ +extern const struct transport_def inet_trans; + +#endif Index: head/contrib/bsnmp/snmpd/trans_lsock.c =================================================================== --- head/contrib/bsnmp/snmpd/trans_lsock.c (revision 345796) +++ head/contrib/bsnmp/snmpd/trans_lsock.c (revision 345797) @@ -1,669 +1,670 @@ /* * 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 tport *, 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 + lsock_recv, + NULL }; 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 = calloc(1, sizeof(*port))) == NULL) return (SNMP_ERR_GENERR); if (!is_stream) { if ((peer = calloc(1, sizeof(*peer))) == NULL) { free(port); return (SNMP_ERR_GENERR); } } 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 = calloc(1, sizeof(*peer))) == NULL) { syslog(LOG_WARNING, "%s: peer malloc failed", p->name); (void)close(accept(fd, NULL, NULL)); return; } 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); } strlcpy(sa.sun_path, p->name, sizeof(sa.sun_path)); sa.sun_family = AF_LOCAL; sa.sun_len = SUN_LEN(&sa); (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); } strlcpy(sa.sun_path, p->name, sizeof(sa.sun_path)); sa.sun_family = AF_LOCAL; sa.sun_len = SUN_LEN(&sa); (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 tport *tp __unused, 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_udp.c =================================================================== --- head/contrib/bsnmp/snmpd/trans_udp.c (revision 345796) +++ head/contrib/bsnmp/snmpd/trans_udp.c (revision 345797) @@ -1,438 +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 #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 tport *, 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 + udp_recv, + NULL }; 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) { if (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); } p->recvdstaddr = true; } 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; struct cmsghdr *cmsg; struct msghdr msg; char cbuf[CMSG_SPACE(sizeof(struct in_addr))]; struct iovec iov; iov.iov_base = __DECONST(void*, buf); iov.iov_len = len; msg.msg_flags = 0; msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_name = __DECONST(void *, addr); msg.msg_namelen = addrlen; if (p->recvdstaddr) { msg.msg_control = cbuf; msg.msg_controllen = sizeof(cbuf); cmsg = CMSG_FIRSTHDR(&msg); cmsg->cmsg_level = IPPROTO_IP; cmsg->cmsg_type = IP_SENDSRCADDR; cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_addr)); memcpy(CMSG_DATA(cmsg), &p->dstaddr, sizeof(struct in_addr)); } else { msg.msg_control = NULL; msg.msg_controllen = 0; } return (sendmsg(p->input.fd, &msg, 0)); } 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 udp_recv(struct tport *tp, struct port_input *pi) { u_char embuf[1000]; char cbuf[CMSG_SPACE(SOCKCREDSIZE(CMGROUP_MAX)) + CMSG_SPACE(sizeof(struct in_addr))]; struct udp_port *p = (struct udp_port *)tp; 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(&p->dstaddr, CMSG_DATA(cmsg), sizeof(struct in_addr)); if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_CREDS) cred = (struct sockcred *)(void *)CMSG_DATA(cmsg); } if (pi->cred) check_priv_dgram(pi, cred); return (0); } /* * 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/tree.def =================================================================== --- head/contrib/bsnmp/snmpd/tree.def (revision 345796) +++ head/contrib/bsnmp/snmpd/tree.def (revision 345797) @@ -1,214 +1,233 @@ # # Copyright (c) 2001-2003 # Fraunhofer Institute for Open Communication Systems (FhG Fokus). # All rights reserved. # +# Copyright (c) 2018 +# 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: tree.def 517 2006-10-31 08:52:04Z brandt_h $ # # System group and private Begemot SNMPd MIB. # include "tc.def" +typedef BegemotSnmpdTransportProto ENUM ( + 1 udp +) + (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) (5 begemotSnmpdCommunityPermission INTEGER GET SET) )) # # 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) + (4 begemotSnmpdTransInet OID op_transport_dummy) ) + (11 begemotSnmpdTransInetTable + (1 begemotSnmpdTransInetEntry : INTEGER OCTETSTRING INTEGER INTEGER op_snmp_trans_inet + (1 begemotSnmpdTransInetAddressType InetAddressType) + (2 begemotSnmpdTransInetAddress OCTETSTRING) + (3 begemotSnmpdTransInetPort INTEGER) + (4 begemotSnmpdTransInetProto BegemotSnmpdTransportProto) + (5 begemotSnmpdTransInetStatus RowStatus GET SET) + + )) ) (2 begemotSnmpdDefs (1 begemotSnmpdAgent (1 begemotSnmpdAgentFreeBSD OID op_dummy) ) ) + (3 begemotSnmpdCompliance) ) )) ) ) (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/lib/libbsnmp/libbsnmp/Makefile =================================================================== --- head/lib/libbsnmp/libbsnmp/Makefile (revision 345796) +++ head/lib/libbsnmp/libbsnmp/Makefile (revision 345797) @@ -1,132 +1,136 @@ # $FreeBSD$ # # Author: Harti Brandt .include CONTRIB= ${SRCTOP}/contrib/bsnmp/lib .PATH: ${CONTRIB} LIB= bsnmp SHLIB_MAJOR= 6 LD_FATAL_WARNINGS= no CFLAGS+= -I${CONTRIB} -DHAVE_ERR_H -DHAVE_GETADDRINFO -DHAVE_STRLCPY CFLAGS+= -DHAVE_STDINT_H -DHAVE_INTTYPES_H -DQUADFMT='"llu"' -DQUADXFMT='"llx"' .if ${MK_OPENSSL} != "no" CFLAGS+= -DHAVE_LIBCRYPTO LIBADD= crypto .endif SRCS= asn1.c snmp.c snmpagent.c snmpclient.c snmpcrypto.c support.c SRCS+= snmptc.h INCS= asn1.h snmp.h snmpagent.h snmpclient.h MAN= asn1.3 bsnmpagent.3 bsnmpclient.3 bsnmplib.3 snmptc.h : tc.def (\ echo -n "/* autogenerated from tc.def; ";\ ls -l -D "%F %T" ${.ALLSRC} | awk '{printf("%s %s", $$6, $$7)}';\ echo "*/";\ echo "#ifndef snmptc_h_1529923773";\ echo "#define snmptc_h_1529923773";\ gensnmptree -E -f <${.ALLSRC};\ echo "#endif" ; \ ) >${.TARGET} CLEANFILES+= snmptc.h MLINKS+= asn1.3 asn_append_oid.3 MLINKS+= asn1.3 asn_commit_header.3 MLINKS+= asn1.3 asn_compare_oid.3 MLINKS+= asn1.3 asn_get_counter64_raw.3 MLINKS+= asn1.3 asn_get_header.3 MLINKS+= asn1.3 asn_get_integer.3 MLINKS+= asn1.3 asn_get_integer_raw.3 MLINKS+= asn1.3 asn_get_ipaddress.3 MLINKS+= asn1.3 asn_get_ipaddress_raw.3 MLINKS+= asn1.3 asn_get_null.3 MLINKS+= asn1.3 asn_get_null_raw.3 MLINKS+= asn1.3 asn_get_objid.3 MLINKS+= asn1.3 asn_get_objid_raw.3 MLINKS+= asn1.3 asn_get_octetstring.3 MLINKS+= asn1.3 asn_get_octetstring_raw.3 MLINKS+= asn1.3 asn_get_sequence.3 MLINKS+= asn1.3 asn_get_timeticks.3 MLINKS+= asn1.3 asn_get_uint32_raw.3 MLINKS+= asn1.3 asn_is_suboid.3 MLINKS+= asn1.3 asn_oid2str.3 MLINKS+= asn1.3 asn_oid2str_r.3 MLINKS+= asn1.3 asn_put_counter64.3 MLINKS+= asn1.3 asn_put_exception.3 MLINKS+= asn1.3 asn_put_header.3 MLINKS+= asn1.3 asn_put_integer.3 MLINKS+= asn1.3 asn_put_ipaddress.3 MLINKS+= asn1.3 asn_put_null.3 MLINKS+= asn1.3 asn_put_objid.3 MLINKS+= asn1.3 asn_put_octetstring.3 MLINKS+= asn1.3 asn_put_temp_header.3 MLINKS+= asn1.3 asn_put_timeticks.3 MLINKS+= asn1.3 asn_put_uint32.3 MLINKS+= asn1.3 asn_skip.3 MLINKS+= asn1.3 asn_slice_oid.3 MLINKS+= bsnmpagent.3 snmp_debug.3 MLINKS+= bsnmpagent.3 snmp_dep_commit.3 MLINKS+= bsnmpagent.3 snmp_dep_finish.3 MLINKS+= bsnmpagent.3 snmp_dep_lookup.3 MLINKS+= bsnmpagent.3 snmp_dep_rollback.3 MLINKS+= bsnmpagent.3 snmp_depop_t.3 MLINKS+= bsnmpagent.3 snmp_get.3 MLINKS+= bsnmpagent.3 snmp_getbulk.3 MLINKS+= bsnmpagent.3 snmp_getnext.3 MLINKS+= bsnmpagent.3 snmp_init_context.3 MLINKS+= bsnmpagent.3 snmp_make_errresp.3 MLINKS+= bsnmpagent.3 snmp_op_t.3 MLINKS+= bsnmpagent.3 snmp_set.3 MLINKS+= bsnmpagent.3 snmp_trace.3 MLINKS+= bsnmpagent.3 tree_size.3 MLINKS+= bsnmpclient.3 snmp_add_binding.3 MLINKS+= bsnmpclient.3 snmp_client.3 MLINKS+= bsnmpclient.3 snmp_client_init.3 MLINKS+= bsnmpclient.3 snmp_client_set_host.3 MLINKS+= bsnmpclient.3 snmp_client_set_port.3 MLINKS+= bsnmpclient.3 snmp_close.3 MLINKS+= bsnmpclient.3 snmp_dialog.3 MLINKS+= bsnmpclient.3 snmp_discover_engine.3 MLINKS+= bsnmpclient.3 snmp_oid_append.3 MLINKS+= bsnmpclient.3 snmp_open.3 MLINKS+= bsnmpclient.3 snmp_parse_server.3 MLINKS+= bsnmpclient.3 snmp_pdu_check.3 MLINKS+= bsnmpclient.3 snmp_pdu_create.3 MLINKS+= bsnmpclient.3 snmp_pdu_send.3 MLINKS+= bsnmpclient.3 snmp_receive.3 MLINKS+= bsnmpclient.3 snmp_send_cb_f.3 MLINKS+= bsnmpclient.3 snmp_table_cb_f.3 MLINKS+= bsnmpclient.3 snmp_table_fetch.3 MLINKS+= bsnmpclient.3 snmp_table_fetch_async.3 MLINKS+= bsnmpclient.3 snmp_timeout_cb_f.3 MLINKS+= bsnmpclient.3 snmp_timeout_start_f.3 MLINKS+= bsnmpclient.3 snmp_timeout_stop_f.3 MLINKS+= bsnmplib.3 TRUTH_GET.3 MLINKS+= bsnmplib.3 TRUTH_MK.3 MLINKS+= bsnmplib.3 TRUTH_OK.3 MLINKS+= bsnmplib.3 snmp_calc_keychange.3 MLINKS+= bsnmplib.3 snmp_get_local_keys.3 MLINKS+= bsnmplib.3 snmp_passwd_to_keys.3 MLINKS+= bsnmplib.3 snmp_pdu_decode.3 MLINKS+= bsnmplib.3 snmp_pdu_decode_header.3 MLINKS+= bsnmplib.3 snmp_pdu_decode_scoped.3 MLINKS+= bsnmplib.3 snmp_pdu_decode_secmode.3 MLINKS+= bsnmplib.3 snmp_pdu_dump.3 MLINKS+= bsnmplib.3 snmp_pdu_encode.3 MLINKS+= bsnmplib.3 snmp_pdu_free.3 MLINKS+= bsnmplib.3 snmp_pdu_init_secparams.3 MLINKS+= bsnmplib.3 snmp_value_copy.3 MLINKS+= bsnmplib.3 snmp_value_free.3 MLINKS+= bsnmplib.3 snmp_value_parse.3 +FILESGROUPS+= DEFS +DEFS= tc.def +DEFSDIR?= ${SHAREDIR}/snmp/defs + .include Index: head/usr.sbin/bsnmpd/bsnmpd/Makefile =================================================================== --- head/usr.sbin/bsnmpd/bsnmpd/Makefile (revision 345796) +++ head/usr.sbin/bsnmpd/bsnmpd/Makefile (revision 345797) @@ -1,155 +1,155 @@ # $FreeBSD$ # # Author: Harti Brandt .include CONTRIB=${SRCTOP}/contrib/bsnmp .PATH: ${CONTRIB}/snmpd CONFS= snmpd.config CONFSMODE= 600 PROG= bsnmpd SRCS= main.c action.c config.c export.c trap.c trans_udp.c trans_lsock.c -SRCS+= oid.h tree.c tree.h +SRCS+= trans_inet.c oid.h tree.c tree.h XSYM= snmpMIB begemotSnmpdModuleTable begemotSnmpd begemotTrapSinkTable \ sysUpTime snmpTrapOID coldStart authenticationFailure \ begemotSnmpdTransUdp begemotSnmpdTransLsock begemotSnmpdLocalPortTable \ - freeBSD freeBSDVersion + freeBSD freeBSDVersion begemotSnmpdTransInet CLEANFILES= oid.h tree.c tree.h MAN= bsnmpd.1 snmpmod.3 MLINKS+= snmpmod.3 FIND_OBJECT_INT.3 MLINKS+= snmpmod.3 FIND_OBJECT_INT_LINK.3 MLINKS+= snmpmod.3 FIND_OBJECT_INT_LINK_INDEX.3 MLINKS+= snmpmod.3 FIND_OBJECT_OID.3 MLINKS+= snmpmod.3 FIND_OBJECT_OID_LINK.3 MLINKS+= snmpmod.3 FIND_OBJECT_OID_LINK_INDEX.3 MLINKS+= snmpmod.3 INSERT_OBJECT_INT.3 MLINKS+= snmpmod.3 INSERT_OBJECT_INT_LINK.3 MLINKS+= snmpmod.3 INSERT_OBJECT_INT_LINK_INDEX.3 MLINKS+= snmpmod.3 INSERT_OBJECT_OID.3 MLINKS+= snmpmod.3 INSERT_OBJECT_OID_LINK.3 MLINKS+= snmpmod.3 INSERT_OBJECT_OID_LINK_INDEX.3 MLINKS+= snmpmod.3 NEXT_OBJECT_INT.3 MLINKS+= snmpmod.3 NEXT_OBJECT_INT_LINK.3 MLINKS+= snmpmod.3 NEXT_OBJECT_INT_LINK_INDEX.3 MLINKS+= snmpmod.3 NEXT_OBJECT_OID.3 MLINKS+= snmpmod.3 NEXT_OBJECT_OID_LINK.3 MLINKS+= snmpmod.3 NEXT_OBJECT_OID_LINK_INDEX.3 MLINKS+= snmpmod.3 bsnmpd_get_target_stats.3 MLINKS+= snmpmod.3 bsnmpd_get_usm_stats.3 MLINKS+= snmpmod.3 bsnmpd_reset_usm_stats.3 MLINKS+= snmpmod.3 buf_alloc.3 MLINKS+= snmpmod.3 buf_size.3 MLINKS+= snmpmod.3 comm_define.3 MLINKS+= snmpmod.3 community.3 MLINKS+= snmpmod.3 fd_deselect.3 MLINKS+= snmpmod.3 fd_resume.3 MLINKS+= snmpmod.3 fd_select.3 MLINKS+= snmpmod.3 fd_suspend.3 MLINKS+= snmpmod.3 get_ticks.3 MLINKS+= snmpmod.3 index_append.3 MLINKS+= snmpmod.3 index_append_off.3 MLINKS+= snmpmod.3 index_compare.3 MLINKS+= snmpmod.3 index_compare_off.3 MLINKS+= snmpmod.3 index_decode.3 MLINKS+= snmpmod.3 ip_commit.3 MLINKS+= snmpmod.3 ip_get.3 MLINKS+= snmpmod.3 ip_rollback.3 MLINKS+= snmpmod.3 ip_save.3 MLINKS+= snmpmod.3 or_register.3 MLINKS+= snmpmod.3 or_unregister.3 MLINKS+= snmpmod.3 oid_commit.3 MLINKS+= snmpmod.3 oid_get.3 MLINKS+= snmpmod.3 oid_rollback.3 MLINKS+= snmpmod.3 oid_save.3 MLINKS+= snmpmod.3 oid_usmNotInTimeWindows.3 MLINKS+= snmpmod.3 oid_usmUnknownEngineIDs.3 MLINKS+= snmpmod.3 oid_zeroDotZero.3 MLINKS+= snmpmod.3 reqid_allocate.3 MLINKS+= snmpmod.3 reqid_base.3 MLINKS+= snmpmod.3 reqid_istype.3 MLINKS+= snmpmod.3 reqid_next.3 MLINKS+= snmpmod.3 reqid_type.3 MLINKS+= snmpmod.3 snmp_input_finish.3 MLINKS+= snmpmod.3 snmp_input_start.3 MLINKS+= snmpmod.3 snmp_output.3 MLINKS+= snmpmod.3 snmp_pdu_auth_access.3 MLINKS+= snmpmod.3 snmp_send_port.3 MLINKS+= snmpmod.3 snmp_send_trap.3 MLINKS+= snmpmod.3 snmpd_target_stat.3 MLINKS+= snmpmod.3 snmpd_usmstats.3 MLINKS+= snmpmod.3 start_tick.3 MLINKS+= snmpmod.3 string_commit.3 MLINKS+= snmpmod.3 string_free.3 MLINKS+= snmpmod.3 string_get.3 MLINKS+= snmpmod.3 string_get_max.3 MLINKS+= snmpmod.3 string_rollback.3 MLINKS+= snmpmod.3 string_save.3 MLINKS+= snmpmod.3 systemg.3 MLINKS+= snmpmod.3 this_tick.3 MLINKS+= snmpmod.3 timer_start.3 MLINKS+= snmpmod.3 timer_start_repeat.3 MLINKS+= snmpmod.3 timer_stop.3 MLINKS+= snmpmod.3 target_activate_address.3 MLINKS+= snmpmod.3 target_address.3 MLINKS+= snmpmod.3 target_delete_address.3 MLINKS+= snmpmod.3 target_delete_notify.3 MLINKS+= snmpmod.3 target_delete_param.3 MLINKS+= snmpmod.3 target_first_address.3 MLINKS+= snmpmod.3 target_first_notify.3 MLINKS+= snmpmod.3 target_first_param.3 MLINKS+= snmpmod.3 target_flush_all.3 MLINKS+= snmpmod.3 target_next_address.3 MLINKS+= snmpmod.3 target_next_notify.3 MLINKS+= snmpmod.3 target_next_param.3 MLINKS+= snmpmod.3 target_new_address.3 MLINKS+= snmpmod.3 target_new_notify.3 MLINKS+= snmpmod.3 target_new_param.3 MLINKS+= snmpmod.3 target_notify.3 MLINKS+= snmpmod.3 target_param.3 MLINKS+= snmpmod.3 usm_delete_user.3 MLINKS+= snmpmod.3 usm_find_user.3 MLINKS+= snmpmod.3 usm_first_user.3 MLINKS+= snmpmod.3 usm_flush_users.3 MLINKS+= snmpmod.3 usm_next_user.3 MLINKS+= snmpmod.3 usm_new_user.3 MLINKS+= snmpmod.3 usm_user.3 FILESGROUPS= BMIBS DEFS BMIBS= FOKUS-MIB.txt BEGEMOT-MIB.txt BEGEMOT-SNMPD.txt BMIBSDIR= ${SHAREDIR}/snmp/mibs DEFS= tree.def DEFSDIR= ${SHAREDIR}/snmp/defs CFLAGS+= -I${CONTRIB}/lib -I${CONTRIB}/snmpd -I. -DUSE_LIBBEGEMOT CFLAGS+= -DHAVE_STDINT_H -DHAVE_INTTYPES_H -DHAVE_ERR_H -DHAVE_STRLCPY LIBADD= begemot bsnmp LDFLAGS= -Wl,-export-dynamic .if ${MK_OPENSSL} != "no" CFLAGS+= -DHAVE_LIBCRYPTO .endif .if ${MK_TCP_WRAPPERS} != "no" CFLAGS+= -DUSE_TCPWRAPPERS LIBADD+= wrap .endif oid.h: tree.def Makefile gensnmptree -I${CONTRIB}/lib -e ${XSYM} < ${.ALLSRC:M*.def} > ${.TARGET} .ORDER: tree.c tree.h tree.h: .NOMETA tree.c tree.h: tree.def gensnmptree -I${CONTRIB}/lib -l -f < ${.ALLSRC} MANFILTER= sed -e 's%@MODPATH@%${LIBDIR}/%g' \ -e 's%@DEFPATH@%${DEFSDIR}/%g' \ -e 's%@MIBSPATH@%${BMIBSDIR}/%g' .include Index: head/usr.sbin/bsnmpd/bsnmpd/snmpd.config =================================================================== --- head/usr.sbin/bsnmpd/bsnmpd/snmpd.config (revision 345796) +++ head/usr.sbin/bsnmpd/bsnmpd/snmpd.config (revision 345797) @@ -1,322 +1,334 @@ # $FreeBSD$ # # Example configuration file for bsnmpd(1). # # # Set some common variables # location := "Room 200" contact := "sysmeister@example.com" system := 1 # FreeBSD traphost := localhost trapport := 162 # # Set the SNMP engine ID. # # The snmpEngineID object required from the SNMPv3 Framework. If not explicitly set via # this configuration file, an ID is assigned based on the value of the # kern.hostid variable # engine := 0x80:0x10:0x08:0x10:0x80:0x25 # snmpEngineID = $(engine) # Change this! read := "public" # Uncomment begemotSnmpdCommunityString.0.2 below that sets the community # string to enable write access. write := "geheim" trap := "mytrap" # # Declarations for SNMP-USER-BASED-SM-MIB authentication and privacy options # NoAuthProtocol := 1.3.6.1.6.3.10.1.1.1 HMACMD5AuthProtocol := 1.3.6.1.6.3.10.1.1.2 HMACSHAAuthProtocol := 1.3.6.1.6.3.10.1.1.3 NoPrivProtocol := 1.3.6.1.6.3.10.1.2.1 DESPrivProtocol := 1.3.6.1.6.3.10.1.2.2 AesCfb128Protocol := 1.3.6.1.6.3.10.1.2.4 # # Enumerations from SNMP-FRAMEWORK-MIB # # Security models securityModelAny := 0 securityModelSNMPv1 := 1 securityModelSNMPv2c := 2 securityModelUSM := 3 # Message Processing models MPmodelSNMPv1 := 0 MPmodelSNMPv2c := 1 MPmodelSNMPv3 := 3 # Security levels noAuthNoPriv := 1 authNoPriv := 2 authPriv := 3 # SNMPv3 USM User definition # # The localized hex password for a user may be obtained by setting SNMPUSER, SNMPPASSWD, # SNMPAUTH and SNMPPRIV environment variables to the desired parameters and invoking # 'bsnmpget -v 3 -D -K -o verbose' against the running bsnmpd(1). For other # usages refer to the bsnmpget(1) manual page. The following lines define a user "bsnmp" # with a private password "bsnmptest", localized for the above engine ID. # #user1 := "bsnmp" #user1passwd := 0x22:0x98:0x1a:0x6e:0x39:0x93:0x16:0x5e:0x6a:0x21:0x1b:0xd8:0xa9:0x81:0x31:0x05:0x16:0x33:0x38:0x60 # # 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) +# begemotSnmpdCommunityString.0.3 = "otherPublic" begemotSnmpdCommunityDisable = 1 # open standard SNMP ports -begemotSnmpdPortStatus.0.0.0.0.161 = 1 +begemotSnmpdTransInetStatus.1.4.0.0.0.0.161.1 = 4 +begemotSnmpdTransInetStatus.2.16.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.161.1 = 4 + +# UDP over IPv4: 127.0.0.1:161 +# begemotSnmpdTransInetStatus.1.4.127.0.0.1.161.1 = 4 + +# UDP over IPv6: ::1:161 +# begemotSnmpdTransInetStatus.2.16.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.1.161.1 = 4 + +# Use domain name and IPv6 link-local address with scope zone id as address +# begemotSnmpdTransInetStatus.16."localhost".161.1 = 4 +# begemotSnmpdTransInetStatus.16."fe80::1%em0".161.1 = 4 # 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 # Uncomment `begemotSnmpdModulePath.".." = ".."' entries below to enable # modules # # Control configuration for the modules in the module specific sections, e.g. # the "usm" module (begemotSnmpdModulePath."usm") can be controlled in the # %usm specific section. You must uncomment the section specific header in # order to use the enclosed variables, e.g. `usmUserStatus.$(engine).$(user1)` # can only be used if %usm is uncommented. # # Modules are loaded in the order listed, so they must be before any # dependent modules, e.g. "mibII" vs "bridge". # # # MIB-2 module # begemotSnmpdModulePath."mibII" = "/usr/lib/snmp_mibII.so" # # Bridge module # This requires the mibII module. # #begemotSnmpdModulePath."bridge" = "/usr/lib/snmp_bridge.so" # # Host resources module # This requires the mibII module. # #begemotSnmpdModulePath."hostres" = "/usr/lib/snmp_hostres.so" # # LM75 Sensor module # #begemotSnmpdModulePath."lm75" = "/usr/lib/snmp_lm75.so" # # Netgraph module # #begemotSnmpdModulePath."netgraph" = "/usr/lib/snmp_netgraph.so" # # pf(4) module # #begemotSnmpdModulePath."pf" = "/usr/lib/snmp_pf.so" # # SNMPv3 Notification Targets # # begemotSnmpdModulePath."target" = "/usr/lib/snmp_target.so" # # SNMPv3 User-based security module - must be loaded for SNMPv3 USM # #begemotSnmpdModulePath."usm" = "/usr/lib/snmp_usm.so" # # SNMPv3 View-based Access Control module # #begemotSnmpdModulePath."vacm" = "/usr/lib/snmp_vacm.so" # # Wireless module # This requires the mibII module. # #begemotSnmpdModulePath."wlan" = "/usr/lib/snmp_wlan.so" # # SNMPv3 USM User definition. # #%usm # # The following block creates a user with name "bsnmp" and sets privacy # and encryption options to SHA256 message digests and AES encryption # for this user. # # usmUserStatus.$(engine).$(user1) = 5 # usmUserAuthProtocol.$(engine).$(user1) = $(HMACSHAAuthProtocol) # usmUserAuthKeyChange.$(engine).$(user1) = $(user1passwd) # usmUserPrivProtocol.$(engine).$(user1) = $(AesCfb128Protocol) # usmUserPrivKeyChange.$(engine).$(user1) = $(user1passwd) # usmUserStatus.$(engine).$(user1) = 1 # # # The following block creates a user with name "public" with no authentication # or encryption options. # # usmUserStatus.$(engine).$(read) = 5 # usmUserAuthProtocol.$(engine).$(read) = $(NoAuthProtocol) # usmUserPrivProtocol.$(engine).$(read) = $(NoPrivProtocol) # usmUserStatus.$(engine).$(read) = 1 # # # Definition of view-based access control entries. # #%vacm # Definition of a SNMPv1 group # vacmSecurityToGroupStatus.$(securityModelSNMPv1).$(read) = 4 # vacmGroupName.$(securityModelSNMPv1).$(read) = $(read) # Definition of SNMPv2 group # vacmSecurityToGroupStatus.$(securityModelSNMPv2c).$(write) = 4 # vacmGroupName.$(securityModelSNMPv2c).$(write) = $(write) # Definition of SNMPv3 group with users "bsnmp" and "public" # vacmSecurityToGroupStatus.$(securityModelUSM).$(user1) = 4 # vacmGroupName.$(securityModelUSM).$(user1) = $(write) # vacmSecurityToGroupStatus.$(securityModelUSM).$(read) = 4 # vacmGroupName.$(securityModelUSM).$(read) = $(write) # # The OID of the .iso.org.dod.internet subtree # # internetoid := 1.3.6.1 # internetoidlen := 4 # # Definitions of two views # # vacmViewTreeFamilyStatus."internet".$(internetoidlen).$(internetoid) = 4 # vacmViewTreeFamilyStatus."restricted".$(internetoidlen).$(internetoid) = 4 # # Access control # # # Read-only access for SNMPv1 users # # vacmAccessStatus.$(read)."".$(securityModelSNMPv1).$(noAuthNoPriv) = 4 # vacmAccessReadViewName.$(read)."".$(securityModelSNMPv1).$(noAuthNoPriv) = "internet" # # Read-write access for SNMPv2 users # # vacmAccessStatus.$(write)."".$(securityModelSNMPv2c).$(noAuthNoPriv) = 4 # vacmAccessReadViewName.$(write)."".$(securityModelSNMPv2c).$(noAuthNoPriv) = "internet" # vacmAccessWriteViewName.$(write)."".$(securityModelSNMPv2c).$(noAuthNoPriv) = "internet" # # Read-write-notify access for SNMPv3 USM users with noAuthNoPriv # # vacmAccessStatus.$(write)."".3.$(noAuthNoPriv) = 4 # vacmAccessReadViewName.$(write)."".$(securityModelUSM).$(noAuthNoPriv) = "internet" # vacmAccessWriteViewName.$(write)."".$(securityModelUSM).$(noAuthNoPriv) = "internet" # vacmAccessNotifyViewName.$(write)."".$(securityModelUSM).$(noAuthNoPriv) = "internet" # #Read-write-notify access to restricted for SNMPv3 USM users with authPriv # # vacmAccessStatus.$(write)."".$(securityModelUSM).$(authPriv) = 4 # vacmAccessReadViewName.$(write)."".$(securityModelUSM).$(authPriv) = "restricted" # vacmAccessWriteViewName.$(write)."".$(securityModelUSM).$(authPriv) = "restricted" # vacmAccessNotifyViewName.$(write)."".$(securityModelUSM).$(authPriv) = "restricted" #%target # Send notifications to target tag "test" # tag := "test" # snmpNotifyRowStatus.$(tag) = 4 # snmpNotifyTag.$(tag) = $(tag) # tagremote := "testremote" # snmpNotifyRowStatus.$(tagremote) = 4 # snmpNotifyTag.$(tagremote) = $(tagremote) # # Specify the target parameters for the notifications - send with the credentials # of user "bsnmp" # # snmpTargetParamsRowStatus.$(tag) = 5 # snmpTargetParamsMPModel.$(tag) = $(MPmodelSNMPv3) # snmpTargetParamsSecurityModel.$(tag) = $(securityModelUSM) # snmpTargetParamsSecurityName.$(tag) = $(user1) # snmpTargetParamsSecurityLevel.$(tag) = $(authPriv) # snmpTargetParamsRowStatus.$(tag) = 1 # # Define the notifications' target address - port 162 on localhost # # snmpTargetAddrRowStatus.$(tag) = 5 # snmpTargetAddrTAddress.$(tag) = 0x7f:0x0:0x0:0x1:0x0:0xa2 # snmpTargetAddrTagList.$(tag) = "test notification" # snmpTargetAddrParams.$(tag) = $(tag) # snmpTargetAddrRowStatus.$(tag) = 1 # # Define the notifications' target address - port 162 on 10.0.0.1 # # snmpTargetAddrRowStatus.$(tagremote) = 5 # snmpTargetAddrTAddress.$(tagremote) = 0x0a:0x00:0x00:0x1:0x0:0xa2 # snmpTargetAddrTagList.$(tagremote) = $(tagremote) # snmpTargetAddrParams.$(tagremote) = $(tag) # snmpTargetAddrRowStatus.$(tagremote) = 1 # Force a polling rate for the 64-bit interface counters in case # the automatic computation is wrong (which may be the case if an interface # announces the wrong bit rate via its MIB). #%mibII #begemotIfForcePoll = 2000 #%netgraph #begemotNgControlNodeName = "snmpd"