Index: head/contrib/bsnmp/VERSION =================================================================== --- head/contrib/bsnmp/VERSION (revision 335884) +++ head/contrib/bsnmp/VERSION (revision 335885) @@ -1 +1 @@ -1.12 +1.13 Index: head/contrib/bsnmp/gensnmptree/gensnmptree.1 =================================================================== --- head/contrib/bsnmp/gensnmptree/gensnmptree.1 (revision 335884) +++ head/contrib/bsnmp/gensnmptree/gensnmptree.1 (revision 335885) @@ -1,246 +1,296 @@ .\" .\" Copyright (c) 2001-2005 .\" Fraunhofer Institute for Open Communication Systems (FhG Fokus). .\" All rights reserved. -.\" Copyright (c) 2006 +.\" 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 May 26, 2006 +.Dd June 29, 2018 .Dt GENSNMPTREE 1 .Os .Sh NAME .Nm gensnmptree .Nd "generate C and header files from a MIB description file" .Sh SYNOPSIS .Nm -.Op Fl dEehlt +.Op Fl 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. +.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. .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 335884) +++ head/contrib/bsnmp/gensnmptree/gensnmptree.c (revision 335885) @@ -1,1554 +1,1740 @@ /* * Copyright (c) 2001-2003 * Fraunhofer Institute for Open Communication Systems (FhG Fokus). * All rights reserved. * - * Copyright (c) 2004-2006 + * 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\ -usage: gensnmptree [-dEehlt] [-I directory] [-i infile] [-p prefix]\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 enums and bits only\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 generated a .def file\n\ + -t generate a .def file\n\ "; /* * A node in the OID tree */ enum ntype { NODE_LEAF = 1, NODE_TREE, NODE_ENTRY, NODE_COLUMN }; enum { FL_GET = 0x01, FL_SET = 0x02, }; struct node; TAILQ_HEAD(node_list, node); struct node { enum ntype type; asn_subid_t id; /* last element of OID */ char *name; /* name of node */ TAILQ_ENTRY(node) link; u_int lno; /* starting line number */ u_int flags; /* allowed operations */ union { struct tree { struct node_list subs; } tree; struct entry { uint32_t index; /* index for table entry */ char *func; /* function for tables */ struct node_list subs; } entry; struct leaf { enum snmp_syntax syntax; /* syntax for this leaf */ char *func; /* function name */ } leaf; struct column { enum snmp_syntax syntax; /* syntax for this column */ } column; } u; }; struct func { const char *name; LIST_ENTRY(func) link; }; static LIST_HEAD(, func) funcs = LIST_HEAD_INITIALIZER(funcs); struct enums { const char *name; long value; TAILQ_ENTRY(enums) link; }; struct type { const char *name; const char *from_fname; u_int from_lno; u_int syntax; int is_enum; int is_bits; TAILQ_HEAD(, enums) enums; LIST_ENTRY(type) link; }; static LIST_HEAD(, type) types = LIST_HEAD_INITIALIZER(types); static void report(const char *, ...) __dead2 __printflike(1, 2); static void report_node(const struct node *, const char *, ...) __dead2 __printflike(2, 3); /************************************************************ * * Allocate memory and panic just in the case... */ static void * xalloc(size_t size) { void *ptr; if ((ptr = malloc(size)) == NULL) err(1, "allocing %zu bytes", size); return (ptr); } static char * savestr(const char *s) { if (s == NULL) return (NULL); return (strcpy(xalloc(strlen(s) + 1), s)); } /************************************************************ * * Input stack */ struct input { FILE *fp; u_int lno; char *fname; char *path; LIST_ENTRY(input) link; }; static LIST_HEAD(, input) inputs = LIST_HEAD_INITIALIZER(inputs); static struct input *input = NULL; #define MAX_PATHS 100 static u_int npaths = 2; static u_int stdpaths = 2; static const char *paths[MAX_PATHS + 1] = { "/usr/share/snmp/defs", "/usr/local/share/snmp/defs", NULL }; static int pbchar = -1; static void path_new(const char *path) { if (npaths >= MAX_PATHS) report("too many -I directives"); memmove(&paths[npaths - stdpaths + 1], &paths[npaths - stdpaths], sizeof(path[0]) * stdpaths); paths[npaths - stdpaths] = savestr(path); npaths++; } static void input_new(FILE *fp, const char *path, const char *fname) { struct input *ip; ip = xalloc(sizeof(*ip)); ip->fp = fp; ip->lno = 1; ip->fname = savestr(fname); ip->path = savestr(path); LIST_INSERT_HEAD(&inputs, ip, link); input = ip; } static void input_close(void) { if (input == NULL) return; fclose(input->fp); free(input->fname); free(input->path); LIST_REMOVE(input, link); free(input); input = LIST_FIRST(&inputs); } static FILE * tryopen(const char *path, const char *fname) { char *fn; FILE *fp; if (path == NULL) fn = savestr(fname); else { fn = xalloc(strlen(path) + strlen(fname) + 2); sprintf(fn, "%s/%s", path, fname); } fp = fopen(fn, "r"); free(fn); return (fp); } static void input_fopen(const char *fname, int loc) { FILE *fp; char *path; u_int p; if (fname[0] == '/') { if ((fp = tryopen(NULL, fname)) != NULL) { input_new(fp, NULL, fname); return; } } else { if (loc) { if (input == NULL) path = NULL; else path = input->path; if ((fp = tryopen(path, fname)) != NULL) { input_new(fp, NULL, fname); return; } } for (p = 0; paths[p] != NULL; p++) if ((fp = tryopen(paths[p], fname)) != NULL) { input_new(fp, paths[p], fname); return; } } report("cannot open '%s'", fname); } static int tgetc(void) { int c; if (pbchar != -1) { c = pbchar; pbchar = -1; return (c); } for (;;) { if (input == NULL) return (EOF); if ((c = getc(input->fp)) != EOF) return (c); input_close(); } } static void tungetc(int c) { if (pbchar != -1) abort(); pbchar = c; } /************************************************************ * * Parsing input */ enum tok { TOK_EOF = 0200, /* end-of-file seen */ TOK_NUM, /* number */ TOK_STR, /* string */ TOK_ACCESS, /* access operator */ TOK_TYPE, /* type operator */ TOK_ENUM, /* enum token (kind of a type) */ TOK_TYPEDEF, /* typedef directive */ TOK_DEFTYPE, /* defined type */ TOK_INCLUDE, /* include directive */ TOK_FILENAME, /* filename ("foo.bar" or ) */ TOK_BITS, /* bits token (kind of a type) */ }; static const struct { const char *str; enum tok tok; u_int val; } keywords[] = { { "GET", TOK_ACCESS, FL_GET }, { "SET", TOK_ACCESS, FL_SET }, { "NULL", TOK_TYPE, SNMP_SYNTAX_NULL }, { "INTEGER", TOK_TYPE, SNMP_SYNTAX_INTEGER }, { "INTEGER32", TOK_TYPE, SNMP_SYNTAX_INTEGER }, { "UNSIGNED32", TOK_TYPE, SNMP_SYNTAX_GAUGE }, { "OCTETSTRING", TOK_TYPE, SNMP_SYNTAX_OCTETSTRING }, { "IPADDRESS", TOK_TYPE, SNMP_SYNTAX_IPADDRESS }, { "OID", TOK_TYPE, SNMP_SYNTAX_OID }, { "TIMETICKS", TOK_TYPE, SNMP_SYNTAX_TIMETICKS }, { "COUNTER", TOK_TYPE, SNMP_SYNTAX_COUNTER }, { "GAUGE", TOK_TYPE, SNMP_SYNTAX_GAUGE }, { "COUNTER64", TOK_TYPE, SNMP_SYNTAX_COUNTER64 }, { "ENUM", TOK_ENUM, SNMP_SYNTAX_INTEGER }, { "BITS", TOK_BITS, SNMP_SYNTAX_OCTETSTRING }, { "typedef", TOK_TYPEDEF, 0 }, { "include", TOK_INCLUDE, 0 }, { NULL, 0, 0 } }; /* arbitrary upper limit on node names and function names */ #define MAXSTR 1000 -char str[MAXSTR]; -u_long val; /* integer values */ -int all_cond; /* all conditions are true */ -int saved_token = -1; +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) { u_int syntax; struct enums *e; syntax = val; if (*tok == TOK_ENUM || *tok == TOK_BITS) { if (t == NULL && vname != NULL) { t = make_type(vname); t->is_enum = (*tok == TOK_ENUM); t->is_bits = (*tok == TOK_BITS); t->syntax = syntax; } if (gettoken() != '(') report("'(' expected after ENUM"); if ((*tok = gettoken()) == TOK_EOF) report("unexpected EOF in ENUM"); do { e = NULL; if (t != NULL) { e = xalloc(sizeof(*e)); } if (*tok == '-') { if ((*tok = gettoken()) == TOK_EOF) report("unexpected EOF in ENUM"); e->value = -(long)val; } else e->value = val; if (*tok != TOK_NUM) report("need value for ENUM/BITS"); if (gettoken() != TOK_STR) report("need string in ENUM/BITS"); e->name = savetok(); TAILQ_INSERT_TAIL(&t->enums, e, link); if ((*tok = gettoken()) == TOK_EOF) report("unexpected EOF in ENUM/BITS"); } while (*tok != ')'); *tok = gettoken(); } else if (*tok == TOK_DEFTYPE) { *tok = gettoken(); } else { if ((*tok = gettoken()) == '|') { if (gettoken() != TOK_STR) report("subtype expected after '|'"); *tok = gettoken(); } } return (syntax); } /* * Parse the next node (complete with all subnodes) */ static struct node * parse(enum tok tok) { struct node *node; struct node *sub; u_int index_count; node = xalloc(sizeof(struct node)); node->lno = input->lno; node->flags = 0; if (tok != '(') report("'(' expected at begin of node"); if (gettoken() != TOK_NUM) report("node id expected after opening '('"); if (val > ASN_MAXID) report("subid too large '%lu'", val); node->id = (asn_subid_t)val; if (gettoken() != TOK_STR) report("node name expected after '(' ID"); node->name = savetok(); if ((tok = gettoken()) == TOK_TYPE || tok == TOK_DEFTYPE || tok == TOK_ENUM || tok == TOK_BITS) { /* LEAF or COLUM */ u_int syntax = parse_type(&tok, NULL, node->name); if (tok == TOK_STR) { /* LEAF */ node->type = NODE_LEAF; node->u.leaf.func = savetok(); node->u.leaf.syntax = syntax; tok = gettoken(); } else { /* COLUMN */ node->type = NODE_COLUMN; node->u.column.syntax = syntax; } while (tok != ')') { if (tok != TOK_ACCESS) report("access keyword or ')' expected"); node->flags |= (u_int)val; tok = gettoken(); } } else if (tok == ':') { /* ENTRY */ node->type = NODE_ENTRY; TAILQ_INIT(&node->u.entry.subs); index_count = 0; node->u.entry.index = 0; tok = gettoken(); while (tok == TOK_TYPE || tok == TOK_DEFTYPE || tok == TOK_ENUM || tok == TOK_BITS) { u_int syntax = parse_type(&tok, NULL, node->name); if (index_count++ == SNMP_INDEXES_MAX) report("too many table indexes"); node->u.entry.index |= syntax << (SNMP_INDEX_SHIFT * index_count); } node->u.entry.index |= index_count; if (index_count == 0) report("need at least one index"); if (tok != TOK_STR) report("function name expected"); node->u.entry.func = savetok(); tok = gettoken(); while (tok != ')') { sub = parse(tok); TAILQ_INSERT_TAIL(&node->u.entry.subs, sub, link); tok = gettoken(); } } else { /* subtree */ node->type = NODE_TREE; TAILQ_INIT(&node->u.tree.subs); while (tok != ')') { sub = parse(tok); TAILQ_INSERT_TAIL(&node->u.tree.subs, sub, link); tok = gettoken(); } } return (node); } /* * Parse a top level element. Return the tree if it was a tree, NULL * otherwise. */ static struct node * parse_top(enum tok tok) { struct type *t; if (tok == '(') return (parse(tok)); if (tok == TOK_TYPEDEF) { if (gettoken() != TOK_STR) report("type name expected after typedef"); t = make_type(str); tok = gettoken(); t->is_enum = (tok == TOK_ENUM); t->is_bits = (tok == TOK_BITS); t->syntax = parse_type(&tok, t, NULL); pushback(tok); return (NULL); } if (tok == TOK_INCLUDE) { if (gettoken() != TOK_FILENAME) report("filename expected in include directive"); input_fopen(str, val); return (NULL); } report("'(' or 'typedef' expected"); } /* * Generate the C-code table part for one node. */ static void gen_node(FILE *fp, struct node *np, struct asn_oid *oid, u_int idx, const char *func) { u_int n; struct node *sub; u_int syntax; if (oid->len == ASN_MAXOIDLEN) report_node(np, "OID too long"); oid->subs[oid->len++] = np->id; if (np->type == NODE_TREE) { TAILQ_FOREACH(sub, &np->u.tree.subs, link) gen_node(fp, sub, oid, 0, NULL); oid->len--; return; } if (np->type == NODE_ENTRY) { TAILQ_FOREACH(sub, &np->u.entry.subs, link) gen_node(fp, sub, oid, np->u.entry.index, np->u.entry.func); oid->len--; return; } /* leaf or column */ if ((np->flags & (FL_GET|FL_SET)) == 0) { oid->len--; return; } fprintf(fp, " {{ %u, {", oid->len); for (n = 0; n < oid->len; n++) fprintf(fp, " %u,", oid->subs[n]); fprintf(fp, " }}, \"%s\", ", np->name); if (np->type == NODE_COLUMN) { syntax = np->u.column.syntax; fprintf(fp, "SNMP_NODE_COLUMN, "); } else { syntax = np->u.leaf.syntax; fprintf(fp, "SNMP_NODE_LEAF, "); } switch (syntax) { case SNMP_SYNTAX_NULL: fprintf(fp, "SNMP_SYNTAX_NULL, "); break; case SNMP_SYNTAX_INTEGER: fprintf(fp, "SNMP_SYNTAX_INTEGER, "); break; case SNMP_SYNTAX_OCTETSTRING: fprintf(fp, "SNMP_SYNTAX_OCTETSTRING, "); break; case SNMP_SYNTAX_IPADDRESS: fprintf(fp, "SNMP_SYNTAX_IPADDRESS, "); break; case SNMP_SYNTAX_OID: fprintf(fp, "SNMP_SYNTAX_OID, "); break; case SNMP_SYNTAX_TIMETICKS: fprintf(fp, "SNMP_SYNTAX_TIMETICKS, "); break; case SNMP_SYNTAX_COUNTER: fprintf(fp, "SNMP_SYNTAX_COUNTER, "); break; case SNMP_SYNTAX_GAUGE: fprintf(fp, "SNMP_SYNTAX_GAUGE, "); break; case SNMP_SYNTAX_COUNTER64: fprintf(fp, "SNMP_SYNTAX_COUNTER64, "); break; case SNMP_SYNTAX_NOSUCHOBJECT: case SNMP_SYNTAX_NOSUCHINSTANCE: case SNMP_SYNTAX_ENDOFMIBVIEW: abort(); } if (np->type == NODE_COLUMN) fprintf(fp, "%s, ", func); else fprintf(fp, "%s, ", np->u.leaf.func); fprintf(fp, "0"); if (np->flags & FL_SET) fprintf(fp, "|SNMP_NODE_CANSET"); fprintf(fp, ", %#x, NULL, NULL },\n", idx); oid->len--; return; } /* * Generate the header file with the function declarations. */ static void gen_header(FILE *fp, struct node *np, u_int oidlen, const char *func) { char f[MAXSTR + 4]; struct node *sub; struct func *ptr; oidlen++; if (np->type == NODE_TREE) { TAILQ_FOREACH(sub, &np->u.tree.subs, link) gen_header(fp, sub, oidlen, NULL); return; } if (np->type == NODE_ENTRY) { TAILQ_FOREACH(sub, &np->u.entry.subs, link) gen_header(fp, sub, oidlen, np->u.entry.func); return; } if((np->flags & (FL_GET|FL_SET)) == 0) return; if (np->type == NODE_COLUMN) { if (func == NULL) errx(1, "column without function (%s) - probably " "outside of a table", np->name); sprintf(f, "%s", func); } else sprintf(f, "%s", np->u.leaf.func); LIST_FOREACH(ptr, &funcs, link) if (strcmp(ptr->name, f) == 0) break; if (ptr == NULL) { ptr = xalloc(sizeof(*ptr)); ptr->name = savestr(f); LIST_INSERT_HEAD(&funcs, ptr, link); fprintf(fp, "int %s(struct snmp_context *, " "struct snmp_value *, u_int, u_int, " "enum snmp_op);\n", f); } fprintf(fp, "# define LEAF_%s %u\n", np->name, np->id); } /* * Generate the OID table. */ static void gen_table(FILE *fp, struct node *node) { struct asn_oid oid; fprintf(fp, "#include \n"); fprintf(fp, "#include \n"); #ifdef HAVE_STDINT_H fprintf(fp, "#include \n"); #endif + 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); printf(" %s%s%s)\n", np->u.leaf.func, (np->flags & FL_GET) ? " GET" : "", (np->flags & FL_SET) ? " SET" : ""); break; case NODE_TREE: if (TAILQ_EMPTY(&np->u.tree.subs)) { printf(")\n"); } else { printf("\n"); TAILQ_FOREACH(sp, &np->u.tree.subs, link) gen_tree(sp, level + 1); printf("%*s)\n", 2 * level, ""); } break; case NODE_ENTRY: printf(" :"); for (i = 0; i < SNMP_INDEX_COUNT(np->u.entry.index); i++) print_syntax(SNMP_INDEX(np->u.entry.index, i)); printf(" %s\n", np->u.entry.func); TAILQ_FOREACH(sp, &np->u.entry.subs, link) gen_tree(sp, level + 1); printf("%*s)\n", 2 * level, ""); break; case NODE_COLUMN: print_syntax(np->u.column.syntax); printf("%s%s)\n", (np->flags & FL_GET) ? " GET" : "", (np->flags & FL_SET) ? " SET" : ""); break; } } static int extract(FILE *fp, const struct node *np, struct asn_oid *oid, const char *obj, const struct asn_oid *idx, const char *iname) { struct node *sub; u_long n; if (oid->len == ASN_MAXOIDLEN) report_node(np, "OID too long"); oid->subs[oid->len++] = np->id; if (strcmp(obj, np->name) == 0) { if (oid->len + idx->len >= ASN_MAXOIDLEN) report_node(np, "OID too long"); fprintf(fp, "#define OID_%s%s\t%u\n", np->name, iname ? iname : "", np->id); fprintf(fp, "#define OIDLEN_%s%s\t%u\n", np->name, iname ? iname : "", oid->len + idx->len); fprintf(fp, "#define OIDX_%s%s\t{ %u, {", np->name, iname ? iname : "", oid->len + idx->len); for (n = 0; n < oid->len; n++) fprintf(fp, " %u,", oid->subs[n]); for (n = 0; n < idx->len; n++) fprintf(fp, " %u,", idx->subs[n]); fprintf(fp, " } }\n"); return (0); } if (np->type == NODE_TREE) { TAILQ_FOREACH(sub, &np->u.tree.subs, link) if (!extract(fp, sub, oid, obj, idx, iname)) return (0); } else if (np->type == NODE_ENTRY) { TAILQ_FOREACH(sub, &np->u.entry.subs, link) if (!extract(fp, sub, oid, obj, idx, iname)) return (0); } oid->len--; return (1); } +/** + * 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); + 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_enums(FILE *fp) +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 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(fp, t); + gen_enum_funcs(fp, t, ccode); } +/** + * 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) +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); 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); + } +} + +/** + * 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 + */ +static void +make_enums(int argc, char *argv[], int gen_funcs_h, int gen_funcs_c) +{ + if (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); + else { + for (int i = 0; i < argc; i++) + if (extract_enum(stdout, argv[i], gen_funcs_h)) + errx(1, "enum not found: %s", argv[i]); + } + } +} + 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; char *infile = NULL; - while ((opt = getopt(argc, argv, "dEehI:i:lp:t")) != EOF) + while ((opt = getopt(argc, argv, "dEeFfhI:i:lp:t")) != EOF) switch (opt) { case 'd': debug = 1; break; - case 'h': - fprintf(stderr, "%s", usgtxt); - exit(0); - case 'E': do_enums = 1; break; case 'e': do_extract = 1; break; + case 'F': + gen_funcs_c = 1; + break; + + case 'f': + gen_funcs_h = 1; + 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; break; } if (do_extract + do_tree + do_enums > 1) errx(1, "conflicting options -e/-t/-E"); if (!do_extract && !do_enums && argc != optind) errx(1, "no arguments allowed"); - if ((do_extract || do_enums) && argc == optind) + if (do_extract && argc == optind) errx(1, "no objects specified"); + 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"); + if (infile == NULL) { input_new(stdin, NULL, ""); } else { if ((fp = fopen(infile, "r")) == NULL) err(1, "%s", infile); input_new(fp, NULL, infile); } root = parse_top(gettoken()); while ((tok = gettoken()) != TOK_EOF) merge(&root, parse_top(tok)); - check_tree(root); + if (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++; } return (0); } if (do_enums) { - while (optind < argc) { - if (extract_enum(stdout, argv[optind])) - errx(1, "enum not found: %s", argv[optind]); - optind++; - } + make_enums(argc - optind, argv + optind, + gen_funcs_h, gen_funcs_c); return (0); } if (do_tree) { gen_tree(root, 0); return (0); } sprintf(fname, "%stree.h", file_prefix); if ((fp = fopen(fname, "w")) == NULL) err(1, "%s: ", fname); gen_header(fp, root, PREFIX_LEN, NULL); fprintf(fp, "\n#ifdef SNMPTREE_TYPES\n"); - gen_enums(fp); + 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); } Index: head/contrib/bsnmp/lib/snmp.h =================================================================== --- head/contrib/bsnmp/lib/snmp.h (revision 335884) +++ head/contrib/bsnmp/lib/snmp.h (revision 335885) @@ -1,284 +1,287 @@ /* * Copyright (c) 2001-2003 * Fraunhofer Institute for Open Communication Systems (FhG Fokus). * All rights reserved. * * Author: Harti Brandt * * Copyright (c) 2010 The FreeBSD Foundation * All rights reserved. * * Portions of this software were developed by Shteryana Sotirova Shopova * under sponsorship from the FreeBSD Foundation. * * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Begemot: bsnmp/lib/snmp.h,v 1.30 2004/08/06 08:46:54 brandt Exp $ * * Header file for SNMP functions. */ #ifndef snmp_h_ #define snmp_h_ #include +#define BSNMP_MAJOR 1 +#define BSNMP_MINOR 13 + #define SNMP_COMMUNITY_MAXLEN 128 #define SNMP_MAX_BINDINGS 100 #define SNMP_CONTEXT_NAME_SIZ (32 + 1) #define SNMP_ENGINE_ID_SIZ 32 #define SNMP_TIME_WINDOW 150 enum snmp_syntax { SNMP_SYNTAX_NULL = 0, SNMP_SYNTAX_INTEGER, /* == INTEGER32 */ SNMP_SYNTAX_OCTETSTRING, SNMP_SYNTAX_OID, SNMP_SYNTAX_IPADDRESS, SNMP_SYNTAX_COUNTER, SNMP_SYNTAX_GAUGE, /* == UNSIGNED32 */ SNMP_SYNTAX_TIMETICKS, /* v2 additions */ SNMP_SYNTAX_COUNTER64, SNMP_SYNTAX_NOSUCHOBJECT, /* exception */ SNMP_SYNTAX_NOSUCHINSTANCE, /* exception */ SNMP_SYNTAX_ENDOFMIBVIEW, /* exception */ }; struct snmp_value { struct asn_oid var; enum snmp_syntax syntax; union snmp_values { int32_t integer; /* also integer32 */ struct { u_int len; u_char *octets; } octetstring; struct asn_oid oid; u_char ipaddress[4]; uint32_t uint32; /* also gauge32, counter32, unsigned32, timeticks */ uint64_t counter64; } v; }; enum snmp_version { SNMP_Verr = 0, SNMP_V1 = 1, SNMP_V2c = 2, SNMP_V3, }; #define SNMP_MPM_SNMP_V1 0 #define SNMP_MPM_SNMP_V2c 1 #define SNMP_MPM_SNMP_V3 3 #define SNMP_ADM_STR32_SIZ (32 + 1) #define SNMP_AUTH_KEY_SIZ 40 #define SNMP_PRIV_KEY_SIZ 32 #define SNMP_USM_AUTH_SIZE 12 #define SNMP_USM_PRIV_SIZE 8 #define SNMP_AUTH_HMACMD5_KEY_SIZ 16 #define SNMP_AUTH_HMACSHA_KEY_SIZ 20 #define SNMP_PRIV_AES_KEY_SIZ 16 #define SNMP_PRIV_DES_KEY_SIZ 8 enum snmp_secmodel { SNMP_SECMODEL_ANY = 0, SNMP_SECMODEL_SNMPv1 = 1, SNMP_SECMODEL_SNMPv2c = 2, SNMP_SECMODEL_USM = 3, SNMP_SECMODEL_UNKNOWN }; enum snmp_usm_level { SNMP_noAuthNoPriv = 1, SNMP_authNoPriv = 2, SNMP_authPriv = 3 }; enum snmp_authentication { SNMP_AUTH_NOAUTH = 0, SNMP_AUTH_HMAC_MD5, SNMP_AUTH_HMAC_SHA }; enum snmp_privacy { SNMP_PRIV_NOPRIV = 0, SNMP_PRIV_DES = 1, SNMP_PRIV_AES }; struct snmp_engine { uint8_t engine_id[SNMP_ENGINE_ID_SIZ]; uint32_t engine_len; int32_t engine_boots; int32_t engine_time; int32_t max_msg_size; }; struct snmp_user { char sec_name[SNMP_ADM_STR32_SIZ]; enum snmp_authentication auth_proto; enum snmp_privacy priv_proto; uint8_t auth_key[SNMP_AUTH_KEY_SIZ]; uint8_t priv_key[SNMP_PRIV_KEY_SIZ]; }; struct snmp_pdu { char community[SNMP_COMMUNITY_MAXLEN + 1]; enum snmp_version version; u_int type; /* SNMPv3 PDU header fields */ int32_t identifier; uint8_t flags; int32_t security_model; struct snmp_engine engine; /* Associated USM user parameters */ struct snmp_user user; uint8_t msg_digest[SNMP_USM_AUTH_SIZE]; uint8_t msg_salt[SNMP_USM_PRIV_SIZE]; /* View-based Access Model */ /* XXX: put in separate structure - conflicts with struct snmp_context */ uint32_t context_engine_len; uint8_t context_engine[SNMP_ENGINE_ID_SIZ]; char context_name[SNMP_CONTEXT_NAME_SIZ]; /* trap only */ struct asn_oid enterprise; u_char agent_addr[4]; int32_t generic_trap; int32_t specific_trap; uint32_t time_stamp; /* others */ int32_t request_id; int32_t error_status; int32_t error_index; /* fixes for encoding */ size_t outer_len; asn_len_t scoped_len; u_char *outer_ptr; u_char *digest_ptr; u_char *encrypted_ptr; u_char *scoped_ptr; u_char *pdu_ptr; u_char *vars_ptr; struct snmp_value bindings[SNMP_MAX_BINDINGS]; u_int nbindings; }; #define snmp_v1_pdu snmp_pdu #define SNMP_PDU_GET 0 #define SNMP_PDU_GETNEXT 1 #define SNMP_PDU_RESPONSE 2 #define SNMP_PDU_SET 3 #define SNMP_PDU_TRAP 4 /* v1 */ #define SNMP_PDU_GETBULK 5 /* v2 */ #define SNMP_PDU_INFORM 6 /* v2 */ #define SNMP_PDU_TRAP2 7 /* v2 */ #define SNMP_PDU_REPORT 8 /* v2 */ #define SNMP_ERR_NOERROR 0 #define SNMP_ERR_TOOBIG 1 #define SNMP_ERR_NOSUCHNAME 2 /* v1 */ #define SNMP_ERR_BADVALUE 3 /* v1 */ #define SNMP_ERR_READONLY 4 /* v1 */ #define SNMP_ERR_GENERR 5 #define SNMP_ERR_NO_ACCESS 6 /* v2 */ #define SNMP_ERR_WRONG_TYPE 7 /* v2 */ #define SNMP_ERR_WRONG_LENGTH 8 /* v2 */ #define SNMP_ERR_WRONG_ENCODING 9 /* v2 */ #define SNMP_ERR_WRONG_VALUE 10 /* v2 */ #define SNMP_ERR_NO_CREATION 11 /* v2 */ #define SNMP_ERR_INCONS_VALUE 12 /* v2 */ #define SNMP_ERR_RES_UNAVAIL 13 /* v2 */ #define SNMP_ERR_COMMIT_FAILED 14 /* v2 */ #define SNMP_ERR_UNDO_FAILED 15 /* v2 */ #define SNMP_ERR_AUTH_ERR 16 /* v2 */ #define SNMP_ERR_NOT_WRITEABLE 17 /* v2 */ #define SNMP_ERR_INCONS_NAME 18 /* v2 */ #define SNMP_TRAP_COLDSTART 0 #define SNMP_TRAP_WARMSTART 1 #define SNMP_TRAP_LINKDOWN 2 #define SNMP_TRAP_LINKUP 3 #define SNMP_TRAP_AUTHENTICATION_FAILURE 4 #define SNMP_TRAP_EGP_NEIGHBOR_LOSS 5 #define SNMP_TRAP_ENTERPRISE 6 enum snmp_code { SNMP_CODE_OK = 0, SNMP_CODE_FAILED, SNMP_CODE_BADVERS, SNMP_CODE_BADLEN, SNMP_CODE_BADENC, SNMP_CODE_OORANGE, SNMP_CODE_BADSECLEVEL, SNMP_CODE_NOTINTIME, SNMP_CODE_BADUSER, SNMP_CODE_BADENGINE, SNMP_CODE_BADDIGEST, SNMP_CODE_EDECRYPT }; #define SNMP_MSG_AUTH_FLAG 0x1 #define SNMP_MSG_PRIV_FLAG 0x2 #define SNMP_MSG_REPORT_FLAG 0x4 #define SNMP_MSG_AUTODISCOVER 0x80 void snmp_value_free(struct snmp_value *); int snmp_value_parse(const char *, enum snmp_syntax, union snmp_values *); int snmp_value_copy(struct snmp_value *, const struct snmp_value *); void snmp_pdu_free(struct snmp_pdu *); void snmp_pdu_init_secparams(struct snmp_pdu *); enum snmp_code snmp_pdu_decode(struct asn_buf *b, struct snmp_pdu *pdu, int32_t *); enum snmp_code snmp_pdu_decode_header(struct asn_buf *, struct snmp_pdu *); enum snmp_code snmp_pdu_decode_scoped(struct asn_buf *, struct snmp_pdu *, int32_t *); enum snmp_code snmp_pdu_encode(struct snmp_pdu *, struct asn_buf *); enum snmp_code snmp_pdu_decode_secmode(struct asn_buf *, struct snmp_pdu *); int snmp_pdu_snoop(const struct asn_buf *); void snmp_pdu_dump(const struct snmp_pdu *pdu); enum snmp_code snmp_passwd_to_keys(struct snmp_user *, char *); enum snmp_code snmp_get_local_keys(struct snmp_user *, uint8_t *, uint32_t); enum snmp_code snmp_calc_keychange(struct snmp_user *, uint8_t *); extern void (*snmp_error)(const char *, ...); extern void (*snmp_printf)(const char *, ...); #define TRUTH_MK(F) ((F) ? 1 : 2) #define TRUTH_GET(T) (((T) == 1) ? 1 : 0) #define TRUTH_OK(T) ((T) == 1 || (T) == 2) #endif Index: head/contrib/bsnmp/lib/tc.def =================================================================== --- head/contrib/bsnmp/lib/tc.def (revision 335884) +++ head/contrib/bsnmp/lib/tc.def (revision 335885) @@ -1,40 +1,48 @@ #- # 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 +) + Index: head/contrib/bsnmp/snmp_mibII/mibII.c =================================================================== --- head/contrib/bsnmp/snmp_mibII/mibII.c (revision 335884) +++ head/contrib/bsnmp/snmp_mibII/mibII.c (revision 335885) @@ -1,1811 +1,1812 @@ /* * Copyright (c) 2001-2003 * Fraunhofer Institute for Open Communication Systems (FhG Fokus). * All rights reserved. * * Author: Harti Brandt * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Begemot: mibII.c 516 2006-10-27 15:54:02Z brandt_h $ * * Implementation of the standard interfaces and ip MIB. */ #include "mibII.h" #include "mibII_oid.h" #include #include /*****************************/ /* our module */ static struct lmodule *module; /* routing socket */ static int route; static void *route_fd; /* if-index allocator */ static uint32_t next_if_index = 1; /* currently fetching the arp table */ static int in_update_arp; /* OR registrations */ static u_int ifmib_reg; static u_int ipmib_reg; static u_int tcpmib_reg; static u_int udpmib_reg; static u_int ipForward_reg; /*****************************/ /* list of all IP addresses */ struct mibifa_list mibifa_list = TAILQ_HEAD_INITIALIZER(mibifa_list); /* list of all interfaces */ struct mibif_list mibif_list = TAILQ_HEAD_INITIALIZER(mibif_list); /* list of dynamic interface names */ struct mibdynif_list mibdynif_list = SLIST_HEAD_INITIALIZER(mibdynif_list); /* list of all interface index mappings */ struct mibindexmap_list mibindexmap_list = STAILQ_HEAD_INITIALIZER(mibindexmap_list); /* list of all stacking entries */ struct mibifstack_list mibifstack_list = TAILQ_HEAD_INITIALIZER(mibifstack_list); /* list of all receive addresses */ struct mibrcvaddr_list mibrcvaddr_list = TAILQ_HEAD_INITIALIZER(mibrcvaddr_list); /* list of all NetToMedia entries */ struct mibarp_list mibarp_list = TAILQ_HEAD_INITIALIZER(mibarp_list); /* number of interfaces */ int32_t mib_if_number; /* last change of table */ uint64_t mib_iftable_last_change; /* last change of stack table */ uint64_t mib_ifstack_last_change; /* if this is set, one of our lists may be bad. refresh them when idle */ int mib_iflist_bad; /* network socket */ int mib_netsock; /* last time refreshed */ uint64_t mibarpticks; /* info on system clocks */ struct clockinfo clockinfo; /* list of all New if registrations */ static struct newifreg_list newifreg_list = TAILQ_HEAD_INITIALIZER(newifreg_list); /* baud rate of fastest interface */ uint64_t mibif_maxspeed; /* user-forced update interval */ u_int mibif_force_hc_update_interval; /* current update interval */ u_int mibif_hc_update_interval; /* HC update timer handle */ static void *hc_update_timer; /* Idle poll timer */ static void *mibII_poll_timer; /* interfaces' data poll interval */ u_int mibII_poll_ticks; /* Idle poll hook */ static void mibII_idle(void *arg __unused); /*****************************/ static const struct asn_oid oid_ifMIB = OIDX_ifMIB; static const struct asn_oid oid_ipMIB = OIDX_ipMIB; static const struct asn_oid oid_tcpMIB = OIDX_tcpMIB; static const struct asn_oid oid_udpMIB = OIDX_udpMIB; static const struct asn_oid oid_ipForward = OIDX_ipForward; static const struct asn_oid oid_linkDown = OIDX_linkDown; static const struct asn_oid oid_linkUp = OIDX_linkUp; static const struct asn_oid oid_ifIndex = OIDX_ifIndex; /*****************************/ /* * Find an interface */ struct mibif * mib_find_if(u_int idx) { struct mibif *ifp; TAILQ_FOREACH(ifp, &mibif_list, link) if (ifp->index == idx) return (ifp); return (NULL); } struct mibif * mib_find_if_sys(u_int sysindex) { struct mibif *ifp; TAILQ_FOREACH(ifp, &mibif_list, link) if (ifp->sysindex == sysindex) return (ifp); return (NULL); } struct mibif * mib_find_if_name(const char *name) { struct mibif *ifp; TAILQ_FOREACH(ifp, &mibif_list, link) if (strcmp(ifp->name, name) == 0) return (ifp); return (NULL); } /* * Check whether an interface is dynamic. The argument may include the * unit number. This assumes, that the name part does NOT contain digits. */ int mib_if_is_dyn(const char *name) { size_t len; struct mibdynif *d; for (len = 0; name[len] != '\0' && isalpha(name[len]) ; len++) ; SLIST_FOREACH(d, &mibdynif_list, link) if (strlen(d->name) == len && strncmp(d->name, name, len) == 0) return (1); return (0); } /* set an interface name to dynamic mode */ void mib_if_set_dyn(const char *name) { struct mibdynif *d; SLIST_FOREACH(d, &mibdynif_list, link) if (strcmp(name, d->name) == 0) return; if ((d = malloc(sizeof(*d))) == NULL) err(1, NULL); strlcpy(d->name, name, sizeof(d->name)); SLIST_INSERT_HEAD(&mibdynif_list, d, link); } /* * register for interface creations */ int mib_register_newif(int (*func)(struct mibif *), const struct lmodule *mod) { struct newifreg *reg; TAILQ_FOREACH(reg, &newifreg_list, link) if (reg->mod == mod) { reg->func = func; return (0); } if ((reg = malloc(sizeof(*reg))) == NULL) { syslog(LOG_ERR, "newifreg: %m"); return (-1); } reg->mod = mod; reg->func = func; TAILQ_INSERT_TAIL(&newifreg_list, reg, link); return (0); } void mib_unregister_newif(const struct lmodule *mod) { struct newifreg *reg; TAILQ_FOREACH(reg, &newifreg_list, link) if (reg->mod == mod) { TAILQ_REMOVE(&newifreg_list, reg, link); free(reg); return; } } struct mibif * mib_first_if(void) { return (TAILQ_FIRST(&mibif_list)); } struct mibif * mib_next_if(const struct mibif *ifp) { return (TAILQ_NEXT(ifp, link)); } /* * Change the admin status of an interface */ int mib_if_admin(struct mibif *ifp, int up) { struct ifreq ifr; strlcpy(ifr.ifr_name, ifp->name, sizeof(ifr.ifr_name)); if (ioctl(mib_netsock, SIOCGIFFLAGS, &ifr) == -1) { syslog(LOG_ERR, "SIOCGIFFLAGS(%s): %m", ifp->name); return (-1); } if (up) ifr.ifr_flags |= IFF_UP; else ifr.ifr_flags &= ~IFF_UP; if (ioctl(mib_netsock, SIOCSIFFLAGS, &ifr) == -1) { syslog(LOG_ERR, "SIOCSIFFLAGS(%s): %m", ifp->name); return (-1); } (void)mib_fetch_ifmib(ifp); return (0); } /* * Generate a link up/down trap */ static void link_trap(struct mibif *ifp, int up) { struct snmp_value ifindex; ifindex.var = oid_ifIndex; ifindex.var.subs[ifindex.var.len++] = ifp->index; ifindex.syntax = SNMP_SYNTAX_INTEGER; ifindex.v.integer = ifp->index; snmp_send_trap(up ? &oid_linkUp : &oid_linkDown, &ifindex, (struct snmp_value *)NULL); } /** * Fetch the GENERIC IFMIB and update the HC counters */ static int fetch_generic_mib(struct mibif *ifp, const struct ifmibdata *old) { int name[6]; size_t len; struct mibif_private *p = ifp->private; name[0] = CTL_NET; name[1] = PF_LINK; name[2] = NETLINK_GENERIC; name[3] = IFMIB_IFDATA; name[4] = ifp->sysindex; name[5] = IFDATA_GENERAL; len = sizeof(ifp->mib); if (sysctl(name, nitems(name), &ifp->mib, &len, NULL, 0) == -1) { if (errno != ENOENT) syslog(LOG_WARNING, "sysctl(ifmib, %s) failed %m", ifp->name); return (-1); } /* * Assume that one of the two following compounds is optimized away */ if (ULONG_MAX >= 0xffffffffffffffffULL) { p->hc_inoctets = ifp->mib.ifmd_data.ifi_ibytes; p->hc_outoctets = ifp->mib.ifmd_data.ifi_obytes; p->hc_omcasts = ifp->mib.ifmd_data.ifi_omcasts; p->hc_opackets = ifp->mib.ifmd_data.ifi_opackets; p->hc_imcasts = ifp->mib.ifmd_data.ifi_imcasts; p->hc_ipackets = ifp->mib.ifmd_data.ifi_ipackets; } else if (ULONG_MAX >= 0xffffffff) { #define UPDATE(HC, MIB) \ if (old->ifmd_data.MIB > ifp->mib.ifmd_data.MIB) \ p->HC += (0x100000000ULL + \ ifp->mib.ifmd_data.MIB) - \ old->ifmd_data.MIB; \ else \ p->HC += ifp->mib.ifmd_data.MIB - \ old->ifmd_data.MIB; UPDATE(hc_inoctets, ifi_ibytes) UPDATE(hc_outoctets, ifi_obytes) UPDATE(hc_omcasts, ifi_omcasts) UPDATE(hc_opackets, ifi_opackets) UPDATE(hc_imcasts, ifi_imcasts) UPDATE(hc_ipackets, ifi_ipackets) #undef UPDATE } else abort(); return (0); } /** * Update the 64-bit interface counters */ static void update_hc_counters(void *arg __unused) { struct mibif *ifp; struct ifmibdata oldmib; TAILQ_FOREACH(ifp, &mibif_list, link) { oldmib = ifp->mib; (void)fetch_generic_mib(ifp, &oldmib); } } /** * Recompute the poll timer for the HC counters */ void mibif_reset_hc_timer(void) { u_int ticks; if ((ticks = mibif_force_hc_update_interval) == 0) { if (mibif_maxspeed <= IF_Mbps(10)) { /* at 10Mbps overflow needs 3436 seconds */ ticks = 3000 * 100; /* 50 minutes */ } else if (mibif_maxspeed <= IF_Mbps(100)) { /* at 100Mbps overflow needs 343 seconds */ ticks = 300 * 100; /* 5 minutes */ } else if (mibif_maxspeed < IF_Mbps(622)) { /* at 622Mbps overflow needs 53 seconds */ ticks = 40 * 100; /* 40 seconds */ } else if (mibif_maxspeed <= IF_Mbps(1000)) { /* at 1Gbps overflow needs 34 seconds */ ticks = 20 * 100; /* 20 seconds */ } else { /* at 10Gbps overflow needs 3.4 seconds */ ticks = 100; /* 1 seconds */ } } if (ticks == mibif_hc_update_interval) return; if (hc_update_timer != NULL) { timer_stop(hc_update_timer); hc_update_timer = NULL; } update_hc_counters(NULL); if ((hc_update_timer = timer_start_repeat(ticks * 10, ticks * 10, update_hc_counters, NULL, module)) == NULL) { syslog(LOG_ERR, "timer_start(%u): %m", ticks); return; } mibif_hc_update_interval = ticks; } /** * Restart the idle poll timer. */ void mibif_restart_mibII_poll_timer(void) { if (mibII_poll_timer != NULL) timer_stop(mibII_poll_timer); if ((mibII_poll_timer = timer_start_repeat(mibII_poll_ticks * 10, mibII_poll_ticks * 10, mibII_idle, NULL, module)) == NULL) syslog(LOG_ERR, "timer_start(%u): %m", mibII_poll_ticks); } /* * Fetch new MIB data. */ int mib_fetch_ifmib(struct mibif *ifp) { int name[6]; size_t len; void *newmib; struct ifmibdata oldmib = ifp->mib; struct ifreq irr; if (fetch_generic_mib(ifp, &oldmib) == -1) return (-1); /* * Quoting RFC2863, 3.1.15: "... LinkUp and linkDown traps are * generated just after ifOperStatus leaves, or just before it * enters, the down state, respectively;" */ if (ifp->trap_enable && ifp->mib.ifmd_data.ifi_link_state != oldmib.ifmd_data.ifi_link_state && (ifp->mib.ifmd_data.ifi_link_state == LINK_STATE_DOWN || oldmib.ifmd_data.ifi_link_state == LINK_STATE_DOWN)) link_trap(ifp, ifp->mib.ifmd_data.ifi_link_state == LINK_STATE_UP ? 1 : 0); ifp->flags &= ~(MIBIF_HIGHSPEED | MIBIF_VERYHIGHSPEED); if (ifp->mib.ifmd_data.ifi_baudrate > 20000000) { ifp->flags |= MIBIF_HIGHSPEED; if (ifp->mib.ifmd_data.ifi_baudrate > 650000000) ifp->flags |= MIBIF_VERYHIGHSPEED; } if (ifp->mib.ifmd_data.ifi_baudrate > mibif_maxspeed) { mibif_maxspeed = ifp->mib.ifmd_data.ifi_baudrate; mibif_reset_hc_timer(); } /* * linkspecific MIB */ name[0] = CTL_NET; name[1] = PF_LINK; name[2] = NETLINK_GENERIC; name[3] = IFMIB_IFDATA; name[4] = ifp->sysindex; name[5] = IFDATA_LINKSPECIFIC; if (sysctl(name, nitems(name), NULL, &len, NULL, 0) == -1) { syslog(LOG_WARNING, "sysctl linkmib estimate (%s): %m", ifp->name); if (ifp->specmib != NULL) { ifp->specmib = NULL; ifp->specmiblen = 0; } goto out; } if (len == 0) { if (ifp->specmib != NULL) { ifp->specmib = NULL; ifp->specmiblen = 0; } goto out; } if (ifp->specmiblen != len) { if ((newmib = realloc(ifp->specmib, len)) == NULL) { ifp->specmib = NULL; ifp->specmiblen = 0; goto out; } ifp->specmib = newmib; ifp->specmiblen = len; } if (sysctl(name, nitems(name), ifp->specmib, &len, NULL, 0) == -1) { syslog(LOG_WARNING, "sysctl linkmib (%s): %m", ifp->name); if (ifp->specmib != NULL) { ifp->specmib = NULL; ifp->specmiblen = 0; } } out: strlcpy(irr.ifr_name, ifp->name, sizeof(irr.ifr_name)); irr.ifr_buffer.buffer = MIBIF_PRIV(ifp)->alias; irr.ifr_buffer.length = sizeof(MIBIF_PRIV(ifp)->alias); if (ioctl(mib_netsock, SIOCGIFDESCR, &irr) == -1) { MIBIF_PRIV(ifp)->alias[0] = 0; if (errno != ENOMSG) syslog(LOG_WARNING, "SIOCGIFDESCR (%s): %m", ifp->name); } else if (irr.ifr_buffer.buffer == NULL) { MIBIF_PRIV(ifp)->alias[0] = 0; syslog(LOG_WARNING, "SIOCGIFDESCR (%s): too long (%zu)", ifp->name, irr.ifr_buffer.length); } ifp->mibtick = get_ticks(); return (0); } /* find first/next address for a given interface */ struct mibifa * mib_first_ififa(const struct mibif *ifp) { struct mibifa *ifa; TAILQ_FOREACH(ifa, &mibifa_list, link) if (ifp->index == ifa->ifindex) return (ifa); return (NULL); } struct mibifa * mib_next_ififa(struct mibifa *ifa0) { struct mibifa *ifa; ifa = ifa0; while ((ifa = TAILQ_NEXT(ifa, link)) != NULL) if (ifa->ifindex == ifa0->ifindex) return (ifa); return (NULL); } /* * Allocate a new IFA */ static struct mibifa * alloc_ifa(u_int ifindex, struct in_addr addr) { struct mibifa *ifa; uint32_t ha; if ((ifa = malloc(sizeof(struct mibifa))) == NULL) { syslog(LOG_ERR, "ifa: %m"); return (NULL); } ifa->inaddr = addr; ifa->ifindex = ifindex; ha = ntohl(ifa->inaddr.s_addr); ifa->index.len = 4; ifa->index.subs[0] = (ha >> 24) & 0xff; ifa->index.subs[1] = (ha >> 16) & 0xff; ifa->index.subs[2] = (ha >> 8) & 0xff; ifa->index.subs[3] = (ha >> 0) & 0xff; ifa->flags = 0; ifa->inbcast.s_addr = 0; ifa->inmask.s_addr = 0xffffffff; INSERT_OBJECT_OID(ifa, &mibifa_list); return (ifa); } /* * Delete an interface address */ static void destroy_ifa(struct mibifa *ifa) { TAILQ_REMOVE(&mibifa_list, ifa, link); free(ifa); } /* * Helper routine to extract the sockaddr structures from a routing * socket message. */ void mib_extract_addrs(int addrs, u_char *info, struct sockaddr **out) { u_int i; for (i = 0; i < RTAX_MAX; i++) { if ((addrs & (1 << i)) != 0) { *out = (struct sockaddr *)(void *)info; info += roundup((*out)->sa_len, sizeof(long)); } else *out = NULL; out++; } } /* * save the phys address of an interface. Handle receive address entries here. */ static void get_physaddr(struct mibif *ifp, struct sockaddr_dl *sdl, u_char *ptr) { u_char *np; struct mibrcvaddr *rcv; if (sdl->sdl_alen == 0) { /* no address */ if (ifp->physaddrlen != 0) { if ((rcv = mib_find_rcvaddr(ifp->index, ifp->physaddr, ifp->physaddrlen)) != NULL) mib_rcvaddr_delete(rcv); free(ifp->physaddr); ifp->physaddr = NULL; ifp->physaddrlen = 0; } return; } if (ifp->physaddrlen != sdl->sdl_alen) { /* length changed */ if (ifp->physaddrlen) { /* delete olf receive address */ if ((rcv = mib_find_rcvaddr(ifp->index, ifp->physaddr, ifp->physaddrlen)) != NULL) mib_rcvaddr_delete(rcv); } if ((np = realloc(ifp->physaddr, sdl->sdl_alen)) == NULL) { free(ifp->physaddr); ifp->physaddr = NULL; ifp->physaddrlen = 0; return; } ifp->physaddr = np; ifp->physaddrlen = sdl->sdl_alen; } else if (memcmp(ifp->physaddr, ptr, ifp->physaddrlen) == 0) { /* no change */ return; } else { /* address changed */ /* delete olf receive address */ if ((rcv = mib_find_rcvaddr(ifp->index, ifp->physaddr, ifp->physaddrlen)) != NULL) mib_rcvaddr_delete(rcv); } memcpy(ifp->physaddr, ptr, ifp->physaddrlen); /* make new receive address */ if ((rcv = mib_rcvaddr_create(ifp, ifp->physaddr, ifp->physaddrlen)) != NULL) rcv->flags |= MIBRCVADDR_HW; } /* * Free an interface */ static void mibif_free(struct mibif *ifp) { struct mibif *ifp1; struct mibindexmap *map; struct mibifa *ifa, *ifa1; struct mibrcvaddr *rcv, *rcv1; struct mibarp *at, *at1; if (ifp->xnotify != NULL) (*ifp->xnotify)(ifp, MIBIF_NOTIFY_DESTROY, ifp->xnotify_data); (void)mib_ifstack_delete(ifp, NULL); (void)mib_ifstack_delete(NULL, ifp); TAILQ_REMOVE(&mibif_list, ifp, link); /* if this was the fastest interface - recompute this */ if (ifp->mib.ifmd_data.ifi_baudrate == mibif_maxspeed) { mibif_maxspeed = ifp->mib.ifmd_data.ifi_baudrate; TAILQ_FOREACH(ifp1, &mibif_list, link) if (ifp1->mib.ifmd_data.ifi_baudrate > mibif_maxspeed) mibif_maxspeed = ifp1->mib.ifmd_data.ifi_baudrate; mibif_reset_hc_timer(); } free(ifp->private); ifp->private = NULL; free(ifp->physaddr); ifp->physaddr = NULL; free(ifp->specmib); ifp->specmib = NULL; STAILQ_FOREACH(map, &mibindexmap_list, link) if (map->mibif == ifp) { map->mibif = NULL; break; } /* purge interface addresses */ ifa = TAILQ_FIRST(&mibifa_list); while (ifa != NULL) { ifa1 = TAILQ_NEXT(ifa, link); if (ifa->ifindex == ifp->index) destroy_ifa(ifa); ifa = ifa1; } /* purge receive addresses */ rcv = TAILQ_FIRST(&mibrcvaddr_list); while (rcv != NULL) { rcv1 = TAILQ_NEXT(rcv, link); if (rcv->ifindex == ifp->index) mib_rcvaddr_delete(rcv); rcv = rcv1; } /* purge ARP entries */ at = TAILQ_FIRST(&mibarp_list); while (at != NULL) { at1 = TAILQ_NEXT(at, link); if (at->index.subs[0] == ifp->index) mib_arp_delete(at); at = at1; } free(ifp); ifp = NULL; mib_if_number--; mib_iftable_last_change = this_tick; } /* * Create a new interface */ static struct mibif * mibif_create(u_int sysindex, const char *name) { struct mibif *ifp; struct mibindexmap *map; if ((ifp = malloc(sizeof(*ifp))) == NULL) { syslog(LOG_WARNING, "%s: %m", __func__); return (NULL); } memset(ifp, 0, sizeof(*ifp)); if ((ifp->private = malloc(sizeof(struct mibif_private))) == NULL) { syslog(LOG_WARNING, "%s: %m", __func__); free(ifp); return (NULL); } memset(ifp->private, 0, sizeof(struct mibif_private)); ifp->sysindex = sysindex; strlcpy(ifp->name, name, sizeof(ifp->name)); strlcpy(ifp->descr, name, sizeof(ifp->descr)); ifp->spec_oid = oid_zeroDotZero; map = NULL; if (!mib_if_is_dyn(ifp->name)) { /* non-dynamic. look whether we know the interface */ STAILQ_FOREACH(map, &mibindexmap_list, link) if (strcmp(map->name, ifp->name) == 0) { ifp->index = map->ifindex; map->mibif = ifp; break; } /* assume it has a connector if it is not dynamic */ ifp->has_connector = 1; ifp->trap_enable = 1; } if (map == NULL) { /* new interface - get new index */ if (next_if_index > 0x7fffffff) errx(1, "ifindex wrap"); if ((map = malloc(sizeof(*map))) == NULL) { syslog(LOG_ERR, "ifmap: %m"); free(ifp); return (NULL); } map->ifindex = next_if_index++; map->sysindex = ifp->sysindex; strcpy(map->name, ifp->name); map->mibif = ifp; STAILQ_INSERT_TAIL(&mibindexmap_list, map, link); } else { /* re-instantiate. Introduce a counter discontinuity */ ifp->counter_disc = get_ticks(); } ifp->index = map->ifindex; ifp->mib.ifmd_data.ifi_link_state = LINK_STATE_UNKNOWN; INSERT_OBJECT_INT(ifp, &mibif_list); mib_if_number++; mib_iftable_last_change = this_tick; /* instantiate default ifStack entries */ (void)mib_ifstack_create(ifp, NULL); (void)mib_ifstack_create(NULL, ifp); return (ifp); } /* * Inform all interested parties about a new interface */ static void notify_newif(struct mibif *ifp) { struct newifreg *reg; TAILQ_FOREACH(reg, &newifreg_list, link) if ((*reg->func)(ifp)) return; } /* * This is called for new interfaces after we have fetched the interface * MIB. If this is a broadcast interface try to guess the broadcast address * depending on the interface type. */ static void check_llbcast(struct mibif *ifp) { static u_char ether_bcast[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; struct mibrcvaddr *rcv; if (!(ifp->mib.ifmd_flags & IFF_BROADCAST)) return; switch (ifp->mib.ifmd_data.ifi_type) { case IFT_ETHER: case IFT_FDDI: case IFT_ISO88025: case IFT_L2VLAN: if (mib_find_rcvaddr(ifp->index, ether_bcast, 6) == NULL && (rcv = mib_rcvaddr_create(ifp, ether_bcast, 6)) != NULL) rcv->flags |= MIBRCVADDR_BCAST; break; } } /* * Retrieve the current interface list from the system. */ void mib_refresh_iflist(void) { struct mibif *ifp, *ifp1; size_t len; u_short idx; int name[6]; int count; struct ifmibdata mib; TAILQ_FOREACH(ifp, &mibif_list, link) ifp->flags &= ~MIBIF_FOUND; len = sizeof(count); if (sysctlbyname("net.link.generic.system.ifcount", &count, &len, NULL, 0) == -1) { syslog(LOG_ERR, "ifcount: %m"); return; } name[0] = CTL_NET; name[1] = PF_LINK; name[2] = NETLINK_GENERIC; name[3] = IFMIB_IFDATA; name[5] = IFDATA_GENERAL; for (idx = 1; idx <= count; idx++) { name[4] = idx; len = sizeof(mib); if (sysctl(name, nitems(name), &mib, &len, NULL, 0) == -1) { if (errno == ENOENT) continue; syslog(LOG_ERR, "ifmib(%u): %m", idx); return; } if ((ifp = mib_find_if_sys(idx)) != NULL) { ifp->flags |= MIBIF_FOUND; continue; } /* Unknown interface - create */ if ((ifp = mibif_create(idx, mib.ifmd_name)) != NULL) { ifp->flags |= MIBIF_FOUND; (void)mib_fetch_ifmib(ifp); check_llbcast(ifp); notify_newif(ifp); } } /* * Purge interfaces that disappeared */ ifp = TAILQ_FIRST(&mibif_list); while (ifp != NULL) { ifp1 = TAILQ_NEXT(ifp, link); if (!(ifp->flags & MIBIF_FOUND)) mibif_free(ifp); ifp = ifp1; } } /* * Find an interface address */ struct mibifa * mib_find_ifa(struct in_addr addr) { struct mibifa *ifa; TAILQ_FOREACH(ifa, &mibifa_list, link) if (ifa->inaddr.s_addr == addr.s_addr) return (ifa); return (NULL); } /* * Process a new ARP entry */ static void process_arp(const struct rt_msghdr *rtm, const struct sockaddr_dl *sdl, const struct sockaddr_in *sa) { struct mibif *ifp; struct mibarp *at; /* IP arp table entry */ if (sdl->sdl_alen == 0) return; if ((ifp = mib_find_if_sys(sdl->sdl_index)) == NULL) return; /* have a valid entry */ if ((at = mib_find_arp(ifp, sa->sin_addr)) == NULL && (at = mib_arp_create(ifp, sa->sin_addr, sdl->sdl_data + sdl->sdl_nlen, sdl->sdl_alen)) == NULL) return; if (rtm->rtm_rmx.rmx_expire == 0) at->flags |= MIBARP_PERM; else at->flags &= ~MIBARP_PERM; at->flags |= MIBARP_FOUND; } /* * Handle a routing socket message. */ static void handle_rtmsg(struct rt_msghdr *rtm) { struct sockaddr *addrs[RTAX_MAX]; struct if_msghdr *ifm; struct ifa_msghdr ifam, *ifamp; struct ifma_msghdr *ifmam; #ifdef RTM_IFANNOUNCE struct if_announcemsghdr *ifan; #endif struct mibif *ifp; struct sockaddr_dl *sdl; struct sockaddr_in *sa; struct mibifa *ifa; struct mibrcvaddr *rcv; u_char *ptr; if (rtm->rtm_version != RTM_VERSION) { syslog(LOG_ERR, "Bogus RTM version %u", rtm->rtm_version); return; } switch (rtm->rtm_type) { case RTM_NEWADDR: ifamp = (struct ifa_msghdr *)rtm; memcpy(&ifam, ifamp, sizeof(ifam)); mib_extract_addrs(ifam.ifam_addrs, (u_char *)(ifamp + 1), addrs); if (addrs[RTAX_IFA] == NULL || addrs[RTAX_NETMASK] == NULL) break; sa = (struct sockaddr_in *)(void *)addrs[RTAX_IFA]; if ((ifa = mib_find_ifa(sa->sin_addr)) == NULL) { /* unknown address */ if ((ifp = mib_find_if_sys(ifam.ifam_index)) == NULL) { syslog(LOG_WARNING, "RTM_NEWADDR for unknown " "interface %u", ifam.ifam_index); break; } if ((ifa = alloc_ifa(ifp->index, sa->sin_addr)) == NULL) break; } sa = (struct sockaddr_in *)(void *)addrs[RTAX_NETMASK]; ifa->inmask = sa->sin_addr; if (addrs[RTAX_BRD] != NULL) { sa = (struct sockaddr_in *)(void *)addrs[RTAX_BRD]; ifa->inbcast = sa->sin_addr; } ifa->flags |= MIBIFA_FOUND; break; case RTM_DELADDR: ifamp = (struct ifa_msghdr *)rtm; memcpy(&ifam, ifamp, sizeof(ifam)); mib_extract_addrs(ifam.ifam_addrs, (u_char *)(ifamp + 1), addrs); if (addrs[RTAX_IFA] == NULL) break; sa = (struct sockaddr_in *)(void *)addrs[RTAX_IFA]; if ((ifa = mib_find_ifa(sa->sin_addr)) != NULL) { ifa->flags |= MIBIFA_FOUND; if (!(ifa->flags & MIBIFA_DESTROYED)) destroy_ifa(ifa); } break; case RTM_NEWMADDR: ifmam = (struct ifma_msghdr *)rtm; mib_extract_addrs(ifmam->ifmam_addrs, (u_char *)(ifmam + 1), addrs); if (addrs[RTAX_IFA] == NULL || addrs[RTAX_IFA]->sa_family != AF_LINK) break; sdl = (struct sockaddr_dl *)(void *)addrs[RTAX_IFA]; if ((rcv = mib_find_rcvaddr(sdl->sdl_index, sdl->sdl_data + sdl->sdl_nlen, sdl->sdl_alen)) == NULL) { /* unknown address */ if ((ifp = mib_find_if_sys(sdl->sdl_index)) == NULL) { syslog(LOG_WARNING, "RTM_NEWMADDR for unknown " "interface %u", sdl->sdl_index); break; } if ((rcv = mib_rcvaddr_create(ifp, sdl->sdl_data + sdl->sdl_nlen, sdl->sdl_alen)) == NULL) break; rcv->flags |= MIBRCVADDR_VOLATILE; } rcv->flags |= MIBRCVADDR_FOUND; break; case RTM_DELMADDR: ifmam = (struct ifma_msghdr *)rtm; mib_extract_addrs(ifmam->ifmam_addrs, (u_char *)(ifmam + 1), addrs); if (addrs[RTAX_IFA] == NULL || addrs[RTAX_IFA]->sa_family != AF_LINK) break; sdl = (struct sockaddr_dl *)(void *)addrs[RTAX_IFA]; if ((rcv = mib_find_rcvaddr(sdl->sdl_index, sdl->sdl_data + sdl->sdl_nlen, sdl->sdl_alen)) != NULL) mib_rcvaddr_delete(rcv); break; case RTM_IFINFO: ifm = (struct if_msghdr *)(void *)rtm; mib_extract_addrs(ifm->ifm_addrs, (u_char *)(ifm + 1), addrs); if ((ifp = mib_find_if_sys(ifm->ifm_index)) == NULL) break; if (addrs[RTAX_IFP] != NULL && addrs[RTAX_IFP]->sa_family == AF_LINK) { sdl = (struct sockaddr_dl *)(void *)addrs[RTAX_IFP]; ptr = sdl->sdl_data + sdl->sdl_nlen; get_physaddr(ifp, sdl, ptr); } (void)mib_fetch_ifmib(ifp); break; #ifdef RTM_IFANNOUNCE case RTM_IFANNOUNCE: ifan = (struct if_announcemsghdr *)rtm; ifp = mib_find_if_sys(ifan->ifan_index); switch (ifan->ifan_what) { case IFAN_ARRIVAL: if (ifp == NULL && (ifp = mibif_create(ifan->ifan_index, ifan->ifan_name)) != NULL) { (void)mib_fetch_ifmib(ifp); check_llbcast(ifp); notify_newif(ifp); } break; case IFAN_DEPARTURE: if (ifp != NULL) mibif_free(ifp); break; } break; #endif case RTM_GET: case RTM_ADD: mib_extract_addrs(rtm->rtm_addrs, (u_char *)(rtm + 1), addrs); if (rtm->rtm_flags & RTF_LLINFO) { if (addrs[RTAX_DST] == NULL || addrs[RTAX_GATEWAY] == NULL || addrs[RTAX_DST]->sa_family != AF_INET || addrs[RTAX_GATEWAY]->sa_family != AF_LINK) break; process_arp(rtm, (struct sockaddr_dl *)(void *)addrs[RTAX_GATEWAY], (struct sockaddr_in *)(void *)addrs[RTAX_DST]); } else { if (rtm->rtm_errno == 0 && (rtm->rtm_flags & RTF_UP)) mib_sroute_process(rtm, addrs[RTAX_GATEWAY], addrs[RTAX_DST], addrs[RTAX_NETMASK]); } break; case RTM_DELETE: mib_extract_addrs(rtm->rtm_addrs, (u_char *)(rtm + 1), addrs); if (rtm->rtm_errno == 0 && (rtm->rtm_flags & RTF_UP)) mib_sroute_process(rtm, addrs[RTAX_GATEWAY], addrs[RTAX_DST], addrs[RTAX_NETMASK]); break; } } /* * send a routing message */ void mib_send_rtmsg(struct rt_msghdr *rtm, struct sockaddr *gw, struct sockaddr *dst, struct sockaddr *mask) { size_t len; struct rt_msghdr *msg; char *cp; ssize_t sent; len = sizeof(*rtm) + SA_SIZE(gw) + SA_SIZE(dst) + SA_SIZE(mask); if ((msg = malloc(len)) == NULL) { syslog(LOG_ERR, "%s: %m", __func__); return; } cp = (char *)(msg + 1); memset(msg, 0, sizeof(*msg)); msg->rtm_flags = 0; msg->rtm_version = RTM_VERSION; msg->rtm_addrs = RTA_DST | RTA_GATEWAY; memcpy(cp, dst, SA_SIZE(dst)); cp += SA_SIZE(dst); memcpy(cp, gw, SA_SIZE(gw)); cp += SA_SIZE(gw); if (mask != NULL) { memcpy(cp, mask, SA_SIZE(mask)); cp += SA_SIZE(mask); msg->rtm_addrs |= RTA_NETMASK; } msg->rtm_msglen = cp - (char *)msg; msg->rtm_type = RTM_GET; if ((sent = write(route, msg, msg->rtm_msglen)) == -1) { syslog(LOG_ERR, "%s: write: %m", __func__); free(msg); return; } if (sent != msg->rtm_msglen) { syslog(LOG_ERR, "%s: short write", __func__); free(msg); return; } free(msg); } /* * Fetch the routing table via sysctl */ u_char * mib_fetch_rtab(int af, int info, int arg, size_t *lenp) { int name[6]; u_char *buf, *newbuf; name[0] = CTL_NET; name[1] = PF_ROUTE; name[2] = 0; name[3] = af; name[4] = info; name[5] = arg; *lenp = 0; /* initial estimate */ if (sysctl(name, nitems(name), NULL, lenp, NULL, 0) == -1) { syslog(LOG_ERR, "sysctl estimate (%d,%d,%d,%d,%d,%d): %m", name[0], name[1], name[2], name[3], name[4], name[5]); return (NULL); } if (*lenp == 0) return (NULL); buf = NULL; for (;;) { if ((newbuf = realloc(buf, *lenp)) == NULL) { syslog(LOG_ERR, "sysctl buffer: %m"); free(buf); return (NULL); } buf = newbuf; if (sysctl(name, nitems(name), buf, lenp, NULL, 0) == 0) break; if (errno != ENOMEM) { syslog(LOG_ERR, "sysctl get: %m"); free(buf); return (NULL); } *lenp += *lenp / 8 + 1; } return (buf); } /* * Update the following info: interface, interface addresses, interface * receive addresses, arp-table. * This does not change the interface list itself. */ static void update_ifa_info(void) { u_char *buf, *next; struct rt_msghdr *rtm; struct mibifa *ifa, *ifa1; struct mibrcvaddr *rcv, *rcv1; size_t needed; static const int infos[][3] = { { 0, NET_RT_IFLIST, 0 }, #ifdef NET_RT_IFMALIST { AF_LINK, NET_RT_IFMALIST, 0 }, #endif }; u_int i; TAILQ_FOREACH(ifa, &mibifa_list, link) ifa->flags &= ~MIBIFA_FOUND; TAILQ_FOREACH(rcv, &mibrcvaddr_list, link) rcv->flags &= ~MIBRCVADDR_FOUND; for (i = 0; i < sizeof(infos) / sizeof(infos[0]); i++) { if ((buf = mib_fetch_rtab(infos[i][0], infos[i][1], infos[i][2], &needed)) == NULL) continue; next = buf; while (next < buf + needed) { rtm = (struct rt_msghdr *)(void *)next; next += rtm->rtm_msglen; handle_rtmsg(rtm); } free(buf); } /* * Purge the address list of unused entries. These may happen for * interface aliases that are on the same subnet. We don't receive * routing socket messages for them. */ ifa = TAILQ_FIRST(&mibifa_list); while (ifa != NULL) { ifa1 = TAILQ_NEXT(ifa, link); if (!(ifa->flags & MIBIFA_FOUND)) destroy_ifa(ifa); ifa = ifa1; } rcv = TAILQ_FIRST(&mibrcvaddr_list); while (rcv != NULL) { rcv1 = TAILQ_NEXT(rcv, link); if (!(rcv->flags & (MIBRCVADDR_FOUND | MIBRCVADDR_BCAST | MIBRCVADDR_HW))) mib_rcvaddr_delete(rcv); rcv = rcv1; } } /* * Update arp table */ void mib_arp_update(void) { struct mibarp *at, *at1; size_t needed; u_char *buf, *next; struct rt_msghdr *rtm; if (in_update_arp) return; /* Aaargh */ in_update_arp = 1; TAILQ_FOREACH(at, &mibarp_list, link) at->flags &= ~MIBARP_FOUND; if ((buf = mib_fetch_rtab(AF_INET, NET_RT_FLAGS, 0, &needed)) == NULL) { in_update_arp = 0; return; } next = buf; while (next < buf + needed) { rtm = (struct rt_msghdr *)(void *)next; next += rtm->rtm_msglen; handle_rtmsg(rtm); } free(buf); at = TAILQ_FIRST(&mibarp_list); while (at != NULL) { at1 = TAILQ_NEXT(at, link); if (!(at->flags & MIBARP_FOUND)) mib_arp_delete(at); at = at1; } mibarpticks = get_ticks(); in_update_arp = 0; } /* * Input on the routing socket. */ static void route_input(int fd, void *udata __unused) { u_char buf[1024 * 16]; ssize_t n; struct rt_msghdr *rtm; if ((n = read(fd, buf, sizeof(buf))) == -1) err(1, "read(rt_socket)"); if (n == 0) errx(1, "EOF on rt_socket"); rtm = (struct rt_msghdr *)(void *)buf; if ((size_t)n != rtm->rtm_msglen) errx(1, "n=%zu, rtm_msglen=%u", (size_t)n, rtm->rtm_msglen); handle_rtmsg(rtm); } /* * execute and SIOCAIFADDR */ static int siocaifaddr(char *ifname, struct in_addr addr, struct in_addr mask, struct in_addr bcast) { struct ifaliasreq addreq; struct sockaddr_in *sa; memset(&addreq, 0, sizeof(addreq)); strlcpy(addreq.ifra_name, ifname, sizeof(addreq.ifra_name)); sa = (struct sockaddr_in *)(void *)&addreq.ifra_addr; sa->sin_family = AF_INET; sa->sin_len = sizeof(*sa); sa->sin_addr = addr; sa = (struct sockaddr_in *)(void *)&addreq.ifra_mask; sa->sin_family = AF_INET; sa->sin_len = sizeof(*sa); sa->sin_addr = mask; sa = (struct sockaddr_in *)(void *)&addreq.ifra_broadaddr; sa->sin_family = AF_INET; sa->sin_len = sizeof(*sa); sa->sin_addr = bcast; return (ioctl(mib_netsock, SIOCAIFADDR, &addreq)); } /* * Exececute a SIOCDIFADDR */ static int siocdifaddr(const char *ifname, struct in_addr addr) { struct ifreq delreq; struct sockaddr_in *sa; memset(&delreq, 0, sizeof(delreq)); strlcpy(delreq.ifr_name, ifname, sizeof(delreq.ifr_name)); sa = (struct sockaddr_in *)(void *)&delreq.ifr_addr; sa->sin_family = AF_INET; sa->sin_len = sizeof(*sa); sa->sin_addr = addr; return (ioctl(mib_netsock, SIOCDIFADDR, &delreq)); } /* * Verify an interface address without fetching the entire list */ static int verify_ifa(const char *name, struct mibifa *ifa) { struct ifreq req; struct sockaddr_in *sa; memset(&req, 0, sizeof(req)); strlcpy(req.ifr_name, name, sizeof(req.ifr_name)); sa = (struct sockaddr_in *)(void *)&req.ifr_addr; sa->sin_family = AF_INET; sa->sin_len = sizeof(*sa); sa->sin_addr = ifa->inaddr; if (ioctl(mib_netsock, SIOCGIFADDR, &req) == -1) return (-1); if (ifa->inaddr.s_addr != sa->sin_addr.s_addr) { syslog(LOG_ERR, "%s: address mismatch", __func__); return (-1); } if (ioctl(mib_netsock, SIOCGIFNETMASK, &req) == -1) return (-1); if (ifa->inmask.s_addr != sa->sin_addr.s_addr) { syslog(LOG_ERR, "%s: netmask mismatch", __func__); return (-1); } return (0); } /* * Restore a deleted interface address. Don't wait for the routing socket * to update us. */ void mib_undestroy_ifa(struct mibifa *ifa) { struct mibif *ifp; if ((ifp = mib_find_if(ifa->ifindex)) == NULL) /* keep it destroyed */ return; if (siocaifaddr(ifp->name, ifa->inaddr, ifa->inmask, ifa->inbcast)) /* keep it destroyed */ return; ifa->flags &= ~MIBIFA_DESTROYED; } /* * Destroy an interface address */ int mib_destroy_ifa(struct mibifa *ifa) { struct mibif *ifp; if ((ifp = mib_find_if(ifa->ifindex)) == NULL) { /* ups. */ mib_iflist_bad = 1; return (-1); } if (siocdifaddr(ifp->name, ifa->inaddr)) { /* ups. */ syslog(LOG_ERR, "SIOCDIFADDR: %m"); mib_iflist_bad = 1; return (-1); } ifa->flags |= MIBIFA_DESTROYED; return (0); } /* * Rollback the modification of an address. Don't bother to wait for * the routing socket. */ void mib_unmodify_ifa(struct mibifa *ifa) { struct mibif *ifp; if ((ifp = mib_find_if(ifa->ifindex)) == NULL) { /* ups. */ mib_iflist_bad = 1; return; } if (siocaifaddr(ifp->name, ifa->inaddr, ifa->inmask, ifa->inbcast)) { /* ups. */ mib_iflist_bad = 1; return; } } /* * Modify an IFA. */ int mib_modify_ifa(struct mibifa *ifa) { struct mibif *ifp; if ((ifp = mib_find_if(ifa->ifindex)) == NULL) { /* ups. */ mib_iflist_bad = 1; return (-1); } if (siocaifaddr(ifp->name, ifa->inaddr, ifa->inmask, ifa->inbcast)) { /* ups. */ mib_iflist_bad = 1; return (-1); } if (verify_ifa(ifp->name, ifa)) { /* ups. */ mib_iflist_bad = 1; return (-1); } return (0); } /* * Destroy a freshly created interface address. Don't bother to wait for * the routing socket. */ void mib_uncreate_ifa(struct mibifa *ifa) { struct mibif *ifp; if ((ifp = mib_find_if(ifa->ifindex)) == NULL) { /* ups. */ mib_iflist_bad = 1; return; } if (siocdifaddr(ifp->name, ifa->inaddr)) { /* ups. */ mib_iflist_bad = 1; return; } destroy_ifa(ifa); } /* * Create a new ifa and verify it */ struct mibifa * mib_create_ifa(u_int ifindex, struct in_addr addr, struct in_addr mask, struct in_addr bcast) { struct mibif *ifp; struct mibifa *ifa; if ((ifp = mib_find_if(ifindex)) == NULL) return (NULL); if ((ifa = alloc_ifa(ifindex, addr)) == NULL) return (NULL); ifa->inmask = mask; ifa->inbcast = bcast; if (siocaifaddr(ifp->name, ifa->inaddr, ifa->inmask, ifa->inbcast)) { syslog(LOG_ERR, "%s: %m", __func__); destroy_ifa(ifa); return (NULL); } if (verify_ifa(ifp->name, ifa)) { destroy_ifa(ifa); return (NULL); } return (ifa); } /* * Get all cloning interfaces and make them dynamic. * Hah! Whe should probably do this on a periodic basis (XXX). */ static void get_cloners(void) { struct if_clonereq req; char *buf, *cp; int i; memset(&req, 0, sizeof(req)); if (ioctl(mib_netsock, SIOCIFGCLONERS, &req) == -1) { syslog(LOG_ERR, "get cloners: %m"); return; } if ((buf = malloc(req.ifcr_total * IFNAMSIZ)) == NULL) { syslog(LOG_ERR, "%m"); return; } req.ifcr_count = req.ifcr_total; req.ifcr_buffer = buf; if (ioctl(mib_netsock, SIOCIFGCLONERS, &req) == -1) { syslog(LOG_ERR, "get cloners: %m"); free(buf); return; } for (cp = buf, i = 0; i < req.ifcr_total; i++, cp += IFNAMSIZ) mib_if_set_dyn(cp); free(buf); } /* * Idle function */ static void mibII_idle(void *arg __unused) { struct mibifa *ifa; if (mib_iflist_bad) { TAILQ_FOREACH(ifa, &mibifa_list, link) ifa->flags &= ~MIBIFA_DESTROYED; /* assume, that all cloning interfaces are dynamic */ get_cloners(); mib_refresh_iflist(); update_ifa_info(); mib_arp_update(); mib_iflist_bad = 0; } mib_arp_update(); } /* * Start the module */ static void mibII_start(void) { if ((route_fd = fd_select(route, route_input, NULL, module)) == NULL) { syslog(LOG_ERR, "fd_select(route): %m"); return; } mib_refresh_iflist(); update_ifa_info(); mib_arp_update(); (void)mib_fetch_route(); mib_iftable_last_change = 0; mib_ifstack_last_change = 0; ifmib_reg = or_register(&oid_ifMIB, "The MIB module to describe generic objects for network interface" " sub-layers.", module); ipmib_reg = or_register(&oid_ipMIB, "The MIB module for managing IP and ICMP implementations, but " "excluding their management of IP routes.", module); tcpmib_reg = or_register(&oid_tcpMIB, "The MIB module for managing TCP implementations.", module); udpmib_reg = or_register(&oid_udpMIB, "The MIB module for managing UDP implementations.", module); ipForward_reg = or_register(&oid_ipForward, "The MIB module for the display of CIDR multipath IP Routes.", module); mibII_poll_timer = NULL; mibII_poll_ticks = MIBII_POLL_TICKS; mibif_restart_mibII_poll_timer(); } /* * Initialize the module */ static int mibII_init(struct lmodule *mod, int argc __unused, char *argv[] __unused) { size_t len; module = mod; len = sizeof(clockinfo); if (sysctlbyname("kern.clockrate", &clockinfo, &len, NULL, 0) == -1) { syslog(LOG_ERR, "kern.clockrate: %m"); return (-1); } if (len != sizeof(clockinfo)) { syslog(LOG_ERR, "kern.clockrate: wrong size"); return (-1); } if ((route = socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC)) == -1) { syslog(LOG_ERR, "PF_ROUTE: %m"); return (-1); } if ((mib_netsock = socket(PF_INET, SOCK_DGRAM, 0)) == -1) { syslog(LOG_ERR, "PF_INET: %m"); (void)close(route); return (-1); } (void)shutdown(mib_netsock, SHUT_RDWR); /* assume, that all cloning interfaces are dynamic */ get_cloners(); return (0); } static int mibII_fini(void) { if (mibII_poll_timer != NULL ) { timer_stop(mibII_poll_timer); mibII_poll_timer = NULL; } if (route_fd != NULL) fd_deselect(route_fd); if (route != -1) (void)close(route); if (mib_netsock != -1) (void)close(mib_netsock); /* XXX free memory */ or_unregister(ipForward_reg); or_unregister(udpmib_reg); or_unregister(tcpmib_reg); or_unregister(ipmib_reg); or_unregister(ifmib_reg); return (0); } static void mibII_loading(const struct lmodule *mod, int loaded) { struct mibif *ifp; if (loaded == 1) return; TAILQ_FOREACH(ifp, &mibif_list, link) if (ifp->xnotify_mod == mod) { ifp->xnotify_mod = NULL; ifp->xnotify_data = NULL; ifp->xnotify = NULL; } mib_unregister_newif(mod); } +extern const struct snmp_module config; const struct snmp_module config = { "This module implements the interface and ip groups.", mibII_init, mibII_fini, NULL, /* idle */ NULL, /* dump */ NULL, /* config */ mibII_start, NULL, mibII_ctree, mibII_CTREE_SIZE, mibII_loading }; /* * Should have a list of these attached to each interface. */ void * mibif_notify(struct mibif *ifp, const struct lmodule *mod, mibif_notify_f func, void *data) { ifp->xnotify = func; ifp->xnotify_data = data; ifp->xnotify_mod = mod; return (ifp); } void mibif_unnotify(void *arg) { struct mibif *ifp = arg; ifp->xnotify = NULL; ifp->xnotify_data = NULL; ifp->xnotify_mod = NULL; } Index: head/contrib/bsnmp/snmp_mibII/mibII_route.c =================================================================== --- head/contrib/bsnmp/snmp_mibII/mibII_route.c (revision 335884) +++ head/contrib/bsnmp/snmp_mibII/mibII_route.c (revision 335885) @@ -1,514 +1,514 @@ /* * Copyright (c) 2001-2003 * Fraunhofer Institute for Open Communication Systems (FhG Fokus). * All rights reserved. * * Author: Harti Brandt * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Begemot: bsnmp/snmp_mibII/mibII_route.c,v 1.9 2005/10/06 07:15:00 brandt_h Exp $ * * Routing table */ #ifdef HAVE_SYS_TREE_H #include #else #include "tree.h" #endif #include "mibII.h" #include "mibII_oid.h" struct sroute { RB_ENTRY(sroute) link; uint32_t ifindex; uint8_t index[13]; uint8_t type; uint8_t proto; }; -RB_HEAD(sroutes, sroute) sroutes = RB_INITIALIZER(&sroutes); +static RB_HEAD(sroutes, sroute) sroutes = RB_INITIALIZER(&sroutes); RB_PROTOTYPE(sroutes, sroute, link, sroute_compare); #define ROUTE_UPDATE_INTERVAL (100 * 60 * 10) /* 10 min */ static uint64_t route_tick; static u_int route_total; /* * Compare two routes */ static int sroute_compare(struct sroute *s1, struct sroute *s2) { return (memcmp(s1->index, s2->index, 13)); } static void sroute_index_append(struct asn_oid *oid, u_int sub, const struct sroute *s) { int i; oid->len = sub + 13; for (i = 0; i < 13; i++) oid->subs[sub + i] = s->index[i]; } #if 0 static void sroute_print(const struct sroute *r) { u_int i; for (i = 0; i < 13 - 1; i++) printf("%u.", r->index[i]); printf("%u proto=%u type=%u", r->index[i], r->proto, r->type); } #endif /* * process routing message */ void mib_sroute_process(struct rt_msghdr *rtm, struct sockaddr *gw, struct sockaddr *dst, struct sockaddr *mask) { struct sockaddr_in *in_dst, *in_gw; struct in_addr in_mask; struct mibif *ifp; struct sroute key; struct sroute *r, *r1; in_addr_t ha; if (dst == NULL || gw == NULL || dst->sa_family != AF_INET || gw->sa_family != AF_INET) return; in_dst = (struct sockaddr_in *)(void *)dst; in_gw = (struct sockaddr_in *)(void *)gw; if (rtm->rtm_flags & RTF_HOST) in_mask.s_addr = 0xffffffff; else if (mask == NULL || mask->sa_len == 0) in_mask.s_addr = 0; else in_mask = ((struct sockaddr_in *)(void *)mask)->sin_addr; /* build the index */ ha = ntohl(in_dst->sin_addr.s_addr); key.index[0] = (ha >> 24) & 0xff; key.index[1] = (ha >> 16) & 0xff; key.index[2] = (ha >> 8) & 0xff; key.index[3] = (ha >> 0) & 0xff; ha = ntohl(in_mask.s_addr); key.index[4] = (ha >> 24) & 0xff; key.index[5] = (ha >> 16) & 0xff; key.index[6] = (ha >> 8) & 0xff; key.index[7] = (ha >> 0) & 0xff; /* ToS */ key.index[8] = 0; ha = ntohl(in_gw->sin_addr.s_addr); key.index[9] = (ha >> 24) & 0xff; key.index[10] = (ha >> 16) & 0xff; key.index[11] = (ha >> 8) & 0xff; key.index[12] = (ha >> 0) & 0xff; if (rtm->rtm_type == RTM_DELETE) { r = RB_FIND(sroutes, &sroutes, &key); if (r == 0) { #ifdef DEBUG_ROUTE syslog(LOG_WARNING, "%s: DELETE: %u.%u.%u.%u " "%u.%u.%u.%u %u %u.%u.%u.%u not found", __func__, key.index[0], key.index[1], key.index[2], key.index[3], key.index[4], key.index[5], key.index[6], key.index[7], key.index[8], key.index[9], key.index[10], key.index[11], key.index[12]); #endif return; } RB_REMOVE(sroutes, &sroutes, r); free(r); route_total--; #ifdef DEBUG_ROUTE printf("%s: DELETE: %u.%u.%u.%u " "%u.%u.%u.%u %u %u.%u.%u.%u\n", __func__, key.index[0], key.index[1], key.index[2], key.index[3], key.index[4], key.index[5], key.index[6], key.index[7], key.index[8], key.index[9], key.index[10], key.index[11], key.index[12]); #endif return; } /* GET or ADD */ ifp = NULL; if ((ifp = mib_find_if_sys(rtm->rtm_index)) == NULL) { if (rtm->rtm_type == RTM_ADD) { /* make it a get so the kernel fills the index */ mib_send_rtmsg(rtm, gw, dst, mask); return; } mib_iflist_bad = 1; } if ((r = malloc(sizeof(*r))) == NULL) { syslog(LOG_ERR, "%m"); return; } memcpy(r->index, key.index, sizeof(r->index)); r->ifindex = (ifp == NULL) ? 0 : ifp->index; r->type = (rtm->rtm_flags & RTF_REJECT) ? 2 : 4; /* cannot really know, what protocol it runs */ r->proto = (rtm->rtm_flags & RTF_LOCAL) ? 2 : (rtm->rtm_flags & RTF_STATIC) ? 3 : (rtm->rtm_flags & RTF_DYNAMIC) ? 4 : 10; r1 = RB_INSERT(sroutes, &sroutes, r); if (r1 != NULL) { #ifdef DEBUG_ROUTE syslog(LOG_WARNING, "%s: %u.%u.%u.%u " "%u.%u.%u.%u %u %u.%u.%u.%u duplicate route", __func__, key.index[0], key.index[1], key.index[2], key.index[3], key.index[4], key.index[5], key.index[6], key.index[7], key.index[8], key.index[9], key.index[10], key.index[11], key.index[12]); #endif r1->ifindex = r->ifindex; r1->type = r->type; r1->proto = r->proto; free(r); return; } route_total++; #ifdef DEBUG_ROUTE printf("%s: ADD/GET: %u.%u.%u.%u " "%u.%u.%u.%u %u %u.%u.%u.%u\n", __func__, key.index[0], key.index[1], key.index[2], key.index[3], key.index[4], key.index[5], key.index[6], key.index[7], key.index[8], key.index[9], key.index[10], key.index[11], key.index[12]); #endif } int mib_fetch_route(void) { u_char *rtab, *next; size_t len; struct sroute *r, *r1; struct rt_msghdr *rtm; struct sockaddr *addrs[RTAX_MAX]; if (route_tick != 0 && route_tick + ROUTE_UPDATE_INTERVAL > this_tick) return (0); /* * Remove all routes */ r = RB_MIN(sroutes, &sroutes); while (r != NULL) { r1 = RB_NEXT(sroutes, &sroutes, r); RB_REMOVE(sroutes, &sroutes, r); free(r); r = r1; } route_total = 0; if ((rtab = mib_fetch_rtab(AF_INET, NET_RT_DUMP, 0, &len)) == NULL) return (-1); next = rtab; for (next = rtab; next < rtab + len; next += rtm->rtm_msglen) { rtm = (struct rt_msghdr *)(void *)next; if (rtm->rtm_type != RTM_GET || !(rtm->rtm_flags & RTF_UP)) continue; mib_extract_addrs(rtm->rtm_addrs, (u_char *)(rtm + 1), addrs); mib_sroute_process(rtm, addrs[RTAX_GATEWAY], addrs[RTAX_DST], addrs[RTAX_NETMASK]); } #if 0 u_int n = 0; r = RB_MIN(sroutes, &sroutes); while (r != NULL) { printf("%u: ", n++); sroute_print(r); printf("\n"); r = RB_NEXT(sroutes, &sroutes, r); } #endif free(rtab); route_tick = get_ticks(); return (0); } /** * Find a route in the table. */ static struct sroute * sroute_get(const struct asn_oid *oid, u_int sub) { struct sroute key; int i; if (oid->len - sub != 13) return (NULL); for (i = 0; i < 13; i++) key.index[i] = oid->subs[sub + i]; return (RB_FIND(sroutes, &sroutes, &key)); } /** * Find next route in the table. There is no such RB_ macro, so must * dig into the innards of the RB stuff. */ static struct sroute * sroute_getnext(struct asn_oid *oid, u_int sub) { u_int i; int comp; struct sroute key; struct sroute *best; struct sroute *s; /* * We now, that the OID is at least the tableEntry OID. If it is, * the user wants the first route. */ if (oid->len == sub) return (RB_MIN(sroutes, &sroutes)); /* * This is also true for any index that consists of zeros and is * shorter than the full index. */ if (oid->len < sub + 13) { for (i = sub; i < oid->len; i++) if (oid->subs[i] != 0) break; if (i == oid->len) return (RB_MIN(sroutes, &sroutes)); /* * Now if the index is too short, we fill it with zeros and then * subtract one from the index. We can do this, because we now, * that there is at least one index element that is not zero. */ for (i = oid->len; i < sub + 13; i++) oid->subs[i] = 0; for (i = sub + 13 - 1; i >= sub; i--) { if (oid->subs[i] != 0) { oid->subs[i]--; break; } oid->subs[i] = ASN_MAXID; } oid->len = sub + 13; } /* build the index */ for (i = sub; i < sub + 13; i++) key.index[i - sub] = oid->subs[i]; /* now find the element */ best = NULL; s = RB_ROOT(&sroutes); while (s != NULL) { comp = sroute_compare(&key, s); if (comp >= 0) { /* The current element is smaller than what we search. * Forget about it and move to the right subtree. */ s = RB_RIGHT(s, link); continue; } /* the current element is larger than what we search. * forget about the right subtree (its even larger), but * the current element may be what we need. */ if (best == NULL || sroute_compare(s, best) < 0) /* this one's better */ best = s; s = RB_LEFT(s, link); } return (best); } /* * Table */ int op_route_table(struct snmp_context *ctx __unused, struct snmp_value *value, u_int sub, u_int iidx __unused, enum snmp_op op) { struct sroute *r; if (mib_fetch_route() == -1) return (SNMP_ERR_GENERR); switch (op) { case SNMP_OP_GETNEXT: if ((r = sroute_getnext(&value->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); sroute_index_append(&value->var, sub, r); break; case SNMP_OP_GET: if ((r = sroute_get(&value->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_SET: if ((r = sroute_get(&value->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); return (SNMP_ERR_NOT_WRITEABLE); case SNMP_OP_ROLLBACK: case SNMP_OP_COMMIT: abort(); default: abort(); } switch (value->var.subs[sub - 1]) { case LEAF_ipCidrRouteDest: value->v.ipaddress[0] = r->index[0]; value->v.ipaddress[1] = r->index[1]; value->v.ipaddress[2] = r->index[2]; value->v.ipaddress[3] = r->index[3]; break; case LEAF_ipCidrRouteMask: value->v.ipaddress[0] = r->index[4]; value->v.ipaddress[1] = r->index[5]; value->v.ipaddress[2] = r->index[6]; value->v.ipaddress[3] = r->index[7]; break; case LEAF_ipCidrRouteTos: value->v.integer = r->index[8]; break; case LEAF_ipCidrRouteNextHop: value->v.ipaddress[0] = r->index[9]; value->v.ipaddress[1] = r->index[10]; value->v.ipaddress[2] = r->index[11]; value->v.ipaddress[3] = r->index[12]; break; case LEAF_ipCidrRouteIfIndex: value->v.integer = r->ifindex; break; case LEAF_ipCidrRouteType: value->v.integer = r->type; break; case LEAF_ipCidrRouteProto: value->v.integer = r->proto; break; case LEAF_ipCidrRouteAge: value->v.integer = 0; break; case LEAF_ipCidrRouteInfo: value->v.oid = oid_zeroDotZero; break; case LEAF_ipCidrRouteNextHopAS: value->v.integer = 0; break; case LEAF_ipCidrRouteMetric1: case LEAF_ipCidrRouteMetric2: case LEAF_ipCidrRouteMetric3: case LEAF_ipCidrRouteMetric4: case LEAF_ipCidrRouteMetric5: value->v.integer = -1; break; case LEAF_ipCidrRouteStatus: value->v.integer = 1; break; } return (SNMP_ERR_NOERROR); } /* * scalars */ int op_route(struct snmp_context *ctx __unused, struct snmp_value *value, u_int sub, u_int iidx __unused, enum snmp_op op) { switch (op) { case SNMP_OP_GETNEXT: abort(); case SNMP_OP_GET: break; case SNMP_OP_SET: return (SNMP_ERR_NOT_WRITEABLE); case SNMP_OP_ROLLBACK: case SNMP_OP_COMMIT: abort(); } if (mib_fetch_route() == -1) return (SNMP_ERR_GENERR); switch (value->var.subs[sub - 1]) { case LEAF_ipCidrRouteNumber: value->v.uint32 = route_total; break; } return (SNMP_ERR_NOERROR); } RB_GENERATE(sroutes, sroute, link, sroute_compare); Index: head/contrib/bsnmp/snmp_ntp/snmp_ntp.c =================================================================== --- head/contrib/bsnmp/snmp_ntp/snmp_ntp.c (revision 335884) +++ head/contrib/bsnmp/snmp_ntp/snmp_ntp.c (revision 335885) @@ -1,1598 +1,1600 @@ /* - * Copyright (c) 2005 + * Copyright (c) 2005,2018 * Hartmut Brandt. * All rights reserved. * * Author: Harti Brandt * * Redistribution of this software and documentation and use in source and * binary forms, with or without modification, are permitted provided that * the following conditions are met: * * 1. Redistributions of source code or documentation must retain the above * copyright notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY FRAUNHOFER FOKUS * AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * FRAUNHOFER FOKUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $Begemot: bsnmp/snmp_ntp/snmp_ntp.c,v 1.9 2005/10/06 07:15:01 brandt_h Exp $ * * NTP interface for SNMPd. */ #include #include #include #include #include #include #include #include #ifdef HAVE_STDINT_H #include #elif defined(HAVE_INTTYPES_H) #include #endif #include #include #include #include #include #include "support.h" #include "snmpmod.h" + +#define SNMPTREE_TYPES #include "ntp_tree.h" #include "ntp_oid.h" #define NTPC_MAX 576 #define NTPC_VERSION 3 #define NTPC_MODE 6 #define NTPC_DMAX 468 #define NTPC_BIT_RESP 0x80 #define NTPC_BIT_ERROR 0x40 #define NTPC_BIT_MORE 0x20 #define NTPC_OPMASK 0x1f #define NTPC_OP_READSTAT 1 #define NTPC_OP_READVAR 2 /* our module handle */ static struct lmodule *module; /* debug flag */ static uint32_t ntp_debug; #define DBG_DUMP_PKTS 0x01 #define DBG_DUMP_VARS 0x02 /* OIDs */ static const struct asn_oid oid_ntpMIB = OIDX_ntpMIB; /* the Object Resource registration index */ static u_int reg_index; /* last time we've fetch the system variables */ static uint64_t sysinfo_tick; /* cached system variables */ static int32_t sys_leap; static int sysb_leap; static int32_t sys_stratum; static int sysb_stratum; static int32_t sys_precision; static int sysb_precision; static char *sys_rootdelay; static char *sys_rootdispersion; static char *sys_refid; static char sys_reftime[8]; static int sysb_reftime; static int32_t sys_poll; static int sysb_poll; static uint32_t sys_peer; static int sysb_peer; static u_char sys_clock[8]; static int sysb_clock; static char *sys_system; static char *sys_processor; static int sysb_jitter; static double sys_jitter; static int sysb_stability; static double sys_stability; /* last time we've fetch the peer list */ static uint64_t peers_tick; /* request sequence number generator */ static uint16_t seqno; /* NTPD socket */ static int ntpd_sock; static void *ntpd_fd; struct peer { /* required entries for macros */ uint32_t index; TAILQ_ENTRY(peer) link; int32_t config; /* config bit */ u_char srcadr[4]; /* PeerAddress */ uint32_t srcport; /* PeerPort */ u_char dstadr[4]; /* HostAddress */ uint32_t dstport; /* HostPort */ int32_t leap; /* Leap */ int32_t hmode; /* Mode */ int32_t stratum; /* Stratum */ int32_t ppoll; /* PeerPoll */ int32_t hpoll; /* HostPoll */ int32_t precision; /* Precision */ char *rootdelay; /* RootDelay */ char *rootdispersion;/* RootDispersion */ char *refid; /* RefId */ u_char reftime[8]; /* RefTime */ u_char orgtime[8]; /* OrgTime */ u_char rcvtime[8]; /* ReceiveTime */ u_char xmttime[8]; /* TransmitTime */ u_int32_t reach; /* Reach */ int32_t timer; /* Timer */ char *offset; /* Offset */ char *delay; /* Delay */ char *dispersion; /* Dispersion */ int32_t filt_entries; }; TAILQ_HEAD(peer_list, peer); /* list of peers */ static struct peer_list peers = TAILQ_HEAD_INITIALIZER(peers); struct filt { /* required fields */ struct asn_oid index; TAILQ_ENTRY(filt) link; char *offset; char *delay; char *dispersion; }; TAILQ_HEAD(filt_list, filt); /* list of filters */ static struct filt_list filts = TAILQ_HEAD_INITIALIZER(filts); /* configuration */ static u_char *ntp_host; static u_char *ntp_port; static uint32_t ntp_timeout; static void ntpd_input(int, void *); static int open_socket(void); /* the initialization function */ static int ntp_init(struct lmodule *mod, int argc, char *argv[] __unused) { module = mod; if (argc != 0) { syslog(LOG_ERR, "bad number of arguments for %s", __func__); return (EINVAL); } ntp_host = strdup("localhost"); ntp_port = strdup("ntp"); ntp_timeout = 50; /* 0.5sec */ return (0); } /* * Module is started */ static void ntp_start(void) { if (open_socket() != -1) { ntpd_fd = fd_select(ntpd_sock, ntpd_input, NULL, module); if (ntpd_fd == NULL) { syslog(LOG_ERR, "fd_select failed on ntpd socket: %m"); return; } } reg_index = or_register(&oid_ntpMIB, "The MIB for NTP.", module); } /* * Called, when the module is to be unloaded after it was successfully loaded */ static int ntp_fini(void) { or_unregister(reg_index); fd_deselect(ntpd_fd); return (0); } const struct snmp_module config = { .comment = "This module implements the NTP MIB", .init = ntp_init, .start = ntp_start, .fini = ntp_fini, .tree = ntp_ctree, .tree_size = ntp_CTREE_SIZE, }; /* * Open the NTPD socket */ static int open_socket(void) { struct addrinfo hints, *res, *res0; int error; const char *cause; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET; hints.ai_socktype = SOCK_DGRAM; error = getaddrinfo(ntp_host, ntp_port, &hints, &res0); if (error) { syslog(LOG_ERR, "%s(%s): %s", ntp_host, ntp_port, gai_strerror(error)); return (-1); } ntpd_sock = -1; cause = "no address"; errno = EADDRNOTAVAIL; for (res = res0; res != NULL; res = res->ai_next) { ntpd_sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (ntpd_sock == -1) { cause = "socket"; continue; } if (connect(ntpd_sock, res->ai_addr, res->ai_addrlen) == -1) { cause = "connect"; (void)close(ntpd_sock); ntpd_sock = -1; continue; } break; } if (ntpd_sock == -1) { syslog(LOG_ERR, "%s: %m", cause); return (-1); } freeaddrinfo(res0); return (0); } /* * Dump a packet */ static void dump_packet(const u_char *pkt, size_t ret) { char buf[8 * 3 + 1]; size_t i, j; for (i = 0; i < ret; i += 8) { buf[0] = '\0'; for (j = 0; i + j < (size_t)ret && j < 8; j++) sprintf(buf + strlen(buf), " %02x", pkt[i + j]); syslog(LOG_INFO, "%04zu:%s", i, buf); } } /* * Execute an NTP request. */ static int ntpd_request(u_int op, u_int associd, const char *vars) { u_char *rpkt; u_char *ptr; size_t vlen; ssize_t ret; if ((rpkt = malloc(NTPC_MAX)) == NULL) { syslog(LOG_ERR, "%m"); return (-1); } memset(rpkt, 0, NTPC_MAX); ptr = rpkt; *ptr++ = (NTPC_VERSION << 3) | NTPC_MODE; *ptr++ = op; if (++seqno == 0) seqno++; *ptr++ = seqno >> 8; *ptr++ = seqno; /* skip status */ ptr += 2; *ptr++ = associd >> 8; *ptr++ = associd; /* skip offset */ ptr += 2; if (vars != NULL) { vlen = strlen(vars); if (vlen > NTPC_DMAX) { syslog(LOG_ERR, "NTP request too long (%zu)", vlen); free(rpkt); return (-1); } *ptr++ = vlen >> 8; *ptr++ = vlen; memcpy(ptr, vars, vlen); ptr += vlen; } else /* skip data length (is already zero) */ ptr += 2; while ((ptr - rpkt) % 4 != 0) *ptr++ = 0; if (ntp_debug & DBG_DUMP_PKTS) { syslog(LOG_INFO, "sending %zd bytes", ptr - rpkt); dump_packet(rpkt, ptr - rpkt); } ret = send(ntpd_sock, rpkt, ptr - rpkt, 0); if (ret == -1) { syslog(LOG_ERR, "cannot send to ntpd: %m"); free(rpkt); return (-1); } return (0); } /* * Callback if packet arrived from NTPD */ static int ntpd_read(uint16_t *op, uint16_t *associd, u_char **data, size_t *datalen) { u_char pkt[NTPC_MAX + 1]; u_char *ptr, *nptr; u_int n; ssize_t ret; size_t z; u_int offset; /* current offset */ int more; /* more flag */ int sel; struct timeval inc, end, rem; fd_set iset; *datalen = 0; *data = NULL; offset = 0; inc.tv_sec = ntp_timeout / 100; inc.tv_usec = (ntp_timeout % 100) * 1000; (void)gettimeofday(&end, NULL); timeradd(&end, &inc, &end); next: /* compute remaining time */ (void)gettimeofday(&rem, NULL); if (timercmp(&rem, &end, >=)) { /* do a poll */ rem.tv_sec = 0; rem.tv_usec = 0; } else { timersub(&end, &rem, &rem); } /* select */ FD_ZERO(&iset); FD_SET(ntpd_sock, &iset); sel = select(ntpd_sock + 1, &iset, NULL, NULL, &rem); if (sel == -1) { if (errno == EINTR) goto next; syslog(LOG_ERR, "select ntpd_sock: %m"); free(*data); return (-1); } if (sel == 0) { syslog(LOG_ERR, "timeout on NTP connection"); free(*data); return (-1); } /* now read it */ ret = recv(ntpd_sock, pkt, sizeof(pkt), 0); if (ret == -1) { syslog(LOG_ERR, "error reading from ntpd: %m"); free(*data); return (-1); } if (ntp_debug & DBG_DUMP_PKTS) { syslog(LOG_INFO, "got %zd bytes", ret); dump_packet(pkt, (size_t)ret); } ptr = pkt; if ((*ptr & 0x3f) != ((NTPC_VERSION << 3) | NTPC_MODE)) { syslog(LOG_ERR, "unexpected packet version 0x%x", *ptr); free(*data); return (-1); } ptr++; if (!(*ptr & NTPC_BIT_RESP)) { syslog(LOG_ERR, "not a response packet"); return (-1); } if (*ptr & NTPC_BIT_ERROR) { z = *datalen - 12; if (z > NTPC_DMAX) z = NTPC_DMAX; syslog(LOG_ERR, "error response: %.*s", (int)z, pkt + 12); free(*data); return (-1); } more = (*ptr & NTPC_BIT_MORE); *op = *ptr++ & NTPC_OPMASK; /* seqno */ n = *ptr++ << 8; n |= *ptr++; if (n != seqno) { syslog(LOG_ERR, "expecting seqno %u, got %u", seqno, n); free(*data); return (-1); } /* status */ n = *ptr++ << 8; n |= *ptr++; /* associd */ *associd = *ptr++ << 8; *associd |= *ptr++; /* offset */ n = *ptr++ << 8; n |= *ptr++; if (n != offset) { syslog(LOG_ERR, "offset: expecting %u, got %u", offset, n); free(*data); return (-1); } /* count */ n = *ptr++ << 8; n |= *ptr++; if ((size_t)ret < 12 + n) { syslog(LOG_ERR, "packet too short"); return (-1); } nptr = realloc(*data, *datalen + n); if (nptr == NULL) { syslog(LOG_ERR, "cannot allocate memory: %m"); free(*data); return (-1); } *data = nptr; memcpy(*data + offset, ptr, n); *datalen += n; if (!more) return (0); offset += n; goto next; } /* * Send a request and wait for the response */ static int ntpd_dialog(u_int op, u_int associd, const char *vars, u_char **data, size_t *datalen) { uint16_t rassocid; uint16_t rop; if (ntpd_request(op, associd, vars) == -1) return (-1); if (ntpd_read(&rop, &rassocid, data, datalen) == -1) return (-1); if (rop != op) { syslog(LOG_ERR, "bad response op 0x%x", rop); free(data); return (-1); } if (associd != rassocid) { syslog(LOG_ERR, "response for wrong associd"); free(data); return (-1); } return (0); } /* * Callback if packet arrived from NTPD */ static void ntpd_input(int fd __unused, void *arg __unused) { uint16_t associd; uint16_t op; u_char *data; size_t datalen; if (ntpd_read(&op, &associd, &data, &datalen) == -1) return; free(data); } /* * Find the value of a variable */ static int ntpd_parse(u_char **data, size_t *datalen, char **namep, char **valp) { u_char *ptr = *data; u_char *end = ptr + *datalen; char *ptr1; char endc; /* skip leading spaces */ while (ptr < end && isspace((int)*ptr)) ptr++; if (ptr == end) return (0); *namep = ptr; /* skip to space or '=' or ','*/ while (ptr < end && !isspace((int)*ptr) && *ptr != '=' && *ptr != ',') ptr++; endc = *ptr; *ptr++ = '\0'; /* skip space */ while (ptr < end && isspace((int)*ptr)) ptr++; if (ptr == end || endc == ',') { /* no value */ *valp = NULL; *datalen -= ptr - *data; *data = ptr; return (1); } if (*ptr == '"') { /* quoted */ ptr++; *valp = ptr; while (ptr < end && *ptr != '"') ptr++; if (ptr == end) return (0); *ptr++ = '\0'; /* find comma */ while (ptr < end && isspace((int)*ptr) && *ptr == ',') ptr++; } else { *valp = ptr; /* skip to end of value */ while (ptr < end && *ptr != ',') ptr++; /* remove trailing blanks */ for (ptr1 = ptr; ptr1 > *valp; ptr1--) if (!isspace((int)ptr1[-1])) break; *ptr1 = '\0'; if (ptr < end) ptr++; } *datalen -= ptr - *data; *data = ptr; return (1); } /* * Parse an int32 value */ static int val_parse_int32(const char *val, int32_t *p, int32_t min, int32_t max, int base) { long n; char *end; errno = 0; n = strtol(val, &end, base); if (errno != 0 || *end != '\0') return (0); if (n < min || n > max) return (0); *p = (int32_t)n; return (1); } /* * Parse an uint32 value */ static int val_parse_uint32(const char *val, uint32_t *p, uint32_t min, uint32_t max, int base) { u_long n; char *end; errno = 0; n = strtoul(val, &end, base); if (errno != 0 || *end != '\0') return (0); if (n < min || n > max) return (0); *p = (uint32_t)n; return (1); } /* * Parse a double */ static int val_parse_double(const char *val, double *p) { char *end; errno = 0; *p = strtod(val, &end); if (errno != 0 || *end != '\0') return (0); return (1); } static int val_parse_ts(const char *val, char *buf) { int r, n; u_int i, f; if (strlen(val) > 2 && val[0] == '0' && val[1] == 'x') { /* hex format */ r = sscanf(val + 2, "%x.%x%n", &i, &f, &n); if (r != 2 || (size_t)n != strlen(val + 2)) return (0); } else { /* probably decimal */ r = sscanf(val, "%d.%d%n", &i, &f, &n); if (r != 2 || (size_t)n != strlen(val)) return (0); } buf[0] = i >> 24; buf[1] = i >> 16; buf[2] = i >> 8; buf[3] = i >> 0; buf[4] = f >> 24; buf[5] = f >> 16; buf[6] = f >> 8; buf[7] = f >> 0; return (1); } /* * Parse an IP address. This resolves non-numeric names. */ static int val_parse_ip(const char *val, u_char ip[4]) { int r, n, error; struct addrinfo hints, *res0; struct sockaddr_in *sin_local; r = sscanf(val, "%hhd.%hhd.%hhd.%hhd%n", &ip[0], &ip[1], &ip[2], &ip[3], &n); if (n == 4 && (size_t)n == strlen(val)) return (0); memset(ip, 0, 4); memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET; hints.ai_socktype = SOCK_DGRAM; error = getaddrinfo(val, NULL, &hints, &res0); if (error) { syslog(LOG_ERR, "%s: %s", val, gai_strerror(error)); return (-1); } if (res0 == NULL) { syslog(LOG_ERR, "%s: no address", val); return (-1); } sin_local = (struct sockaddr_in *)(void *)res0->ai_addr; ip[3] = sin_local->sin_addr.s_addr >> 24; ip[2] = sin_local->sin_addr.s_addr >> 16; ip[1] = sin_local->sin_addr.s_addr >> 8; ip[0] = sin_local->sin_addr.s_addr >> 0; freeaddrinfo(res0); return (0); } /* * Fetch system info */ static int fetch_sysinfo(void) { u_char *data; u_char *ptr; size_t datalen; char *name; char *val; if (ntpd_dialog(NTPC_OP_READVAR, 0, "leap,stratum,precision,rootdelay,rootdispersion,refid,reftime," "poll,peer,clock,system,processor,jitter,stability", &data, &datalen)) return (-1); /* clear info */ sysb_leap = 0; sysb_stratum = 0; sysb_precision = 0; free(sys_rootdelay); sys_rootdelay = NULL; free(sys_rootdispersion); sys_rootdispersion = NULL; free(sys_refid); sys_refid = NULL; sysb_reftime = 0; sysb_poll = 0; sysb_peer = 0; sysb_clock = 0; free(sys_system); sys_system = NULL; free(sys_processor); sys_processor = NULL; sysb_jitter = 0; sysb_stability = 0; ptr = data; while (ntpd_parse(&ptr, &datalen, &name, &val)) { if (ntp_debug & DBG_DUMP_VARS) syslog(LOG_DEBUG, "%s: '%s'='%s'", __func__, name, val); if (strcmp(name, "leap") == 0 || strcmp(name, "sys.leap") == 0) { sysb_leap = val_parse_int32(val, &sys_leap, 0, 3, 2); } else if (strcmp(name, "stratum") == 0 || strcmp(name, "sys.stratum") == 0) { sysb_stratum = val_parse_int32(val, &sys_stratum, 0, 255, 0); } else if (strcmp(name, "precision") == 0 || strcmp(name, "sys.precision") == 0) { sysb_precision = val_parse_int32(val, &sys_precision, INT32_MIN, INT32_MAX, 0); } else if (strcmp(name, "rootdelay") == 0 || strcmp(name, "sys.rootdelay") == 0) { sys_rootdelay = strdup(val); } else if (strcmp(name, "rootdispersion") == 0 || strcmp(name, "sys.rootdispersion") == 0) { sys_rootdispersion = strdup(val); } else if (strcmp(name, "refid") == 0 || strcmp(name, "sys.refid") == 0) { sys_refid = strdup(val); } else if (strcmp(name, "reftime") == 0 || strcmp(name, "sys.reftime") == 0) { sysb_reftime = val_parse_ts(val, sys_reftime); } else if (strcmp(name, "poll") == 0 || strcmp(name, "sys.poll") == 0) { sysb_poll = val_parse_int32(val, &sys_poll, INT32_MIN, INT32_MAX, 0); } else if (strcmp(name, "peer") == 0 || strcmp(name, "sys.peer") == 0) { sysb_peer = val_parse_uint32(val, &sys_peer, 0, UINT32_MAX, 0); } else if (strcmp(name, "clock") == 0 || strcmp(name, "sys.clock") == 0) { sysb_clock = val_parse_ts(val, sys_clock); } else if (strcmp(name, "system") == 0 || strcmp(name, "sys.system") == 0) { sys_system = strdup(val); } else if (strcmp(name, "processor") == 0 || strcmp(name, "sys.processor") == 0) { sys_processor = strdup(val); } else if (strcmp(name, "jitter") == 0 || strcmp(name, "sys.jitter") == 0) { sysb_jitter = val_parse_double(val, &sys_jitter); } else if (strcmp(name, "stability") == 0 || strcmp(name, "sys.stability") == 0) { sysb_stability = val_parse_double(val, &sys_stability); } } free(data); return (0); } static int parse_filt(char *val, uint16_t associd, int which) { char *w; int cnt; struct filt *f; cnt = 0; for (w = strtok(val, " \t"); w != NULL; w = strtok(NULL, " \t")) { TAILQ_FOREACH(f, &filts, link) if (f->index.subs[0] == associd && f->index.subs[1] == (asn_subid_t)(cnt + 1)) break; if (f == NULL) { f = malloc(sizeof(*f)); memset(f, 0, sizeof(*f)); f->index.len = 2; f->index.subs[0] = associd; f->index.subs[1] = cnt + 1; INSERT_OBJECT_OID(f, &filts); } switch (which) { case 0: f->offset = strdup(w); break; case 1: f->delay = strdup(w); break; case 2: f->dispersion = strdup(w); break; default: abort(); } cnt++; } return (cnt); } /* * Fetch the complete peer list */ static int fetch_peers(void) { u_char *data, *pdata, *ptr; size_t datalen, pdatalen; int i; struct peer *p; struct filt *f; uint16_t associd; char *name, *val; /* free the old list */ while ((p = TAILQ_FIRST(&peers)) != NULL) { TAILQ_REMOVE(&peers, p, link); free(p->rootdelay); free(p->rootdispersion); free(p->refid); free(p->offset); free(p->delay); free(p->dispersion); free(p); } while ((f = TAILQ_FIRST(&filts)) != NULL) { TAILQ_REMOVE(&filts, f, link); free(f->offset); free(f->delay); free(f->dispersion); free(f); } /* fetch the list of associations */ if (ntpd_dialog(NTPC_OP_READSTAT, 0, NULL, &data, &datalen)) return (-1); for (i = 0; i < (int)(datalen / 4); i++) { associd = data[4 * i + 0] << 8; associd |= data[4 * i + 1] << 0; /* ask for the association variables */ if (ntpd_dialog(NTPC_OP_READVAR, associd, "config,srcadr,srcport,dstadr,dstport,leap,hmode,stratum," "hpoll,ppoll,precision,rootdelay,rootdispersion,refid," "reftime,org,rec,xmt,reach,timer,offset,delay,dispersion," "filtdelay,filtoffset,filtdisp", &pdata, &pdatalen)) { free(data); return (-1); } /* now save and parse the data */ p = malloc(sizeof(*p)); if (p == NULL) { free(data); syslog(LOG_ERR, "%m"); return (-1); } memset(p, 0, sizeof(*p)); p->index = associd; INSERT_OBJECT_INT(p, &peers); ptr = pdata; while (ntpd_parse(&ptr, &pdatalen, &name, &val)) { if (ntp_debug & DBG_DUMP_VARS) syslog(LOG_DEBUG, "%s: '%s'='%s'", __func__, name, val); if (strcmp(name, "config") == 0 || strcmp(name, "peer.config") == 0) { val_parse_int32(val, &p->config, 0, 1, 0); } else if (strcmp(name, "srcadr") == 0 || strcmp(name, "peer.srcadr") == 0) { val_parse_ip(val, p->srcadr); } else if (strcmp(name, "srcport") == 0 || strcmp(name, "peer.srcport") == 0) { val_parse_uint32(val, &p->srcport, 1, 65535, 0); } else if (strcmp(name, "dstadr") == 0 || strcmp(name, "peer.dstadr") == 0) { val_parse_ip(val, p->dstadr); } else if (strcmp(name, "dstport") == 0 || strcmp(name, "peer.dstport") == 0) { val_parse_uint32(val, &p->dstport, 1, 65535, 0); } else if (strcmp(name, "leap") == 0 || strcmp(name, "peer.leap") == 0) { val_parse_int32(val, &p->leap, 0, 3, 2); } else if (strcmp(name, "hmode") == 0 || strcmp(name, "peer.hmode") == 0) { val_parse_int32(val, &p->hmode, 0, 7, 0); } else if (strcmp(name, "stratum") == 0 || strcmp(name, "peer.stratum") == 0) { val_parse_int32(val, &p->stratum, 0, 255, 0); } else if (strcmp(name, "ppoll") == 0 || strcmp(name, "peer.ppoll") == 0) { val_parse_int32(val, &p->ppoll, INT32_MIN, INT32_MAX, 0); } else if (strcmp(name, "hpoll") == 0 || strcmp(name, "peer.hpoll") == 0) { val_parse_int32(val, &p->hpoll, INT32_MIN, INT32_MAX, 0); } else if (strcmp(name, "precision") == 0 || strcmp(name, "peer.precision") == 0) { val_parse_int32(val, &p->hpoll, INT32_MIN, INT32_MAX, 0); } else if (strcmp(name, "rootdelay") == 0 || strcmp(name, "peer.rootdelay") == 0) { p->rootdelay = strdup(val); } else if (strcmp(name, "rootdispersion") == 0 || strcmp(name, "peer.rootdispersion") == 0) { p->rootdispersion = strdup(val); } else if (strcmp(name, "refid") == 0 || strcmp(name, "peer.refid") == 0) { p->refid = strdup(val); } else if (strcmp(name, "reftime") == 0 || strcmp(name, "sys.reftime") == 0) { val_parse_ts(val, p->reftime); } else if (strcmp(name, "org") == 0 || strcmp(name, "sys.org") == 0) { val_parse_ts(val, p->orgtime); } else if (strcmp(name, "rec") == 0 || strcmp(name, "sys.rec") == 0) { val_parse_ts(val, p->rcvtime); } else if (strcmp(name, "xmt") == 0 || strcmp(name, "sys.xmt") == 0) { val_parse_ts(val, p->xmttime); } else if (strcmp(name, "reach") == 0 || strcmp(name, "peer.reach") == 0) { val_parse_uint32(val, &p->reach, 0, 65535, 0); } else if (strcmp(name, "timer") == 0 || strcmp(name, "peer.timer") == 0) { val_parse_int32(val, &p->timer, INT32_MIN, INT32_MAX, 0); } else if (strcmp(name, "offset") == 0 || strcmp(name, "peer.offset") == 0) { p->offset = strdup(val); } else if (strcmp(name, "delay") == 0 || strcmp(name, "peer.delay") == 0) { p->delay = strdup(val); } else if (strcmp(name, "dispersion") == 0 || strcmp(name, "peer.dispersion") == 0) { p->dispersion = strdup(val); } else if (strcmp(name, "filtdelay") == 0 || strcmp(name, "peer.filtdelay") == 0) { p->filt_entries = parse_filt(val, associd, 0); } else if (strcmp(name, "filtoffset") == 0 || strcmp(name, "peer.filtoffset") == 0) { p->filt_entries = parse_filt(val, associd, 1); } else if (strcmp(name, "filtdisp") == 0 || strcmp(name, "peer.filtdisp") == 0) { p->filt_entries = parse_filt(val, associd, 2); } } free(pdata); } free(data); return (0); } /* * System variables - read-only scalars only. */ int op_ntpSystem(struct snmp_context *ctx __unused, struct snmp_value *value, u_int sub, u_int iidx __unused, enum snmp_op op) { asn_subid_t which = value->var.subs[sub - 1]; switch (op) { case SNMP_OP_GETNEXT: abort(); case SNMP_OP_GET: if (this_tick > sysinfo_tick) { if (fetch_sysinfo() == -1) return (SNMP_ERR_GENERR); sysinfo_tick = this_tick; } switch (which) { case LEAF_ntpSysLeap: if (!sysb_leap) return (SNMP_ERR_NOSUCHNAME); value->v.integer = sys_leap; break; case LEAF_ntpSysStratum: if (!sysb_stratum) return (SNMP_ERR_NOSUCHNAME); value->v.integer = sys_stratum; break; case LEAF_ntpSysPrecision: if (!sysb_precision) return (SNMP_ERR_NOSUCHNAME); value->v.integer = sys_precision; break; case LEAF_ntpSysRootDelay: if (sys_rootdelay == NULL) return (SNMP_ERR_NOSUCHNAME); return (string_get(value, sys_rootdelay, -1)); case LEAF_ntpSysRootDispersion: if (sys_rootdispersion == NULL) return (SNMP_ERR_NOSUCHNAME); return (string_get(value, sys_rootdispersion, -1)); case LEAF_ntpSysRefId: if (sys_refid == NULL) return (SNMP_ERR_NOSUCHNAME); return (string_get(value, sys_refid, -1)); case LEAF_ntpSysRefTime: if (sysb_reftime == 0) return (SNMP_ERR_NOSUCHNAME); return (string_get(value, sys_reftime, 8)); case LEAF_ntpSysPoll: if (sysb_poll == 0) return (SNMP_ERR_NOSUCHNAME); value->v.integer = sys_poll; break; case LEAF_ntpSysPeer: if (sysb_peer == 0) return (SNMP_ERR_NOSUCHNAME); value->v.uint32 = sys_peer; break; case LEAF_ntpSysClock: if (sysb_clock == 0) return (SNMP_ERR_NOSUCHNAME); return (string_get(value, sys_clock, 8)); case LEAF_ntpSysSystem: if (sys_system == NULL) return (SNMP_ERR_NOSUCHNAME); return (string_get(value, sys_system, -1)); case LEAF_ntpSysProcessor: if (sys_processor == NULL) return (SNMP_ERR_NOSUCHNAME); return (string_get(value, sys_processor, -1)); default: abort(); } return (SNMP_ERR_NOERROR); case SNMP_OP_SET: return (SNMP_ERR_NOT_WRITEABLE); case SNMP_OP_COMMIT: case SNMP_OP_ROLLBACK: abort(); } abort(); } int op_ntpPeersVarTable(struct snmp_context *ctx __unused, struct snmp_value *value, u_int sub, u_int iidx, enum snmp_op op) { asn_subid_t which = value->var.subs[sub - 1]; uint32_t peer; struct peer *t; if (this_tick > peers_tick) { if (fetch_peers() == -1) return (SNMP_ERR_GENERR); peers_tick = this_tick; } switch (op) { case SNMP_OP_GETNEXT: t = NEXT_OBJECT_INT(&peers, &value->var, sub); if (t == NULL) return (SNMP_ERR_NOSUCHNAME); value->var.len = sub + 1; value->var.subs[sub] = t->index; break; case SNMP_OP_GET: t = FIND_OBJECT_INT(&peers, &value->var, sub); if (t == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_SET: if (index_decode(&value->var, sub, iidx, &peer)) return (SNMP_ERR_NO_CREATION); t = FIND_OBJECT_INT(&peers, &value->var, sub); if (t != NULL) return (SNMP_ERR_NOT_WRITEABLE); return (SNMP_ERR_NO_CREATION); case SNMP_OP_COMMIT: case SNMP_OP_ROLLBACK: default: abort(); } /* * Come here for GET and COMMIT */ switch (which) { case LEAF_ntpPeersConfigured: value->v.integer = t->config; break; case LEAF_ntpPeersPeerAddress: return (ip_get(value, t->srcadr)); case LEAF_ntpPeersPeerPort: value->v.uint32 = t->srcport; break; case LEAF_ntpPeersHostAddress: return (ip_get(value, t->dstadr)); case LEAF_ntpPeersHostPort: value->v.uint32 = t->dstport; break; case LEAF_ntpPeersLeap: value->v.integer = t->leap; break; case LEAF_ntpPeersMode: value->v.integer = t->hmode; break; case LEAF_ntpPeersStratum: value->v.integer = t->stratum; break; case LEAF_ntpPeersPeerPoll: value->v.integer = t->ppoll; break; case LEAF_ntpPeersHostPoll: value->v.integer = t->hpoll; break; case LEAF_ntpPeersPrecision: value->v.integer = t->precision; break; case LEAF_ntpPeersRootDelay: return (string_get(value, t->rootdelay, -1)); case LEAF_ntpPeersRootDispersion: return (string_get(value, t->rootdispersion, -1)); case LEAF_ntpPeersRefId: return (string_get(value, t->refid, -1)); case LEAF_ntpPeersRefTime: return (string_get(value, t->reftime, 8)); case LEAF_ntpPeersOrgTime: return (string_get(value, t->orgtime, 8)); case LEAF_ntpPeersReceiveTime: return (string_get(value, t->rcvtime, 8)); case LEAF_ntpPeersTransmitTime: return (string_get(value, t->xmttime, 8)); case LEAF_ntpPeersReach: value->v.uint32 = t->reach; break; case LEAF_ntpPeersTimer: value->v.uint32 = t->timer; break; case LEAF_ntpPeersOffset: return (string_get(value, t->offset, -1)); case LEAF_ntpPeersDelay: return (string_get(value, t->delay, -1)); case LEAF_ntpPeersDispersion: return (string_get(value, t->dispersion, -1)); default: abort(); } return (SNMP_ERR_NOERROR); } int op_ntpFilterPeersVarTable(struct snmp_context *ctx __unused, struct snmp_value *value, u_int sub, u_int iidx, enum snmp_op op) { asn_subid_t which = value->var.subs[sub - 1]; uint32_t peer; struct peer *t; if (this_tick > peers_tick) { if (fetch_peers() == -1) return (SNMP_ERR_GENERR); peers_tick = this_tick; } switch (op) { case SNMP_OP_GETNEXT: t = NEXT_OBJECT_INT(&peers, &value->var, sub); if (t == NULL) return (SNMP_ERR_NOSUCHNAME); value->var.len = sub + 1; value->var.subs[sub] = t->index; break; case SNMP_OP_GET: t = FIND_OBJECT_INT(&peers, &value->var, sub); if (t == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_SET: if (index_decode(&value->var, sub, iidx, &peer)) return (SNMP_ERR_NO_CREATION); t = FIND_OBJECT_INT(&peers, &value->var, sub); if (t != NULL) return (SNMP_ERR_NOT_WRITEABLE); return (SNMP_ERR_NO_CREATION); case SNMP_OP_COMMIT: case SNMP_OP_ROLLBACK: default: abort(); } /* * Come here for GET and COMMIT */ switch (which) { case LEAF_ntpFilterValidEntries: value->v.integer = t->filt_entries; break; default: abort(); } return (SNMP_ERR_NOERROR); } int op_ntpFilterRegisterTable(struct snmp_context *ctx __unused, struct snmp_value *value __unused, u_int sub __unused, u_int iidx __unused, enum snmp_op op __unused) { asn_subid_t which = value->var.subs[sub - 1]; uint32_t peer; uint32_t filt; struct filt *t; if (this_tick > peers_tick) { if (fetch_peers() == -1) return (SNMP_ERR_GENERR); peers_tick = this_tick; } switch (op) { case SNMP_OP_GETNEXT: t = NEXT_OBJECT_OID(&filts, &value->var, sub); if (t == NULL) return (SNMP_ERR_NOSUCHNAME); index_append(&value->var, sub, &t->index); break; case SNMP_OP_GET: t = FIND_OBJECT_OID(&filts, &value->var, sub); if (t == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_SET: if (index_decode(&value->var, sub, iidx, &peer, &filt)) return (SNMP_ERR_NO_CREATION); t = FIND_OBJECT_OID(&filts, &value->var, sub); if (t != NULL) return (SNMP_ERR_NOT_WRITEABLE); return (SNMP_ERR_NO_CREATION); case SNMP_OP_COMMIT: case SNMP_OP_ROLLBACK: default: abort(); } /* * Come here for GET and COMMIT */ switch (which) { case LEAF_ntpFilterPeersOffset: return (string_get(value, t->offset, -1)); case LEAF_ntpFilterPeersDelay: return (string_get(value, t->delay, -1)); case LEAF_ntpFilterPeersDispersion: return (string_get(value, t->dispersion, -1)); default: abort(); } return (SNMP_ERR_NOERROR); } /* * System variables - read-only scalars only. */ int op_begemot_ntp(struct snmp_context *ctx __unused, struct snmp_value *value, u_int sub, u_int iidx __unused, enum snmp_op op) { asn_subid_t which = value->var.subs[sub - 1]; int ret; switch (op) { case SNMP_OP_GETNEXT: abort(); case SNMP_OP_GET: switch (which) { case LEAF_begemotNtpHost: return (string_get(value, ntp_host, -1)); case LEAF_begemotNtpPort: return (string_get(value, ntp_port, -1)); case LEAF_begemotNtpTimeout: value->v.uint32 = ntp_timeout; return (SNMP_ERR_NOERROR); case LEAF_begemotNtpDebug: value->v.uint32 = ntp_debug; return (SNMP_ERR_NOERROR); case LEAF_begemotNtpJitter: if (this_tick > sysinfo_tick) { if (fetch_sysinfo() == -1) return (SNMP_ERR_GENERR); sysinfo_tick = this_tick; } if (!sysb_jitter) return (SNMP_ERR_NOSUCHNAME); value->v.counter64 = sys_jitter / 1000 * (1ULL << 32); return (SNMP_ERR_NOERROR); case LEAF_begemotNtpStability: if (this_tick > sysinfo_tick) { if (fetch_sysinfo() == -1) return (SNMP_ERR_GENERR); sysinfo_tick = this_tick; } if (!sysb_stability) return (SNMP_ERR_NOSUCHNAME); value->v.counter64 = sys_stability * (1ULL << 32); return (SNMP_ERR_NOERROR); } abort(); case SNMP_OP_SET: switch (which) { case LEAF_begemotNtpHost: /* only at initialization */ if (community != COMM_INITIALIZE) return (SNMP_ERR_NOT_WRITEABLE); if ((ret = string_save(value, ctx, -1, &ntp_host)) != SNMP_ERR_NOERROR) return (ret); return (SNMP_ERR_NOERROR); case LEAF_begemotNtpPort: /* only at initialization */ if (community != COMM_INITIALIZE) return (SNMP_ERR_NOT_WRITEABLE); if ((ret = string_save(value, ctx, -1, &ntp_port)) != SNMP_ERR_NOERROR) return (ret); return (SNMP_ERR_NOERROR); case LEAF_begemotNtpTimeout: ctx->scratch->int1 = ntp_timeout; if (value->v.uint32 < 1) return (SNMP_ERR_WRONG_VALUE); ntp_timeout = value->v.integer; return (SNMP_ERR_NOERROR); case LEAF_begemotNtpDebug: ctx->scratch->int1 = ntp_debug; ntp_debug = value->v.integer; return (SNMP_ERR_NOERROR); } abort(); case SNMP_OP_ROLLBACK: switch (which) { case LEAF_begemotNtpHost: string_rollback(ctx, &ntp_host); return (SNMP_ERR_NOERROR); case LEAF_begemotNtpPort: string_rollback(ctx, &ntp_port); return (SNMP_ERR_NOERROR); case LEAF_begemotNtpTimeout: ntp_timeout = ctx->scratch->int1; return (SNMP_ERR_NOERROR); case LEAF_begemotNtpDebug: ntp_debug = ctx->scratch->int1; return (SNMP_ERR_NOERROR); } abort(); case SNMP_OP_COMMIT: switch (which) { case LEAF_begemotNtpHost: string_commit(ctx); return (SNMP_ERR_NOERROR); case LEAF_begemotNtpPort: string_commit(ctx); return (SNMP_ERR_NOERROR); case LEAF_begemotNtpTimeout: case LEAF_begemotNtpDebug: return (SNMP_ERR_NOERROR); } abort(); } abort(); } Index: head/contrib/bsnmp/snmp_target/target_snmp.c =================================================================== --- head/contrib/bsnmp/snmp_target/target_snmp.c (revision 335884) +++ head/contrib/bsnmp/snmp_target/target_snmp.c (revision 335885) @@ -1,840 +1,842 @@ /*- - * Copyright (c) 2010 The FreeBSD Foundation + * Copyright (c) 2010,2018 The FreeBSD Foundation * All rights reserved. * * This software was developed by Shteryana Sotirova Shopova under * sponsorship from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include "asn1.h" #include "snmp.h" #include "snmpmod.h" +#define SNMPTREE_TYPES #include "target_tree.h" #include "target_oid.h" static struct lmodule *target_module; /* For the registration. */ static const struct asn_oid oid_target = OIDX_snmpTargetMIB; static const struct asn_oid oid_notification = OIDX_snmpNotificationMIB; static uint reg_target; static uint reg_notification; static int32_t target_lock; static const struct asn_oid oid_udp_domain = OIDX_snmpUDPDomain; /* * Internal datastructures and forward declarations. */ static void target_append_index(struct asn_oid *, uint, const char *); static int target_decode_index(const struct asn_oid *, uint, char *); static struct target_address *target_get_address(const struct asn_oid *, uint); static struct target_address *target_get_next_address(const struct asn_oid *, uint); static struct target_param *target_get_param(const struct asn_oid *, uint); static struct target_param *target_get_next_param(const struct asn_oid *, uint); static struct target_notify *target_get_notify(const struct asn_oid *, uint); static struct target_notify *target_get_next_notify(const struct asn_oid *, uint); int op_snmp_target(struct snmp_context *ctx __unused, struct snmp_value *val, uint32_t sub, uint32_t iidx __unused, enum snmp_op op) { struct snmpd_target_stats *ctx_stats; if (val->var.subs[sub - 1] == LEAF_snmpTargetSpinLock) { switch (op) { case SNMP_OP_GET: if (++target_lock == INT32_MAX) target_lock = 0; val->v.integer = target_lock; break; case SNMP_OP_GETNEXT: abort(); case SNMP_OP_SET: if (val->v.integer != target_lock) return (SNMP_ERR_INCONS_VALUE); break; case SNMP_OP_ROLLBACK: /* FALLTHROUGH */ case SNMP_OP_COMMIT: break; } return (SNMP_ERR_NOERROR); } else if (op == SNMP_OP_SET) return (SNMP_ERR_NOT_WRITEABLE); if ((ctx_stats = bsnmpd_get_target_stats()) == NULL) return (SNMP_ERR_GENERR); if (op == SNMP_OP_GET) { switch (val->var.subs[sub - 1]) { case LEAF_snmpUnavailableContexts: val->v.uint32 = ctx_stats->unavail_contexts; break; case LEAF_snmpUnknownContexts: val->v.uint32 = ctx_stats->unknown_contexts; break; default: return (SNMP_ERR_NOSUCHNAME); } return (SNMP_ERR_NOERROR); } abort(); } int op_snmp_target_addrs(struct snmp_context *ctx __unused, struct snmp_value *val, uint32_t sub, uint32_t iidx __unused, enum snmp_op op) { char aname[SNMP_ADM_STR32_SIZ]; struct target_address *addrs; switch (op) { case SNMP_OP_GET: if ((addrs = target_get_address(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_GETNEXT: if ((addrs = target_get_next_address(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); target_append_index(&val->var, sub, addrs->name); break; case SNMP_OP_SET: if ((addrs = target_get_address(&val->var, sub)) == NULL && (val->var.subs[sub - 1] != LEAF_snmpTargetAddrRowStatus || val->v.integer != RowStatus_createAndWait)) return (SNMP_ERR_NOSUCHNAME); if (addrs != NULL) { if (community != COMM_INITIALIZE && addrs->type == StorageType_readOnly) return (SNMP_ERR_NOT_WRITEABLE); if (addrs->status == RowStatus_active && val->v.integer != RowStatus_destroy) return (SNMP_ERR_INCONS_VALUE); } switch (val->var.subs[sub - 1]) { case LEAF_snmpTargetAddrTDomain: return (SNMP_ERR_INCONS_VALUE); case LEAF_snmpTargetAddrTAddress: if (val->v.octetstring.len != SNMP_UDP_ADDR_SIZ) return (SNMP_ERR_INCONS_VALUE); ctx->scratch->ptr1 = malloc(SNMP_UDP_ADDR_SIZ); if (ctx->scratch->ptr1 == NULL) return (SNMP_ERR_GENERR); memcpy(ctx->scratch->ptr1, addrs->address, SNMP_UDP_ADDR_SIZ); memcpy(addrs->address, val->v.octetstring.octets, SNMP_UDP_ADDR_SIZ); break; case LEAF_snmpTargetAddrTagList: if (val->v.octetstring.len >= SNMP_TAG_SIZ) return (SNMP_ERR_INCONS_VALUE); ctx->scratch->int1 = strlen(addrs->taglist) + 1; ctx->scratch->ptr1 = malloc(ctx->scratch->int1); if (ctx->scratch->ptr1 == NULL) return (SNMP_ERR_GENERR); strlcpy(ctx->scratch->ptr1, addrs->taglist, ctx->scratch->int1); memcpy(addrs->taglist, val->v.octetstring.octets, val->v.octetstring.len); addrs->taglist[val->v.octetstring.len] = '\0'; break; case LEAF_snmpTargetAddrParams: if (val->v.octetstring.len >= SNMP_ADM_STR32_SIZ) return (SNMP_ERR_INCONS_VALUE); ctx->scratch->int1 = strlen(addrs->paramname) + 1; ctx->scratch->ptr1 = malloc(ctx->scratch->int1); if (ctx->scratch->ptr1 == NULL) return (SNMP_ERR_GENERR); strlcpy(ctx->scratch->ptr1, addrs->paramname, ctx->scratch->int1); memcpy(addrs->paramname, val->v.octetstring.octets, val->v.octetstring.len); addrs->paramname[val->v.octetstring.len] = '\0'; break; case LEAF_snmpTargetAddrRetryCount: ctx->scratch->int1 = addrs->retry; addrs->retry = val->v.integer; break; case LEAF_snmpTargetAddrTimeout: ctx->scratch->int1 = addrs->timeout; addrs->timeout = val->v.integer / 10; break; case LEAF_snmpTargetAddrStorageType: return (SNMP_ERR_INCONS_VALUE); case LEAF_snmpTargetAddrRowStatus: if (addrs != NULL) { if (val->v.integer != RowStatus_active && val->v.integer != RowStatus_destroy) return (SNMP_ERR_INCONS_VALUE); if (val->v.integer == RowStatus_active && (addrs->address[0] == 0 || strlen(addrs->taglist) == 0 || strlen(addrs->paramname) == 0)) return (SNMP_ERR_INCONS_VALUE); ctx->scratch->int1 = addrs->status; addrs->status = val->v.integer; return (SNMP_ERR_NOERROR); } if (val->v.integer != RowStatus_createAndWait || target_decode_index(&val->var, sub, aname) < 0) return (SNMP_ERR_INCONS_VALUE); if ((addrs = target_new_address(aname)) == NULL) return (SNMP_ERR_GENERR); addrs->status = RowStatus_destroy; if (community != COMM_INITIALIZE) addrs->type = StorageType_volatile; else addrs->type = StorageType_readOnly; break; } return (SNMP_ERR_NOERROR); case SNMP_OP_COMMIT: switch (val->var.subs[sub - 1]) { case LEAF_snmpTargetAddrTAddress: case LEAF_snmpTargetAddrTagList: case LEAF_snmpTargetAddrParams: free(ctx->scratch->ptr1); break; case LEAF_snmpTargetAddrRowStatus: if ((addrs = target_get_address(&val->var, sub)) == NULL) return (SNMP_ERR_GENERR); if (val->v.integer == RowStatus_destroy) return (target_delete_address(addrs)); else if (val->v.integer == RowStatus_active) return (target_activate_address(addrs)); break; default: break; } return (SNMP_ERR_NOERROR); case SNMP_OP_ROLLBACK: if ((addrs = target_get_address(&val->var, sub)) == NULL) return (SNMP_ERR_GENERR); switch (val->var.subs[sub - 1]) { case LEAF_snmpTargetAddrTAddress: memcpy(addrs->address, ctx->scratch->ptr1, SNMP_UDP_ADDR_SIZ); free(ctx->scratch->ptr1); break; case LEAF_snmpTargetAddrTagList: strlcpy(addrs->taglist, ctx->scratch->ptr1, ctx->scratch->int1); free(ctx->scratch->ptr1); break; case LEAF_snmpTargetAddrParams: strlcpy(addrs->paramname, ctx->scratch->ptr1, ctx->scratch->int1); free(ctx->scratch->ptr1); break; case LEAF_snmpTargetAddrRetryCount: addrs->retry = ctx->scratch->int1; break; case LEAF_snmpTargetAddrTimeout: addrs->timeout = ctx->scratch->int1; break; case LEAF_snmpTargetAddrRowStatus: if (ctx->scratch->int1 == RowStatus_destroy) return (target_delete_address(addrs)); break; default: break; } return (SNMP_ERR_NOERROR); default: abort(); } switch (val->var.subs[sub - 1]) { case LEAF_snmpTargetAddrTDomain: return (oid_get(val, &oid_udp_domain)); case LEAF_snmpTargetAddrTAddress: return (string_get(val, addrs->address, SNMP_UDP_ADDR_SIZ)); case LEAF_snmpTargetAddrTimeout: val->v.integer = addrs->timeout; break; case LEAF_snmpTargetAddrRetryCount: val->v.integer = addrs->retry; break; case LEAF_snmpTargetAddrTagList: return (string_get(val, addrs->taglist, -1)); case LEAF_snmpTargetAddrParams: return (string_get(val, addrs->paramname, -1)); case LEAF_snmpTargetAddrStorageType: val->v.integer = addrs->type; break; case LEAF_snmpTargetAddrRowStatus: val->v.integer = addrs->status; break; default: abort(); } return (SNMP_ERR_NOERROR); } int op_snmp_target_params(struct snmp_context *ctx __unused, struct snmp_value *val, uint32_t sub, uint32_t iidx __unused, enum snmp_op op) { char pname[SNMP_ADM_STR32_SIZ]; struct target_param *param; switch (op) { case SNMP_OP_GET: if ((param = target_get_param(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_GETNEXT: if ((param = target_get_next_param(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); target_append_index(&val->var, sub, param->name); break; case SNMP_OP_SET: if ((param = target_get_param(&val->var, sub)) == NULL && (val->var.subs[sub - 1] != LEAF_snmpTargetParamsRowStatus || val->v.integer != RowStatus_createAndWait)) return (SNMP_ERR_NOSUCHNAME); if (param != NULL) { if (community != COMM_INITIALIZE && param->type == StorageType_readOnly) return (SNMP_ERR_NOT_WRITEABLE); if (param->status == RowStatus_active && val->v.integer != RowStatus_destroy) return (SNMP_ERR_INCONS_VALUE); } switch (val->var.subs[sub - 1]) { case LEAF_snmpTargetParamsMPModel: if (val->v.integer != SNMP_MPM_SNMP_V1 && val->v.integer != SNMP_MPM_SNMP_V2c && val->v.integer != SNMP_MPM_SNMP_V3) return (SNMP_ERR_INCONS_VALUE); ctx->scratch->int1 = param->mpmodel; param->mpmodel = val->v.integer; break; case LEAF_snmpTargetParamsSecurityModel: if (val->v.integer != SNMP_SECMODEL_SNMPv1 && val->v.integer != SNMP_SECMODEL_SNMPv2c && val->v.integer != SNMP_SECMODEL_USM) return (SNMP_ERR_INCONS_VALUE); ctx->scratch->int1 = param->sec_model; param->sec_model = val->v.integer; break; case LEAF_snmpTargetParamsSecurityName: if (val->v.octetstring.len >= SNMP_ADM_STR32_SIZ) return (SNMP_ERR_INCONS_VALUE); ctx->scratch->int1 = strlen(param->secname) + 1; ctx->scratch->ptr1 = malloc(ctx->scratch->int1); if (ctx->scratch->ptr1 == NULL) return (SNMP_ERR_GENERR); strlcpy(ctx->scratch->ptr1, param->secname, ctx->scratch->int1); memcpy(param->secname, val->v.octetstring.octets, val->v.octetstring.len); param->secname[val->v.octetstring.len] = '\0'; break; case LEAF_snmpTargetParamsSecurityLevel: if (val->v.integer != SNMP_noAuthNoPriv && val->v.integer != SNMP_authNoPriv && val->v.integer != SNMP_authPriv) return (SNMP_ERR_INCONS_VALUE); ctx->scratch->int1 = param->sec_level; param->sec_level = val->v.integer; break; case LEAF_snmpTargetParamsStorageType: return (SNMP_ERR_INCONS_VALUE); case LEAF_snmpTargetParamsRowStatus: if (param != NULL) { if (val->v.integer != RowStatus_active && val->v.integer != RowStatus_destroy) return (SNMP_ERR_INCONS_VALUE); if (val->v.integer == RowStatus_active && (param->sec_model == 0 || param->sec_level == 0 || strlen(param->secname) == 0)) return (SNMP_ERR_INCONS_VALUE); ctx->scratch->int1 = param->status; param->status = val->v.integer; return (SNMP_ERR_NOERROR); } if (val->v.integer != RowStatus_createAndWait || target_decode_index(&val->var, sub, pname) < 0) return (SNMP_ERR_INCONS_VALUE); if ((param = target_new_param(pname)) == NULL) return (SNMP_ERR_GENERR); param->status = RowStatus_destroy; if (community != COMM_INITIALIZE) param->type = StorageType_volatile; else param->type = StorageType_readOnly; break; } return (SNMP_ERR_NOERROR); case SNMP_OP_COMMIT: switch (val->var.subs[sub - 1]) { case LEAF_snmpTargetParamsSecurityName: free(ctx->scratch->ptr1); break; case LEAF_snmpTargetParamsRowStatus: if ((param = target_get_param(&val->var, sub)) == NULL) return (SNMP_ERR_GENERR); if (val->v.integer == RowStatus_destroy) return (target_delete_param(param)); break; default: break; } return (SNMP_ERR_NOERROR); case SNMP_OP_ROLLBACK: if ((param = target_get_param(&val->var, sub)) == NULL && (val->var.subs[sub - 1] != LEAF_snmpTargetParamsRowStatus || val->v.integer != RowStatus_createAndWait)) return (SNMP_ERR_GENERR); switch (val->var.subs[sub - 1]) { case LEAF_snmpTargetParamsMPModel: param->mpmodel = ctx->scratch->int1; break; case LEAF_snmpTargetParamsSecurityModel: param->sec_model = ctx->scratch->int1; break; case LEAF_snmpTargetParamsSecurityName: strlcpy(param->secname, ctx->scratch->ptr1, sizeof(param->secname)); free(ctx->scratch->ptr1); break; case LEAF_snmpTargetParamsSecurityLevel: param->sec_level = ctx->scratch->int1; break; case LEAF_snmpTargetParamsRowStatus: if (ctx->scratch->int1 == RowStatus_destroy) return (target_delete_param(param)); break; default: break; } return (SNMP_ERR_NOERROR); default: abort(); } switch (val->var.subs[sub - 1]) { case LEAF_snmpTargetParamsMPModel: val->v.integer = param->mpmodel; break; case LEAF_snmpTargetParamsSecurityModel: val->v.integer = param->sec_model; break; case LEAF_snmpTargetParamsSecurityName: return (string_get(val, param->secname, -1)); case LEAF_snmpTargetParamsSecurityLevel: val->v.integer = param->sec_level; break; case LEAF_snmpTargetParamsStorageType: val->v.integer = param->type; break; case LEAF_snmpTargetParamsRowStatus: val->v.integer = param->status; break; default: abort(); } return (SNMP_ERR_NOERROR); } int op_snmp_notify(struct snmp_context *ctx __unused, struct snmp_value *val, uint32_t sub, uint32_t iidx __unused, enum snmp_op op) { char nname[SNMP_ADM_STR32_SIZ]; struct target_notify *notify; switch (op) { case SNMP_OP_GET: if ((notify = target_get_notify(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_GETNEXT: if ((notify = target_get_next_notify(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); target_append_index(&val->var, sub, notify->name); break; case SNMP_OP_SET: if ((notify = target_get_notify(&val->var, sub)) == NULL && (val->var.subs[sub - 1] != LEAF_snmpNotifyRowStatus || val->v.integer != RowStatus_createAndGo)) return (SNMP_ERR_NOSUCHNAME); if (notify != NULL) { if (community != COMM_INITIALIZE && notify->type == StorageType_readOnly) return (SNMP_ERR_NOT_WRITEABLE); } switch (val->var.subs[sub - 1]) { case LEAF_snmpNotifyTag: if (val->v.octetstring.len >= SNMP_TAG_SIZ) return (SNMP_ERR_INCONS_VALUE); ctx->scratch->int1 = strlen(notify->taglist) + 1; ctx->scratch->ptr1 = malloc(ctx->scratch->int1); if (ctx->scratch->ptr1 == NULL) return (SNMP_ERR_GENERR); strlcpy(ctx->scratch->ptr1, notify->taglist, ctx->scratch->int1); memcpy(notify->taglist, val->v.octetstring.octets, val->v.octetstring.len); notify->taglist[val->v.octetstring.len] = '\0'; break; case LEAF_snmpNotifyType: /* FALLTHROUGH */ case LEAF_snmpNotifyStorageType: return (SNMP_ERR_INCONS_VALUE); case LEAF_snmpNotifyRowStatus: if (notify != NULL) { if (val->v.integer != RowStatus_active && val->v.integer != RowStatus_destroy) return (SNMP_ERR_INCONS_VALUE); ctx->scratch->int1 = notify->status; notify->status = val->v.integer; return (SNMP_ERR_NOERROR); } if (val->v.integer != RowStatus_createAndGo || target_decode_index(&val->var, sub, nname) < 0) return (SNMP_ERR_INCONS_VALUE); if ((notify = target_new_notify(nname)) == NULL) return (SNMP_ERR_GENERR); notify->status = RowStatus_destroy; if (community != COMM_INITIALIZE) notify->type = StorageType_volatile; else notify->type = StorageType_readOnly; break; } return (SNMP_ERR_NOERROR); case SNMP_OP_COMMIT: switch (val->var.subs[sub - 1]) { case LEAF_snmpNotifyTag: free(ctx->scratch->ptr1); break; case LEAF_snmpNotifyRowStatus: notify = target_get_notify(&val->var, sub); if (notify == NULL) return (SNMP_ERR_GENERR); if (val->v.integer == RowStatus_destroy) return (target_delete_notify(notify)); else notify->status = RowStatus_active; break; default: break; } return (SNMP_ERR_NOERROR); case SNMP_OP_ROLLBACK: if ((notify = target_get_notify(&val->var, sub)) == NULL) return (SNMP_ERR_GENERR); switch (val->var.subs[sub - 1]) { case LEAF_snmpNotifyTag: strlcpy(notify->taglist, ctx->scratch->ptr1, ctx->scratch->int1); free(ctx->scratch->ptr1); break; case LEAF_snmpNotifyRowStatus: if (ctx->scratch->int1 == RowStatus_destroy) return (target_delete_notify(notify)); break; default: break; } return (SNMP_ERR_NOERROR); default: abort(); } switch (val->var.subs[sub - 1]) { case LEAF_snmpNotifyTag: return (string_get(val, notify->taglist, -1)); case LEAF_snmpNotifyType: val->v.integer = snmpNotifyType_trap; break; case LEAF_snmpNotifyStorageType: val->v.integer = notify->type; break; case LEAF_snmpNotifyRowStatus: val->v.integer = notify->status; break; default: abort(); } return (SNMP_ERR_NOERROR); } static void target_append_index(struct asn_oid *oid, uint sub, const char *name) { uint32_t i; oid->len = sub + strlen(name); for (i = 0; i < strlen(name); i++) oid->subs[sub + i] = name[i]; } static int target_decode_index(const struct asn_oid *oid, uint sub, char *name) { uint32_t i; if (oid->len - sub != oid->subs[sub] + 1 || oid->subs[sub] >= SNMP_ADM_STR32_SIZ) return (-1); for (i = 0; i < oid->subs[sub]; i++) name[i] = oid->subs[sub + i + 1]; name[i] = '\0'; return (0); } static struct target_address * target_get_address(const struct asn_oid *oid, uint sub) { char aname[SNMP_ADM_STR32_SIZ]; struct target_address *addrs; if (target_decode_index(oid, sub, aname) < 0) return (NULL); for (addrs = target_first_address(); addrs != NULL; addrs = target_next_address(addrs)) if (strcmp(aname, addrs->name) == 0) return (addrs); return (NULL); } static struct target_address * target_get_next_address(const struct asn_oid * oid, uint sub) { char aname[SNMP_ADM_STR32_SIZ]; struct target_address *addrs; if (oid->len - sub == 0) return (target_first_address()); if (target_decode_index(oid, sub, aname) < 0) return (NULL); for (addrs = target_first_address(); addrs != NULL; addrs = target_next_address(addrs)) if (strcmp(aname, addrs->name) == 0) return (target_next_address(addrs)); return (NULL); } static struct target_param * target_get_param(const struct asn_oid *oid, uint sub) { char pname[SNMP_ADM_STR32_SIZ]; struct target_param *param; if (target_decode_index(oid, sub, pname) < 0) return (NULL); for (param = target_first_param(); param != NULL; param = target_next_param(param)) if (strcmp(pname, param->name) == 0) return (param); return (NULL); } static struct target_param * target_get_next_param(const struct asn_oid *oid, uint sub) { char pname[SNMP_ADM_STR32_SIZ]; struct target_param *param; if (oid->len - sub == 0) return (target_first_param()); if (target_decode_index(oid, sub, pname) < 0) return (NULL); for (param = target_first_param(); param != NULL; param = target_next_param(param)) if (strcmp(pname, param->name) == 0) return (target_next_param(param)); return (NULL); } static struct target_notify * target_get_notify(const struct asn_oid *oid, uint sub) { char nname[SNMP_ADM_STR32_SIZ]; struct target_notify *notify; if (target_decode_index(oid, sub, nname) < 0) return (NULL); for (notify = target_first_notify(); notify != NULL; notify = target_next_notify(notify)) if (strcmp(nname, notify->name) == 0) return (notify); return (NULL); } static struct target_notify * target_get_next_notify(const struct asn_oid *oid, uint sub) { char nname[SNMP_ADM_STR32_SIZ]; struct target_notify *notify; if (oid->len - sub == 0) return (target_first_notify()); if (target_decode_index(oid, sub, nname) < 0) return (NULL); for (notify = target_first_notify(); notify != NULL; notify = target_next_notify(notify)) if (strcmp(nname, notify->name) == 0) return (target_next_notify(notify)); return (NULL); } static int target_init(struct lmodule *mod, int argc __unused, char *argv[] __unused) { target_module = mod; target_lock = random(); return (0); } static int target_fini(void) { target_flush_all(); or_unregister(reg_target); or_unregister(reg_notification); return (0); } static void target_start(void) { reg_target = or_register(&oid_target, "The MIB module for managing SNMP Management Targets.", target_module); reg_notification = or_register(&oid_notification, "The MIB module for configuring generation of SNMP notifications.", target_module); } static void target_dump(void) { /* XXX: dump the module stats & list of mgmt targets */ } -const char target_comment[] = \ +static const char target_comment[] = \ "This module implements SNMP Management Target MIB Module defined in RFC 3413."; +extern const struct snmp_module config; const struct snmp_module config = { .comment = target_comment, .init = target_init, .fini = target_fini, .start = target_start, .tree = target_ctree, .dump = target_dump, .tree_size = target_CTREE_SIZE, }; Index: head/contrib/bsnmp/snmp_target/target_tree.def =================================================================== --- head/contrib/bsnmp/snmp_target/target_tree.def (revision 335884) +++ head/contrib/bsnmp/snmp_target/target_tree.def (revision 335885) @@ -1,104 +1,87 @@ #- # 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$ # -#include "tc.def" - -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 -) +include "tc.def" (1 internet (6 snmpV2 (1 snmpDomains (1 snmpUDPDomain ) ) (3 snmpModules (12 snmpTargetMIB (1 snmpTargetObjects (1 snmpTargetSpinLock INTEGER op_snmp_target GET SET) (2 snmpTargetAddrTable (1 snmpTargetAddrEntry : OCTETSTRING op_snmp_target_addrs (1 snmpTargetAddrName OCTETSTRING) (2 snmpTargetAddrTDomain OID GET SET) (3 snmpTargetAddrTAddress OCTETSTRING | TAddress GET SET) (4 snmpTargetAddrTimeout INTEGER GET SET) (5 snmpTargetAddrRetryCount INTEGER GET SET) (6 snmpTargetAddrTagList OCTETSTRING | SnmpTagList GET SET) (7 snmpTargetAddrParams OCTETSTRING GET SET) (8 snmpTargetAddrStorageType StorageType GET SET) (9 snmpTargetAddrRowStatus RowStatus GET SET) ) ) (3 snmpTargetParamsTable (1 snmpTargetParamsEntry : OCTETSTRING op_snmp_target_params (1 snmpTargetParamsName OCTETSTRING) (2 snmpTargetParamsMPModel INTEGER GET SET) (3 snmpTargetParamsSecurityModel INTEGER GET SET) (4 snmpTargetParamsSecurityName OCTETSTRING | SnmpAdminString GET SET) (5 snmpTargetParamsSecurityLevel ENUM ( 1 noAuthNoPriv 2 authNoPriv 3 authPriv ) GET SET) (6 snmpTargetParamsStorageType StorageType GET SET) (7 snmpTargetParamsRowStatus RowStatus GET SET) ) ) (4 snmpUnavailableContexts COUNTER op_snmp_target GET) (5 snmpUnknownContexts COUNTER op_snmp_target GET) ) ) (13 snmpNotificationMIB (1 snmpNotifyObjects (1 snmpNotifyTable (1 snmpNotifyEntry : OCTETSTRING op_snmp_notify (1 snmpNotifyName OCTETSTRING) (2 snmpNotifyTag OCTETSTRING | SnmpTagValue GET SET) (3 snmpNotifyType ENUM ( 1 trap 2 inform ) GET SET) (4 snmpNotifyStorageType StorageType GET SET) (5 snmpNotifyRowStatus RowStatus GET SET) ) ) ) ) ) ) ) Index: head/contrib/bsnmp/snmp_usm/usm_snmp.c =================================================================== --- head/contrib/bsnmp/snmp_usm/usm_snmp.c (revision 335884) +++ head/contrib/bsnmp/snmp_usm/usm_snmp.c (revision 335885) @@ -1,618 +1,620 @@ /*- * Copyright (c) 2010 The FreeBSD Foundation * All rights reserved. * * This software was developed by Shteryana Sotirova Shopova under * sponsorship from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include "asn1.h" #include "snmp.h" #include "snmpmod.h" +#define SNMPTREE_TYPES #include "usm_tree.h" #include "usm_oid.h" static struct lmodule *usm_module; /* For the registration. */ static const struct asn_oid oid_usm = OIDX_snmpUsmMIB; static const struct asn_oid oid_usmNoAuthProtocol = OIDX_usmNoAuthProtocol; static const struct asn_oid oid_usmHMACMD5AuthProtocol = \ OIDX_usmHMACMD5AuthProtocol; static const struct asn_oid oid_usmHMACSHAAuthProtocol = \ OIDX_usmHMACSHAAuthProtocol; static const struct asn_oid oid_usmNoPrivProtocol = OIDX_usmNoPrivProtocol; static const struct asn_oid oid_usmDESPrivProtocol = OIDX_usmDESPrivProtocol; static const struct asn_oid oid_usmAesCfb128Protocol = OIDX_usmAesCfb128Protocol; static const struct asn_oid oid_usmUserSecurityName = OIDX_usmUserSecurityName; /* The registration. */ static uint reg_usm; static int32_t usm_lock; static struct usm_user * usm_get_user(const struct asn_oid *, uint); static struct usm_user * usm_get_next_user(const struct asn_oid *, uint); static void usm_append_userindex(struct asn_oid *, uint, const struct usm_user *); static int usm_user_index_decode(const struct asn_oid *, uint, uint8_t *, uint32_t *, char *); int op_usm_stats(struct snmp_context *ctx __unused, struct snmp_value *val, uint32_t sub __unused, uint32_t iidx __unused, enum snmp_op op) { struct snmpd_usmstat *usmstats; if (op == SNMP_OP_SET) return (SNMP_ERR_NOT_WRITEABLE); if ((usmstats = bsnmpd_get_usm_stats()) == NULL) return (SNMP_ERR_GENERR); if (op == SNMP_OP_GET) { switch (val->var.subs[sub - 1]) { case LEAF_usmStatsUnsupportedSecLevels: val->v.uint32 = usmstats->unsupported_seclevels; break; case LEAF_usmStatsNotInTimeWindows: val->v.uint32 = usmstats->not_in_time_windows; break; case LEAF_usmStatsUnknownUserNames: val->v.uint32 = usmstats->unknown_users; break; case LEAF_usmStatsUnknownEngineIDs: val->v.uint32 = usmstats->unknown_engine_ids; break; case LEAF_usmStatsWrongDigests: val->v.uint32 = usmstats->wrong_digests; break; case LEAF_usmStatsDecryptionErrors: val->v.uint32 = usmstats->decrypt_errors; break; default: return (SNMP_ERR_NOSUCHNAME); } return (SNMP_ERR_NOERROR); } abort(); } int op_usm_lock(struct snmp_context *ctx __unused, struct snmp_value *val, uint32_t sub, uint32_t iidx __unused, enum snmp_op op) { if (val->var.subs[sub - 1] != LEAF_usmUserSpinLock) return (SNMP_ERR_NOSUCHNAME); switch (op) { case SNMP_OP_GET: if (++usm_lock == INT32_MAX) usm_lock = 0; val->v.integer = usm_lock; break; case SNMP_OP_GETNEXT: abort(); case SNMP_OP_SET: if (val->v.integer != usm_lock) return (SNMP_ERR_INCONS_VALUE); break; case SNMP_OP_ROLLBACK: /* FALLTHROUGH */ case SNMP_OP_COMMIT: break; } return (SNMP_ERR_NOERROR); } int op_usm_users(struct snmp_context *ctx, struct snmp_value *val, uint32_t sub, uint32_t iidx __unused, enum snmp_op op) { uint32_t elen; struct usm_user *uuser, *clone; char uname[SNMP_ADM_STR32_SIZ]; uint8_t eid[SNMP_ENGINE_ID_SIZ]; switch (op) { case SNMP_OP_GET: if ((uuser = usm_get_user(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_GETNEXT: if ((uuser = usm_get_next_user(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); usm_append_userindex(&val->var, sub, uuser); break; case SNMP_OP_SET: if ((uuser = usm_get_user(&val->var, sub)) == NULL && val->var.subs[sub - 1] != LEAF_usmUserStatus && val->var.subs[sub - 1] != LEAF_usmUserCloneFrom) return (SNMP_ERR_NOSUCHNAME); /* * XXX (ngie): need to investigate the MIB to determine how * this is possible given some of the transitions below. */ if (community != COMM_INITIALIZE && uuser != NULL && uuser->type == StorageType_readOnly) return (SNMP_ERR_NOT_WRITEABLE); switch (val->var.subs[sub - 1]) { case LEAF_usmUserSecurityName: return (SNMP_ERR_NOT_WRITEABLE); case LEAF_usmUserCloneFrom: if (uuser != NULL || usm_user_index_decode(&val->var, sub, eid, &elen, uname) < 0 || !(asn_is_suboid(&oid_usmUserSecurityName, &val->v.oid))) return (SNMP_ERR_WRONG_VALUE); if ((clone = usm_get_user(&val->v.oid, sub)) == NULL) return (SNMP_ERR_INCONS_VALUE); if ((uuser = usm_new_user(eid, elen, uname)) == NULL) return (SNMP_ERR_GENERR); uuser->status = RowStatus_notReady; if (community != COMM_INITIALIZE) uuser->type = StorageType_volatile; else uuser->type = StorageType_readOnly; uuser->suser.auth_proto = clone->suser.auth_proto; uuser->suser.priv_proto = clone->suser.priv_proto; memcpy(uuser->suser.auth_key, clone->suser.auth_key, sizeof(uuser->suser.auth_key)); memcpy(uuser->suser.priv_key, clone->suser.priv_key, sizeof(uuser->suser.priv_key)); ctx->scratch->int1 = RowStatus_createAndWait; break; case LEAF_usmUserAuthProtocol: ctx->scratch->int1 = uuser->suser.auth_proto; if (asn_compare_oid(&oid_usmNoAuthProtocol, &val->v.oid) == 0) uuser->suser.auth_proto = SNMP_AUTH_NOAUTH; else if (asn_compare_oid(&oid_usmHMACMD5AuthProtocol, &val->v.oid) == 0) uuser->suser.auth_proto = SNMP_AUTH_HMAC_MD5; else if (asn_compare_oid(&oid_usmHMACSHAAuthProtocol, &val->v.oid) == 0) uuser->suser.auth_proto = SNMP_AUTH_HMAC_SHA; else return (SNMP_ERR_WRONG_VALUE); break; case LEAF_usmUserAuthKeyChange: case LEAF_usmUserOwnAuthKeyChange: if (val->var.subs[sub - 1] == LEAF_usmUserOwnAuthKeyChange && (usm_user == NULL || strcmp(uuser->suser.sec_name, usm_user->suser.sec_name) != 0)) return (SNMP_ERR_NO_ACCESS); if (val->v.octetstring.len > SNMP_AUTH_KEY_SIZ) return (SNMP_ERR_INCONS_VALUE); ctx->scratch->ptr1 = malloc(SNMP_AUTH_KEY_SIZ); if (ctx->scratch->ptr1 == NULL) return (SNMP_ERR_GENERR); memcpy(ctx->scratch->ptr1, uuser->suser.auth_key, SNMP_AUTH_KEY_SIZ); memcpy(uuser->suser.auth_key, val->v.octetstring.octets, val->v.octetstring.len); break; case LEAF_usmUserPrivProtocol: ctx->scratch->int1 = uuser->suser.priv_proto; if (asn_compare_oid(&oid_usmNoPrivProtocol, &val->v.oid) == 0) uuser->suser.priv_proto = SNMP_PRIV_NOPRIV; else if (asn_compare_oid(&oid_usmDESPrivProtocol, &val->v.oid) == 0) uuser->suser.priv_proto = SNMP_PRIV_DES; else if (asn_compare_oid(&oid_usmAesCfb128Protocol, &val->v.oid) == 0) uuser->suser.priv_proto = SNMP_PRIV_AES; else return (SNMP_ERR_WRONG_VALUE); break; case LEAF_usmUserPrivKeyChange: case LEAF_usmUserOwnPrivKeyChange: if (val->var.subs[sub - 1] == LEAF_usmUserOwnPrivKeyChange && (usm_user == NULL || strcmp(uuser->suser.sec_name, usm_user->suser.sec_name) != 0)) return (SNMP_ERR_NO_ACCESS); if (val->v.octetstring.len > SNMP_PRIV_KEY_SIZ) return (SNMP_ERR_INCONS_VALUE); ctx->scratch->ptr1 = malloc(SNMP_PRIV_KEY_SIZ); if (ctx->scratch->ptr1 == NULL) return (SNMP_ERR_GENERR); memcpy(ctx->scratch->ptr1, uuser->suser.priv_key, sizeof(uuser->suser.priv_key)); memcpy(uuser->suser.priv_key, val->v.octetstring.octets, val->v.octetstring.len); break; case LEAF_usmUserPublic: if (val->v.octetstring.len > SNMP_ADM_STR32_SIZ) return (SNMP_ERR_INCONS_VALUE); if (uuser->user_public_len > 0) { ctx->scratch->ptr2 = malloc(uuser->user_public_len); if (ctx->scratch->ptr2 == NULL) return (SNMP_ERR_GENERR); memcpy(ctx->scratch->ptr2, uuser->user_public, uuser->user_public_len); ctx->scratch->int2 = uuser->user_public_len; } if (val->v.octetstring.len > 0) { memcpy(uuser->user_public, val->v.octetstring.octets, val->v.octetstring.len); uuser->user_public_len = val->v.octetstring.len; } else { memset(uuser->user_public, 0, sizeof(uuser->user_public)); uuser->user_public_len = 0; } break; case LEAF_usmUserStorageType: return (SNMP_ERR_INCONS_VALUE); case LEAF_usmUserStatus: if (uuser == NULL) { if (val->v.integer != RowStatus_createAndWait || usm_user_index_decode(&val->var, sub, eid, &elen, uname) < 0) return (SNMP_ERR_INCONS_VALUE); uuser = usm_new_user(eid, elen, uname); if (uuser == NULL) return (SNMP_ERR_GENERR); uuser->status = RowStatus_notReady; if (community != COMM_INITIALIZE) uuser->type = StorageType_volatile; else uuser->type = StorageType_readOnly; } else if (val->v.integer != RowStatus_active && val->v.integer != RowStatus_destroy) return (SNMP_ERR_INCONS_VALUE); uuser->status = val->v.integer; break; } return (SNMP_ERR_NOERROR); case SNMP_OP_COMMIT: switch (val->var.subs[sub - 1]) { case LEAF_usmUserAuthKeyChange: case LEAF_usmUserOwnAuthKeyChange: case LEAF_usmUserPrivKeyChange: case LEAF_usmUserOwnPrivKeyChange: free(ctx->scratch->ptr1); break; case LEAF_usmUserPublic: if (ctx->scratch->ptr2 != NULL) free(ctx->scratch->ptr2); break; case LEAF_usmUserStatus: if (val->v.integer != RowStatus_destroy) break; if ((uuser = usm_get_user(&val->var, sub)) == NULL) return (SNMP_ERR_GENERR); usm_delete_user(uuser); break; default: break; } return (SNMP_ERR_NOERROR); case SNMP_OP_ROLLBACK: if ((uuser = usm_get_user(&val->var, sub)) == NULL) return (SNMP_ERR_GENERR); switch (val->var.subs[sub - 1]) { case LEAF_usmUserAuthProtocol: uuser->suser.auth_proto = ctx->scratch->int1; break; case LEAF_usmUserAuthKeyChange: case LEAF_usmUserOwnAuthKeyChange: memcpy(uuser->suser.auth_key, ctx->scratch->ptr1, sizeof(uuser->suser.auth_key)); free(ctx->scratch->ptr1); break; case LEAF_usmUserPrivProtocol: uuser->suser.priv_proto = ctx->scratch->int1; break; case LEAF_usmUserPrivKeyChange: case LEAF_usmUserOwnPrivKeyChange: memcpy(uuser->suser.priv_key, ctx->scratch->ptr1, sizeof(uuser->suser.priv_key)); free(ctx->scratch->ptr1); break; case LEAF_usmUserPublic: if (ctx->scratch->ptr2 != NULL) { memcpy(uuser->user_public, ctx->scratch->ptr2, ctx->scratch->int2); uuser->user_public_len = ctx->scratch->int2; free(ctx->scratch->ptr2); } else { memset(uuser->user_public, 0, sizeof(uuser->user_public)); uuser->user_public_len = 0; } break; case LEAF_usmUserCloneFrom: case LEAF_usmUserStatus: if (ctx->scratch->int1 == RowStatus_createAndWait) usm_delete_user(uuser); break; default: break; } return (SNMP_ERR_NOERROR); default: abort(); } switch (val->var.subs[sub - 1]) { case LEAF_usmUserSecurityName: return (string_get(val, uuser->suser.sec_name, -1)); case LEAF_usmUserCloneFrom: memcpy(&val->v.oid, &oid_zeroDotZero, sizeof(oid_zeroDotZero)); break; case LEAF_usmUserAuthProtocol: switch (uuser->suser.auth_proto) { case SNMP_AUTH_HMAC_MD5: memcpy(&val->v.oid, &oid_usmHMACMD5AuthProtocol, sizeof(oid_usmHMACMD5AuthProtocol)); break; case SNMP_AUTH_HMAC_SHA: memcpy(&val->v.oid, &oid_usmHMACSHAAuthProtocol, sizeof(oid_usmHMACSHAAuthProtocol)); break; default: memcpy(&val->v.oid, &oid_usmNoAuthProtocol, sizeof(oid_usmNoAuthProtocol)); break; } break; case LEAF_usmUserAuthKeyChange: case LEAF_usmUserOwnAuthKeyChange: return (string_get(val, (char *)uuser->suser.auth_key, 0)); case LEAF_usmUserPrivProtocol: switch (uuser->suser.priv_proto) { case SNMP_PRIV_DES: memcpy(&val->v.oid, &oid_usmDESPrivProtocol, sizeof(oid_usmDESPrivProtocol)); break; case SNMP_PRIV_AES: memcpy(&val->v.oid, &oid_usmAesCfb128Protocol, sizeof(oid_usmAesCfb128Protocol)); break; default: memcpy(&val->v.oid, &oid_usmNoPrivProtocol, sizeof(oid_usmNoPrivProtocol)); break; } break; case LEAF_usmUserPrivKeyChange: case LEAF_usmUserOwnPrivKeyChange: return (string_get(val, (char *)uuser->suser.priv_key, 0)); case LEAF_usmUserPublic: return (string_get(val, uuser->user_public, uuser->user_public_len)); case LEAF_usmUserStorageType: val->v.integer = uuser->type; break; case LEAF_usmUserStatus: val->v.integer = uuser->status; break; } return (SNMP_ERR_NOERROR); } static int usm_user_index_decode(const struct asn_oid *oid, uint sub, uint8_t *engine, uint32_t *elen, char *uname) { uint32_t i, nlen; int uname_off; if (oid->subs[sub] > SNMP_ENGINE_ID_SIZ) return (-1); for (i = 0; i < oid->subs[sub]; i++) engine[i] = oid->subs[sub + i + 1]; *elen = i; uname_off = sub + oid->subs[sub] + 1; if ((nlen = oid->subs[uname_off]) >= SNMP_ADM_STR32_SIZ) return (-1); for (i = 0; i < nlen; i++) uname[i] = oid->subs[uname_off + i + 1]; uname[nlen] = '\0'; return (0); } static void usm_append_userindex(struct asn_oid *oid, uint sub, const struct usm_user *uuser) { uint32_t i; oid->len = sub + uuser->user_engine_len + strlen(uuser->suser.sec_name); oid->len += 2; oid->subs[sub] = uuser->user_engine_len; for (i = 1; i < uuser->user_engine_len + 1; i++) oid->subs[sub + i] = uuser->user_engine_id[i - 1]; sub += uuser->user_engine_len + 1; oid->subs[sub] = strlen(uuser->suser.sec_name); for (i = 1; i <= oid->subs[sub]; i++) oid->subs[sub + i] = uuser->suser.sec_name[i - 1]; } static struct usm_user * usm_get_user(const struct asn_oid *oid, uint sub) { uint32_t enginelen; char username[SNMP_ADM_STR32_SIZ]; uint8_t engineid[SNMP_ENGINE_ID_SIZ]; if (usm_user_index_decode(oid, sub, engineid, &enginelen, username) < 0) return (NULL); return (usm_find_user(engineid, enginelen, username)); } static struct usm_user * usm_get_next_user(const struct asn_oid *oid, uint sub) { uint32_t enginelen; char username[SNMP_ADM_STR32_SIZ]; uint8_t engineid[SNMP_ENGINE_ID_SIZ]; struct usm_user *uuser; if (oid->len - sub == 0) return (usm_first_user()); if (usm_user_index_decode(oid, sub, engineid, &enginelen, username) < 0) return (NULL); if ((uuser = usm_find_user(engineid, enginelen, username)) != NULL) return (usm_next_user(uuser)); return (NULL); } /* * USM snmp module initialization hook. * Returns 0 on success, < 0 on error. */ static int usm_init(struct lmodule * mod, int argc __unused, char *argv[] __unused) { usm_module = mod; usm_lock = random(); bsnmpd_reset_usm_stats(); return (0); } /* * USM snmp module finalization hook. */ static int usm_fini(void) { usm_flush_users(); or_unregister(reg_usm); return (0); } /* * USM snmp module start operation. */ static void usm_start(void) { reg_usm = or_register(&oid_usm, "The MIB module for managing SNMP User-Based Security Model.", usm_module); } static void usm_dump(void) { struct usm_user *uuser; struct snmpd_usmstat *usmstats; const char *const authstr[] = { "noauth", "md5", "sha", NULL }; const char *const privstr[] = { "nopriv", "des", "aes", NULL }; if ((usmstats = bsnmpd_get_usm_stats()) != NULL) { syslog(LOG_ERR, "UnsupportedSecLevels\t\t%u", usmstats->unsupported_seclevels); syslog(LOG_ERR, "NotInTimeWindows\t\t%u", usmstats->not_in_time_windows); syslog(LOG_ERR, "UnknownUserNames\t\t%u", usmstats->unknown_users); syslog(LOG_ERR, "UnknownEngineIDs\t\t%u", usmstats->unknown_engine_ids); syslog(LOG_ERR, "WrongDigests\t\t%u", usmstats->wrong_digests); syslog(LOG_ERR, "DecryptionErrors\t\t%u", usmstats->decrypt_errors); } syslog(LOG_ERR, "USM users"); for (uuser = usm_first_user(); uuser != NULL; (uuser = usm_next_user(uuser))) syslog(LOG_ERR, "user %s\t\t%s, %s", uuser->suser.sec_name, authstr[uuser->suser.auth_proto], privstr[uuser->suser.priv_proto]); } -const char usm_comment[] = \ +static const char usm_comment[] = \ "This module implements SNMP User-based Security Model defined in RFC 3414."; +extern const struct snmp_module config; const struct snmp_module config = { .comment = usm_comment, .init = usm_init, .fini = usm_fini, .start = usm_start, .tree = usm_ctree, .dump = usm_dump, .tree_size = usm_CTREE_SIZE, }; Index: head/contrib/bsnmp/snmp_usm/usm_tree.def =================================================================== --- head/contrib/bsnmp/snmp_usm/usm_tree.def (revision 335884) +++ head/contrib/bsnmp/snmp_usm/usm_tree.def (revision 335885) @@ -1,111 +1,94 @@ #- # 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$ # -#include "tc.def" - -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 -) +include "tc.def" (1 internet (6 snmpV2 (3 snmpModules (10 snmpFrameworkMIB (1 snmpFrameworkAdmin (1 snmpAuthProtocols (1 usmNoAuthProtocol ) (2 usmHMACMD5AuthProtocol ) (3 usmHMACSHAAuthProtocol ) ) (2 snmpPrivProtocols (1 usmNoPrivProtocol ) (2 usmDESPrivProtocol ) (4 usmAesCfb128Protocol ) ) ) ) (15 snmpUsmMIB (1 usmMIBObjects (1 usmStats (1 usmStatsUnsupportedSecLevels COUNTER op_usm_stats GET) (2 usmStatsNotInTimeWindows COUNTER op_usm_stats GET) (3 usmStatsUnknownUserNames COUNTER op_usm_stats GET) (4 usmStatsUnknownEngineIDs COUNTER op_usm_stats GET) (5 usmStatsWrongDigests COUNTER op_usm_stats GET) (6 usmStatsDecryptionErrors COUNTER op_usm_stats GET) ) (2 usmUser (1 usmUserSpinLock INTEGER op_usm_lock GET SET) (2 usmUserTable (1 usmUserEntry : OCTETSTRING | SnmpEngineID OCTETSTRING op_usm_users (1 usmUserEngineID OCTETSTRING | SnmpEngineID) (2 usmUserName OCTETSTRING) (3 usmUserSecurityName OCTETSTRING | SnmpAdminString GET) (4 usmUserCloneFrom OID GET SET) (5 usmUserAuthProtocol OID GET SET) (6 usmUserAuthKeyChange OCTETSTRING | KeyChange GET SET) (7 usmUserOwnAuthKeyChange OCTETSTRING | KeyChange GET SET) (8 usmUserPrivProtocol OID GET SET) (9 usmUserPrivKeyChange OCTETSTRING | KeyChange GET SET) (10 usmUserOwnPrivKeyChange OCTETSTRING | KeyChange GET SET) (11 usmUserPublic OCTETSTRING GET SET) (12 usmUserStorageType StorageType GET SET) (13 usmUserStatus RowStatus GET SET) ) ) ) ) ) (20 snmpUsmAesMIB ) ) ) ) Index: head/contrib/bsnmp/snmp_vacm/vacm_snmp.c =================================================================== --- head/contrib/bsnmp/snmp_vacm/vacm_snmp.c (revision 335884) +++ head/contrib/bsnmp/snmp_vacm/vacm_snmp.c (revision 335885) @@ -1,1026 +1,1028 @@ /*- - * Copyright (c) 2010 The FreeBSD Foundation + * Copyright (c) 2010,2018 The FreeBSD Foundation * All rights reserved. * * This software was developed by Shteryana Sotirova Shopova under * sponsorship from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include "asn1.h" #include "snmp.h" #include "snmpmod.h" +#define SNMPTREE_TYPES #include "vacm_tree.h" #include "vacm_oid.h" static struct lmodule *vacm_module; /* For the registration. */ static const struct asn_oid oid_vacm = OIDX_snmpVacmMIB; static uint reg_vacm; static int32_t vacm_lock; /* * Internal datastructures and forward declarations. */ static void vacm_append_userindex(struct asn_oid *, uint, const struct vacm_user *); static int vacm_user_index_decode(const struct asn_oid *, uint, int32_t *, char *); static struct vacm_user *vacm_get_user(const struct asn_oid *, uint); static struct vacm_user *vacm_get_next_user(const struct asn_oid *, uint); static void vacm_append_access_rule_index(struct asn_oid *, uint, const struct vacm_access *); static int vacm_access_rule_index_decode(const struct asn_oid *, uint, char *, char *, int32_t *, int32_t *); static struct vacm_access * vacm_get_access_rule(const struct asn_oid *, uint); static struct vacm_access * vacm_get_next_access_rule(const struct asn_oid *, uint); static int vacm_view_index_decode(const struct asn_oid *, uint, char *, struct asn_oid *); static void vacm_append_viewindex(struct asn_oid *, uint, const struct vacm_view *); static struct vacm_view *vacm_get_view(const struct asn_oid *, uint); static struct vacm_view *vacm_get_next_view(const struct asn_oid *, uint); static struct vacm_view *vacm_get_view_by_name(u_char *, u_int); static struct vacm_context *vacm_get_context(const struct asn_oid *, uint); static struct vacm_context *vacm_get_next_context(const struct asn_oid *, uint); static void vacm_append_ctxindex(struct asn_oid *, uint, const struct vacm_context *); int op_vacm_context(struct snmp_context *ctx __unused, struct snmp_value *val, uint32_t sub, uint32_t iidx __unused, enum snmp_op op) { char cname[SNMP_ADM_STR32_SIZ]; size_t cnamelen; struct vacm_context *vacm_ctx; if (val->var.subs[sub - 1] != LEAF_vacmContextName) abort(); switch (op) { case SNMP_OP_GET: if ((vacm_ctx = vacm_get_context(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_GETNEXT: if ((vacm_ctx = vacm_get_next_context(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); vacm_append_ctxindex(&val->var, sub, vacm_ctx); break; case SNMP_OP_SET: if ((vacm_ctx = vacm_get_context(&val->var, sub)) != NULL) return (SNMP_ERR_WRONG_VALUE); if (community != COMM_INITIALIZE) return (SNMP_ERR_NOT_WRITEABLE); if (val->var.subs[sub] >= SNMP_ADM_STR32_SIZ) return (SNMP_ERR_WRONG_VALUE); if (index_decode(&val->var, sub, iidx, &cname, &cnamelen)) return (SNMP_ERR_GENERR); cname[cnamelen] = '\0'; if ((vacm_ctx = vacm_add_context(cname, reg_vacm)) == NULL) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case SNMP_OP_COMMIT: /* FALLTHROUGH*/ case SNMP_OP_ROLLBACK: return (SNMP_ERR_NOERROR); default: abort(); } return (string_get(val, vacm_ctx->ctxname, -1)); } int op_vacm_security_to_group(struct snmp_context *ctx, struct snmp_value *val, uint32_t sub, uint32_t iidx __unused, enum snmp_op op) { int32_t smodel; char uname[SNMP_ADM_STR32_SIZ]; struct vacm_user *user; switch (op) { case SNMP_OP_GET: if ((user = vacm_get_user(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_GETNEXT: if ((user = vacm_get_next_user(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); vacm_append_userindex(&val->var, sub, user); break; case SNMP_OP_SET: if ((user = vacm_get_user(&val->var, sub)) == NULL && val->var.subs[sub - 1] != LEAF_vacmSecurityToGroupStatus) return (SNMP_ERR_NOSUCHNAME); if (user != NULL) { if (community != COMM_INITIALIZE && user->type == StorageType_readOnly) return (SNMP_ERR_NOT_WRITEABLE); if (user->status == RowStatus_active && val->v.integer != RowStatus_destroy) return (SNMP_ERR_INCONS_VALUE); } switch (val->var.subs[sub - 1]) { case LEAF_vacmGroupName: ctx->scratch->ptr1 = user->group->groupname; ctx->scratch->int1 = strlen(user->group->groupname); return (vacm_user_set_group(user, val->v.octetstring.octets,val->v.octetstring.len)); case LEAF_vacmSecurityToGroupStorageType: return (SNMP_ERR_INCONS_VALUE); case LEAF_vacmSecurityToGroupStatus: if (user == NULL) { if (val->v.integer != RowStatus_createAndGo || vacm_user_index_decode(&val->var, sub, &smodel, uname) < 0) return (SNMP_ERR_INCONS_VALUE); user = vacm_new_user(smodel, uname); if (user == NULL) return (SNMP_ERR_GENERR); user->status = RowStatus_destroy; if (community != COMM_INITIALIZE) user->type = StorageType_volatile; else user->type = StorageType_readOnly; } else if (val->v.integer != RowStatus_active && val->v.integer != RowStatus_destroy) return (SNMP_ERR_INCONS_VALUE); ctx->scratch->int1 = user->status; user->status = val->v.integer; break; } return (SNMP_ERR_NOERROR); case SNMP_OP_COMMIT: if (val->var.subs[sub - 1] != LEAF_vacmSecurityToGroupStatus) return (SNMP_ERR_NOERROR); if ((user = vacm_get_user(&val->var, sub)) == NULL) return (SNMP_ERR_GENERR); switch (val->v.integer) { case RowStatus_destroy: return (vacm_delete_user(user)); case RowStatus_createAndGo: user->status = RowStatus_active; break; default: break; } return (SNMP_ERR_NOERROR); case SNMP_OP_ROLLBACK: if ((user = vacm_get_user(&val->var, sub)) == NULL) return (SNMP_ERR_GENERR); switch (val->var.subs[sub - 1]) { case LEAF_vacmGroupName: return (vacm_user_set_group(user, ctx->scratch->ptr1, ctx->scratch->int1)); case LEAF_vacmSecurityToGroupStatus: if (ctx->scratch->int1 == RowStatus_destroy) return (vacm_delete_user(user)); user->status = ctx->scratch->int1; break; default: break; } return (SNMP_ERR_NOERROR); default: abort(); } switch (val->var.subs[sub - 1]) { case LEAF_vacmGroupName: return (string_get(val, user->group->groupname, -1)); case LEAF_vacmSecurityToGroupStorageType: val->v.integer = user->type; break; case LEAF_vacmSecurityToGroupStatus: val->v.integer = user->status; break; default: abort(); } return (SNMP_ERR_NOERROR); } int op_vacm_access(struct snmp_context *ctx, struct snmp_value *val, uint32_t sub, uint32_t iidx __unused, enum snmp_op op) { int32_t smodel, slevel; char gname[SNMP_ADM_STR32_SIZ], cprefix[SNMP_ADM_STR32_SIZ]; struct vacm_access *acl; switch (op) { case SNMP_OP_GET: if ((acl = vacm_get_access_rule(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_GETNEXT: if ((acl = vacm_get_next_access_rule(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); vacm_append_access_rule_index(&val->var, sub, acl); break; case SNMP_OP_SET: if ((acl = vacm_get_access_rule(&val->var, sub)) == NULL && val->var.subs[sub - 1] != LEAF_vacmAccessStatus) return (SNMP_ERR_NOSUCHNAME); if (acl != NULL && community != COMM_INITIALIZE && acl->type == StorageType_readOnly) return (SNMP_ERR_NOT_WRITEABLE); switch (val->var.subs[sub - 1]) { case LEAF_vacmAccessContextMatch: ctx->scratch->int1 = acl->ctx_match; if (val->v.integer == vacmAccessContextMatch_exact) acl->ctx_match = 1; else if (val->v.integer == vacmAccessContextMatch_prefix) acl->ctx_match = 0; else return (SNMP_ERR_WRONG_VALUE); break; case LEAF_vacmAccessReadViewName: ctx->scratch->ptr1 = acl->read_view; acl->read_view = vacm_get_view_by_name(val->v.octetstring.octets, val->v.octetstring.len); if (acl->read_view == NULL) { acl->read_view = ctx->scratch->ptr1; return (SNMP_ERR_INCONS_VALUE); } return (SNMP_ERR_NOERROR); case LEAF_vacmAccessWriteViewName: ctx->scratch->ptr1 = acl->write_view; if ((acl->write_view = vacm_get_view_by_name(val->v.octetstring.octets, val->v.octetstring.len)) == NULL) { acl->write_view = ctx->scratch->ptr1; return (SNMP_ERR_INCONS_VALUE); } break; case LEAF_vacmAccessNotifyViewName: ctx->scratch->ptr1 = acl->notify_view; if ((acl->notify_view = vacm_get_view_by_name(val->v.octetstring.octets, val->v.octetstring.len)) == NULL) { acl->notify_view = ctx->scratch->ptr1; return (SNMP_ERR_INCONS_VALUE); } break; case LEAF_vacmAccessStorageType: return (SNMP_ERR_INCONS_VALUE); case LEAF_vacmAccessStatus: if (acl == NULL) { if (val->v.integer != RowStatus_createAndGo || vacm_access_rule_index_decode(&val->var, sub, gname, cprefix, &smodel, &slevel) < 0) return (SNMP_ERR_INCONS_VALUE); if ((acl = vacm_new_access_rule(gname, cprefix, smodel, slevel)) == NULL) return (SNMP_ERR_GENERR); acl->status = RowStatus_destroy; if (community != COMM_INITIALIZE) acl->type = StorageType_volatile; else acl->type = StorageType_readOnly; } else if (val->v.integer != RowStatus_active && val->v.integer != RowStatus_destroy) return (SNMP_ERR_INCONS_VALUE); ctx->scratch->int1 = acl->status; acl->status = val->v.integer; break; } return (SNMP_ERR_NOERROR); case SNMP_OP_COMMIT: if (val->var.subs[sub - 1] != LEAF_vacmAccessStatus) return (SNMP_ERR_NOERROR); if ((acl = vacm_get_access_rule(&val->var, sub)) == NULL) return (SNMP_ERR_GENERR); if (val->v.integer == RowStatus_destroy) return (vacm_delete_access_rule(acl)); else acl->status = RowStatus_active; return (SNMP_ERR_NOERROR); case SNMP_OP_ROLLBACK: if ((acl = vacm_get_access_rule(&val->var, sub)) == NULL) return (SNMP_ERR_GENERR); switch (val->var.subs[sub - 1]) { case LEAF_vacmAccessContextMatch: acl->ctx_match = ctx->scratch->int1; break; case LEAF_vacmAccessReadViewName: acl->read_view = ctx->scratch->ptr1; break; case LEAF_vacmAccessWriteViewName: acl->write_view = ctx->scratch->ptr1; break; case LEAF_vacmAccessNotifyViewName: acl->notify_view = ctx->scratch->ptr1; break; case LEAF_vacmAccessStatus: if (ctx->scratch->int1 == RowStatus_destroy) return (vacm_delete_access_rule(acl)); default: break; } return (SNMP_ERR_NOERROR); default: abort(); } switch (val->var.subs[sub - 1]) { case LEAF_vacmAccessContextMatch: return (string_get(val, acl->ctx_prefix, -1)); case LEAF_vacmAccessReadViewName: if (acl->read_view != NULL) return (string_get(val, acl->read_view->viewname, -1)); else return (string_get(val, NULL, 0)); case LEAF_vacmAccessWriteViewName: if (acl->write_view != NULL) return (string_get(val, acl->write_view->viewname, -1)); else return (string_get(val, NULL, 0)); case LEAF_vacmAccessNotifyViewName: if (acl->notify_view != NULL) return (string_get(val, acl->notify_view->viewname, -1)); else return (string_get(val, NULL, 0)); case LEAF_vacmAccessStorageType: val->v.integer = acl->type; break; case LEAF_vacmAccessStatus: val->v.integer = acl->status; break; default: abort(); } return (SNMP_ERR_NOERROR); } int op_vacm_view_lock(struct snmp_context *ctx __unused, struct snmp_value *val, uint32_t sub, uint32_t iidx __unused, enum snmp_op op) { if (val->var.subs[sub - 1] != LEAF_vacmViewSpinLock) return (SNMP_ERR_NOSUCHNAME); switch (op) { case SNMP_OP_GET: if (++vacm_lock == INT32_MAX) vacm_lock = 0; val->v.integer = vacm_lock; break; case SNMP_OP_GETNEXT: abort(); case SNMP_OP_SET: if (val->v.integer != vacm_lock) return (SNMP_ERR_INCONS_VALUE); break; case SNMP_OP_ROLLBACK: /* FALLTHROUGH */ case SNMP_OP_COMMIT: break; } return (SNMP_ERR_NOERROR); } int op_vacm_view(struct snmp_context *ctx, struct snmp_value *val, uint32_t sub, uint32_t iidx __unused, enum snmp_op op) { char vname[SNMP_ADM_STR32_SIZ]; struct asn_oid oid; struct vacm_view *view; switch (op) { case SNMP_OP_GET: if ((view = vacm_get_view(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_GETNEXT: if ((view = vacm_get_next_view(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); vacm_append_viewindex(&val->var, sub, view); break; case SNMP_OP_SET: if ((view = vacm_get_view(&val->var, sub)) == NULL && val->var.subs[sub - 1] != LEAF_vacmViewTreeFamilyStatus) return (SNMP_ERR_NOSUCHNAME); if (view != NULL) { if (community != COMM_INITIALIZE && view->type == StorageType_readOnly) return (SNMP_ERR_NOT_WRITEABLE); if (view->status == RowStatus_active && val->v.integer != RowStatus_destroy) return (SNMP_ERR_INCONS_VALUE); } switch (val->var.subs[sub - 1]) { case LEAF_vacmViewTreeFamilyMask: if (val->v.octetstring.len > sizeof(view->mask)) ctx->scratch->ptr1 = malloc(sizeof(view->mask)); if (ctx->scratch->ptr1 == NULL) return (SNMP_ERR_GENERR); memset(ctx->scratch->ptr1, 0, sizeof(view->mask)); memcpy(ctx->scratch->ptr1, view->mask, sizeof(view->mask)); memset(view->mask, 0, sizeof(view->mask)); memcpy(view->mask, val->v.octetstring.octets, val->v.octetstring.len); break; case LEAF_vacmViewTreeFamilyType: ctx->scratch->int1 = view->exclude; if (val->v.integer == vacmViewTreeFamilyType_included) view->exclude = 0; else if (val->v.integer == vacmViewTreeFamilyType_excluded) view->exclude = 1; else return (SNMP_ERR_WRONG_VALUE); break; case LEAF_vacmViewTreeFamilyStorageType: return (SNMP_ERR_INCONS_VALUE); case LEAF_vacmViewTreeFamilyStatus: if (view == NULL) { if (val->v.integer != RowStatus_createAndGo || vacm_view_index_decode(&val->var, sub, vname, &oid) < 0) return (SNMP_ERR_INCONS_VALUE); if ((view = vacm_new_view(vname, &oid)) == NULL) return (SNMP_ERR_GENERR); view->status = RowStatus_destroy; if (community != COMM_INITIALIZE) view->type = StorageType_volatile; else view->type = StorageType_readOnly; } else if (val->v.integer != RowStatus_active && val->v.integer != RowStatus_destroy) return (SNMP_ERR_INCONS_VALUE); ctx->scratch->int1 = view->status; view->status = val->v.integer; break; } return (SNMP_ERR_NOERROR); case SNMP_OP_COMMIT: switch (val->var.subs[sub - 1]) { case LEAF_vacmViewTreeFamilyMask: free(ctx->scratch->ptr1); break; case LEAF_vacmViewTreeFamilyStatus: if ((view = vacm_get_view(&val->var, sub)) == NULL) return (SNMP_ERR_GENERR); switch (val->v.integer) { case RowStatus_destroy: return (vacm_delete_view(view)); case RowStatus_createAndGo: view->status = RowStatus_active; break; default: /* NOTREACHED*/ return (SNMP_ERR_GENERR); } default: break; } return (SNMP_ERR_NOERROR); case SNMP_OP_ROLLBACK: if ((view = vacm_get_view(&val->var, sub)) == NULL) return (SNMP_ERR_GENERR); switch (val->var.subs[sub - 1]) { case LEAF_vacmViewTreeFamilyMask: memcpy(view->mask, ctx->scratch->ptr1, sizeof(view->mask)); free(ctx->scratch->ptr1); break; case LEAF_vacmViewTreeFamilyType: view->exclude = ctx->scratch->int1; break; case LEAF_vacmViewTreeFamilyStatus: if (ctx->scratch->int1 == RowStatus_destroy) return (vacm_delete_view(view)); break; default: break; } return (SNMP_ERR_NOERROR); default: abort(); } switch (val->var.subs[sub - 1]) { case LEAF_vacmViewTreeFamilyMask: return (string_get(val, view->mask, sizeof(view->mask))); case LEAF_vacmViewTreeFamilyType: if (view->exclude) val->v.integer = vacmViewTreeFamilyType_excluded; else val->v.integer = vacmViewTreeFamilyType_included; break; case LEAF_vacmViewTreeFamilyStorageType: val->v.integer = view->type; break; case LEAF_vacmViewTreeFamilyStatus: val->v.integer = view->status; break; default: abort(); } return (SNMP_ERR_NOERROR); } static void vacm_append_userindex(struct asn_oid *oid, uint sub, const struct vacm_user *user) { uint32_t i; oid->len = sub + strlen(user->secname) + 2; oid->subs[sub++] = user->sec_model; oid->subs[sub] = strlen(user->secname); for (i = 1; i <= strlen(user->secname); i++) oid->subs[sub + i] = user->secname[i - 1]; } static int vacm_user_index_decode(const struct asn_oid *oid, uint sub, int32_t *smodel, char *uname) { uint32_t i; *smodel = oid->subs[sub++]; if (oid->subs[sub] >= SNMP_ADM_STR32_SIZ) return (-1); for (i = 0; i < oid->subs[sub]; i++) uname[i] = oid->subs[sub + i + 1]; uname[i] = '\0'; return (0); } static struct vacm_user * vacm_get_user(const struct asn_oid *oid, uint sub) { int32_t smodel; char uname[SNMP_ADM_STR32_SIZ]; struct vacm_user *user; if (vacm_user_index_decode(oid, sub, &smodel, uname) < 0) return (NULL); for (user = vacm_first_user(); user != NULL; user = vacm_next_user(user)) if (strcmp(uname, user->secname) == 0 && user->sec_model == smodel) return (user); return (NULL); } static struct vacm_user * vacm_get_next_user(const struct asn_oid *oid, uint sub) { int32_t smodel; char uname[SNMP_ADM_STR32_SIZ]; struct vacm_user *user; if (oid->len - sub == 0) return (vacm_first_user()); if (vacm_user_index_decode(oid, sub, &smodel, uname) < 0) return (NULL); for (user = vacm_first_user(); user != NULL; user = vacm_next_user(user)) if (strcmp(uname, user->secname) == 0 && user->sec_model == smodel) return (vacm_next_user(user)); return (NULL); } static void vacm_append_access_rule_index(struct asn_oid *oid, uint sub, const struct vacm_access *acl) { uint32_t i; oid->len = sub + strlen(acl->group->groupname) + strlen(acl->ctx_prefix) + 4; oid->subs[sub] = strlen(acl->group->groupname); for (i = 1; i <= strlen(acl->group->groupname); i++) oid->subs[sub + i] = acl->group->groupname[i - 1]; sub += strlen(acl->group->groupname) + 1; oid->subs[sub] = strlen(acl->ctx_prefix); for (i = 1; i <= strlen(acl->ctx_prefix); i++) oid->subs[sub + i] = acl->ctx_prefix[i - 1]; sub += strlen(acl->ctx_prefix) + 1; oid->subs[sub++] = acl->sec_model; oid->subs[sub] = acl->sec_level; } static int vacm_access_rule_index_decode(const struct asn_oid *oid, uint sub, char *gname, char *cprefix, int32_t *smodel, int32_t *slevel) { uint32_t i; if (oid->subs[sub] >= SNMP_ADM_STR32_SIZ) return (-1); for (i = 0; i < oid->subs[sub]; i++) gname[i] = oid->subs[sub + i + 1]; gname[i] = '\0'; sub += strlen(gname) + 1; if (oid->subs[sub] >= SNMP_ADM_STR32_SIZ) return (-1); for (i = 0; i < oid->subs[sub]; i++) cprefix[i] = oid->subs[sub + i + 1]; cprefix[i] = '\0'; sub += strlen(cprefix) + 1; *smodel = oid->subs[sub++]; *slevel = oid->subs[sub]; return (0); } struct vacm_access * vacm_get_access_rule(const struct asn_oid *oid, uint sub) { int32_t smodel, slevel; char gname[SNMP_ADM_STR32_SIZ], prefix[SNMP_ADM_STR32_SIZ]; struct vacm_access *acl; if (vacm_access_rule_index_decode(oid, sub, gname, prefix, &smodel, &slevel) < 0) return (NULL); for (acl = vacm_first_access_rule(); acl != NULL; acl = vacm_next_access_rule(acl)) if (strcmp(gname, acl->group->groupname) == 0 && strcmp(prefix, acl->ctx_prefix) == 0 && smodel == acl->sec_model && slevel == acl->sec_level) return (acl); return (NULL); } struct vacm_access * vacm_get_next_access_rule(const struct asn_oid *oid __unused, uint sub __unused) { int32_t smodel, slevel; char gname[SNMP_ADM_STR32_SIZ], prefix[SNMP_ADM_STR32_SIZ]; struct vacm_access *acl; if (oid->len - sub == 0) return (vacm_first_access_rule()); if (vacm_access_rule_index_decode(oid, sub, gname, prefix, &smodel, &slevel) < 0) return (NULL); for (acl = vacm_first_access_rule(); acl != NULL; acl = vacm_next_access_rule(acl)) if (strcmp(gname, acl->group->groupname) == 0 && strcmp(prefix, acl->ctx_prefix) == 0 && smodel == acl->sec_model && slevel == acl->sec_model) return (vacm_next_access_rule(acl)); return (NULL); } static int vacm_view_index_decode(const struct asn_oid *oid, uint sub, char *vname, struct asn_oid *view_oid) { uint32_t i; int viod_off; if (oid->subs[sub] >= SNMP_ADM_STR32_SIZ) return (-1); for (i = 0; i < oid->subs[sub]; i++) vname[i] = oid->subs[sub + i + 1]; vname[i] = '\0'; viod_off = sub + oid->subs[sub] + 1; if ((view_oid->len = oid->subs[viod_off]) > ASN_MAXOIDLEN) return (-1); memcpy(&view_oid->subs[0], &oid->subs[viod_off + 1], view_oid->len * sizeof(view_oid->subs[0])); return (0); } static void vacm_append_viewindex(struct asn_oid *oid, uint sub, const struct vacm_view *view) { uint32_t i; oid->len = sub + strlen(view->viewname) + 1; oid->subs[sub] = strlen(view->viewname); for (i = 1; i <= strlen(view->viewname); i++) oid->subs[sub + i] = view->viewname[i - 1]; sub += strlen(view->viewname) + 1; oid->subs[sub] = view->subtree.len; oid->len++; asn_append_oid(oid, &view->subtree); } struct vacm_view * vacm_get_view(const struct asn_oid *oid, uint sub) { char vname[SNMP_ADM_STR32_SIZ]; struct asn_oid subtree; struct vacm_view *view; if (vacm_view_index_decode(oid, sub, vname, &subtree) < 0) return (NULL); for (view = vacm_first_view(); view != NULL; view = vacm_next_view(view)) if (strcmp(vname, view->viewname) == 0 && asn_compare_oid(&subtree, &view->subtree)== 0) return (view); return (NULL); } struct vacm_view * vacm_get_next_view(const struct asn_oid *oid, uint sub) { char vname[SNMP_ADM_STR32_SIZ]; struct asn_oid subtree; struct vacm_view *view; if (oid->len - sub == 0) return (vacm_first_view()); if (vacm_view_index_decode(oid, sub, vname, &subtree) < 0) return (NULL); for (view = vacm_first_view(); view != NULL; view = vacm_next_view(view)) if (strcmp(vname, view->viewname) == 0 && asn_compare_oid(&subtree, &view->subtree)== 0) return (vacm_next_view(view)); return (NULL); } static struct vacm_view * vacm_get_view_by_name(u_char *octets, u_int len) { struct vacm_view *view; for (view = vacm_first_view(); view != NULL; view = vacm_next_view(view)) if (strlen(view->viewname) == len && memcmp(octets, view->viewname, len) == 0) return (view); return (NULL); } static struct vacm_context * vacm_get_context(const struct asn_oid *oid, uint sub) { char cname[SNMP_ADM_STR32_SIZ]; size_t cnamelen; u_int index_count; struct vacm_context *vacm_ctx; if (oid->subs[sub] >= SNMP_ADM_STR32_SIZ) return (NULL); index_count = 0; index_count = SNMP_INDEX(index_count, 1); if (index_decode(oid, sub, index_count, &cname, &cnamelen)) return (NULL); for (vacm_ctx = vacm_first_context(); vacm_ctx != NULL; vacm_ctx = vacm_next_context(vacm_ctx)) if (strcmp(cname, vacm_ctx->ctxname) == 0) return (vacm_ctx); return (NULL); } static struct vacm_context * vacm_get_next_context(const struct asn_oid *oid, uint sub) { char cname[SNMP_ADM_STR32_SIZ]; size_t cnamelen; u_int index_count; struct vacm_context *vacm_ctx; if (oid->len - sub == 0) return (vacm_first_context()); if (oid->subs[sub] >= SNMP_ADM_STR32_SIZ) return (NULL); index_count = 0; index_count = SNMP_INDEX(index_count, 1); if (index_decode(oid, sub, index_count, &cname, &cnamelen)) return (NULL); for (vacm_ctx = vacm_first_context(); vacm_ctx != NULL; vacm_ctx = vacm_next_context(vacm_ctx)) if (strcmp(cname, vacm_ctx->ctxname) == 0) return (vacm_next_context(vacm_ctx)); return (NULL); } static void vacm_append_ctxindex(struct asn_oid *oid, uint sub, const struct vacm_context *ctx) { uint32_t i; oid->len = sub + strlen(ctx->ctxname) + 1; oid->subs[sub] = strlen(ctx->ctxname); for (i = 1; i <= strlen(ctx->ctxname); i++) oid->subs[sub + i] = ctx->ctxname[i - 1]; } /* * VACM snmp module initialization hook. * Returns 0 on success, < 0 on error. */ static int vacm_init(struct lmodule *mod, int argc __unused, char *argv[] __unused) { vacm_module = mod; vacm_lock = random(); vacm_groups_init(); /* XXX: TODO - initialize structures */ return (0); } /* * VACM snmp module finalization hook. */ static int vacm_fini(void) { /* XXX: TODO - cleanup */ vacm_flush_contexts(reg_vacm); or_unregister(reg_vacm); return (0); } /* * VACM snmp module start operation. */ static void vacm_start(void) { static char dflt_ctx[] = ""; reg_vacm = or_register(&oid_vacm, "The MIB module for managing SNMP View-based Access Control Model.", vacm_module); (void)vacm_add_context(dflt_ctx, reg_vacm); } static void vacm_dump(void) { struct vacm_context *vacmctx; struct vacm_user *vuser; struct vacm_access *vacl; struct vacm_view *view; static char oidbuf[ASN_OIDSTRLEN]; syslog(LOG_ERR, "\n"); syslog(LOG_ERR, "Context list:"); for (vacmctx = vacm_first_context(); vacmctx != NULL; vacmctx = vacm_next_context(vacmctx)) syslog(LOG_ERR, "Context \"%s\", module id %d", vacmctx->ctxname, vacmctx->regid); syslog(LOG_ERR, "VACM users:"); for (vuser = vacm_first_user(); vuser != NULL; vuser = vacm_next_user(vuser)) syslog(LOG_ERR, "Uname %s, Group %s, model %d", vuser->secname, vuser->group!= NULL?vuser->group->groupname:"Unknown", vuser->sec_model); syslog(LOG_ERR, "VACM Access rules:"); for (vacl = vacm_first_access_rule(); vacl != NULL; vacl = vacm_next_access_rule(vacl)) syslog(LOG_ERR, "Group %s, CtxPrefix %s, Model %d, Level %d, " "RV %s, WR %s, NV %s", vacl->group!=NULL? vacl->group->groupname:"Unknown", vacl->ctx_prefix, vacl->sec_model, vacl->sec_level, vacl->read_view!=NULL? vacl->read_view->viewname:"None", vacl->write_view!=NULL? vacl->write_view->viewname:"None", vacl->notify_view!=NULL? vacl->notify_view->viewname:"None"); syslog(LOG_ERR, "VACM Views:"); for (view = vacm_first_view(); view != NULL; view = vacm_next_view(view)) syslog(LOG_ERR, "View %s, Tree %s - %s", view->viewname, asn_oid2str_r(&view->subtree, oidbuf), view->exclude? "excluded":"included"); } -const char vacm_comment[] = \ +static const char vacm_comment[] = \ "This module implements SNMP View-based Access Control Model defined in RFC 3415."; +extern const struct snmp_module config; const struct snmp_module config = { .comment = vacm_comment, .init = vacm_init, .fini = vacm_fini, .start = vacm_start, .tree = vacm_ctree, .dump = vacm_dump, .tree_size = vacm_CTREE_SIZE, }; Index: head/contrib/bsnmp/snmp_vacm/vacm_tree.def =================================================================== --- head/contrib/bsnmp/snmp_vacm/vacm_tree.def (revision 335884) +++ head/contrib/bsnmp/snmp_vacm/vacm_tree.def (revision 335885) @@ -1,106 +1,89 @@ #- # 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$ # -#include "tc.def" - -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 -) +include "tc.def" (1 internet (6 snmpV2 (3 snmpModules (16 snmpVacmMIB (1 vacmMIBObjects (1 vacmContextTable (1 vacmContextEntry : OCTETSTRING op_vacm_context (1 vacmContextName OCTETSTRING GET) ) ) (2 vacmSecurityToGroupTable (1 vacmSecurityToGroupEntry : INTEGER OCTETSTRING op_vacm_security_to_group (1 vacmSecurityModel INTEGER) (2 vacmSecurityName OCTETSTRING) (3 vacmGroupName OCTETSTRING GET SET) (4 vacmSecurityToGroupStorageType StorageType GET SET) (5 vacmSecurityToGroupStatus RowStatus GET SET) ) ) (4 vacmAccessTable (1 vacmAccessEntry : OCTETSTRING OCTETSTRING INTEGER ENUM ( 1 noAuthNoPriv 2 authNoPriv 3 authPriv ) op_vacm_access (1 vacmAccessContextPrefix OCTETSTRING) (2 vacmAccessSecurityModel INTEGER) (3 vacmAccessSecurityLevel ENUM ( 1 noAuthNoPriv 2 authNoPriv 3 authPriv )) (4 vacmAccessContextMatch ENUM ( 1 exact 2 prefix ) GET SET) (5 vacmAccessReadViewName OCTETSTRING GET SET) (6 vacmAccessWriteViewName OCTETSTRING GET SET) (7 vacmAccessNotifyViewName OCTETSTRING GET SET) (8 vacmAccessStorageType StorageType GET SET) (9 vacmAccessStatus RowStatus GET SET) ) ) (5 vacmMIBViews (1 vacmViewSpinLock INTEGER op_vacm_view_lock GET SET) (2 vacmViewTreeFamilyTable (1 vacmViewTreeFamilyEntry : OCTETSTRING OID op_vacm_view (1 vacmViewTreeFamilyViewName OCTETSTRING) (2 vacmViewTreeFamilySubtree OID) (3 vacmViewTreeFamilyMask OCTETSTRING GET SET) (4 vacmViewTreeFamilyType ENUM ( 1 included 2 excluded ) GET SET) (5 vacmViewTreeFamilyStorageType StorageType GET SET) (6 vacmViewTreeFamilyStatus RowStatus GET SET) ) ) ) ) (2 vacmMIBConformance (1 vacmMIBCompliances ) (2 vacmMIBGroups ) ) ) ) ) ) Index: head/contrib/bsnmp/snmpd/main.c =================================================================== --- head/contrib/bsnmp/snmpd/main.c (revision 335884) +++ head/contrib/bsnmp/snmpd/main.c (revision 335885) @@ -1,3111 +1,3111 @@ /* * Copyright (c) 2001-2003 * Fraunhofer Institute for Open Communication Systems (FhG Fokus). * All rights reserved. * * Author: Harti Brandt * * Copyright (c) 2010 The FreeBSD Foundation * All rights reserved. * * Portions of this software were developed by Shteryana Sotirova Shopova * under sponsorship from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Begemot: bsnmp/snmpd/main.c,v 1.100 2006/02/14 09:04:20 brandt_h Exp $ * * SNMPd main stuff. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef USE_TCPWRAPPERS #include #include #endif #include "support.h" #include "snmpmod.h" #include "snmpd.h" #include "tree.h" #include "oid.h" #define PATH_PID "/var/run/%s.pid" #define PATH_CONFIG "/etc/%s.config" #define PATH_ENGINE "/var/%s.engine" uint64_t this_tick; /* start of processing of current packet (absolute) */ uint64_t start_tick; /* start of processing */ struct systemg systemg = { NULL, { 8, { 1, 3, 6, 1, 4, 1, 1115, 7352 }}, NULL, NULL, NULL, 64 + 8 + 4, 0 }; struct debug debug = { 0, /* dump_pdus */ LOG_DEBUG, /* log_pri */ 0, /* evdebug */ }; struct snmpd snmpd = { 2048, /* txbuf */ 2048, /* rxbuf */ 0, /* comm_dis */ 0, /* auth_traps */ {0, 0, 0, 0}, /* trap1addr */ VERS_ENABLE_ALL,/* version_enable */ }; struct snmpd_stats snmpd_stats; struct snmpd_usmstat snmpd_usmstats; /* snmpEngine */ struct snmp_engine snmpd_engine; /* snmpSerialNo */ int32_t snmp_serial_no; struct snmpd_target_stats snmpd_target_stats; /* search path for config files */ const char *syspath = PATH_SYSCONFIG; /* list of all loaded modules */ struct lmodules lmodules = TAILQ_HEAD_INITIALIZER(lmodules); /* list of loaded modules during start-up in the order they were loaded */ static struct lmodules modules_start = TAILQ_HEAD_INITIALIZER(modules_start); /* list of all known communities */ struct community_list community_list = TAILQ_HEAD_INITIALIZER(community_list); /* list of all known USM users */ static struct usm_userlist usm_userlist = SLIST_HEAD_INITIALIZER(usm_userlist); /* A list of all VACM users configured, including v1, v2c and v3 */ static struct vacm_userlist vacm_userlist = SLIST_HEAD_INITIALIZER(vacm_userlist); /* A list of all VACM groups */ static struct vacm_grouplist vacm_grouplist = SLIST_HEAD_INITIALIZER(vacm_grouplist); static struct vacm_group vacm_default_group = { .groupname = "", }; /* The list of configured access entries */ static struct vacm_accesslist vacm_accesslist = TAILQ_HEAD_INITIALIZER(vacm_accesslist); /* The list of configured views */ static struct vacm_viewlist vacm_viewlist = SLIST_HEAD_INITIALIZER(vacm_viewlist); /* The list of configured contexts */ static struct vacm_contextlist vacm_contextlist = SLIST_HEAD_INITIALIZER(vacm_contextlist); /* list of all installed object resources */ struct objres_list objres_list = TAILQ_HEAD_INITIALIZER(objres_list); /* community value generator */ static u_int next_community_index = 1; /* list of all known ranges */ struct idrange_list idrange_list = TAILQ_HEAD_INITIALIZER(idrange_list); /* identifier generator */ u_int next_idrange = 1; /* list of all current timers */ struct timer_list timer_list = LIST_HEAD_INITIALIZER(timer_list); /* list of file descriptors */ struct fdesc_list fdesc_list = LIST_HEAD_INITIALIZER(fdesc_list); /* program arguments */ static char **progargs; static int nprogargs; /* current community */ u_int community; static struct community *comm; /* current USM user */ struct usm_user *usm_user; /* file names */ static char config_file[MAXPATHLEN + 1]; static char pid_file[MAXPATHLEN + 1]; char engine_file[MAXPATHLEN + 1]; #ifndef USE_LIBBEGEMOT /* event context */ static evContext evctx; #endif /* signal mask */ static sigset_t blocked_sigs; /* signal handling */ static int work; #define WORK_DOINFO 0x0001 #define WORK_RECONFIG 0x0002 /* oids */ static const struct asn_oid oid_snmpMIB = OIDX_snmpMIB, oid_begemotSnmpd = OIDX_begemotSnmpd, oid_coldStart = OIDX_coldStart, oid_authenticationFailure = OIDX_authenticationFailure; const struct asn_oid oid_zeroDotZero = { 2, { 0, 0 }}; const struct asn_oid oid_usmUnknownEngineIDs = { 11, { 1, 3, 6, 1, 6, 3, 15, 1, 1, 4, 0}}; const struct asn_oid oid_usmNotInTimeWindows = { 11, { 1, 3, 6, 1, 6, 3, 15, 1, 1, 2, 0}}; /* request id generator for traps */ u_int trap_reqid; /* help text */ static const char usgtxt[] = "\ Begemot simple SNMP daemon. Copyright (c) 2001-2002 Fraunhofer Institute for\n\ Open Communication Systems (FhG Fokus). All rights reserved.\n\ Copyright (c) 2010 The FreeBSD Foundation. All rights reserved.\n\ usage: snmpd [-dh] [-c file] [-D options] [-e file] [-I path]\n\ [-l prefix] [-m variable=value] [-p file]\n\ options:\n\ -d don't daemonize\n\ -h print this info\n\ -c file specify configuration file\n\ -D options debugging options\n\ -e file specify engine id file\n\ -I path system include path\n\ -l prefix default basename for pid and config file\n\ -m var=val define variable\n\ -p file specify pid file\n\ "; /* hosts_access(3) request */ #ifdef USE_TCPWRAPPERS static struct request_info req; #endif /* transports */ extern const struct transport_def udp_trans; extern const struct transport_def lsock_trans; struct transport_list transport_list = TAILQ_HEAD_INITIALIZER(transport_list); /* forward declarations */ static void snmp_printf_func(const char *fmt, ...); static void snmp_error_func(const char *err, ...); static void snmp_debug_func(const char *err, ...); static void asn_error_func(const struct asn_buf *b, const char *err, ...); /* * Allocate rx/tx buffer. We allocate one byte more for rx. */ void * buf_alloc(int tx) { void *buf; if ((buf = malloc(tx ? snmpd.txbuf : snmpd.rxbuf)) == NULL) { syslog(LOG_CRIT, "cannot allocate buffer"); if (tx) snmpd_stats.noTxbuf++; else snmpd_stats.noRxbuf++; return (NULL); } return (buf); } /* * Return the buffer size. */ size_t buf_size(int tx) { return (tx ? snmpd.txbuf : snmpd.rxbuf); } /* * Prepare a PDU for output */ void snmp_output(struct snmp_pdu *pdu, u_char *sndbuf, size_t *sndlen, const char *dest) { struct asn_buf resp_b; enum snmp_code code; resp_b.asn_ptr = sndbuf; resp_b.asn_len = snmpd.txbuf; if ((code = snmp_pdu_encode(pdu, &resp_b)) != SNMP_CODE_OK) { syslog(LOG_ERR, "cannot encode message (code=%d)", code); abort(); } if (debug.dump_pdus) { snmp_printf("%s <- ", dest); snmp_pdu_dump(pdu); } *sndlen = (size_t)(resp_b.asn_ptr - sndbuf); } /* * Check USM PDU header credentials against local SNMP Engine & users. */ static enum snmp_code snmp_pdu_auth_user(struct snmp_pdu *pdu) { usm_user = NULL; /* un-authenticated snmpEngineId discovery */ if (pdu->engine.engine_len == 0 && strlen(pdu->user.sec_name) == 0) { pdu->engine.engine_len = snmpd_engine.engine_len; memcpy(pdu->engine.engine_id, snmpd_engine.engine_id, snmpd_engine.engine_len); update_snmpd_engine_time(); pdu->engine.engine_boots = snmpd_engine.engine_boots; pdu->engine.engine_time = snmpd_engine.engine_time; pdu->flags |= SNMP_MSG_AUTODISCOVER; return (SNMP_CODE_OK); } if ((usm_user = usm_find_user(pdu->engine.engine_id, pdu->engine.engine_len, pdu->user.sec_name)) == NULL || usm_user->status != 1 /* active */) return (SNMP_CODE_BADUSER); if (usm_user->user_engine_len != snmpd_engine.engine_len || memcmp(usm_user->user_engine_id, snmpd_engine.engine_id, snmpd_engine.engine_len) != 0) return (SNMP_CODE_BADENGINE); pdu->user.priv_proto = usm_user->suser.priv_proto; memcpy(pdu->user.priv_key, usm_user->suser.priv_key, sizeof(pdu->user.priv_key)); /* authenticated snmpEngineId discovery */ if ((pdu->flags & SNMP_MSG_AUTH_FLAG) != 0) { update_snmpd_engine_time(); pdu->user.auth_proto = usm_user->suser.auth_proto; memcpy(pdu->user.auth_key, usm_user->suser.auth_key, sizeof(pdu->user.auth_key)); if (pdu->engine.engine_boots == 0 && pdu->engine.engine_time == 0) { update_snmpd_engine_time(); pdu->flags |= SNMP_MSG_AUTODISCOVER; return (SNMP_CODE_OK); } if (pdu->engine.engine_boots != snmpd_engine.engine_boots || abs(pdu->engine.engine_time - snmpd_engine.engine_time) > SNMP_TIME_WINDOW) return (SNMP_CODE_NOTINTIME); } if (((pdu->flags & SNMP_MSG_PRIV_FLAG) != 0 && (pdu->flags & SNMP_MSG_AUTH_FLAG) == 0) || ((pdu->flags & SNMP_MSG_AUTH_FLAG) == 0 && usm_user->suser.auth_proto != SNMP_AUTH_NOAUTH) || ((pdu->flags & SNMP_MSG_PRIV_FLAG) == 0 && usm_user->suser.priv_proto != SNMP_PRIV_NOPRIV)) return (SNMP_CODE_BADSECLEVEL); return (SNMP_CODE_OK); } /* * Check whether access to each of var bindings in the PDU is allowed based * on the user credentials against the configured User groups & VACM views. */ enum snmp_code snmp_pdu_auth_access(struct snmp_pdu *pdu, int32_t *ip) { const char *uname; int32_t suboid, smodel; uint32_t i; struct vacm_user *vuser; struct vacm_access *acl; struct vacm_context *vacmctx; struct vacm_view *view; /* * At least a default context exists if the snmpd_vacm(3) module is * running. */ if (SLIST_EMPTY(&vacm_contextlist) || (pdu->flags & SNMP_MSG_AUTODISCOVER) != 0) return (SNMP_CODE_OK); switch (pdu->version) { case SNMP_V1: if ((uname = comm_string(community)) == NULL) return (SNMP_CODE_FAILED); smodel = SNMP_SECMODEL_SNMPv1; break; case SNMP_V2c: if ((uname = comm_string(community)) == NULL) return (SNMP_CODE_FAILED); smodel = SNMP_SECMODEL_SNMPv2c; break; case SNMP_V3: uname = pdu->user.sec_name; if ((smodel = pdu->security_model) != SNMP_SECMODEL_USM) return (SNMP_CODE_FAILED); /* Compare the PDU context engine id against the agent's */ if (pdu->context_engine_len != snmpd_engine.engine_len || memcmp(pdu->context_engine, snmpd_engine.engine_id, snmpd_engine.engine_len) != 0) return (SNMP_CODE_FAILED); break; default: abort(); } SLIST_FOREACH(vuser, &vacm_userlist, vvu) if (strcmp(uname, vuser->secname) == 0 && vuser->sec_model == smodel) break; if (vuser == NULL || vuser->group == NULL) return (SNMP_CODE_FAILED); /* XXX: shteryana - recheck */ TAILQ_FOREACH_REVERSE(acl, &vacm_accesslist, vacm_accesslist, vva) { if (acl->group != vuser->group) continue; SLIST_FOREACH(vacmctx, &vacm_contextlist, vcl) if (memcmp(vacmctx->ctxname, acl->ctx_prefix, acl->ctx_match) == 0) goto match; } return (SNMP_CODE_FAILED); match: switch (pdu->type) { case SNMP_PDU_GET: case SNMP_PDU_GETNEXT: case SNMP_PDU_GETBULK: if ((view = acl->read_view) == NULL) return (SNMP_CODE_FAILED); break; case SNMP_PDU_SET: if ((view = acl->write_view) == NULL) return (SNMP_CODE_FAILED); break; case SNMP_PDU_TRAP: case SNMP_PDU_INFORM: case SNMP_PDU_TRAP2: case SNMP_PDU_REPORT: if ((view = acl->notify_view) == NULL) return (SNMP_CODE_FAILED); break; case SNMP_PDU_RESPONSE: /* NOTREACHED */ return (SNMP_CODE_FAILED); default: abort(); } for (i = 0; i < pdu->nbindings; i++) { /* XXX - view->mask*/ suboid = asn_is_suboid(&view->subtree, &pdu->bindings[i].var); if ((!suboid && !view->exclude) || (suboid && view->exclude)) { *ip = i + 1; return (SNMP_CODE_FAILED); } } return (SNMP_CODE_OK); } /* * SNMP input. Start: decode the PDU, find the user or community. */ enum snmpd_input_err snmp_input_start(const u_char *buf, size_t len, const char *source, struct snmp_pdu *pdu, int32_t *ip, size_t *pdulen) { struct asn_buf b; enum snmp_code code; enum snmpd_input_err ret; int sret; /* update uptime */ this_tick = get_ticks(); b.asn_cptr = buf; b.asn_len = len; 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, POLL_IN)) < 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]; #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 (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 */ void snmp_send_port(void *targ, const struct asn_oid *port, struct snmp_pdu *pdu, const struct sockaddr *addr, socklen_t addrlen) { struct transport *trans = targ; struct tport *tp; u_char *sndbuf; size_t sndlen; ssize_t len; TAILQ_FOREACH(tp, &trans->table, link) if (asn_compare_oid(port, &tp->index) == 0) break; if (tp == 0) return; if ((sndbuf = buf_alloc(1)) == NULL) return; snmp_output(pdu, sndbuf, &sndlen, "SNMP PROXY"); len = trans->vtab->send(tp, sndbuf, sndlen, addr, addrlen); if (len == -1) syslog(LOG_ERR, "sendto: %m"); else if ((size_t)len != sndlen) syslog(LOG_ERR, "sendto: short write %zu/%zu", sndlen, (size_t)len); free(sndbuf); } /* * Close an input source */ void snmpd_input_close(struct port_input *pi) { if (pi->id != NULL) fd_deselect(pi->id); if (pi->fd >= 0) (void)close(pi->fd); if (pi->buf != NULL) free(pi->buf); } /* * Dump internal state. */ #ifdef USE_LIBBEGEMOT static void info_func(void) #else static void info_func(evContext ctx __unused, void *uap __unused, const void *tag __unused) #endif { struct lmodule *m; u_int i; char buf[10000]; syslog(LOG_DEBUG, "Dump of SNMPd %lu\n", (u_long)getpid()); for (i = 0; i < tree_size; i++) { switch (tree[i].type) { case SNMP_NODE_LEAF: sprintf(buf, "LEAF: %s %s", tree[i].name, asn_oid2str(&tree[i].oid)); break; case SNMP_NODE_COLUMN: sprintf(buf, "COL: %s %s", tree[i].name, asn_oid2str(&tree[i].oid)); break; } syslog(LOG_DEBUG, "%s", buf); } TAILQ_FOREACH(m, &lmodules, link) if (m->config->dump) (*m->config->dump)(); } /* * Re-read configuration */ #ifdef USE_LIBBEGEMOT static void config_func(void) #else static void config_func(evContext ctx __unused, void *uap __unused, const void *tag __unused) #endif { struct lmodule *m; if (read_config(config_file, NULL)) { syslog(LOG_ERR, "error reading config file '%s'", config_file); return; } TAILQ_FOREACH(m, &lmodules, link) if (m->config->config) (*m->config->config)(); } /* * On USR1 dump actual configuration. */ static void onusr1(int s __unused) { work |= WORK_DOINFO; } static void onhup(int s __unused) { work |= WORK_RECONFIG; } static void onterm(int s __unused) { /* allow clean-up */ exit(0); } static void init_sigs(void) { struct sigaction sa; sa.sa_handler = onusr1; sa.sa_flags = SA_RESTART; sigemptyset(&sa.sa_mask); if (sigaction(SIGUSR1, &sa, NULL)) { syslog(LOG_ERR, "sigaction: %m"); exit(1); } sa.sa_handler = onhup; if (sigaction(SIGHUP, &sa, NULL)) { syslog(LOG_ERR, "sigaction: %m"); exit(1); } sa.sa_handler = onterm; sa.sa_flags = 0; sigemptyset(&sa.sa_mask); if (sigaction(SIGTERM, &sa, NULL)) { syslog(LOG_ERR, "sigaction: %m"); exit(1); } if (sigaction(SIGINT, &sa, NULL)) { syslog(LOG_ERR, "sigaction: %m"); exit(1); } } static void block_sigs(void) { sigset_t set; sigfillset(&set); if (sigprocmask(SIG_BLOCK, &set, &blocked_sigs) == -1) { syslog(LOG_ERR, "SIG_BLOCK: %m"); exit(1); } } static void unblock_sigs(void) { if (sigprocmask(SIG_SETMASK, &blocked_sigs, NULL) == -1) { syslog(LOG_ERR, "SIG_SETMASK: %m"); exit(1); } } /* * Shut down */ static void term(void) { (void)unlink(pid_file); } static void trans_stop(void) { struct transport *t; TAILQ_FOREACH(t, &transport_list, link) (void)t->vtab->stop(1); } /* * Define a macro from the command line */ static void do_macro(char *arg) { char *eq; int err; if ((eq = strchr(arg, '=')) == NULL) err = define_macro(arg, ""); else { *eq++ = '\0'; err = define_macro(arg, eq); } if (err == -1) { syslog(LOG_ERR, "cannot save macro: %m"); exit(1); } } /* * Re-implement getsubopt from scratch, because the second argument is broken * and will not compile with WARNS=5. */ static int getsubopt1(char **arg, const char *const *options, char **valp, char **optp) { static const char *const delim = ",\t "; u_int i; char *ptr; *optp = NULL; /* skip leading junk */ for (ptr = *arg; *ptr != '\0'; ptr++) if (strchr(delim, *ptr) == NULL) break; if (*ptr == '\0') { *arg = ptr; return (-1); } *optp = ptr; /* find the end of the option */ while (*++ptr != '\0') if (strchr(delim, *ptr) != NULL || *ptr == '=') break; if (*ptr != '\0') { if (*ptr == '=') { *ptr++ = '\0'; *valp = ptr; while (*ptr != '\0' && strchr(delim, *ptr) == NULL) ptr++; if (*ptr != '\0') *ptr++ = '\0'; } else *ptr++ = '\0'; } *arg = ptr; for (i = 0; *options != NULL; options++, i++) if (strcmp(*optp, *options) == 0) return (i); return (-1); } int main(int argc, char *argv[]) { int opt; FILE *fp; int background = 1; struct tport *p; const char *prefix = "snmpd"; struct lmodule *m; char *value = NULL, *option; /* XXX */ struct transport *t; #define DBG_DUMP 0 #define DBG_EVENTS 1 #define DBG_TRACE 2 static const char *const debug_opts[] = { "dump", "events", "trace", NULL }; snmp_printf = snmp_printf_func; snmp_error = snmp_error_func; snmp_debug = snmp_debug_func; asn_error = asn_error_func; while ((opt = getopt(argc, argv, "c:dD:e:hI:l:m:p:")) != EOF) switch (opt) { case 'c': strlcpy(config_file, optarg, sizeof(config_file)); break; case 'd': background = 0; break; case 'D': while (*optarg) { switch (getsubopt1(&optarg, debug_opts, &value, &option)) { case DBG_DUMP: debug.dump_pdus = 1; break; case DBG_EVENTS: debug.evdebug++; break; case DBG_TRACE: if (value == NULL) syslog(LOG_ERR, "no value for 'trace'"); else snmp_trace = strtoul(value, NULL, 0); break; case -1: if (suboptarg) syslog(LOG_ERR, "unknown debug flag '%s'", option); else syslog(LOG_ERR, "missing debug flag"); break; } } break; case 'e': strlcpy(engine_file, optarg, sizeof(engine_file)); break; case 'h': fprintf(stderr, "%s", usgtxt); exit(0); case 'I': syspath = optarg; break; case 'l': prefix = optarg; break; case 'm': do_macro(optarg); break; case 'p': strlcpy(pid_file, optarg, sizeof(pid_file)); break; } openlog(prefix, LOG_PID | (background ? 0 : LOG_PERROR), LOG_USER); setlogmask(LOG_UPTO(debug.logpri - 1)); if (background && daemon(0, 0) < 0) { syslog(LOG_ERR, "daemon: %m"); exit(1); } argc -= optind; argv += optind; progargs = argv; nprogargs = argc; srandomdev(); snmp_serial_no = random(); #ifdef USE_TCPWRAPPERS /* * Initialize hosts_access(3) handler. */ request_init(&req, RQ_DAEMON, "snmpd", 0); sock_methods(&req); #endif /* * Initialize the tree. */ if ((tree = malloc(sizeof(struct snmp_node) * CTREE_SIZE)) == NULL) { syslog(LOG_ERR, "%m"); exit(1); } memcpy(tree, ctree, sizeof(struct snmp_node) * CTREE_SIZE); tree_size = CTREE_SIZE; /* * Get standard communities */ 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"); #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/trans_udp.c =================================================================== --- head/contrib/bsnmp/snmpd/trans_udp.c (revision 335884) +++ head/contrib/bsnmp/snmpd/trans_udp.c (revision 335885) @@ -1,438 +1,438 @@ /* * 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 }; 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 *)CMSG_DATA(cmsg); + 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/trap.c =================================================================== --- head/contrib/bsnmp/snmpd/trap.c (revision 335884) +++ head/contrib/bsnmp/snmpd/trap.c (revision 335885) @@ -1,909 +1,911 @@ /* * Copyright (c) 2001-2003 * Fraunhofer Institute for Open Communication Systems (FhG Fokus). * All rights reserved. * * Author: Harti Brandt * * Copyright (c) 2010 The FreeBSD Foundation * All rights reserved. * * Portions of this software were developed by Shteryana Sotirova Shopova * under sponsorship from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Begemot: bsnmp/snmpd/trap.c,v 1.9 2005/10/04 11:21:39 brandt_h Exp $ * * TrapSinkTable */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "snmpmod.h" #include "snmpd.h" + +#define SNMPTREE_TYPES #include "tree.h" #include "oid.h" struct trapsink_list trapsink_list = TAILQ_HEAD_INITIALIZER(trapsink_list); /* List of target addresses */ static struct target_addresslist target_addresslist = SLIST_HEAD_INITIALIZER(target_addresslist); /* List of target parameters */ static struct target_paramlist target_paramlist = SLIST_HEAD_INITIALIZER(target_paramlist); /* List of notification targets */ static struct target_notifylist target_notifylist = SLIST_HEAD_INITIALIZER(target_notifylist); static const struct asn_oid oid_begemotTrapSinkTable = OIDX_begemotTrapSinkTable; static const struct asn_oid oid_sysUpTime = OIDX_sysUpTime; static const struct asn_oid oid_snmpTrapOID = OIDX_snmpTrapOID; struct trapsink_dep { struct snmp_dependency dep; u_int set; u_int status; u_char comm[SNMP_COMMUNITY_MAXLEN + 1]; u_int version; u_int rb; u_int rb_status; u_int rb_version; u_char rb_comm[SNMP_COMMUNITY_MAXLEN + 1]; }; enum { TDEP_STATUS = 0x0001, TDEP_COMM = 0x0002, TDEP_VERSION = 0x0004, TDEP_CREATE = 0x0001, TDEP_MODIFY = 0x0002, TDEP_DESTROY = 0x0004, }; static int trapsink_create(struct trapsink_dep *tdep) { struct trapsink *t; struct sockaddr_in sa; if ((t = malloc(sizeof(*t))) == NULL) return (SNMP_ERR_RES_UNAVAIL); t->index = tdep->dep.idx; t->status = TRAPSINK_NOT_READY; t->comm[0] = '\0'; t->version = TRAPSINK_V2; if ((t->socket = socket(PF_INET, SOCK_DGRAM, 0)) == -1) { syslog(LOG_ERR, "socket(UDP): %m"); free(t); return (SNMP_ERR_RES_UNAVAIL); } (void)shutdown(t->socket, SHUT_RD); memset(&sa, 0, sizeof(sa)); sa.sin_len = sizeof(sa); sa.sin_family = AF_INET; sa.sin_addr.s_addr = htonl((t->index.subs[0] << 24) | (t->index.subs[1] << 16) | (t->index.subs[2] << 8) | (t->index.subs[3] << 0)); sa.sin_port = htons(t->index.subs[4]); if (connect(t->socket, (struct sockaddr *)&sa, sa.sin_len) == -1) { syslog(LOG_ERR, "connect(%s,%u): %m", inet_ntoa(sa.sin_addr), ntohs(sa.sin_port)); (void)close(t->socket); free(t); return (SNMP_ERR_GENERR); } if (tdep->set & TDEP_VERSION) t->version = tdep->version; if (tdep->set & TDEP_COMM) strcpy(t->comm, tdep->comm); if (t->comm[0] != '\0') t->status = TRAPSINK_NOT_IN_SERVICE; /* look whether we should activate */ if (tdep->status == 4) { if (t->status == TRAPSINK_NOT_READY) { if (t->socket != -1) (void)close(t->socket); free(t); return (SNMP_ERR_INCONS_VALUE); } t->status = TRAPSINK_ACTIVE; } INSERT_OBJECT_OID(t, &trapsink_list); tdep->rb |= TDEP_CREATE; return (SNMP_ERR_NOERROR); } static void trapsink_free(struct trapsink *t) { TAILQ_REMOVE(&trapsink_list, t, link); if (t->socket != -1) (void)close(t->socket); free(t); } static int trapsink_modify(struct trapsink *t, struct trapsink_dep *tdep) { tdep->rb_status = t->status; tdep->rb_version = t->version; strcpy(tdep->rb_comm, t->comm); if (tdep->set & TDEP_STATUS) { /* if we are active and should move to not_in_service do * this first */ if (tdep->status == 2 && tdep->rb_status == TRAPSINK_ACTIVE) { t->status = TRAPSINK_NOT_IN_SERVICE; tdep->rb |= TDEP_MODIFY; } } if (tdep->set & TDEP_VERSION) t->version = tdep->version; if (tdep->set & TDEP_COMM) strcpy(t->comm, tdep->comm); if (tdep->set & TDEP_STATUS) { /* if we were inactive and should go active - do this now */ if (tdep->status == 1 && tdep->rb_status != TRAPSINK_ACTIVE) { if (t->comm[0] == '\0') { t->status = tdep->rb_status; t->version = tdep->rb_version; strcpy(t->comm, tdep->rb_comm); return (SNMP_ERR_INCONS_VALUE); } t->status = TRAPSINK_ACTIVE; tdep->rb |= TDEP_MODIFY; } } return (SNMP_ERR_NOERROR); } static int trapsink_unmodify(struct trapsink *t, struct trapsink_dep *tdep) { if (tdep->set & TDEP_STATUS) t->status = tdep->rb_status; if (tdep->set & TDEP_VERSION) t->version = tdep->rb_version; if (tdep->set & TDEP_COMM) strcpy(t->comm, tdep->rb_comm); return (SNMP_ERR_NOERROR); } static int trapsink_destroy(struct snmp_context *ctx __unused, struct trapsink *t, struct trapsink_dep *tdep) { t->status = TRAPSINK_DESTROY; tdep->rb_status = t->status; tdep->rb |= TDEP_DESTROY; return (SNMP_ERR_NOERROR); } static int trapsink_undestroy(struct trapsink *t, struct trapsink_dep *tdep) { t->status = tdep->rb_status; return (SNMP_ERR_NOERROR); } static int trapsink_dep(struct snmp_context *ctx, struct snmp_dependency *dep, enum snmp_depop op) { struct trapsink_dep *tdep = (struct trapsink_dep *)dep; struct trapsink *t; t = FIND_OBJECT_OID(&trapsink_list, &dep->idx, 0); switch (op) { case SNMP_DEPOP_COMMIT: if (tdep->set & TDEP_STATUS) { switch (tdep->status) { case 1: case 2: if (t == NULL) return (SNMP_ERR_INCONS_VALUE); return (trapsink_modify(t, tdep)); case 4: case 5: if (t != NULL) return (SNMP_ERR_INCONS_VALUE); return (trapsink_create(tdep)); case 6: if (t == NULL) return (SNMP_ERR_NOERROR); return (trapsink_destroy(ctx, t, tdep)); } } else if (tdep->set != 0) return (trapsink_modify(t, tdep)); return (SNMP_ERR_NOERROR); case SNMP_DEPOP_ROLLBACK: if (tdep->rb & TDEP_CREATE) { trapsink_free(t); return (SNMP_ERR_NOERROR); } if (tdep->rb & TDEP_MODIFY) return (trapsink_unmodify(t, tdep)); if(tdep->rb & TDEP_DESTROY) return (trapsink_undestroy(t, tdep)); return (SNMP_ERR_NOERROR); case SNMP_DEPOP_FINISH: if ((tdep->rb & TDEP_DESTROY) && t != NULL && ctx->code == SNMP_RET_OK) trapsink_free(t); return (SNMP_ERR_NOERROR); } abort(); } int op_trapsink(struct snmp_context *ctx, struct snmp_value *value, u_int sub, u_int iidx, enum snmp_op op) { struct trapsink *t; u_char ipa[4]; int32_t port; struct asn_oid idx; struct trapsink_dep *tdep; u_char *p; t = NULL; /* gcc */ switch (op) { case SNMP_OP_GETNEXT: if ((t = NEXT_OBJECT_OID(&trapsink_list, &value->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); index_append(&value->var, sub, &t->index); break; case SNMP_OP_GET: if ((t = FIND_OBJECT_OID(&trapsink_list, &value->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_SET: if (index_decode(&value->var, sub, iidx, ipa, &port) || port == 0 || port > 65535) return (SNMP_ERR_NO_CREATION); t = FIND_OBJECT_OID(&trapsink_list, &value->var, sub); asn_slice_oid(&idx, &value->var, sub, value->var.len); tdep = (struct trapsink_dep *)snmp_dep_lookup(ctx, &oid_begemotTrapSinkTable, &idx, sizeof(*tdep), trapsink_dep); if (tdep == NULL) return (SNMP_ERR_RES_UNAVAIL); switch (value->var.subs[sub - 1]) { case LEAF_begemotTrapSinkStatus: if (tdep->set & TDEP_STATUS) return (SNMP_ERR_INCONS_VALUE); switch (value->v.integer) { case 1: case 2: if (t == NULL) return (SNMP_ERR_INCONS_VALUE); break; case 4: case 5: if (t != NULL) return (SNMP_ERR_INCONS_VALUE); break; case 6: break; default: return (SNMP_ERR_WRONG_VALUE); } tdep->status = value->v.integer; tdep->set |= TDEP_STATUS; return (SNMP_ERR_NOERROR); case LEAF_begemotTrapSinkComm: if (tdep->set & TDEP_COMM) return (SNMP_ERR_INCONS_VALUE); if (value->v.octetstring.len == 0 || value->v.octetstring.len > SNMP_COMMUNITY_MAXLEN) return (SNMP_ERR_WRONG_VALUE); for (p = value->v.octetstring.octets; p < value->v.octetstring.octets + value->v.octetstring.len; p++) { if (!isascii(*p) || !isprint(*p)) return (SNMP_ERR_WRONG_VALUE); } tdep->set |= TDEP_COMM; strncpy(tdep->comm, value->v.octetstring.octets, value->v.octetstring.len); tdep->comm[value->v.octetstring.len] = '\0'; return (SNMP_ERR_NOERROR); case LEAF_begemotTrapSinkVersion: if (tdep->set & TDEP_VERSION) return (SNMP_ERR_INCONS_VALUE); if (value->v.integer != TRAPSINK_V1 && value->v.integer != TRAPSINK_V2) return (SNMP_ERR_WRONG_VALUE); tdep->version = value->v.integer; tdep->set |= TDEP_VERSION; return (SNMP_ERR_NOERROR); } if (t == NULL) return (SNMP_ERR_INCONS_NAME); else return (SNMP_ERR_NOT_WRITEABLE); case SNMP_OP_ROLLBACK: case SNMP_OP_COMMIT: return (SNMP_ERR_NOERROR); } switch (value->var.subs[sub - 1]) { case LEAF_begemotTrapSinkStatus: value->v.integer = t->status; break; case LEAF_begemotTrapSinkComm: return (string_get(value, t->comm, -1)); case LEAF_begemotTrapSinkVersion: value->v.integer = t->version; break; } return (SNMP_ERR_NOERROR); } static void snmp_create_v1_trap(struct snmp_pdu *pdu, char *com, const struct asn_oid *trap_oid) { memset(pdu, 0, sizeof(*pdu)); strlcpy(pdu->community, com, sizeof(pdu->community)); pdu->version = SNMP_V1; pdu->type = SNMP_PDU_TRAP; pdu->enterprise = systemg.object_id; memcpy(pdu->agent_addr, snmpd.trap1addr, 4); pdu->generic_trap = trap_oid->subs[trap_oid->len - 1] - 1; pdu->specific_trap = 0; pdu->time_stamp = get_ticks() - start_tick; pdu->nbindings = 0; } static void snmp_create_v2_trap(struct snmp_pdu *pdu, char *com, const struct asn_oid *trap_oid) { memset(pdu, 0, sizeof(*pdu)); strlcpy(pdu->community, com, sizeof(pdu->community)); pdu->version = SNMP_V2c; pdu->type = SNMP_PDU_TRAP2; pdu->request_id = reqid_next(trap_reqid); pdu->error_index = 0; pdu->error_status = SNMP_ERR_NOERROR; pdu->bindings[0].var = oid_sysUpTime; pdu->bindings[0].var.subs[pdu->bindings[0].var.len++] = 0; pdu->bindings[0].syntax = SNMP_SYNTAX_TIMETICKS; pdu->bindings[0].v.uint32 = get_ticks() - start_tick; pdu->bindings[1].var = oid_snmpTrapOID; pdu->bindings[1].var.subs[pdu->bindings[1].var.len++] = 0; pdu->bindings[1].syntax = SNMP_SYNTAX_OID; pdu->bindings[1].v.oid = *trap_oid; pdu->nbindings = 2; } static void snmp_create_v3_trap(struct snmp_pdu *pdu, struct target_param *target, const struct asn_oid *trap_oid) { struct usm_user *usmuser; memset(pdu, 0, sizeof(*pdu)); pdu->version = SNMP_V3; pdu->type = SNMP_PDU_TRAP2; pdu->request_id = reqid_next(trap_reqid); pdu->error_index = 0; pdu->error_status = SNMP_ERR_NOERROR; pdu->bindings[0].var = oid_sysUpTime; pdu->bindings[0].var.subs[pdu->bindings[0].var.len++] = 0; pdu->bindings[0].syntax = SNMP_SYNTAX_TIMETICKS; pdu->bindings[0].v.uint32 = get_ticks() - start_tick; pdu->bindings[1].var = oid_snmpTrapOID; pdu->bindings[1].var.subs[pdu->bindings[1].var.len++] = 0; pdu->bindings[1].syntax = SNMP_SYNTAX_OID; pdu->bindings[1].v.oid = *trap_oid; pdu->nbindings = 2; update_snmpd_engine_time(); memcpy(pdu->engine.engine_id, snmpd_engine.engine_id, snmpd_engine.engine_len); pdu->engine.engine_len = snmpd_engine.engine_len; pdu->engine.engine_boots = snmpd_engine.engine_boots; pdu->engine.engine_time = snmpd_engine.engine_time; pdu->engine.max_msg_size = snmpd_engine.max_msg_size; strlcpy(pdu->user.sec_name, target->secname, sizeof(pdu->user.sec_name)); pdu->security_model = target->sec_model; pdu->context_engine_len = snmpd_engine.engine_len; memcpy(pdu->context_engine, snmpd_engine.engine_id, snmpd_engine.engine_len); if (target->sec_model == SNMP_SECMODEL_USM && target->sec_level != SNMP_noAuthNoPriv) { usmuser = usm_find_user(pdu->engine.engine_id, pdu->engine.engine_len, pdu->user.sec_name); if (usmuser != NULL) { pdu->user.auth_proto = usmuser->suser.auth_proto; pdu->user.priv_proto = usmuser->suser.priv_proto; memcpy(pdu->user.auth_key, usmuser->suser.auth_key, sizeof(pdu->user.auth_key)); memcpy(pdu->user.priv_key, usmuser->suser.priv_key, sizeof(pdu->user.priv_key)); } snmp_pdu_init_secparams(pdu); } } void snmp_send_trap(const struct asn_oid *trap_oid, ...) { struct snmp_pdu pdu; struct trapsink *t; const struct snmp_value *v; struct target_notify *n; struct target_address *ta; struct target_param *tp; va_list ap; u_char *sndbuf; char *tag; size_t sndlen; ssize_t len; int32_t ip; TAILQ_FOREACH(t, &trapsink_list, link) { if (t->status != TRAPSINK_ACTIVE) continue; if (t->version == TRAPSINK_V1) snmp_create_v1_trap(&pdu, t->comm, trap_oid); else snmp_create_v2_trap(&pdu, t->comm, trap_oid); va_start(ap, trap_oid); while ((v = va_arg(ap, const struct snmp_value *)) != NULL) pdu.bindings[pdu.nbindings++] = *v; va_end(ap); if (snmp_pdu_auth_access(&pdu, &ip) != SNMP_CODE_OK) { syslog(LOG_DEBUG, "send trap to %s failed: no access", t->comm); continue; } if ((sndbuf = buf_alloc(1)) == NULL) { syslog(LOG_ERR, "trap send buffer: %m"); return; } snmp_output(&pdu, sndbuf, &sndlen, "TRAP"); if ((len = send(t->socket, sndbuf, sndlen, 0)) == -1) syslog(LOG_ERR, "send: %m"); else if ((size_t)len != sndlen) syslog(LOG_ERR, "send: short write %zu/%zu", sndlen, (size_t)len); free(sndbuf); } SLIST_FOREACH(n, &target_notifylist, tn) { if (n->status != RowStatus_active || n->taglist[0] == '\0') continue; SLIST_FOREACH(ta, &target_addresslist, ta) if ((tag = strstr(ta->taglist, n->taglist)) != NULL && (tag[strlen(n->taglist)] == ' ' || tag[strlen(n->taglist)] == '\0' || tag[strlen(n->taglist)] == '\t' || tag[strlen(n->taglist)] == '\r' || tag[strlen(n->taglist)] == '\n') && ta->status == RowStatus_active) break; if (ta == NULL) continue; SLIST_FOREACH(tp, &target_paramlist, tp) if (strcmp(tp->name, ta->paramname) == 0 && tp->status == 1) break; if (tp == NULL) continue; switch (tp->mpmodel) { case SNMP_MPM_SNMP_V1: snmp_create_v1_trap(&pdu, tp->secname, trap_oid); break; case SNMP_MPM_SNMP_V2c: snmp_create_v2_trap(&pdu, tp->secname, trap_oid); break; case SNMP_MPM_SNMP_V3: snmp_create_v3_trap(&pdu, tp, trap_oid); break; default: continue; } va_start(ap, trap_oid); while ((v = va_arg(ap, const struct snmp_value *)) != NULL) pdu.bindings[pdu.nbindings++] = *v; va_end(ap); if (snmp_pdu_auth_access(&pdu, &ip) != SNMP_CODE_OK) { syslog(LOG_DEBUG, "send trap to %s failed: no access", t->comm); continue; } if ((sndbuf = buf_alloc(1)) == NULL) { syslog(LOG_ERR, "trap send buffer: %m"); return; } snmp_output(&pdu, sndbuf, &sndlen, "TRAP"); if ((len = send(ta->socket, sndbuf, sndlen, 0)) == -1) syslog(LOG_ERR, "send: %m"); else if ((size_t)len != sndlen) syslog(LOG_ERR, "send: short write %zu/%zu", sndlen, (size_t)len); free(sndbuf); } } /* * RFC 3413 SNMP Management Target MIB */ struct snmpd_target_stats * bsnmpd_get_target_stats(void) { return (&snmpd_target_stats); } struct target_address * target_first_address(void) { return (SLIST_FIRST(&target_addresslist)); } struct target_address * target_next_address(struct target_address *addrs) { if (addrs == NULL) return (NULL); return (SLIST_NEXT(addrs, ta)); } struct target_address * target_new_address(char *aname) { int cmp; struct target_address *addrs, *temp, *prev; SLIST_FOREACH(addrs, &target_addresslist, ta) if (strcmp(aname, addrs->name) == 0) return (NULL); if ((addrs = (struct target_address *)malloc(sizeof(*addrs))) == NULL) return (NULL); memset(addrs, 0, sizeof(*addrs)); strlcpy(addrs->name, aname, sizeof(addrs->name)); addrs->timeout = 150; addrs->retry = 3; /* XXX */ if ((prev = SLIST_FIRST(&target_addresslist)) == NULL || strcmp(aname, prev->name) < 0) { SLIST_INSERT_HEAD(&target_addresslist, addrs, ta); return (addrs); } SLIST_FOREACH(temp, &target_addresslist, ta) { if ((cmp = strcmp(aname, temp->name)) <= 0) break; prev = temp; } if (temp == NULL || cmp < 0) SLIST_INSERT_AFTER(prev, addrs, ta); else if (cmp > 0) SLIST_INSERT_AFTER(temp, addrs, ta); else { syslog(LOG_ERR, "Target address %s exists", addrs->name); free(addrs); return (NULL); } return (addrs); } int target_activate_address(struct target_address *addrs) { struct sockaddr_in sa; if ((addrs->socket = socket(PF_INET, SOCK_DGRAM, 0)) == -1) { syslog(LOG_ERR, "socket(UDP): %m"); return (SNMP_ERR_RES_UNAVAIL); } (void)shutdown(addrs->socket, SHUT_RD); memset(&sa, 0, sizeof(sa)); sa.sin_len = sizeof(sa); sa.sin_family = AF_INET; sa.sin_addr.s_addr = htonl((addrs->address[0] << 24) | (addrs->address[1] << 16) | (addrs->address[2] << 8) | (addrs->address[3] << 0)); sa.sin_port = htons(addrs->address[4]) << 8 | htons(addrs->address[5]) << 0; if (connect(addrs->socket, (struct sockaddr *)&sa, sa.sin_len) == -1) { syslog(LOG_ERR, "connect(%s,%u): %m", inet_ntoa(sa.sin_addr), ntohs(sa.sin_port)); (void)close(addrs->socket); return (SNMP_ERR_GENERR); } addrs->status = RowStatus_active; return (SNMP_ERR_NOERROR); } int target_delete_address(struct target_address *addrs) { SLIST_REMOVE(&target_addresslist, addrs, target_address, ta); if (addrs->status == RowStatus_active) close(addrs->socket); free(addrs); return (0); } struct target_param * target_first_param(void) { return (SLIST_FIRST(&target_paramlist)); } struct target_param * target_next_param(struct target_param *param) { if (param == NULL) return (NULL); return (SLIST_NEXT(param, tp)); } struct target_param * target_new_param(char *pname) { int cmp; struct target_param *param, *temp, *prev; SLIST_FOREACH(param, &target_paramlist, tp) if (strcmp(pname, param->name) == 0) return (NULL); if ((param = (struct target_param *)malloc(sizeof(*param))) == NULL) return (NULL); memset(param, 0, sizeof(*param)); strlcpy(param->name, pname, sizeof(param->name)); if ((prev = SLIST_FIRST(&target_paramlist)) == NULL || strcmp(pname, prev->name) < 0) { SLIST_INSERT_HEAD(&target_paramlist, param, tp); return (param); } SLIST_FOREACH(temp, &target_paramlist, tp) { if ((cmp = strcmp(pname, temp->name)) <= 0) break; prev = temp; } if (temp == NULL || cmp < 0) SLIST_INSERT_AFTER(prev, param, tp); else if (cmp > 0) SLIST_INSERT_AFTER(temp, param, tp); else { syslog(LOG_ERR, "Target parameter %s exists", param->name); free(param); return (NULL); } return (param); } int target_delete_param(struct target_param *param) { SLIST_REMOVE(&target_paramlist, param, target_param, tp); free(param); return (0); } struct target_notify * target_first_notify(void) { return (SLIST_FIRST(&target_notifylist)); } struct target_notify * target_next_notify(struct target_notify *notify) { if (notify == NULL) return (NULL); return (SLIST_NEXT(notify, tn)); } struct target_notify * target_new_notify(char *nname) { int cmp; struct target_notify *notify, *temp, *prev; SLIST_FOREACH(notify, &target_notifylist, tn) if (strcmp(nname, notify->name) == 0) return (NULL); if ((notify = (struct target_notify *)malloc(sizeof(*notify))) == NULL) return (NULL); memset(notify, 0, sizeof(*notify)); strlcpy(notify->name, nname, sizeof(notify->name)); if ((prev = SLIST_FIRST(&target_notifylist)) == NULL || strcmp(nname, prev->name) < 0) { SLIST_INSERT_HEAD(&target_notifylist, notify, tn); return (notify); } SLIST_FOREACH(temp, &target_notifylist, tn) { if ((cmp = strcmp(nname, temp->name)) <= 0) break; prev = temp; } if (temp == NULL || cmp < 0) SLIST_INSERT_AFTER(prev, notify, tn); else if (cmp > 0) SLIST_INSERT_AFTER(temp, notify, tn); else { syslog(LOG_ERR, "Notification target %s exists", notify->name); free(notify); return (NULL); } return (notify); } int target_delete_notify(struct target_notify *notify) { SLIST_REMOVE(&target_notifylist, notify, target_notify, tn); free(notify); return (0); } void target_flush_all(void) { struct target_address *addrs; struct target_param *param; struct target_notify *notify; while ((addrs = SLIST_FIRST(&target_addresslist)) != NULL) { SLIST_REMOVE_HEAD(&target_addresslist, ta); if (addrs->status == RowStatus_active) close(addrs->socket); free(addrs); } SLIST_INIT(&target_addresslist); while ((param = SLIST_FIRST(&target_paramlist)) != NULL) { SLIST_REMOVE_HEAD(&target_paramlist, tp); free(param); } SLIST_INIT(&target_paramlist); while ((notify = SLIST_FIRST(&target_notifylist)) != NULL) { SLIST_REMOVE_HEAD(&target_notifylist, tn); free(notify); } SLIST_INIT(&target_notifylist); } Index: head/contrib/bsnmp/snmpd/tree.def =================================================================== --- head/contrib/bsnmp/snmpd/tree.def (revision 335884) +++ head/contrib/bsnmp/snmpd/tree.def (revision 335885) @@ -1,223 +1,214 @@ # # Copyright (c) 2001-2003 # Fraunhofer Institute for Open Communication Systems (FhG Fokus). # All rights reserved. # # Author: Harti Brandt # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. # # $Begemot: tree.def 517 2006-10-31 08:52:04Z brandt_h $ # # System group and private Begemot SNMPd MIB. # -#include "tc.def" - -typedef RowStatus ENUM ( - 1 active - 2 notInService - 3 notReady - 4 createAndGo - 5 createAndWait - 6 destroy -) +include "tc.def" (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) ) ) (2 begemotSnmpdDefs (1 begemotSnmpdAgent (1 begemotSnmpdAgentFreeBSD OID op_dummy) ) ) ) )) ) ) (6 snmpV2 (3 snmpModules (1 snmpMIB (1 snmpMIBObjects (4 snmpTrap (1 snmpTrapOID OID op_snmp_trap) ) (5 snmpTraps (1 coldStart OID op_snmp_trap) (2 warmStart OID op_snmp_trap) (5 authenticationFailure OID op_snmp_trap) ) (6 snmpSet (1 snmpSetSerialNo INTEGER op_snmp_set GET SET) ) ) ) (10 snmpFrameworkMIB (2 snmpFrameworkMIBObjects (1 snmpEngine (1 snmpEngineID OCTETSTRING | SnmpEngineID op_snmp_engine GET) (2 snmpEngineBoots INTEGER op_snmp_engine GET) (3 snmpEngineTime INTEGER op_snmp_engine GET) (4 snmpEngineMaxMessageSize INTEGER op_snmp_engine GET) ) ) ) )) ) Index: head/contrib/libbegemot/rpoll.c =================================================================== --- head/contrib/libbegemot/rpoll.c (revision 335884) +++ head/contrib/libbegemot/rpoll.c (revision 335885) @@ -1,740 +1,729 @@ /* * Copyright (c)1996-2002 by Hartmut Brandt * All rights reserved. * * Author: Hartmut Brandt * * Redistribution of this software and documentation and use in source and * binary forms, with or without modification, are permitted provided that * the following conditions are met: * * 1. Redistributions of source code or documentation must retain the above * copyright notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY THE AUTHOR * AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE AUTHOR OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * These functions try to hide the poll/select/setitimer interface from the * user. You associate callback functions with file descriptors and timers. * * $Begemot: libbegemot/rpoll.c,v 1.14 2004/09/21 15:59:00 brandt Exp $ */ # include # include # include # include # include # include # include # include # include # include # include -/* - * There happens to be linuxes which read siginfo.h when including - * signal.h, which, for no appearent reason, defines these symbols. - */ -# ifdef POLL_IN -# undef POLL_IN -# endif -# ifdef POLL_OUT -# undef POLL_OUT -# endif - # include "rpoll.h" /* # define DEBUG */ # ifdef USE_POLL # ifdef NEED_POLL_XOPEN_TWIDDLE # define __USE_XOPEN # endif # include # ifdef NEED_POLL_XOPEN_TWIDDLE # undef __USE_XOPEN # endif # include # endif /* * the second define is for Linux, which sometimes fails to * declare INFTIM. */ # if defined(USE_SELECT) || !defined(INFTIM) # define INFTIM (-1) # endif # if defined(SIGPOLL) # define SIGNAL SIGPOLL # else # if defined(SIGIO) # define SIGNAL SIGIO # endif # endif # ifdef USE_POLL # define poll_in (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI) # define poll_out (POLLOUT | POLLWRNORM | POLLWRBAND) # define poll_except (POLLERR | POLLHUP) # endif # ifdef BROKEN_SELECT_PROTO # define SELECT_CAST(P) (int *)P # else # define SELECT_CAST(P) P # endif typedef int64_t tval_t; static inline tval_t GETUSECS(void); static inline tval_t GETUSECS(void) { struct timeval tval; (void)gettimeofday(&tval, NULL); return (tval_t)tval.tv_sec * 1000000 + tval.tv_usec; } /* * Simple fatal exit. */ static void _panic(const char *fmt, ...) { va_list ap; va_start(ap, fmt); fprintf(stderr, "panic: "); vfprintf(stderr, fmt, ap); fprintf(stderr, "\n"); va_end(ap); exit(1); } static void * _xrealloc(void *p, size_t s) { void *ptr; if(p == NULL) { if((ptr=malloc(s)) == NULL && (s!=0 || (ptr=malloc(1)) == NULL)) _panic("out of memory: xrealloc(%lx, %lu)", (unsigned long)p, (unsigned long)s); } else if(s == 0) { free(p); if((ptr=malloc(s)) == NULL && (ptr=malloc(1)) == NULL) _panic("out of memory: xrealloc(%lx, %lu)", (unsigned long)p, (unsigned long)s); } else { if((ptr = realloc(p, s)) == NULL) _panic("out of memory: xrealloc(%lx, %lu)", (unsigned long)p, (unsigned long)s); } return ptr; } /* * This structure holds one registration record for files */ typedef struct { int fd; /* file descriptor (-1 if struct unused) */ int mask; /* event flags */ void * arg; /* client arg */ poll_f func; /* handler */ # ifdef USE_POLL struct pollfd *pfd; /* pointer to corresponding poll() structure */ # endif } PollReg_t; /* * Now for timers */ typedef struct { uint64_t usecs; /* microsecond value of the timer */ int repeat; /* one shot or repeat? */ void *arg; /* client arg */ timer_f func; /* handler, 0 means disfunct */ tval_t when; /* next time to trigger in usecs! */ } PollTim_t; /* how many records should our table grow at once? */ # define POLL_REG_GROW 100 # ifdef USE_POLL static struct pollfd * pfd; /* fd list for poll() */ # endif # ifdef USE_SELECT static fd_set rset, wset, xset; /* file descriptor sets for select() */ static int maxfd; /* maximum fd number */ # endif static int in_dispatch; static PollReg_t * regs; /* registration records */ static u_int regs_alloc; /* how many are allocated */ static u_int regs_used; /* upper used limit */ static sigset_t bset; /* blocked signals */ static int rebuild; /* rebuild table on next dispatch() */ static int * tfd; /* sorted entries */ static u_int tfd_alloc; /* number of entries allocated */ static u_int tfd_used; /* number of entries used */ static PollTim_t * tims; /* timer registration records */ static u_int tims_alloc; /* how many are allocated */ static u_int tims_used; /* how many are used */ static int resort; /* resort on next dispatch */ int rpoll_trace; int rpoll_policy; /* if 0 start sched callbacks from 0 else try round robin */ static void poll_build(void); static void poll_blocksig(void); static void poll_unblocksig(void); static void sort_timers(void); /* * Private function to block SIGPOLL or SIGIO for a short time. * Don't forget to call poll_unblock before return from the calling function. * Don't change the mask between this calls (your changes will be lost). */ static void poll_blocksig(void) { sigset_t set; sigemptyset(&set); sigaddset(&set, SIGNAL); if(sigprocmask(SIG_BLOCK, &set, &bset)) _panic("sigprocmask(SIG_BLOCK): %s", strerror(errno)); } /* * unblock the previously blocked signal */ static void poll_unblocksig(void) { if(sigprocmask(SIG_SETMASK, &bset, NULL)) _panic("sigprocmask(SIG_SETMASK): %s", strerror(errno)); } /* * Register the file descriptor fd. If the event corresponding to * mask arrives func is called with arg. * If fd is already registered with that func and arg, only the mask * is changed. * We block the IO-signal, so the dispatch function can be called from * within the signal handler. */ int poll_register(int fd, poll_f func, void *arg, int mask) { PollReg_t * p; poll_blocksig(); /* already registered? */ for(p = regs; p < ®s[regs_alloc]; p++) if(p->fd == fd && p->func == func && p->arg == arg) { p->mask = mask; break; } if(p == ®s[regs_alloc]) { /* no - register */ /* find a free slot */ for(p = regs; p < ®s[regs_alloc]; p++) if(p->fd == -1) break; if(p == ®s[regs_alloc]) { size_t newsize = regs_alloc + POLL_REG_GROW; regs = _xrealloc(regs, sizeof(regs[0]) * newsize); for(p = ®s[regs_alloc]; p < ®s[newsize]; p++) { p->fd = -1; # ifdef USE_POLL p->pfd = NULL; # endif } p = ®s[regs_alloc]; regs_alloc = newsize; } p->fd = fd; p->arg = arg; p->mask = mask; p->func = func; regs_used++; rebuild = 1; } poll_unblocksig(); if(rpoll_trace) fprintf(stderr, "poll_register(%d, %p, %p, %#x)->%tu", fd, (void *)func, (void *)arg, mask, p - regs); return p - regs; } /* * remove registration */ void poll_unregister(int handle) { if(rpoll_trace) fprintf(stderr, "poll_unregister(%d)", handle); poll_blocksig(); regs[handle].fd = -1; # ifdef USE_POLL regs[handle].pfd = NULL; # endif rebuild = 1; regs_used--; poll_unblocksig(); } /* * Build the structures used by poll() or select() */ static void poll_build(void) { PollReg_t * p; # ifdef USE_POLL struct pollfd * f; f = pfd = _xrealloc(pfd, sizeof(pfd[0]) * regs_used); for(p = regs; p < ®s[regs_alloc]; p++) if(p->fd >= 0) { f->fd = p->fd; f->events = 0; - if(p->mask & POLL_IN) + if(p->mask & RPOLL_IN) f->events |= poll_in; - if(p->mask & POLL_OUT) + if(p->mask & RPOLL_OUT) f->events |= poll_out; - if(p->mask & POLL_EXCEPT) + if(p->mask & RPOLL_EXCEPT) f->events |= poll_except; f->revents = 0; p->pfd = f++; } assert(f == &pfd[regs_used]); # endif # ifdef USE_SELECT FD_ZERO(&rset); FD_ZERO(&wset); FD_ZERO(&xset); maxfd = -1; for(p = regs; p < ®s[regs_alloc]; p++) if(p->fd >= 0) { if(p->fd > maxfd) maxfd = p->fd; - if(p->mask & POLL_IN) + if(p->mask & RPOLL_IN) FD_SET(p->fd, &rset); - if(p->mask & POLL_OUT) + if(p->mask & RPOLL_OUT) FD_SET(p->fd, &wset); - if(p->mask & POLL_EXCEPT) + if(p->mask & RPOLL_EXCEPT) FD_SET(p->fd, &xset); } # endif } int poll_start_timer(u_int msecs, int repeat, timer_f func, void *arg) { return (poll_start_utimer((unsigned long long)msecs * 1000, repeat, func, arg)); } int poll_start_utimer(unsigned long long usecs, int repeat, timer_f func, void *arg) { PollTim_t *p; /* find unused entry */ for(p = tims; p < &tims[tims_alloc]; p++) if(p->func == NULL) break; if(p == &tims[tims_alloc]) { if(tims_alloc == tims_used) { size_t newsize = tims_alloc + POLL_REG_GROW; tims = _xrealloc(tims, sizeof(tims[0]) * newsize); for(p = &tims[tims_alloc]; p < &tims[newsize]; p++) p->func = NULL; p = &tims[tims_alloc]; tims_alloc = newsize; } } /* create entry */ p->usecs = usecs; p->repeat = repeat; p->arg = arg; p->func = func; p->when = GETUSECS() + usecs; tims_used++; resort = 1; if(rpoll_trace) fprintf(stderr, "poll_start_utimer(%llu, %d, %p, %p)->%tu", usecs, repeat, (void *)func, (void *)arg, p - tims); return p - tims; } /* * Here we have to look into the sorted table, whether any entry there points * into the registration table for the deleted entry. This is needed, * because a unregistration can occure while we are scanning through the * table in dispatch(). Do this only, if we are really there - resorting * will sort out things if we are called from outside the loop. */ void poll_stop_timer(int handle) { u_int i; if(rpoll_trace) fprintf(stderr, "poll_stop_timer(%d)", handle); tims[handle].func = NULL; tims_used--; resort = 1; if(!in_dispatch) return; for(i = 0; i < tfd_used; i++) if(tfd[i] == handle) { tfd[i] = -1; break; } } /* * Squeeze and sort timer table. * Should perhaps use a custom sort. */ static int tim_cmp(const void *p1, const void *p2) { int t1 = *(const int *)p1; int t2 = *(const int *)p2; return tims[t1].when < tims[t2].when ? -1 : tims[t1].when > tims[t2].when ? +1 : 0; } /* * Reconstruct the tfd-array. This will be an sorted array of indexes * to the used entries in tims. The next timer to expire will be infront * of the array. tfd_used is the number of used entries. The array is * re-allocated if needed. */ static void sort_timers(void) { int *pp; u_int i; if(tims_used > tfd_alloc) { tfd_alloc = tims_used; tfd = _xrealloc(tfd, sizeof(int *) * tfd_alloc); } pp = tfd; for(i = 0; i < tims_alloc; i++) if(tims[i].func) *pp++ = i; assert(pp - tfd == (ptrdiff_t)tims_used); tfd_used = tims_used; if(tfd_used > 1) qsort(tfd, tfd_used, sizeof(int), tim_cmp); } /* * Poll the file descriptors and dispatch to the right function * If wait is true the poll blocks until somewhat happens. * Don't use a pointer here, because the called function may cause * a reallocation! The check for pfd != NULL is required, because * a sequence of unregister/register could make the wrong callback * to be called. So we clear pfd in unregister and check here. */ void poll_dispatch(int wait) { u_int i, idx; int ret; tval_t now; tval_t tout; static u_int last_index; # ifdef USE_SELECT fd_set nrset, nwset, nxset; struct timeval tv; # endif in_dispatch = 1; if(rebuild) { rebuild = 0; poll_build(); } if(resort) { resort = 0; sort_timers(); } /* in wait mode - compute the timeout */ if(wait) { if(tfd_used) { now = GETUSECS(); # ifdef DEBUG { fprintf(stderr, "now=%llu", now); for(i = 0; i < tims_used; i++) fprintf(stderr, "timers[%2d] = %lld", i, tfd[i]->when - now); } # endif if((tout = tims[tfd[0]].when - now) < 0) tout = 0; } else tout = INFTIM; } else tout = 0; # ifdef DEBUG fprintf(stderr, "rpoll -- selecting with tout=%u", tout); # endif # ifdef USE_POLL ret = poll(pfd, regs_used, tout == INFTIM ? INFTIM : (tout / 1000)); # endif # ifdef USE_SELECT nrset = rset; nwset = wset; nxset = xset; if(tout != INFTIM) { tv.tv_sec = tout / 1000000; tv.tv_usec = tout % 1000000; } ret = select(maxfd+1, SELECT_CAST(&nrset), SELECT_CAST(&nwset), SELECT_CAST(&nxset), (tout==INFTIM) ? NULL : &tv); # endif if(ret == -1) { if(errno == EINTR) return; _panic("poll/select: %s", strerror(errno)); } /* dispatch files */ if(ret > 0) { for(i = 0; i < regs_alloc; i++) { idx = rpoll_policy ? ((last_index+i) % regs_alloc) : i; assert(idx < regs_alloc); if(regs[idx].fd >= 0) { int mask = 0; # ifdef USE_POLL if(regs[idx].pfd) { - if ((regs[idx].mask & POLL_IN) && + if ((regs[idx].mask & RPOLL_IN) && (regs[idx].pfd->revents & poll_in)) - mask |= POLL_IN; - if ((regs[idx].mask & POLL_OUT) && + mask |= RPOLL_IN; + if ((regs[idx].mask & RPOLL_OUT) && (regs[idx].pfd->revents & poll_out)) - mask |= POLL_OUT; - if((regs[idx].mask & POLL_EXCEPT) && + mask |= RPOLL_OUT; + if((regs[idx].mask & RPOLL_EXCEPT) && (regs[idx].pfd->revents & poll_except)) - mask |= POLL_EXCEPT; + mask |= RPOLL_EXCEPT; } # endif # ifdef USE_SELECT - if ((regs[idx].mask & POLL_IN) && + if ((regs[idx].mask & RPOLL_IN) && FD_ISSET(regs[idx].fd, &nrset)) - mask |= POLL_IN; - if ((regs[idx].mask & POLL_OUT) && + mask |= RPOLL_IN; + if ((regs[idx].mask & RPOLL_OUT) && FD_ISSET(regs[idx].fd, &nwset)) - mask |= POLL_OUT; - if ((regs[idx].mask & POLL_EXCEPT) && + mask |= RPOLL_OUT; + if ((regs[idx].mask & RPOLL_EXCEPT) && FD_ISSET(regs[idx].fd, &nxset)) - mask |= POLL_EXCEPT; + mask |= RPOLL_EXCEPT; # endif assert(idx < regs_alloc); if(mask) { if(rpoll_trace) fprintf(stderr, "poll_dispatch() -- " "file %d/%d %x", regs[idx].fd, idx, mask); (*regs[idx].func)(regs[idx].fd, mask, regs[idx].arg); } } } last_index++; } /* dispatch timeouts */ if(tfd_used) { now = GETUSECS(); for(i = 0; i < tfd_used; i++) { if(tfd[i] < 0) continue; if(tims[tfd[i]].when > now) break; if(rpoll_trace) fprintf(stderr, "rpoll_dispatch() -- timeout %d",tfd[i]); (*tims[tfd[i]].func)(tfd[i], tims[tfd[i]].arg); if(tfd[i] < 0) continue; if(tims[tfd[i]].repeat) tims[tfd[i]].when = now + tims[tfd[i]].usecs; else { tims[tfd[i]].func = NULL; tims_used--; tfd[i] = -1; } resort = 1; } } in_dispatch = 0; } # ifdef TESTME struct timeval start, now; int t0, t1; double elaps(void); void infunc(int fd, int mask, void *arg); double elaps(void) { gettimeofday(&now, NULL); return (double)(10 * now.tv_sec + now.tv_usec / 100000 - 10 * start.tv_sec - start.tv_usec / 100000) / 10; } void infunc(int fd, int mask, void *arg) { char buf[1024]; int ret; mask = mask; arg = arg; if((ret = read(fd, buf, sizeof(buf))) < 0) _panic("read: %s", strerror(errno)); write(1, "stdin:", 6); write(1, buf, ret); } void tfunc0(int tid, void *arg); void tfunc1(int tid, void *arg); void tfunc0(int tid, void *arg) { printf("%4.1f -- %d: %s\n", elaps(), tid, (char *)arg); } void tfunc1(int tid, void *arg) { printf("%4.1f -- %d: %s\n", elaps(), tid, (char *)arg); } void tfunc2(int tid, void *arg) { static u_int count = 0; if (++count % 10000 == 0) printf("%4.1f -- %d\n", elaps(), tid); } void first(int tid, void *arg); void second(int tid, void *arg); void second(int tid, void *arg) { printf("%4.1f -- %d: %s\n", elaps(), tid, (char *)arg); poll_start_utimer(5500000, 0, first, "first"); poll_stop_timer(t1); t0 = poll_start_timer(1000, 1, tfunc0, "1 second"); } void first(int tid, void *arg) { printf("%4.1f -- %d: %s\n", elaps(), tid, (char *)arg); poll_start_timer(3700, 0, second, "second"); poll_stop_timer(t0); t1 = poll_start_timer(250, 1, tfunc1, "1/4 second"); } int main(int argc, char *argv[]) { argv = argv; gettimeofday(&start, NULL); - poll_register(0, infunc, NULL, POLL_IN); + poll_register(0, infunc, NULL, RPOLL_IN); if (argc < 2) { t0 = poll_start_timer(1000, 1, tfunc0, "1 second"); poll_start_timer(2500, 0, first, "first"); } else { t0 = poll_start_utimer(300, 1, tfunc2, NULL); } while(1) poll_dispatch(1); return 0; } # endif Index: head/contrib/libbegemot/rpoll.h =================================================================== --- head/contrib/libbegemot/rpoll.h (revision 335884) +++ head/contrib/libbegemot/rpoll.h (revision 335885) @@ -1,68 +1,63 @@ /* * Copyright (c)1996-2002 by Hartmut Brandt * All rights reserved. * * Author: Hartmut Brandt * * Redistribution of this software and documentation and use in source and * binary forms, with or without modification, are permitted provided that * the following conditions are met: * * 1. Redistributions of source code or documentation must retain the above * copyright notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY THE AUTHOR * AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE AUTHOR OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * $Begemot: libbegemot/rpoll.h,v 1.5 2004/09/21 15:49:26 brandt Exp $ */ # ifndef rpoll_h_ # define rpoll_h_ # ifdef __cplusplus extern "C" { # endif typedef void (*poll_f)(int fd, int mask, void *arg); typedef void (*timer_f)(int, void *); int poll_register(int fd, poll_f func, void *arg, int mask); void poll_unregister(int); void poll_dispatch(int wait); int poll_start_timer(u_int msecs, int repeat, timer_f func, void *arg); int poll_start_utimer(unsigned long long usecs, int repeat, timer_f func, void *arg); void poll_stop_timer(int); -# if defined(POLL_IN) -# undef POLL_IN -# endif -# if defined(POLL_OUT) -# undef POLL_OUT -# endif - -# define POLL_IN 1 -# define POLL_OUT 2 -# define POLL_EXCEPT 4 +enum { + RPOLL_IN = 1, + RPOLL_OUT = 2, + RPOLL_EXCEPT = 4, +}; extern int rpoll_policy; extern int rpoll_trace; # ifdef __cplusplus } # endif # endif Index: head/lib/libbsnmp/libbsnmp/Makefile =================================================================== --- head/lib/libbsnmp/libbsnmp/Makefile (revision 335884) +++ head/lib/libbsnmp/libbsnmp/Makefile (revision 335885) @@ -1,118 +1,132 @@ # $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 .include Index: head/share/mk/bsd.snmpmod.mk =================================================================== --- head/share/mk/bsd.snmpmod.mk (revision 335884) +++ head/share/mk/bsd.snmpmod.mk (revision 335885) @@ -1,42 +1,44 @@ # $FreeBSD$ INCSDIR= ${INCLUDEDIR}/bsnmp SHLIB_NAME= snmp_${MOD}.so.${SHLIB_MAJOR} SRCS+= ${MOD}_oid.h ${MOD}_tree.c ${MOD}_tree.h CLEANFILES+= ${MOD}_oid.h ${MOD}_tree.c ${MOD}_tree.h CFLAGS+= -I. +GENSNMPTREEFLAGS+= -I${SHAREDIR}/snmpdefs + ${MOD}_oid.h: ${MOD}_tree.def ${EXTRAMIBDEFS} ${EXTRAMIBSYMS} - cat ${.ALLSRC} | gensnmptree -e ${XSYM} > ${.TARGET} + cat ${.ALLSRC} | gensnmptree ${GENSNMPTREEFLAGS} -e ${XSYM} > ${.TARGET} .ORDER: ${MOD}_tree.c ${MOD}_tree.h ${MOD}_tree.h: .NOMETA ${MOD}_tree.c ${MOD}_tree.h: ${MOD}_tree.def ${EXTRAMIBDEFS} - cat ${.ALLSRC} | gensnmptree -p ${MOD}_ + cat ${.ALLSRC} | gensnmptree -f ${GENSNMPTREEFLAGS} -p ${MOD}_ .if defined(DEFS) FILESGROUPS+= DEFS DEFSDIR?= ${SHAREDIR}/snmp/defs .endif .if defined(BMIBS) FILESGROUPS+= BMIBS BMIBSDIR?= ${SHAREDIR}/snmp/mibs .endif .if !target(smilint) && !empty(BMIBS) LOCALBASE?= /usr/local SMILINT?= ${LOCALBASE}/bin/smilint SMIPATH?= ${BMIBSDIR}:${LOCALBASE}/share/snmp/mibs SMILINT_FLAGS?= -c /dev/null -l6 -i group-membership smilint: ${BMIBS} SMIPATH=${SMIPATH} ${SMILINT} ${SMILINT_FLAGS} ${.ALLSRC} .endif smilint: .PHONY .include Index: head/usr.sbin/bsnmpd/bsnmpd/Makefile =================================================================== --- head/usr.sbin/bsnmpd/bsnmpd/Makefile (revision 335884) +++ head/usr.sbin/bsnmpd/bsnmpd/Makefile (revision 335885) @@ -1,157 +1,153 @@ # $FreeBSD$ # # Author: Harti Brandt .include CONTRIB=${SRCTOP}/contrib/bsnmp .PATH: ${CONTRIB}/snmpd PROG= bsnmpd SRCS= main.c action.c config.c export.c trap.c trans_udp.c trans_lsock.c SRCS+= oid.h tree.c tree.h XSYM= snmpMIB begemotSnmpdModuleTable begemotSnmpd begemotTrapSinkTable \ sysUpTime snmpTrapOID coldStart authenticationFailure \ begemotSnmpdTransUdp begemotSnmpdTransLsock begemotSnmpdLocalPortTable \ freeBSD freeBSDVersion 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+= -DSNMPTREE_TYPES 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 -e ${XSYM} < ${.ALLSRC:M*.def} > ${.TARGET} + 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 -l < ${.ALLSRC} + gensnmptree -I${CONTRIB}/lib -l -f < ${.ALLSRC} MANFILTER= sed -e 's%@MODPATH@%${LIBDIR}/%g' \ -e 's%@DEFPATH@%${DEFSDIR}/%g' \ -e 's%@MIBSPATH@%${BMIBSDIR}/%g' - -NO_WCAST_ALIGN= yes -WARNS?= 6 .include Index: head/usr.sbin/bsnmpd/modules/snmp_bridge/Makefile =================================================================== --- head/usr.sbin/bsnmpd/modules/snmp_bridge/Makefile (revision 335884) +++ head/usr.sbin/bsnmpd/modules/snmp_bridge/Makefile (revision 335885) @@ -1,19 +1,18 @@ # # $FreeBSD$ # MOD= bridge SRCS= bridge_snmp.c bridge_if.c bridge_port.c bridge_addrs.c \ bridge_pf.c bridge_sys.c -CFLAGS+= -DSNMPTREE_TYPES XSYM= dot1dBridge newRoot topologyChange begemotBridgeNewRoot \ begemotBridgeTopologyChange begemotBridgeBaseName MAN= snmp_bridge.3 BMIBS= BRIDGE-MIB.txt BEGEMOT-BRIDGE-MIB.txt RSTP-MIB.txt DEFS= ${MOD}_tree.def INCS= ${MOD}_snmp.h .include Index: head/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_addrs.c =================================================================== --- head/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_addrs.c (revision 335884) +++ head/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_addrs.c (revision 335885) @@ -1,591 +1,592 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2006 Shteryana Shopova * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * Bridge MIB implementation for SNMPd. * Bridge addresses. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#define SNMPTREE_TYPES #include "bridge_tree.h" #include "bridge_snmp.h" TAILQ_HEAD(tp_entries, tp_entry); /* * Free the bridge address list. */ static void bridge_tpe_free(struct tp_entries *headp) { struct tp_entry *t; while ((t = TAILQ_FIRST(headp)) != NULL) { TAILQ_REMOVE(headp, t, tp_e); free(t); } } /* * Free the bridge address entries from the address list, * for the specified bridge interface only. */ static void bridge_tpe_bif_free(struct tp_entries *headp, struct bridge_if *bif) { struct tp_entry *tp; while (bif->f_tpa != NULL && bif->sysindex == bif->f_tpa->sysindex) { tp = TAILQ_NEXT(bif->f_tpa, tp_e); TAILQ_REMOVE(headp, bif->f_tpa, tp_e); free(bif->f_tpa); bif->f_tpa = tp; } } /* * Compare two mac addresses. * m1 < m2 : -1 * m1 > m2 : +1 * m1 = m2 : 0 */ static int bridge_compare_macs(const uint8_t *m1, const uint8_t *m2) { int i; for (i = 0; i < ETHER_ADDR_LEN; i++) { if (m1[i] < m2[i]) return (-1); if (m1[i] > m2[i]) return (1); } return (0); } /* * Insert an address entry in the bridge address TAILQ starting to search * for its place from the position of the first bridge address for the bridge * interface. Update the first bridge address if necessary. */ static void bridge_addrs_insert_at(struct tp_entries *headp, struct tp_entry *ta, struct tp_entry **f_tpa) { struct tp_entry *t1; assert(f_tpa != NULL); for (t1 = *f_tpa; t1 != NULL && ta->sysindex == t1->sysindex; t1 = TAILQ_NEXT(t1, tp_e)) { if (bridge_compare_macs(ta->tp_addr, t1->tp_addr) < 0) { TAILQ_INSERT_BEFORE(t1, ta, tp_e); if (*f_tpa == t1) (*f_tpa) = ta; return; } } if (t1 == NULL) TAILQ_INSERT_TAIL(headp, ta, tp_e); else TAILQ_INSERT_BEFORE(t1, ta, tp_e); } /* * Find an address entry's position in the address list * according to bridge interface name. */ static struct tp_entry * bridge_addrs_find_pos(struct tp_entries *headp, uint32_t b_idx) { uint32_t t_idx; struct tp_entry *t1; if ((t1 = TAILQ_FIRST(headp)) == NULL || bridge_compare_sysidx(b_idx, t1->sysindex) < 0) return (NULL); t_idx = t1->sysindex; for (t1 = TAILQ_NEXT(t1, tp_e); t1 != NULL; t1 = TAILQ_NEXT(t1, tp_e)) { if (t1->sysindex != t_idx) { if (bridge_compare_sysidx(b_idx, t1->sysindex) < 0) return (TAILQ_PREV(t1, tp_entries, tp_e)); else t_idx = t1->sysindex; } } if (t1 == NULL) t1 = TAILQ_LAST(headp, tp_entries); return (t1); } /* * Insert a bridge address in the bridge addresses list. */ static void bridge_addrs_bif_insert(struct tp_entries *headp, struct tp_entry *te, struct tp_entry **f_tpa) { struct tp_entry *temp; if (*f_tpa != NULL) bridge_addrs_insert_at(headp, te, f_tpa); else { temp = bridge_addrs_find_pos(headp, te->sysindex); if (temp == NULL) TAILQ_INSERT_HEAD(headp, te, tp_e); else TAILQ_INSERT_AFTER(headp, temp, te, tp_e); *f_tpa = te; } } static struct tp_entries tp_entries = TAILQ_HEAD_INITIALIZER(tp_entries); static time_t address_list_age; void bridge_addrs_update_listage(void) { address_list_age = time(NULL); } void bridge_addrs_fini(void) { bridge_tpe_free(&tp_entries); } void bridge_addrs_free(struct bridge_if *bif) { bridge_tpe_bif_free(&tp_entries, bif); } /* * Find the first address in the list. */ static struct tp_entry * bridge_addrs_first(void) { return (TAILQ_FIRST(&tp_entries)); } /* * Find the next address in the list. */ static struct tp_entry * bridge_addrs_next(struct tp_entry *te) { return (TAILQ_NEXT(te, tp_e)); } /* * Find the first address, learnt by the specified bridge interface. */ struct tp_entry * bridge_addrs_bif_first(struct bridge_if *bif) { return (bif->f_tpa); } /* * Find the next address, learnt by the specified bridge interface. */ struct tp_entry * bridge_addrs_bif_next(struct tp_entry *te) { struct tp_entry *te_next; if ((te_next = TAILQ_NEXT(te, tp_e)) == NULL || te_next->sysindex != te->sysindex) return (NULL); return (te_next); } /* * Remove a bridge address from the list. */ void bridge_addrs_remove(struct tp_entry *te, struct bridge_if *bif) { if (bif->f_tpa == te) bif->f_tpa = bridge_addrs_bif_next(te); TAILQ_REMOVE(&tp_entries, te, tp_e); free(te); } /* * Allocate memory for a new bridge address and insert it in the list. */ struct tp_entry * bridge_new_addrs(uint8_t *mac, struct bridge_if *bif) { struct tp_entry *te; if ((te = (struct tp_entry *) malloc(sizeof(*te))) == NULL) { syslog(LOG_ERR, "bridge new address: failed: %s", strerror(errno)); return (NULL); } bzero(te, sizeof(*te)); te->sysindex = bif->sysindex; bcopy(mac, te->tp_addr, ETHER_ADDR_LEN); bridge_addrs_bif_insert(&tp_entries, te, &(bif->f_tpa)); return (te); } /* * Given a mac address, learnt on a bridge, * find the corrsponding TP entry for it. */ struct tp_entry * bridge_addrs_find(uint8_t *mac, struct bridge_if *bif) { struct tp_entry *te; for (te = bif->f_tpa; te != NULL; te = TAILQ_NEXT(te, tp_e)) { if (te->sysindex != bif->sysindex) { te = NULL; break; } if (bridge_compare_macs(te->tp_addr, mac) == 0) break; } return (te); } void bridge_addrs_dump(struct bridge_if *bif) { struct tp_entry *te; syslog(LOG_ERR, "Addresses count - %d", bif->num_addrs); for (te = bridge_addrs_bif_first(bif); te != NULL; te = bridge_addrs_bif_next(te)) { syslog(LOG_ERR, "address %x:%x:%x:%x:%x:%x on port %d.%d", te->tp_addr[0], te->tp_addr[1], te->tp_addr[2], te->tp_addr[3], te->tp_addr[4], te->tp_addr[5], te->sysindex, te->port_no); } } /* * RFC4188 specifics. */ /* * Construct the SNMP index from the address DST Mac. */ static void bridge_addrs_index_append(struct asn_oid *oid, uint sub, const struct tp_entry *te) { int i; oid->len = sub + ETHER_ADDR_LEN + 1; oid->subs[sub] = ETHER_ADDR_LEN; for (i = 1; i <= ETHER_ADDR_LEN; i++) oid->subs[sub + i] = te->tp_addr[i - 1]; } /* * Find the address entry for the SNMP index from the default bridge only. */ static struct tp_entry * bridge_addrs_get(const struct asn_oid *oid, uint sub, struct bridge_if *bif) { int i; uint8_t tp_addr[ETHER_ADDR_LEN]; if (oid->len - sub != ETHER_ADDR_LEN + 1 || oid->subs[sub] != ETHER_ADDR_LEN) return (NULL); for (i = 0; i < ETHER_ADDR_LEN; i++) tp_addr[i] = oid->subs[sub + i + 1]; return (bridge_addrs_find(tp_addr, bif)); } /* * Find the next address entry for the SNMP index * from the default bridge only. */ static struct tp_entry * bridge_addrs_getnext(const struct asn_oid *oid, uint sub, struct bridge_if *bif) { int i; uint8_t tp_addr[ETHER_ADDR_LEN]; static struct tp_entry *te; if (oid->len - sub == 0) return (bridge_addrs_bif_first(bif)); if (oid->len - sub != ETHER_ADDR_LEN + 1 || oid->subs[sub] != ETHER_ADDR_LEN) return (NULL); for (i = 0; i < ETHER_ADDR_LEN; i++) tp_addr[i] = oid->subs[sub + i + 1]; if ((te = bridge_addrs_find(tp_addr, bif)) == NULL) return (NULL); return (bridge_addrs_bif_next(te)); } int op_dot1d_tp_fdb(struct snmp_context *c __unused, struct snmp_value *val, uint sub, uint iidx __unused, enum snmp_op op) { struct bridge_if *bif; struct tp_entry *te; if ((bif = bridge_get_default()) == NULL) return (SNMP_ERR_NOSUCHNAME); if (time(NULL) - bif->addrs_age > bridge_get_data_maxage() && bridge_update_addrs(bif) <= 0) return (SNMP_ERR_NOSUCHNAME); switch (op) { case SNMP_OP_GET: if ((te = bridge_addrs_get(&val->var, sub, bif)) == NULL) return (SNMP_ERR_NOSUCHNAME); goto get; case SNMP_OP_GETNEXT: if ((te = bridge_addrs_getnext(&val->var, sub, bif)) == NULL) return (SNMP_ERR_NOSUCHNAME); bridge_addrs_index_append(&val->var, sub, te); goto get; case SNMP_OP_SET: return (SNMP_ERR_NOT_WRITEABLE); case SNMP_OP_ROLLBACK: case SNMP_OP_COMMIT: break; } abort(); get: switch (val->var.subs[sub - 1]) { case LEAF_dot1dTpFdbAddress: return (string_get(val, te->tp_addr, ETHER_ADDR_LEN)); case LEAF_dot1dTpFdbPort : val->v.integer = te->port_no; return (SNMP_ERR_NOERROR); case LEAF_dot1dTpFdbStatus: val->v.integer = te->status; return (SNMP_ERR_NOERROR); } abort(); } /* * Private BEGEMOT-BRIDGE-MIB specifics. */ /* * Construct the SNMP index from the bridge interface name * and the address DST Mac. */ static int bridge_addrs_begemot_index_append(struct asn_oid *oid, uint sub, const struct tp_entry *te) { uint i, n_len; const char *b_name; if ((b_name = bridge_if_find_name(te->sysindex)) == NULL) return (-1); n_len = strlen(b_name); oid->len = sub++; oid->subs[oid->len++] = n_len; for (i = 1; i <= n_len; i++) oid->subs[oid->len++] = b_name[i - 1]; oid->subs[oid->len++] = ETHER_ADDR_LEN; for (i = 1 ; i <= ETHER_ADDR_LEN; i++) oid->subs[oid->len++] = te->tp_addr[i - 1]; return (0); } /* * Find a bridge address entry by the bridge interface name * and the address DST Mac. */ static struct tp_entry * bridge_addrs_begemot_get(const struct asn_oid *oid, uint sub) { uint i, n_len; uint8_t tp_addr[ETHER_ADDR_LEN]; char bif_name[IFNAMSIZ]; struct bridge_if *bif; n_len = oid->subs[sub]; if (oid->len - sub != n_len + ETHER_ADDR_LEN + 3 || n_len >= IFNAMSIZ || oid->subs[sub + n_len + 1] != ETHER_ADDR_LEN) return (NULL); for (i = 0; i < n_len; i++) bif_name[i] = oid->subs[n_len + i + 1]; bif_name[i] = '\0'; for (i = 1; i <= ETHER_ADDR_LEN; i++) tp_addr[i - 1] = oid->subs[n_len + i + 1]; if ((bif = bridge_if_find_ifname(bif_name)) == NULL) return (NULL); return (bridge_addrs_find(tp_addr, bif)); } /* * Find the next bridge address entry by the bridge interface name * and the address DST Mac. */ static struct tp_entry * bridge_addrs_begemot_getnext(const struct asn_oid *oid, uint sub) { uint i, n_len; uint8_t tp_addr[ETHER_ADDR_LEN]; char bif_name[IFNAMSIZ]; struct bridge_if *bif; struct tp_entry *tp; if (oid->len - sub == 0) return (bridge_addrs_first()); n_len = oid->subs[sub]; if (oid->len - sub != n_len + ETHER_ADDR_LEN + 2 || n_len >= IFNAMSIZ || oid->subs[sub + n_len + 1] != ETHER_ADDR_LEN) return (NULL); for (i = 1; i <= n_len; i++) bif_name[i - 1] = oid->subs[sub + i]; bif_name[i - 1] = '\0'; for (i = 1; i <= ETHER_ADDR_LEN; i++) tp_addr[i - 1] = oid->subs[sub + n_len + i + 1]; if ((bif = bridge_if_find_ifname(bif_name)) == NULL || (tp = bridge_addrs_find(tp_addr, bif)) == NULL) return (NULL); return (bridge_addrs_next(tp)); } int op_begemot_tp_fdb(struct snmp_context *c __unused, struct snmp_value *val, uint sub, uint iidx __unused, enum snmp_op op) { struct tp_entry *te; if (time(NULL) - address_list_age > bridge_get_data_maxage()) bridge_update_all_addrs(); switch (op) { case SNMP_OP_GET: if ((te = bridge_addrs_begemot_get(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); goto get; case SNMP_OP_GETNEXT: if ((te = bridge_addrs_begemot_getnext(&val->var, sub)) == NULL || bridge_addrs_begemot_index_append(&val->var, sub, te) < 0) return (SNMP_ERR_NOSUCHNAME); goto get; case SNMP_OP_SET: return (SNMP_ERR_NOT_WRITEABLE); case SNMP_OP_ROLLBACK: case SNMP_OP_COMMIT: break; } abort(); get: switch (val->var.subs[sub - 1]) { case LEAF_begemotBridgeTpFdbAddress: return (string_get(val, te->tp_addr, ETHER_ADDR_LEN)); case LEAF_begemotBridgeTpFdbPort: val->v.integer = te->port_no; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeTpFdbStatus: val->v.integer = te->status; return (SNMP_ERR_NOERROR); } abort(); } Index: head/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_if.c =================================================================== --- head/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_if.c (revision 335884) +++ head/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_if.c (revision 335885) @@ -1,1481 +1,1482 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2006 Shteryana Shopova * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * Bridge MIB implementation for SNMPd. * Bridge interface objects. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#define SNMPTREE_TYPES #include "bridge_tree.h" #include "bridge_snmp.h" #include "bridge_oid.h" static const struct asn_oid oid_newRoot = OIDX_newRoot; static const struct asn_oid oid_TopologyChange = OIDX_topologyChange; static const struct asn_oid oid_begemotBrigeName = \ OIDX_begemotBridgeBaseName; static const struct asn_oid oid_begemotNewRoot = OIDX_begemotBridgeNewRoot; static const struct asn_oid oid_begemotTopologyChange = \ OIDX_begemotBridgeTopologyChange; TAILQ_HEAD(bridge_ifs, bridge_if); /* * Free the bridge interface list. */ static void bridge_ifs_free(struct bridge_ifs *headp) { struct bridge_if *b; while ((b = TAILQ_FIRST(headp)) != NULL) { TAILQ_REMOVE(headp, b, b_if); free(b); } } /* * Insert an entry in the bridge interface TAILQ. Keep the * TAILQ sorted by the bridge's interface name. */ static void bridge_ifs_insert(struct bridge_ifs *headp, struct bridge_if *b) { struct bridge_if *temp; if ((temp = TAILQ_FIRST(headp)) == NULL || strcmp(b->bif_name, temp->bif_name) < 0) { TAILQ_INSERT_HEAD(headp, b, b_if); return; } TAILQ_FOREACH(temp, headp, b_if) if(strcmp(b->bif_name, temp->bif_name) < 0) TAILQ_INSERT_BEFORE(temp, b, b_if); TAILQ_INSERT_TAIL(headp, b, b_if); } /* The global bridge interface list. */ static struct bridge_ifs bridge_ifs = TAILQ_HEAD_INITIALIZER(bridge_ifs); static time_t bridge_list_age; /* * Free the global list. */ void bridge_ifs_fini(void) { bridge_ifs_free(&bridge_ifs); } /* * Find a bridge interface entry by the bridge interface system index. */ struct bridge_if * bridge_if_find_ifs(uint32_t sysindex) { struct bridge_if *b; TAILQ_FOREACH(b, &bridge_ifs, b_if) if (b->sysindex == sysindex) return (b); return (NULL); } /* * Find a bridge interface entry by the bridge interface name. */ struct bridge_if * bridge_if_find_ifname(const char *b_name) { struct bridge_if *b; TAILQ_FOREACH(b, &bridge_ifs, b_if) if (strcmp(b_name, b->bif_name) == 0) return (b); return (NULL); } /* * Find a bridge name by the bridge interface system index. */ const char * bridge_if_find_name(uint32_t sysindex) { struct bridge_if *b; TAILQ_FOREACH(b, &bridge_ifs, b_if) if (b->sysindex == sysindex) return (b->bif_name); return (NULL); } /* * Given two bridge interfaces' system indexes, find their * corresponding names and return the result of the name * comparison. Returns: * error : -2 * i1 < i2 : -1 * i1 > i2 : +1 * i1 = i2 : 0 */ int bridge_compare_sysidx(uint32_t i1, uint32_t i2) { int c; const char *b1, *b2; if (i1 == i2) return (0); if ((b1 = bridge_if_find_name(i1)) == NULL) { syslog(LOG_ERR, "Bridge interface %d does not exist", i1); return (-2); } if ((b2 = bridge_if_find_name(i2)) == NULL) { syslog(LOG_ERR, "Bridge interface %d does not exist", i2); return (-2); } if ((c = strcmp(b1, b2)) < 0) return (-1); else if (c > 0) return (1); return (0); } /* * Fetch the first bridge interface from the list. */ struct bridge_if * bridge_first_bif(void) { return (TAILQ_FIRST(&bridge_ifs)); } /* * Fetch the next bridge interface from the list. */ struct bridge_if * bridge_next_bif(struct bridge_if *b_pr) { return (TAILQ_NEXT(b_pr, b_if)); } /* * Create a new entry for a bridge interface and insert * it in the list. */ static struct bridge_if * bridge_new_bif(const char *bif_n, uint32_t sysindex, const u_char *physaddr) { struct bridge_if *bif; if ((bif = (struct bridge_if *) malloc(sizeof(*bif)))== NULL) { syslog(LOG_ERR, "bridge new interface failed: %s", strerror(errno)); return (NULL); } bzero(bif, sizeof(struct bridge_if)); strlcpy(bif->bif_name, bif_n, IFNAMSIZ); bcopy(physaddr, bif->br_addr.octet, ETHER_ADDR_LEN); bif->sysindex = sysindex; bif->br_type = BaseType_transparent_only; /* 1 - all bridges default hold time * 100 - centi-seconds */ bif->hold_time = 1 * 100; bif->prot_spec = dot1dStpProtocolSpecification_ieee8021d; bridge_ifs_insert(&bridge_ifs, bif); return (bif); } /* * Remove a bridge interface from the list, freeing all it's ports * and address entries. */ void bridge_remove_bif(struct bridge_if *bif) { bridge_members_free(bif); bridge_addrs_free(bif); TAILQ_REMOVE(&bridge_ifs, bif, b_if); free(bif); } /* * Prepare the variable (bridge interface name) for the private * begemot notifications. */ static struct snmp_value* bridge_basename_var(struct bridge_if *bif, struct snmp_value* b_val) { uint i; b_val->var = oid_begemotBrigeName; b_val->var.subs[b_val->var.len++] = strlen(bif->bif_name); if ((b_val->v.octetstring.octets = (u_char *) malloc(strlen(bif->bif_name))) == NULL) return (NULL); for (i = 0; i < strlen(bif->bif_name); i++) b_val->var.subs[b_val->var.len++] = bif->bif_name[i]; b_val->v.octetstring.len = strlen(bif->bif_name); bcopy(bif->bif_name, b_val->v.octetstring.octets, strlen(bif->bif_name)); b_val->syntax = SNMP_SYNTAX_OCTETSTRING; return (b_val); } /* * Compare the values of the old and the new root port and * send a new root notification, if they are not matching. */ static void bridge_new_root(struct bridge_if *bif) { struct snmp_value bif_idx; if (bridge_get_default() == bif) snmp_send_trap(&oid_newRoot, (struct snmp_value *) NULL); if (bridge_basename_var(bif, &bif_idx) == NULL) return; snmp_send_trap(&oid_begemotTopologyChange, &bif_idx, (struct snmp_value *) NULL); } /* * Compare the new and old topology change times and send a * topology change notification if necessary. */ static void bridge_top_change(struct bridge_if *bif) { struct snmp_value bif_idx; if (bridge_get_default() == bif) snmp_send_trap(&oid_TopologyChange, (struct snmp_value *) NULL); if (bridge_basename_var(bif, &bif_idx) == NULL) return; snmp_send_trap(&oid_begemotNewRoot, &bif_idx, (struct snmp_value *) NULL); } static int bridge_if_create(const char* b_name, int8_t up) { if (bridge_create(b_name) < 0) return (-1); if (up == 1 && (bridge_set_if_up(b_name, 1) < 0)) return (-1); /* * Do not create a new bridge entry here - * wait until the mibII module notifies us. */ return (0); } static int bridge_if_destroy(struct bridge_if *bif) { if (bridge_destroy(bif->bif_name) < 0) return (-1); bridge_remove_bif(bif); return (0); } /* * Calculate the timeticks since the last topology change. */ static int bridge_get_time_since_tc(struct bridge_if *bif, uint32_t *ticks) { struct timeval ct; if (gettimeofday(&ct, NULL) < 0) { syslog(LOG_ERR, "bridge get time since last TC:" "gettimeofday failed: %s", strerror(errno)); return (-1); } if (ct.tv_usec - bif->last_tc_time.tv_usec < 0) { ct.tv_sec -= 1; ct.tv_usec += 1000000; } ct.tv_sec -= bif->last_tc_time.tv_sec; ct.tv_usec -= bif->last_tc_time.tv_usec; *ticks = ct.tv_sec * 100 + ct.tv_usec/10000; return (0); } /* * Update the info we have for a single bridge interface. * Return: * 1, if successful * 0, if the interface was deleted * -1, error occurred while fetching the info from the kernel. */ static int bridge_update_bif(struct bridge_if *bif) { struct mibif *ifp; /* Walk through the mibII interface list. */ for (ifp = mib_first_if(); ifp != NULL; ifp = mib_next_if(ifp)) if (strcmp(ifp->name, bif->bif_name) == 0) break; if (ifp == NULL) { /* Ops, we do not exist anymore. */ bridge_remove_bif(bif); return (0); } if (ifp->physaddr != NULL ) bcopy(ifp->physaddr, bif->br_addr.octet, ETHER_ADDR_LEN); else bridge_get_basemac(bif->bif_name, bif->br_addr.octet, ETHER_ADDR_LEN); if (ifp->mib.ifmd_flags & IFF_RUNNING) bif->if_status = RowStatus_active; else bif->if_status = RowStatus_notInService; switch (bridge_getinfo_bif(bif)) { case 2: bridge_new_root(bif); break; case 1: bridge_top_change(bif); break; case -1: bridge_remove_bif(bif); return (-1); default: break; } /* * The number of ports is accessible via SNMP - * update the ports each time the bridge interface data * is refreshed too. */ bif->num_ports = bridge_update_memif(bif); bif->entry_age = time(NULL); return (1); } /* * Update all bridge interfaces' ports only - * make sure each bridge interface exists first. */ void bridge_update_all_ports(void) { struct mibif *ifp; struct bridge_if *bif, *t_bif; for (bif = bridge_first_bif(); bif != NULL; bif = t_bif) { t_bif = bridge_next_bif(bif); for (ifp = mib_first_if(); ifp != NULL; ifp = mib_next_if(ifp)) if (strcmp(ifp->name, bif->bif_name) == 0) break; if (ifp != NULL) bif->num_ports = bridge_update_memif(bif); else /* Ops, we do not exist anymore. */ bridge_remove_bif(bif); } bridge_ports_update_listage(); } /* * Update all addresses only. */ void bridge_update_all_addrs(void) { struct mibif *ifp; struct bridge_if *bif, *t_bif; for (bif = bridge_first_bif(); bif != NULL; bif = t_bif) { t_bif = bridge_next_bif(bif); for (ifp = mib_first_if(); ifp != NULL; ifp = mib_next_if(ifp)) if (strcmp(ifp->name, bif->bif_name) == 0) break; if (ifp != NULL) bif->num_addrs = bridge_update_addrs(bif); else /* Ops, we don't exist anymore. */ bridge_remove_bif(bif); } bridge_addrs_update_listage(); } /* * Update only the bridge interfaces' data - skip addresses. */ void bridge_update_all_ifs(void) { struct bridge_if *bif, *t_bif; for (bif = bridge_first_bif(); bif != NULL; bif = t_bif) { t_bif = bridge_next_bif(bif); bridge_update_bif(bif); } bridge_ports_update_listage(); bridge_list_age = time(NULL); } /* * Update all info we have for all bridges. */ void bridge_update_all(void *arg __unused) { struct bridge_if *bif, *t_bif; for (bif = bridge_first_bif(); bif != NULL; bif = t_bif) { t_bif = bridge_next_bif(bif); if (bridge_update_bif(bif) <= 0) continue; /* Update our learnt addresses. */ bif->num_addrs = bridge_update_addrs(bif); } bridge_list_age = time(NULL); bridge_ports_update_listage(); bridge_addrs_update_listage(); } /* * Callback for polling our last topology change time - * check whether we are root or whether a TC was detected once every * 30 seconds, so that we can send the newRoot and TopologyChange traps * on time. The rest of the data is polled only once every 5 min. */ void bridge_update_tc_time(void *arg __unused) { struct bridge_if *bif; struct mibif *ifp; TAILQ_FOREACH(bif, &bridge_ifs, b_if) { /* Walk through the mibII interface list. */ for (ifp = mib_first_if(); ifp != NULL; ifp = mib_next_if(ifp)) if (strcmp(ifp->name, bif->bif_name) == 0) break; if (ifp == NULL) { bridge_remove_bif(bif); continue; } switch (bridge_get_op_param(bif)) { case 2: bridge_new_root(bif); break; case 1: bridge_top_change(bif); break; } } } /* * Callback for handling new bridge interface creation. */ int bridge_attach_newif(struct mibif *ifp) { u_char mac[ETHER_ADDR_LEN]; struct bridge_if *bif; if (ifp->mib.ifmd_data.ifi_type != IFT_BRIDGE) return (0); /* Make sure it does not exist in our list. */ TAILQ_FOREACH(bif, &bridge_ifs, b_if) if(strcmp(bif->bif_name, ifp->name) == 0) { syslog(LOG_ERR, "bridge interface %s already " "in list", bif->bif_name); return (-1); } if (ifp->physaddr == NULL) { if (bridge_get_basemac(ifp->name, mac, sizeof(mac)) == NULL) { syslog(LOG_ERR, "bridge attach new %s failed - " "no bridge mac address", ifp->name); return (-1); } } else bcopy(ifp->physaddr, &mac, sizeof(mac)); if ((bif = bridge_new_bif(ifp->name, ifp->sysindex, mac)) == NULL) return (-1); if (ifp->mib.ifmd_flags & IFF_RUNNING) bif->if_status = RowStatus_active; else bif->if_status = RowStatus_notInService; /* Skip sending notifications if the interface was just created. */ if (bridge_getinfo_bif(bif) < 0 || (bif->num_ports = bridge_getinfo_bif_ports(bif)) < 0 || (bif->num_addrs = bridge_getinfo_bif_addrs(bif)) < 0) { bridge_remove_bif(bif); return (-1); } /* Check whether we are the default bridge interface. */ if (strcmp(ifp->name, bridge_get_default_name()) == 0) bridge_set_default(bif); return (0); } void bridge_ifs_dump(void) { struct bridge_if *bif; for (bif = bridge_first_bif(); bif != NULL; bif = bridge_next_bif(bif)) { syslog(LOG_ERR, "Bridge %s, index - %d", bif->bif_name, bif->sysindex); bridge_ports_dump(bif); bridge_addrs_dump(bif); } } /* * RFC4188 specifics. */ int op_dot1d_base(struct snmp_context *ctx __unused, struct snmp_value *value, uint sub, uint iidx __unused, enum snmp_op op) { struct bridge_if *bif; if ((bif = bridge_get_default()) == NULL) return (SNMP_ERR_NOSUCHNAME); if (time(NULL) - bif->entry_age > bridge_get_data_maxage() && bridge_update_bif(bif) <= 0) /* It was just deleted. */ return (SNMP_ERR_NOSUCHNAME); switch (op) { case SNMP_OP_GET: switch (value->var.subs[sub - 1]) { case LEAF_dot1dBaseBridgeAddress: return (string_get(value, bif->br_addr.octet, ETHER_ADDR_LEN)); case LEAF_dot1dBaseNumPorts: value->v.integer = bif->num_ports; return (SNMP_ERR_NOERROR); case LEAF_dot1dBaseType: value->v.integer = bif->br_type; return (SNMP_ERR_NOERROR); } abort(); case SNMP_OP_SET: return (SNMP_ERR_NOT_WRITEABLE); case SNMP_OP_GETNEXT: case SNMP_OP_ROLLBACK: case SNMP_OP_COMMIT: break; } abort(); } int op_dot1d_stp(struct snmp_context *ctx, struct snmp_value *val, uint sub, uint iidx __unused, enum snmp_op op) { struct bridge_if *bif; if ((bif = bridge_get_default()) == NULL) return (SNMP_ERR_NOSUCHNAME); if (time(NULL) - bif->entry_age > bridge_get_data_maxage() && bridge_update_bif(bif) <= 0) /* It was just deleted. */ return (SNMP_ERR_NOSUCHNAME); switch (op) { case SNMP_OP_GET: switch (val->var.subs[sub - 1]) { case LEAF_dot1dStpProtocolSpecification: val->v.integer = bif->prot_spec; return (SNMP_ERR_NOERROR); case LEAF_dot1dStpPriority: val->v.integer = bif->priority; return (SNMP_ERR_NOERROR); case LEAF_dot1dStpTimeSinceTopologyChange: if (bridge_get_time_since_tc(bif, &(val->v.uint32)) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case LEAF_dot1dStpTopChanges: val->v.uint32 = bif->top_changes; return (SNMP_ERR_NOERROR); case LEAF_dot1dStpDesignatedRoot: return (string_get(val, bif->design_root, SNMP_BRIDGE_ID_LEN)); case LEAF_dot1dStpRootCost: val->v.integer = bif->root_cost; return (SNMP_ERR_NOERROR); case LEAF_dot1dStpRootPort: val->v.integer = bif->root_port; return (SNMP_ERR_NOERROR); case LEAF_dot1dStpMaxAge: val->v.integer = bif->max_age; return (SNMP_ERR_NOERROR); case LEAF_dot1dStpHelloTime: val->v.integer = bif->hello_time; return (SNMP_ERR_NOERROR); case LEAF_dot1dStpHoldTime: val->v.integer = bif->hold_time; return (SNMP_ERR_NOERROR); case LEAF_dot1dStpForwardDelay: val->v.integer = bif->fwd_delay; return (SNMP_ERR_NOERROR); case LEAF_dot1dStpBridgeMaxAge: val->v.integer = bif->bridge_max_age; return (SNMP_ERR_NOERROR); case LEAF_dot1dStpBridgeHelloTime: val->v.integer = bif->bridge_hello_time; return (SNMP_ERR_NOERROR); case LEAF_dot1dStpBridgeForwardDelay: val->v.integer = bif->bridge_fwd_delay; return (SNMP_ERR_NOERROR); case LEAF_dot1dStpVersion: val->v.integer = bif->stp_version; return (SNMP_ERR_NOERROR); case LEAF_dot1dStpTxHoldCount: val->v.integer = bif->tx_hold_count; return (SNMP_ERR_NOERROR); } abort(); case SNMP_OP_GETNEXT: abort(); case SNMP_OP_SET: switch (val->var.subs[sub - 1]) { case LEAF_dot1dStpPriority: if (val->v.integer > SNMP_BRIDGE_MAX_PRIORITY || val->v.integer % 4096 != 0) return (SNMP_ERR_WRONG_VALUE); ctx->scratch->int1 = bif->priority; if (bridge_set_priority(bif, val->v.integer) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case LEAF_dot1dStpBridgeMaxAge: if (val->v.integer < SNMP_BRIDGE_MIN_MAGE || val->v.integer > SNMP_BRIDGE_MAX_MAGE) return (SNMP_ERR_WRONG_VALUE); ctx->scratch->int1 = bif->bridge_max_age; if (bridge_set_maxage(bif, val->v.integer) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case LEAF_dot1dStpBridgeHelloTime: if (val->v.integer < SNMP_BRIDGE_MIN_HTIME || val->v.integer > SNMP_BRIDGE_MAX_HTIME) return (SNMP_ERR_WRONG_VALUE); ctx->scratch->int1 = bif->bridge_hello_time; if (bridge_set_hello_time(bif, val->v.integer) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case LEAF_dot1dStpBridgeForwardDelay: if (val->v.integer < SNMP_BRIDGE_MIN_FDELAY || val->v.integer > SNMP_BRIDGE_MAX_FDELAY) return (SNMP_ERR_WRONG_VALUE); ctx->scratch->int1 = bif->bridge_fwd_delay; if (bridge_set_forward_delay(bif, val->v.integer) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case LEAF_dot1dStpVersion: if (val->v.integer != dot1dStpVersion_stpCompatible && val->v.integer != dot1dStpVersion_rstp) return (SNMP_ERR_WRONG_VALUE); ctx->scratch->int1 = bif->stp_version; if (bridge_set_stp_version(bif, val->v.integer) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case LEAF_dot1dStpTxHoldCount: if (val->v.integer < SNMP_BRIDGE_MIN_TXHC || val->v.integer > SNMP_BRIDGE_MAX_TXHC) return (SNMP_ERR_WRONG_VALUE); ctx->scratch->int1 = bif->tx_hold_count; if (bridge_set_tx_hold_count(bif, val->v.integer) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case LEAF_dot1dStpProtocolSpecification: case LEAF_dot1dStpTimeSinceTopologyChange: case LEAF_dot1dStpTopChanges: case LEAF_dot1dStpDesignatedRoot: case LEAF_dot1dStpRootCost: case LEAF_dot1dStpRootPort: case LEAF_dot1dStpMaxAge: case LEAF_dot1dStpHelloTime: case LEAF_dot1dStpHoldTime: case LEAF_dot1dStpForwardDelay: return (SNMP_ERR_NOT_WRITEABLE); } abort(); case SNMP_OP_ROLLBACK: switch (val->var.subs[sub - 1]) { case LEAF_dot1dStpPriority: bridge_set_priority(bif, ctx->scratch->int1); break; case LEAF_dot1dStpBridgeMaxAge: bridge_set_maxage(bif, ctx->scratch->int1); break; case LEAF_dot1dStpBridgeHelloTime: bridge_set_hello_time(bif, ctx->scratch->int1); break; case LEAF_dot1dStpBridgeForwardDelay: bridge_set_forward_delay(bif, ctx->scratch->int1); break; case LEAF_dot1dStpVersion: bridge_set_stp_version(bif, ctx->scratch->int1); break; case LEAF_dot1dStpTxHoldCount: bridge_set_tx_hold_count(bif, ctx->scratch->int1); break; } return (SNMP_ERR_NOERROR); case SNMP_OP_COMMIT: return (SNMP_ERR_NOERROR); } abort(); } int op_dot1d_tp(struct snmp_context *ctx, struct snmp_value *value, uint sub, uint iidx __unused, enum snmp_op op) { struct bridge_if *bif; if ((bif = bridge_get_default()) == NULL) return (SNMP_ERR_NOSUCHNAME); if (time(NULL) - bif->entry_age > bridge_get_data_maxage() && bridge_update_bif(bif) <= 0) /* It was just deleted. */ return (SNMP_ERR_NOSUCHNAME); switch (op) { case SNMP_OP_GET: switch (value->var.subs[sub - 1]) { case LEAF_dot1dTpLearnedEntryDiscards: value->v.uint32 = bif->lrnt_drops; return (SNMP_ERR_NOERROR); case LEAF_dot1dTpAgingTime: value->v.integer = bif->age_time; return (SNMP_ERR_NOERROR); } abort(); case SNMP_OP_GETNEXT: abort(); case SNMP_OP_SET: switch (value->var.subs[sub - 1]) { case LEAF_dot1dTpLearnedEntryDiscards: return (SNMP_ERR_NOT_WRITEABLE); case LEAF_dot1dTpAgingTime: if (value->v.integer < SNMP_BRIDGE_MIN_AGE_TIME || value->v.integer > SNMP_BRIDGE_MAX_AGE_TIME) return (SNMP_ERR_WRONG_VALUE); ctx->scratch->int1 = bif->age_time; if (bridge_set_aging_time(bif, value->v.integer) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); } abort(); case SNMP_OP_ROLLBACK: if (value->var.subs[sub - 1] == LEAF_dot1dTpAgingTime) bridge_set_aging_time(bif, ctx->scratch->int1); return (SNMP_ERR_NOERROR); case SNMP_OP_COMMIT: return (SNMP_ERR_NOERROR); } abort(); } /* * Private BEGEMOT-BRIDGE-MIB specifics. */ /* * Get the bridge name from an OID index. */ static char * bridge_name_index_get(const struct asn_oid *oid, uint sub, char *b_name) { uint i; if (oid->len - sub != oid->subs[sub] + 1 || oid->subs[sub] >= IFNAMSIZ) return (NULL); for (i = 0; i < oid->subs[sub]; i++) b_name[i] = oid->subs[sub + i + 1]; b_name[i] = '\0'; return (b_name); } static void bridge_if_index_append(struct asn_oid *oid, uint sub, const struct bridge_if *bif) { uint i; oid->len = sub + strlen(bif->bif_name) + 1; oid->subs[sub] = strlen(bif->bif_name); for (i = 1; i <= strlen(bif->bif_name); i++) oid->subs[sub + i] = bif->bif_name[i - 1]; } static struct bridge_if * bridge_if_index_get(const struct asn_oid *oid, uint sub) { uint i; char bif_name[IFNAMSIZ]; if (oid->len - sub != oid->subs[sub] + 1 || oid->subs[sub] >= IFNAMSIZ) return (NULL); for (i = 0; i < oid->subs[sub]; i++) bif_name[i] = oid->subs[sub + i + 1]; bif_name[i] = '\0'; return (bridge_if_find_ifname(bif_name)); } static struct bridge_if * bridge_if_index_getnext(const struct asn_oid *oid, uint sub) { uint i; char bif_name[IFNAMSIZ]; struct bridge_if *bif; if (oid->len - sub == 0) return (bridge_first_bif()); if (oid->len - sub != oid->subs[sub] + 1 || oid->subs[sub] >= IFNAMSIZ) return (NULL); for (i = 0; i < oid->subs[sub]; i++) bif_name[i] = oid->subs[sub + i + 1]; bif_name[i] = '\0'; if ((bif = bridge_if_find_ifname(bif_name)) == NULL) return (NULL); return (bridge_next_bif(bif)); } static int bridge_set_if_status(struct snmp_context *ctx, struct snmp_value *val, uint sub) { struct bridge_if *bif; char bif_name[IFNAMSIZ]; bif = bridge_if_index_get(&val->var, sub); switch (val->v.integer) { case RowStatus_active: if (bif == NULL) return (SNMP_ERR_INCONS_VALUE); ctx->scratch->int1 = bif->if_status; switch (bif->if_status) { case RowStatus_active: return (SNMP_ERR_NOERROR); case RowStatus_notInService: if (bridge_set_if_up(bif->bif_name, 1) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); default: break; } return (SNMP_ERR_INCONS_VALUE); case RowStatus_notInService: if (bif == NULL) return (SNMP_ERR_INCONS_VALUE); ctx->scratch->int1 = bif->if_status; switch (bif->if_status) { case RowStatus_active: if (bridge_set_if_up(bif->bif_name, 1) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case RowStatus_notInService: return (SNMP_ERR_NOERROR); default: break; } return (SNMP_ERR_INCONS_VALUE); case RowStatus_notReady: return (SNMP_ERR_INCONS_VALUE); case RowStatus_createAndGo: if (bif != NULL) return (SNMP_ERR_INCONS_VALUE); ctx->scratch->int1 = RowStatus_destroy; if (bridge_name_index_get(&val->var, sub, bif_name) == NULL) return (SNMP_ERR_BADVALUE); if (bridge_if_create(bif_name, 1) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case RowStatus_createAndWait: if (bif != NULL) return (SNMP_ERR_INCONS_VALUE); if (bridge_name_index_get(&val->var, sub, bif_name) == NULL) return (SNMP_ERR_BADVALUE); ctx->scratch->int1 = RowStatus_destroy; if (bridge_if_create(bif_name, 0) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case RowStatus_destroy: if (bif == NULL) return (SNMP_ERR_NOSUCHNAME); ctx->scratch->int1 = bif->if_status; bif->if_status = RowStatus_destroy; } return (SNMP_ERR_NOERROR); } static int bridge_rollback_if_status(struct snmp_context *ctx, struct snmp_value *val, uint sub) { struct bridge_if *bif; if ((bif = bridge_if_index_get(&val->var, sub)) == NULL) return (SNMP_ERR_GENERR); switch (ctx->scratch->int1) { case RowStatus_destroy: bridge_if_destroy(bif); return (SNMP_ERR_NOERROR); case RowStatus_notInService: if (bif->if_status != ctx->scratch->int1) bridge_set_if_up(bif->bif_name, 0); bif->if_status = RowStatus_notInService; return (SNMP_ERR_NOERROR); case RowStatus_active: if (bif->if_status != ctx->scratch->int1) bridge_set_if_up(bif->bif_name, 1); bif->if_status = RowStatus_active; return (SNMP_ERR_NOERROR); } abort(); } static int bridge_commit_if_status(struct snmp_value *val, uint sub) { struct bridge_if *bif; if ((bif = bridge_if_index_get(&val->var, sub)) == NULL) return (SNMP_ERR_GENERR); if (bif->if_status == RowStatus_destroy && bridge_if_destroy(bif) < 0) return (SNMP_ERR_COMMIT_FAILED); return (SNMP_ERR_NOERROR); } int op_begemot_base_bridge(struct snmp_context *ctx, struct snmp_value *val, uint sub, uint iidx __unused, enum snmp_op op) { struct bridge_if *bif; if (time(NULL) - bridge_list_age > bridge_get_data_maxage()) bridge_update_all_ifs(); switch (op) { case SNMP_OP_GET: if ((bif = bridge_if_index_get(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); goto get; case SNMP_OP_GETNEXT: if ((bif = bridge_if_index_getnext(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); bridge_if_index_append(&val->var, sub, bif); goto get; case SNMP_OP_SET: switch (val->var.subs[sub - 1]) { case LEAF_begemotBridgeBaseStatus: return (bridge_set_if_status(ctx, val, sub)); case LEAF_begemotBridgeBaseName: case LEAF_begemotBridgeBaseAddress: case LEAF_begemotBridgeBaseNumPorts: case LEAF_begemotBridgeBaseType: return (SNMP_ERR_NOT_WRITEABLE); } abort(); case SNMP_OP_ROLLBACK: return (bridge_rollback_if_status(ctx, val, sub)); case SNMP_OP_COMMIT: return (bridge_commit_if_status(val, sub)); } abort(); get: switch (val->var.subs[sub - 1]) { case LEAF_begemotBridgeBaseName: return (string_get(val, bif->bif_name, -1)); case LEAF_begemotBridgeBaseAddress: return (string_get(val, bif->br_addr.octet, ETHER_ADDR_LEN)); case LEAF_begemotBridgeBaseNumPorts: val->v.integer = bif->num_ports; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeBaseType: val->v.integer = bif->br_type; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeBaseStatus: val->v.integer = bif->if_status; return (SNMP_ERR_NOERROR); } abort(); } int op_begemot_stp(struct snmp_context *ctx, struct snmp_value *val, uint sub, uint iidx __unused, enum snmp_op op) { struct bridge_if *bif; if (time(NULL) - bridge_list_age > bridge_get_data_maxage()) bridge_update_all_ifs(); switch (op) { case SNMP_OP_GET: if ((bif = bridge_if_index_get(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); goto get; case SNMP_OP_GETNEXT: if ((bif = bridge_if_index_getnext(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); bridge_if_index_append(&val->var, sub, bif); goto get; case SNMP_OP_SET: if ((bif = bridge_if_index_get(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); switch (val->var.subs[sub - 1]) { case LEAF_begemotBridgeStpPriority: if (val->v.integer > SNMP_BRIDGE_MAX_PRIORITY || val->v.integer % 4096 != 0) return (SNMP_ERR_WRONG_VALUE); ctx->scratch->int1 = bif->priority; if (bridge_set_priority(bif, val->v.integer) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpBridgeMaxAge: if (val->v.integer < SNMP_BRIDGE_MIN_MAGE || val->v.integer > SNMP_BRIDGE_MAX_MAGE) return (SNMP_ERR_WRONG_VALUE); ctx->scratch->int1 = bif->bridge_max_age; if (bridge_set_maxage(bif, val->v.integer) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpBridgeHelloTime: if (val->v.integer < SNMP_BRIDGE_MIN_HTIME || val->v.integer > SNMP_BRIDGE_MAX_HTIME) return (SNMP_ERR_WRONG_VALUE); ctx->scratch->int1 = bif->bridge_hello_time; if (bridge_set_hello_time(bif, val->v.integer) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpBridgeForwardDelay: if (val->v.integer < SNMP_BRIDGE_MIN_FDELAY || val->v.integer > SNMP_BRIDGE_MAX_FDELAY) return (SNMP_ERR_WRONG_VALUE); ctx->scratch->int1 = bif->bridge_fwd_delay; if (bridge_set_forward_delay(bif, val->v.integer) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpVersion: if (val->v.integer != begemotBridgeStpVersion_stpCompatible && val->v.integer != begemotBridgeStpVersion_rstp) return (SNMP_ERR_WRONG_VALUE); ctx->scratch->int1 = bif->stp_version; if (bridge_set_stp_version(bif, val->v.integer) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpTxHoldCount: if (val->v.integer < SNMP_BRIDGE_MIN_TXHC || val->v.integer > SNMP_BRIDGE_MAX_TXHC) return (SNMP_ERR_WRONG_VALUE); ctx->scratch->int1 = bif->tx_hold_count; if (bridge_set_tx_hold_count(bif, val->v.integer) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpProtocolSpecification: case LEAF_begemotBridgeStpTimeSinceTopologyChange: case LEAF_begemotBridgeStpTopChanges: case LEAF_begemotBridgeStpDesignatedRoot: case LEAF_begemotBridgeStpRootCost: case LEAF_begemotBridgeStpRootPort: case LEAF_begemotBridgeStpMaxAge: case LEAF_begemotBridgeStpHelloTime: case LEAF_begemotBridgeStpHoldTime: case LEAF_begemotBridgeStpForwardDelay: return (SNMP_ERR_NOT_WRITEABLE); } abort(); case SNMP_OP_ROLLBACK: if ((bif = bridge_if_index_get(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); switch (val->var.subs[sub - 1]) { case LEAF_begemotBridgeStpPriority: bridge_set_priority(bif, ctx->scratch->int1); break; case LEAF_begemotBridgeStpBridgeMaxAge: bridge_set_maxage(bif, ctx->scratch->int1); break; case LEAF_begemotBridgeStpBridgeHelloTime: bridge_set_hello_time(bif, ctx->scratch->int1); break; case LEAF_begemotBridgeStpBridgeForwardDelay: bridge_set_forward_delay(bif, ctx->scratch->int1); break; case LEAF_begemotBridgeStpVersion: bridge_set_stp_version(bif, ctx->scratch->int1); break; case LEAF_begemotBridgeStpTxHoldCount: bridge_set_tx_hold_count(bif, ctx->scratch->int1); break; } return (SNMP_ERR_NOERROR); case SNMP_OP_COMMIT: return (SNMP_ERR_NOERROR); } abort(); get: switch (val->var.subs[sub - 1]) { case LEAF_begemotBridgeStpProtocolSpecification: val->v.integer = bif->prot_spec; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpPriority: val->v.integer = bif->priority; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpTimeSinceTopologyChange: if (bridge_get_time_since_tc(bif, &(val->v.uint32)) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpTopChanges: val->v.uint32 = bif->top_changes; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpDesignatedRoot: return (string_get(val, bif->design_root, SNMP_BRIDGE_ID_LEN)); case LEAF_begemotBridgeStpRootCost: val->v.integer = bif->root_cost; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpRootPort: val->v.integer = bif->root_port; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpMaxAge: val->v.integer = bif->max_age; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpHelloTime: val->v.integer = bif->hello_time; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpHoldTime: val->v.integer = bif->hold_time; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpForwardDelay: val->v.integer = bif->fwd_delay; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpBridgeMaxAge: val->v.integer = bif->bridge_max_age; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpBridgeHelloTime: val->v.integer = bif->bridge_hello_time; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpBridgeForwardDelay: val->v.integer = bif->bridge_fwd_delay; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpVersion: val->v.integer = bif->stp_version; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpTxHoldCount: val->v.integer = bif->tx_hold_count; return (SNMP_ERR_NOERROR); } abort(); } int op_begemot_tp(struct snmp_context *ctx, struct snmp_value *val, uint sub, uint iidx __unused, enum snmp_op op) { struct bridge_if *bif; if (time(NULL) - bridge_list_age > bridge_get_data_maxage()) bridge_update_all_ifs(); switch (op) { case SNMP_OP_GET: if ((bif = bridge_if_index_get(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); goto get; case SNMP_OP_GETNEXT: if ((bif = bridge_if_index_getnext(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); bridge_if_index_append(&val->var, sub, bif); goto get; case SNMP_OP_SET: if ((bif = bridge_if_index_get(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); switch (val->var.subs[sub - 1]) { case LEAF_begemotBridgeTpAgingTime: if (val->v.integer < SNMP_BRIDGE_MIN_AGE_TIME || val->v.integer > SNMP_BRIDGE_MAX_AGE_TIME) return (SNMP_ERR_WRONG_VALUE); ctx->scratch->int1 = bif->age_time; if (bridge_set_aging_time(bif, val->v.integer) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeTpMaxAddresses: ctx->scratch->int1 = bif->max_addrs; if (bridge_set_max_cache(bif, val->v.integer) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeTpLearnedEntryDiscards: return (SNMP_ERR_NOT_WRITEABLE); } abort(); case SNMP_OP_ROLLBACK: if ((bif = bridge_if_index_get(&val->var, sub)) == NULL) return (SNMP_ERR_GENERR); switch (val->var.subs[sub - 1]) { case LEAF_begemotBridgeTpAgingTime: bridge_set_aging_time(bif, ctx->scratch->int1); break; case LEAF_begemotBridgeTpMaxAddresses: bridge_set_max_cache(bif, ctx->scratch->int1); break; } return (SNMP_ERR_NOERROR); case SNMP_OP_COMMIT: return (SNMP_ERR_NOERROR); } abort(); get: switch (val->var.subs[sub - 1]) { case LEAF_begemotBridgeTpLearnedEntryDiscards: val->v.uint32 = bif->lrnt_drops; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeTpAgingTime: val->v.integer = bif->age_time; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeTpMaxAddresses: val->v.integer = bif->max_addrs; return (SNMP_ERR_NOERROR); } abort(); } Index: head/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_pf.c =================================================================== --- head/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_pf.c (revision 335884) +++ head/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_pf.c (revision 335885) @@ -1,118 +1,119 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2006 Shteryana Shopova * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * Bridge MIB implementation for SNMPd. * Bridge pfil controls. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include +#define SNMPTREE_TYPES #include "bridge_tree.h" #include "bridge_snmp.h" static int val2snmp_truth(uint8_t val) { if (val == 0) return (2); return (1); } static int snmp_truth2val(int32_t truth) { if (truth == 2) return (0); else if (truth == 1) return (1); return (-1); } int op_begemot_bridge_pf(struct snmp_context *ctx, struct snmp_value *val, uint sub, uint iidx __unused, enum snmp_op op) { int k_val; if (val->var.subs[sub - 1] > LEAF_begemotBridgeLayer2PfStatus) return (SNMP_ERR_NOSUCHNAME); switch (op) { case SNMP_OP_GETNEXT: abort(); case SNMP_OP_ROLLBACK: bridge_do_pfctl(val->var.subs[sub - 1] - 1, op, &(ctx->scratch->int1)); return (SNMP_ERR_NOERROR); case SNMP_OP_COMMIT: return (SNMP_ERR_NOERROR); case SNMP_OP_SET: ctx->scratch->int1 = bridge_get_pfval(val->var.subs[sub - 1]); if ((k_val = snmp_truth2val(val->v.integer)) < 0) return (SNMP_ERR_BADVALUE); return (SNMP_ERR_NOERROR); case SNMP_OP_GET: switch (val->var.subs[sub - 1]) { case LEAF_begemotBridgePfilStatus: case LEAF_begemotBridgePfilMembers: case LEAF_begemotBridgePfilIpOnly: case LEAF_begemotBridgeLayer2PfStatus: if (bridge_do_pfctl(val->var.subs[sub - 1] - 1, op, &k_val) < 0) return (SNMP_ERR_GENERR); val->v.integer = val2snmp_truth(k_val); return (SNMP_ERR_NOERROR); } abort(); } abort(); } Index: head/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_port.c =================================================================== --- head/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_port.c (revision 335884) +++ head/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_port.c (revision 335885) @@ -1,1515 +1,1516 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2006 Shteryana Shopova * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * Bridge MIB implementation for SNMPd. * Bridge ports. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#define SNMPTREE_TYPES #include "bridge_tree.h" #include "bridge_snmp.h" TAILQ_HEAD(bridge_ports, bridge_port); /* * Free the bridge base ports list. */ static void bridge_ports_free(struct bridge_ports *headp) { struct bridge_port *bp; while ((bp = TAILQ_FIRST(headp)) != NULL) { TAILQ_REMOVE(headp, bp, b_p); free(bp); } } /* * Free the bridge base ports from the base ports list, * members of a specified bridge interface only. */ static void bridge_port_memif_free(struct bridge_ports *headp, struct bridge_if *bif) { struct bridge_port *bp; while (bif->f_bp != NULL && bif->sysindex == bif->f_bp->sysindex) { bp = TAILQ_NEXT(bif->f_bp, b_p); TAILQ_REMOVE(headp, bif->f_bp, b_p); free(bif->f_bp); bif->f_bp = bp; } } /* * Insert a port entry in the base port TAILQ starting to search * for its place from the position of the first bridge port for the bridge * interface. Update the first bridge port if necessary. */ static void bridge_port_insert_at(struct bridge_ports *headp, struct bridge_port *bp, struct bridge_port **f_bp) { struct bridge_port *t1; assert(f_bp != NULL); for (t1 = *f_bp; t1 != NULL && bp->sysindex == t1->sysindex; t1 = TAILQ_NEXT(t1, b_p)) { if (bp->if_idx < t1->if_idx) { TAILQ_INSERT_BEFORE(t1, bp, b_p); if (*f_bp == t1) *f_bp = bp; return; } } /* * Handle the case when our first port was actually the * last element of the TAILQ. */ if (t1 == NULL) TAILQ_INSERT_TAIL(headp, bp, b_p); else TAILQ_INSERT_BEFORE(t1, bp, b_p); } /* * Find a port entry's position in the ports list according * to it's parent bridge interface name. Returns a NULL if * we should be at the TAILQ head, otherwise the entry after * which we should be inserted. */ static struct bridge_port * bridge_port_find_pos(struct bridge_ports *headp, uint32_t b_idx) { uint32_t t_idx; struct bridge_port *t1; if ((t1 = TAILQ_FIRST(headp)) == NULL || bridge_compare_sysidx(b_idx, t1->sysindex) < 0) return (NULL); t_idx = t1->sysindex; for (t1 = TAILQ_NEXT(t1, b_p); t1 != NULL; t1 = TAILQ_NEXT(t1, b_p)) { if (t1->sysindex != t_idx) { if (bridge_compare_sysidx(b_idx, t1->sysindex) < 0) return (TAILQ_PREV(t1, bridge_ports, b_p)); else t_idx = t1->sysindex; } } if (t1 == NULL) t1 = TAILQ_LAST(headp, bridge_ports); return (t1); } /* * Insert a bridge member interface in the ports TAILQ. */ static void bridge_port_memif_insert(struct bridge_ports *headp, struct bridge_port *bp, struct bridge_port **f_bp) { struct bridge_port *temp; if (*f_bp != NULL) bridge_port_insert_at(headp, bp, f_bp); else { temp = bridge_port_find_pos(headp, bp->sysindex); if (temp == NULL) TAILQ_INSERT_HEAD(headp, bp, b_p); else TAILQ_INSERT_AFTER(headp, temp, bp, b_p); *f_bp = bp; } } /* The global ports list. */ static struct bridge_ports bridge_ports = TAILQ_HEAD_INITIALIZER(bridge_ports); static time_t ports_list_age; void bridge_ports_update_listage(void) { ports_list_age = time(NULL); } void bridge_ports_fini(void) { bridge_ports_free(&bridge_ports); } void bridge_members_free(struct bridge_if *bif) { bridge_port_memif_free(&bridge_ports, bif); } /* * Find the first port in the ports list. */ static struct bridge_port * bridge_port_first(void) { return (TAILQ_FIRST(&bridge_ports)); } /* * Find the next port in the ports list. */ static struct bridge_port * bridge_port_next(struct bridge_port *bp) { return (TAILQ_NEXT(bp, b_p)); } /* * Find the first member of the specified bridge interface. */ struct bridge_port * bridge_port_bif_first(struct bridge_if *bif) { return (bif->f_bp); } /* * Find the next member of the specified bridge interface. */ struct bridge_port * bridge_port_bif_next(struct bridge_port *bp) { struct bridge_port *bp_next; if ((bp_next = TAILQ_NEXT(bp, b_p)) == NULL || bp_next->sysindex != bp->sysindex) return (NULL); return (bp_next); } /* * Remove a bridge port from the ports list. */ void bridge_port_remove(struct bridge_port *bp, struct bridge_if *bif) { if (bif->f_bp == bp) bif->f_bp = bridge_port_bif_next(bp); TAILQ_REMOVE(&bridge_ports, bp, b_p); free(bp); } /* * Allocate memory for a new bridge port and insert it * in the base ports list. Return a pointer to the port's * structure in case we want to do anything else with it. */ struct bridge_port * bridge_new_port(struct mibif *mif, struct bridge_if *bif) { struct bridge_port *bp; if ((bp = (struct bridge_port *) malloc(sizeof(*bp))) == NULL) { syslog(LOG_ERR, "bridge new member: failed: %s", strerror(errno)); return (NULL); } bzero(bp, sizeof(*bp)); bp->sysindex = bif->sysindex; bp->if_idx = mif->index; bp->port_no = mif->sysindex; strlcpy(bp->p_name, mif->name, IFNAMSIZ); bp->circuit = oid_zeroDotZero; /* * Initialize all rstpMib specific values to false/default. * These will be set to their true values later if the bridge * supports RSTP. */ bp->proto_migr = TruthValue_false; bp->admin_edge = TruthValue_false; bp->oper_edge = TruthValue_false; bp->oper_ptp = TruthValue_false; bp->admin_ptp = StpPortAdminPointToPointType_auto; bridge_port_memif_insert(&bridge_ports, bp, &(bif->f_bp)); return (bp); } /* * Update our info from the corresponding mibII interface info. */ void bridge_port_getinfo_mibif(struct mibif *m_if, struct bridge_port *bp) { bp->max_info = m_if->mib.ifmd_data.ifi_mtu; bp->in_frames = m_if->mib.ifmd_data.ifi_ipackets; bp->out_frames = m_if->mib.ifmd_data.ifi_opackets; bp->in_drops = m_if->mib.ifmd_data.ifi_iqdrops; } /* * Find a port, whose SNMP's mibII ifIndex matches one of the ports, * members of the specified bridge interface. */ struct bridge_port * bridge_port_find(int32_t if_idx, struct bridge_if *bif) { struct bridge_port *bp; for (bp = bif->f_bp; bp != NULL; bp = TAILQ_NEXT(bp, b_p)) { if (bp->sysindex != bif->sysindex) { bp = NULL; break; } if (bp->if_idx == if_idx) break; } return (bp); } void bridge_ports_dump(struct bridge_if *bif) { struct bridge_port *bp; for (bp = bridge_port_bif_first(bif); bp != NULL; bp = bridge_port_bif_next(bp)) { syslog(LOG_ERR, "memif - %s, index - %d", bp->p_name, bp->port_no); } } /* * RFC4188 specifics. */ int op_dot1d_base_port(struct snmp_context *c __unused, struct snmp_value *val, uint sub, uint iidx __unused, enum snmp_op op) { struct bridge_if *bif; struct bridge_port *bp; if ((bif = bridge_get_default()) == NULL) return (SNMP_ERR_NOSUCHNAME); if (time(NULL) - bif->ports_age > bridge_get_data_maxage() && bridge_update_memif(bif) <= 0) return (SNMP_ERR_NOSUCHNAME); switch (op) { case SNMP_OP_GET: if (val->var.len - sub != 1) return (SNMP_ERR_NOSUCHNAME); if ((bp = bridge_port_find(val->var.subs[sub], bif)) == NULL) return (SNMP_ERR_NOSUCHNAME); goto get; case SNMP_OP_GETNEXT: if (val->var.len - sub == 0) { if ((bp = bridge_port_bif_first(bif)) == NULL) return (SNMP_ERR_NOSUCHNAME); } else { if ((bp = bridge_port_find(val->var.subs[sub], bif)) == NULL || (bp = bridge_port_bif_next(bp)) == NULL) return (SNMP_ERR_NOSUCHNAME); } val->var.len = sub + 1; val->var.subs[sub] = bp->port_no; goto get; case SNMP_OP_SET: return (SNMP_ERR_NOT_WRITEABLE); case SNMP_OP_ROLLBACK: case SNMP_OP_COMMIT: break; } abort(); get: switch (val->var.subs[sub - 1]) { case LEAF_dot1dBasePort: val->v.integer = bp->port_no; return (SNMP_ERR_NOERROR); case LEAF_dot1dBasePortIfIndex: val->v.integer = bp->if_idx; return (SNMP_ERR_NOERROR); case LEAF_dot1dBasePortCircuit: val->v.oid = bp->circuit; return (SNMP_ERR_NOERROR); case LEAF_dot1dBasePortDelayExceededDiscards: val->v.uint32 = bp->dly_ex_drops; return (SNMP_ERR_NOERROR); case LEAF_dot1dBasePortMtuExceededDiscards: val->v.uint32 = bp->dly_mtu_drops; return (SNMP_ERR_NOERROR); } abort(); } int op_dot1d_stp_port(struct snmp_context *ctx, struct snmp_value *val, uint sub, uint iidx __unused, enum snmp_op op) { struct bridge_if *bif; struct bridge_port *bp; if ((bif = bridge_get_default()) == NULL) return (SNMP_ERR_NOSUCHNAME); if (time(NULL) - bif->ports_age > bridge_get_data_maxage() && bridge_update_memif(bif) <= 0) return (SNMP_ERR_NOSUCHNAME); switch (op) { case SNMP_OP_GET: if (val->var.len - sub != 1) return (SNMP_ERR_NOSUCHNAME); if ((bp = bridge_port_find(val->var.subs[sub], bif)) == NULL) return (SNMP_ERR_NOSUCHNAME); goto get; case SNMP_OP_GETNEXT: if (val->var.len - sub == 0) { if ((bp = bridge_port_bif_first(bif)) == NULL) return (SNMP_ERR_NOSUCHNAME); } else { if ((bp = bridge_port_find(val->var.subs[sub], bif)) == NULL || (bp = bridge_port_bif_next(bp)) == NULL) return (SNMP_ERR_NOSUCHNAME); } val->var.len = sub + 1; val->var.subs[sub] = bp->port_no; goto get; case SNMP_OP_SET: if (val->var.len - sub != 1) return (SNMP_ERR_NOSUCHNAME); if ((bp = bridge_port_find(val->var.subs[sub], bif)) == NULL) return (SNMP_ERR_NOSUCHNAME); switch (val->var.subs[sub - 1]) { case LEAF_dot1dStpPortPriority: if (val->v.integer < 0 || val->v.integer > 255) return (SNMP_ERR_WRONG_VALUE); ctx->scratch->int1 = bp->priority; if (bridge_port_set_priority(bif->bif_name, bp, val->v.integer) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case LEAF_dot1dStpPortEnable: if (val->v.integer != dot1dStpPortEnable_enabled && val->v.integer != dot1dStpPortEnable_disabled) return (SNMP_ERR_WRONG_VALUE); ctx->scratch->int1 = bp->enable; if (bridge_port_set_stp_enable(bif->bif_name, bp, val->v.integer) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case LEAF_dot1dStpPortPathCost: if (val->v.integer < SNMP_PORT_MIN_PATHCOST || val->v.integer > SNMP_PORT_MAX_PATHCOST) return (SNMP_ERR_WRONG_VALUE); ctx->scratch->int1 = bp->path_cost; if (bridge_port_set_path_cost(bif->bif_name, bp, val->v.integer) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case LEAF_dot1dStpPort: case LEAF_dot1dStpPortState: case LEAF_dot1dStpPortDesignatedRoot: case LEAF_dot1dStpPortDesignatedCost: case LEAF_dot1dStpPortDesignatedBridge: case LEAF_dot1dStpPortDesignatedPort: case LEAF_dot1dStpPortForwardTransitions: return (SNMP_ERR_NOT_WRITEABLE); } abort(); case SNMP_OP_ROLLBACK: if ((bp = bridge_port_find(val->var.subs[sub], bif)) == NULL) return (SNMP_ERR_GENERR); switch (val->var.subs[sub - 1]) { case LEAF_dot1dStpPortPriority: bridge_port_set_priority(bif->bif_name, bp, ctx->scratch->int1); break; case LEAF_dot1dStpPortEnable: bridge_port_set_stp_enable(bif->bif_name, bp, ctx->scratch->int1); break; case LEAF_dot1dStpPortPathCost: bridge_port_set_path_cost(bif->bif_name, bp, ctx->scratch->int1); break; } return (SNMP_ERR_NOERROR); case SNMP_OP_COMMIT: return (SNMP_ERR_NOERROR); } abort(); get: switch (val->var.subs[sub - 1]) { case LEAF_dot1dStpPort: val->v.integer = bp->port_no; return (SNMP_ERR_NOERROR); case LEAF_dot1dStpPortPriority: val->v.integer = bp->priority; return (SNMP_ERR_NOERROR); case LEAF_dot1dStpPortState: val->v.integer = bp->state; return (SNMP_ERR_NOERROR); case LEAF_dot1dStpPortEnable: val->v.integer = bp->enable; return (SNMP_ERR_NOERROR); case LEAF_dot1dStpPortPathCost: val->v.integer = bp->path_cost; return (SNMP_ERR_NOERROR); case LEAF_dot1dStpPortDesignatedRoot: return (string_get(val, bp->design_root, SNMP_BRIDGE_ID_LEN)); case LEAF_dot1dStpPortDesignatedCost: val->v.integer = bp->design_cost; return (SNMP_ERR_NOERROR); case LEAF_dot1dStpPortDesignatedBridge: return (string_get(val, bp->design_bridge, SNMP_BRIDGE_ID_LEN)); case LEAF_dot1dStpPortDesignatedPort: return (string_get(val, bp->design_port, 2)); case LEAF_dot1dStpPortForwardTransitions: val->v.uint32 = bp->fwd_trans; return (SNMP_ERR_NOERROR); } abort(); } int op_dot1d_stp_ext_port(struct snmp_context *ctx, struct snmp_value *val, uint sub, uint iidx __unused, enum snmp_op op) { struct bridge_if *bif; struct bridge_port *bp; if ((bif = bridge_get_default()) == NULL) return (SNMP_ERR_NOSUCHNAME); if (time(NULL) - bif->ports_age > bridge_get_data_maxage() && bridge_update_memif(bif) <= 0) return (SNMP_ERR_NOSUCHNAME); switch (op) { case SNMP_OP_GET: if (val->var.len - sub != 1) return (SNMP_ERR_NOSUCHNAME); if ((bp = bridge_port_find(val->var.subs[sub], bif)) == NULL) return (SNMP_ERR_NOSUCHNAME); goto get; case SNMP_OP_GETNEXT: if (val->var.len - sub == 0) { if ((bp = bridge_port_bif_first(bif)) == NULL) return (SNMP_ERR_NOSUCHNAME); } else { if ((bp = bridge_port_find(val->var.subs[sub], bif)) == NULL || (bp = bridge_port_bif_next(bp)) == NULL) return (SNMP_ERR_NOSUCHNAME); } val->var.len = sub + 1; val->var.subs[sub] = bp->port_no; goto get; case SNMP_OP_SET: if (val->var.len - sub != 1) return (SNMP_ERR_NOSUCHNAME); if ((bp = bridge_port_find(val->var.subs[sub], bif)) == NULL) return (SNMP_ERR_NOSUCHNAME); switch (val->var.subs[sub - 1]) { case LEAF_dot1dStpPortAdminEdgePort: if (val->v.integer != TruthValue_true && val->v.integer != TruthValue_false) return (SNMP_ERR_WRONG_VALUE); ctx->scratch->int1 = bp->admin_edge; if (bridge_port_set_admin_edge(bif->bif_name, bp, val->v.integer) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case LEAF_dot1dStpPortAdminPointToPoint: if (val->v.integer < 0 || val->v.integer > StpPortAdminPointToPointType_auto) return (SNMP_ERR_WRONG_VALUE); ctx->scratch->int1 = bp->admin_ptp; if (bridge_port_set_admin_ptp(bif->bif_name, bp, val->v.integer) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case LEAF_dot1dStpPortAdminPathCost: if (val->v.integer < SNMP_PORT_MIN_PATHCOST || val->v.integer > SNMP_PORT_MAX_PATHCOST) return (SNMP_ERR_WRONG_VALUE); ctx->scratch->int1 = bp->admin_path_cost; if (bridge_port_set_path_cost(bif->bif_name, bp, val->v.integer) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case LEAF_dot1dStpPortProtocolMigration: case LEAF_dot1dStpPortOperEdgePort: case LEAF_dot1dStpPortOperPointToPoint: return (SNMP_ERR_NOT_WRITEABLE); } abort(); case SNMP_OP_ROLLBACK: if ((bp = bridge_port_find(val->var.subs[sub], bif)) == NULL) return (SNMP_ERR_GENERR); switch (val->var.subs[sub - 1]) { case LEAF_dot1dStpPortAdminEdgePort: bridge_port_set_admin_edge(bif->bif_name, bp, ctx->scratch->int1); break; case LEAF_dot1dStpPortAdminPointToPoint: bridge_port_set_admin_ptp(bif->bif_name, bp, ctx->scratch->int1); break; case LEAF_dot1dStpPortAdminPathCost: bridge_port_set_path_cost(bif->bif_name, bp, ctx->scratch->int1); break; } return (SNMP_ERR_NOERROR); case SNMP_OP_COMMIT: return (SNMP_ERR_NOERROR); } abort(); get: switch (val->var.subs[sub - 1]) { case LEAF_dot1dStpPortProtocolMigration: val->v.integer = bp->proto_migr; return (SNMP_ERR_NOERROR); case LEAF_dot1dStpPortAdminEdgePort: val->v.integer = bp->admin_edge; return (SNMP_ERR_NOERROR); case LEAF_dot1dStpPortOperEdgePort: val->v.integer = bp->oper_edge; return (SNMP_ERR_NOERROR); case LEAF_dot1dStpPortAdminPointToPoint: val->v.integer = bp->admin_ptp; return (SNMP_ERR_NOERROR); case LEAF_dot1dStpPortOperPointToPoint: val->v.integer = bp->oper_ptp; return (SNMP_ERR_NOERROR); case LEAF_dot1dStpPortAdminPathCost: val->v.integer = bp->admin_path_cost; return (SNMP_ERR_NOERROR); } abort(); } int op_dot1d_tp_port(struct snmp_context *c __unused, struct snmp_value *val, uint sub, uint iidx __unused, enum snmp_op op) { struct bridge_if *bif; struct bridge_port *bp; if ((bif = bridge_get_default()) == NULL) return (SNMP_ERR_NOSUCHNAME); if (time(NULL) - bif->ports_age > bridge_get_data_maxage() && bridge_update_memif(bif) <= 0) return (SNMP_ERR_NOSUCHNAME); switch (op) { case SNMP_OP_GET: if (val->var.len - sub != 1) return (SNMP_ERR_NOSUCHNAME); if ((bp = bridge_port_find(val->var.subs[sub], bif)) == NULL) return (SNMP_ERR_NOSUCHNAME); goto get; case SNMP_OP_GETNEXT: if (val->var.len - sub == 0) { if ((bp = bridge_port_bif_first(bif)) == NULL) return (SNMP_ERR_NOSUCHNAME); } else { if ((bp = bridge_port_find(val->var.subs[sub], bif)) == NULL || (bp = bridge_port_bif_next(bp)) == NULL) return (SNMP_ERR_NOSUCHNAME); } val->var.len = sub + 1; val->var.subs[sub] = bp->port_no; goto get; case SNMP_OP_SET: return (SNMP_ERR_NOT_WRITEABLE); case SNMP_OP_ROLLBACK: case SNMP_OP_COMMIT: break; } abort(); get: switch (val->var.subs[sub - 1]) { case LEAF_dot1dTpPort: val->v.integer = bp->port_no; return (SNMP_ERR_NOERROR); case LEAF_dot1dTpPortMaxInfo: val->v.integer = bp->max_info; return (SNMP_ERR_NOERROR); case LEAF_dot1dTpPortInFrames: val->v.uint32 = bp->in_frames; return (SNMP_ERR_NOERROR); case LEAF_dot1dTpPortOutFrames: val->v.uint32 = bp->out_frames; return (SNMP_ERR_NOERROR); case LEAF_dot1dTpPortInDiscards: val->v.uint32 = bp->in_drops; return (SNMP_ERR_NOERROR); } abort(); } /* * Private BEGEMOT-BRIDGE-MIB specifics. */ /* * Construct a bridge port entry index. */ static int bridge_port_index_append(struct asn_oid *oid, uint sub, const struct bridge_port *bp) { uint i; const char *b_name; if ((b_name = bridge_if_find_name(bp->sysindex)) == NULL) return (-1); oid->len = sub + strlen(b_name) + 1 + 1; oid->subs[sub] = strlen(b_name); for (i = 1; i <= strlen(b_name); i++) oid->subs[sub + i] = b_name[i - 1]; oid->subs[sub + i] = bp->port_no; return (0); } /* * Get the port entry from an entry's index. */ static struct bridge_port * bridge_port_index_get(const struct asn_oid *oid, uint sub, int8_t status) { uint i; int32_t port_no; char bif_name[IFNAMSIZ]; struct bridge_if *bif; struct bridge_port *bp; if (oid->len - sub != oid->subs[sub] + 2 || oid->subs[sub] >= IFNAMSIZ) return (NULL); for (i = 0; i < oid->subs[sub]; i++) bif_name[i] = oid->subs[sub + i + 1]; bif_name[i] = '\0'; port_no = oid->subs[sub + i + 1]; if ((bif = bridge_if_find_ifname(bif_name)) == NULL) return (NULL); if ((bp = bridge_port_find(port_no, bif)) == NULL || (status == 0 && bp->status != RowStatus_active)) return (NULL); return (bp); } /* * Get the next port entry from an entry's index. */ static struct bridge_port * bridge_port_index_getnext(const struct asn_oid *oid, uint sub, int8_t status) { uint i; int32_t port_no; char bif_name[IFNAMSIZ]; struct bridge_if *bif; struct bridge_port *bp; if (oid->len - sub == 0) bp = bridge_port_first(); else { if (oid->len - sub != oid->subs[sub] + 2 || oid->subs[sub] >= IFNAMSIZ) return (NULL); for (i = 0; i < oid->subs[sub]; i++) bif_name[i] = oid->subs[sub + i + 1]; bif_name[i] = '\0'; port_no = oid->subs[sub + i + 1]; if ((bif = bridge_if_find_ifname(bif_name)) == NULL || (bp = bridge_port_find(port_no, bif)) == NULL) return (NULL); bp = bridge_port_next(bp); } if (status == 1) return (bp); while (bp != NULL) { if (bp->status == RowStatus_active) break; bp = bridge_port_next(bp); } return (bp); } /* * Read the bridge name and port index from a ASN OID structure. */ static int bridge_port_index_decode(const struct asn_oid *oid, uint sub, char *b_name, int32_t *idx) { uint i; if (oid->len - sub != oid->subs[sub] + 2 || oid->subs[sub] >= IFNAMSIZ) return (-1); for (i = 0; i < oid->subs[sub]; i++) b_name[i] = oid->subs[sub + i + 1]; b_name[i] = '\0'; *idx = oid->subs[sub + i + 1]; return (0); } static int bridge_port_set_status(struct snmp_context *ctx, struct snmp_value *val, uint sub) { int32_t if_idx; char b_name[IFNAMSIZ]; struct bridge_if *bif; struct bridge_port *bp; struct mibif *mif; if (bridge_port_index_decode(&val->var, sub, b_name, &if_idx) < 0) return (SNMP_ERR_INCONS_VALUE); if ((bif = bridge_if_find_ifname(b_name)) == NULL || (mif = mib_find_if(if_idx)) == NULL) return (SNMP_ERR_INCONS_VALUE); bp = bridge_port_find(if_idx, bif); switch (val->v.integer) { case RowStatus_active: if (bp == NULL) return (SNMP_ERR_INCONS_VALUE); if (bp->span_enable == 0) return (SNMP_ERR_INCONS_VALUE); ctx->scratch->int1 = bp->status; bp->status = RowStatus_active; break; case RowStatus_notInService: if (bp == NULL || bp->span_enable == 0 || bp->status == RowStatus_active) return (SNMP_ERR_INCONS_VALUE); ctx->scratch->int1 = bp->status; bp->status = RowStatus_notInService; case RowStatus_notReady: /* FALLTHROUGH */ case RowStatus_createAndGo: return (SNMP_ERR_INCONS_VALUE); case RowStatus_createAndWait: if (bp != NULL) return (SNMP_ERR_INCONS_VALUE); if ((bp = bridge_new_port(mif, bif)) == NULL) return (SNMP_ERR_GENERR); ctx->scratch->int1 = RowStatus_destroy; bp->status = RowStatus_notReady; break; case RowStatus_destroy: if (bp == NULL) return (SNMP_ERR_INCONS_VALUE); ctx->scratch->int1 = bp->status; bp->status = RowStatus_destroy; break; } return (SNMP_ERR_NOERROR); } static int bridge_port_rollback_status(struct snmp_context *ctx, struct snmp_value *val, uint sub) { int32_t if_idx; char b_name[IFNAMSIZ]; struct bridge_if *bif; struct bridge_port *bp; if (bridge_port_index_decode(&val->var, sub, b_name, &if_idx) < 0) return (SNMP_ERR_GENERR); if ((bif = bridge_if_find_ifname(b_name)) == NULL || (bp = bridge_port_find(if_idx, bif)) == NULL) return (SNMP_ERR_GENERR); if (ctx->scratch->int1 == RowStatus_destroy) bridge_port_remove(bp, bif); else bp->status = ctx->scratch->int1; return (SNMP_ERR_NOERROR); } static int bridge_port_commit_status(struct snmp_value *val, uint sub) { int32_t if_idx; char b_name[IFNAMSIZ]; struct bridge_if *bif; struct bridge_port *bp; if (bridge_port_index_decode(&val->var, sub, b_name, &if_idx) < 0) return (SNMP_ERR_GENERR); if ((bif = bridge_if_find_ifname(b_name)) == NULL || (bp = bridge_port_find(if_idx, bif)) == NULL) return (SNMP_ERR_GENERR); switch (bp->status) { case RowStatus_active: if (bridge_port_addm(bp, b_name) < 0) return (SNMP_ERR_COMMIT_FAILED); break; case RowStatus_destroy: if (bridge_port_delm(bp, b_name) < 0) return (SNMP_ERR_COMMIT_FAILED); bridge_port_remove(bp, bif); break; } return (SNMP_ERR_NOERROR); } static int bridge_port_set_span_enable(struct snmp_context *ctx, struct snmp_value *val, uint sub) { int32_t if_idx; char b_name[IFNAMSIZ]; struct bridge_if *bif; struct bridge_port *bp; struct mibif *mif; if (val->v.integer != begemotBridgeBaseSpanEnabled_enabled && val->v.integer != begemotBridgeBaseSpanEnabled_disabled) return (SNMP_ERR_BADVALUE); if (bridge_port_index_decode(&val->var, sub, b_name, &if_idx) < 0) return (SNMP_ERR_INCONS_VALUE); if ((bif = bridge_if_find_ifname(b_name)) == NULL) return (SNMP_ERR_INCONS_VALUE); if ((bp = bridge_port_find(if_idx, bif)) == NULL) { if ((mif = mib_find_if(if_idx)) == NULL) return (SNMP_ERR_INCONS_VALUE); if ((bp = bridge_new_port(mif, bif)) == NULL) return (SNMP_ERR_GENERR); ctx->scratch->int1 = RowStatus_destroy; } else if (bp->status == RowStatus_active) { return (SNMP_ERR_INCONS_VALUE); } else { ctx->scratch->int1 = bp->status; } bp->span_enable = val->v.integer; bp->status = RowStatus_notInService; return (SNMP_ERR_NOERROR); } int op_begemot_base_port(struct snmp_context *ctx, struct snmp_value *val, uint sub, uint iidx __unused, enum snmp_op op) { int8_t status, which; const char *bname; struct bridge_port *bp; if (time(NULL) - ports_list_age > bridge_get_data_maxage()) bridge_update_all_ports(); which = val->var.subs[sub - 1]; status = 0; switch (op) { case SNMP_OP_GET: if (which == LEAF_begemotBridgeBaseSpanEnabled || which == LEAF_begemotBridgeBasePortStatus) status = 1; if ((bp = bridge_port_index_get(&val->var, sub, status)) == NULL) return (SNMP_ERR_NOSUCHNAME); goto get; case SNMP_OP_GETNEXT: if (which == LEAF_begemotBridgeBaseSpanEnabled || which == LEAF_begemotBridgeBasePortStatus) status = 1; if ((bp = bridge_port_index_getnext(&val->var, sub, status)) == NULL || bridge_port_index_append(&val->var, sub, bp) < 0) return (SNMP_ERR_NOSUCHNAME); goto get; case SNMP_OP_SET: switch (which) { case LEAF_begemotBridgeBaseSpanEnabled: return (bridge_port_set_span_enable(ctx, val, sub)); case LEAF_begemotBridgeBasePortStatus: return (bridge_port_set_status(ctx, val, sub)); case LEAF_begemotBridgeBasePortPrivate: if ((bp = bridge_port_index_get(&val->var, sub, status)) == NULL) return (SNMP_ERR_NOSUCHNAME); if ((bname = bridge_if_find_name(bp->sysindex)) == NULL) return (SNMP_ERR_GENERR); ctx->scratch->int1 = bp->priv_set; return (bridge_port_set_private(bname, bp, val->v.integer)); case LEAF_begemotBridgeBasePort: case LEAF_begemotBridgeBasePortIfIndex: case LEAF_begemotBridgeBasePortDelayExceededDiscards: case LEAF_begemotBridgeBasePortMtuExceededDiscards: return (SNMP_ERR_NOT_WRITEABLE); } abort(); case SNMP_OP_ROLLBACK: switch (which) { case LEAF_begemotBridgeBaseSpanEnabled: /* FALLTHROUGH */ case LEAF_begemotBridgeBasePortStatus: return (bridge_port_rollback_status(ctx, val, sub)); case LEAF_begemotBridgeBasePortPrivate: if ((bp = bridge_port_index_get(&val->var, sub, status)) == NULL) return (SNMP_ERR_GENERR); if ((bname = bridge_if_find_name(bp->sysindex)) == NULL) return (SNMP_ERR_GENERR); return (bridge_port_set_private(bname, bp, ctx->scratch->int1)); } return (SNMP_ERR_NOERROR); case SNMP_OP_COMMIT: if (which == LEAF_begemotBridgeBasePortStatus) return (bridge_port_commit_status(val, sub)); return (SNMP_ERR_NOERROR); } abort(); get: switch (which) { case LEAF_begemotBridgeBasePort: val->v.integer = bp->port_no; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeBasePortIfIndex: val->v.integer = bp->if_idx; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeBaseSpanEnabled: val->v.integer = bp->span_enable; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeBasePortDelayExceededDiscards: val->v.uint32 = bp->dly_ex_drops; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeBasePortMtuExceededDiscards: val->v.uint32 = bp->dly_mtu_drops; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeBasePortStatus: val->v.integer = bp->status; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeBasePortPrivate: val->v.integer = bp->priv_set; return (SNMP_ERR_NOERROR); } abort(); } int op_begemot_stp_port(struct snmp_context *ctx, struct snmp_value *val, uint sub, uint iidx __unused, enum snmp_op op) { struct bridge_port *bp; const char *b_name; if (time(NULL) - ports_list_age > bridge_get_data_maxage()) bridge_update_all_ports(); switch (op) { case SNMP_OP_GET: if ((bp = bridge_port_index_get(&val->var, sub, 0)) == NULL) return (SNMP_ERR_NOSUCHNAME); goto get; case SNMP_OP_GETNEXT: if ((bp = bridge_port_index_getnext(&val->var, sub, 0)) == NULL || bridge_port_index_append(&val->var, sub, bp) < 0) return (SNMP_ERR_NOSUCHNAME); goto get; case SNMP_OP_SET: if ((bp = bridge_port_index_get(&val->var, sub, 0)) == NULL) return (SNMP_ERR_NOSUCHNAME); if ((b_name = bridge_if_find_name(bp->sysindex)) == NULL) return (SNMP_ERR_GENERR); switch (val->var.subs[sub - 1]) { case LEAF_begemotBridgeStpPortPriority: if (val->v.integer < 0 || val->v.integer > 255) return (SNMP_ERR_WRONG_VALUE); ctx->scratch->int1 = bp->priority; if (bridge_port_set_priority(b_name, bp, val->v.integer) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpPortEnable: if (val->v.integer != begemotBridgeStpPortEnable_enabled || val->v.integer != begemotBridgeStpPortEnable_disabled) return (SNMP_ERR_WRONG_VALUE); ctx->scratch->int1 = bp->enable; if (bridge_port_set_stp_enable(b_name, bp, val->v.integer) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpPortPathCost: if (val->v.integer < SNMP_PORT_MIN_PATHCOST || val->v.integer > SNMP_PORT_MAX_PATHCOST) return (SNMP_ERR_WRONG_VALUE); ctx->scratch->int1 = bp->path_cost; if (bridge_port_set_path_cost(b_name, bp, val->v.integer) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpPort: case LEAF_begemotBridgeStpPortState: case LEAF_begemotBridgeStpPortDesignatedRoot: case LEAF_begemotBridgeStpPortDesignatedCost: case LEAF_begemotBridgeStpPortDesignatedBridge: case LEAF_begemotBridgeStpPortDesignatedPort: case LEAF_begemotBridgeStpPortForwardTransitions: return (SNMP_ERR_NOT_WRITEABLE); } abort(); case SNMP_OP_ROLLBACK: if ((bp = bridge_port_index_get(&val->var, sub, 0)) == NULL || (b_name = bridge_if_find_name(bp->sysindex)) == NULL) return (SNMP_ERR_GENERR); switch (val->var.subs[sub - 1]) { case LEAF_begemotBridgeStpPortPriority: bridge_port_set_priority(b_name, bp, ctx->scratch->int1); break; case LEAF_begemotBridgeStpPortEnable: bridge_port_set_stp_enable(b_name, bp, ctx->scratch->int1); break; case LEAF_begemotBridgeStpPortPathCost: bridge_port_set_path_cost(b_name, bp, ctx->scratch->int1); break; } return (SNMP_ERR_NOERROR); case SNMP_OP_COMMIT: return (SNMP_ERR_NOERROR); } abort(); get: switch (val->var.subs[sub - 1]) { case LEAF_begemotBridgeStpPort: val->v.integer = bp->port_no; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpPortPriority: val->v.integer = bp->priority; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpPortState: val->v.integer = bp->state; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpPortEnable: val->v.integer = bp->enable; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpPortPathCost: val->v.integer = bp->path_cost; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpPortDesignatedRoot: return (string_get(val, bp->design_root, SNMP_BRIDGE_ID_LEN)); case LEAF_begemotBridgeStpPortDesignatedCost: val->v.integer = bp->design_cost; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpPortDesignatedBridge: return (string_get(val, bp->design_bridge, SNMP_BRIDGE_ID_LEN)); case LEAF_begemotBridgeStpPortDesignatedPort: return (string_get(val, bp->design_port, 2)); case LEAF_begemotBridgeStpPortForwardTransitions: val->v.uint32 = bp->fwd_trans; return (SNMP_ERR_NOERROR); } abort(); } int op_begemot_stp_ext_port(struct snmp_context *ctx, struct snmp_value *val, uint sub, uint iidx __unused, enum snmp_op op) { struct bridge_port *bp; const char *b_name; if (time(NULL) - ports_list_age > bridge_get_data_maxage()) bridge_update_all_ports(); switch (op) { case SNMP_OP_GET: if ((bp = bridge_port_index_get(&val->var, sub, 0)) == NULL) return (SNMP_ERR_NOSUCHNAME); goto get; case SNMP_OP_GETNEXT: if ((bp = bridge_port_index_getnext(&val->var, sub, 0)) == NULL || bridge_port_index_append(&val->var, sub, bp) < 0) return (SNMP_ERR_NOSUCHNAME); goto get; case SNMP_OP_SET: if ((bp = bridge_port_index_get(&val->var, sub, 0)) == NULL) return (SNMP_ERR_NOSUCHNAME); if ((b_name = bridge_if_find_name(bp->sysindex)) == NULL) return (SNMP_ERR_GENERR); switch (val->var.subs[sub - 1]) { case LEAF_begemotBridgeStpPortAdminEdgePort: if (val->v.integer != TruthValue_true && val->v.integer != TruthValue_false) return (SNMP_ERR_WRONG_VALUE); ctx->scratch->int1 = bp->admin_edge; if (bridge_port_set_admin_edge(b_name, bp, val->v.integer) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpPortAdminPointToPoint: if (val->v.integer < 0 || val->v.integer > StpPortAdminPointToPointType_auto) return (SNMP_ERR_WRONG_VALUE); ctx->scratch->int1 = bp->admin_ptp; if (bridge_port_set_admin_ptp(b_name, bp, val->v.integer) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpPortAdminPathCost: if (val->v.integer < SNMP_PORT_MIN_PATHCOST || val->v.integer > SNMP_PORT_MAX_PATHCOST) return (SNMP_ERR_WRONG_VALUE); ctx->scratch->int1 = bp->admin_path_cost; if (bridge_port_set_path_cost(b_name, bp, val->v.integer) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpPortProtocolMigration: case LEAF_begemotBridgeStpPortOperEdgePort: case LEAF_begemotBridgeStpPortOperPointToPoint: return (SNMP_ERR_NOT_WRITEABLE); } abort(); case SNMP_OP_ROLLBACK: if ((bp = bridge_port_index_get(&val->var, sub, 0)) == NULL || (b_name = bridge_if_find_name(bp->sysindex)) == NULL) return (SNMP_ERR_GENERR); switch (val->var.subs[sub - 1]) { case LEAF_begemotBridgeStpPortAdminEdgePort: bridge_port_set_admin_edge(b_name, bp, ctx->scratch->int1); break; case LEAF_begemotBridgeStpPortAdminPointToPoint: bridge_port_set_admin_ptp(b_name, bp, ctx->scratch->int1); break; case LEAF_begemotBridgeStpPortAdminPathCost: bridge_port_set_path_cost(b_name, bp, ctx->scratch->int1); break; } return (SNMP_ERR_NOERROR); case SNMP_OP_COMMIT: return (SNMP_ERR_NOERROR); } abort(); get: switch (val->var.subs[sub - 1]) { case LEAF_begemotBridgeStpPortProtocolMigration: val->v.integer = bp->proto_migr; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpPortAdminEdgePort: val->v.integer = bp->admin_edge; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpPortOperEdgePort: val->v.integer = bp->oper_edge; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpPortAdminPointToPoint: val->v.integer = bp->admin_ptp; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpPortOperPointToPoint: val->v.integer = bp->oper_ptp; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeStpPortAdminPathCost: val->v.integer = bp->admin_path_cost; return (SNMP_ERR_NOERROR); } abort(); } int op_begemot_tp_port(struct snmp_context *c __unused, struct snmp_value *val, uint sub, uint iidx __unused, enum snmp_op op) { struct bridge_port *bp; if (time(NULL) - ports_list_age > bridge_get_data_maxage()) bridge_update_all_ports(); switch (op) { case SNMP_OP_GET: if ((bp = bridge_port_index_get(&val->var, sub, 0)) == NULL) return (SNMP_ERR_NOSUCHNAME); goto get; case SNMP_OP_GETNEXT: if ((bp = bridge_port_index_getnext(&val->var, sub, 0)) == NULL || bridge_port_index_append(&val->var, sub, bp) < 0) return (SNMP_ERR_NOSUCHNAME); goto get; case SNMP_OP_SET: return (SNMP_ERR_NOT_WRITEABLE); case SNMP_OP_ROLLBACK: case SNMP_OP_COMMIT: break; } abort(); get: switch (val->var.subs[sub - 1]) { case LEAF_begemotBridgeTpPort: val->v.integer = bp->port_no; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeTpPortMaxInfo: val->v.integer = bp->max_info; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeTpPortInFrames: val->v.uint32 = bp->in_frames; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeTpPortOutFrames: val->v.uint32 = bp->out_frames; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeTpPortInDiscards: val->v.uint32 = bp->in_drops; return (SNMP_ERR_NOERROR); } abort(); } Index: head/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_snmp.c =================================================================== --- head/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_snmp.c (revision 335884) +++ head/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_snmp.c (revision 335885) @@ -1,340 +1,341 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2006 Shteryana Shopova * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * Bridge MIB implementation for SNMPd. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#define SNMPTREE_TYPES #include "bridge_tree.h" #include "bridge_snmp.h" #include "bridge_oid.h" static struct lmodule *bridge_module; /* For the registration. */ static const struct asn_oid oid_dot1Bridge = OIDX_dot1dBridge; /* The registration. */ static uint reg_bridge; /* Periodic timer for polling all bridges' data. */ static void *bridge_data_timer; static void *bridge_tc_timer; static int bridge_data_maxage = SNMP_BRIDGE_DATA_MAXAGE; static int bridge_poll_ticks = SNMP_BRIDGE_POLL_INTERVAL * 100; static int bridge_tc_poll_ticks = SNMP_BRIDGE_TC_POLL_INTERVAL * 100; /* * Our default bridge, whose info will be visible under * the dot1dBridge subtree and functions to set/fetch it. */ static char bif_default_name[IFNAMSIZ] = "bridge0"; static struct bridge_if *bif_default; struct bridge_if * bridge_get_default(void) { struct mibif *ifp; if (bif_default != NULL) { /* Walk through the mibII interface list. */ for (ifp = mib_first_if(); ifp != NULL; ifp = mib_next_if(ifp)) if (strcmp(ifp->name, bif_default->bif_name) == 0) break; if (ifp == NULL) bif_default = NULL; } return (bif_default); } void bridge_set_default(struct bridge_if *bif) { bif_default = bif; syslog(LOG_ERR, "Set default bridge interface to: %s", bif == NULL ? "(none)" : bif->bif_name); } const char * bridge_get_default_name(void) { return (bif_default_name); } static int bridge_set_default_name(const char *bif_name, uint len) { struct bridge_if *bif; if (len >= IFNAMSIZ) return (-1); bcopy(bif_name, bif_default_name, len); bif_default_name[len] = '\0'; if ((bif = bridge_if_find_ifname(bif_default_name)) == NULL) { bif_default = NULL; return (0); } bif_default = bif; return (1); } int bridge_get_data_maxage(void) { return (bridge_data_maxage); } static void bridge_set_poll_ticks(int poll_ticks) { if (bridge_data_timer != NULL) timer_stop(bridge_data_timer); bridge_poll_ticks = poll_ticks; bridge_data_timer = timer_start_repeat(bridge_poll_ticks, bridge_poll_ticks, bridge_update_all, NULL, bridge_module); } /* * The bridge module configuration via SNMP. */ static int bridge_default_name_save(struct snmp_context *ctx, const char *bridge_default) { if ((ctx->scratch->int1 = strlen(bridge_default)) >= IFNAMSIZ) return (-1); if ((ctx->scratch->ptr1 = malloc(IFNAMSIZ)) == NULL) return (-1); strncpy(ctx->scratch->ptr1, bridge_default, ctx->scratch->int1); return (0); } int op_begemot_bridge_config(struct snmp_context *ctx, struct snmp_value *val, uint sub, uint iidx __unused, enum snmp_op op) { switch (op) { case SNMP_OP_GET: switch (val->var.subs[sub - 1]) { case LEAF_begemotBridgeDefaultBridgeIf: return (string_get(val, bridge_get_default_name(), -1)); case LEAF_begemotBridgeDataUpdate: val->v.integer = bridge_data_maxage; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeDataPoll: val->v.integer = bridge_poll_ticks / 100; return (SNMP_ERR_NOERROR); } abort(); case SNMP_OP_GETNEXT: abort(); case SNMP_OP_SET: switch (val->var.subs[sub - 1]) { case LEAF_begemotBridgeDefaultBridgeIf: /* * Cannot use string_save() here - requires either * a fixed-sized or var-length string - not less * than or equal. */ if (bridge_default_name_save(ctx, bridge_get_default_name()) < 0) return (SNMP_ERR_RES_UNAVAIL); if (bridge_set_default_name(val->v.octetstring.octets, val->v.octetstring.len) < 0) return (SNMP_ERR_BADVALUE); return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeDataUpdate: if (val->v.integer < SNMP_BRIDGE_DATA_MAXAGE_MIN || val->v.integer > SNMP_BRIDGE_DATA_MAXAGE_MAX) return (SNMP_ERR_WRONG_VALUE); ctx->scratch->int1 = bridge_data_maxage; bridge_data_maxage = val->v.integer; return (SNMP_ERR_NOERROR); case LEAF_begemotBridgeDataPoll: if (val->v.integer < SNMP_BRIDGE_POLL_INTERVAL_MIN || val->v.integer > SNMP_BRIDGE_POLL_INTERVAL_MAX) return (SNMP_ERR_WRONG_VALUE); ctx->scratch->int1 = val->v.integer; return (SNMP_ERR_NOERROR); } abort(); case SNMP_OP_ROLLBACK: switch (val->var.subs[sub - 1]) { case LEAF_begemotBridgeDefaultBridgeIf: bridge_set_default_name(ctx->scratch->ptr1, ctx->scratch->int1); free(ctx->scratch->ptr1); break; case LEAF_begemotBridgeDataUpdate: bridge_data_maxage = ctx->scratch->int1; break; } return (SNMP_ERR_NOERROR); case SNMP_OP_COMMIT: switch (val->var.subs[sub - 1]) { case LEAF_begemotBridgeDefaultBridgeIf: free(ctx->scratch->ptr1); break; case LEAF_begemotBridgeDataPoll: bridge_set_poll_ticks(ctx->scratch->int1 * 100); break; } return (SNMP_ERR_NOERROR); } abort(); } /* * Bridge mib module initialization hook. * Returns 0 on success, < 0 on error. */ static int bridge_init(struct lmodule * mod, int argc __unused, char *argv[] __unused) { bridge_module = mod; if (bridge_kmod_load() < 0) return (-1); if (bridge_ioctl_init() < 0) return (-1); /* Register to get creation messages for bridge interfaces. */ if (mib_register_newif(bridge_attach_newif, bridge_module)) { syslog(LOG_ERR, "Cannot register newif function: %s", strerror(errno)); return (-1); } return (0); } /* * Bridge mib module finalization hook. */ static int bridge_fini(void) { mib_unregister_newif(bridge_module); or_unregister(reg_bridge); if (bridge_data_timer != NULL) { timer_stop(bridge_data_timer); bridge_data_timer = NULL; } if (bridge_tc_timer != NULL) { timer_stop(bridge_tc_timer); bridge_tc_timer = NULL; } bridge_ifs_fini(); bridge_ports_fini(); bridge_addrs_fini(); return (0); } /* * Bridge mib module start operation. */ static void bridge_start(void) { reg_bridge = or_register(&oid_dot1Bridge, "The IETF MIB for Bridges (RFC 4188).", bridge_module); bridge_data_timer = timer_start_repeat(bridge_poll_ticks, bridge_poll_ticks, bridge_update_all, NULL, bridge_module); bridge_tc_timer = timer_start_repeat(bridge_tc_poll_ticks, bridge_tc_poll_ticks, bridge_update_tc_time, NULL, bridge_module); } static void bridge_dump(void) { struct bridge_if *bif; if ((bif = bridge_get_default()) == NULL) syslog(LOG_ERR, "Dump: no default bridge interface"); else syslog(LOG_ERR, "Dump: default bridge interface %s", bif->bif_name); bridge_ifs_dump(); bridge_pf_dump(); } const struct snmp_module config = { .comment = "This module implements the bridge mib (RFC 4188).", .init = bridge_init, .fini = bridge_fini, .start = bridge_start, .tree = bridge_ctree, .dump = bridge_dump, .tree_size = bridge_CTREE_SIZE, }; Index: head/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_sys.c =================================================================== --- head/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_sys.c (revision 335884) +++ head/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_sys.c (revision 335885) @@ -1,1509 +1,1510 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2006 Shteryana Shopova * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * Bridge MIB implementation for SNMPd. * Bridge OS specific ioctls. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#define SNMPTREE_TYPES #include "bridge_tree.h" #include "bridge_snmp.h" int sock = -1; int bridge_ioctl_init(void) { if ((sock = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { syslog(LOG_ERR, "cannot open socket : %s", strerror(errno)); return (-1); } return (0); } /* * Load the if_bridge.ko module in kernel if not already there. */ int bridge_kmod_load(void) { int fileid, modid; const char mod_name[] = "if_bridge"; struct module_stat mstat; /* Scan files in kernel. */ mstat.version = sizeof(struct module_stat); for (fileid = kldnext(0); fileid > 0; fileid = kldnext(fileid)) { /* Scan modules in file. */ for (modid = kldfirstmod(fileid); modid > 0; modid = modfnext(modid)) { if (modstat(modid, &mstat) < 0) continue; if (strcmp(mod_name, mstat.name) == 0) return (0); } } /* Not present - load it. */ if (kldload(mod_name) < 0) { syslog(LOG_ERR, "failed to load %s kernel module", mod_name); return (-1); } return (1); } /************************************************************************ * Bridge interfaces. */ /* * Convert the kernel uint64_t value for a bridge id */ static void snmp_uint64_to_bridgeid(uint64_t id, bridge_id b_id) { int i; u_char *o; o = (u_char *) &id; for (i = 0; i < SNMP_BRIDGE_ID_LEN; i++, o++) b_id[SNMP_BRIDGE_ID_LEN - i - 1] = *o; } /* * Fetch the bridge configuration parameters from the kernel excluding * it's base MAC address. */ static int bridge_get_conf_param(struct bridge_if *bif) { struct ifdrv ifd; struct ifbrparam b_param; strlcpy(ifd.ifd_name, bif->bif_name, IFNAMSIZ); ifd.ifd_len = sizeof(b_param); ifd.ifd_data = &b_param; /* Bridge priority. */ ifd.ifd_cmd = BRDGGPRI; if (ioctl(sock, SIOCGDRVSPEC, &ifd) < 0) { syslog(LOG_ERR, "update bridge: ioctl(BRDGGPRI) failed: %s", strerror(errno)); return (-1); } bif->priority = b_param.ifbrp_prio; /* Configured max age. */ ifd.ifd_cmd = BRDGGMA; if (ioctl(sock, SIOCGDRVSPEC, &ifd) < 0) { syslog(LOG_ERR, "update bridge: ioctl(BRDGGMA) failed: %s", strerror(errno)); return (-1); } /* Centi-seconds. */ bif->bridge_max_age = 100 * b_param.ifbrp_maxage; /* Configured hello time. */ ifd.ifd_cmd = BRDGGHT; if (ioctl(sock, SIOCGDRVSPEC, &ifd) < 0) { syslog(LOG_ERR, "update bridge: ioctl(BRDGGHT) failed: %s", strerror(errno)); return (-1); } bif->bridge_hello_time = 100 * b_param.ifbrp_hellotime; /* Forward delay. */ ifd.ifd_cmd = BRDGGFD; if (ioctl(sock, SIOCGDRVSPEC, &ifd) < 0) { syslog(LOG_ERR, "update bridge: ioctl(BRDGGFD) failed: %s", strerror(errno)); return (-1); } bif->bridge_fwd_delay = 100 * b_param.ifbrp_fwddelay; /* Number of dropped addresses. */ ifd.ifd_cmd = BRDGGRTE; if (ioctl(sock, SIOCGDRVSPEC, &ifd) < 0) { syslog(LOG_ERR, "update bridge: ioctl(BRDGGRTE) failed: %s", strerror(errno)); return (-1); } bif->lrnt_drops = b_param.ifbrp_cexceeded; /* Address table timeout. */ ifd.ifd_cmd = BRDGGTO; if (ioctl(sock, SIOCGDRVSPEC, &ifd) < 0) { syslog(LOG_ERR, "update bridge: ioctl(BRDGGTO) failed: %s", strerror(errno)); return (-1); } bif->age_time = b_param.ifbrp_ctime; /* Address table size. */ ifd.ifd_cmd = BRDGGCACHE; if (ioctl(sock, SIOCGDRVSPEC, &ifd) < 0) { syslog(LOG_ERR, "update bridge: ioctl(BRDGGCACHE) " "failed: %s", strerror(errno)); return (-1); } bif->max_addrs = b_param.ifbrp_csize; return (0); } /* * Fetch the current bridge STP operational parameters. * Returns: -1 - on error; * 0 - old TC time and Root Port values are same; * 1 - topologyChange notification should be sent; * 2 - newRoot notification should be sent. */ int bridge_get_op_param(struct bridge_if *bif) { int new_root_send; struct ifdrv ifd; struct ifbropreq b_req; strlcpy(ifd.ifd_name, bif->bif_name, IFNAMSIZ); ifd.ifd_len = sizeof(b_req); ifd.ifd_data = &b_req; ifd.ifd_cmd = BRDGPARAM; if (ioctl(sock, SIOCGDRVSPEC, &ifd) < 0) { syslog(LOG_ERR, "update bridge: ioctl(BRDGPARAM) failed: %s", strerror(errno)); return (-1); } bif->max_age = 100 * b_req.ifbop_maxage; bif->hello_time = 100 * b_req.ifbop_hellotime; bif->fwd_delay = 100 * b_req.ifbop_fwddelay; bif->stp_version = b_req.ifbop_protocol; bif->tx_hold_count = b_req.ifbop_holdcount; if (b_req.ifbop_root_port == 0 && bif->root_port != b_req.ifbop_root_port) new_root_send = 2; else new_root_send = 0; bif->root_port = b_req.ifbop_root_port; bif->root_cost = b_req.ifbop_root_path_cost; snmp_uint64_to_bridgeid(b_req.ifbop_designated_root, bif->design_root); if (bif->last_tc_time.tv_sec != b_req.ifbop_last_tc_time.tv_sec) { bif->top_changes++; bif->last_tc_time.tv_sec = b_req.ifbop_last_tc_time.tv_sec; bif->last_tc_time.tv_usec = b_req.ifbop_last_tc_time.tv_usec; /* * "The trap is not sent if a (begemotBridge)NewRoot * trap is sent for the same transition." */ if (new_root_send == 0) return (1); } return (new_root_send); } int bridge_getinfo_bif(struct bridge_if *bif) { if (bridge_get_conf_param(bif) < 0) return (-1); return (bridge_get_op_param(bif)); } int bridge_set_priority(struct bridge_if *bif, int32_t priority) { struct ifdrv ifd; struct ifbrparam b_param; strlcpy(ifd.ifd_name, bif->bif_name, IFNAMSIZ); ifd.ifd_len = sizeof(b_param); ifd.ifd_data = &b_param; b_param.ifbrp_prio = (uint32_t) priority; ifd.ifd_cmd = BRDGSPRI; if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) { syslog(LOG_ERR, "set bridge param: ioctl(BRDGSPRI) " "failed: %s", strerror(errno)); return (-1); } /* * Re-fetching the data from the driver after that might be a good * idea, since changing our bridge's priority should invoke * recalculation of the active spanning tree topology in the network. */ bif->priority = priority; return (0); } /* * Convert 1/100 of seconds to 1/256 of seconds. * Timeout ::= TEXTUAL-CONVENTION. * To convert a Timeout value into a value in units of * 1/256 seconds, the following algorithm should be used: * b = floor( (n * 256) / 100) * The conversion to 1/256 of a second happens in the kernel - * just make sure we correctly convert the seconds to Timout * and vice versa. */ static uint32_t snmp_timeout2_sec(int32_t secs) { return (secs / 100); } int bridge_set_maxage(struct bridge_if *bif, int32_t max_age) { struct ifdrv ifd; struct ifbrparam b_param; strlcpy(ifd.ifd_name, bif->bif_name, IFNAMSIZ); ifd.ifd_len = sizeof(b_param); ifd.ifd_data = &b_param; b_param.ifbrp_maxage = snmp_timeout2_sec(max_age); ifd.ifd_cmd = BRDGSMA; if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) { syslog(LOG_ERR, "set bridge param: ioctl(BRDGSMA) " "failed: %s", strerror(errno)); return (-1); } bif->bridge_max_age = max_age; return (0); } int bridge_set_hello_time(struct bridge_if *bif, int32_t hello_time) { struct ifdrv ifd; struct ifbrparam b_param; strlcpy(ifd.ifd_name, bif->bif_name, IFNAMSIZ); ifd.ifd_len = sizeof(b_param); ifd.ifd_data = &b_param; b_param.ifbrp_hellotime = snmp_timeout2_sec(hello_time); ifd.ifd_cmd = BRDGSHT; if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) { syslog(LOG_ERR, "set bridge param: ioctl(BRDGSHT) " "failed: %s", strerror(errno)); return (-1); } bif->bridge_hello_time = b_param.ifbrp_hellotime; return (0); } int bridge_set_forward_delay(struct bridge_if *bif, int32_t fwd_delay) { struct ifdrv ifd; struct ifbrparam b_param; strlcpy(ifd.ifd_name, bif->bif_name, IFNAMSIZ); ifd.ifd_len = sizeof(b_param); ifd.ifd_data = &b_param; b_param.ifbrp_fwddelay = snmp_timeout2_sec(fwd_delay); ifd.ifd_cmd = BRDGSFD; if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) { syslog(LOG_ERR, "set bridge param: ioctl(BRDGSFD) " "failed: %s", strerror(errno)); return (-1); } bif->bridge_fwd_delay = b_param.ifbrp_fwddelay; return (0); } int bridge_set_aging_time(struct bridge_if *bif, int32_t age_time) { struct ifdrv ifd; struct ifbrparam b_param; strlcpy(ifd.ifd_name, bif->bif_name, IFNAMSIZ); ifd.ifd_len = sizeof(b_param); ifd.ifd_data = &b_param; b_param.ifbrp_ctime = (uint32_t) age_time; ifd.ifd_cmd = BRDGSTO; if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) { syslog(LOG_ERR, "set bridge param: ioctl(BRDGSTO) " "failed: %s", strerror(errno)); return (-1); } bif->age_time = age_time; return (0); } int bridge_set_max_cache(struct bridge_if *bif, int32_t max_cache) { struct ifdrv ifd; struct ifbrparam b_param; strlcpy(ifd.ifd_name, bif->bif_name, IFNAMSIZ); ifd.ifd_len = sizeof(b_param); ifd.ifd_data = &b_param; b_param.ifbrp_csize = max_cache; ifd.ifd_cmd = BRDGSCACHE; if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) { syslog(LOG_ERR, "set bridge param: ioctl(BRDGSCACHE) " "failed: %s", strerror(errno)); return (-1); } bif->max_addrs = b_param.ifbrp_csize; return (0); } int bridge_set_tx_hold_count(struct bridge_if *bif, int32_t tx_hc) { struct ifdrv ifd; struct ifbrparam b_param; if (tx_hc < SNMP_BRIDGE_MIN_TXHC || tx_hc > SNMP_BRIDGE_MAX_TXHC) return (-1); strlcpy(ifd.ifd_name, bif->bif_name, IFNAMSIZ); ifd.ifd_len = sizeof(b_param); ifd.ifd_data = &b_param; b_param.ifbrp_txhc = tx_hc; ifd.ifd_cmd = BRDGSTXHC; if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) { syslog(LOG_ERR, "set bridge param: ioctl(BRDGSTXHC) " "failed: %s", strerror(errno)); return (-1); } bif->tx_hold_count = b_param.ifbrp_txhc; return (0); } int bridge_set_stp_version(struct bridge_if *bif, int32_t stp_proto) { struct ifdrv ifd; struct ifbrparam b_param; strlcpy(ifd.ifd_name, bif->bif_name, IFNAMSIZ); ifd.ifd_len = sizeof(b_param); ifd.ifd_data = &b_param; b_param.ifbrp_proto = stp_proto; ifd.ifd_cmd = BRDGSPROTO; if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) { syslog(LOG_ERR, "set bridge param: ioctl(BRDGSPROTO) " "failed: %s", strerror(errno)); return (-1); } bif->stp_version = b_param.ifbrp_proto; return (0); } /* * Set the bridge interface status to up/down. */ int bridge_set_if_up(const char* b_name, int8_t up) { int flags; struct ifreq ifr; bzero(&ifr, sizeof(ifr)); strlcpy(ifr.ifr_name, b_name, sizeof(ifr.ifr_name)); if (ioctl(sock, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) { syslog(LOG_ERR, "set bridge up: ioctl(SIOCGIFFLAGS) " "failed: %s", strerror(errno)); return (-1); } flags = (ifr.ifr_flags & 0xffff) | (ifr.ifr_flagshigh << 16); if (up == 1) flags |= IFF_UP; else flags &= ~IFF_UP; ifr.ifr_flags = flags & 0xffff; ifr.ifr_flagshigh = flags >> 16; if (ioctl(sock, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) { syslog(LOG_ERR, "set bridge up: ioctl(SIOCSIFFLAGS) " "failed: %s", strerror(errno)); return (-1); } return (0); } int bridge_create(const char *b_name) { char *new_name; struct ifreq ifr; bzero(&ifr, sizeof(ifr)); strlcpy(ifr.ifr_name, b_name, sizeof(ifr.ifr_name)); if (ioctl(sock, SIOCIFCREATE, &ifr) < 0) { syslog(LOG_ERR, "create bridge: ioctl(SIOCIFCREATE) " "failed: %s", strerror(errno)); return (-1); } if (strcmp(b_name, ifr.ifr_name) == 0) return (0); if ((new_name = strdup(b_name)) == NULL) { syslog(LOG_ERR, "create bridge: strdup() failed"); return (-1); } ifr.ifr_data = new_name; if (ioctl(sock, SIOCSIFNAME, (caddr_t) &ifr) < 0) { syslog(LOG_ERR, "create bridge: ioctl(SIOCSIFNAME) " "failed: %s", strerror(errno)); free(new_name); return (-1); } return (0); } int bridge_destroy(const char *b_name) { struct ifreq ifr; bzero(&ifr, sizeof(ifr)); strlcpy(ifr.ifr_name, b_name, sizeof(ifr.ifr_name)); if (ioctl(sock, SIOCIFDESTROY, &ifr) < 0) { syslog(LOG_ERR, "destroy bridge: ioctl(SIOCIFDESTROY) " "failed: %s", strerror(errno)); return (-1); } return (0); } /* * Fetch the bridge base MAC address. Return pointer to the * buffer containing the MAC address, NULL on failure. */ u_char * bridge_get_basemac(const char *bif_name, u_char *mac, size_t mlen) { int len; char if_name[IFNAMSIZ]; struct ifaddrs *ifap, *ifa; struct sockaddr_dl sdl; if (getifaddrs(&ifap) != 0) { syslog(LOG_ERR, "bridge get mac: getifaddrs() failed - %s", strerror(errno)); return (NULL); } for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) { if (ifa->ifa_addr->sa_family != AF_LINK) continue; /* * Not just casting because of alignment constraints * on sparc64. */ bcopy(ifa->ifa_addr, &sdl, sizeof(struct sockaddr_dl)); if (sdl.sdl_alen > mlen) continue; if ((len = sdl.sdl_nlen) >= IFNAMSIZ) len = IFNAMSIZ - 1; bcopy(sdl.sdl_data, if_name, len); if_name[len] = '\0'; if (strcmp(bif_name, if_name) == 0) { bcopy(sdl.sdl_data + sdl.sdl_nlen, mac, sdl.sdl_alen); freeifaddrs(ifap); return (mac); } } freeifaddrs(ifap); return (NULL); } /************************************************************************ * Bridge ports. */ /* * Convert the kernel STP port state into * the corresopnding enumerated type from SNMP Bridge MIB. */ static int state2snmp_st(uint8_t ifbr_state) { switch (ifbr_state) { case BSTP_IFSTATE_DISABLED: return (StpPortState_disabled); case BSTP_IFSTATE_LISTENING: return (StpPortState_listening); case BSTP_IFSTATE_LEARNING: return (StpPortState_learning); case BSTP_IFSTATE_FORWARDING: return (StpPortState_forwarding); case BSTP_IFSTATE_BLOCKING: case BSTP_IFSTATE_DISCARDING: return (StpPortState_blocking); } return (StpPortState_broken); } /* * Fill in a bridge member information according to data polled from kernel. */ static void bridge_port_getinfo_conf(struct ifbreq *k_info, struct bridge_port *bp) { bp->state = state2snmp_st(k_info->ifbr_state); bp->priority = k_info->ifbr_priority; /* * RFC 4188: * "New implementations should support dot1dStpPortPathCost32. * If the port path costs exceeds the maximum value of this * object then this object should report the maximum value, * namely 65535. Applications should try to read the * dot1dStpPortPathCost32 object if this object reports * the maximum value." */ if (k_info->ifbr_ifsflags & IFBIF_BSTP_ADMCOST) bp->admin_path_cost = k_info->ifbr_path_cost; else bp->admin_path_cost = 0; bp->path_cost = k_info->ifbr_path_cost; if (k_info->ifbr_ifsflags & IFBIF_STP) bp->enable = dot1dStpPortEnable_enabled; else bp->enable = dot1dStpPortEnable_disabled; /* Begemot Bridge MIB only. */ if (k_info->ifbr_ifsflags & IFBIF_SPAN) bp->span_enable = begemotBridgeBaseSpanEnabled_enabled; else bp->span_enable = begemotBridgeBaseSpanEnabled_disabled; if (k_info->ifbr_ifsflags & IFBIF_PRIVATE) bp->priv_set = TruthValue_true; else bp->priv_set = TruthValue_false; if (k_info->ifbr_ifsflags & IFBIF_BSTP_ADMEDGE) bp->admin_edge = TruthValue_true; else bp->admin_edge = TruthValue_false; if (k_info->ifbr_ifsflags & IFBIF_BSTP_EDGE) bp->oper_edge = TruthValue_true; else bp->oper_edge = TruthValue_false; if (k_info->ifbr_ifsflags & IFBIF_BSTP_AUTOPTP) { bp->admin_ptp = StpPortAdminPointToPointType_auto; if (k_info->ifbr_ifsflags & IFBIF_BSTP_PTP) bp->oper_ptp = TruthValue_true; else bp->oper_ptp = TruthValue_false; } else if (k_info->ifbr_ifsflags & IFBIF_BSTP_PTP) { bp->admin_ptp = StpPortAdminPointToPointType_forceTrue; bp->oper_ptp = TruthValue_true; } else { bp->admin_ptp = StpPortAdminPointToPointType_forceFalse; bp->oper_ptp = TruthValue_false; } } /* * Fill in a bridge interface STP information according to * data polled from kernel. */ static void bridge_port_getinfo_opstp(struct ifbpstpreq *bp_stp, struct bridge_port *bp) { bp->enable = dot1dStpPortEnable_enabled; bp->fwd_trans = bp_stp->ifbp_fwd_trans; bp->design_cost = bp_stp->ifbp_design_cost; snmp_uint64_to_bridgeid(bp_stp->ifbp_design_root, bp->design_root); snmp_uint64_to_bridgeid(bp_stp->ifbp_design_bridge, bp->design_bridge); bcopy(&(bp_stp->ifbp_design_port), &(bp->design_port), sizeof(uint16_t)); } /* * Clear a bridge interface STP information. */ static void bridge_port_clearinfo_opstp(struct bridge_port *bp) { if (bp->enable == dot1dStpPortEnable_enabled) { bp->design_cost = 0; bzero(&(bp->design_root), sizeof(bridge_id)); bzero(&(bp->design_bridge), sizeof(bridge_id)); bzero(&(bp->design_port), sizeof(port_id)); bp->fwd_trans = 0; } bp->enable = dot1dStpPortEnable_disabled; } /* * Set a bridge member priority. */ int bridge_port_set_priority(const char *bif_name, struct bridge_port *bp, int32_t priority) { struct ifdrv ifd; struct ifbreq b_req; strlcpy(ifd.ifd_name, bif_name, sizeof(ifd.ifd_name)); ifd.ifd_len = sizeof(b_req); ifd.ifd_data = &b_req; strlcpy(b_req.ifbr_ifsname, bp->p_name, sizeof(b_req.ifbr_ifsname)); b_req.ifbr_priority = (uint8_t) priority; ifd.ifd_cmd = BRDGSIFPRIO; if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) { syslog(LOG_ERR, "set member %s param: ioctl(BRDGSIFPRIO) " "failed: %s", bp->p_name, strerror(errno)); return (-1); } bp->priority = priority; return (0); } /* * Set a bridge member STP-enabled flag. */ int bridge_port_set_stp_enable(const char *bif_name, struct bridge_port *bp, uint32_t enable) { struct ifdrv ifd; struct ifbreq b_req; if (bp->enable == enable) return (0); bzero(&b_req, sizeof(b_req)); strlcpy(ifd.ifd_name, bif_name, sizeof(ifd.ifd_name)); ifd.ifd_len = sizeof(b_req); ifd.ifd_data = &b_req; strlcpy(b_req.ifbr_ifsname, bp->p_name, sizeof(b_req.ifbr_ifsname)); ifd.ifd_cmd = BRDGGIFFLGS; if (ioctl(sock, SIOCGDRVSPEC, &ifd) < 0) { syslog(LOG_ERR, "get member %s param: ioctl(BRDGGIFFLGS) " "failed: %s", bp->p_name, strerror(errno)); return (-1); } if (enable == dot1dStpPortEnable_enabled) b_req.ifbr_ifsflags |= IFBIF_STP; else b_req.ifbr_ifsflags &= ~IFBIF_STP; ifd.ifd_cmd = BRDGSIFFLGS; if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) { syslog(LOG_ERR, "set member %s param: ioctl(BRDGSIFFLGS) " "failed: %s", bp->p_name, strerror(errno)); return (-1); } bp->enable = enable; return (0); } /* * Set a bridge member STP path cost. */ int bridge_port_set_path_cost(const char *bif_name, struct bridge_port *bp, int32_t path_cost) { struct ifdrv ifd; struct ifbreq b_req; if (path_cost < SNMP_PORT_MIN_PATHCOST || path_cost > SNMP_PORT_PATHCOST_OBSOLETE) return (-2); strlcpy(ifd.ifd_name, bif_name, sizeof(ifd.ifd_name)); ifd.ifd_len = sizeof(b_req); ifd.ifd_data = &b_req; strlcpy(b_req.ifbr_ifsname, bp->p_name, sizeof(b_req.ifbr_ifsname)); b_req.ifbr_path_cost = path_cost; ifd.ifd_cmd = BRDGSIFCOST; if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) { syslog(LOG_ERR, "set member %s param: ioctl(BRDGSIFCOST) " "failed: %s", bp->p_name, strerror(errno)); return (-1); } bp->admin_path_cost = path_cost; return (0); } /* * Set the PonitToPoint status of the link administratively. */ int bridge_port_set_admin_ptp(const char *bif_name, struct bridge_port *bp, uint32_t admin_ptp) { struct ifdrv ifd; struct ifbreq b_req; if (bp->admin_ptp == admin_ptp) return (0); bzero(&b_req, sizeof(b_req)); strlcpy(ifd.ifd_name, bif_name, sizeof(ifd.ifd_name)); ifd.ifd_len = sizeof(b_req); ifd.ifd_data = &b_req; strlcpy(b_req.ifbr_ifsname, bp->p_name, sizeof(b_req.ifbr_ifsname)); ifd.ifd_cmd = BRDGGIFFLGS; if (ioctl(sock, SIOCGDRVSPEC, &ifd) < 0) { syslog(LOG_ERR, "get member %s param: ioctl(BRDGGIFFLGS) " "failed: %s", bp->p_name, strerror(errno)); return (-1); } switch (admin_ptp) { case StpPortAdminPointToPointType_forceTrue: b_req.ifbr_ifsflags &= ~IFBIF_BSTP_AUTOPTP; b_req.ifbr_ifsflags |= IFBIF_BSTP_PTP; break; case StpPortAdminPointToPointType_forceFalse: b_req.ifbr_ifsflags &= ~IFBIF_BSTP_AUTOPTP; b_req.ifbr_ifsflags &= ~IFBIF_BSTP_PTP; break; case StpPortAdminPointToPointType_auto: b_req.ifbr_ifsflags |= IFBIF_BSTP_AUTOPTP; break; } ifd.ifd_cmd = BRDGSIFFLGS; if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) { syslog(LOG_ERR, "set member %s param: ioctl(BRDGSIFFLGS) " "failed: %s", bp->p_name, strerror(errno)); return (-1); } bp->admin_ptp = admin_ptp; return (0); } /* * Set admin edge. */ int bridge_port_set_admin_edge(const char *bif_name, struct bridge_port *bp, uint32_t enable) { struct ifdrv ifd; struct ifbreq b_req; if (bp->admin_edge == enable) return (0); bzero(&b_req, sizeof(b_req)); strlcpy(ifd.ifd_name, bif_name, sizeof(ifd.ifd_name)); ifd.ifd_len = sizeof(b_req); ifd.ifd_data = &b_req; strlcpy(b_req.ifbr_ifsname, bp->p_name, sizeof(b_req.ifbr_ifsname)); ifd.ifd_cmd = BRDGGIFFLGS; if (ioctl(sock, SIOCGDRVSPEC, &ifd) < 0) { syslog(LOG_ERR, "get member %s param: ioctl(BRDGGIFFLGS) " "failed: %s", bp->p_name, strerror(errno)); return (-1); } if (enable == TruthValue_true) { b_req.ifbr_ifsflags &= ~IFBIF_BSTP_AUTOEDGE; b_req.ifbr_ifsflags |= IFBIF_BSTP_EDGE; } else b_req.ifbr_ifsflags &= ~IFBIF_BSTP_EDGE; ifd.ifd_cmd = BRDGSIFFLGS; if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) { syslog(LOG_ERR, "set member %s param: ioctl(BRDGSIFFLGS) " "failed: %s", bp->p_name, strerror(errno)); return (-1); } bp->admin_edge = enable; return (0); } /* * Set 'private' flag. */ int bridge_port_set_private(const char *bif_name, struct bridge_port *bp, uint32_t priv_set) { struct ifdrv ifd; struct ifbreq b_req; if (bp->priv_set == priv_set) return (0); bzero(&b_req, sizeof(b_req)); strlcpy(ifd.ifd_name, bif_name, sizeof(ifd.ifd_name)); ifd.ifd_len = sizeof(b_req); ifd.ifd_data = &b_req; strlcpy(b_req.ifbr_ifsname, bp->p_name, sizeof(b_req.ifbr_ifsname)); ifd.ifd_cmd = BRDGGIFFLGS; if (ioctl(sock, SIOCGDRVSPEC, &ifd) < 0) { syslog(LOG_ERR, "get member %s param: ioctl(BRDGGIFFLGS) " "failed: %s", bp->p_name, strerror(errno)); return (-1); } if (priv_set == TruthValue_true) b_req.ifbr_ifsflags |= IFBIF_PRIVATE; else if (priv_set == TruthValue_false) b_req.ifbr_ifsflags &= ~IFBIF_PRIVATE; else return (SNMP_ERR_WRONG_VALUE); ifd.ifd_cmd = BRDGSIFFLGS; if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) { syslog(LOG_ERR, "set member %s param: ioctl(BRDGSIFFLGS) " "failed: %s", bp->p_name, strerror(errno)); return (-1); } bp->priv_set = priv_set; return (0); } /* * Add a bridge member port. */ int bridge_port_addm(struct bridge_port *bp, const char *b_name) { struct ifdrv ifd; struct ifbreq b_req; bzero(&ifd, sizeof(ifd)); bzero(&b_req, sizeof(b_req)); strlcpy(ifd.ifd_name, b_name, sizeof(ifd.ifd_name)); ifd.ifd_len = sizeof(b_req); ifd.ifd_data = &b_req; strlcpy(b_req.ifbr_ifsname, bp->p_name, sizeof(b_req.ifbr_ifsname)); if (bp->span_enable == begemotBridgeBaseSpanEnabled_enabled) ifd.ifd_cmd = BRDGADDS; else ifd.ifd_cmd = BRDGADD; if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) { syslog(LOG_ERR, "%s - add member : ioctl(%s) failed: %s", bp->p_name, (ifd.ifd_cmd == BRDGADDS ? "BRDGADDS" : "BRDGADD"), strerror(errno)); return (-1); } return (0); } /* * Delete a bridge member port. */ int bridge_port_delm(struct bridge_port *bp, const char *b_name) { struct ifdrv ifd; struct ifbreq b_req; bzero(&ifd, sizeof(ifd)); bzero(&b_req, sizeof(b_req)); strlcpy(ifd.ifd_name, b_name, sizeof(ifd.ifd_name)); ifd.ifd_len = sizeof(b_req); ifd.ifd_data = &b_req; strlcpy(b_req.ifbr_ifsname, bp->p_name, sizeof(b_req.ifbr_ifsname)); if (bp->span_enable == begemotBridgeBaseSpanEnabled_enabled) ifd.ifd_cmd = BRDGDELS; else ifd.ifd_cmd = BRDGDEL; if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) { syslog(LOG_ERR, "%s - add member : ioctl(%s) failed: %s", bp->p_name, (ifd.ifd_cmd == BRDGDELS ? "BRDGDELS" : "BRDGDEL"), strerror(errno)); return (-1); } return (0); } /* * Fetch the bridge member list from kernel. * Return -1 on error, or buffer len if successful. */ static int32_t bridge_port_get_iflist(struct bridge_if *bif, struct ifbreq **buf) { int n = 128; uint32_t len; struct ifbreq *ninbuf; struct ifbifconf ifbc; struct ifdrv ifd; *buf = NULL; strlcpy(ifd.ifd_name, bif->bif_name, IFNAMSIZ); ifd.ifd_cmd = BRDGGIFS; ifd.ifd_len = sizeof(ifbc); ifd.ifd_data = &ifbc; for ( ; ; ) { len = n * sizeof(struct ifbreq); if ((ninbuf = (struct ifbreq *)realloc(*buf, len)) == NULL) { syslog(LOG_ERR, "get bridge member list: " "realloc failed: %s", strerror(errno)); free(*buf); *buf = NULL; return (-1); } ifbc.ifbic_len = len; ifbc.ifbic_req = *buf = ninbuf; if (ioctl(sock, SIOCGDRVSPEC, &ifd) < 0) { syslog(LOG_ERR, "get bridge member list: ioctl " "(BRDGGIFS) failed: %s", strerror(errno)); free(*buf); buf = NULL; return (-1); } if ((ifbc.ifbic_len + sizeof(struct ifbreq)) < len) break; n += 64; } return (ifbc.ifbic_len); } /* * Fetch the bridge STP member list from kernel. * Return -1 on error, or buffer len if successful. */ static int32_t bridge_port_get_ifstplist(struct bridge_if *bif, struct ifbpstpreq **buf) { int n = 128; uint32_t len; struct ifbpstpreq *ninbuf; struct ifbpstpconf ifbstp; struct ifdrv ifd; *buf = NULL; strlcpy(ifd.ifd_name, bif->bif_name, IFNAMSIZ); ifd.ifd_cmd = BRDGGIFSSTP; ifd.ifd_len = sizeof(ifbstp); ifd.ifd_data = &ifbstp; for ( ; ; ) { len = n * sizeof(struct ifbpstpreq); if ((ninbuf = (struct ifbpstpreq *) realloc(*buf, len)) == NULL) { syslog(LOG_ERR, "get bridge STP ports list: " "realloc failed: %s", strerror(errno)); free(*buf); *buf = NULL; return (-1); } ifbstp.ifbpstp_len = len; ifbstp.ifbpstp_req = *buf = ninbuf; if (ioctl(sock, SIOCGDRVSPEC, &ifd) < 0) { syslog(LOG_ERR, "get bridge STP ports list: ioctl " "(BRDGGIFSSTP) failed: %s", strerror(errno)); free(*buf); buf = NULL; return (-1); } if ((ifbstp.ifbpstp_len + sizeof(struct ifbpstpreq)) < len) break; n += 64; } return (ifbstp.ifbpstp_len); } /* * Locate a bridge if STP params structure in a buffer. */ static struct ifbpstpreq * bridge_port_find_ifstplist(uint8_t port_no, struct ifbpstpreq *buf, uint32_t buf_len) { uint32_t i; struct ifbpstpreq *bstp; for (i = 0; i < buf_len / sizeof(struct ifbpstpreq); i++) { bstp = buf + i; if (bstp->ifbp_portno == port_no) return (bstp); } return (NULL); } /* * Read the initial info for all members of a bridge interface. * Returns the number of ports, 0 - if none, otherwise * -1 if some other error occurred. */ int bridge_getinfo_bif_ports(struct bridge_if *bif) { uint32_t i; int32_t buf_len; struct ifbreq *b_req_buf, *b_req; struct ifbpstpreq *bs_req_buf, *bs_req; struct bridge_port *bp; struct mibif *m_if; if ((buf_len = bridge_port_get_iflist(bif, &b_req_buf)) < 0) return (-1); for (i = 0; i < buf_len / sizeof(struct ifbreq); i++) { b_req = b_req_buf + i; if ((m_if = mib_find_if_sys(b_req->ifbr_portno)) != NULL) { /* Hopefully we will not fail here. */ if ((bp = bridge_new_port(m_if, bif)) != NULL) { bp->status = RowStatus_active; bridge_port_getinfo_conf(b_req, bp); bridge_port_getinfo_mibif(m_if, bp); } } else { syslog(LOG_ERR, "bridge member %s not present " "in mibII ifTable", b_req->ifbr_ifsname); } } free(b_req_buf); if ((buf_len = bridge_port_get_ifstplist(bif, &bs_req_buf)) < 0) return (-1); for (bp = bridge_port_bif_first(bif); bp != NULL; bp = bridge_port_bif_next(bp)) { if ((bs_req = bridge_port_find_ifstplist(bp->port_no, bs_req_buf, buf_len)) == NULL) bridge_port_clearinfo_opstp(bp); else bridge_port_getinfo_opstp(bs_req, bp); } free(bs_req_buf); return (i); } /* * Update the information for the bridge interface members. */ int bridge_update_memif(struct bridge_if *bif) { int added, updated; uint32_t i; int32_t buf_len; struct ifbreq *b_req_buf, *b_req; struct ifbpstpreq *bs_req_buf, *bs_req; struct bridge_port *bp, *bp_next; struct mibif *m_if; if ((buf_len = bridge_port_get_iflist(bif, &b_req_buf)) < 0) return (-1); added = updated = 0; #define BP_FOUND 0x01 for (i = 0; i < buf_len / sizeof(struct ifbreq); i++) { b_req = b_req_buf + i; if ((m_if = mib_find_if_sys(b_req->ifbr_portno)) == NULL) { syslog(LOG_ERR, "bridge member %s not present " "in mibII ifTable", b_req->ifbr_ifsname); continue; } if ((bp = bridge_port_find(m_if->index, bif)) == NULL && (bp = bridge_new_port(m_if, bif)) != NULL) { bp->status = RowStatus_active; added++; } if (bp != NULL) { updated++; bridge_port_getinfo_conf(b_req, bp); bridge_port_getinfo_mibif(m_if, bp); bp->flags |= BP_FOUND; } } free(b_req_buf); /* Clean up list. */ for (bp = bridge_port_bif_first(bif); bp != NULL; bp = bp_next) { bp_next = bridge_port_bif_next(bp); if ((bp->flags & BP_FOUND) == 0 && bp->status == RowStatus_active) bridge_port_remove(bp, bif); else bp->flags |= ~BP_FOUND; } #undef BP_FOUND if ((buf_len = bridge_port_get_ifstplist(bif, &bs_req_buf)) < 0) return (-1); for (bp = bridge_port_bif_first(bif); bp != NULL; bp = bridge_port_bif_next(bp)) { if ((bs_req = bridge_port_find_ifstplist(bp->port_no, bs_req_buf, buf_len)) == NULL) bridge_port_clearinfo_opstp(bp); else bridge_port_getinfo_opstp(bs_req, bp); } free(bs_req_buf); bif->ports_age = time(NULL); return (updated); } /************************************************************************ * Bridge addresses. */ /* * Update the bridge address info according to the polled data. */ static void bridge_addrs_info_ifaddrlist(struct ifbareq *ifba, struct tp_entry *tpe) { tpe->port_no = if_nametoindex(ifba->ifba_ifsname); if ((ifba->ifba_flags & IFBAF_TYPEMASK) == IFBAF_STATIC) tpe->status = TpFdbStatus_mgmt; else tpe->status = TpFdbStatus_learned; } /* * Read the bridge addresses from kernel. * Return -1 on error, or buffer len if successful. */ static int32_t bridge_addrs_getinfo_ifalist(struct bridge_if *bif, struct ifbareq **buf) { int n = 128; uint32_t len; struct ifbareq *ninbuf; struct ifbaconf bac; struct ifdrv ifd; *buf = NULL; strlcpy(ifd.ifd_name, bif->bif_name, IFNAMSIZ); ifd.ifd_cmd = BRDGRTS; ifd.ifd_len = sizeof(bac); ifd.ifd_data = &bac; for ( ; ; ) { len = n * sizeof(struct ifbareq); if ((ninbuf = (struct ifbareq *)realloc(*buf, len)) == NULL) { syslog(LOG_ERR, "get bridge address list: " " realloc failed: %s", strerror(errno)); free(*buf); *buf = NULL; return (-1); } bac.ifbac_len = len; bac.ifbac_req = *buf = ninbuf; if (ioctl(sock, SIOCGDRVSPEC, &ifd) < 0) { syslog(LOG_ERR, "get bridge address list: " "ioctl(BRDGRTS) failed: %s", strerror(errno)); free(*buf); buf = NULL; return (-1); } if ((bac.ifbac_len + sizeof(struct ifbareq)) < len) break; n += 64; } return (bac.ifbac_len); } /* * Read the initial info for all addresses on a bridge interface. * Returns the number of addresses, 0 - if none, otherwise * -1 if some other error occurred. */ int bridge_getinfo_bif_addrs(struct bridge_if *bif) { uint32_t i; int32_t buf_len; struct ifbareq *addr_req_buf, *addr_req; struct tp_entry *te; if ((buf_len = bridge_addrs_getinfo_ifalist(bif, &addr_req_buf)) < 0) return (-1); for (i = 0; i < buf_len / sizeof(struct ifbareq); i++) { addr_req = addr_req_buf + i; if ((te = bridge_new_addrs(addr_req->ifba_dst, bif)) != NULL) bridge_addrs_info_ifaddrlist(addr_req, te); } free(addr_req_buf); return (i); } /* * Update the addresses for the bridge interface. */ int bridge_update_addrs(struct bridge_if *bif) { int added, updated; uint32_t i; int32_t buf_len; struct tp_entry *te, *te_next; struct ifbareq *addr_req_buf, *addr_req; if ((buf_len = bridge_addrs_getinfo_ifalist(bif, &addr_req_buf)) < 0) return (-1); added = updated = 0; #define BA_FOUND 0x01 for (i = 0; i < buf_len / sizeof(struct ifbareq); i++) { addr_req = addr_req_buf + i; if ((te = bridge_addrs_find(addr_req->ifba_dst, bif)) == NULL) { added++; if ((te = bridge_new_addrs(addr_req->ifba_dst, bif)) == NULL) continue; } else updated++; bridge_addrs_info_ifaddrlist(addr_req, te); te-> flags |= BA_FOUND; } free(addr_req_buf); for (te = bridge_addrs_bif_first(bif); te != NULL; te = te_next) { te_next = bridge_addrs_bif_next(te); if ((te-> flags & BA_FOUND) == 0) bridge_addrs_remove(te, bif); else te-> flags &= ~BA_FOUND; } #undef BA_FOUND bif->addrs_age = time(NULL); return (updated + added); } /************************************************************************ * Bridge packet filtering. */ const char bridge_sysctl[] = "net.link.bridge."; static struct { int32_t val; const char *name; } bridge_pf_sysctl[] = { { 1, "pfil_bridge" }, { 1, "pfil_member" }, { 1, "pfil_onlyip" }, { 0, "ipfw" }, }; int32_t bridge_get_pfval(uint8_t which) { if (which > nitems(bridge_pf_sysctl) || which < 1) return (-1); return (bridge_pf_sysctl[which - 1].val); } int32_t bridge_do_pfctl(int32_t bridge_ctl, enum snmp_op op, int32_t *val) { char *mib_oid; size_t len, s_len; int32_t i, s_i; if (bridge_ctl >= LEAF_begemotBridgeLayer2PfStatus) return (-2); if (op == SNMP_OP_SET) { s_i = *val; s_len = sizeof(s_i); } else s_len = 0; len = sizeof(i); asprintf(&mib_oid, "%s%s", bridge_sysctl, bridge_pf_sysctl[bridge_ctl].name); if (mib_oid == NULL) return (-1); if (sysctlbyname(mib_oid, &i, &len, (op == SNMP_OP_SET ? &s_i : NULL), s_len) == -1) { syslog(LOG_ERR, "sysctl(%s) failed - %s", mib_oid, strerror(errno)); free(mib_oid); return (-1); } bridge_pf_sysctl[bridge_ctl].val = i; *val = i; free(mib_oid); return (i); } void bridge_pf_dump(void) { uint8_t i; for (i = 0; i < nitems(bridge_pf_sysctl); i++) { syslog(LOG_ERR, "%s%s = %d", bridge_sysctl, bridge_pf_sysctl[i].name, bridge_pf_sysctl[i].val); } } Index: head/usr.sbin/bsnmpd/modules/snmp_hast/Makefile =================================================================== --- head/usr.sbin/bsnmpd/modules/snmp_hast/Makefile (revision 335884) +++ head/usr.sbin/bsnmpd/modules/snmp_hast/Makefile (revision 335885) @@ -1,42 +1,41 @@ # $FreeBSD$ .include .PATH: ${SRCTOP}/sbin/hastd MOD= hast SRCS= ebuf.c SRCS+= hast_compression.c hast_proto.c hast_snmp.c SRCS+= lzf.c SRCS+= nv.c SRCS+= parse.y pjdlog.c SRCS+= proto.c proto_common.c proto_uds.c SRCS+= token.l SRCS+= y.tab.h MAN= snmp_hast.3 NO_WFORMAT= NO_WCAST_ALIGN= NO_WMISSING_VARIABLE_DECLARATIONS= CFLAGS+=-I${SRCTOP}/sbin/hastd CFLAGS+=-DHAVE_CAPSICUM CFLAGS+=-DINET .if ${MK_INET6_SUPPORT} != "no" CFLAGS+=-DINET6 .endif # This is needed to have WARNS > 1. CFLAGS+=-DYY_NO_UNPUT CFLAGS+=-DYY_NO_INPUT -CFLAGS+= -DSNMPTREE_TYPES LIBADD= util XSYM= begemotHast DEFS= ${MOD}_tree.def BMIBS= BEGEMOT-HAST-MIB.txt YFLAGS+=-v CLEANFILES=y.tab.c y.tab.h y.output .include Index: head/usr.sbin/bsnmpd/modules/snmp_pf/Makefile =================================================================== --- head/usr.sbin/bsnmpd/modules/snmp_pf/Makefile (revision 335884) +++ head/usr.sbin/bsnmpd/modules/snmp_pf/Makefile (revision 335885) @@ -1,13 +1,12 @@ # $FreeBSD$ # # Author: Philip Paeps MOD= pf SRCS= pf_snmp.c -CFLAGS+= -DSNMPTREE_TYPES XSYM= begemotPf DEFS= ${MOD}_tree.def BMIBS= BEGEMOT-PF-MIB.txt .include Index: head/usr.sbin/bsnmpd/modules/snmp_pf/pf_snmp.c =================================================================== --- head/usr.sbin/bsnmpd/modules/snmp_pf/pf_snmp.c (revision 335884) +++ head/usr.sbin/bsnmpd/modules/snmp_pf/pf_snmp.c (revision 335885) @@ -1,1799 +1,1800 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2005 Philip Paeps * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include +#define SNMPTREE_TYPES #include "pf_oid.h" #include "pf_tree.h" struct lmodule *module; static int dev = -1; static int started; static uint64_t pf_tick; static struct pf_status pfs; enum { IN, OUT }; enum { IPV4, IPV6 }; enum { PASS, BLOCK }; #define PFI_IFTYPE_GROUP 0 #define PFI_IFTYPE_INSTANCE 1 #define PFI_IFTYPE_DETACHED 2 struct pfi_entry { struct pfi_kif pfi; u_int index; TAILQ_ENTRY(pfi_entry) link; }; TAILQ_HEAD(pfi_table, pfi_entry); static struct pfi_table pfi_table; static time_t pfi_table_age; static int pfi_table_count; #define PFI_TABLE_MAXAGE 5 struct pft_entry { struct pfr_tstats pft; u_int index; TAILQ_ENTRY(pft_entry) link; }; TAILQ_HEAD(pft_table, pft_entry); static struct pft_table pft_table; static time_t pft_table_age; static int pft_table_count; #define PFT_TABLE_MAXAGE 5 struct pfa_entry { struct pfr_astats pfas; u_int index; TAILQ_ENTRY(pfa_entry) link; }; TAILQ_HEAD(pfa_table, pfa_entry); static struct pfa_table pfa_table; static time_t pfa_table_age; static int pfa_table_count; #define PFA_TABLE_MAXAGE 5 struct pfq_entry { struct pf_altq altq; u_int index; TAILQ_ENTRY(pfq_entry) link; }; TAILQ_HEAD(pfq_table, pfq_entry); static struct pfq_table pfq_table; static time_t pfq_table_age; static int pfq_table_count; static int altq_enabled = 0; #define PFQ_TABLE_MAXAGE 5 struct pfl_entry { char name[MAXPATHLEN + PF_RULE_LABEL_SIZE]; u_int64_t evals; u_int64_t bytes[2]; u_int64_t pkts[2]; u_int index; TAILQ_ENTRY(pfl_entry) link; }; TAILQ_HEAD(pfl_table, pfl_entry); static struct pfl_table pfl_table; static time_t pfl_table_age; static int pfl_table_count; #define PFL_TABLE_MAXAGE 5 /* Forward declarations */ static int pfi_refresh(void); static int pfq_refresh(void); static int pfs_refresh(void); static int pft_refresh(void); static int pfa_refresh(void); static int pfl_refresh(void); static struct pfi_entry * pfi_table_find(u_int idx); static struct pfq_entry * pfq_table_find(u_int idx); static struct pft_entry * pft_table_find(u_int idx); static struct pfa_entry * pfa_table_find(u_int idx); static struct pfl_entry * pfl_table_find(u_int idx); static int altq_is_enabled(int pfdevice); int pf_status(struct snmp_context __unused *ctx, struct snmp_value *val, u_int sub, u_int __unused vindex, enum snmp_op op) { asn_subid_t which = val->var.subs[sub - 1]; time_t runtime; unsigned char str[128]; if (op == SNMP_OP_SET) return (SNMP_ERR_NOT_WRITEABLE); if (op == SNMP_OP_GET) { if (pfs_refresh() == -1) return (SNMP_ERR_GENERR); switch (which) { case LEAF_pfStatusRunning: val->v.uint32 = pfs.running; break; case LEAF_pfStatusRuntime: runtime = (pfs.since > 0) ? time(NULL) - pfs.since : 0; val->v.uint32 = runtime * 100; break; case LEAF_pfStatusDebug: val->v.uint32 = pfs.debug; break; case LEAF_pfStatusHostId: sprintf(str, "0x%08x", ntohl(pfs.hostid)); return (string_get(val, str, strlen(str))); default: return (SNMP_ERR_NOSUCHNAME); } return (SNMP_ERR_NOERROR); } abort(); } int pf_counter(struct snmp_context __unused *ctx, struct snmp_value *val, u_int sub, u_int __unused vindex, enum snmp_op op) { asn_subid_t which = val->var.subs[sub - 1]; if (op == SNMP_OP_SET) return (SNMP_ERR_NOT_WRITEABLE); if (op == SNMP_OP_GET) { if (pfs_refresh() == -1) return (SNMP_ERR_GENERR); switch (which) { case LEAF_pfCounterMatch: val->v.counter64 = pfs.counters[PFRES_MATCH]; break; case LEAF_pfCounterBadOffset: val->v.counter64 = pfs.counters[PFRES_BADOFF]; break; case LEAF_pfCounterFragment: val->v.counter64 = pfs.counters[PFRES_FRAG]; break; case LEAF_pfCounterShort: val->v.counter64 = pfs.counters[PFRES_SHORT]; break; case LEAF_pfCounterNormalize: val->v.counter64 = pfs.counters[PFRES_NORM]; break; case LEAF_pfCounterMemDrop: val->v.counter64 = pfs.counters[PFRES_MEMORY]; break; default: return (SNMP_ERR_NOSUCHNAME); } return (SNMP_ERR_NOERROR); } abort(); } int pf_statetable(struct snmp_context __unused *ctx, struct snmp_value *val, u_int sub, u_int __unused vindex, enum snmp_op op) { asn_subid_t which = val->var.subs[sub - 1]; if (op == SNMP_OP_SET) return (SNMP_ERR_NOT_WRITEABLE); if (op == SNMP_OP_GET) { if (pfs_refresh() == -1) return (SNMP_ERR_GENERR); switch (which) { case LEAF_pfStateTableCount: val->v.uint32 = pfs.states; break; case LEAF_pfStateTableSearches: val->v.counter64 = pfs.fcounters[FCNT_STATE_SEARCH]; break; case LEAF_pfStateTableInserts: val->v.counter64 = pfs.fcounters[FCNT_STATE_INSERT]; break; case LEAF_pfStateTableRemovals: val->v.counter64 = pfs.fcounters[FCNT_STATE_REMOVALS]; break; default: return (SNMP_ERR_NOSUCHNAME); } return (SNMP_ERR_NOERROR); } abort(); } int pf_srcnodes(struct snmp_context __unused *ctx, struct snmp_value *val, u_int sub, u_int __unused vindex, enum snmp_op op) { asn_subid_t which = val->var.subs[sub - 1]; if (op == SNMP_OP_SET) return (SNMP_ERR_NOT_WRITEABLE); if (op == SNMP_OP_GET) { if (pfs_refresh() == -1) return (SNMP_ERR_GENERR); switch (which) { case LEAF_pfSrcNodesCount: val->v.uint32 = pfs.src_nodes; break; case LEAF_pfSrcNodesSearches: val->v.counter64 = pfs.scounters[SCNT_SRC_NODE_SEARCH]; break; case LEAF_pfSrcNodesInserts: val->v.counter64 = pfs.scounters[SCNT_SRC_NODE_INSERT]; break; case LEAF_pfSrcNodesRemovals: val->v.counter64 = pfs.scounters[SCNT_SRC_NODE_REMOVALS]; break; default: return (SNMP_ERR_NOSUCHNAME); } return (SNMP_ERR_NOERROR); } abort(); } int pf_limits(struct snmp_context __unused *ctx, struct snmp_value *val, u_int sub, u_int __unused vindex, enum snmp_op op) { asn_subid_t which = val->var.subs[sub - 1]; struct pfioc_limit pl; if (op == SNMP_OP_SET) return (SNMP_ERR_NOT_WRITEABLE); if (op == SNMP_OP_GET) { bzero(&pl, sizeof(struct pfioc_limit)); switch (which) { case LEAF_pfLimitsStates: pl.index = PF_LIMIT_STATES; break; case LEAF_pfLimitsSrcNodes: pl.index = PF_LIMIT_SRC_NODES; break; case LEAF_pfLimitsFrags: pl.index = PF_LIMIT_FRAGS; break; default: return (SNMP_ERR_NOSUCHNAME); } if (ioctl(dev, DIOCGETLIMIT, &pl)) { syslog(LOG_ERR, "pf_limits(): ioctl(): %s", strerror(errno)); return (SNMP_ERR_GENERR); } val->v.uint32 = pl.limit; return (SNMP_ERR_NOERROR); } abort(); } int pf_timeouts(struct snmp_context __unused *ctx, struct snmp_value *val, u_int sub, u_int __unused vindex, enum snmp_op op) { asn_subid_t which = val->var.subs[sub - 1]; struct pfioc_tm pt; if (op == SNMP_OP_SET) return (SNMP_ERR_NOT_WRITEABLE); if (op == SNMP_OP_GET) { bzero(&pt, sizeof(struct pfioc_tm)); switch (which) { case LEAF_pfTimeoutsTcpFirst: pt.timeout = PFTM_TCP_FIRST_PACKET; break; case LEAF_pfTimeoutsTcpOpening: pt.timeout = PFTM_TCP_OPENING; break; case LEAF_pfTimeoutsTcpEstablished: pt.timeout = PFTM_TCP_ESTABLISHED; break; case LEAF_pfTimeoutsTcpClosing: pt.timeout = PFTM_TCP_CLOSING; break; case LEAF_pfTimeoutsTcpFinWait: pt.timeout = PFTM_TCP_FIN_WAIT; break; case LEAF_pfTimeoutsTcpClosed: pt.timeout = PFTM_TCP_CLOSED; break; case LEAF_pfTimeoutsUdpFirst: pt.timeout = PFTM_UDP_FIRST_PACKET; break; case LEAF_pfTimeoutsUdpSingle: pt.timeout = PFTM_UDP_SINGLE; break; case LEAF_pfTimeoutsUdpMultiple: pt.timeout = PFTM_UDP_MULTIPLE; break; case LEAF_pfTimeoutsIcmpFirst: pt.timeout = PFTM_ICMP_FIRST_PACKET; break; case LEAF_pfTimeoutsIcmpError: pt.timeout = PFTM_ICMP_ERROR_REPLY; break; case LEAF_pfTimeoutsOtherFirst: pt.timeout = PFTM_OTHER_FIRST_PACKET; break; case LEAF_pfTimeoutsOtherSingle: pt.timeout = PFTM_OTHER_SINGLE; break; case LEAF_pfTimeoutsOtherMultiple: pt.timeout = PFTM_OTHER_MULTIPLE; break; case LEAF_pfTimeoutsFragment: pt.timeout = PFTM_FRAG; break; case LEAF_pfTimeoutsInterval: pt.timeout = PFTM_INTERVAL; break; case LEAF_pfTimeoutsAdaptiveStart: pt.timeout = PFTM_ADAPTIVE_START; break; case LEAF_pfTimeoutsAdaptiveEnd: pt.timeout = PFTM_ADAPTIVE_END; break; case LEAF_pfTimeoutsSrcNode: pt.timeout = PFTM_SRC_NODE; break; default: return (SNMP_ERR_NOSUCHNAME); } if (ioctl(dev, DIOCGETTIMEOUT, &pt)) { syslog(LOG_ERR, "pf_timeouts(): ioctl(): %s", strerror(errno)); return (SNMP_ERR_GENERR); } val->v.integer = pt.seconds; return (SNMP_ERR_NOERROR); } abort(); } int pf_logif(struct snmp_context __unused *ctx, struct snmp_value *val, u_int sub, u_int __unused vindex, enum snmp_op op) { asn_subid_t which = val->var.subs[sub - 1]; unsigned char str[IFNAMSIZ]; if (op == SNMP_OP_SET) return (SNMP_ERR_NOT_WRITEABLE); if (op == SNMP_OP_GET) { if (pfs_refresh() == -1) return (SNMP_ERR_GENERR); switch (which) { case LEAF_pfLogInterfaceName: strlcpy(str, pfs.ifname, sizeof str); return (string_get(val, str, strlen(str))); case LEAF_pfLogInterfaceIp4BytesIn: val->v.counter64 = pfs.bcounters[IPV4][IN]; break; case LEAF_pfLogInterfaceIp4BytesOut: val->v.counter64 = pfs.bcounters[IPV4][OUT]; break; case LEAF_pfLogInterfaceIp4PktsInPass: val->v.counter64 = pfs.pcounters[IPV4][IN][PF_PASS]; break; case LEAF_pfLogInterfaceIp4PktsInDrop: val->v.counter64 = pfs.pcounters[IPV4][IN][PF_DROP]; break; case LEAF_pfLogInterfaceIp4PktsOutPass: val->v.counter64 = pfs.pcounters[IPV4][OUT][PF_PASS]; break; case LEAF_pfLogInterfaceIp4PktsOutDrop: val->v.counter64 = pfs.pcounters[IPV4][OUT][PF_DROP]; break; case LEAF_pfLogInterfaceIp6BytesIn: val->v.counter64 = pfs.bcounters[IPV6][IN]; break; case LEAF_pfLogInterfaceIp6BytesOut: val->v.counter64 = pfs.bcounters[IPV6][OUT]; break; case LEAF_pfLogInterfaceIp6PktsInPass: val->v.counter64 = pfs.pcounters[IPV6][IN][PF_PASS]; break; case LEAF_pfLogInterfaceIp6PktsInDrop: val->v.counter64 = pfs.pcounters[IPV6][IN][PF_DROP]; break; case LEAF_pfLogInterfaceIp6PktsOutPass: val->v.counter64 = pfs.pcounters[IPV6][OUT][PF_PASS]; break; case LEAF_pfLogInterfaceIp6PktsOutDrop: val->v.counter64 = pfs.pcounters[IPV6][OUT][PF_DROP]; break; default: return (SNMP_ERR_NOSUCHNAME); } return (SNMP_ERR_NOERROR); } abort(); } int pf_interfaces(struct snmp_context __unused *ctx, struct snmp_value *val, u_int sub, u_int __unused vindex, enum snmp_op op) { asn_subid_t which = val->var.subs[sub - 1]; if (op == SNMP_OP_SET) return (SNMP_ERR_NOT_WRITEABLE); if (op == SNMP_OP_GET) { if ((time(NULL) - pfi_table_age) > PFI_TABLE_MAXAGE) if (pfi_refresh() == -1) return (SNMP_ERR_GENERR); switch (which) { case LEAF_pfInterfacesIfNumber: val->v.uint32 = pfi_table_count; break; default: return (SNMP_ERR_NOSUCHNAME); } return (SNMP_ERR_NOERROR); } abort(); } int pf_iftable(struct snmp_context __unused *ctx, struct snmp_value *val, u_int sub, u_int __unused vindex, enum snmp_op op) { asn_subid_t which = val->var.subs[sub - 1]; struct pfi_entry *e = NULL; if ((time(NULL) - pfi_table_age) > PFI_TABLE_MAXAGE) pfi_refresh(); switch (op) { case SNMP_OP_SET: return (SNMP_ERR_NOT_WRITEABLE); case SNMP_OP_GETNEXT: if ((e = NEXT_OBJECT_INT(&pfi_table, &val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); val->var.len = sub + 1; val->var.subs[sub] = e->index; break; case SNMP_OP_GET: if (val->var.len - sub != 1) return (SNMP_ERR_NOSUCHNAME); if ((e = pfi_table_find(val->var.subs[sub])) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_COMMIT: case SNMP_OP_ROLLBACK: default: abort(); } switch (which) { case LEAF_pfInterfacesIfDescr: return (string_get(val, e->pfi.pfik_name, -1)); case LEAF_pfInterfacesIfType: val->v.integer = PFI_IFTYPE_INSTANCE; break; case LEAF_pfInterfacesIfTZero: val->v.uint32 = (time(NULL) - e->pfi.pfik_tzero) * 100; break; case LEAF_pfInterfacesIfRefsRule: val->v.uint32 = e->pfi.pfik_rulerefs; break; case LEAF_pfInterfacesIf4BytesInPass: val->v.counter64 = e->pfi.pfik_bytes[IPV4][IN][PASS]; break; case LEAF_pfInterfacesIf4BytesInBlock: val->v.counter64 = e->pfi.pfik_bytes[IPV4][IN][BLOCK]; break; case LEAF_pfInterfacesIf4BytesOutPass: val->v.counter64 = e->pfi.pfik_bytes[IPV4][OUT][PASS]; break; case LEAF_pfInterfacesIf4BytesOutBlock: val->v.counter64 = e->pfi.pfik_bytes[IPV4][OUT][BLOCK]; break; case LEAF_pfInterfacesIf4PktsInPass: val->v.counter64 = e->pfi.pfik_packets[IPV4][IN][PASS]; break; case LEAF_pfInterfacesIf4PktsInBlock: val->v.counter64 = e->pfi.pfik_packets[IPV4][IN][BLOCK]; break; case LEAF_pfInterfacesIf4PktsOutPass: val->v.counter64 = e->pfi.pfik_packets[IPV4][OUT][PASS]; break; case LEAF_pfInterfacesIf4PktsOutBlock: val->v.counter64 = e->pfi.pfik_packets[IPV4][OUT][BLOCK]; break; case LEAF_pfInterfacesIf6BytesInPass: val->v.counter64 = e->pfi.pfik_bytes[IPV6][IN][PASS]; break; case LEAF_pfInterfacesIf6BytesInBlock: val->v.counter64 = e->pfi.pfik_bytes[IPV6][IN][BLOCK]; break; case LEAF_pfInterfacesIf6BytesOutPass: val->v.counter64 = e->pfi.pfik_bytes[IPV6][OUT][PASS]; break; case LEAF_pfInterfacesIf6BytesOutBlock: val->v.counter64 = e->pfi.pfik_bytes[IPV6][OUT][BLOCK]; break; case LEAF_pfInterfacesIf6PktsInPass: val->v.counter64 = e->pfi.pfik_packets[IPV6][IN][PASS]; break; case LEAF_pfInterfacesIf6PktsInBlock: val->v.counter64 = e->pfi.pfik_packets[IPV6][IN][BLOCK]; break; case LEAF_pfInterfacesIf6PktsOutPass: val->v.counter64 = e->pfi.pfik_packets[IPV6][OUT][PASS]; break; case LEAF_pfInterfacesIf6PktsOutBlock: val->v.counter64 = e->pfi.pfik_packets[IPV6][OUT][BLOCK]; break; default: return (SNMP_ERR_NOSUCHNAME); } return (SNMP_ERR_NOERROR); } int pf_tables(struct snmp_context __unused *ctx, struct snmp_value *val, u_int sub, u_int __unused vindex, enum snmp_op op) { asn_subid_t which = val->var.subs[sub - 1]; if (op == SNMP_OP_SET) return (SNMP_ERR_NOT_WRITEABLE); if (op == SNMP_OP_GET) { if ((time(NULL) - pft_table_age) > PFT_TABLE_MAXAGE) if (pft_refresh() == -1) return (SNMP_ERR_GENERR); switch (which) { case LEAF_pfTablesTblNumber: val->v.uint32 = pft_table_count; break; default: return (SNMP_ERR_NOSUCHNAME); } return (SNMP_ERR_NOERROR); } abort(); } int pf_tbltable(struct snmp_context __unused *ctx, struct snmp_value *val, u_int sub, u_int __unused vindex, enum snmp_op op) { asn_subid_t which = val->var.subs[sub - 1]; struct pft_entry *e = NULL; if ((time(NULL) - pft_table_age) > PFT_TABLE_MAXAGE) pft_refresh(); switch (op) { case SNMP_OP_SET: return (SNMP_ERR_NOT_WRITEABLE); case SNMP_OP_GETNEXT: if ((e = NEXT_OBJECT_INT(&pft_table, &val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); val->var.len = sub + 1; val->var.subs[sub] = e->index; break; case SNMP_OP_GET: if (val->var.len - sub != 1) return (SNMP_ERR_NOSUCHNAME); if ((e = pft_table_find(val->var.subs[sub])) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_COMMIT: case SNMP_OP_ROLLBACK: default: abort(); } switch (which) { case LEAF_pfTablesTblDescr: return (string_get(val, e->pft.pfrts_name, -1)); case LEAF_pfTablesTblCount: val->v.integer = e->pft.pfrts_cnt; break; case LEAF_pfTablesTblTZero: val->v.uint32 = (time(NULL) - e->pft.pfrts_tzero) * 100; break; case LEAF_pfTablesTblRefsAnchor: val->v.integer = e->pft.pfrts_refcnt[PFR_REFCNT_ANCHOR]; break; case LEAF_pfTablesTblRefsRule: val->v.integer = e->pft.pfrts_refcnt[PFR_REFCNT_RULE]; break; case LEAF_pfTablesTblEvalMatch: val->v.counter64 = e->pft.pfrts_match; break; case LEAF_pfTablesTblEvalNoMatch: val->v.counter64 = e->pft.pfrts_nomatch; break; case LEAF_pfTablesTblBytesInPass: val->v.counter64 = e->pft.pfrts_bytes[PFR_DIR_IN][PFR_OP_PASS]; break; case LEAF_pfTablesTblBytesInBlock: val->v.counter64 = e->pft.pfrts_bytes[PFR_DIR_IN][PFR_OP_BLOCK]; break; case LEAF_pfTablesTblBytesInXPass: val->v.counter64 = e->pft.pfrts_bytes[PFR_DIR_IN][PFR_OP_XPASS]; break; case LEAF_pfTablesTblBytesOutPass: val->v.counter64 = e->pft.pfrts_bytes[PFR_DIR_OUT][PFR_OP_PASS]; break; case LEAF_pfTablesTblBytesOutBlock: val->v.counter64 = e->pft.pfrts_bytes[PFR_DIR_OUT][PFR_OP_BLOCK]; break; case LEAF_pfTablesTblBytesOutXPass: val->v.counter64 = e->pft.pfrts_bytes[PFR_DIR_OUT][PFR_OP_XPASS]; break; case LEAF_pfTablesTblPktsInPass: val->v.counter64 = e->pft.pfrts_packets[PFR_DIR_IN][PFR_OP_PASS]; break; case LEAF_pfTablesTblPktsInBlock: val->v.counter64 = e->pft.pfrts_packets[PFR_DIR_IN][PFR_OP_BLOCK]; break; case LEAF_pfTablesTblPktsInXPass: val->v.counter64 = e->pft.pfrts_packets[PFR_DIR_IN][PFR_OP_XPASS]; break; case LEAF_pfTablesTblPktsOutPass: val->v.counter64 = e->pft.pfrts_packets[PFR_DIR_OUT][PFR_OP_PASS]; break; case LEAF_pfTablesTblPktsOutBlock: val->v.counter64 = e->pft.pfrts_packets[PFR_DIR_OUT][PFR_OP_BLOCK]; break; case LEAF_pfTablesTblPktsOutXPass: val->v.counter64 = e->pft.pfrts_packets[PFR_DIR_OUT][PFR_OP_XPASS]; break; default: return (SNMP_ERR_NOSUCHNAME); } return (SNMP_ERR_NOERROR); } int pf_tbladdr(struct snmp_context __unused *ctx, struct snmp_value __unused *val, u_int __unused sub, u_int __unused vindex, enum snmp_op __unused op) { asn_subid_t which = val->var.subs[sub - 1]; struct pfa_entry *e = NULL; if ((time(NULL) - pfa_table_age) > PFA_TABLE_MAXAGE) pfa_refresh(); switch (op) { case SNMP_OP_SET: return (SNMP_ERR_NOT_WRITEABLE); case SNMP_OP_GETNEXT: if ((e = NEXT_OBJECT_INT(&pfa_table, &val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); val->var.len = sub + 1; val->var.subs[sub] = e->index; break; case SNMP_OP_GET: if (val->var.len - sub != 1) return (SNMP_ERR_NOSUCHNAME); if ((e = pfa_table_find(val->var.subs[sub])) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_COMMIT: case SNMP_OP_ROLLBACK: default: abort(); } switch (which) { case LEAF_pfTablesAddrNetType: if (e->pfas.pfras_a.pfra_af == AF_INET) val->v.integer = pfTablesAddrNetType_ipv4; else if (e->pfas.pfras_a.pfra_af == AF_INET6) val->v.integer = pfTablesAddrNetType_ipv6; else return (SNMP_ERR_GENERR); break; case LEAF_pfTablesAddrNet: if (e->pfas.pfras_a.pfra_af == AF_INET) { return (string_get(val, (u_char *)&e->pfas.pfras_a.pfra_ip4addr, 4)); } else if (e->pfas.pfras_a.pfra_af == AF_INET6) return (string_get(val, (u_char *)&e->pfas.pfras_a.pfra_ip6addr, 16)); else return (SNMP_ERR_GENERR); break; case LEAF_pfTablesAddrPrefix: val->v.integer = (int32_t) e->pfas.pfras_a.pfra_net; break; case LEAF_pfTablesAddrTZero: val->v.uint32 = (time(NULL) - e->pfas.pfras_tzero) * 100; break; case LEAF_pfTablesAddrBytesInPass: val->v.counter64 = e->pfas.pfras_bytes[PFR_DIR_IN][PFR_OP_PASS]; break; case LEAF_pfTablesAddrBytesInBlock: val->v.counter64 = e->pfas.pfras_bytes[PFR_DIR_IN][PFR_OP_BLOCK]; break; case LEAF_pfTablesAddrBytesOutPass: val->v.counter64 = e->pfas.pfras_bytes[PFR_DIR_OUT][PFR_OP_PASS]; break; case LEAF_pfTablesAddrBytesOutBlock: val->v.counter64 = e->pfas.pfras_bytes[PFR_DIR_OUT][PFR_OP_BLOCK]; break; case LEAF_pfTablesAddrPktsInPass: val->v.counter64 = e->pfas.pfras_packets[PFR_DIR_IN][PFR_OP_PASS]; break; case LEAF_pfTablesAddrPktsInBlock: val->v.counter64 = e->pfas.pfras_packets[PFR_DIR_IN][PFR_OP_BLOCK]; break; case LEAF_pfTablesAddrPktsOutPass: val->v.counter64 = e->pfas.pfras_packets[PFR_DIR_OUT][PFR_OP_PASS]; break; case LEAF_pfTablesAddrPktsOutBlock: val->v.counter64 = e->pfas.pfras_packets[PFR_DIR_OUT][PFR_OP_BLOCK]; break; default: return (SNMP_ERR_NOSUCHNAME); } return (SNMP_ERR_NOERROR); } int pf_altq(struct snmp_context __unused *ctx, struct snmp_value *val, u_int sub, u_int __unused vindex, enum snmp_op op) { asn_subid_t which = val->var.subs[sub - 1]; if (!altq_enabled) return (SNMP_ERR_NOSUCHNAME); if (op == SNMP_OP_SET) return (SNMP_ERR_NOT_WRITEABLE); if (op == SNMP_OP_GET) { if ((time(NULL) - pfq_table_age) > PFQ_TABLE_MAXAGE) if (pfq_refresh() == -1) return (SNMP_ERR_GENERR); switch (which) { case LEAF_pfAltqQueueNumber: val->v.uint32 = pfq_table_count; break; default: return (SNMP_ERR_NOSUCHNAME); } return (SNMP_ERR_NOERROR); } abort(); return (SNMP_ERR_GENERR); } int pf_altqq(struct snmp_context __unused *ctx, struct snmp_value *val, u_int sub, u_int __unused vindex, enum snmp_op op) { asn_subid_t which = val->var.subs[sub - 1]; struct pfq_entry *e = NULL; if (!altq_enabled) return (SNMP_ERR_NOSUCHNAME); if ((time(NULL) - pfq_table_age) > PFQ_TABLE_MAXAGE) pfq_refresh(); switch (op) { case SNMP_OP_SET: return (SNMP_ERR_NOT_WRITEABLE); case SNMP_OP_GETNEXT: if ((e = NEXT_OBJECT_INT(&pfq_table, &val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); val->var.len = sub + 1; val->var.subs[sub] = e->index; break; case SNMP_OP_GET: if (val->var.len - sub != 1) return (SNMP_ERR_NOSUCHNAME); if ((e = pfq_table_find(val->var.subs[sub])) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_COMMIT: case SNMP_OP_ROLLBACK: default: abort(); } switch (which) { case LEAF_pfAltqQueueDescr: return (string_get(val, e->altq.qname, -1)); case LEAF_pfAltqQueueParent: return (string_get(val, e->altq.parent, -1)); case LEAF_pfAltqQueueScheduler: val->v.integer = e->altq.scheduler; break; case LEAF_pfAltqQueueBandwidth: val->v.uint32 = e->altq.bandwidth; break; case LEAF_pfAltqQueuePriority: val->v.integer = e->altq.priority; break; case LEAF_pfAltqQueueLimit: val->v.integer = e->altq.qlimit; break; default: return (SNMP_ERR_NOSUCHNAME); } return (SNMP_ERR_NOERROR); } int pf_labels(struct snmp_context __unused *ctx, struct snmp_value *val, u_int sub, u_int __unused vindex, enum snmp_op op) { asn_subid_t which = val->var.subs[sub - 1]; if (op == SNMP_OP_SET) return (SNMP_ERR_NOT_WRITEABLE); if (op == SNMP_OP_GET) { if ((time(NULL) - pfl_table_age) > PFL_TABLE_MAXAGE) if (pfl_refresh() == -1) return (SNMP_ERR_GENERR); switch (which) { case LEAF_pfLabelsLblNumber: val->v.uint32 = pfl_table_count; break; default: return (SNMP_ERR_NOSUCHNAME); } return (SNMP_ERR_NOERROR); } abort(); return (SNMP_ERR_GENERR); } int pf_lbltable(struct snmp_context __unused *ctx, struct snmp_value *val, u_int sub, u_int __unused vindex, enum snmp_op op) { asn_subid_t which = val->var.subs[sub - 1]; struct pfl_entry *e = NULL; if ((time(NULL) - pfl_table_age) > PFL_TABLE_MAXAGE) pfl_refresh(); switch (op) { case SNMP_OP_SET: return (SNMP_ERR_NOT_WRITEABLE); case SNMP_OP_GETNEXT: if ((e = NEXT_OBJECT_INT(&pfl_table, &val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); val->var.len = sub + 1; val->var.subs[sub] = e->index; break; case SNMP_OP_GET: if (val->var.len - sub != 1) return (SNMP_ERR_NOSUCHNAME); if ((e = pfl_table_find(val->var.subs[sub])) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_COMMIT: case SNMP_OP_ROLLBACK: default: abort(); } switch (which) { case LEAF_pfLabelsLblName: return (string_get(val, e->name, -1)); case LEAF_pfLabelsLblEvals: val->v.counter64 = e->evals; break; case LEAF_pfLabelsLblBytesIn: val->v.counter64 = e->bytes[IN]; break; case LEAF_pfLabelsLblBytesOut: val->v.counter64 = e->bytes[OUT]; break; case LEAF_pfLabelsLblPktsIn: val->v.counter64 = e->pkts[IN]; break; case LEAF_pfLabelsLblPktsOut: val->v.counter64 = e->pkts[OUT]; break; default: return (SNMP_ERR_NOSUCHNAME); } return (SNMP_ERR_NOERROR); } static struct pfi_entry * pfi_table_find(u_int idx) { struct pfi_entry *e; TAILQ_FOREACH(e, &pfi_table, link) if (e->index == idx) return (e); return (NULL); } static struct pfq_entry * pfq_table_find(u_int idx) { struct pfq_entry *e; TAILQ_FOREACH(e, &pfq_table, link) if (e->index == idx) return (e); return (NULL); } static struct pft_entry * pft_table_find(u_int idx) { struct pft_entry *e; TAILQ_FOREACH(e, &pft_table, link) if (e->index == idx) return (e); return (NULL); } static struct pfa_entry * pfa_table_find(u_int idx) { struct pfa_entry *e; TAILQ_FOREACH(e, &pfa_table, link) if (e->index == idx) return (e); return (NULL); } static struct pfl_entry * pfl_table_find(u_int idx) { struct pfl_entry *e; TAILQ_FOREACH(e, &pfl_table, link) if (e->index == idx) return (e); return (NULL); } static int pfi_refresh(void) { struct pfioc_iface io; struct pfi_kif *p = NULL; struct pfi_entry *e; int i, numifs = 1; if (started && this_tick <= pf_tick) return (0); while (!TAILQ_EMPTY(&pfi_table)) { e = TAILQ_FIRST(&pfi_table); TAILQ_REMOVE(&pfi_table, e, link); free(e); } bzero(&io, sizeof(io)); io.pfiio_esize = sizeof(struct pfi_kif); for (;;) { p = reallocf(p, numifs * sizeof(struct pfi_kif)); if (p == NULL) { syslog(LOG_ERR, "pfi_refresh(): reallocf() numifs=%d: %s", numifs, strerror(errno)); goto err2; } io.pfiio_size = numifs; io.pfiio_buffer = p; if (ioctl(dev, DIOCIGETIFACES, &io)) { syslog(LOG_ERR, "pfi_refresh(): ioctl(): %s", strerror(errno)); goto err2; } if (numifs >= io.pfiio_size) break; numifs = io.pfiio_size; } for (i = 0; i < numifs; i++) { e = malloc(sizeof(struct pfi_entry)); if (e == NULL) goto err1; e->index = i + 1; memcpy(&e->pfi, p+i, sizeof(struct pfi_kif)); TAILQ_INSERT_TAIL(&pfi_table, e, link); } pfi_table_age = time(NULL); pfi_table_count = numifs; pf_tick = this_tick; free(p); return (0); err1: while (!TAILQ_EMPTY(&pfi_table)) { e = TAILQ_FIRST(&pfi_table); TAILQ_REMOVE(&pfi_table, e, link); free(e); } err2: free(p); return(-1); } static int pfq_refresh(void) { struct pfioc_altq pa; struct pfq_entry *e; int i, numqs, ticket; if (started && this_tick <= pf_tick) return (0); while (!TAILQ_EMPTY(&pfq_table)) { e = TAILQ_FIRST(&pfq_table); TAILQ_REMOVE(&pfq_table, e, link); free(e); } bzero(&pa, sizeof(pa)); if (ioctl(dev, DIOCGETALTQS, &pa)) { syslog(LOG_ERR, "pfq_refresh: ioctl(DIOCGETALTQS): %s", strerror(errno)); return (-1); } numqs = pa.nr; ticket = pa.ticket; for (i = 0; i < numqs; i++) { e = malloc(sizeof(struct pfq_entry)); if (e == NULL) { syslog(LOG_ERR, "pfq_refresh(): " "malloc(): %s", strerror(errno)); goto err; } pa.ticket = ticket; pa.nr = i; if (ioctl(dev, DIOCGETALTQ, &pa)) { syslog(LOG_ERR, "pfq_refresh(): " "ioctl(DIOCGETALTQ): %s", strerror(errno)); goto err; } if (pa.altq.qid > 0) { memcpy(&e->altq, &pa.altq, sizeof(struct pf_altq)); e->index = pa.altq.qid; pfq_table_count = i; INSERT_OBJECT_INT_LINK_INDEX(e, &pfq_table, link, index); } } pfq_table_age = time(NULL); pf_tick = this_tick; return (0); err: free(e); while (!TAILQ_EMPTY(&pfq_table)) { e = TAILQ_FIRST(&pfq_table); TAILQ_REMOVE(&pfq_table, e, link); free(e); } return(-1); } static int pfs_refresh(void) { if (started && this_tick <= pf_tick) return (0); bzero(&pfs, sizeof(struct pf_status)); if (ioctl(dev, DIOCGETSTATUS, &pfs)) { syslog(LOG_ERR, "pfs_refresh(): ioctl(): %s", strerror(errno)); return (-1); } pf_tick = this_tick; return (0); } static int pft_refresh(void) { struct pfioc_table io; struct pfr_tstats *t = NULL; struct pft_entry *e; int i, numtbls = 1; if (started && this_tick <= pf_tick) return (0); while (!TAILQ_EMPTY(&pft_table)) { e = TAILQ_FIRST(&pft_table); TAILQ_REMOVE(&pft_table, e, link); free(e); } bzero(&io, sizeof(io)); io.pfrio_esize = sizeof(struct pfr_tstats); for (;;) { t = reallocf(t, numtbls * sizeof(struct pfr_tstats)); if (t == NULL) { syslog(LOG_ERR, "pft_refresh(): reallocf() numtbls=%d: %s", numtbls, strerror(errno)); goto err2; } io.pfrio_size = numtbls; io.pfrio_buffer = t; if (ioctl(dev, DIOCRGETTSTATS, &io)) { syslog(LOG_ERR, "pft_refresh(): ioctl(): %s", strerror(errno)); goto err2; } if (numtbls >= io.pfrio_size) break; numtbls = io.pfrio_size; } for (i = 0; i < numtbls; i++) { e = malloc(sizeof(struct pft_entry)); if (e == NULL) goto err1; e->index = i + 1; memcpy(&e->pft, t+i, sizeof(struct pfr_tstats)); TAILQ_INSERT_TAIL(&pft_table, e, link); } pft_table_age = time(NULL); pft_table_count = numtbls; pf_tick = this_tick; free(t); return (0); err1: while (!TAILQ_EMPTY(&pft_table)) { e = TAILQ_FIRST(&pft_table); TAILQ_REMOVE(&pft_table, e, link); free(e); } err2: free(t); return(-1); } static int pfa_table_addrs(u_int sidx, struct pfr_table *pt) { struct pfioc_table io; struct pfr_astats *t = NULL; struct pfa_entry *e; int i, numaddrs = 1; if (pt == NULL) return (-1); memset(&io, 0, sizeof(io)); strlcpy(io.pfrio_table.pfrt_name, pt->pfrt_name, sizeof(io.pfrio_table.pfrt_name)); for (;;) { t = reallocf(t, numaddrs * sizeof(struct pfr_astats)); if (t == NULL) { syslog(LOG_ERR, "pfa_table_addrs(): reallocf(): %s", strerror(errno)); numaddrs = -1; goto error; } memset(t, 0, sizeof(*t)); io.pfrio_size = numaddrs; io.pfrio_buffer = t; io.pfrio_esize = sizeof(struct pfr_astats); if (ioctl(dev, DIOCRGETASTATS, &io)) { syslog(LOG_ERR, "pfa_table_addrs(): ioctl() on %s: %s", pt->pfrt_name, strerror(errno)); numaddrs = -1; break; } if (numaddrs >= io.pfrio_size) break; numaddrs = io.pfrio_size; } for (i = 0; i < numaddrs; i++) { if ((t + i)->pfras_a.pfra_af != AF_INET && (t + i)->pfras_a.pfra_af != AF_INET6) { numaddrs = i; break; } e = (struct pfa_entry *)malloc(sizeof(struct pfa_entry)); if (e == NULL) { syslog(LOG_ERR, "pfa_table_addrs(): malloc(): %s", strerror(errno)); numaddrs = -1; break; } e->index = sidx + i; memcpy(&e->pfas, t + i, sizeof(struct pfr_astats)); TAILQ_INSERT_TAIL(&pfa_table, e, link); } free(t); error: return (numaddrs); } static int pfa_refresh(void) { struct pfioc_table io; struct pfr_table *pt = NULL, *it = NULL; struct pfa_entry *e; int i, numtbls = 1, cidx, naddrs; if (started && this_tick <= pf_tick) return (0); while (!TAILQ_EMPTY(&pfa_table)) { e = TAILQ_FIRST(&pfa_table); TAILQ_REMOVE(&pfa_table, e, link); free(e); } memset(&io, 0, sizeof(io)); io.pfrio_esize = sizeof(struct pfr_table); for (;;) { pt = reallocf(pt, numtbls * sizeof(struct pfr_table)); if (pt == NULL) { syslog(LOG_ERR, "pfa_refresh(): reallocf() %s", strerror(errno)); return (-1); } memset(pt, 0, sizeof(*pt)); io.pfrio_size = numtbls; io.pfrio_buffer = pt; if (ioctl(dev, DIOCRGETTABLES, &io)) { syslog(LOG_ERR, "pfa_refresh(): ioctl(): %s", strerror(errno)); goto err2; } if (numtbls >= io.pfrio_size) break; numtbls = io.pfrio_size; } cidx = 1; for (it = pt, i = 0; i < numtbls; it++, i++) { /* * Skip the table if not active - ioctl(DIOCRGETASTATS) will * return ESRCH for this entry anyway. */ if (!(it->pfrt_flags & PFR_TFLAG_ACTIVE)) continue; if ((naddrs = pfa_table_addrs(cidx, it)) < 0) goto err1; cidx += naddrs; } pfa_table_age = time(NULL); pfa_table_count = cidx; pf_tick = this_tick; free(pt); return (0); err1: while (!TAILQ_EMPTY(&pfa_table)) { e = TAILQ_FIRST(&pfa_table); TAILQ_REMOVE(&pfa_table, e, link); free(e); } err2: free(pt); return (-1); } static int pfl_scan_ruleset(const char *path) { struct pfioc_rule pr; struct pfl_entry *e; u_int32_t nr, i; bzero(&pr, sizeof(pr)); strlcpy(pr.anchor, path, sizeof(pr.anchor)); pr.rule.action = PF_PASS; if (ioctl(dev, DIOCGETRULES, &pr)) { syslog(LOG_ERR, "pfl_scan_ruleset: ioctl(DIOCGETRULES): %s", strerror(errno)); goto err; } for (nr = pr.nr, i = 0; i < nr; i++) { pr.nr = i; if (ioctl(dev, DIOCGETRULE, &pr)) { syslog(LOG_ERR, "pfl_scan_ruleset: ioctl(DIOCGETRULE):" " %s", strerror(errno)); goto err; } if (pr.rule.label[0]) { e = (struct pfl_entry *)malloc(sizeof(*e)); if (e == NULL) goto err; strlcpy(e->name, path, sizeof(e->name)); if (path[0]) strlcat(e->name, "/", sizeof(e->name)); strlcat(e->name, pr.rule.label, sizeof(e->name)); e->evals = pr.rule.evaluations; e->bytes[IN] = pr.rule.bytes[IN]; e->bytes[OUT] = pr.rule.bytes[OUT]; e->pkts[IN] = pr.rule.packets[IN]; e->pkts[OUT] = pr.rule.packets[OUT]; e->index = ++pfl_table_count; TAILQ_INSERT_TAIL(&pfl_table, e, link); } } return (0); err: return (-1); } static int pfl_walk_rulesets(const char *path) { struct pfioc_ruleset prs; char newpath[MAXPATHLEN]; u_int32_t nr, i; if (pfl_scan_ruleset(path)) goto err; bzero(&prs, sizeof(prs)); strlcpy(prs.path, path, sizeof(prs.path)); if (ioctl(dev, DIOCGETRULESETS, &prs)) { syslog(LOG_ERR, "pfl_walk_rulesets: ioctl(DIOCGETRULESETS): %s", strerror(errno)); goto err; } for (nr = prs.nr, i = 0; i < nr; i++) { prs.nr = i; if (ioctl(dev, DIOCGETRULESET, &prs)) { syslog(LOG_ERR, "pfl_walk_rulesets: ioctl(DIOCGETRULESET):" " %s", strerror(errno)); goto err; } if (strcmp(prs.name, PF_RESERVED_ANCHOR) == 0) continue; strlcpy(newpath, path, sizeof(newpath)); if (path[0]) strlcat(newpath, "/", sizeof(newpath)); strlcat(newpath, prs.name, sizeof(newpath)); if (pfl_walk_rulesets(newpath)) goto err; } return (0); err: return (-1); } static int pfl_refresh(void) { struct pfl_entry *e; if (started && this_tick <= pf_tick) return (0); while (!TAILQ_EMPTY(&pfl_table)) { e = TAILQ_FIRST(&pfl_table); TAILQ_REMOVE(&pfl_table, e, link); free(e); } pfl_table_count = 0; if (pfl_walk_rulesets("")) goto err; pfl_table_age = time(NULL); pf_tick = this_tick; return (0); err: while (!TAILQ_EMPTY(&pfl_table)) { e = TAILQ_FIRST(&pfl_table); TAILQ_REMOVE(&pfl_table, e, link); free(e); } pfl_table_count = 0; return (-1); } /* * check whether altq support is enabled in kernel */ static int altq_is_enabled(int pfdev) { struct pfioc_altq pa; errno = 0; if (ioctl(pfdev, DIOCGETALTQS, &pa)) { if (errno == ENODEV) { syslog(LOG_INFO, "No ALTQ support in kernel\n" "ALTQ related functions disabled\n"); return (0); } else syslog(LOG_ERR, "DIOCGETALTQS returned an error: %s", strerror(errno)); return (-1); } return (1); } /* * Implement the bsnmpd module interface */ static int pf_init(struct lmodule *mod, int __unused argc, char __unused *argv[]) { module = mod; if ((dev = open("/dev/pf", O_RDONLY)) == -1) { syslog(LOG_ERR, "pf_init(): open(): %s\n", strerror(errno)); return (-1); } if ((altq_enabled = altq_is_enabled(dev)) == -1) { syslog(LOG_ERR, "pf_init(): altq test failed"); return (-1); } /* Prepare internal state */ TAILQ_INIT(&pfi_table); TAILQ_INIT(&pfq_table); TAILQ_INIT(&pft_table); TAILQ_INIT(&pfa_table); TAILQ_INIT(&pfl_table); pfi_refresh(); if (altq_enabled) { pfq_refresh(); } pfs_refresh(); pft_refresh(); pfa_refresh(); pfl_refresh(); started = 1; return (0); } static int pf_fini(void) { struct pfi_entry *i1, *i2; struct pfq_entry *q1, *q2; struct pft_entry *t1, *t2; struct pfa_entry *a1, *a2; struct pfl_entry *l1, *l2; /* Empty the list of interfaces */ i1 = TAILQ_FIRST(&pfi_table); while (i1 != NULL) { i2 = TAILQ_NEXT(i1, link); free(i1); i1 = i2; } /* List of queues */ q1 = TAILQ_FIRST(&pfq_table); while (q1 != NULL) { q2 = TAILQ_NEXT(q1, link); free(q1); q1 = q2; } /* List of tables */ t1 = TAILQ_FIRST(&pft_table); while (t1 != NULL) { t2 = TAILQ_NEXT(t1, link); free(t1); t1 = t2; } /* List of table addresses */ a1 = TAILQ_FIRST(&pfa_table); while (a1 != NULL) { a2 = TAILQ_NEXT(a1, link); free(a1); a1 = a2; } /* And the list of labeled filter rules */ l1 = TAILQ_FIRST(&pfl_table); while (l1 != NULL) { l2 = TAILQ_NEXT(l1, link); free(l1); l1 = l2; } close(dev); return (0); } static void pf_dump(void) { pfi_refresh(); if (altq_enabled) { pfq_refresh(); } pft_refresh(); pfa_refresh(); pfl_refresh(); syslog(LOG_ERR, "Dump: pfi_table_age = %jd", (intmax_t)pfi_table_age); syslog(LOG_ERR, "Dump: pfi_table_count = %d", pfi_table_count); syslog(LOG_ERR, "Dump: pfq_table_age = %jd", (intmax_t)pfq_table_age); syslog(LOG_ERR, "Dump: pfq_table_count = %d", pfq_table_count); syslog(LOG_ERR, "Dump: pft_table_age = %jd", (intmax_t)pft_table_age); syslog(LOG_ERR, "Dump: pft_table_count = %d", pft_table_count); syslog(LOG_ERR, "Dump: pfa_table_age = %jd", (intmax_t)pfa_table_age); syslog(LOG_ERR, "Dump: pfa_table_count = %d", pfa_table_count); syslog(LOG_ERR, "Dump: pfl_table_age = %jd", (intmax_t)pfl_table_age); syslog(LOG_ERR, "Dump: pfl_table_count = %d", pfl_table_count); } const struct snmp_module config = { .comment = "This module implements a MIB for the pf packet filter.", .init = pf_init, .fini = pf_fini, .tree = pf_ctree, .dump = pf_dump, .tree_size = pf_CTREE_SIZE, }; Index: head/usr.sbin/bsnmpd/modules/snmp_target/Makefile =================================================================== --- head/usr.sbin/bsnmpd/modules/snmp_target/Makefile (revision 335884) +++ head/usr.sbin/bsnmpd/modules/snmp_target/Makefile (revision 335885) @@ -1,20 +1,22 @@ # $FreeBSD$ # # Author: Shteryana Shopova CONTRIB= ${SRCTOP}/contrib/bsnmp .PATH: ${CONTRIB}/snmp_target MOD= target SRCS= target_snmp.c XSYM= snmpTargetMIB snmpNotificationMIB snmpUDPDomain MAN= snmp_target.3 -CFLAGS+= -I${CONTRIB}/lib -I${CONTRIB}/snmpd -DSNMPTREE_TYPES +CFLAGS+= -I${CONTRIB}/lib -I${CONTRIB}/snmpd CFLAGS+= -DHAVE_ERR_H -DHAVE_GETADDRINFO -DHAVE_STRLCPY -DHAVE_SYS_TREE_H + +GENSNMPTREEFLAGS+= -I${CONTRIB}/lib DEFS= ${MOD}_tree.def BMIBS= .include Index: head/usr.sbin/bsnmpd/modules/snmp_usm/Makefile =================================================================== --- head/usr.sbin/bsnmpd/modules/snmp_usm/Makefile (revision 335884) +++ head/usr.sbin/bsnmpd/modules/snmp_usm/Makefile (revision 335885) @@ -1,22 +1,24 @@ # $FreeBSD$ # # Author: Shteryana Shopova CONTRIB= ${SRCTOP}/contrib/bsnmp .PATH: ${CONTRIB}/snmp_usm MOD= usm SRCS= usm_snmp.c XSYM= snmpUsmMIB usmNoAuthProtocol usmHMACMD5AuthProtocol \ usmHMACSHAAuthProtocol usmNoPrivProtocol usmDESPrivProtocol \ usmAesCfb128Protocol usmUserSecurityName MAN= snmp_usm.3 -CFLAGS+= -I${CONTRIB}/lib -I${CONTRIB}/snmpd -DSNMPTREE_TYPES +CFLAGS+= -I${CONTRIB}/lib -I${CONTRIB}/snmpd CFLAGS+= -DHAVE_ERR_H -DHAVE_GETADDRINFO -DHAVE_STRLCPY -DHAVE_SYS_TREE_H + +GENSNMPTREEFLAGS+= -I${CONTRIB}/lib DEFS= ${MOD}_tree.def BMIBS= .include Index: head/usr.sbin/bsnmpd/modules/snmp_vacm/Makefile =================================================================== --- head/usr.sbin/bsnmpd/modules/snmp_vacm/Makefile (revision 335884) +++ head/usr.sbin/bsnmpd/modules/snmp_vacm/Makefile (revision 335885) @@ -1,20 +1,22 @@ # $FreeBSD$ # # Author: Shteryana Shopova CONTRIB= ${SRCTOP}/contrib/bsnmp .PATH: ${CONTRIB}/snmp_vacm MOD= vacm SRCS= vacm_snmp.c XSYM= snmpVacmMIB MAN= snmp_vacm.3 -CFLAGS+= -I${CONTRIB}/lib -I${CONTRIB}/snmpd -DSNMPTREE_TYPES +CFLAGS+= -I${CONTRIB}/lib -I${CONTRIB}/snmpd CFLAGS+= -DHAVE_ERR_H -DHAVE_GETADDRINFO -DHAVE_STRLCPY -DHAVE_SYS_TREE_H + +GENSNMPTREEFLAGS+= -I${CONTRIB}/lib DEFS= ${MOD}_tree.def BMIBS= .include Index: head/usr.sbin/bsnmpd/modules/snmp_wlan/Makefile =================================================================== --- head/usr.sbin/bsnmpd/modules/snmp_wlan/Makefile (revision 335884) +++ head/usr.sbin/bsnmpd/modules/snmp_wlan/Makefile (revision 335885) @@ -1,15 +1,14 @@ # # $FreeBSD$ # MOD= wlan SRCS= wlan_snmp.c wlan_sys.c -CFLAGS+= -DSNMPTREE_TYPES XSYM= begemotWlan BMIBS= BEGEMOT-WIRELESS-MIB.txt MAN= snmp_${MOD}.3 DEFS= ${MOD}_tree.def .include Index: head/usr.sbin/bsnmpd/modules/snmp_wlan/wlan_snmp.c =================================================================== --- head/usr.sbin/bsnmpd/modules/snmp_wlan/wlan_snmp.c (revision 335884) +++ head/usr.sbin/bsnmpd/modules/snmp_wlan/wlan_snmp.c (revision 335885) @@ -1,4515 +1,4516 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2010 The FreeBSD Foundation * All rights reserved. * * This software was developed by Shteryana Sotirova Shopova under * sponsorship from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#define SNMPTREE_TYPES #include "wlan_tree.h" #include "wlan_snmp.h" #include "wlan_oid.h" static struct lmodule *wlan_module; /* For the registration. */ static const struct asn_oid oid_wlan = OIDX_begemotWlan; /* The registration. */ static uint reg_wlan; /* Periodic timer for polling the module's data. */ static void *wlan_data_timer; /* * Poll data from kernel every 15 minutes unless explicitly requested by an * SNMP client. * XXX: make that configurable. */ static int wlan_poll_ticks = (15 * 60) * 100; /* The age of each table. */ #define WLAN_LIST_MAXAGE 5 static time_t wlan_iflist_age; static time_t wlan_peerlist_age; static time_t wlan_chanlist_age; static time_t wlan_roamlist_age; static time_t wlan_tx_paramlist_age; static time_t wlan_scanlist_age; static time_t wlan_maclist_age; static time_t wlan_mrlist_age; /* * The list of all virtual wireless interfaces - sorted by name. */ SLIST_HEAD(wlan_ifaces, wlan_iface); static struct wlan_ifaces wlan_ifaces = SLIST_HEAD_INITIALIZER(wlan_ifaces); static struct wlan_config wlan_config; /* Forward declarations */ static int bits_get(struct snmp_value *, const u_char *, ssize_t); static int wlan_add_wif(struct wlan_iface *); static void wlan_delete_wif(struct wlan_iface *); static int wlan_attach_newif(struct mibif *); static int wlan_iface_create(struct wlan_iface *); static int wlan_iface_destroy(struct wlan_iface *); static struct wlan_iface * wlan_new_wif(char *); static void wlan_free_interface(struct wlan_iface *); static void wlan_free_iflist(void); static void wlan_free_peerlist(struct wlan_iface *); static void wlan_scan_free_results(struct wlan_iface *); static void wlan_mac_free_maclist(struct wlan_iface *); static void wlan_mesh_free_routes(struct wlan_iface *); static int wlan_update_interface(struct wlan_iface *); static void wlan_update_interface_list(void); static void wlan_update_peers(void); static void wlan_update_channels(void); static void wlan_update_roam_params(void); static void wlan_update_tx_params(void); static void wlan_scan_update_results(void); static void wlan_mac_update_aclmacs(void); static void wlan_mesh_update_routes(void); static struct wlan_iface * wlan_find_interface(const char *); static struct wlan_peer * wlan_find_peer(struct wlan_iface *, uint8_t *); static struct ieee80211_channel* wlan_find_channel(struct wlan_iface *, uint32_t); static struct wlan_scan_result * wlan_scan_find_result(struct wlan_iface *, uint8_t *, uint8_t *); static struct wlan_mac_mac * wlan_mac_find_mac(struct wlan_iface *, uint8_t *); static struct wlan_mesh_route * wlan_mesh_find_route(struct wlan_iface *, uint8_t *); static struct wlan_iface * wlan_first_interface(void); static struct wlan_iface * wlan_next_interface(struct wlan_iface *); static struct wlan_iface * wlan_mesh_first_interface(void); static struct wlan_iface * wlan_mesh_next_interface(struct wlan_iface *); static struct wlan_iface * wlan_get_interface(const struct asn_oid *, uint); static struct wlan_iface * wlan_get_snmp_interface(const struct asn_oid *, uint); static struct wlan_peer * wlan_get_peer(const struct asn_oid *, uint, struct wlan_iface **); static struct ieee80211_channel *wlan_get_channel(const struct asn_oid *, uint, struct wlan_iface **); static struct ieee80211_roamparam *wlan_get_roam_param(const struct asn_oid *, uint, struct wlan_iface **); static struct ieee80211_txparam *wlan_get_tx_param(const struct asn_oid *, uint, struct wlan_iface **, uint32_t *); static struct wlan_scan_result *wlan_get_scanr(const struct asn_oid *, uint, struct wlan_iface **); static struct wlan_mac_mac * wlan_get_acl_mac(const struct asn_oid *, uint, struct wlan_iface **); static struct wlan_iface * wlan_mesh_get_iface(const struct asn_oid *, uint); static struct wlan_peer * wlan_mesh_get_peer(const struct asn_oid *, uint, struct wlan_iface **); static struct wlan_mesh_route * wlan_mesh_get_route(const struct asn_oid *, uint, struct wlan_iface **); static struct wlan_iface * wlan_get_next_interface(const struct asn_oid *, uint); static struct wlan_iface * wlan_get_next_snmp_interface(const struct asn_oid *, uint); static struct wlan_peer * wlan_get_next_peer(const struct asn_oid *, uint, struct wlan_iface **); static struct ieee80211_channel *wlan_get_next_channel(const struct asn_oid *, uint, struct wlan_iface **); static struct ieee80211_roamparam *wlan_get_next_roam_param(const struct asn_oid *, uint sub, struct wlan_iface **, uint32_t *); static struct ieee80211_txparam *wlan_get_next_tx_param(const struct asn_oid *, uint, struct wlan_iface **, uint32_t *); static struct wlan_scan_result *wlan_get_next_scanr(const struct asn_oid *, uint , struct wlan_iface **); static struct wlan_mac_mac * wlan_get_next_acl_mac(const struct asn_oid *, uint, struct wlan_iface **); static struct wlan_iface * wlan_mesh_get_next_iface(const struct asn_oid *, uint); static struct wlan_peer * wlan_mesh_get_next_peer(const struct asn_oid *, uint, struct wlan_iface **); static struct wlan_mesh_route * wlan_mesh_get_next_route(const struct asn_oid *, uint sub, struct wlan_iface **); static uint8_t *wlan_get_ifname(const struct asn_oid *, uint, uint8_t *); static int wlan_mac_index_decode(const struct asn_oid *, uint, char *, uint8_t *); static int wlan_channel_index_decode(const struct asn_oid *, uint, char *, uint32_t *); static int wlan_phy_index_decode(const struct asn_oid *, uint, char *, uint32_t *); static int wlan_scanr_index_decode(const struct asn_oid *oid, uint sub, char *wname, uint8_t *ssid, uint8_t *bssid); static void wlan_append_ifindex(struct asn_oid *, uint, const struct wlan_iface *); static void wlan_append_mac_index(struct asn_oid *, uint, char *, uint8_t *); static void wlan_append_channel_index(struct asn_oid *, uint, const struct wlan_iface *, const struct ieee80211_channel *); static void wlan_append_phy_index(struct asn_oid *, uint, char *, uint32_t); static void wlan_append_scanr_index(struct asn_oid *, uint, char *, uint8_t *, uint8_t *); static int wlan_acl_mac_set_status(struct snmp_context *, struct snmp_value *, uint); static int wlan_mesh_route_set_status(struct snmp_context *, struct snmp_value *, uint); static int32_t wlan_get_channel_type(struct ieee80211_channel *); static int wlan_scan_compare_result(struct wlan_scan_result *, struct wlan_scan_result *); static int wlan_mac_delete_mac(struct wlan_iface *, struct wlan_mac_mac *); static int wlan_mesh_delete_route(struct wlan_iface *, struct wlan_mesh_route *); /* * The module's GET/SET data hooks per each table or group of objects as * required by bsnmpd(1). */ int op_wlan_iface(struct snmp_context *ctx, struct snmp_value *val, uint32_t sub, uint32_t iidx __unused, enum snmp_op op) { int rc; char wname[IFNAMSIZ]; struct wlan_iface *wif; wlan_update_interface_list(); switch (op) { case SNMP_OP_GET: if ((wif = wlan_get_snmp_interface(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_GETNEXT: if ((wif = wlan_get_next_snmp_interface(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); wlan_append_ifindex(&val->var, sub, wif); break; case SNMP_OP_SET: if ((wif = wlan_get_snmp_interface(&val->var, sub)) == NULL) { if (val->var.subs[sub - 1] != LEAF_wlanIfaceName) return (SNMP_ERR_NOSUCHNAME); if (wlan_get_ifname(&val->var, sub, wname) == NULL) return (SNMP_ERR_INCONS_VALUE); if ((wif = wlan_new_wif(wname)) == NULL) return (SNMP_ERR_GENERR); wif->internal = 1; } if (wif->status == RowStatus_active && val->var.subs[sub - 1] != LEAF_wlanIfaceStatus && val->var.subs[sub - 1] != LEAF_wlanIfaceState) return (SNMP_ERR_INCONS_VALUE); switch (val->var.subs[sub - 1]) { case LEAF_wlanIfaceIndex: return (SNMP_ERR_NOT_WRITEABLE); case LEAF_wlanIfaceName: if (val->v.octetstring.len >= IFNAMSIZ) return (SNMP_ERR_INCONS_VALUE); if ((ctx->scratch->ptr1 = malloc(IFNAMSIZ)) == NULL) return (SNMP_ERR_GENERR); strlcpy(ctx->scratch->ptr1, wif->wname, IFNAMSIZ); memcpy(wif->wname, val->v.octetstring.octets, val->v.octetstring.len); wif->wname[val->v.octetstring.len] = '\0'; return (SNMP_ERR_NOERROR); case LEAF_wlanParentIfName: if (val->v.octetstring.len >= IFNAMSIZ) return (SNMP_ERR_INCONS_VALUE); if ((ctx->scratch->ptr1 = malloc(IFNAMSIZ)) == NULL) return (SNMP_ERR_GENERR); strlcpy(ctx->scratch->ptr1, wif->pname, IFNAMSIZ); memcpy(wif->pname, val->v.octetstring.octets, val->v.octetstring.len); wif->pname[val->v.octetstring.len] = '\0'; return (SNMP_ERR_NOERROR); case LEAF_wlanIfaceOperatingMode: ctx->scratch->int1 = wif->mode; wif->mode = val->v.integer; return (SNMP_ERR_NOERROR); case LEAF_wlanIfaceFlags: if (val->v.octetstring.len > sizeof(wif->flags)) return (SNMP_ERR_INCONS_VALUE); ctx->scratch->ptr1 = malloc(sizeof(wif->flags)); if (ctx->scratch->ptr1 == NULL) return (SNMP_ERR_GENERR); memcpy(ctx->scratch->ptr1, (uint8_t *)&wif->flags, sizeof(wif->flags)); memcpy((uint8_t *)&wif->flags, val->v.octetstring.octets, sizeof(wif->flags)); return (SNMP_ERR_NOERROR); case LEAF_wlanIfaceBssid: if (val->v.octetstring.len != IEEE80211_ADDR_LEN) return (SNMP_ERR_INCONS_VALUE); ctx->scratch->ptr1 = malloc(IEEE80211_ADDR_LEN); if (ctx->scratch->ptr1 == NULL) return (SNMP_ERR_GENERR); memcpy(ctx->scratch->ptr1, wif->dbssid, IEEE80211_ADDR_LEN); memcpy(wif->dbssid, val->v.octetstring.octets, IEEE80211_ADDR_LEN); return (SNMP_ERR_NOERROR); case LEAF_wlanIfaceLocalAddress: if (val->v.octetstring.len != IEEE80211_ADDR_LEN) return (SNMP_ERR_INCONS_VALUE); ctx->scratch->ptr1 = malloc(IEEE80211_ADDR_LEN); if (ctx->scratch->ptr1 == NULL) return (SNMP_ERR_GENERR); memcpy(ctx->scratch->ptr1, wif->dlmac, IEEE80211_ADDR_LEN); memcpy(wif->dlmac, val->v.octetstring.octets, IEEE80211_ADDR_LEN); return (SNMP_ERR_NOERROR); case LEAF_wlanIfaceStatus: ctx->scratch->int1 = wif->status; wif->status = val->v.integer; if (wif->status == RowStatus_active) { rc = wlan_iface_create(wif); /* XXX */ if (rc != SNMP_ERR_NOERROR) { wif->status = ctx->scratch->int1; return (rc); } } else if (wif->status == RowStatus_destroy) return (wlan_iface_destroy(wif)); else wif->status = RowStatus_notReady; return (SNMP_ERR_NOERROR); case LEAF_wlanIfaceState: ctx->scratch->int1 = wif->state; wif->state = val->v.integer; if (wif->status == RowStatus_active) if (wlan_config_state(wif, 1) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); } abort(); case SNMP_OP_ROLLBACK: if ((wif = wlan_get_snmp_interface(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); switch (val->var.subs[sub - 1]) { case LEAF_wlanIfaceName: strlcpy(wif->wname, ctx->scratch->ptr1, IFNAMSIZ); free(ctx->scratch->ptr1); break; case LEAF_wlanParentIfName: strlcpy(wif->pname, ctx->scratch->ptr1, IFNAMSIZ); free(ctx->scratch->ptr1); break; case LEAF_wlanIfaceOperatingMode: wif->mode = ctx->scratch->int1; break; case LEAF_wlanIfaceFlags: memcpy((uint8_t *)&wif->flags, ctx->scratch->ptr1, sizeof(wif->flags)); free(ctx->scratch->ptr1); break; case LEAF_wlanIfaceBssid: memcpy(wif->dbssid, ctx->scratch->ptr1, IEEE80211_ADDR_LEN); free(ctx->scratch->ptr1); break; case LEAF_wlanIfaceLocalAddress: memcpy(wif->dlmac, ctx->scratch->ptr1, IEEE80211_ADDR_LEN); free(ctx->scratch->ptr1); break; case LEAF_wlanIfaceStatus: wif->status = ctx->scratch->int1; if (ctx->scratch->int1 == RowStatus_active) return (SNMP_ERR_GENERR); /* XXX: FIXME */ else if (wif->internal != 0) return (wlan_iface_destroy(wif)); break; case LEAF_wlanIfaceState: wif->state = ctx->scratch->int1; if (wif->status == RowStatus_active) if (wlan_config_state(wif, 1) < 0) return (SNMP_ERR_GENERR); break; } return (SNMP_ERR_NOERROR); case SNMP_OP_COMMIT: switch (val->var.subs[sub - 1]) { case LEAF_wlanIfaceName: case LEAF_wlanParentIfName: case LEAF_wlanIfaceFlags: case LEAF_wlanIfaceBssid: case LEAF_wlanIfaceLocalAddress: free(ctx->scratch->ptr1); /* FALLTHROUGH */ default: return (SNMP_ERR_NOERROR); } default: abort(); } switch (val->var.subs[sub - 1]) { case LEAF_wlanIfaceIndex: val->v.integer = wif->index; return (SNMP_ERR_NOERROR); case LEAF_wlanIfaceName: return (string_get(val, wif->wname, -1)); case LEAF_wlanParentIfName: return (string_get(val, wif->pname, -1)); case LEAF_wlanIfaceOperatingMode: val->v.integer = wif->mode; return (SNMP_ERR_NOERROR); case LEAF_wlanIfaceFlags: return (bits_get(val, (uint8_t *)&wif->flags, sizeof(wif->flags))); case LEAF_wlanIfaceBssid: return (string_get(val, wif->dbssid, IEEE80211_ADDR_LEN)); case LEAF_wlanIfaceLocalAddress: return (string_get(val, wif->dlmac, IEEE80211_ADDR_LEN)); case LEAF_wlanIfaceStatus: val->v.integer = wif->status; return (SNMP_ERR_NOERROR); case LEAF_wlanIfaceState: val->v.integer = wif->state; return (SNMP_ERR_NOERROR); } abort(); } int op_wlan_if_parent(struct snmp_context *ctx __unused, struct snmp_value *val, uint32_t sub, uint32_t iidx __unused, enum snmp_op op) { struct wlan_iface *wif; wlan_update_interface_list(); switch (op) { case SNMP_OP_GET: if ((wif = wlan_get_interface(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_GETNEXT: if ((wif = wlan_get_next_interface(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); wlan_append_ifindex(&val->var, sub, wif); break; case SNMP_OP_SET: return (SNMP_ERR_NOT_WRITEABLE); case SNMP_OP_COMMIT: /* FALLTHROUGH */ case SNMP_OP_ROLLBACK: /* FALLTHROUGH */ default: abort(); } switch (val->var.subs[sub - 1]) { case LEAF_wlanIfParentDriverCapabilities: return (bits_get(val, (uint8_t *)&wif->drivercaps, sizeof(wif->drivercaps))); case LEAF_wlanIfParentCryptoCapabilities: return (bits_get(val, (uint8_t *)&wif->cryptocaps, sizeof(wif->cryptocaps))); case LEAF_wlanIfParentHTCapabilities: return (bits_get(val, (uint8_t *)&wif->htcaps, sizeof(wif->htcaps))); } abort(); } int op_wlan_iface_config(struct snmp_context *ctx, struct snmp_value *val, uint32_t sub, uint32_t iidx __unused, enum snmp_op op) { int intval, vlen, rc; char *strval; struct wlan_iface *wif; wlan_update_interface_list(); switch (op) { case SNMP_OP_GET: if ((wif = wlan_get_interface(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); goto get_config; case SNMP_OP_GETNEXT: if ((wif = wlan_get_next_interface(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); wlan_append_ifindex(&val->var, sub, wif); goto get_config; case SNMP_OP_SET: if ((wif = wlan_get_interface(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); intval = val->v.integer; strval = NULL; vlen = 0; /* Simple sanity checks & save old data. */ switch (val->var.subs[sub - 1]) { case LEAF_wlanIfaceCountryCode: if (val->v.octetstring.len != WLAN_COUNTRY_CODE_SIZE) return (SNMP_ERR_INCONS_VALUE); break; case LEAF_wlanIfaceDesiredSsid: if (val->v.octetstring.len > IEEE80211_NWID_LEN) return (SNMP_ERR_INCONS_VALUE); break; case LEAF_wlanIfaceDesiredBssid: if (val->v.octetstring.len != IEEE80211_ADDR_LEN) return (SNMP_ERR_INCONS_VALUE); break; case LEAF_wlanIfacePacketBurst: ctx->scratch->int1 = wif->packet_burst; break; case LEAF_wlanIfaceRegDomain: ctx->scratch->int1 = wif->reg_domain; break; case LEAF_wlanIfaceDesiredChannel: ctx->scratch->int1 = wif->desired_channel; break; case LEAF_wlanIfaceDynamicFreqSelection: ctx->scratch->int1 = wif->dyn_frequency; break; case LEAF_wlanIfaceFastFrames: ctx->scratch->int1 = wif->fast_frames; break; case LEAF_wlanIfaceDturbo: ctx->scratch->int1 = wif->dturbo; break; case LEAF_wlanIfaceTxPower: ctx->scratch->int1 = wif->tx_power; break; case LEAF_wlanIfaceFragmentThreshold: ctx->scratch->int1 = wif->frag_threshold; break; case LEAF_wlanIfaceRTSThreshold: ctx->scratch->int1 = wif->rts_threshold; break; case LEAF_wlanIfaceWlanPrivacySubscribe: ctx->scratch->int1 = wif->priv_subscribe; break; case LEAF_wlanIfaceBgScan: ctx->scratch->int1 = wif->bg_scan; break; case LEAF_wlanIfaceBgScanIdle: ctx->scratch->int1 = wif->bg_scan_idle; break; case LEAF_wlanIfaceBgScanInterval: ctx->scratch->int1 = wif->bg_scan_interval; break; case LEAF_wlanIfaceBeaconMissedThreshold: ctx->scratch->int1 = wif->beacons_missed; break; case LEAF_wlanIfaceRoamingMode: ctx->scratch->int1 = wif->roam_mode; break; case LEAF_wlanIfaceDot11d: ctx->scratch->int1 = wif->dot11d; break; case LEAF_wlanIfaceDot11h: ctx->scratch->int1 = wif->dot11h; break; case LEAF_wlanIfaceDynamicWds: ctx->scratch->int1 = wif->dynamic_wds; break; case LEAF_wlanIfacePowerSave: ctx->scratch->int1 = wif->power_save; break; case LEAF_wlanIfaceApBridge: ctx->scratch->int1 = wif->ap_bridge; break; case LEAF_wlanIfaceBeaconInterval: ctx->scratch->int1 = wif->beacon_interval; break; case LEAF_wlanIfaceDtimPeriod: ctx->scratch->int1 = wif->dtim_period; break; case LEAF_wlanIfaceHideSsid: ctx->scratch->int1 = wif->hide_ssid; break; case LEAF_wlanIfaceInactivityProccess: ctx->scratch->int1 = wif->inact_process; break; case LEAF_wlanIfaceDot11gProtMode: ctx->scratch->int1 = wif->do11g_protect; break; case LEAF_wlanIfaceDot11gPureMode: ctx->scratch->int1 = wif->dot11g_pure; break; case LEAF_wlanIfaceDot11nPureMode: ctx->scratch->int1 = wif->dot11n_pure; break; case LEAF_wlanIfaceDot11nAmpdu: ctx->scratch->int1 = wif->ampdu; break; case LEAF_wlanIfaceDot11nAmpduDensity: ctx->scratch->int1 = wif->ampdu_density; break; case LEAF_wlanIfaceDot11nAmpduLimit: ctx->scratch->int1 = wif->ampdu_limit; break; case LEAF_wlanIfaceDot11nAmsdu: ctx->scratch->int1 = wif->amsdu; break; case LEAF_wlanIfaceDot11nAmsduLimit: ctx->scratch->int1 = wif->amsdu_limit; break; case LEAF_wlanIfaceDot11nHighThroughput: ctx->scratch->int1 = wif->ht_enabled; break; case LEAF_wlanIfaceDot11nHTCompatible: ctx->scratch->int1 = wif->ht_compatible; break; case LEAF_wlanIfaceDot11nHTProtMode: ctx->scratch->int1 = wif->ht_prot_mode; break; case LEAF_wlanIfaceDot11nRIFS: ctx->scratch->int1 = wif->rifs; break; case LEAF_wlanIfaceDot11nShortGI: ctx->scratch->int1 = wif->short_gi; break; case LEAF_wlanIfaceDot11nSMPSMode: ctx->scratch->int1 = wif->smps_mode; break; case LEAF_wlanIfaceTdmaSlot: ctx->scratch->int1 = wif->tdma_slot; break; case LEAF_wlanIfaceTdmaSlotCount: ctx->scratch->int1 = wif->tdma_slot_count; break; case LEAF_wlanIfaceTdmaSlotLength: ctx->scratch->int1 = wif->tdma_slot_length; break; case LEAF_wlanIfaceTdmaBeaconInterval: ctx->scratch->int1 = wif->tdma_binterval; break; default: abort(); } if (val->syntax != SNMP_SYNTAX_OCTETSTRING) goto set_config; ctx->scratch->int1 = val->v.octetstring.len; ctx->scratch->ptr1 = malloc(val->v.octetstring.len + 1); if (ctx->scratch->ptr1 == NULL) return (SNMP_ERR_GENERR); /* XXX */ if (val->var.subs[sub - 1] == LEAF_wlanIfaceDesiredSsid) strlcpy(ctx->scratch->ptr1, val->v.octetstring.octets, val->v.octetstring.len + 1); else memcpy(ctx->scratch->ptr1, val->v.octetstring.octets, val->v.octetstring.len); strval = val->v.octetstring.octets; vlen = val->v.octetstring.len; goto set_config; case SNMP_OP_ROLLBACK: intval = ctx->scratch->int1; strval = NULL; vlen = 0; if ((wif = wlan_get_interface(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); switch (val->var.subs[sub - 1]) { case LEAF_wlanIfaceCountryCode: case LEAF_wlanIfaceDesiredSsid: case LEAF_wlanIfaceDesiredBssid: strval = ctx->scratch->ptr1; vlen = ctx->scratch->int1; break; default: break; } goto set_config; case SNMP_OP_COMMIT: switch (val->var.subs[sub - 1]) { case LEAF_wlanIfaceCountryCode: case LEAF_wlanIfaceDesiredSsid: case LEAF_wlanIfaceDesiredBssid: free(ctx->scratch->ptr1); /* FALLTHROUGH */ default: return (SNMP_ERR_NOERROR); } } abort(); get_config: if (wlan_config_get_ioctl(wif, val->var.subs[sub - 1]) < 0) return (SNMP_ERR_GENERR); switch (val->var.subs[sub - 1]) { case LEAF_wlanIfacePacketBurst: val->v.integer = wif->packet_burst; break; case LEAF_wlanIfaceCountryCode: return (string_get(val, wif->country_code, WLAN_COUNTRY_CODE_SIZE)); case LEAF_wlanIfaceRegDomain: val->v.integer = wif->reg_domain; break; case LEAF_wlanIfaceDesiredSsid: return (string_get(val, wif->desired_ssid, -1)); case LEAF_wlanIfaceDesiredChannel: val->v.integer = wif->desired_channel; break; case LEAF_wlanIfaceDynamicFreqSelection: val->v.integer = wif->dyn_frequency; break; case LEAF_wlanIfaceFastFrames: val->v.integer = wif->fast_frames; break; case LEAF_wlanIfaceDturbo: val->v.integer = wif->dturbo; break; case LEAF_wlanIfaceTxPower: val->v.integer = wif->tx_power; break; case LEAF_wlanIfaceFragmentThreshold: val->v.integer = wif->frag_threshold; break; case LEAF_wlanIfaceRTSThreshold: val->v.integer = wif->rts_threshold; break; case LEAF_wlanIfaceWlanPrivacySubscribe: val->v.integer = wif->priv_subscribe; break; case LEAF_wlanIfaceBgScan: val->v.integer = wif->bg_scan; break; case LEAF_wlanIfaceBgScanIdle: val->v.integer = wif->bg_scan_idle; break; case LEAF_wlanIfaceBgScanInterval: val->v.integer = wif->bg_scan_interval; break; case LEAF_wlanIfaceBeaconMissedThreshold: val->v.integer = wif->beacons_missed; break; case LEAF_wlanIfaceDesiredBssid: return (string_get(val, wif->desired_bssid, IEEE80211_ADDR_LEN)); case LEAF_wlanIfaceRoamingMode: val->v.integer = wif->roam_mode; break; case LEAF_wlanIfaceDot11d: val->v.integer = wif->dot11d; break; case LEAF_wlanIfaceDot11h: val->v.integer = wif->dot11h; break; case LEAF_wlanIfaceDynamicWds: val->v.integer = wif->dynamic_wds; break; case LEAF_wlanIfacePowerSave: val->v.integer = wif->power_save; break; case LEAF_wlanIfaceApBridge: val->v.integer = wif->ap_bridge; break; case LEAF_wlanIfaceBeaconInterval: val->v.integer = wif->beacon_interval; break; case LEAF_wlanIfaceDtimPeriod: val->v.integer = wif->dtim_period; break; case LEAF_wlanIfaceHideSsid: val->v.integer = wif->hide_ssid; break; case LEAF_wlanIfaceInactivityProccess: val->v.integer = wif->inact_process; break; case LEAF_wlanIfaceDot11gProtMode: val->v.integer = wif->do11g_protect; break; case LEAF_wlanIfaceDot11gPureMode: val->v.integer = wif->dot11g_pure; break; case LEAF_wlanIfaceDot11nPureMode: val->v.integer = wif->dot11n_pure; break; case LEAF_wlanIfaceDot11nAmpdu: val->v.integer = wif->ampdu; break; case LEAF_wlanIfaceDot11nAmpduDensity: val->v.integer = wif->ampdu_density; break; case LEAF_wlanIfaceDot11nAmpduLimit: val->v.integer = wif->ampdu_limit; break; case LEAF_wlanIfaceDot11nAmsdu: val->v.integer = wif->amsdu; break; case LEAF_wlanIfaceDot11nAmsduLimit: val->v.integer = wif->amsdu_limit; break; case LEAF_wlanIfaceDot11nHighThroughput: val->v.integer = wif->ht_enabled; break; case LEAF_wlanIfaceDot11nHTCompatible: val->v.integer = wif->ht_compatible; break; case LEAF_wlanIfaceDot11nHTProtMode: val->v.integer = wif->ht_prot_mode; break; case LEAF_wlanIfaceDot11nRIFS: val->v.integer = wif->rifs; break; case LEAF_wlanIfaceDot11nShortGI: val->v.integer = wif->short_gi; break; case LEAF_wlanIfaceDot11nSMPSMode: val->v.integer = wif->smps_mode; break; case LEAF_wlanIfaceTdmaSlot: val->v.integer = wif->tdma_slot; break; case LEAF_wlanIfaceTdmaSlotCount: val->v.integer = wif->tdma_slot_count; break; case LEAF_wlanIfaceTdmaSlotLength: val->v.integer = wif->tdma_slot_length; break; case LEAF_wlanIfaceTdmaBeaconInterval: val->v.integer = wif->tdma_binterval; break; } return (SNMP_ERR_NOERROR); set_config: rc = wlan_config_set_ioctl(wif, val->var.subs[sub - 1], intval, strval, vlen); if (op == SNMP_OP_ROLLBACK) { switch (val->var.subs[sub - 1]) { case LEAF_wlanIfaceCountryCode: case LEAF_wlanIfaceDesiredSsid: case LEAF_wlanIfaceDesiredBssid: free(ctx->scratch->ptr1); /* FALLTHROUGH */ default: break; } } if (rc < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); } int op_wlan_if_peer(struct snmp_context *ctx, struct snmp_value *val, uint32_t sub, uint32_t iidx __unused, enum snmp_op op) { struct wlan_peer *wip; struct wlan_iface *wif; wlan_update_interface_list(); wlan_update_peers(); switch (op) { case SNMP_OP_GET: if ((wip = wlan_get_peer(&val->var, sub, &wif)) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_GETNEXT: if ((wip = wlan_get_next_peer(&val->var, sub, &wif)) == NULL) return (SNMP_ERR_NOSUCHNAME); wlan_append_mac_index(&val->var, sub, wif->wname, wip->pmac); break; case SNMP_OP_SET: if ((wip = wlan_get_peer(&val->var, sub, &wif)) == NULL) return (SNMP_ERR_NOSUCHNAME); if (val->var.subs[sub - 1] != LEAF_wlanIfacePeerVlanTag) return (SNMP_ERR_GENERR); ctx->scratch->int1 = wip->vlan; if (wlan_peer_set_vlan(wif, wip, val->v.integer) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case SNMP_OP_COMMIT: return (SNMP_ERR_NOERROR); case SNMP_OP_ROLLBACK: if ((wip = wlan_get_peer(&val->var, sub, &wif)) == NULL) return (SNMP_ERR_NOSUCHNAME); if (val->var.subs[sub - 1] != LEAF_wlanIfacePeerVlanTag) return (SNMP_ERR_GENERR); if (wlan_peer_set_vlan(wif, wip, ctx->scratch->int1) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); default: abort(); } switch (val->var.subs[sub - 1]) { case LEAF_wlanIfacePeerAddress: return (string_get(val, wip->pmac, IEEE80211_ADDR_LEN)); case LEAF_wlanIfacePeerAssociationId: val->v.integer = wip->associd; break; case LEAF_wlanIfacePeerVlanTag: val->v.integer = wip->vlan; break; case LEAF_wlanIfacePeerFrequency: val->v.integer = wip->frequency; break; case LEAF_wlanIfacePeerCurrentTXRate: val->v.integer = wip->txrate; break; case LEAF_wlanIfacePeerRxSignalStrength: val->v.integer = wip->rssi; break; case LEAF_wlanIfacePeerIdleTimer: val->v.integer = wip->idle; break; case LEAF_wlanIfacePeerTxSequenceNo: val->v.integer = wip->txseqs; break; case LEAF_wlanIfacePeerRxSequenceNo: val->v.integer = wip->rxseqs; break; case LEAF_wlanIfacePeerTxPower: val->v.integer = wip->txpower; break; case LEAF_wlanIfacePeerCapabilities: return (bits_get(val, (uint8_t *)&wip->capinfo, sizeof(wip->capinfo))); case LEAF_wlanIfacePeerFlags: return (bits_get(val, (uint8_t *)&wip->state, sizeof(wip->state))); default: abort(); } return (SNMP_ERR_NOERROR); } int op_wlan_channels(struct snmp_context *ctx __unused, struct snmp_value *val, uint32_t sub, uint32_t iidx __unused, enum snmp_op op) { int32_t bits; struct ieee80211_channel *channel; struct wlan_iface *wif; wlan_update_interface_list(); wlan_update_channels(); switch (op) { case SNMP_OP_GET: if ((channel = wlan_get_channel(&val->var, sub, &wif)) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_GETNEXT: channel = wlan_get_next_channel(&val->var, sub, &wif); if (channel == NULL || wif == NULL) return (SNMP_ERR_NOSUCHNAME); wlan_append_channel_index(&val->var, sub, wif, channel); break; case SNMP_OP_SET: return (SNMP_ERR_NOT_WRITEABLE); case SNMP_OP_COMMIT: /* FALLTHROUGH */ case SNMP_OP_ROLLBACK: /* FALLTHROUGH */ default: abort(); } switch (val->var.subs[sub - 1]) { case LEAF_wlanIfaceChannelIeeeId: val->v.integer = channel->ic_ieee; break; case LEAF_wlanIfaceChannelType: val->v.integer = wlan_get_channel_type(channel); break; case LEAF_wlanIfaceChannelFlags: bits = wlan_channel_flags_to_snmp(channel->ic_flags); return (bits_get(val, (uint8_t *)&bits, sizeof(bits))); case LEAF_wlanIfaceChannelFrequency: val->v.integer = channel->ic_freq; break; case LEAF_wlanIfaceChannelMaxRegPower: val->v.integer = channel->ic_maxregpower; break; case LEAF_wlanIfaceChannelMaxTxPower: val->v.integer = channel->ic_maxpower; break; case LEAF_wlanIfaceChannelMinTxPower: val->v.integer = channel->ic_minpower; break; case LEAF_wlanIfaceChannelState: bits = wlan_channel_state_to_snmp(channel->ic_state); return (bits_get(val, (uint8_t *)&bits, sizeof(bits))); case LEAF_wlanIfaceChannelHTExtension: val->v.integer = channel->ic_extieee; break; case LEAF_wlanIfaceChannelMaxAntennaGain: val->v.integer = channel->ic_maxantgain; break; } return (SNMP_ERR_NOERROR); } int op_wlan_roam_params(struct snmp_context *ctx __unused, struct snmp_value *val, uint32_t sub, uint32_t iidx __unused, enum snmp_op op) { uint32_t phy; struct ieee80211_roamparam *rparam; struct wlan_iface *wif; wlan_update_interface_list(); wlan_update_roam_params(); switch (op) { case SNMP_OP_GET: rparam = wlan_get_roam_param(&val->var, sub, &wif); if (rparam == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_GETNEXT: rparam = wlan_get_next_roam_param(&val->var, sub, &wif, &phy); if (rparam == NULL || wif == NULL) return (SNMP_ERR_NOSUCHNAME); wlan_append_phy_index(&val->var, sub, wif->wname, phy); break; case SNMP_OP_SET: return (SNMP_ERR_NOT_WRITEABLE); case SNMP_OP_COMMIT: /* FALLTHROUGH */ case SNMP_OP_ROLLBACK: /* FALLTHROUGH */ default: abort(); } switch (val->var.subs[sub - 1]) { case LEAF_wlanIfRoamRxSignalStrength: val->v.integer = rparam->rssi/2; break; case LEAF_wlanIfRoamTxRateThreshold: val->v.integer = rparam->rate/2; break; default: abort(); } return (SNMP_ERR_NOERROR); } int op_wlan_tx_params(struct snmp_context *ctx, struct snmp_value *val, uint32_t sub, uint32_t iidx __unused, enum snmp_op op) { uint32_t phy; struct ieee80211_txparam *txparam; struct wlan_iface *wif; wlan_update_interface_list(); wlan_update_tx_params(); switch (op) { case SNMP_OP_GET: txparam = wlan_get_tx_param(&val->var, sub, &wif, &phy); if (txparam == NULL) return (SNMP_ERR_NOSUCHNAME); goto get_txparams; case SNMP_OP_GETNEXT: txparam = wlan_get_next_tx_param(&val->var, sub, &wif, &phy); if (txparam == NULL || wif == NULL) return (SNMP_ERR_NOSUCHNAME); wlan_append_phy_index(&val->var, sub, wif->wname, phy); goto get_txparams; case SNMP_OP_SET: txparam = wlan_get_tx_param(&val->var, sub, &wif, &phy); if (txparam == NULL || wif == NULL) return (SNMP_ERR_NOSUCHNAME); switch (val->var.subs[sub - 1]) { case LEAF_wlanIfTxUnicastRate: ctx->scratch->int1 = txparam->ucastrate; txparam->ucastrate = val->v.integer * 2; break; case LEAF_wlanIfTxMcastRate: ctx->scratch->int1 = txparam->mcastrate; txparam->mcastrate = val->v.integer * 2; break; case LEAF_wlanIfTxMgmtRate: ctx->scratch->int1 = txparam->mgmtrate; txparam->mgmtrate = val->v.integer * 2; break; case LEAF_wlanIfTxMaxRetryCount: ctx->scratch->int1 = txparam->maxretry; txparam->maxretry = val->v.integer; break; default: abort(); } if (wlan_set_tx_params(wif, phy) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case SNMP_OP_COMMIT: return (SNMP_ERR_NOERROR); case SNMP_OP_ROLLBACK: txparam = wlan_get_tx_param(&val->var, sub, &wif, &phy); if (txparam == NULL || wif == NULL) return (SNMP_ERR_NOSUCHNAME); switch (val->var.subs[sub - 1]) { case LEAF_wlanIfTxUnicastRate: txparam->ucastrate = ctx->scratch->int1; break; case LEAF_wlanIfTxMcastRate: txparam->mcastrate = ctx->scratch->int1; break; case LEAF_wlanIfTxMgmtRate: txparam->mgmtrate = ctx->scratch->int1; break; case LEAF_wlanIfTxMaxRetryCount: txparam->maxretry = ctx->scratch->int1; break; default: abort(); } if (wlan_set_tx_params(wif, phy) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); default: abort(); } get_txparams: switch (val->var.subs[sub - 1]) { case LEAF_wlanIfTxUnicastRate: val->v.integer = txparam->ucastrate / 2; break; case LEAF_wlanIfTxMcastRate: val->v.integer = txparam->mcastrate / 2; break; case LEAF_wlanIfTxMgmtRate: val->v.integer = txparam->mgmtrate / 2; break; case LEAF_wlanIfTxMaxRetryCount: val->v.integer = txparam->maxretry; break; default: abort(); } return (SNMP_ERR_NOERROR); } int op_wlan_scan_config(struct snmp_context *ctx, struct snmp_value *val, uint32_t sub, uint32_t iidx __unused, enum snmp_op op) { struct wlan_iface *wif; wlan_update_interface_list(); switch (op) { case SNMP_OP_GET: if ((wif = wlan_get_interface(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_GETNEXT: if ((wif = wlan_get_next_interface(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); wlan_append_ifindex(&val->var, sub, wif); break; case SNMP_OP_SET: if ((wif = wlan_get_interface(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); if (wif->scan_status == wlanScanConfigStatus_running && val->var.subs[sub - 1] != LEAF_wlanScanConfigStatus) return (SNMP_ERR_INCONS_VALUE); switch (val->var.subs[sub - 1]) { case LEAF_wlanScanFlags: ctx->scratch->int1 = wif->scan_flags; wif->scan_flags = val->v.integer; break; case LEAF_wlanScanDuration: ctx->scratch->int1 = wif->scan_duration; wif->scan_duration = val->v.integer; break; case LEAF_wlanScanMinChannelDwellTime: ctx->scratch->int1 = wif->scan_mindwell; wif->scan_mindwell = val->v.integer; break; case LEAF_wlanScanMaxChannelDwellTime: ctx->scratch->int1 = wif->scan_maxdwell; wif->scan_maxdwell = val->v.integer; break; case LEAF_wlanScanConfigStatus: if (val->v.integer == wlanScanConfigStatus_running || val->v.integer == wlanScanConfigStatus_cancel) { ctx->scratch->int1 = wif->scan_status; wif->scan_status = val->v.integer; break; } return (SNMP_ERR_INCONS_VALUE); } return (SNMP_ERR_NOERROR); case SNMP_OP_COMMIT: if ((wif = wlan_get_interface(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); if (val->var.subs[sub - 1] == LEAF_wlanScanConfigStatus) if (wif->scan_status == wlanScanConfigStatus_running) (void)wlan_set_scan_config(wif); /* XXX */ return (SNMP_ERR_NOERROR); case SNMP_OP_ROLLBACK: if ((wif = wlan_get_interface(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); switch (val->var.subs[sub - 1]) { case LEAF_wlanScanFlags: wif->scan_flags = ctx->scratch->int1; break; case LEAF_wlanScanDuration: wif->scan_duration = ctx->scratch->int1; break; case LEAF_wlanScanMinChannelDwellTime: wif->scan_mindwell = ctx->scratch->int1; break; case LEAF_wlanScanMaxChannelDwellTime: wif->scan_maxdwell = ctx->scratch->int1; break; case LEAF_wlanScanConfigStatus: wif->scan_status = ctx->scratch->int1; break; } return (SNMP_ERR_NOERROR); default: abort(); } switch (val->var.subs[sub - 1]) { case LEAF_wlanScanFlags: val->v.integer = wif->scan_flags; break; case LEAF_wlanScanDuration: val->v.integer = wif->scan_duration; break; case LEAF_wlanScanMinChannelDwellTime: val->v.integer = wif->scan_mindwell; break; case LEAF_wlanScanMaxChannelDwellTime: val->v.integer = wif->scan_maxdwell; break; case LEAF_wlanScanConfigStatus: val->v.integer = wif->scan_status; break; } return (SNMP_ERR_NOERROR); } int op_wlan_scan_results(struct snmp_context *ctx __unused, struct snmp_value *val, uint32_t sub, uint32_t iidx __unused, enum snmp_op op) { struct wlan_scan_result *sr; struct wlan_iface *wif; wlan_update_interface_list(); wlan_scan_update_results(); switch (op) { case SNMP_OP_GET: if ((sr = wlan_get_scanr(&val->var, sub, &wif)) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_GETNEXT: if ((sr = wlan_get_next_scanr(&val->var, sub, &wif)) == NULL) return (SNMP_ERR_NOSUCHNAME); wlan_append_scanr_index(&val->var, sub, wif->wname, sr->ssid, sr->bssid); break; case SNMP_OP_SET: return (SNMP_ERR_NOT_WRITEABLE); case SNMP_OP_COMMIT: /* FALLTHROUGH */ case SNMP_OP_ROLLBACK: /* FALLTHROUGH */ default: abort(); } switch (val->var.subs[sub - 1]) { case LEAF_wlanScanResultID: return (string_get(val, sr->ssid, -1)); case LEAF_wlanScanResultBssid: return (string_get(val, sr->bssid, IEEE80211_ADDR_LEN)); case LEAF_wlanScanResultChannel: val->v.integer = sr->opchannel; /* XXX */ break; case LEAF_wlanScanResultRate: val->v.integer = sr->rssi; break; case LEAF_wlanScanResultNoise: val->v.integer = sr->noise; break; case LEAF_wlanScanResultBeaconInterval: val->v.integer = sr->bintval; break; case LEAF_wlanScanResultCapabilities: return (bits_get(val, &sr->capinfo, sizeof(sr->capinfo))); default: abort(); } return (SNMP_ERR_NOERROR); } int op_wlan_iface_stats(struct snmp_context *ctx __unused, struct snmp_value *val, uint32_t sub, uint32_t iidx __unused, enum snmp_op op) { struct wlan_iface *wif; wlan_update_interface_list(); switch (op) { case SNMP_OP_GET: if ((wif = wlan_get_interface(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_GETNEXT: if ((wif = wlan_get_next_interface(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); wlan_append_ifindex(&val->var, sub, wif); break; case SNMP_OP_SET: /* XXX: LEAF_wlanStatsReset */ return (SNMP_ERR_NOT_WRITEABLE); case SNMP_OP_COMMIT: /* FALLTHROUGH */ case SNMP_OP_ROLLBACK: /* FALLTHROUGH */ default: abort(); } if (wlan_get_stats(wif) < 0) return (SNMP_ERR_GENERR); switch (val->var.subs[sub - 1]) { case LEAF_wlanStatsRxBadVersion: val->v.uint32 = wif->stats.is_rx_badversion; break; case LEAF_wlanStatsRxTooShort: val->v.uint32 = wif->stats.is_rx_tooshort; break; case LEAF_wlanStatsRxWrongBssid: val->v.uint32 = wif->stats.is_rx_wrongbss; break; case LEAF_wlanStatsRxDiscardedDups: val->v.uint32 = wif->stats.is_rx_dup; break; case LEAF_wlanStatsRxWrongDir: val->v.uint32 = wif->stats.is_rx_wrongdir; break; case LEAF_wlanStatsRxDiscardMcastEcho: val->v.uint32 = wif->stats.is_rx_mcastecho; break; case LEAF_wlanStatsRxDiscardNoAssoc: val->v.uint32 = wif->stats.is_rx_notassoc; break; case LEAF_wlanStatsRxWepNoPrivacy: val->v.uint32 = wif->stats.is_rx_noprivacy; break; case LEAF_wlanStatsRxWepUnencrypted: val->v.uint32 = wif->stats.is_rx_unencrypted; break; case LEAF_wlanStatsRxWepFailed: val->v.uint32 = wif->stats.is_rx_wepfail; break; case LEAF_wlanStatsRxDecapsulationFailed: val->v.uint32 = wif->stats.is_rx_decap; break; case LEAF_wlanStatsRxDiscardMgmt: val->v.uint32 = wif->stats.is_rx_mgtdiscard; break; case LEAF_wlanStatsRxControl: val->v.uint32 = wif->stats.is_rx_ctl; break; case LEAF_wlanStatsRxBeacon: val->v.uint32 = wif->stats.is_rx_beacon; break; case LEAF_wlanStatsRxRateSetTooBig: val->v.uint32 = wif->stats.is_rx_rstoobig; break; case LEAF_wlanStatsRxElemMissing: val->v.uint32 = wif->stats.is_rx_elem_missing; break; case LEAF_wlanStatsRxElemTooBig: val->v.uint32 = wif->stats.is_rx_elem_toobig; break; case LEAF_wlanStatsRxElemTooSmall: val->v.uint32 = wif->stats.is_rx_elem_toosmall; break; case LEAF_wlanStatsRxElemUnknown: val->v.uint32 = wif->stats.is_rx_elem_unknown; break; case LEAF_wlanStatsRxChannelMismatch: val->v.uint32 = wif->stats.is_rx_chanmismatch; break; case LEAF_wlanStatsRxDropped: val->v.uint32 = wif->stats.is_rx_nodealloc; break; case LEAF_wlanStatsRxSsidMismatch: val->v.uint32 = wif->stats.is_rx_ssidmismatch; break; case LEAF_wlanStatsRxAuthNotSupported: val->v.uint32 = wif->stats.is_rx_auth_unsupported; break; case LEAF_wlanStatsRxAuthFailed: val->v.uint32 = wif->stats.is_rx_auth_fail; break; case LEAF_wlanStatsRxAuthCM: val->v.uint32 = wif->stats.is_rx_auth_countermeasures; break; case LEAF_wlanStatsRxAssocWrongBssid: val->v.uint32 = wif->stats.is_rx_assoc_bss; break; case LEAF_wlanStatsRxAssocNoAuth: val->v.uint32 = wif->stats.is_rx_assoc_notauth; break; case LEAF_wlanStatsRxAssocCapMismatch: val->v.uint32 = wif->stats.is_rx_assoc_capmismatch; break; case LEAF_wlanStatsRxAssocNoRateMatch: val->v.uint32 = wif->stats.is_rx_assoc_norate; break; case LEAF_wlanStatsRxBadWpaIE: val->v.uint32 = wif->stats.is_rx_assoc_badwpaie; break; case LEAF_wlanStatsRxDeauthenticate: val->v.uint32 = wif->stats.is_rx_deauth; break; case LEAF_wlanStatsRxDisassociate: val->v.uint32 = wif->stats.is_rx_disassoc; break; case LEAF_wlanStatsRxUnknownSubtype: val->v.uint32 = wif->stats.is_rx_badsubtype; break; case LEAF_wlanStatsRxFailedNoBuf: val->v.uint32 = wif->stats.is_rx_nobuf; break; case LEAF_wlanStatsRxBadAuthRequest: val->v.uint32 = wif->stats.is_rx_bad_auth; break; case LEAF_wlanStatsRxUnAuthorized: val->v.uint32 = wif->stats.is_rx_unauth; break; case LEAF_wlanStatsRxBadKeyId: val->v.uint32 = wif->stats.is_rx_badkeyid; break; case LEAF_wlanStatsRxCCMPSeqViolation: val->v.uint32 = wif->stats.is_rx_ccmpreplay; break; case LEAF_wlanStatsRxCCMPBadFormat: val->v.uint32 = wif->stats.is_rx_ccmpformat; break; case LEAF_wlanStatsRxCCMPFailedMIC: val->v.uint32 = wif->stats.is_rx_ccmpmic; break; case LEAF_wlanStatsRxTKIPSeqViolation: val->v.uint32 = wif->stats.is_rx_tkipreplay; break; case LEAF_wlanStatsRxTKIPBadFormat: val->v.uint32 = wif->stats.is_rx_tkipformat; break; case LEAF_wlanStatsRxTKIPFailedMIC: val->v.uint32 = wif->stats.is_rx_tkipmic; break; case LEAF_wlanStatsRxTKIPFailedICV: val->v.uint32 = wif->stats.is_rx_tkipicv; break; case LEAF_wlanStatsRxDiscardACL: val->v.uint32 = wif->stats.is_rx_acl; break; case LEAF_wlanStatsTxFailedNoBuf: val->v.uint32 = wif->stats.is_tx_nobuf; break; case LEAF_wlanStatsTxFailedNoNode: val->v.uint32 = wif->stats.is_tx_nonode; break; case LEAF_wlanStatsTxUnknownMgmt: val->v.uint32 = wif->stats.is_tx_unknownmgt; break; case LEAF_wlanStatsTxBadCipher: val->v.uint32 = wif->stats.is_tx_badcipher; break; case LEAF_wlanStatsTxNoDefKey: val->v.uint32 = wif->stats.is_tx_nodefkey; break; case LEAF_wlanStatsTxFragmented: val->v.uint32 = wif->stats.is_tx_fragframes; break; case LEAF_wlanStatsTxFragmentsCreated: val->v.uint32 = wif->stats.is_tx_frags; break; case LEAF_wlanStatsActiveScans: val->v.uint32 = wif->stats.is_scan_active; break; case LEAF_wlanStatsPassiveScans: val->v.uint32 = wif->stats.is_scan_passive; break; case LEAF_wlanStatsTimeoutInactivity: val->v.uint32 = wif->stats.is_node_timeout; break; case LEAF_wlanStatsCryptoNoMem: val->v.uint32 = wif->stats.is_crypto_nomem; break; case LEAF_wlanStatsSwCryptoTKIP: val->v.uint32 = wif->stats.is_crypto_tkip; break; case LEAF_wlanStatsSwCryptoTKIPEnMIC: val->v.uint32 = wif->stats.is_crypto_tkipenmic; break; case LEAF_wlanStatsSwCryptoTKIPDeMIC: val->v.uint32 = wif->stats.is_crypto_tkipdemic; break; case LEAF_wlanStatsCryptoTKIPCM: val->v.uint32 = wif->stats.is_crypto_tkipcm; break; case LEAF_wlanStatsSwCryptoCCMP: val->v.uint32 = wif->stats.is_crypto_ccmp; break; case LEAF_wlanStatsSwCryptoWEP: val->v.uint32 = wif->stats.is_crypto_wep; break; case LEAF_wlanStatsCryptoCipherKeyRejected: val->v.uint32 = wif->stats.is_crypto_setkey_cipher; break; case LEAF_wlanStatsCryptoNoKey: val->v.uint32 = wif->stats.is_crypto_setkey_nokey; break; case LEAF_wlanStatsCryptoDeleteKeyFailed: val->v.uint32 = wif->stats.is_crypto_delkey; break; case LEAF_wlanStatsCryptoUnknownCipher: val->v.uint32 = wif->stats.is_crypto_badcipher; break; case LEAF_wlanStatsCryptoAttachFailed: val->v.uint32 = wif->stats.is_crypto_attachfail; break; case LEAF_wlanStatsCryptoKeyFailed: val->v.uint32 = wif->stats.is_crypto_keyfail; break; case LEAF_wlanStatsCryptoEnMICFailed: val->v.uint32 = wif->stats.is_crypto_enmicfail; break; case LEAF_wlanStatsIBSSCapMismatch: val->v.uint32 = wif->stats.is_ibss_capmismatch; break; case LEAF_wlanStatsUnassocStaPSPoll: val->v.uint32 = wif->stats.is_ps_unassoc; break; case LEAF_wlanStatsBadAidPSPoll: val->v.uint32 = wif->stats.is_ps_badaid; break; case LEAF_wlanStatsEmptyPSPoll: val->v.uint32 = wif->stats.is_ps_qempty; break; case LEAF_wlanStatsRxFFBadHdr: val->v.uint32 = wif->stats.is_ff_badhdr; break; case LEAF_wlanStatsRxFFTooShort: val->v.uint32 = wif->stats.is_ff_tooshort; break; case LEAF_wlanStatsRxFFSplitError: val->v.uint32 = wif->stats.is_ff_split; break; case LEAF_wlanStatsRxFFDecap: val->v.uint32 = wif->stats.is_ff_decap; break; case LEAF_wlanStatsTxFFEncap: val->v.uint32 = wif->stats.is_ff_encap; break; case LEAF_wlanStatsRxBadBintval: val->v.uint32 = wif->stats.is_rx_badbintval; break; case LEAF_wlanStatsRxDemicFailed: val->v.uint32 = wif->stats.is_rx_demicfail; break; case LEAF_wlanStatsRxDefragFailed: val->v.uint32 = wif->stats.is_rx_defrag; break; case LEAF_wlanStatsRxMgmt: val->v.uint32 = wif->stats.is_rx_mgmt; break; case LEAF_wlanStatsRxActionMgmt: val->v.uint32 = wif->stats.is_rx_action; break; case LEAF_wlanStatsRxAMSDUTooShort: val->v.uint32 = wif->stats.is_amsdu_tooshort; break; case LEAF_wlanStatsRxAMSDUSplitError: val->v.uint32 = wif->stats.is_amsdu_split; break; case LEAF_wlanStatsRxAMSDUDecap: val->v.uint32 = wif->stats.is_amsdu_decap; break; case LEAF_wlanStatsTxAMSDUEncap: val->v.uint32 = wif->stats.is_amsdu_encap; break; case LEAF_wlanStatsAMPDUBadBAR: val->v.uint32 = wif->stats.is_ampdu_bar_bad; break; case LEAF_wlanStatsAMPDUOowBar: val->v.uint32 = wif->stats.is_ampdu_bar_oow; break; case LEAF_wlanStatsAMPDUMovedBAR: val->v.uint32 = wif->stats.is_ampdu_bar_move; break; case LEAF_wlanStatsAMPDURxBAR: val->v.uint32 = wif->stats.is_ampdu_bar_rx; break; case LEAF_wlanStatsAMPDURxOor: val->v.uint32 = wif->stats.is_ampdu_rx_oor; break; case LEAF_wlanStatsAMPDURxCopied: val->v.uint32 = wif->stats.is_ampdu_rx_copy; break; case LEAF_wlanStatsAMPDURxDropped: val->v.uint32 = wif->stats.is_ampdu_rx_drop; break; case LEAF_wlanStatsTxDiscardBadState: val->v.uint32 = wif->stats.is_tx_badstate; break; case LEAF_wlanStatsTxFailedNoAssoc: val->v.uint32 = wif->stats.is_tx_notassoc; break; case LEAF_wlanStatsTxClassifyFailed: val->v.uint32 = wif->stats.is_tx_classify; break; case LEAF_wlanStatsDwdsMcastDiscard: val->v.uint32 = wif->stats.is_dwds_mcast; break; case LEAF_wlanStatsHTAssocRejectNoHT: val->v.uint32 = wif->stats.is_ht_assoc_nohtcap; break; case LEAF_wlanStatsHTAssocDowngrade: val->v.uint32 = wif->stats.is_ht_assoc_downgrade; break; case LEAF_wlanStatsHTAssocRateMismatch: val->v.uint32 = wif->stats.is_ht_assoc_norate; break; case LEAF_wlanStatsAMPDURxAge: val->v.uint32 = wif->stats.is_ampdu_rx_age; break; case LEAF_wlanStatsAMPDUMoved: val->v.uint32 = wif->stats.is_ampdu_rx_move; break; case LEAF_wlanStatsADDBADisabledReject: val->v.uint32 = wif->stats.is_addba_reject; break; case LEAF_wlanStatsADDBANoRequest: val->v.uint32 = wif->stats.is_addba_norequest; break; case LEAF_wlanStatsADDBABadToken: val->v.uint32 = wif->stats.is_addba_badtoken; break; case LEAF_wlanStatsADDBABadPolicy: val->v.uint32 = wif->stats.is_addba_badpolicy; break; case LEAF_wlanStatsAMPDUStopped: val->v.uint32 = wif->stats.is_ampdu_stop; break; case LEAF_wlanStatsAMPDUStopFailed: val->v.uint32 = wif->stats.is_ampdu_stop_failed; break; case LEAF_wlanStatsAMPDURxReorder: val->v.uint32 = wif->stats.is_ampdu_rx_reorder; break; case LEAF_wlanStatsScansBackground: val->v.uint32 = wif->stats.is_scan_bg; break; case LEAF_wlanLastDeauthReason: val->v.uint32 = wif->stats.is_rx_deauth_code; break; case LEAF_wlanLastDissasocReason: val->v.uint32 = wif->stats.is_rx_disassoc_code; break; case LEAF_wlanLastAuthFailReason: val->v.uint32 = wif->stats.is_rx_authfail_code; break; case LEAF_wlanStatsBeaconMissedEvents: val->v.uint32 = wif->stats.is_beacon_miss; break; case LEAF_wlanStatsRxDiscardBadStates: val->v.uint32 = wif->stats.is_rx_badstate; break; case LEAF_wlanStatsFFFlushed: val->v.uint32 = wif->stats.is_ff_flush; break; case LEAF_wlanStatsTxControlFrames: val->v.uint32 = wif->stats.is_tx_ctl; break; case LEAF_wlanStatsAMPDURexmt: val->v.uint32 = wif->stats.is_ampdu_rexmt; break; case LEAF_wlanStatsAMPDURexmtFailed: val->v.uint32 = wif->stats.is_ampdu_rexmt_fail; break; case LEAF_wlanStatsReset: val->v.uint32 = wlanStatsReset_no_op; break; default: abort(); } return (SNMP_ERR_NOERROR); } int op_wlan_wep_iface(struct snmp_context *ctx, struct snmp_value *val, uint32_t sub, uint32_t iidx __unused, enum snmp_op op) { struct wlan_iface *wif; wlan_update_interface_list(); switch (op) { case SNMP_OP_GET: if ((wif = wlan_get_interface(&val->var, sub)) == NULL || !wif->wepsupported) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_GETNEXT: /* XXX: filter wif->wepsupported */ if ((wif = wlan_get_next_interface(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); wlan_append_ifindex(&val->var, sub, wif); break; case SNMP_OP_SET: if ((wif = wlan_get_interface(&val->var, sub)) == NULL || !wif->wepsupported) return (SNMP_ERR_NOSUCHNAME); switch (val->var.subs[sub - 1]) { case LEAF_wlanWepMode: if (val->v.integer < wlanWepMode_off || val->v.integer > wlanWepMode_mixed) return (SNMP_ERR_INCONS_VALUE); ctx->scratch->int1 = wif->wepmode; wif->wepmode = val->v.integer; if (wlan_set_wepmode(wif) < 0) { wif->wepmode = ctx->scratch->int1; return (SNMP_ERR_GENERR); } break; case LEAF_wlanWepDefTxKey: if (val->v.integer < 0 || val->v.integer > IEEE80211_WEP_NKID) return (SNMP_ERR_INCONS_VALUE); ctx->scratch->int1 = wif->weptxkey; wif->weptxkey = val->v.integer; if (wlan_set_weptxkey(wif) < 0) { wif->weptxkey = ctx->scratch->int1; return (SNMP_ERR_GENERR); } break; default: abort(); } return (SNMP_ERR_NOERROR); case SNMP_OP_COMMIT: return (SNMP_ERR_NOERROR); case SNMP_OP_ROLLBACK: if ((wif = wlan_get_interface(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); switch (val->var.subs[sub - 1]) { case LEAF_wlanWepMode: wif->wepmode = ctx->scratch->int1; if (wlan_set_wepmode(wif) < 0) return (SNMP_ERR_GENERR); break; case LEAF_wlanWepDefTxKey: wif->weptxkey = ctx->scratch->int1; if (wlan_set_weptxkey(wif) < 0) return (SNMP_ERR_GENERR); break; default: abort(); } return (SNMP_ERR_NOERROR); default: abort(); } switch (val->var.subs[sub - 1]) { case LEAF_wlanWepMode: if (wlan_get_wepmode(wif) < 0) return (SNMP_ERR_GENERR); val->v.integer = wif->wepmode; break; case LEAF_wlanWepDefTxKey: if (wlan_get_weptxkey(wif) < 0) return (SNMP_ERR_GENERR); val->v.integer = wif->weptxkey; break; default: abort(); } return (SNMP_ERR_NOERROR); } int op_wlan_wep_key(struct snmp_context *ctx __unused, struct snmp_value *val __unused, uint32_t sub __unused, uint32_t iidx __unused, enum snmp_op op __unused) { return (SNMP_ERR_NOSUCHNAME); } int op_wlan_mac_access_control(struct snmp_context *ctx, struct snmp_value *val, uint32_t sub, uint32_t iidx __unused, enum snmp_op op) { struct wlan_iface *wif; wlan_update_interface_list(); switch (op) { case SNMP_OP_GET: if ((wif = wlan_get_interface(&val->var, sub)) == NULL || !wif->macsupported) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_GETNEXT: /* XXX: filter wif->macsupported */ if ((wif = wlan_get_next_interface(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); wlan_append_ifindex(&val->var, sub, wif); break; case SNMP_OP_SET: if ((wif = wlan_get_interface(&val->var, sub)) == NULL || !wif->macsupported) return (SNMP_ERR_NOSUCHNAME); switch (val->var.subs[sub - 1]) { case LEAF_wlanMACAccessControlPolicy: ctx->scratch->int1 = wif->mac_policy; wif->mac_policy = val->v.integer; break; case LEAF_wlanMACAccessControlNacl: return (SNMP_ERR_NOT_WRITEABLE); case LEAF_wlanMACAccessControlFlush: break; default: abort(); } return (SNMP_ERR_NOERROR); case SNMP_OP_COMMIT: if ((wif = wlan_get_interface(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); switch (val->var.subs[sub - 1]) { case LEAF_wlanMACAccessControlPolicy: if (wlan_set_mac_policy(wif) < 0) { wif->mac_policy = ctx->scratch->int1; return (SNMP_ERR_GENERR); } break; case LEAF_wlanMACAccessControlFlush: if (wlan_flush_mac_mac(wif) < 0) return (SNMP_ERR_GENERR); break; default: abort(); } return (SNMP_ERR_NOERROR); case SNMP_OP_ROLLBACK: if ((wif = wlan_get_interface(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); if (val->var.subs[sub - 1] == LEAF_wlanMACAccessControlPolicy) wif->mac_policy = ctx->scratch->int1; return (SNMP_ERR_NOERROR); default: abort(); } if (wlan_get_mac_policy(wif) < 0) return (SNMP_ERR_GENERR); switch (val->var.subs[sub - 1]) { case LEAF_wlanMACAccessControlPolicy: val->v.integer = wif->mac_policy; break; case LEAF_wlanMACAccessControlNacl: val->v.integer = wif->mac_nacls; break; case LEAF_wlanMACAccessControlFlush: val->v.integer = wlanMACAccessControlFlush_no_op; break; default: abort(); } return (SNMP_ERR_NOERROR); } int op_wlan_mac_acl_mac(struct snmp_context *ctx, struct snmp_value *val, uint32_t sub, uint32_t iidx __unused, enum snmp_op op) { struct wlan_iface *wif; struct wlan_mac_mac *macl; wlan_update_interface_list(); wlan_mac_update_aclmacs(); switch (op) { case SNMP_OP_GET: if ((macl = wlan_get_acl_mac(&val->var, sub, &wif)) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_GETNEXT: if ((macl = wlan_get_next_acl_mac(&val->var, sub, &wif)) == NULL) return (SNMP_ERR_NOSUCHNAME); wlan_append_mac_index(&val->var, sub, wif->wname, macl->mac); break; case SNMP_OP_SET: switch (val->var.subs[sub - 1]) { case LEAF_wlanMACAccessControlMAC: return (SNMP_ERR_INCONS_NAME); case LEAF_wlanMACAccessControlMACStatus: return(wlan_acl_mac_set_status(ctx, val, sub)); default: abort(); } case SNMP_OP_COMMIT: if ((macl = wlan_get_acl_mac(&val->var, sub, &wif)) == NULL) return (SNMP_ERR_NOSUCHNAME); if (val->v.integer == RowStatus_destroy && wlan_mac_delete_mac(wif, macl) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case SNMP_OP_ROLLBACK: if ((macl = wlan_get_acl_mac(&val->var, sub, &wif)) == NULL) return (SNMP_ERR_NOSUCHNAME); if (ctx->scratch->int1 == RowStatus_destroy && wlan_mac_delete_mac(wif, macl) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); default: abort(); } switch (val->var.subs[sub - 1]) { case LEAF_wlanMACAccessControlMAC: return (string_get(val, macl->mac, IEEE80211_ADDR_LEN)); case LEAF_wlanMACAccessControlMACStatus: val->v.integer = macl->mac_status; break; default: abort(); } return (SNMP_ERR_NOERROR); } int op_wlan_mesh_config(struct snmp_context *ctx, struct snmp_value *val, uint32_t sub, uint32_t iidx __unused, enum snmp_op op) { int which; switch (val->var.subs[sub - 1]) { case LEAF_wlanMeshMaxRetries: which = WLAN_MESH_MAX_RETRIES; break; case LEAF_wlanMeshHoldingTimeout: which = WLAN_MESH_HOLDING_TO; break; case LEAF_wlanMeshConfirmTimeout: which = WLAN_MESH_CONFIRM_TO; break; case LEAF_wlanMeshRetryTimeout: which = WLAN_MESH_RETRY_TO; break; default: abort(); } switch (op) { case SNMP_OP_GET: if (wlan_do_sysctl(&wlan_config, which, 0) < 0) return (SNMP_ERR_GENERR); break; case SNMP_OP_GETNEXT: abort(); case SNMP_OP_SET: switch (val->var.subs[sub - 1]) { case LEAF_wlanMeshRetryTimeout : ctx->scratch->int1 = wlan_config.mesh_retryto; wlan_config.mesh_retryto = val->v.integer; break; case LEAF_wlanMeshHoldingTimeout: ctx->scratch->int1 = wlan_config.mesh_holdingto; wlan_config.mesh_holdingto = val->v.integer; break; case LEAF_wlanMeshConfirmTimeout: ctx->scratch->int1 = wlan_config.mesh_confirmto; wlan_config.mesh_confirmto = val->v.integer; break; case LEAF_wlanMeshMaxRetries: ctx->scratch->int1 = wlan_config.mesh_maxretries; wlan_config.mesh_maxretries = val->v.integer; break; } if (wlan_do_sysctl(&wlan_config, which, 1) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case SNMP_OP_COMMIT: return (SNMP_ERR_NOERROR); case SNMP_OP_ROLLBACK: switch (val->var.subs[sub - 1]) { case LEAF_wlanMeshRetryTimeout: wlan_config.mesh_retryto = ctx->scratch->int1; break; case LEAF_wlanMeshConfirmTimeout: wlan_config.mesh_confirmto = ctx->scratch->int1; break; case LEAF_wlanMeshHoldingTimeout: wlan_config.mesh_holdingto= ctx->scratch->int1; break; case LEAF_wlanMeshMaxRetries: wlan_config.mesh_maxretries = ctx->scratch->int1; break; } if (wlan_do_sysctl(&wlan_config, which, 1) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); default: abort(); } switch (val->var.subs[sub - 1]) { case LEAF_wlanMeshRetryTimeout: val->v.integer = wlan_config.mesh_retryto; break; case LEAF_wlanMeshHoldingTimeout: val->v.integer = wlan_config.mesh_holdingto; break; case LEAF_wlanMeshConfirmTimeout: val->v.integer = wlan_config.mesh_confirmto; break; case LEAF_wlanMeshMaxRetries: val->v.integer = wlan_config.mesh_maxretries; break; } return (SNMP_ERR_NOERROR); } int op_wlan_mesh_iface(struct snmp_context *ctx, struct snmp_value *val, uint32_t sub, uint32_t iidx __unused, enum snmp_op op) { int rc; struct wlan_iface *wif; wlan_update_interface_list(); switch (op) { case SNMP_OP_GET: if ((wif = wlan_mesh_get_iface(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_GETNEXT: if ((wif = wlan_mesh_get_next_iface(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); wlan_append_ifindex(&val->var, sub, wif); break; case SNMP_OP_SET: if ((wif = wlan_mesh_get_iface(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); switch (val->var.subs[sub - 1]) { case LEAF_wlanMeshId: if (val->v.octetstring.len > IEEE80211_NWID_LEN) return (SNMP_ERR_INCONS_VALUE); ctx->scratch->ptr1 = malloc(val->v.octetstring.len + 1); if (ctx->scratch->ptr1 == NULL) return (SNMP_ERR_GENERR); strlcpy(ctx->scratch->ptr1, wif->desired_ssid, val->v.octetstring.len + 1); ctx->scratch->int1 = strlen(wif->desired_ssid); memcpy(wif->desired_ssid, val->v.octetstring.octets, val->v.octetstring.len); wif->desired_ssid[val->v.octetstring.len] = '\0'; break; case LEAF_wlanMeshTTL: ctx->scratch->int1 = wif->mesh_ttl; wif->mesh_ttl = val->v.integer; break; case LEAF_wlanMeshPeeringEnabled: ctx->scratch->int1 = wif->mesh_peering; wif->mesh_peering = val->v.integer; break; case LEAF_wlanMeshForwardingEnabled: ctx->scratch->int1 = wif->mesh_forwarding; wif->mesh_forwarding = val->v.integer; break; case LEAF_wlanMeshMetric: ctx->scratch->int1 = wif->mesh_metric; wif->mesh_metric = val->v.integer; break; case LEAF_wlanMeshPath: ctx->scratch->int1 = wif->mesh_path; wif->mesh_path = val->v.integer; break; case LEAF_wlanMeshRoutesFlush: if (val->v.integer != wlanMeshRoutesFlush_flush) return (SNMP_ERR_INCONS_VALUE); return (SNMP_ERR_NOERROR); default: abort(); } if (val->var.subs[sub - 1] == LEAF_wlanMeshId) rc = wlan_config_set_dssid(wif, val->v.octetstring.octets, val->v.octetstring.len); else rc = wlan_mesh_config_set(wif, val->var.subs[sub - 1]); if (rc < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case SNMP_OP_COMMIT: if ((wif = wlan_mesh_get_iface(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); if (val->var.subs[sub - 1] == LEAF_wlanMeshRoutesFlush && wlan_mesh_flush_routes(wif) < 0) return (SNMP_ERR_GENERR); if (val->var.subs[sub - 1] == LEAF_wlanMeshId) free(ctx->scratch->ptr1); return (SNMP_ERR_NOERROR); case SNMP_OP_ROLLBACK: if ((wif = wlan_mesh_get_iface(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); switch (val->var.subs[sub - 1]) { case LEAF_wlanMeshId: strlcpy(wif->desired_ssid, ctx->scratch->ptr1, IEEE80211_NWID_LEN); free(ctx->scratch->ptr1); break; case LEAF_wlanMeshTTL: wif->mesh_ttl = ctx->scratch->int1; break; case LEAF_wlanMeshPeeringEnabled: wif->mesh_peering = ctx->scratch->int1; break; case LEAF_wlanMeshForwardingEnabled: wif->mesh_forwarding = ctx->scratch->int1; break; case LEAF_wlanMeshMetric: wif->mesh_metric = ctx->scratch->int1; break; case LEAF_wlanMeshPath: wif->mesh_path = ctx->scratch->int1; break; case LEAF_wlanMeshRoutesFlush: return (SNMP_ERR_NOERROR); default: abort(); } if (val->var.subs[sub - 1] == LEAF_wlanMeshId) rc = wlan_config_set_dssid(wif, wif->desired_ssid, strlen(wif->desired_ssid)); else rc = wlan_mesh_config_set(wif, val->var.subs[sub - 1]); if (rc < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); default: abort(); } if (val->var.subs[sub - 1] == LEAF_wlanMeshId) rc = wlan_config_get_dssid(wif); else rc = wlan_mesh_config_get(wif, val->var.subs[sub - 1]); if (rc < 0) return (SNMP_ERR_GENERR); switch (val->var.subs[sub - 1]) { case LEAF_wlanMeshId: return (string_get(val, wif->desired_ssid, -1)); case LEAF_wlanMeshTTL: val->v.integer = wif->mesh_ttl; break; case LEAF_wlanMeshPeeringEnabled: val->v.integer = wif->mesh_peering; break; case LEAF_wlanMeshForwardingEnabled: val->v.integer = wif->mesh_forwarding; break; case LEAF_wlanMeshMetric: val->v.integer = wif->mesh_metric; break; case LEAF_wlanMeshPath: val->v.integer = wif->mesh_path; break; case LEAF_wlanMeshRoutesFlush: val->v.integer = wlanMeshRoutesFlush_no_op; break; default: abort(); } return (SNMP_ERR_NOERROR); } int op_wlan_mesh_neighbor(struct snmp_context *ctx __unused, struct snmp_value *val, uint32_t sub, uint32_t iidx __unused, enum snmp_op op) { struct wlan_peer *wip; struct wlan_iface *wif; wlan_update_interface_list(); wlan_update_peers(); switch (op) { case SNMP_OP_GET: if ((wip = wlan_mesh_get_peer(&val->var, sub, &wif)) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_GETNEXT: wip = wlan_mesh_get_next_peer(&val->var, sub, &wif); if (wip == NULL) return (SNMP_ERR_NOSUCHNAME); wlan_append_mac_index(&val->var, sub, wif->wname, wip->pmac); break; case SNMP_OP_SET: return (SNMP_ERR_NOT_WRITEABLE); case SNMP_OP_COMMIT: /* FALLTHROUGH */ case SNMP_OP_ROLLBACK: /* FALLTHROUGH */ default: abort(); } switch (val->var.subs[sub - 1]) { case LEAF_wlanMeshNeighborAddress: return (string_get(val, wip->pmac, IEEE80211_ADDR_LEN)); case LEAF_wlanMeshNeighborFrequency: val->v.integer = wip->frequency; break; case LEAF_wlanMeshNeighborLocalId: val->v.integer = wip->local_id; break; case LEAF_wlanMeshNeighborPeerId: val->v.integer = wip->peer_id; break; case LEAF_wlanMeshNeighborPeerState: return (bits_get(val, (uint8_t *)&wip->state, sizeof(wip->state))); case LEAF_wlanMeshNeighborCurrentTXRate: val->v.integer = wip->txrate; break; case LEAF_wlanMeshNeighborRxSignalStrength: val->v.integer = wip->rssi; break; case LEAF_wlanMeshNeighborIdleTimer: val->v.integer = wip->idle; break; case LEAF_wlanMeshNeighborTxSequenceNo: val->v.integer = wip->txseqs; break; case LEAF_wlanMeshNeighborRxSequenceNo: val->v.integer = wip->rxseqs; break; default: abort(); } return (SNMP_ERR_NOERROR); } int op_wlan_mesh_route(struct snmp_context *ctx, struct snmp_value *val, uint32_t sub, uint32_t iidx __unused, enum snmp_op op) { struct wlan_mesh_route *wmr; struct wlan_iface *wif; wlan_update_interface_list(); wlan_mesh_update_routes(); switch (op) { case SNMP_OP_GET: if ((wmr = wlan_mesh_get_route(&val->var, sub, &wif)) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_GETNEXT: wmr = wlan_mesh_get_next_route(&val->var, sub, &wif); if (wmr == NULL) return (SNMP_ERR_NOSUCHNAME); wlan_append_mac_index(&val->var, sub, wif->wname, wmr->imroute.imr_dest); break; case SNMP_OP_SET: switch (val->var.subs[sub - 1]) { case LEAF_wlanMeshRouteDestination: return (SNMP_ERR_INCONS_NAME); case LEAF_wlanMeshRouteStatus: return(wlan_mesh_route_set_status(ctx, val, sub)); default: return (SNMP_ERR_NOT_WRITEABLE); } abort(); case SNMP_OP_COMMIT: if ((wmr = wlan_mesh_get_route(&val->var, sub, &wif)) == NULL) return (SNMP_ERR_NOSUCHNAME); if (val->v.integer == RowStatus_destroy && wlan_mesh_delete_route(wif, wmr) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case SNMP_OP_ROLLBACK: if ((wmr = wlan_mesh_get_route(&val->var, sub, &wif)) == NULL) return (SNMP_ERR_NOSUCHNAME); if (ctx->scratch->int1 == RowStatus_destroy && wlan_mesh_delete_route(wif, wmr) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); default: abort(); } switch (val->var.subs[sub - 1]) { case LEAF_wlanMeshRouteDestination: return (string_get(val, wmr->imroute.imr_dest, IEEE80211_ADDR_LEN)); case LEAF_wlanMeshRouteNextHop: return (string_get(val, wmr->imroute.imr_nexthop, IEEE80211_ADDR_LEN)); case LEAF_wlanMeshRouteHops: val->v.integer = wmr->imroute.imr_nhops; break; case LEAF_wlanMeshRouteMetric: val->v.integer = wmr->imroute.imr_metric; break; case LEAF_wlanMeshRouteLifeTime: val->v.integer = wmr->imroute.imr_lifetime; break; case LEAF_wlanMeshRouteLastMseq: val->v.integer = wmr->imroute.imr_lastmseq; break; case LEAF_wlanMeshRouteFlags: val->v.integer = 0; if ((wmr->imroute.imr_flags & IEEE80211_MESHRT_FLAGS_VALID) != 0) val->v.integer |= (0x1 << wlanMeshRouteFlags_valid); if ((wmr->imroute.imr_flags & IEEE80211_MESHRT_FLAGS_PROXY) != 0) val->v.integer |= (0x1 << wlanMeshRouteFlags_proxy); return (bits_get(val, (uint8_t *)&val->v.integer, sizeof(val->v.integer))); case LEAF_wlanMeshRouteStatus: val->v.integer = wmr->mroute_status; break; } return (SNMP_ERR_NOERROR); } int op_wlan_mesh_stats(struct snmp_context *ctx __unused, struct snmp_value *val, uint32_t sub, uint32_t iidx __unused, enum snmp_op op) { struct wlan_iface *wif; wlan_update_interface_list(); switch (op) { case SNMP_OP_GET: if ((wif = wlan_mesh_get_iface(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_GETNEXT: if ((wif = wlan_mesh_get_next_iface(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); wlan_append_ifindex(&val->var, sub, wif); break; case SNMP_OP_SET: return (SNMP_ERR_NOT_WRITEABLE); case SNMP_OP_COMMIT: /* FALLTHROUGH */ case SNMP_OP_ROLLBACK: /* FALLTHROUGH */ default: abort(); } if (wlan_get_stats(wif) < 0) return (SNMP_ERR_GENERR); switch (val->var.subs[sub - 1]) { case LEAF_wlanMeshDroppedBadSta: val->v.uint32 = wif->stats.is_mesh_wrongmesh; break; case LEAF_wlanMeshDroppedNoLink: val->v.uint32 = wif->stats.is_mesh_nolink; break; case LEAF_wlanMeshNoFwdTtl: val->v.uint32 = wif->stats.is_mesh_fwd_ttl; break; case LEAF_wlanMeshNoFwdBuf: val->v.uint32 = wif->stats.is_mesh_fwd_nobuf; break; case LEAF_wlanMeshNoFwdTooShort: val->v.uint32 = wif->stats.is_mesh_fwd_tooshort; break; case LEAF_wlanMeshNoFwdDisabled: val->v.uint32 = wif->stats.is_mesh_fwd_disabled; break; case LEAF_wlanMeshNoFwdPathUnknown: val->v.uint32 = wif->stats.is_mesh_fwd_nopath; break; case LEAF_wlanMeshDroppedBadAE: val->v.uint32 = wif->stats.is_mesh_badae; break; case LEAF_wlanMeshRouteAddFailed: val->v.uint32 = wif->stats.is_mesh_rtaddfailed; break; case LEAF_wlanMeshDroppedNoProxy: val->v.uint32 = wif->stats.is_mesh_notproxy; break; case LEAF_wlanMeshDroppedMisaligned: val->v.uint32 = wif->stats.is_rx_badalign; break; default: abort(); } return (SNMP_ERR_NOERROR); } int op_wlan_hwmp_config(struct snmp_context *ctx, struct snmp_value *val, uint32_t sub, uint32_t iidx __unused, enum snmp_op op) { int which; switch (val->var.subs[sub - 1]) { case LEAF_wlanHWMPRouteInactiveTimeout: which = WLAN_HWMP_INACTIVITY_TO; break; case LEAF_wlanHWMPRootAnnounceInterval: which = WLAN_HWMP_RANN_INT; break; case LEAF_wlanHWMPRootInterval: which = WLAN_HWMP_ROOT_INT; break; case LEAF_wlanHWMPRootTimeout: which = WLAN_HWMP_ROOT_TO; break; case LEAF_wlanHWMPPathLifetime: which = WLAN_HWMP_PATH_LIFETIME; break; case LEAF_wlanHWMPReplyForwardBit: which = WLAN_HWMP_REPLY_FORWARD; break; case LEAF_wlanHWMPTargetOnlyBit: which = WLAN_HWMP_TARGET_ONLY; break; default: abort(); } switch (op) { case SNMP_OP_GET: if (wlan_do_sysctl(&wlan_config, which, 0) < 0) return (SNMP_ERR_GENERR); break; case SNMP_OP_GETNEXT: abort(); case SNMP_OP_SET: switch (val->var.subs[sub - 1]) { case LEAF_wlanHWMPRouteInactiveTimeout: ctx->scratch->int1 = wlan_config.hwmp_inact; wlan_config.hwmp_inact = val->v.integer; break; case LEAF_wlanHWMPRootAnnounceInterval: ctx->scratch->int1 = wlan_config.hwmp_rannint; wlan_config.hwmp_rannint = val->v.integer; break; case LEAF_wlanHWMPRootInterval: ctx->scratch->int1 = wlan_config.hwmp_rootint; wlan_config.hwmp_rootint = val->v.integer; break; case LEAF_wlanHWMPRootTimeout: ctx->scratch->int1 = wlan_config.hwmp_roottimeout; wlan_config.hwmp_roottimeout = val->v.integer; break; case LEAF_wlanHWMPPathLifetime: ctx->scratch->int1 = wlan_config.hwmp_pathlifetime; wlan_config.hwmp_pathlifetime = val->v.integer; break; case LEAF_wlanHWMPReplyForwardBit: ctx->scratch->int1 = wlan_config.hwmp_replyforward; wlan_config.hwmp_replyforward = val->v.integer; break; case LEAF_wlanHWMPTargetOnlyBit: ctx->scratch->int1 = wlan_config.hwmp_targetonly; wlan_config.hwmp_targetonly = val->v.integer; break; } if (wlan_do_sysctl(&wlan_config, which, 1) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case SNMP_OP_COMMIT: return (SNMP_ERR_NOERROR); case SNMP_OP_ROLLBACK: switch (val->var.subs[sub - 1]) { case LEAF_wlanHWMPRouteInactiveTimeout: wlan_config.hwmp_inact = ctx->scratch->int1; break; case LEAF_wlanHWMPRootAnnounceInterval: wlan_config.hwmp_rannint = ctx->scratch->int1; break; case LEAF_wlanHWMPRootInterval: wlan_config.hwmp_rootint = ctx->scratch->int1; break; case LEAF_wlanHWMPRootTimeout: wlan_config.hwmp_roottimeout = ctx->scratch->int1; break; case LEAF_wlanHWMPPathLifetime: wlan_config.hwmp_pathlifetime = ctx->scratch->int1; break; case LEAF_wlanHWMPReplyForwardBit: wlan_config.hwmp_replyforward = ctx->scratch->int1; break; case LEAF_wlanHWMPTargetOnlyBit: wlan_config.hwmp_targetonly = ctx->scratch->int1; break; } if (wlan_do_sysctl(&wlan_config, which, 1) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); default: abort(); } switch (val->var.subs[sub - 1]) { case LEAF_wlanHWMPRouteInactiveTimeout: val->v.integer = wlan_config.hwmp_inact; break; case LEAF_wlanHWMPRootAnnounceInterval: val->v.integer = wlan_config.hwmp_rannint; break; case LEAF_wlanHWMPRootInterval: val->v.integer = wlan_config.hwmp_rootint; break; case LEAF_wlanHWMPRootTimeout: val->v.integer = wlan_config.hwmp_roottimeout; break; case LEAF_wlanHWMPPathLifetime: val->v.integer = wlan_config.hwmp_pathlifetime; break; case LEAF_wlanHWMPReplyForwardBit: val->v.integer = wlan_config.hwmp_replyforward; break; case LEAF_wlanHWMPTargetOnlyBit: val->v.integer = wlan_config.hwmp_targetonly; break; } return (SNMP_ERR_NOERROR); } int op_wlan_hwmp_iface(struct snmp_context *ctx, struct snmp_value *val, uint32_t sub, uint32_t iidx __unused, enum snmp_op op) { struct wlan_iface *wif; wlan_update_interface_list(); switch (op) { case SNMP_OP_GET: if ((wif = wlan_mesh_get_iface(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_GETNEXT: if ((wif = wlan_mesh_get_next_iface(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); wlan_append_ifindex(&val->var, sub, wif); break; case SNMP_OP_SET: if ((wif = wlan_mesh_get_iface(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); switch (val->var.subs[sub - 1]) { case LEAF_wlanHWMPRootMode: ctx->scratch->int1 = wif->hwmp_root_mode; wif->hwmp_root_mode = val->v.integer; break; case LEAF_wlanHWMPMaxHops: ctx->scratch->int1 = wif->hwmp_max_hops; wif->hwmp_max_hops = val->v.integer; break; default: abort(); } if (wlan_hwmp_config_set(wif, val->var.subs[sub - 1]) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); case SNMP_OP_COMMIT: return (SNMP_ERR_NOERROR); case SNMP_OP_ROLLBACK: if ((wif = wlan_mesh_get_iface(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); switch (val->var.subs[sub - 1]) { case LEAF_wlanHWMPRootMode: wif->hwmp_root_mode = ctx->scratch->int1; break; case LEAF_wlanHWMPMaxHops: wif->hwmp_max_hops = ctx->scratch->int1; break; default: abort(); } if (wlan_hwmp_config_set(wif, val->var.subs[sub - 1]) < 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); default: abort(); } if (wlan_hwmp_config_get(wif, val->var.subs[sub - 1]) < 0) return (SNMP_ERR_GENERR); switch (val->var.subs[sub - 1]) { case LEAF_wlanHWMPRootMode: val->v.integer = wif->hwmp_root_mode; break; case LEAF_wlanHWMPMaxHops: val->v.integer = wif->hwmp_max_hops; break; default: abort(); } return (SNMP_ERR_NOERROR); } int op_wlan_hwmp_stats(struct snmp_context *ctx __unused, struct snmp_value *val, uint32_t sub, uint32_t iidx __unused, enum snmp_op op) { struct wlan_iface *wif; wlan_update_interface_list(); switch (op) { case SNMP_OP_GET: if ((wif = wlan_mesh_get_iface(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_GETNEXT: if ((wif = wlan_mesh_get_next_iface(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); wlan_append_ifindex(&val->var, sub, wif); break; case SNMP_OP_SET: return (SNMP_ERR_NOT_WRITEABLE); case SNMP_OP_COMMIT: /* FALLTHROUGH */ case SNMP_OP_ROLLBACK: /* FALLTHROUGH */ default: abort(); } if (wlan_get_stats(wif) < 0) return (SNMP_ERR_GENERR); switch (val->var.subs[sub - 1]) { case LEAF_wlanMeshHWMPWrongSeqNo: val->v.uint32 = wif->stats.is_hwmp_wrongseq; break; case LEAF_wlanMeshHWMPTxRootPREQ: val->v.uint32 = wif->stats.is_hwmp_rootreqs; break; case LEAF_wlanMeshHWMPTxRootRANN: val->v.uint32 = wif->stats.is_hwmp_rootrann; break; case LEAF_wlanMeshHWMPProxy: val->v.uint32 = wif->stats.is_hwmp_proxy; break; default: abort(); } return (SNMP_ERR_NOERROR); } /* * Encode BITS type for a response packet - XXX: this belongs to the snmp lib. */ static int bits_get(struct snmp_value *value, const u_char *ptr, ssize_t len) { int size; if (ptr == NULL) { value->v.octetstring.len = 0; value->v.octetstring.octets = NULL; return (SNMP_ERR_NOERROR); } /* Determine length - up to 8 octets supported so far. */ for (size = len; size > 0; size--) if (ptr[size - 1] != 0) break; if (size == 0) size = 1; value->v.octetstring.len = (u_long)size; if ((value->v.octetstring.octets = malloc((size_t)size)) == NULL) return (SNMP_ERR_RES_UNAVAIL); memcpy(value->v.octetstring.octets, ptr, (size_t)size); return (SNMP_ERR_NOERROR); } /* * Calls for adding/updating/freeing/etc of wireless interfaces. */ static void wlan_free_interface(struct wlan_iface *wif) { wlan_free_peerlist(wif); free(wif->chanlist); wlan_scan_free_results(wif); wlan_mac_free_maclist(wif); wlan_mesh_free_routes(wif); free(wif); } static void wlan_free_iflist(void) { struct wlan_iface *w; while ((w = SLIST_FIRST(&wlan_ifaces)) != NULL) { SLIST_REMOVE_HEAD(&wlan_ifaces, w_if); wlan_free_interface(w); } } static struct wlan_iface * wlan_find_interface(const char *wname) { struct wlan_iface *wif; SLIST_FOREACH(wif, &wlan_ifaces, w_if) if (strcmp(wif->wname, wname) == 0) { if (wif->status != RowStatus_active) return (NULL); break; } return (wif); } static struct wlan_iface * wlan_first_interface(void) { return (SLIST_FIRST(&wlan_ifaces)); } static struct wlan_iface * wlan_next_interface(struct wlan_iface *wif) { if (wif == NULL) return (NULL); return (SLIST_NEXT(wif, w_if)); } /* * Add a new interface to the list - sorted by name. */ static int wlan_add_wif(struct wlan_iface *wif) { int cmp; struct wlan_iface *temp, *prev; if ((prev = SLIST_FIRST(&wlan_ifaces)) == NULL || strcmp(wif->wname, prev->wname) < 0) { SLIST_INSERT_HEAD(&wlan_ifaces, wif, w_if); return (0); } SLIST_FOREACH(temp, &wlan_ifaces, w_if) { if ((cmp = strcmp(wif->wname, temp->wname)) <= 0) break; prev = temp; } if (temp == NULL) SLIST_INSERT_AFTER(prev, wif, w_if); else if (cmp > 0) SLIST_INSERT_AFTER(temp, wif, w_if); else { syslog(LOG_ERR, "Wlan iface %s already in list", wif->wname); return (-1); } return (0); } static struct wlan_iface * wlan_new_wif(char *wname) { struct wlan_iface *wif; /* Make sure it's not in the list. */ for (wif = wlan_first_interface(); wif != NULL; wif = wlan_next_interface(wif)) if (strcmp(wname, wif->wname) == 0) { wif->internal = 0; return (wif); } if ((wif = (struct wlan_iface *)malloc(sizeof(*wif))) == NULL) return (NULL); memset(wif, 0, sizeof(struct wlan_iface)); strlcpy(wif->wname, wname, IFNAMSIZ); wif->status = RowStatus_notReady; wif->state = wlanIfaceState_down; wif->mode = WlanIfaceOperatingModeType_station; if (wlan_add_wif(wif) < 0) { free(wif); return (NULL); } return (wif); } static void wlan_delete_wif(struct wlan_iface *wif) { SLIST_REMOVE(&wlan_ifaces, wif, wlan_iface, w_if); wlan_free_interface(wif); } static int wlan_attach_newif(struct mibif *mif) { struct wlan_iface *wif; if (mif->mib.ifmd_data.ifi_type != IFT_ETHER || wlan_check_media(mif->name) != IFM_IEEE80211) return (0); if ((wif = wlan_new_wif(mif->name)) == NULL) return (-1); (void)wlan_get_opmode(wif); wif->index = mif->index; wif->status = RowStatus_active; (void)wlan_update_interface(wif); return (0); } static int wlan_iface_create(struct wlan_iface *wif) { int rc; if ((rc = wlan_clone_create(wif)) == SNMP_ERR_NOERROR) { /* * The rest of the info will be updated once the * snmp_mibII module notifies us of the interface. */ wif->status = RowStatus_active; if (wif->state == wlanIfaceState_up) (void)wlan_config_state(wif, 1); } return (rc); } static int wlan_iface_destroy(struct wlan_iface *wif) { int rc = SNMP_ERR_NOERROR; if (wif->internal == 0) rc = wlan_clone_destroy(wif); if (rc == SNMP_ERR_NOERROR) wlan_delete_wif(wif); return (rc); } static int wlan_update_interface(struct wlan_iface *wif) { int i; (void)wlan_config_state(wif, 0); (void)wlan_get_driver_caps(wif); for (i = LEAF_wlanIfacePacketBurst; i <= LEAF_wlanIfaceTdmaBeaconInterval; i++) (void)wlan_config_get_ioctl(wif, i); (void)wlan_get_stats(wif); /* * XXX: wlan_get_channel_list() not needed - * fetched with wlan_get_driver_caps() */ (void)wlan_get_channel_list(wif); (void)wlan_get_roam_params(wif); (void)wlan_get_tx_params(wif); (void)wlan_get_scan_results(wif); (void)wlan_get_wepmode(wif); (void)wlan_get_weptxkey(wif); (void)wlan_get_mac_policy(wif); (void)wlan_get_mac_acl_macs(wif); (void)wlan_get_peerinfo(wif); if (wif->mode == WlanIfaceOperatingModeType_meshPoint) { for (i = LEAF_wlanMeshTTL; i <= LEAF_wlanMeshPath; i++) (void)wlan_mesh_config_get(wif, i); (void)wlan_mesh_get_routelist(wif); for (i = LEAF_wlanHWMPRootMode; i <= LEAF_wlanHWMPMaxHops; i++) (void)wlan_hwmp_config_get(wif, i); } return (0); } static void wlan_update_interface_list(void) { struct wlan_iface *wif, *twif; if ((time(NULL) - wlan_iflist_age) <= WLAN_LIST_MAXAGE) return; /* * The snmp_mibII module would have notified us for new interfaces, * so only check if any have been deleted. */ SLIST_FOREACH_SAFE(wif, &wlan_ifaces, w_if, twif) if (wif->status == RowStatus_active && wlan_get_opmode(wif) < 0) wlan_delete_wif(wif); wlan_iflist_age = time(NULL); } static void wlan_append_ifindex(struct asn_oid *oid, uint sub, const struct wlan_iface *w) { uint32_t i; oid->len = sub + strlen(w->wname) + 1; oid->subs[sub] = strlen(w->wname); for (i = 1; i <= strlen(w->wname); i++) oid->subs[sub + i] = w->wname[i - 1]; } static uint8_t * wlan_get_ifname(const struct asn_oid *oid, uint sub, uint8_t *wname) { uint32_t i; memset(wname, 0, IFNAMSIZ); if (oid->len - sub != oid->subs[sub] + 1 || oid->subs[sub] >= IFNAMSIZ) return (NULL); for (i = 0; i < oid->subs[sub]; i++) wname[i] = oid->subs[sub + i + 1]; wname[i] = '\0'; return (wname); } static struct wlan_iface * wlan_get_interface(const struct asn_oid *oid, uint sub) { uint8_t wname[IFNAMSIZ]; if (wlan_get_ifname(oid, sub, wname) == NULL) return (NULL); return (wlan_find_interface(wname)); } static struct wlan_iface * wlan_get_next_interface(const struct asn_oid *oid, uint sub) { uint32_t i; uint8_t wname[IFNAMSIZ]; struct wlan_iface *wif; if (oid->len - sub == 0) { for (wif = wlan_first_interface(); wif != NULL; wif = wlan_next_interface(wif)) if (wif->status == RowStatus_active) break; return (wif); } if (oid->len - sub != oid->subs[sub] + 1 || oid->subs[sub] >= IFNAMSIZ) return (NULL); memset(wname, 0, IFNAMSIZ); for (i = 0; i < oid->subs[sub]; i++) wname[i] = oid->subs[sub + i + 1]; wname[i] = '\0'; if ((wif = wlan_find_interface(wname)) == NULL) return (NULL); while ((wif = wlan_next_interface(wif)) != NULL) if (wif->status == RowStatus_active) break; return (wif); } static struct wlan_iface * wlan_get_snmp_interface(const struct asn_oid *oid, uint sub) { uint8_t wname[IFNAMSIZ]; struct wlan_iface *wif; if (wlan_get_ifname(oid, sub, wname) == NULL) return (NULL); for (wif = wlan_first_interface(); wif != NULL; wif = wlan_next_interface(wif)) if (strcmp(wif->wname, wname) == 0) break; return (wif); } static struct wlan_iface * wlan_get_next_snmp_interface(const struct asn_oid *oid, uint sub) { uint32_t i; uint8_t wname[IFNAMSIZ]; struct wlan_iface *wif; if (oid->len - sub == 0) return (wlan_first_interface()); if (oid->len - sub != oid->subs[sub] + 1 || oid->subs[sub] >= IFNAMSIZ) return (NULL); memset(wname, 0, IFNAMSIZ); for (i = 0; i < oid->subs[sub]; i++) wname[i] = oid->subs[sub + i + 1]; wname[i] = '\0'; for (wif = wlan_first_interface(); wif != NULL; wif = wlan_next_interface(wif)) if (strcmp(wif->wname, wname) == 0) break; return (wlan_next_interface(wif)); } /* * Decode/Append an index for tables indexed by the wireless interface * name and a MAC address - ACL MACs and Mesh Routes. */ static int wlan_mac_index_decode(const struct asn_oid *oid, uint sub, char *wname, uint8_t *mac) { uint32_t i; int mac_off; if (oid->len - sub != oid->subs[sub] + 2 + IEEE80211_ADDR_LEN || oid->subs[sub] >= IFNAMSIZ) return (-1); for (i = 0; i < oid->subs[sub]; i++) wname[i] = oid->subs[sub + i + 1]; wname[i] = '\0'; mac_off = sub + oid->subs[sub] + 1; if (oid->subs[mac_off] != IEEE80211_ADDR_LEN) return (-1); for (i = 0; i < IEEE80211_ADDR_LEN; i++) mac[i] = oid->subs[mac_off + i + 1]; return (0); } static void wlan_append_mac_index(struct asn_oid *oid, uint sub, char *wname, uint8_t *mac) { uint32_t i; oid->len = sub + strlen(wname) + IEEE80211_ADDR_LEN + 2; oid->subs[sub] = strlen(wname); for (i = 1; i <= strlen(wname); i++) oid->subs[sub + i] = wname[i - 1]; sub += strlen(wname) + 1; oid->subs[sub] = IEEE80211_ADDR_LEN; for (i = 1; i <= IEEE80211_ADDR_LEN; i++) oid->subs[sub + i] = mac[i - 1]; } /* * Decode/Append an index for tables indexed by the wireless interface * name and the PHY mode - Roam and TX params. */ static int wlan_phy_index_decode(const struct asn_oid *oid, uint sub, char *wname, uint32_t *phy) { uint32_t i; if (oid->len - sub != oid->subs[sub] + 2 || oid->subs[sub] >= IFNAMSIZ) return (-1); for (i = 0; i < oid->subs[sub]; i++) wname[i] = oid->subs[sub + i + 1]; wname[i] = '\0'; *phy = oid->subs[sub + oid->subs[sub] + 1]; return (0); } static void wlan_append_phy_index(struct asn_oid *oid, uint sub, char *wname, uint32_t phy) { uint32_t i; oid->len = sub + strlen(wname) + 2; oid->subs[sub] = strlen(wname); for (i = 1; i <= strlen(wname); i++) oid->subs[sub + i] = wname[i - 1]; oid->subs[sub + strlen(wname) + 1] = phy; } /* * Calls for manipulating the peerlist of a wireless interface. */ static void wlan_free_peerlist(struct wlan_iface *wif) { struct wlan_peer *wip; while ((wip = SLIST_FIRST(&wif->peerlist)) != NULL) { SLIST_REMOVE_HEAD(&wif->peerlist, wp); free(wip); } SLIST_INIT(&wif->peerlist); } static struct wlan_peer * wlan_find_peer(struct wlan_iface *wif, uint8_t *peermac) { struct wlan_peer *wip; SLIST_FOREACH(wip, &wif->peerlist, wp) if (memcmp(wip->pmac, peermac, IEEE80211_ADDR_LEN) == 0) break; return (wip); } struct wlan_peer * wlan_new_peer(const uint8_t *pmac) { struct wlan_peer *wip; if ((wip = (struct wlan_peer *)malloc(sizeof(*wip))) == NULL) return (NULL); memset(wip, 0, sizeof(struct wlan_peer)); memcpy(wip->pmac, pmac, IEEE80211_ADDR_LEN); return (wip); } void wlan_free_peer(struct wlan_peer *wip) { free(wip); } int wlan_add_peer(struct wlan_iface *wif, struct wlan_peer *wip) { struct wlan_peer *temp, *prev; SLIST_FOREACH(temp, &wif->peerlist, wp) if (memcmp(temp->pmac, wip->pmac, IEEE80211_ADDR_LEN) == 0) return (-1); if ((prev = SLIST_FIRST(&wif->peerlist)) == NULL || memcmp(wip->pmac, prev->pmac, IEEE80211_ADDR_LEN) < 0) { SLIST_INSERT_HEAD(&wif->peerlist, wip, wp); return (0); } SLIST_FOREACH(temp, &wif->peerlist, wp) { if (memcmp(wip->pmac, temp->pmac, IEEE80211_ADDR_LEN) < 0) break; prev = temp; } SLIST_INSERT_AFTER(prev, wip, wp); return (0); } static void wlan_update_peers(void) { struct wlan_iface *wif; if ((time(NULL) - wlan_peerlist_age) <= WLAN_LIST_MAXAGE) return; for (wif = wlan_first_interface(); wif != NULL; wif = wlan_next_interface(wif)) { if (wif->status != RowStatus_active) continue; wlan_free_peerlist(wif); (void)wlan_get_peerinfo(wif); } wlan_peerlist_age = time(NULL); } static struct wlan_peer * wlan_get_peer(const struct asn_oid *oid, uint sub, struct wlan_iface **wif) { char wname[IFNAMSIZ]; uint8_t pmac[IEEE80211_ADDR_LEN]; if (wlan_mac_index_decode(oid, sub, wname, pmac) < 0) return (NULL); if ((*wif = wlan_find_interface(wname)) == NULL) return (NULL); return (wlan_find_peer(*wif, pmac)); } static struct wlan_peer * wlan_get_next_peer(const struct asn_oid *oid, uint sub, struct wlan_iface **wif) { char wname[IFNAMSIZ]; char pmac[IEEE80211_ADDR_LEN]; struct wlan_peer *wip; if (oid->len - sub == 0) { for (*wif = wlan_first_interface(); *wif != NULL; *wif = wlan_next_interface(*wif)) { if ((*wif)->mode == WlanIfaceOperatingModeType_meshPoint) continue; wip = SLIST_FIRST(&(*wif)->peerlist); if (wip != NULL) return (wip); } return (NULL); } if (wlan_mac_index_decode(oid, sub, wname, pmac) < 0 || (*wif = wlan_find_interface(wname)) == NULL || (wip = wlan_find_peer(*wif, pmac)) == NULL) return (NULL); if ((wip = SLIST_NEXT(wip, wp)) != NULL) return (wip); while ((*wif = wlan_next_interface(*wif)) != NULL) { if ((*wif)->mode == WlanIfaceOperatingModeType_meshPoint) continue; if ((wip = SLIST_FIRST(&(*wif)->peerlist)) != NULL) break; } return (wip); } /* * Calls for manipulating the active channel list of a wireless interface. */ static void wlan_update_channels(void) { struct wlan_iface *wif; if ((time(NULL) - wlan_chanlist_age) <= WLAN_LIST_MAXAGE) return; for (wif = wlan_first_interface(); wif != NULL; wif = wlan_next_interface(wif)) { if (wif->status != RowStatus_active) continue; (void)wlan_get_channel_list(wif); } wlan_chanlist_age = time(NULL); } static int wlan_channel_index_decode(const struct asn_oid *oid, uint sub, char *wname, uint32_t *cindex) { uint32_t i; if (oid->len - sub != oid->subs[sub] + 2 || oid->subs[sub] >= IFNAMSIZ) return (-1); for (i = 0; i < oid->subs[sub]; i++) wname[i] = oid->subs[sub + i + 1]; wname[i] = '\0'; *cindex = oid->subs[sub + oid->subs[sub] + 1]; return (0); } static void wlan_append_channel_index(struct asn_oid *oid, uint sub, const struct wlan_iface *wif, const struct ieee80211_channel *channel) { uint32_t i; oid->len = sub + strlen(wif->wname) + 2; oid->subs[sub] = strlen(wif->wname); for (i = 1; i <= strlen(wif->wname); i++) oid->subs[sub + i] = wif->wname[i - 1]; oid->subs[sub + strlen(wif->wname) + 1] = (channel - wif->chanlist) + 1; } static int32_t wlan_get_channel_type(struct ieee80211_channel *c) { if (IEEE80211_IS_CHAN_FHSS(c)) return (WlanChannelType_fhss); if (IEEE80211_IS_CHAN_A(c)) return (WlanChannelType_dot11a); if (IEEE80211_IS_CHAN_B(c)) return (WlanChannelType_dot11b); if (IEEE80211_IS_CHAN_ANYG(c)) return (WlanChannelType_dot11g); if (IEEE80211_IS_CHAN_HALF(c)) return (WlanChannelType_tenMHz); if (IEEE80211_IS_CHAN_QUARTER(c)) return (WlanChannelType_fiveMHz); if (IEEE80211_IS_CHAN_TURBO(c)) return (WlanChannelType_turbo); if (IEEE80211_IS_CHAN_HT(c)) return (WlanChannelType_ht); return (-1); } static struct ieee80211_channel * wlan_find_channel(struct wlan_iface *wif, uint32_t cindex) { if (wif->chanlist == NULL || cindex > wif->nchannels) return (NULL); return (wif->chanlist + cindex - 1); } static struct ieee80211_channel * wlan_get_channel(const struct asn_oid *oid, uint sub, struct wlan_iface **wif) { uint32_t cindex; char wname[IFNAMSIZ]; if (wlan_channel_index_decode(oid, sub, wname, &cindex) < 0) return (NULL); if ((*wif = wlan_find_interface(wname)) == NULL) return (NULL); return (wlan_find_channel(*wif, cindex)); } static struct ieee80211_channel * wlan_get_next_channel(const struct asn_oid *oid, uint sub, struct wlan_iface **wif) { uint32_t cindex; char wname[IFNAMSIZ]; if (oid->len - sub == 0) { for (*wif = wlan_first_interface(); *wif != NULL; *wif = wlan_next_interface(*wif)) { if ((*wif)->status != RowStatus_active) continue; if ((*wif)->nchannels != 0 && (*wif)->chanlist != NULL) return ((*wif)->chanlist); } return (NULL); } if (wlan_channel_index_decode(oid, sub, wname, &cindex) < 0) return (NULL); if ((*wif = wlan_find_interface(wname)) == NULL) return (NULL); if (cindex < (*wif)->nchannels) return ((*wif)->chanlist + cindex); while ((*wif = wlan_next_interface(*wif)) != NULL) if ((*wif)->status == RowStatus_active) if ((*wif)->nchannels != 0 && (*wif)->chanlist != NULL) return ((*wif)->chanlist); return (NULL); } /* * Calls for manipulating the roam params of a wireless interface. */ static void wlan_update_roam_params(void) { struct wlan_iface *wif; if ((time(NULL) - wlan_roamlist_age) <= WLAN_LIST_MAXAGE) return; for (wif = wlan_first_interface(); wif != NULL; wif = wlan_next_interface(wif)) { if (wif->status != RowStatus_active) continue; (void)wlan_get_roam_params(wif); } wlan_roamlist_age = time(NULL); } static struct ieee80211_roamparam * wlan_get_roam_param(const struct asn_oid *oid, uint sub, struct wlan_iface **wif) { uint32_t phy; char wname[IFNAMSIZ]; if (wlan_phy_index_decode(oid, sub, wname, &phy) < 0) return (NULL); if ((*wif = wlan_find_interface(wname)) == NULL) return (NULL); if (phy == 0 || phy > IEEE80211_MODE_MAX) return (NULL); return ((*wif)->roamparams.params + phy - 1); } static struct ieee80211_roamparam * wlan_get_next_roam_param(const struct asn_oid *oid, uint sub, struct wlan_iface **wif, uint32_t *phy) { char wname[IFNAMSIZ]; if (oid->len - sub == 0) { for (*wif = wlan_first_interface(); *wif != NULL; *wif = wlan_next_interface(*wif)) { if ((*wif)->status != RowStatus_active) continue; *phy = 1; return ((*wif)->roamparams.params); } return (NULL); } if (wlan_phy_index_decode(oid, sub, wname, phy) < 0) return (NULL); if (*phy == 0 || (*wif = wlan_find_interface(wname)) == NULL) return (NULL); if (++(*phy) <= IEEE80211_MODE_MAX) return ((*wif)->roamparams.params + *phy - 1); *phy = 1; while ((*wif = wlan_next_interface(*wif)) != NULL) if ((*wif)->status == RowStatus_active) return ((*wif)->roamparams.params); return (NULL); } /* * Calls for manipulating the tx params of a wireless interface. */ static void wlan_update_tx_params(void) { struct wlan_iface *wif; if ((time(NULL) - wlan_tx_paramlist_age) <= WLAN_LIST_MAXAGE) return; for (wif = wlan_first_interface(); wif != NULL; wif = wlan_next_interface(wif)) { if (wif->status != RowStatus_active) continue; (void)wlan_get_tx_params(wif); } wlan_tx_paramlist_age = time(NULL); } static struct ieee80211_txparam * wlan_get_tx_param(const struct asn_oid *oid, uint sub, struct wlan_iface **wif, uint32_t *phy) { char wname[IFNAMSIZ]; if (wlan_phy_index_decode(oid, sub, wname, phy) < 0) return (NULL); if ((*wif = wlan_find_interface(wname)) == NULL) return (NULL); if (*phy == 0 || *phy > IEEE80211_MODE_MAX) return (NULL); return ((*wif)->txparams.params + *phy - 1); } static struct ieee80211_txparam * wlan_get_next_tx_param(const struct asn_oid *oid, uint sub, struct wlan_iface **wif, uint32_t *phy) { char wname[IFNAMSIZ]; if (oid->len - sub == 0) { for (*wif = wlan_first_interface(); *wif != NULL; *wif = wlan_next_interface(*wif)) { if ((*wif)->status != RowStatus_active) continue; *phy = 1; return ((*wif)->txparams.params); } return (NULL); } if (wlan_phy_index_decode(oid, sub, wname, phy) < 0) return (NULL); if (*phy == 0 || (*wif = wlan_find_interface(wname)) == NULL) return (NULL); if (++(*phy) <= IEEE80211_MODE_MAX) return ((*wif)->txparams.params + *phy - 1); *phy = 1; while ((*wif = wlan_next_interface(*wif)) != NULL) if ((*wif)->status == RowStatus_active) return ((*wif)->txparams.params); return (NULL); } /* * Calls for manipulating the scan results for a wireless interface. */ static void wlan_scan_free_results(struct wlan_iface *wif) { struct wlan_scan_result *sr; while ((sr = SLIST_FIRST(&wif->scanlist)) != NULL) { SLIST_REMOVE_HEAD(&wif->scanlist, wsr); free(sr); } SLIST_INIT(&wif->scanlist); } static struct wlan_scan_result * wlan_scan_find_result(struct wlan_iface *wif, uint8_t *ssid, uint8_t *bssid) { struct wlan_scan_result *sr; SLIST_FOREACH(sr, &wif->scanlist, wsr) if (strlen(ssid) == strlen(sr->ssid) && strcmp(sr->ssid, ssid) == 0 && memcmp(sr->bssid, bssid, IEEE80211_ADDR_LEN) == 0) break; return (sr); } struct wlan_scan_result * wlan_scan_new_result(const uint8_t *ssid, const uint8_t *bssid) { struct wlan_scan_result *sr; sr = (struct wlan_scan_result *)malloc(sizeof(*sr)); if (sr == NULL) return (NULL); memset(sr, 0, sizeof(*sr)); if (ssid[0] != '\0') strlcpy(sr->ssid, ssid, IEEE80211_NWID_LEN + 1); memcpy(sr->bssid, bssid, IEEE80211_ADDR_LEN); return (sr); } void wlan_scan_free_result(struct wlan_scan_result *sr) { free(sr); } static int wlan_scan_compare_result(struct wlan_scan_result *sr1, struct wlan_scan_result *sr2) { uint32_t i; if (strlen(sr1->ssid) < strlen(sr2->ssid)) return (-1); if (strlen(sr1->ssid) > strlen(sr2->ssid)) return (1); for (i = 0; i < strlen(sr1->ssid) && i < strlen(sr2->ssid); i++) { if (sr1->ssid[i] < sr2->ssid[i]) return (-1); if (sr1->ssid[i] > sr2->ssid[i]) return (1); } for (i = 0; i < IEEE80211_ADDR_LEN; i++) { if (sr1->bssid[i] < sr2->bssid[i]) return (-1); if (sr1->bssid[i] > sr2->bssid[i]) return (1); } return (0); } int wlan_scan_add_result(struct wlan_iface *wif, struct wlan_scan_result *sr) { struct wlan_scan_result *prev, *temp; SLIST_FOREACH(temp, &wif->scanlist, wsr) if (strlen(temp->ssid) == strlen(sr->ssid) && strcmp(sr->ssid, temp->ssid) == 0 && memcmp(sr->bssid, temp->bssid, IEEE80211_ADDR_LEN) == 0) return (-1); if ((prev = SLIST_FIRST(&wif->scanlist)) == NULL || wlan_scan_compare_result(sr, prev) < 0) { SLIST_INSERT_HEAD(&wif->scanlist, sr, wsr); return (0); } SLIST_FOREACH(temp, &wif->scanlist, wsr) { if (wlan_scan_compare_result(sr, temp) < 0) break; prev = temp; } SLIST_INSERT_AFTER(prev, sr, wsr); return (0); } static void wlan_scan_update_results(void) { struct wlan_iface *wif; if ((time(NULL) - wlan_scanlist_age) <= WLAN_LIST_MAXAGE) return; for (wif = wlan_first_interface(); wif != NULL; wif = wlan_next_interface(wif)) { if (wif->status != RowStatus_active) continue; wlan_scan_free_results(wif); (void)wlan_get_scan_results(wif); } wlan_scanlist_age = time(NULL); } static int wlan_scanr_index_decode(const struct asn_oid *oid, uint sub, char *wname, uint8_t *ssid, uint8_t *bssid) { uint32_t i; int offset; if (oid->subs[sub] >= IFNAMSIZ) return (-1); for (i = 0; i < oid->subs[sub]; i++) wname[i] = oid->subs[sub + i + 1]; wname[oid->subs[sub]] = '\0'; offset = sub + oid->subs[sub] + 1; if (oid->subs[offset] > IEEE80211_NWID_LEN) return (-1); for (i = 0; i < oid->subs[offset]; i++) ssid[i] = oid->subs[offset + i + 1]; ssid[i] = '\0'; offset = sub + oid->subs[sub] + oid->subs[offset] + 2; if (oid->subs[offset] != IEEE80211_ADDR_LEN) return (-1); for (i = 0; i < IEEE80211_ADDR_LEN; i++) bssid[i] = oid->subs[offset + i + 1]; return (0); } static void wlan_append_scanr_index(struct asn_oid *oid, uint sub, char *wname, uint8_t *ssid, uint8_t *bssid) { uint32_t i; oid->len = sub + strlen(wname) + strlen(ssid) + IEEE80211_ADDR_LEN + 3; oid->subs[sub] = strlen(wname); for (i = 1; i <= strlen(wname); i++) oid->subs[sub + i] = wname[i - 1]; sub += strlen(wname) + 1; oid->subs[sub] = strlen(ssid); for (i = 1; i <= strlen(ssid); i++) oid->subs[sub + i] = ssid[i - 1]; sub += strlen(ssid) + 1; oid->subs[sub] = IEEE80211_ADDR_LEN; for (i = 1; i <= IEEE80211_ADDR_LEN; i++) oid->subs[sub + i] = bssid[i - 1]; } static struct wlan_scan_result * wlan_get_scanr(const struct asn_oid *oid, uint sub, struct wlan_iface **wif) { char wname[IFNAMSIZ]; uint8_t ssid[IEEE80211_NWID_LEN + 1]; uint8_t bssid[IEEE80211_ADDR_LEN]; if (wlan_scanr_index_decode(oid, sub, wname, ssid, bssid) < 0) return (NULL); if ((*wif = wlan_find_interface(wname)) == NULL) return (NULL); return (wlan_scan_find_result(*wif, ssid, bssid)); } static struct wlan_scan_result * wlan_get_next_scanr(const struct asn_oid *oid, uint sub, struct wlan_iface **wif) { char wname[IFNAMSIZ]; uint8_t ssid[IEEE80211_NWID_LEN + 1]; uint8_t bssid[IEEE80211_ADDR_LEN]; struct wlan_scan_result *sr; if (oid->len - sub == 0) { for (*wif = wlan_first_interface(); *wif != NULL; *wif = wlan_next_interface(*wif)) { sr = SLIST_FIRST(&(*wif)->scanlist); if (sr != NULL) return (sr); } return (NULL); } if (wlan_scanr_index_decode(oid, sub, wname, ssid, bssid) < 0 || (*wif = wlan_find_interface(wname)) == NULL || (sr = wlan_scan_find_result(*wif, ssid, bssid)) == NULL) return (NULL); if ((sr = SLIST_NEXT(sr, wsr)) != NULL) return (sr); while ((*wif = wlan_next_interface(*wif)) != NULL) if ((sr = SLIST_FIRST(&(*wif)->scanlist)) != NULL) break; return (sr); } /* * MAC Access Control. */ static void wlan_mac_free_maclist(struct wlan_iface *wif) { struct wlan_mac_mac *wmm; while ((wmm = SLIST_FIRST(&wif->mac_maclist)) != NULL) { SLIST_REMOVE_HEAD(&wif->mac_maclist, wm); free(wmm); } SLIST_INIT(&wif->mac_maclist); } static struct wlan_mac_mac * wlan_mac_find_mac(struct wlan_iface *wif, uint8_t *mac) { struct wlan_mac_mac *wmm; SLIST_FOREACH(wmm, &wif->mac_maclist, wm) if (memcmp(wmm->mac, mac, IEEE80211_ADDR_LEN) == 0) break; return (wmm); } struct wlan_mac_mac * wlan_mac_new_mac(const uint8_t *mac) { struct wlan_mac_mac *wmm; if ((wmm = (struct wlan_mac_mac *)malloc(sizeof(*wmm))) == NULL) return (NULL); memset(wmm, 0, sizeof(*wmm)); memcpy(wmm->mac, mac, IEEE80211_ADDR_LEN); wmm->mac_status = RowStatus_notReady; return (wmm); } void wlan_mac_free_mac(struct wlan_mac_mac *wmm) { free(wmm); } int wlan_mac_add_mac(struct wlan_iface *wif, struct wlan_mac_mac *wmm) { struct wlan_mac_mac *temp, *prev; SLIST_FOREACH(temp, &wif->mac_maclist, wm) if (memcmp(temp->mac, wmm->mac, IEEE80211_ADDR_LEN) == 0) return (-1); if ((prev = SLIST_FIRST(&wif->mac_maclist)) == NULL || memcmp(wmm->mac, prev->mac,IEEE80211_ADDR_LEN) < 0) { SLIST_INSERT_HEAD(&wif->mac_maclist, wmm, wm); return (0); } SLIST_FOREACH(temp, &wif->mac_maclist, wm) { if (memcmp(wmm->mac, temp->mac, IEEE80211_ADDR_LEN) < 0) break; prev = temp; } SLIST_INSERT_AFTER(prev, wmm, wm); return (0); } static int wlan_mac_delete_mac(struct wlan_iface *wif, struct wlan_mac_mac *wmm) { if (wmm->mac_status == RowStatus_active && wlan_del_mac_acl_mac(wif, wmm) < 0) return (-1); SLIST_REMOVE(&wif->mac_maclist, wmm, wlan_mac_mac, wm); free(wmm); return (0); } static void wlan_mac_update_aclmacs(void) { struct wlan_iface *wif; struct wlan_mac_mac *wmm, *twmm; if ((time(NULL) - wlan_maclist_age) <= WLAN_LIST_MAXAGE) return; for (wif = wlan_first_interface(); wif != NULL; wif = wlan_next_interface(wif)) { if (wif->status != RowStatus_active) continue; /* * Nuke old entries - XXX - they are likely not to * change often - reconsider. */ SLIST_FOREACH_SAFE(wmm, &wif->mac_maclist, wm, twmm) if (wmm->mac_status == RowStatus_active) { SLIST_REMOVE(&wif->mac_maclist, wmm, wlan_mac_mac, wm); wlan_mac_free_mac(wmm); } (void)wlan_get_mac_acl_macs(wif); } wlan_maclist_age = time(NULL); } static struct wlan_mac_mac * wlan_get_acl_mac(const struct asn_oid *oid, uint sub, struct wlan_iface **wif) { char wname[IFNAMSIZ]; char mac[IEEE80211_ADDR_LEN]; if (wlan_mac_index_decode(oid, sub, wname, mac) < 0) return (NULL); if ((*wif = wlan_find_interface(wname)) == NULL) return (NULL); return (wlan_mac_find_mac(*wif, mac)); } static struct wlan_mac_mac * wlan_get_next_acl_mac(const struct asn_oid *oid, uint sub, struct wlan_iface **wif) { char wname[IFNAMSIZ]; char mac[IEEE80211_ADDR_LEN]; struct wlan_mac_mac *wmm; if (oid->len - sub == 0) { for (*wif = wlan_first_interface(); *wif != NULL; *wif = wlan_next_interface(*wif)) { wmm = SLIST_FIRST(&(*wif)->mac_maclist); if (wmm != NULL) return (wmm); } return (NULL); } if (wlan_mac_index_decode(oid, sub, wname, mac) < 0 || (*wif = wlan_find_interface(wname)) == NULL || (wmm = wlan_mac_find_mac(*wif, mac)) == NULL) return (NULL); if ((wmm = SLIST_NEXT(wmm, wm)) != NULL) return (wmm); while ((*wif = wlan_next_interface(*wif)) != NULL) if ((wmm = SLIST_FIRST(&(*wif)->mac_maclist)) != NULL) break; return (wmm); } static int wlan_acl_mac_set_status(struct snmp_context *ctx, struct snmp_value *val, uint sub) { char wname[IFNAMSIZ]; uint8_t mac[IEEE80211_ADDR_LEN]; struct wlan_iface *wif; struct wlan_mac_mac *macl; if (wlan_mac_index_decode(&val->var, sub, wname, mac) < 0) return (SNMP_ERR_GENERR); macl = wlan_get_acl_mac(&val->var, sub, &wif); switch (val->v.integer) { case RowStatus_createAndGo: if (macl != NULL) return (SNMP_ERR_INCONS_NAME); break; case RowStatus_destroy: if (macl == NULL) return (SNMP_ERR_NOSUCHNAME); ctx->scratch->int1 = RowStatus_active; return (SNMP_ERR_NOERROR); default: return (SNMP_ERR_INCONS_VALUE); } if (wif == NULL || !wif->macsupported) return (SNMP_ERR_INCONS_VALUE); if ((macl = wlan_mac_new_mac((const uint8_t *)mac)) == NULL) return (SNMP_ERR_GENERR); ctx->scratch->int1 = RowStatus_destroy; if (wlan_mac_add_mac(wif, macl) < 0) { wlan_mac_free_mac(macl); return (SNMP_ERR_GENERR); } ctx->scratch->int1 = RowStatus_destroy; if (wlan_add_mac_acl_mac(wif, macl) < 0) { (void)wlan_mac_delete_mac(wif, macl); return (SNMP_ERR_GENERR); } return (SNMP_ERR_NOERROR); } /* * Wireless interfaces operating as mesh points. */ static struct wlan_iface * wlan_mesh_first_interface(void) { struct wlan_iface *wif; SLIST_FOREACH(wif, &wlan_ifaces, w_if) if (wif->mode == WlanIfaceOperatingModeType_meshPoint && wif->status == RowStatus_active) break; return (wif); } static struct wlan_iface * wlan_mesh_next_interface(struct wlan_iface *wif) { struct wlan_iface *nwif; while ((nwif = wlan_next_interface(wif)) != NULL) { if (nwif->mode == WlanIfaceOperatingModeType_meshPoint && nwif->status == RowStatus_active) break; wif = nwif; } return (nwif); } static struct wlan_iface * wlan_mesh_get_iface(const struct asn_oid *oid, uint sub) { struct wlan_iface *wif; if ((wif = wlan_get_interface(oid, sub)) == NULL) return (NULL); if (wif->mode != WlanIfaceOperatingModeType_meshPoint) return (NULL); return (wif); } static struct wlan_iface * wlan_mesh_get_next_iface(const struct asn_oid *oid, uint sub) { uint32_t i; uint8_t wname[IFNAMSIZ]; struct wlan_iface *wif; if (oid->len - sub == 0) return (wlan_mesh_first_interface()); if (oid->len - sub != oid->subs[sub] + 1 || oid->subs[sub] >= IFNAMSIZ) return (NULL); memset(wname, 0, IFNAMSIZ); for (i = 0; i < oid->subs[sub]; i++) wname[i] = oid->subs[sub + i + 1]; wname[i] = '\0'; if ((wif = wlan_find_interface(wname)) == NULL) return (NULL); return (wlan_mesh_next_interface(wif)); } /* * The neighbors of wireless interfaces operating as mesh points. */ static struct wlan_peer * wlan_mesh_get_peer(const struct asn_oid *oid, uint sub, struct wlan_iface **wif) { char wname[IFNAMSIZ]; uint8_t pmac[IEEE80211_ADDR_LEN]; if (wlan_mac_index_decode(oid, sub, wname, pmac) < 0) return (NULL); if ((*wif = wlan_find_interface(wname)) == NULL || (*wif)->mode != WlanIfaceOperatingModeType_meshPoint) return (NULL); return (wlan_find_peer(*wif, pmac)); } static struct wlan_peer * wlan_mesh_get_next_peer(const struct asn_oid *oid, uint sub, struct wlan_iface **wif) { char wname[IFNAMSIZ]; char pmac[IEEE80211_ADDR_LEN]; struct wlan_peer *wip; if (oid->len - sub == 0) { for (*wif = wlan_mesh_first_interface(); *wif != NULL; *wif = wlan_mesh_next_interface(*wif)) { wip = SLIST_FIRST(&(*wif)->peerlist); if (wip != NULL) return (wip); } return (NULL); } if (wlan_mac_index_decode(oid, sub, wname, pmac) < 0 || (*wif = wlan_find_interface(wname)) == NULL || (*wif)->mode != WlanIfaceOperatingModeType_meshPoint || (wip = wlan_find_peer(*wif, pmac)) == NULL) return (NULL); if ((wip = SLIST_NEXT(wip, wp)) != NULL) return (wip); while ((*wif = wlan_mesh_next_interface(*wif)) != NULL) if ((wip = SLIST_FIRST(&(*wif)->peerlist)) != NULL) break; return (wip); } /* * Mesh routing table. */ static void wlan_mesh_free_routes(struct wlan_iface *wif) { struct wlan_mesh_route *wmr; while ((wmr = SLIST_FIRST(&wif->mesh_routelist)) != NULL) { SLIST_REMOVE_HEAD(&wif->mesh_routelist, wr); free(wmr); } SLIST_INIT(&wif->mesh_routelist); } static struct wlan_mesh_route * wlan_mesh_find_route(struct wlan_iface *wif, uint8_t *dstmac) { struct wlan_mesh_route *wmr; if (wif->mode != WlanIfaceOperatingModeType_meshPoint) return (NULL); SLIST_FOREACH(wmr, &wif->mesh_routelist, wr) if (memcmp(wmr->imroute.imr_dest, dstmac, IEEE80211_ADDR_LEN) == 0) break; return (wmr); } struct wlan_mesh_route * wlan_mesh_new_route(const uint8_t *dstmac) { struct wlan_mesh_route *wmr; if ((wmr = (struct wlan_mesh_route *)malloc(sizeof(*wmr))) == NULL) return (NULL); memset(wmr, 0, sizeof(*wmr)); memcpy(wmr->imroute.imr_dest, dstmac, IEEE80211_ADDR_LEN); wmr->mroute_status = RowStatus_notReady; return (wmr); } void wlan_mesh_free_route(struct wlan_mesh_route *wmr) { free(wmr); } int wlan_mesh_add_rtentry(struct wlan_iface *wif, struct wlan_mesh_route *wmr) { struct wlan_mesh_route *temp, *prev; SLIST_FOREACH(temp, &wif->mesh_routelist, wr) if (memcmp(temp->imroute.imr_dest, wmr->imroute.imr_dest, IEEE80211_ADDR_LEN) == 0) return (-1); if ((prev = SLIST_FIRST(&wif->mesh_routelist)) == NULL || memcmp(wmr->imroute.imr_dest, prev->imroute.imr_dest, IEEE80211_ADDR_LEN) < 0) { SLIST_INSERT_HEAD(&wif->mesh_routelist, wmr, wr); return (0); } SLIST_FOREACH(temp, &wif->mesh_routelist, wr) { if (memcmp(wmr->imroute.imr_dest, temp->imroute.imr_dest, IEEE80211_ADDR_LEN) < 0) break; prev = temp; } SLIST_INSERT_AFTER(prev, wmr, wr); return (0); } static int wlan_mesh_delete_route(struct wlan_iface *wif, struct wlan_mesh_route *wmr) { if (wmr->mroute_status == RowStatus_active && wlan_mesh_del_route(wif, wmr) < 0) return (-1); SLIST_REMOVE(&wif->mesh_routelist, wmr, wlan_mesh_route, wr); free(wmr); return (0); } static void wlan_mesh_update_routes(void) { struct wlan_iface *wif; struct wlan_mesh_route *wmr, *twmr; if ((time(NULL) - wlan_mrlist_age) <= WLAN_LIST_MAXAGE) return; for (wif = wlan_mesh_first_interface(); wif != NULL; wif = wlan_mesh_next_interface(wif)) { /* * Nuke old entries - XXX - they are likely not to * change often - reconsider. */ SLIST_FOREACH_SAFE(wmr, &wif->mesh_routelist, wr, twmr) if (wmr->mroute_status == RowStatus_active) { SLIST_REMOVE(&wif->mesh_routelist, wmr, wlan_mesh_route, wr); wlan_mesh_free_route(wmr); } (void)wlan_mesh_get_routelist(wif); } wlan_mrlist_age = time(NULL); } static struct wlan_mesh_route * wlan_mesh_get_route(const struct asn_oid *oid, uint sub, struct wlan_iface **wif) { char wname[IFNAMSIZ]; char dstmac[IEEE80211_ADDR_LEN]; if (wlan_mac_index_decode(oid, sub, wname, dstmac) < 0) return (NULL); if ((*wif = wlan_find_interface(wname)) == NULL) return (NULL); return (wlan_mesh_find_route(*wif, dstmac)); } static struct wlan_mesh_route * wlan_mesh_get_next_route(const struct asn_oid *oid, uint sub, struct wlan_iface **wif) { char wname[IFNAMSIZ]; char dstmac[IEEE80211_ADDR_LEN]; struct wlan_mesh_route *wmr; if (oid->len - sub == 0) { for (*wif = wlan_mesh_first_interface(); *wif != NULL; *wif = wlan_mesh_next_interface(*wif)) { wmr = SLIST_FIRST(&(*wif)->mesh_routelist); if (wmr != NULL) return (wmr); } return (NULL); } if (wlan_mac_index_decode(oid, sub, wname, dstmac) < 0 || (*wif = wlan_find_interface(wname)) == NULL || (wmr = wlan_mesh_find_route(*wif, dstmac)) == NULL) return (NULL); if ((wmr = SLIST_NEXT(wmr, wr)) != NULL) return (wmr); while ((*wif = wlan_mesh_next_interface(*wif)) != NULL) if ((wmr = SLIST_FIRST(&(*wif)->mesh_routelist)) != NULL) break; return (wmr); } static int wlan_mesh_route_set_status(struct snmp_context *ctx, struct snmp_value *val, uint sub) { char wname[IFNAMSIZ]; char mac[IEEE80211_ADDR_LEN]; struct wlan_mesh_route *wmr; struct wlan_iface *wif; if (wlan_mac_index_decode(&val->var, sub, wname, mac) < 0) return (SNMP_ERR_GENERR); wmr = wlan_mesh_get_route(&val->var, sub, &wif); switch (val->v.integer) { case RowStatus_createAndGo: if (wmr != NULL) return (SNMP_ERR_INCONS_NAME); break; case RowStatus_destroy: if (wmr == NULL) return (SNMP_ERR_NOSUCHNAME); ctx->scratch->int1 = RowStatus_active; return (SNMP_ERR_NOERROR); default: return (SNMP_ERR_INCONS_VALUE); } if ((wif = wlan_find_interface(wname)) == NULL) return (SNMP_ERR_INCONS_NAME); if ((wmr = wlan_mesh_new_route(mac)) == NULL) return (SNMP_ERR_GENERR); if (wlan_mesh_add_rtentry(wif, wmr) < 0) { wlan_mesh_free_route(wmr); return (SNMP_ERR_GENERR); } ctx->scratch->int1 = RowStatus_destroy; if (wlan_mesh_add_route(wif, wmr) < 0) { (void)wlan_mesh_delete_route(wif, wmr); return (SNMP_ERR_GENERR); } return (SNMP_ERR_NOERROR); } /* * Wlan snmp module initialization hook. * Returns 0 on success, < 0 on error. */ static int wlan_init(struct lmodule * mod __unused, int argc __unused, char *argv[] __unused) { if (wlan_kmodules_load() < 0) return (-1); if (wlan_ioctl_init() < 0) return (-1); /* Register for new interface creation notifications. */ if (mib_register_newif(wlan_attach_newif, wlan_module)) { syslog(LOG_ERR, "Cannot register newif function: %s", strerror(errno)); return (-1); } return (0); } /* * Wlan snmp module finalization hook. */ static int wlan_fini(void) { mib_unregister_newif(wlan_module); or_unregister(reg_wlan); /* XXX: Cleanup! */ wlan_free_iflist(); return (0); } /* * Refetch all available data from the kernel. */ static void wlan_update_data(void *arg __unused) { } /* * Wlan snmp module start operation. */ static void wlan_start(void) { struct mibif *ifp; reg_wlan = or_register(&oid_wlan, "The MIB module for managing wireless networking.", wlan_module); /* Add the existing wlan interfaces. */ for (ifp = mib_first_if(); ifp != NULL; ifp = mib_next_if(ifp)) wlan_attach_newif(ifp); wlan_data_timer = timer_start_repeat(wlan_poll_ticks, wlan_poll_ticks, wlan_update_data, NULL, wlan_module); } /* * Dump the Wlan snmp module data on SIGUSR1. */ static void wlan_dump(void) { /* XXX: Print some debug info to syslog. */ struct wlan_iface *wif; for (wif = wlan_first_interface(); wif != NULL; wif = wlan_next_interface(wif)) syslog(LOG_ERR, "wlan iface %s", wif->wname); } const char wlan_comment[] = \ "This module implements the BEGEMOT MIB for wireless networking."; const struct snmp_module config = { .comment = wlan_comment, .init = wlan_init, .fini = wlan_fini, .start = wlan_start, .tree = wlan_ctree, .dump = wlan_dump, .tree_size = wlan_CTREE_SIZE, }; Index: head/usr.sbin/bsnmpd/modules/snmp_wlan/wlan_sys.c =================================================================== --- head/usr.sbin/bsnmpd/modules/snmp_wlan/wlan_sys.c (revision 335884) +++ head/usr.sbin/bsnmpd/modules/snmp_wlan/wlan_sys.c (revision 335885) @@ -1,3147 +1,3148 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2010 The FreeBSD Foundation * All rights reserved. * This software was developed by Shteryana Sotirova Shopova under * sponsorship from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#define SNMPTREE_TYPES #include "wlan_tree.h" #include "wlan_snmp.h" static int sock = -1; static int wlan_ioctl(char *, uint16_t, int *, void *, size_t *, int); static int wlan_kmod_load(const char *); static uint32_t wlan_drivercaps_to_snmp(uint32_t); static uint32_t wlan_cryptocaps_to_snmp(uint32_t); static uint32_t wlan_htcaps_to_snmp(uint32_t); static uint32_t wlan_peerstate_to_snmp(uint32_t); static uint32_t wlan_peercaps_to_snmp(uint32_t ); static uint32_t wlan_channel_flags_to_snmp_phy(uint32_t); static uint32_t wlan_regdomain_to_snmp(int); static uint32_t wlan_snmp_to_scan_flags(int); static int wlan_config_snmp2ioctl(int); static int wlan_snmp_to_regdomain(enum WlanRegDomainCode); static int wlan_config_get_country(struct wlan_iface *); static int wlan_config_set_country(struct wlan_iface *, char *, int); static int wlan_config_get_dchannel(struct wlan_iface *wif); static int wlan_config_set_dchannel(struct wlan_iface *wif, uint32_t); static int wlan_config_get_bssid(struct wlan_iface *); static int wlan_config_set_bssid(struct wlan_iface *, uint8_t *); static void wlan_config_set_snmp_intval(struct wlan_iface *, int, int); static int wlan_config_snmp2value(int, int, int *); static int wlan_config_check(struct wlan_iface *, int); static int wlan_config_get_intval(struct wlan_iface *, int); static int wlan_config_set_intval(struct wlan_iface *, int, int); static int wlan_add_new_scan_result(struct wlan_iface *, const struct ieee80211req_scan_result *, uint8_t *); static int wlan_add_mac_macinfo(struct wlan_iface *, const struct ieee80211req_maclist *); static struct wlan_peer *wlan_add_peerinfo(const struct ieee80211req_sta_info *); int wlan_ioctl_init(void) { if ((sock = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { syslog(LOG_ERR, "cannot open socket : %s", strerror(errno)); return (-1); } return (0); } /* * Load the needed modules in kernel if not already there. */ enum wlan_kmodules { WLAN_KMOD = 0, WLAN_KMOD_ACL, WLAN_KMOD_WEP, WLAN_KMODS_MAX }; static const char *wmod_names[] = { "wlan", "wlan_wlan_acl", "wlan_wep", NULL }; static int wlan_kmod_load(const char *modname) { int fileid, modid; struct module_stat mstat; mstat.version = sizeof(struct module_stat); for (fileid = kldnext(0); fileid > 0; fileid = kldnext(fileid)) { for (modid = kldfirstmod(fileid); modid > 0; modid = modfnext(modid)) { if (modstat(modid, &mstat) < 0) continue; if (strcmp(modname, mstat.name) == 0) return (0); } } /* Not present - load it. */ if (kldload(modname) < 0) { syslog(LOG_ERR, "failed to load %s kernel module - %s", modname, strerror(errno)); return (-1); } return (1); } int wlan_kmodules_load(void) { if (wlan_kmod_load(wmod_names[WLAN_KMOD]) < 0) return (-1); if (wlan_kmod_load(wmod_names[WLAN_KMOD_ACL]) > 0) syslog(LOG_NOTICE, "SNMP wlan loaded %s module", wmod_names[WLAN_KMOD_ACL]); if (wlan_kmod_load(wmod_names[WLAN_KMOD_WEP]) > 0) syslog(LOG_NOTICE, "SNMP wlan loaded %s module", wmod_names[WLAN_KMOD_WEP]); return (0); } /* XXX: FIXME */ static int wlan_ioctl(char *wif_name, uint16_t req_type, int *val, void *arg, size_t *argsize, int set) { struct ieee80211req ireq; memset(&ireq, 0, sizeof(struct ieee80211req)); strlcpy(ireq.i_name, wif_name, IFNAMSIZ); ireq.i_type = req_type; ireq.i_val = *val; ireq.i_len = *argsize; ireq.i_data = arg; if (ioctl(sock, set ? SIOCS80211 : SIOCG80211, &ireq) < 0) { syslog(LOG_ERR, "iface %s - %s param: ioctl(%d) " "failed: %s", wif_name, set ? "set" : "get", req_type, strerror(errno)); return (-1); } *argsize = ireq.i_len; *val = ireq.i_val; return (0); } int wlan_check_media(char *ifname) { struct ifmediareq ifmr; memset(&ifmr, 0, sizeof(struct ifmediareq)); strlcpy(ifmr.ifm_name, ifname, sizeof(ifmr.ifm_name)); if (ioctl(sock, SIOCGIFMEDIA, &ifmr) < 0 || ifmr.ifm_count == 0) return (0); /* Interface doesn't support SIOCGIFMEDIA. */ if ((ifmr.ifm_status & IFM_AVALID) == 0) return (0); return (IFM_TYPE(ifmr.ifm_active)); } int wlan_get_opmode(struct wlan_iface *wif) { struct ifmediareq ifmr; memset(&ifmr, 0, sizeof(struct ifmediareq)); strlcpy(ifmr.ifm_name, wif->wname, sizeof(ifmr.ifm_name)); if (ioctl(sock, SIOCGIFMEDIA, &ifmr) < 0) { if (errno == ENXIO) return (-1); wif->mode = WlanIfaceOperatingModeType_station; return (0); } if (ifmr.ifm_current & IFM_IEEE80211_ADHOC) { if (ifmr.ifm_current & IFM_FLAG0) wif->mode = WlanIfaceOperatingModeType_adhocDemo; else wif->mode = WlanIfaceOperatingModeType_ibss; } else if (ifmr.ifm_current & IFM_IEEE80211_HOSTAP) wif->mode = WlanIfaceOperatingModeType_hostAp; else if (ifmr.ifm_current & IFM_IEEE80211_MONITOR) wif->mode = WlanIfaceOperatingModeType_monitor; else if (ifmr.ifm_current & IFM_IEEE80211_MBSS) wif->mode = WlanIfaceOperatingModeType_meshPoint; else if (ifmr.ifm_current & IFM_IEEE80211_WDS) wif->mode = WlanIfaceOperatingModeType_wds; return (0); } int wlan_config_state(struct wlan_iface *wif, uint8_t set) { int flags; struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); strcpy(ifr.ifr_name, wif->wname); if (ioctl(sock, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) { syslog(LOG_ERR, "set %s status: ioctl(SIOCGIFFLAGS) " "failed: %s", wif->wname, strerror(errno)); return (-1); } if (set == 0) { if ((ifr.ifr_flags & IFF_UP) != 0) wif->state = wlanIfaceState_up; else wif->state = wlanIfaceState_down; return (0); } flags = (ifr.ifr_flags & 0xffff) | (ifr.ifr_flagshigh << 16); if (wif->state == wlanIfaceState_up) flags |= IFF_UP; else flags &= ~IFF_UP; ifr.ifr_flags = flags & 0xffff; ifr.ifr_flagshigh = flags >> 16; if (ioctl(sock, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) { syslog(LOG_ERR, "set %s %s: ioctl(SIOCSIFFLAGS) failed: %s", wif->wname, wif->state == wlanIfaceState_up?"up":"down", strerror(errno)); return (-1); } return (0); } int wlan_get_local_addr(struct wlan_iface *wif) { int len; char ifname[IFNAMSIZ]; struct ifaddrs *ifap, *ifa; struct sockaddr_dl sdl; if (getifaddrs(&ifap) != 0) { syslog(LOG_ERR, "wlan get mac: getifaddrs() failed - %s", strerror(errno)); return (-1); } for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) { if (ifa->ifa_addr->sa_family != AF_LINK) continue; memcpy(&sdl, ifa->ifa_addr, sizeof(struct sockaddr_dl)); if (sdl.sdl_alen > IEEE80211_ADDR_LEN) continue; if ((len = sdl.sdl_nlen) >= IFNAMSIZ) len = IFNAMSIZ - 1; memcpy(ifname, sdl.sdl_data, len); ifname[len] = '\0'; if (strcmp(wif->wname, ifname) == 0) break; } freeifaddrs(ifap); return (0); } int wlan_get_parent(struct wlan_iface *wif __unused) { /* XXX: There's no way to fetch this from the kernel. */ return (0); } /* XXX */ #define IEEE80211_C_STA 0x00000001 /* CAPABILITY: STA available */ #define IEEE80211_C_8023ENCAP 0x00000002 /* CAPABILITY: 802.3 encap */ #define IEEE80211_C_FF 0x00000040 /* CAPABILITY: ATH FF avail */ #define IEEE80211_C_TURBOP 0x00000080 /* CAPABILITY: ATH Turbo avail*/ #define IEEE80211_C_IBSS 0x00000100 /* CAPABILITY: IBSS available */ #define IEEE80211_C_PMGT 0x00000200 /* CAPABILITY: Power mgmt */ #define IEEE80211_C_HOSTAP 0x00000400 /* CAPABILITY: HOSTAP avail */ #define IEEE80211_C_AHDEMO 0x00000800 /* CAPABILITY: Old Adhoc Demo */ #define IEEE80211_C_SWRETRY 0x00001000 /* CAPABILITY: sw tx retry */ #define IEEE80211_C_TXPMGT 0x00002000 /* CAPABILITY: tx power mgmt */ #define IEEE80211_C_SHSLOT 0x00004000 /* CAPABILITY: short slottime */ #define IEEE80211_C_SHPREAMBLE 0x00008000 /* CAPABILITY: short preamble */ #define IEEE80211_C_MONITOR 0x00010000 /* CAPABILITY: monitor mode */ #define IEEE80211_C_DFS 0x00020000 /* CAPABILITY: DFS/radar avail*/ #define IEEE80211_C_MBSS 0x00040000 /* CAPABILITY: MBSS available */ /* 0x7c0000 available */ #define IEEE80211_C_WPA1 0x00800000 /* CAPABILITY: WPA1 avail */ #define IEEE80211_C_WPA2 0x01000000 /* CAPABILITY: WPA2 avail */ #define IEEE80211_C_WPA 0x01800000 /* CAPABILITY: WPA1+WPA2 avail*/ #define IEEE80211_C_BURST 0x02000000 /* CAPABILITY: frame bursting */ #define IEEE80211_C_WME 0x04000000 /* CAPABILITY: WME avail */ #define IEEE80211_C_WDS 0x08000000 /* CAPABILITY: 4-addr support */ /* 0x10000000 reserved */ #define IEEE80211_C_BGSCAN 0x20000000 /* CAPABILITY: bg scanning */ #define IEEE80211_C_TXFRAG 0x40000000 /* CAPABILITY: tx fragments */ #define IEEE80211_C_TDMA 0x80000000 /* CAPABILITY: TDMA avail */ static uint32_t wlan_drivercaps_to_snmp(uint32_t dcaps) { uint32_t scaps = 0; if ((dcaps & IEEE80211_C_STA) != 0) scaps |= (0x1 << WlanDriverCaps_station); if ((dcaps & IEEE80211_C_8023ENCAP) != 0) scaps |= (0x1 << WlanDriverCaps_ieee8023encap); if ((dcaps & IEEE80211_C_FF) != 0) scaps |= (0x1 << WlanDriverCaps_athFastFrames); if ((dcaps & IEEE80211_C_TURBOP) != 0) scaps |= (0x1 << WlanDriverCaps_athTurbo); if ((dcaps & IEEE80211_C_IBSS) != 0) scaps |= (0x1 << WlanDriverCaps_ibss); if ((dcaps & IEEE80211_C_PMGT) != 0) scaps |= (0x1 << WlanDriverCaps_pmgt); if ((dcaps & IEEE80211_C_HOSTAP) != 0) scaps |= (0x1 << WlanDriverCaps_hostAp); if ((dcaps & IEEE80211_C_AHDEMO) != 0) scaps |= (0x1 << WlanDriverCaps_ahDemo); if ((dcaps & IEEE80211_C_SWRETRY) != 0) scaps |= (0x1 << WlanDriverCaps_swRetry); if ((dcaps & IEEE80211_C_TXPMGT) != 0) scaps |= (0x1 << WlanDriverCaps_txPmgt); if ((dcaps & IEEE80211_C_SHSLOT) != 0) scaps |= (0x1 << WlanDriverCaps_shortSlot); if ((dcaps & IEEE80211_C_SHPREAMBLE) != 0) scaps |= (0x1 << WlanDriverCaps_shortPreamble); if ((dcaps & IEEE80211_C_MONITOR) != 0) scaps |= (0x1 << WlanDriverCaps_monitor); if ((dcaps & IEEE80211_C_DFS) != 0) scaps |= (0x1 << WlanDriverCaps_dfs); if ((dcaps & IEEE80211_C_MBSS) != 0) scaps |= (0x1 << WlanDriverCaps_mbss); if ((dcaps & IEEE80211_C_WPA1) != 0) scaps |= (0x1 << WlanDriverCaps_wpa1); if ((dcaps & IEEE80211_C_WPA2) != 0) scaps |= (0x1 << WlanDriverCaps_wpa2); if ((dcaps & IEEE80211_C_BURST) != 0) scaps |= (0x1 << WlanDriverCaps_burst); if ((dcaps & IEEE80211_C_WME) != 0) scaps |= (0x1 << WlanDriverCaps_wme); if ((dcaps & IEEE80211_C_WDS) != 0) scaps |= (0x1 << WlanDriverCaps_wds); if ((dcaps & IEEE80211_C_BGSCAN) != 0) scaps |= (0x1 << WlanDriverCaps_bgScan); if ((dcaps & IEEE80211_C_TXFRAG) != 0) scaps |= (0x1 << WlanDriverCaps_txFrag); if ((dcaps & IEEE80211_C_TDMA) != 0) scaps |= (0x1 << WlanDriverCaps_tdma); return (scaps); } static uint32_t wlan_cryptocaps_to_snmp(uint32_t ccaps) { uint32_t scaps = 0; #if NOT_YET if ((ccaps & IEEE80211_CRYPTO_WEP) != 0) scaps |= (0x1 << wlanCryptoCaps_wep); if ((ccaps & IEEE80211_CRYPTO_TKIP) != 0) scaps |= (0x1 << wlanCryptoCaps_tkip); if ((ccaps & IEEE80211_CRYPTO_AES_OCB) != 0) scaps |= (0x1 << wlanCryptoCaps_aes); if ((ccaps & IEEE80211_CRYPTO_AES_CCM) != 0) scaps |= (0x1 << wlanCryptoCaps_aesCcm); if ((ccaps & IEEE80211_CRYPTO_TKIPMIC) != 0) scaps |= (0x1 << wlanCryptoCaps_tkipMic); if ((ccaps & IEEE80211_CRYPTO_CKIP) != 0) scaps |= (0x1 << wlanCryptoCaps_ckip); #else /* !NOT_YET */ scaps = ccaps; #endif return (scaps); } #define IEEE80211_HTC_AMPDU 0x00010000 /* CAPABILITY: A-MPDU tx */ #define IEEE80211_HTC_AMSDU 0x00020000 /* CAPABILITY: A-MSDU tx */ /* NB: HT40 is implied by IEEE80211_HTCAP_CHWIDTH40 */ #define IEEE80211_HTC_HT 0x00040000 /* CAPABILITY: HT operation */ #define IEEE80211_HTC_SMPS 0x00080000 /* CAPABILITY: MIMO power save*/ #define IEEE80211_HTC_RIFS 0x00100000 /* CAPABILITY: RIFS support */ static uint32_t wlan_htcaps_to_snmp(uint32_t hcaps) { uint32_t scaps = 0; if ((hcaps & IEEE80211_HTCAP_LDPC) != 0) scaps |= (0x1 << WlanHTCaps_ldpc); if ((hcaps & IEEE80211_HTCAP_CHWIDTH40) != 0) scaps |= (0x1 << WlanHTCaps_chwidth40); if ((hcaps & IEEE80211_HTCAP_GREENFIELD) != 0) scaps |= (0x1 << WlanHTCaps_greenField); if ((hcaps & IEEE80211_HTCAP_SHORTGI20) != 0) scaps |= (0x1 << WlanHTCaps_shortGi20); if ((hcaps & IEEE80211_HTCAP_SHORTGI40) != 0) scaps |= (0x1 << WlanHTCaps_shortGi40); if ((hcaps & IEEE80211_HTCAP_TXSTBC) != 0) scaps |= (0x1 << WlanHTCaps_txStbc); if ((hcaps & IEEE80211_HTCAP_DELBA) != 0) scaps |= (0x1 << WlanHTCaps_delba); if ((hcaps & IEEE80211_HTCAP_MAXAMSDU_7935) != 0) scaps |= (0x1 << WlanHTCaps_amsdu7935); if ((hcaps & IEEE80211_HTCAP_DSSSCCK40) != 0) scaps |= (0x1 << WlanHTCaps_dssscck40); if ((hcaps & IEEE80211_HTCAP_PSMP) != 0) scaps |= (0x1 << WlanHTCaps_psmp); if ((hcaps & IEEE80211_HTCAP_40INTOLERANT) != 0) scaps |= (0x1 << WlanHTCaps_fortyMHzIntolerant); if ((hcaps & IEEE80211_HTCAP_LSIGTXOPPROT) != 0) scaps |= (0x1 << WlanHTCaps_lsigTxOpProt); if ((hcaps & IEEE80211_HTC_AMPDU) != 0) scaps |= (0x1 << WlanHTCaps_htcAmpdu); if ((hcaps & IEEE80211_HTC_AMSDU) != 0) scaps |= (0x1 << WlanHTCaps_htcAmsdu); if ((hcaps & IEEE80211_HTC_HT) != 0) scaps |= (0x1 << WlanHTCaps_htcHt); if ((hcaps & IEEE80211_HTC_SMPS) != 0) scaps |= (0x1 << WlanHTCaps_htcSmps); if ((hcaps & IEEE80211_HTC_RIFS) != 0) scaps |= (0x1 << WlanHTCaps_htcRifs); return (scaps); } /* XXX: Not here? */ #define WLAN_SET_TDMA_OPMODE(w) do { \ if ((w)->mode == WlanIfaceOperatingModeType_adhocDemo && \ ((w)->drivercaps & WlanDriverCaps_tdma) != 0) \ (w)->mode = WlanIfaceOperatingModeType_tdma; \ } while (0) int wlan_get_driver_caps(struct wlan_iface *wif) { int val = 0; size_t argsize; struct ieee80211_devcaps_req dc; memset(&dc, 0, sizeof(struct ieee80211_devcaps_req)); argsize = sizeof(struct ieee80211_devcaps_req); if (wlan_ioctl(wif->wname, IEEE80211_IOC_DEVCAPS, &val, &dc, &argsize, 0) < 0) return (-1); wif->drivercaps = wlan_drivercaps_to_snmp(dc.dc_drivercaps); wif->cryptocaps = wlan_cryptocaps_to_snmp(dc.dc_cryptocaps); wif->htcaps = wlan_htcaps_to_snmp(dc.dc_htcaps); WLAN_SET_TDMA_OPMODE(wif); argsize = dc.dc_chaninfo.ic_nchans * sizeof(struct ieee80211_channel); wif->chanlist = (struct ieee80211_channel *)malloc(argsize); if (wif->chanlist == NULL) return (0); memcpy(wif->chanlist, dc.dc_chaninfo.ic_chans, argsize); wif->nchannels = dc.dc_chaninfo.ic_nchans; return (0); } uint8_t wlan_channel_state_to_snmp(uint8_t cstate) { uint8_t cs = 0; if ((cstate & IEEE80211_CHANSTATE_RADAR) != 0) cs |= (0x1 << WlanIfaceChannelStateType_radar); if ((cstate & IEEE80211_CHANSTATE_CACDONE) != 0) cs |= (0x1 << WlanIfaceChannelStateType_cacDone); if ((cstate & IEEE80211_CHANSTATE_CWINT) != 0) cs |= (0x1 << WlanIfaceChannelStateType_interferenceDetected); if ((cstate & IEEE80211_CHANSTATE_NORADAR) != 0) cs |= (0x1 << WlanIfaceChannelStateType_radarClear); return (cs); } uint32_t wlan_channel_flags_to_snmp(uint32_t cflags) { uint32_t cf = 0; if ((cflags & IEEE80211_CHAN_TURBO) != 0) cf |= (0x1 << WlanIfaceChannelFlagsType_turbo); if ((cflags & IEEE80211_CHAN_CCK) != 0) cf |= (0x1 << WlanIfaceChannelFlagsType_cck); if ((cflags & IEEE80211_CHAN_OFDM) != 0) cf |= (0x1 << WlanIfaceChannelFlagsType_ofdm); if ((cflags & IEEE80211_CHAN_2GHZ) != 0) cf |= (0x1 << WlanIfaceChannelFlagsType_spectrum2Ghz); if ((cflags & IEEE80211_CHAN_5GHZ) != 0) cf |= (0x1 << WlanIfaceChannelFlagsType_spectrum5Ghz); if ((cflags & IEEE80211_CHAN_PASSIVE) != 0) cf |= (0x1 << WlanIfaceChannelFlagsType_passiveScan); if ((cflags & IEEE80211_CHAN_DYN) != 0) cf |= (0x1 << WlanIfaceChannelFlagsType_dynamicCckOfdm); if ((cflags & IEEE80211_CHAN_GFSK) != 0) cf |= (0x1 << WlanIfaceChannelFlagsType_gfsk); if ((cflags & IEEE80211_CHAN_GSM) != 0) cf |= (0x1 << WlanIfaceChannelFlagsType_spectrum900Mhz); if ((cflags & IEEE80211_CHAN_STURBO) != 0) cf |= (0x1 << WlanIfaceChannelFlagsType_dot11aStaticTurbo); if ((cflags & IEEE80211_CHAN_HALF) != 0) cf |= (0x1 << WlanIfaceChannelFlagsType_halfRate); if ((cflags & IEEE80211_CHAN_QUARTER) != 0) cf |= (0x1 << WlanIfaceChannelFlagsType_quarterRate); if ((cflags & IEEE80211_CHAN_HT20) != 0) cf |= (0x1 << WlanIfaceChannelFlagsType_ht20); if ((cflags & IEEE80211_CHAN_HT40U) != 0) cf |= (0x1 << WlanIfaceChannelFlagsType_ht40u); if ((cflags & IEEE80211_CHAN_HT40D) != 0) cf |= (0x1 << WlanIfaceChannelFlagsType_ht40d); if ((cflags & IEEE80211_CHAN_DFS) != 0) cf |= (0x1 << WlanIfaceChannelFlagsType_dfs); if ((cflags & IEEE80211_CHAN_4MSXMIT) != 0) cf |= (0x1 << WlanIfaceChannelFlagsType_xmit4ms); if ((cflags & IEEE80211_CHAN_NOADHOC) != 0) cf |= (0x1 << WlanIfaceChannelFlagsType_noAdhoc); if ((cflags & IEEE80211_CHAN_NOHOSTAP) != 0) cf |= (0x1 << WlanIfaceChannelFlagsType_noHostAp); if ((cflags & IEEE80211_CHAN_11D) != 0) cf |= (0x1 << WlanIfaceChannelFlagsType_dot11d); return (cf); } /* XXX: */ #define WLAN_SNMP_MAX_CHANS 256 int wlan_get_channel_list(struct wlan_iface *wif) { int val = 0; uint32_t i, nchans; size_t argsize; struct ieee80211req_chaninfo *chaninfo; struct ieee80211req_chanlist active; const struct ieee80211_channel *c; argsize = sizeof(struct ieee80211req_chaninfo) + sizeof(struct ieee80211_channel) * WLAN_SNMP_MAX_CHANS; chaninfo = (struct ieee80211req_chaninfo *)malloc(argsize); if (chaninfo == NULL) return (-1); if (wlan_ioctl(wif->wname, IEEE80211_IOC_CHANINFO, &val, chaninfo, &argsize, 0) < 0) return (-1); argsize = sizeof(active); if (wlan_ioctl(wif->wname, IEEE80211_IOC_CHANLIST, &val, &active, &argsize, 0) < 0) goto error; for (i = 0, nchans = 0; i < chaninfo->ic_nchans; i++) { c = &chaninfo->ic_chans[i]; if (!isset(active.ic_channels, c->ic_ieee)) continue; nchans++; } wif->chanlist = (struct ieee80211_channel *)reallocf(wif->chanlist, nchans * sizeof(*c)); if (wif->chanlist == NULL) goto error; wif->nchannels = nchans; for (i = 0, nchans = 0; i < chaninfo->ic_nchans; i++) { c = &chaninfo->ic_chans[i]; if (!isset(active.ic_channels, c->ic_ieee)) continue; memcpy(wif->chanlist + nchans, c, sizeof (*c)); nchans++; } free(chaninfo); return (0); error: wif->nchannels = 0; free(chaninfo); return (-1); } static enum WlanIfPhyMode wlan_channel_flags_to_snmp_phy(uint32_t cflags) { /* XXX: recheck */ if ((cflags & IEEE80211_CHAN_A) != 0) return (WlanIfPhyMode_dot11a); if ((cflags & IEEE80211_CHAN_B) != 0) return (WlanIfPhyMode_dot11b); if ((cflags & IEEE80211_CHAN_G) != 0 || (cflags & IEEE80211_CHAN_PUREG) != 0) return (WlanIfPhyMode_dot11g); if ((cflags & IEEE80211_CHAN_FHSS) != 0) return (WlanIfPhyMode_fh); if ((cflags & IEEE80211_CHAN_TURBO) != 0 && (cflags & IEEE80211_CHAN_A) != 0) return (WlanIfPhyMode_turboA); if ((cflags & IEEE80211_CHAN_TURBO) != 0 && (cflags & IEEE80211_CHAN_G) != 0) return (WlanIfPhyMode_turboG); if ((cflags & IEEE80211_CHAN_STURBO) != 0) return (WlanIfPhyMode_sturboA); if ((cflags & IEEE80211_CHAN_HALF) != 0) return (WlanIfPhyMode_ofdmHalf); if ((cflags & IEEE80211_CHAN_QUARTER) != 0) return (WlanIfPhyMode_ofdmQuarter); return (WlanIfPhyMode_auto); } int wlan_get_roam_params(struct wlan_iface *wif) { int val = 0; size_t argsize; argsize = sizeof(struct ieee80211_roamparams_req); if (wlan_ioctl(wif->wname, IEEE80211_IOC_ROAM, &val, &wif->roamparams, &argsize, 0) < 0) return (-1); return (0); } int wlan_get_tx_params(struct wlan_iface *wif) { int val = 0; size_t argsize; /* * XXX: Reset IEEE80211_RATE_MCS bit on IEEE80211_MODE_11NA * and IEEE80211_MODE_11NG modes. */ argsize = sizeof(struct ieee80211_txparams_req); if (wlan_ioctl(wif->wname, IEEE80211_IOC_TXPARAMS, &val, &wif->txparams, &argsize, 0) < 0) return (-1); return (0); } int wlan_set_tx_params(struct wlan_iface *wif, int32_t pmode __unused) { int val = 0; size_t argsize; /* * XXX: Set IEEE80211_RATE_MCS bit on IEEE80211_MODE_11NA * and IEEE80211_MODE_11NG modes. */ argsize = sizeof(struct ieee80211_txparams_req); if (wlan_ioctl(wif->wname, IEEE80211_IOC_TXPARAMS, &val, &wif->txparams, &argsize, 1) < 0) return (-1); return (0); } int wlan_clone_create(struct wlan_iface *wif) { struct ifreq ifr; struct ieee80211_clone_params wcp; static const uint8_t zerobssid[IEEE80211_ADDR_LEN]; memset(&wcp, 0, sizeof(wcp)); memset(&ifr, 0, sizeof(ifr)); /* Sanity checks. */ if (wif == NULL || wif->pname[0] == '\0' || wif->mode > WLAN_IFMODE_MAX) return (SNMP_ERR_INCONS_VALUE); if (wif->mode == WlanIfaceOperatingModeType_wds && memcmp(wif->dbssid, zerobssid, IEEE80211_ADDR_LEN) == 0) return (SNMP_ERR_INCONS_VALUE); strlcpy(wcp.icp_parent, wif->pname, IFNAMSIZ); if ((wif->flags & WlanIfaceFlagsType_uniqueBssid) != 0) wcp.icp_flags |= IEEE80211_CLONE_BSSID; if ((wif->flags & WlanIfaceFlagsType_noBeacons) != 0) wcp.icp_flags |= IEEE80211_CLONE_NOBEACONS; if (wif->mode == WlanIfaceOperatingModeType_wds && (wif->flags & WlanIfaceFlagsType_wdsLegacy) != 0) wcp.icp_flags |= IEEE80211_CLONE_WDSLEGACY; switch (wif->mode) { case WlanIfaceOperatingModeType_ibss: wcp.icp_opmode = IEEE80211_M_IBSS; break; case WlanIfaceOperatingModeType_station: wcp.icp_opmode = IEEE80211_M_STA; break; case WlanIfaceOperatingModeType_wds: wcp.icp_opmode = IEEE80211_M_WDS; break; case WlanIfaceOperatingModeType_adhocDemo: wcp.icp_opmode = IEEE80211_M_AHDEMO; break; case WlanIfaceOperatingModeType_hostAp: wcp.icp_opmode = IEEE80211_M_HOSTAP; break; case WlanIfaceOperatingModeType_monitor: wcp.icp_opmode = IEEE80211_M_MONITOR; break; case WlanIfaceOperatingModeType_meshPoint: wcp.icp_opmode = IEEE80211_M_MBSS; break; case WlanIfaceOperatingModeType_tdma: wcp.icp_opmode = IEEE80211_M_AHDEMO; wcp.icp_flags |= IEEE80211_CLONE_TDMA; break; } memcpy(wcp.icp_bssid, wif->dbssid, IEEE80211_ADDR_LEN); if (memcmp(wif->dlmac, zerobssid, IEEE80211_ADDR_LEN) != 0) { memcpy(wcp.icp_macaddr, wif->dlmac, IEEE80211_ADDR_LEN); wcp.icp_flags |= IEEE80211_CLONE_MACADDR; } strlcpy(ifr.ifr_name, wif->wname, IFNAMSIZ); ifr.ifr_data = (caddr_t) &wcp; if (ioctl(sock, SIOCIFCREATE2, (caddr_t) &ifr) < 0) { syslog(LOG_ERR, "wlan clone create: ioctl(SIOCIFCREATE2) " "failed: %s", strerror(errno)); return (SNMP_ERR_GENERR); } return (SNMP_ERR_NOERROR); } int wlan_clone_destroy(struct wlan_iface *wif) { struct ifreq ifr; if (wif == NULL) return (SNMP_ERR_INCONS_VALUE); memset(&ifr, 0, sizeof(ifr)); strcpy(ifr.ifr_name, wif->wname); if (ioctl(sock, SIOCIFDESTROY, &ifr) < 0) { syslog(LOG_ERR, "wlan clone destroy: ioctl(SIOCIFDESTROY) " "failed: %s", strerror(errno)); return (SNMP_ERR_GENERR); } return (SNMP_ERR_NOERROR); } static int wlan_config_snmp2ioctl(int which) { int op; switch (which) { case LEAF_wlanIfacePacketBurst: op = IEEE80211_IOC_BURST; break; case LEAF_wlanIfaceCountryCode: op = IEEE80211_IOC_REGDOMAIN; break; case LEAF_wlanIfaceRegDomain: op = IEEE80211_IOC_REGDOMAIN; break; case LEAF_wlanIfaceDesiredSsid: op = IEEE80211_IOC_SSID; break; case LEAF_wlanIfaceDesiredChannel: op = IEEE80211_IOC_CURCHAN; break; case LEAF_wlanIfaceDynamicFreqSelection: op = IEEE80211_IOC_DFS; break; case LEAF_wlanIfaceFastFrames: op = IEEE80211_IOC_FF; break; case LEAF_wlanIfaceDturbo: op = IEEE80211_IOC_TURBOP; break; case LEAF_wlanIfaceTxPower: op = IEEE80211_IOC_TXPOWER; break; case LEAF_wlanIfaceFragmentThreshold: op = IEEE80211_IOC_FRAGTHRESHOLD; break; case LEAF_wlanIfaceRTSThreshold: op = IEEE80211_IOC_RTSTHRESHOLD; break; case LEAF_wlanIfaceWlanPrivacySubscribe: op = IEEE80211_IOC_WPS; break; case LEAF_wlanIfaceBgScan: op = IEEE80211_IOC_BGSCAN; break; case LEAF_wlanIfaceBgScanIdle: op = IEEE80211_IOC_BGSCAN_IDLE; break; case LEAF_wlanIfaceBgScanInterval: op = IEEE80211_IOC_BGSCAN_INTERVAL; break; case LEAF_wlanIfaceBeaconMissedThreshold: op = IEEE80211_IOC_BMISSTHRESHOLD; break; case LEAF_wlanIfaceDesiredBssid: op = IEEE80211_IOC_BSSID; break; case LEAF_wlanIfaceRoamingMode: op = IEEE80211_IOC_ROAMING; break; case LEAF_wlanIfaceDot11d: op = IEEE80211_IOC_DOTD; break; case LEAF_wlanIfaceDot11h: op = IEEE80211_IOC_DOTH; break; case LEAF_wlanIfaceDynamicWds: op = IEEE80211_IOC_DWDS; break; case LEAF_wlanIfacePowerSave: op = IEEE80211_IOC_POWERSAVE; break; case LEAF_wlanIfaceApBridge: op = IEEE80211_IOC_APBRIDGE; break; case LEAF_wlanIfaceBeaconInterval: op = IEEE80211_IOC_BEACON_INTERVAL; break; case LEAF_wlanIfaceDtimPeriod: op = IEEE80211_IOC_DTIM_PERIOD; break; case LEAF_wlanIfaceHideSsid: op = IEEE80211_IOC_HIDESSID; break; case LEAF_wlanIfaceInactivityProccess: op = IEEE80211_IOC_INACTIVITY; break; case LEAF_wlanIfaceDot11gProtMode: op = IEEE80211_IOC_PROTMODE; break; case LEAF_wlanIfaceDot11gPureMode: op = IEEE80211_IOC_PUREG; break; case LEAF_wlanIfaceDot11nPureMode: op = IEEE80211_IOC_PUREN; break; case LEAF_wlanIfaceDot11nAmpdu: op = IEEE80211_IOC_AMPDU; break; case LEAF_wlanIfaceDot11nAmpduDensity: op = IEEE80211_IOC_AMPDU_DENSITY; break; case LEAF_wlanIfaceDot11nAmpduLimit: op = IEEE80211_IOC_AMPDU_LIMIT; break; case LEAF_wlanIfaceDot11nAmsdu: op = IEEE80211_IOC_AMSDU; break; case LEAF_wlanIfaceDot11nAmsduLimit: op = IEEE80211_IOC_AMSDU_LIMIT; break; case LEAF_wlanIfaceDot11nHighThroughput: op = IEEE80211_IOC_HTCONF; break; case LEAF_wlanIfaceDot11nHTCompatible: op = IEEE80211_IOC_HTCOMPAT; break; case LEAF_wlanIfaceDot11nHTProtMode: op = IEEE80211_IOC_HTPROTMODE; break; case LEAF_wlanIfaceDot11nRIFS: op = IEEE80211_IOC_RIFS; break; case LEAF_wlanIfaceDot11nShortGI: op = IEEE80211_IOC_SHORTGI; break; case LEAF_wlanIfaceDot11nSMPSMode: op = IEEE80211_IOC_SMPS; break; case LEAF_wlanIfaceTdmaSlot: op = IEEE80211_IOC_TDMA_SLOT; break; case LEAF_wlanIfaceTdmaSlotCount: op = IEEE80211_IOC_TDMA_SLOTCNT; break; case LEAF_wlanIfaceTdmaSlotLength: op = IEEE80211_IOC_TDMA_SLOTLEN; break; case LEAF_wlanIfaceTdmaBeaconInterval: op = IEEE80211_IOC_TDMA_BINTERVAL; break; default: op = -1; } return (op); } static enum WlanRegDomainCode wlan_regdomain_to_snmp(int which) { enum WlanRegDomainCode reg_domain; switch (which) { case SKU_FCC: reg_domain = WlanRegDomainCode_fcc; break; case SKU_CA: reg_domain = WlanRegDomainCode_ca; break; case SKU_ETSI: reg_domain = WlanRegDomainCode_etsi; break; case SKU_ETSI2: reg_domain = WlanRegDomainCode_etsi2; break; case SKU_ETSI3: reg_domain = WlanRegDomainCode_etsi3; break; case SKU_FCC3: reg_domain = WlanRegDomainCode_fcc3; break; case SKU_JAPAN: reg_domain = WlanRegDomainCode_japan; break; case SKU_KOREA: reg_domain = WlanRegDomainCode_korea; break; case SKU_APAC: reg_domain = WlanRegDomainCode_apac; break; case SKU_APAC2: reg_domain = WlanRegDomainCode_apac2; break; case SKU_APAC3: reg_domain = WlanRegDomainCode_apac3; break; case SKU_ROW: reg_domain = WlanRegDomainCode_row; break; case SKU_NONE: reg_domain = WlanRegDomainCode_none; break; case SKU_DEBUG: reg_domain = WlanRegDomainCode_debug; break; case SKU_SR9: reg_domain = WlanRegDomainCode_sr9; break; case SKU_XR9: reg_domain = WlanRegDomainCode_xr9; break; case SKU_GZ901: reg_domain = WlanRegDomainCode_gz901; break; case 0: reg_domain = WlanRegDomainCode_none; break; default: syslog(LOG_ERR, "unknown regdomain (0x%x) ", which); reg_domain = WlanRegDomainCode_none; break; } return (reg_domain); } static int wlan_snmp_to_regdomain(enum WlanRegDomainCode regdomain) { int which; switch (regdomain) { case WlanRegDomainCode_fcc: which = SKU_FCC; break; case WlanRegDomainCode_ca: which = SKU_CA; break; case WlanRegDomainCode_etsi: which = SKU_ETSI; break; case WlanRegDomainCode_etsi2: which = SKU_ETSI2; break; case WlanRegDomainCode_etsi3: which = SKU_ETSI3; break; case WlanRegDomainCode_fcc3: which = SKU_FCC3; break; case WlanRegDomainCode_japan: which = SKU_JAPAN; break; case WlanRegDomainCode_korea: which = SKU_KOREA; break; case WlanRegDomainCode_apac: which = SKU_APAC; break; case WlanRegDomainCode_apac2: which = SKU_APAC2; break; case WlanRegDomainCode_apac3: which = SKU_APAC3; break; case WlanRegDomainCode_row: which = SKU_ROW; break; case WlanRegDomainCode_none: which = SKU_NONE; break; case WlanRegDomainCode_debug: which = SKU_DEBUG; break; case WlanRegDomainCode_sr9: which = SKU_SR9; break; case WlanRegDomainCode_xr9: which = SKU_XR9; break; case WlanRegDomainCode_gz901: which = SKU_GZ901; break; default: syslog(LOG_ERR, "unknown snmp regdomain (0x%x) ", regdomain); which = SKU_NONE; break; } return (which); } static int wlan_config_get_country(struct wlan_iface *wif) { int val = 0; size_t argsize; struct ieee80211_regdomain regdomain; memset(®domain, 0, sizeof(regdomain)); argsize = sizeof(regdomain); if (wlan_ioctl(wif->wname, IEEE80211_IOC_REGDOMAIN, &val, ®domain, &argsize, 0) < 0) return (-1); wif->reg_domain = wlan_regdomain_to_snmp(regdomain.regdomain); wif->country_code[0] = regdomain.isocc[0]; wif->country_code[1] = regdomain.isocc[1]; wif->country_code[2] = regdomain.location; return (0); } static int wlan_config_set_country(struct wlan_iface *wif, char *ccode, int rdomain) { int val = 0, txpowermax; uint32_t i; size_t argsize = 0; struct ieee80211_regdomain_req *regdomain; if (wlan_get_channel_list(wif) < 0) return (-1); if (wif->nchannels == 0) { syslog(LOG_ERR, "iface %s - set regdomain failed", wif->wname); return (-1); } if (wlan_ioctl(wif->wname, IEEE80211_IOC_TXPOWMAX, &txpowermax, 0, &argsize, 0) < 0) return (-1); regdomain = malloc(IEEE80211_REGDOMAIN_SIZE(wif->nchannels)); if (regdomain == NULL) return (-1); memset(regdomain, 0, IEEE80211_REGDOMAIN_SIZE(wif->nchannels)); argsize = IEEE80211_REGDOMAIN_SIZE(wif->nchannels); /* XXX: recheck with how this is done by ifconfig(8) */ regdomain->rd.regdomain = wlan_snmp_to_regdomain(rdomain); regdomain->rd.isocc[0] = ccode[0]; regdomain->rd.isocc[1] = ccode[1]; regdomain->rd.location = ccode[2]; /* XXX: fill the channel list properly */ regdomain->chaninfo.ic_nchans = wif->nchannels; memcpy(regdomain->chaninfo.ic_chans, wif->chanlist, wif->nchannels * sizeof(struct ieee80211_channel)); for (i = 0; i < wif->nchannels; i++) regdomain->chaninfo.ic_chans[i].ic_maxregpower = txpowermax; wif->state = wlanIfaceState_down; if (wlan_config_state(wif, 1) < 0 || wlan_ioctl(wif->wname, IEEE80211_IOC_REGDOMAIN, &val, regdomain, &argsize, 1) < 0) { free(regdomain); return (-1); } wif->state = wlanIfaceState_up; (void)wlan_config_state(wif, 1); wif->reg_domain = wlan_regdomain_to_snmp(regdomain->rd.regdomain); wif->country_code[0] = regdomain->rd.isocc[0]; wif->country_code[1] = regdomain->rd.isocc[1]; wif->country_code[2] = regdomain->rd.location; free(regdomain); return (0); } int wlan_config_get_dssid(struct wlan_iface *wif) { int val = -1; size_t argsize = IEEE80211_NWID_LEN + 1; char ssid[IEEE80211_NWID_LEN + 1]; memset(ssid, 0, IEEE80211_NWID_LEN + 1); if (wlan_ioctl(wif->wname, (wif->mode == WlanIfaceOperatingModeType_meshPoint) ? IEEE80211_IOC_MESH_ID : IEEE80211_IOC_SSID, &val, ssid, &argsize, 0) < 0) return (-1); if (argsize > IEEE80211_NWID_LEN) argsize = IEEE80211_NWID_LEN; memcpy(wif->desired_ssid, ssid, argsize); wif->desired_ssid[argsize] = '\0'; return (0); } int wlan_config_set_dssid(struct wlan_iface *wif, char *ssid, int slen) { int val = 0; size_t argsize = slen; if (wlan_ioctl(wif->wname, (wif->mode == WlanIfaceOperatingModeType_meshPoint) ? IEEE80211_IOC_MESH_ID : IEEE80211_IOC_SSID, &val, ssid, &argsize, 1) < 0) return (-1); if (argsize > IEEE80211_NWID_LEN) argsize = IEEE80211_NWID_LEN; memcpy(wif->desired_ssid, ssid, argsize); wif->desired_ssid[argsize] = '\0'; return (0); } static int wlan_config_get_dchannel(struct wlan_iface *wif) { uint32_t i = 0; int val = 0; size_t argsize = sizeof(struct ieee80211_channel); struct ieee80211_channel chan; if (wlan_get_channel_list(wif) < 0) return (-1); memset(&chan, 0, sizeof(chan)); if (wlan_ioctl(wif->wname, IEEE80211_IOC_CURCHAN, &val, &chan, &argsize, 0) < 0) return (-1); for (i = 0; i < wif->nchannels; i++) if (chan.ic_ieee == wif->chanlist[i].ic_ieee && chan.ic_flags == wif->chanlist[i].ic_flags) { wif->desired_channel = i + 1; break; } return (0); } static int wlan_config_set_dchannel(struct wlan_iface *wif, uint32_t dchannel) { int val = 0; size_t argsize = sizeof(struct ieee80211_channel); struct ieee80211_channel chan; if (wlan_get_channel_list(wif) < 0) return (-1); if (dchannel > wif->nchannels) return (-1); memcpy(&chan, wif->chanlist + dchannel - 1, sizeof(chan)); if (wlan_ioctl(wif->wname, IEEE80211_IOC_CURCHAN, &val, &chan, &argsize, 1) < 0) return (-1); wif->desired_channel = dchannel; return (0); } static int wlan_config_get_bssid(struct wlan_iface *wif) { int val = 0; size_t argsize = IEEE80211_ADDR_LEN; char bssid[IEEE80211_ADDR_LEN]; memset(bssid, 0, IEEE80211_ADDR_LEN); if (wlan_ioctl(wif->wname, IEEE80211_IOC_BSSID, &val, bssid, &argsize, 0) < 0 || argsize != IEEE80211_ADDR_LEN) return (-1); memcpy(wif->desired_bssid, bssid, IEEE80211_ADDR_LEN); return (0); } static int wlan_config_set_bssid(struct wlan_iface *wif, uint8_t *bssid) { int val = 0; size_t argsize = IEEE80211_ADDR_LEN; if (wlan_ioctl(wif->wname, IEEE80211_IOC_BSSID, &val, bssid, &argsize, 1) < 0 || argsize != IEEE80211_ADDR_LEN) return (-1); memcpy(wif->desired_bssid, bssid, IEEE80211_ADDR_LEN); return (0); } /* * Convert the value returned by the kernel to the appropriate SNMP * representation and set the corresponding interface member accordingly. */ static void wlan_config_set_snmp_intval(struct wlan_iface *wif, int op, int val) { switch (op) { case IEEE80211_IOC_BURST: if (val == 0) wif->packet_burst = TruthValue_false; else wif->packet_burst = TruthValue_true; break; case IEEE80211_IOC_DFS: if (val == 0) wif->dyn_frequency = TruthValue_false; else wif->dyn_frequency = TruthValue_true; break; case IEEE80211_IOC_FF: if (val == 0) wif->fast_frames = TruthValue_false; else wif->fast_frames = TruthValue_true; break; case IEEE80211_IOC_TURBOP: if (val == 0) wif->dturbo = TruthValue_false; else wif->dturbo = TruthValue_true; break; case IEEE80211_IOC_TXPOWER: wif->tx_power = val / 2; break; case IEEE80211_IOC_FRAGTHRESHOLD: wif->frag_threshold = val; break; case IEEE80211_IOC_RTSTHRESHOLD: wif->rts_threshold = val; break; case IEEE80211_IOC_WPS: if (val == 0) wif->priv_subscribe = TruthValue_false; else wif->priv_subscribe = TruthValue_true; break; case IEEE80211_IOC_BGSCAN: if (val == 0) wif->bg_scan = TruthValue_false; else wif->bg_scan = TruthValue_true; break; case IEEE80211_IOC_BGSCAN_IDLE: wif->bg_scan_idle = val; break; case IEEE80211_IOC_BGSCAN_INTERVAL: wif->bg_scan_interval = val; break; case IEEE80211_IOC_BMISSTHRESHOLD: wif->beacons_missed = val; break; case IEEE80211_IOC_ROAMING: switch (val) { case IEEE80211_ROAMING_DEVICE: wif->roam_mode = wlanIfaceRoamingMode_device; break; case IEEE80211_ROAMING_MANUAL: wif->roam_mode = wlanIfaceRoamingMode_manual; break; case IEEE80211_ROAMING_AUTO: /* FALTHROUGH */ default: wif->roam_mode = wlanIfaceRoamingMode_auto; break; } break; case IEEE80211_IOC_DOTD: if (val == 0) wif->dot11d = TruthValue_false; else wif->dot11d = TruthValue_true; break; case IEEE80211_IOC_DOTH: if (val == 0) wif->dot11h = TruthValue_false; else wif->dot11h = TruthValue_true; break; case IEEE80211_IOC_DWDS: if (val == 0) wif->dynamic_wds = TruthValue_false; else wif->dynamic_wds = TruthValue_true; break; case IEEE80211_IOC_POWERSAVE: if (val == 0) wif->power_save = TruthValue_false; else wif->power_save = TruthValue_true; break; case IEEE80211_IOC_APBRIDGE: if (val == 0) wif->ap_bridge = TruthValue_false; else wif->ap_bridge = TruthValue_true; break; case IEEE80211_IOC_BEACON_INTERVAL: wif->beacon_interval = val; break; case IEEE80211_IOC_DTIM_PERIOD: wif->dtim_period = val; break; case IEEE80211_IOC_HIDESSID: if (val == 0) wif->hide_ssid = TruthValue_false; else wif->hide_ssid = TruthValue_true; break; case IEEE80211_IOC_INACTIVITY: if (val == 0) wif->inact_process = TruthValue_false; else wif->inact_process = TruthValue_true; break; case IEEE80211_IOC_PROTMODE: switch (val) { case IEEE80211_PROTMODE_CTS: wif->do11g_protect = wlanIfaceDot11gProtMode_cts; break; case IEEE80211_PROTMODE_RTSCTS: wif->do11g_protect = wlanIfaceDot11gProtMode_rtscts; break; case IEEE80211_PROTMODE_OFF: /* FALLTHROUGH */ default: wif->do11g_protect = wlanIfaceDot11gProtMode_off; break; } break; case IEEE80211_IOC_PUREG: if (val == 0) wif->dot11g_pure = TruthValue_false; else wif->dot11g_pure = TruthValue_true; break; case IEEE80211_IOC_PUREN: if (val == 0) wif->dot11n_pure = TruthValue_false; else wif->dot11n_pure = TruthValue_true; break; case IEEE80211_IOC_AMPDU: switch (val) { case 0: wif->ampdu = WlanIfaceDot11nPduType_disabled; break; case 1: wif->ampdu = WlanIfaceDot11nPduType_txOnly; break; case 2: wif->ampdu = WlanIfaceDot11nPduType_rxOnly; break; case 3: /* FALLTHROUGH */ default: wif->ampdu = WlanIfaceDot11nPduType_txAndRx; break; } break; case IEEE80211_IOC_AMPDU_DENSITY: switch (val) { case IEEE80211_HTCAP_MPDUDENSITY_025: wif->ampdu_density = 25; break; case IEEE80211_HTCAP_MPDUDENSITY_05: wif->ampdu_density = 50; break; case IEEE80211_HTCAP_MPDUDENSITY_1: wif->ampdu_density = 100; break; case IEEE80211_HTCAP_MPDUDENSITY_2: wif->ampdu_density = 200; break; case IEEE80211_HTCAP_MPDUDENSITY_4: wif->ampdu_density = 400; break; case IEEE80211_HTCAP_MPDUDENSITY_8: wif->ampdu_density = 800; break; case IEEE80211_HTCAP_MPDUDENSITY_16: wif->ampdu_density = 1600; break; case IEEE80211_HTCAP_MPDUDENSITY_NA: default: wif->ampdu_density = 0; break; } break; case IEEE80211_IOC_AMPDU_LIMIT: switch (val) { case IEEE80211_HTCAP_MAXRXAMPDU_8K: wif->ampdu_limit = 8192; break; case IEEE80211_HTCAP_MAXRXAMPDU_16K: wif->ampdu_limit = 16384; break; case IEEE80211_HTCAP_MAXRXAMPDU_32K: wif->ampdu_limit = 32768; break; case IEEE80211_HTCAP_MAXRXAMPDU_64K: default: wif->ampdu_limit = 65536; break; } break; case IEEE80211_IOC_AMSDU: switch (val) { case 0: wif->amsdu = WlanIfaceDot11nPduType_disabled; break; case 1: wif->amsdu = WlanIfaceDot11nPduType_txOnly; break; case 3: wif->amsdu = WlanIfaceDot11nPduType_txAndRx; break; case 2: default: /* FALLTHROUGH */ wif->amsdu = WlanIfaceDot11nPduType_rxOnly; break; } break; case IEEE80211_IOC_AMSDU_LIMIT: wif->amsdu_limit = val; break; case IEEE80211_IOC_HTCONF: if (val == 0) /* XXX */ wif->ht_enabled = TruthValue_false; else wif->ht_enabled = TruthValue_true; break; case IEEE80211_IOC_HTCOMPAT: if (val == 0) wif->ht_compatible = TruthValue_false; else wif->ht_compatible = TruthValue_true; break; case IEEE80211_IOC_HTPROTMODE: if (val == IEEE80211_PROTMODE_RTSCTS) wif->ht_prot_mode = wlanIfaceDot11nHTProtMode_rts; else wif->ht_prot_mode = wlanIfaceDot11nHTProtMode_off; break; case IEEE80211_IOC_RIFS: if (val == 0) wif->rifs = TruthValue_false; else wif->rifs = TruthValue_true; break; case IEEE80211_IOC_SHORTGI: if (val == 0) wif->short_gi = TruthValue_false; else wif->short_gi = TruthValue_true; break; case IEEE80211_IOC_SMPS: switch (val) { case IEEE80211_HTCAP_SMPS_DYNAMIC: wif->smps_mode = wlanIfaceDot11nSMPSMode_dynamic; break; case IEEE80211_HTCAP_SMPS_ENA: wif->smps_mode = wlanIfaceDot11nSMPSMode_static; break; case IEEE80211_HTCAP_SMPS_OFF: /* FALLTHROUGH */ default: wif->smps_mode = wlanIfaceDot11nSMPSMode_disabled; break; } break; case IEEE80211_IOC_TDMA_SLOT: wif->tdma_slot = val; break; case IEEE80211_IOC_TDMA_SLOTCNT: wif->tdma_slot_count = val; break; case IEEE80211_IOC_TDMA_SLOTLEN: wif->tdma_slot_length = val; break; case IEEE80211_IOC_TDMA_BINTERVAL: wif->tdma_binterval = val; break; default: break; } } /* * Convert an SNMP value to the kernel equivalent and also do sanity check * for each specific type. */ static int wlan_config_snmp2value(int which, int sval, int *value) { *value = 0; switch (which) { case IEEE80211_IOC_BURST: case IEEE80211_IOC_DFS: case IEEE80211_IOC_FF: case IEEE80211_IOC_TURBOP: case IEEE80211_IOC_WPS: case IEEE80211_IOC_BGSCAN: case IEEE80211_IOC_DOTD: case IEEE80211_IOC_DOTH: case IEEE80211_IOC_DWDS: case IEEE80211_IOC_POWERSAVE: case IEEE80211_IOC_APBRIDGE: case IEEE80211_IOC_HIDESSID: case IEEE80211_IOC_INACTIVITY: case IEEE80211_IOC_PUREG: case IEEE80211_IOC_PUREN: case IEEE80211_IOC_HTCONF: case IEEE80211_IOC_HTCOMPAT: case IEEE80211_IOC_RIFS: if (sval == TruthValue_true) *value = 1; else if (sval != TruthValue_false) return (SNMP_ERR_INCONS_VALUE); break; case IEEE80211_IOC_REGDOMAIN: break; case IEEE80211_IOC_SSID: break; case IEEE80211_IOC_CURCHAN: break; case IEEE80211_IOC_TXPOWER: *value = sval * 2; break; case IEEE80211_IOC_FRAGTHRESHOLD: if (sval < IEEE80211_FRAG_MIN || sval > IEEE80211_FRAG_MAX) return (SNMP_ERR_INCONS_VALUE); *value = sval; break; case IEEE80211_IOC_RTSTHRESHOLD: if (sval < IEEE80211_RTS_MIN || sval > IEEE80211_RTS_MAX) return (SNMP_ERR_INCONS_VALUE); *value = sval; break; case IEEE80211_IOC_BGSCAN_IDLE: if (sval < WLAN_BGSCAN_IDLE_MIN) return (SNMP_ERR_INCONS_VALUE); *value = sval; break; case IEEE80211_IOC_BGSCAN_INTERVAL: if (sval < WLAN_SCAN_VALID_MIN) return (SNMP_ERR_INCONS_VALUE); *value = sval; break; case IEEE80211_IOC_BMISSTHRESHOLD: if (sval < IEEE80211_HWBMISS_MIN || sval > IEEE80211_HWBMISS_MAX) return (SNMP_ERR_INCONS_VALUE); *value = sval; break; case IEEE80211_IOC_BSSID: break; case IEEE80211_IOC_ROAMING: switch (sval) { case wlanIfaceRoamingMode_device: *value = IEEE80211_ROAMING_DEVICE; break; case wlanIfaceRoamingMode_manual: *value = IEEE80211_ROAMING_MANUAL; break; case wlanIfaceRoamingMode_auto: *value = IEEE80211_ROAMING_AUTO; break; default: return (SNMP_ERR_INCONS_VALUE); } break; case IEEE80211_IOC_BEACON_INTERVAL: if (sval < IEEE80211_BINTVAL_MIN || sval > IEEE80211_BINTVAL_MAX) return (SNMP_ERR_INCONS_VALUE); *value = sval; break; case IEEE80211_IOC_DTIM_PERIOD: if (sval < IEEE80211_DTIM_MIN || sval > IEEE80211_DTIM_MAX) return (SNMP_ERR_INCONS_VALUE); *value = sval; break; case IEEE80211_IOC_PROTMODE: switch (sval) { case wlanIfaceDot11gProtMode_cts: *value = IEEE80211_PROTMODE_CTS; break; case wlanIfaceDot11gProtMode_rtscts: *value = IEEE80211_PROTMODE_RTSCTS; break; case wlanIfaceDot11gProtMode_off: *value = IEEE80211_PROTMODE_OFF; break; default: return (SNMP_ERR_INCONS_VALUE); } break; case IEEE80211_IOC_AMPDU: switch (sval) { case WlanIfaceDot11nPduType_disabled: break; case WlanIfaceDot11nPduType_txOnly: *value = 1; break; case WlanIfaceDot11nPduType_rxOnly: *value = 2; break; case WlanIfaceDot11nPduType_txAndRx: *value = 3; break; default: return (SNMP_ERR_INCONS_VALUE); } break; case IEEE80211_IOC_AMPDU_DENSITY: switch (sval) { case 0: *value = IEEE80211_HTCAP_MPDUDENSITY_NA; break; case 25: *value = IEEE80211_HTCAP_MPDUDENSITY_025; break; case 50: *value = IEEE80211_HTCAP_MPDUDENSITY_05; break; case 100: *value = IEEE80211_HTCAP_MPDUDENSITY_1; break; case 200: *value = IEEE80211_HTCAP_MPDUDENSITY_2; break; case 400: *value = IEEE80211_HTCAP_MPDUDENSITY_4; break; case 800: *value = IEEE80211_HTCAP_MPDUDENSITY_8; break; case 1600: *value = IEEE80211_HTCAP_MPDUDENSITY_16; break; default: return (SNMP_ERR_INCONS_VALUE); } break; case IEEE80211_IOC_AMPDU_LIMIT: switch (sval) { case 8192: *value = IEEE80211_HTCAP_MAXRXAMPDU_8K; break; case 16384: *value = IEEE80211_HTCAP_MAXRXAMPDU_16K; break; case 32768: *value = IEEE80211_HTCAP_MAXRXAMPDU_32K; break; case 65536: *value = IEEE80211_HTCAP_MAXRXAMPDU_64K; break; default: return (SNMP_ERR_INCONS_VALUE); } break; case IEEE80211_IOC_AMSDU: switch (sval) { case WlanIfaceDot11nPduType_disabled: break; case WlanIfaceDot11nPduType_txOnly: *value = 1; break; case WlanIfaceDot11nPduType_rxOnly: *value = 2; break; case WlanIfaceDot11nPduType_txAndRx: *value = 3; break; default: return (SNMP_ERR_INCONS_VALUE); } break; case IEEE80211_IOC_AMSDU_LIMIT: if (sval == 3839 || sval == 0) *value = IEEE80211_HTCAP_MAXAMSDU_3839; else if (sval == 7935) *value = IEEE80211_HTCAP_MAXAMSDU_7935; else return (SNMP_ERR_INCONS_VALUE); break; case IEEE80211_IOC_HTPROTMODE: switch (sval) { case wlanIfaceDot11nHTProtMode_rts: *value = IEEE80211_PROTMODE_RTSCTS; break; case wlanIfaceDot11nHTProtMode_off: break; default: return (SNMP_ERR_INCONS_VALUE); } break; case IEEE80211_IOC_SHORTGI: if (sval == TruthValue_true) *value = IEEE80211_HTCAP_SHORTGI20 | IEEE80211_HTCAP_SHORTGI40; else if (sval != TruthValue_false) return (SNMP_ERR_INCONS_VALUE); break; case IEEE80211_IOC_SMPS: switch (sval) { case wlanIfaceDot11nSMPSMode_disabled: *value = IEEE80211_HTCAP_SMPS_OFF; break; case wlanIfaceDot11nSMPSMode_static: *value = IEEE80211_HTCAP_SMPS_ENA; break; case wlanIfaceDot11nSMPSMode_dynamic: *value = IEEE80211_HTCAP_SMPS_DYNAMIC; break; default: return (SNMP_ERR_INCONS_VALUE); } break; case IEEE80211_IOC_TDMA_SLOT: if (sval < 0 || sval > WLAN_TDMA_MAXSLOTS) /* XXX */ return (SNMP_ERR_INCONS_VALUE); *value = sval; break; case IEEE80211_IOC_TDMA_SLOTCNT: if (sval < 0 || sval > WLAN_TDMA_MAXSLOTS) /* XXX */ return (SNMP_ERR_INCONS_VALUE); *value = sval; break; case IEEE80211_IOC_TDMA_SLOTLEN: if (sval < 2*100 || sval > 0xfffff) /* XXX */ return (SNMP_ERR_INCONS_VALUE); *value = sval; break; case IEEE80211_IOC_TDMA_BINTERVAL: if (sval < 1) /* XXX */ return (SNMP_ERR_INCONS_VALUE); *value = sval; break; default: return (SNMP_ERR_INCONS_VALUE); } return (SNMP_ERR_NOERROR); } /* * Sanity checks for the wlanIfaceConfigTable. */ static int wlan_config_check(struct wlan_iface *wif, int op) { switch (op) { case IEEE80211_IOC_BURST: if ((wif->drivercaps & (0x1 << WlanDriverCaps_burst)) == 0) { wif->packet_burst = TruthValue_false; return (-1); } break; case IEEE80211_IOC_DFS: if ((wif->drivercaps & (0x1 << WlanDriverCaps_dfs)) == 0) { wif->dyn_frequency = TruthValue_false; return (-1); } break; case IEEE80211_IOC_FF: if ((wif->drivercaps & (0x1 << WlanDriverCaps_athFastFrames)) == 0) { wif->fast_frames = TruthValue_false; return (-1); } break; case IEEE80211_IOC_TURBOP: if ((wif->drivercaps & (0x1 << WlanDriverCaps_athTurbo)) == 0) { wif->dturbo = TruthValue_false; return (-1); } break; case IEEE80211_IOC_TXPOWER: if ((wif->drivercaps & (0x1 << WlanDriverCaps_txPmgt)) == 0) { wif->tx_power = 0; return (-1); } break; case IEEE80211_IOC_FRAGTHRESHOLD: if ((wif->drivercaps & (0x1 << WlanDriverCaps_txFrag)) == 0) { wif->frag_threshold = IEEE80211_FRAG_MAX; return (-1); } break; case IEEE80211_IOC_DWDS: if ((wif->drivercaps & (0x1 << WlanDriverCaps_wds)) == 0) { wif->dynamic_wds = TruthValue_false; return (-1); } break; case IEEE80211_IOC_POWERSAVE: if ((wif->drivercaps & (0x1 << WlanDriverCaps_pmgt)) == 0) { wif->power_save = TruthValue_false; return (-1); } break; case IEEE80211_IOC_BEACON_INTERVAL: if (wif->mode != WlanIfaceOperatingModeType_hostAp && wif->mode != WlanIfaceOperatingModeType_meshPoint && wif->mode != WlanIfaceOperatingModeType_ibss) { wif->beacon_interval = 100; /* XXX */ return (-1); } break; case IEEE80211_IOC_DTIM_PERIOD: if (wif->mode != WlanIfaceOperatingModeType_hostAp && wif->mode != WlanIfaceOperatingModeType_meshPoint && wif->mode != WlanIfaceOperatingModeType_ibss) { wif->dtim_period = 1; /* XXX */ return (-1); } break; case IEEE80211_IOC_PUREN: if ((wif->htcaps & (0x1 << WlanHTCaps_htcHt)) == 0) { wif->dot11n_pure = TruthValue_false; return (-1); } break; case IEEE80211_IOC_AMPDU: if ((wif->htcaps & (0x1 << WlanHTCaps_htcAmpdu)) == 0) { wif->ampdu = WlanIfaceDot11nPduType_disabled; return (-1); } break; case IEEE80211_IOC_AMSDU: if ((wif->htcaps & (0x1 << WlanHTCaps_htcAmsdu)) == 0) { wif->amsdu = WlanIfaceDot11nPduType_disabled; return (-1); } break; case IEEE80211_IOC_RIFS: if ((wif->htcaps & (0x1 << WlanHTCaps_htcRifs)) == 0) { wif->rifs = TruthValue_false; return (-1); } break; case IEEE80211_IOC_SHORTGI: if ((wif->htcaps & (0x1 << WlanHTCaps_shortGi20 | 0x1 << WlanHTCaps_shortGi40)) == 0) { wif->short_gi = TruthValue_false; return (-1); } break; case IEEE80211_IOC_SMPS: if ((wif->htcaps & (0x1 << WlanHTCaps_htcSmps)) == 0) { wif->smps_mode = wlanIfaceDot11nSMPSMode_disabled; return (-1); } break; case IEEE80211_IOC_TDMA_SLOT: if ((wif->drivercaps & (0x1 << WlanDriverCaps_tdma)) == 0) { wif->tdma_slot = 0; return (-1); } break; case IEEE80211_IOC_TDMA_SLOTCNT: if ((wif->drivercaps & (0x1 << WlanDriverCaps_tdma)) == 0) { wif->tdma_slot_count = 0; return (-1); } break; case IEEE80211_IOC_TDMA_SLOTLEN: if ((wif->drivercaps & (0x1 << WlanDriverCaps_tdma)) == 0) { wif->tdma_slot_length = 0; return (-1); } break; case IEEE80211_IOC_TDMA_BINTERVAL: if ((wif->drivercaps & (0x1 << WlanDriverCaps_tdma)) == 0) { wif->tdma_binterval = 0; return (-1); } break; default: break; } return (0); } static int wlan_config_get_intval(struct wlan_iface *wif, int op) { int val = 0; size_t argsize = 0; if (wlan_config_check(wif, op) < 0) return (0); if (wlan_ioctl(wif->wname, op, &val, NULL, &argsize, 0) < 0) return (-1); wlan_config_set_snmp_intval(wif, op, val); return (0); } static int wlan_config_set_intval(struct wlan_iface *wif, int op, int sval) { size_t argsize = 0; int val; if (wlan_config_check(wif, op) < 0) return (-1); if (wlan_config_snmp2value(op, sval, &val) != SNMP_ERR_NOERROR) return (-1); if (wlan_ioctl(wif->wname, op, &val, NULL, &argsize, 1) < 0) return (-1); wlan_config_set_snmp_intval(wif, op, val); return (0); } int wlan_config_get_ioctl(struct wlan_iface *wif, int which) { int op; switch (which) { case LEAF_wlanIfaceCountryCode: /* FALLTHROUGH */ case LEAF_wlanIfaceRegDomain: return (wlan_config_get_country(wif)); case LEAF_wlanIfaceDesiredSsid: return (wlan_config_get_dssid(wif)); case LEAF_wlanIfaceDesiredChannel: return (wlan_config_get_dchannel(wif)); case LEAF_wlanIfaceDesiredBssid: return (wlan_config_get_bssid(wif)); default: op = wlan_config_snmp2ioctl(which); return (wlan_config_get_intval(wif, op)); } return (-1); } int wlan_config_set_ioctl(struct wlan_iface *wif, int which, int val, char *strval, int len) { int op; switch (which) { case LEAF_wlanIfaceCountryCode: return (wlan_config_set_country(wif, strval, wif->reg_domain)); case LEAF_wlanIfaceRegDomain: return (wlan_config_set_country(wif, wif->country_code, val)); case LEAF_wlanIfaceDesiredSsid: return (wlan_config_set_dssid(wif, strval, len)); case LEAF_wlanIfaceDesiredChannel: return (wlan_config_set_dchannel(wif, val)); case LEAF_wlanIfaceDesiredBssid: return (wlan_config_set_bssid(wif, strval)); default: op = wlan_config_snmp2ioctl(which); return (wlan_config_set_intval(wif, op, val)); } return (-1); } static uint32_t wlan_snmp_to_scan_flags(int flags) { int sr_flags = 0; if ((flags & (0x1 << WlanScanFlagsType_noSelection)) != 0) sr_flags |= IEEE80211_IOC_SCAN_NOPICK; if ((flags & (0x1 << WlanScanFlagsType_activeScan)) != 0) sr_flags |= IEEE80211_IOC_SCAN_ACTIVE; if ((flags & (0x1 << WlanScanFlagsType_pickFirst)) != 0) sr_flags |= IEEE80211_IOC_SCAN_PICK1ST; if ((flags & (0x1 << WlanScanFlagsType_backgroundScan)) != 0) sr_flags |= IEEE80211_IOC_SCAN_BGSCAN; if ((flags & (0x1 << WlanScanFlagsType_once)) != 0) sr_flags |= IEEE80211_IOC_SCAN_ONCE; if ((flags & (0x1 << WlanScanFlagsType_noBroadcast)) != 0) sr_flags |= IEEE80211_IOC_SCAN_NOBCAST; if ((flags & (0x1 << WlanScanFlagsType_noAutoSequencing)) != 0) sr_flags |= IEEE80211_IOC_SCAN_NOJOIN; if ((flags & (0x1 << WlanScanFlagsType_flushCashe)) != 0) sr_flags |= IEEE80211_IOC_SCAN_FLUSH; if ((flags & (0x1 << WlanScanFlagsType_chechCashe)) != 0) sr_flags |= IEEE80211_IOC_SCAN_CHECK; return (sr_flags); } int wlan_set_scan_config(struct wlan_iface *wif) { int val = 0; size_t argsize; struct ieee80211_scan_req sr; memset(&sr, 0, sizeof(sr)); argsize = sizeof(struct ieee80211_scan_req); sr.sr_flags = wlan_snmp_to_scan_flags(wif->scan_flags); sr.sr_flags |= IEEE80211_IOC_SCAN_BGSCAN; sr.sr_duration = wif->scan_duration; sr.sr_mindwell = wif->scan_mindwell; sr.sr_maxdwell = wif->scan_maxdwell; sr.sr_nssid = 0; if (wlan_ioctl(wif->wname, IEEE80211_IOC_SCAN_REQ, &val, &sr, &argsize, 1) < 0) return (-1); wif->scan_status = wlanScanConfigStatus_running; return (0); } static uint32_t wlan_peercaps_to_snmp(uint32_t pcaps) { uint32_t scaps = 0; if ((pcaps & IEEE80211_CAPINFO_ESS) != 0) scaps |= (0x1 << WlanPeerCapabilityFlags_ess); if ((pcaps & IEEE80211_CAPINFO_IBSS) != 0) scaps |= (0x1 << WlanPeerCapabilityFlags_ibss); if ((pcaps & IEEE80211_CAPINFO_CF_POLLABLE) != 0) scaps |= (0x1 << WlanPeerCapabilityFlags_cfPollable); if ((pcaps & IEEE80211_CAPINFO_CF_POLLREQ) != 0) scaps |= (0x1 << WlanPeerCapabilityFlags_cfPollRequest); if ((pcaps & IEEE80211_CAPINFO_PRIVACY) != 0) scaps |= (0x1 << WlanPeerCapabilityFlags_privacy); if ((pcaps & IEEE80211_CAPINFO_SHORT_PREAMBLE) != 0) scaps |= (0x1 << WlanPeerCapabilityFlags_shortPreamble); if ((pcaps & IEEE80211_CAPINFO_PBCC) != 0) scaps |= (0x1 << WlanPeerCapabilityFlags_pbcc); if ((pcaps & IEEE80211_CAPINFO_CHNL_AGILITY) != 0) scaps |= (0x1 << WlanPeerCapabilityFlags_channelAgility); if ((pcaps & IEEE80211_CAPINFO_SHORT_SLOTTIME) != 0) scaps |= (0x1 << WlanPeerCapabilityFlags_shortSlotTime); if ((pcaps & IEEE80211_CAPINFO_RSN) != 0) scaps |= (0x1 << WlanPeerCapabilityFlags_rsn); if ((pcaps & IEEE80211_CAPINFO_DSSSOFDM) != 0) scaps |= (0x1 << WlanPeerCapabilityFlags_dsssofdm); return (scaps); } static int wlan_add_new_scan_result(struct wlan_iface *wif, const struct ieee80211req_scan_result *isr, uint8_t *ssid) { struct wlan_scan_result *sr; if ((sr = wlan_scan_new_result(ssid, isr->isr_bssid)) == NULL) return (-1); sr->opchannel = wlan_channel_flags_to_snmp_phy(isr->isr_flags); sr->rssi = isr->isr_rssi; sr->frequency = isr->isr_freq; sr->noise = isr->isr_noise; sr->bintval = isr->isr_intval; sr->capinfo = wlan_peercaps_to_snmp(isr->isr_capinfo); if (wlan_scan_add_result(wif, sr) < 0) { wlan_scan_free_result(sr); return (-1); } return (0); } int wlan_get_scan_results(struct wlan_iface *wif) { int ssidlen, val = 0; uint8_t buf[24 * 1024]; size_t argsize; const uint8_t *cp, *idp; uint8_t ssid[IEEE80211_NWID_LEN + 1]; struct ieee80211req_scan_result isr; argsize = sizeof(buf); if (wlan_ioctl(wif->wname, IEEE80211_IOC_SCAN_RESULTS, &val, &buf, &argsize, 0) < 0) return (-1); if (argsize < sizeof(struct ieee80211req_scan_result)) return (0); cp = buf; do { memcpy(&isr, cp, sizeof(struct ieee80211req_scan_result)); memset(ssid, 0, IEEE80211_NWID_LEN + 1); if (isr.isr_meshid_len) { idp = cp + isr.isr_ie_off + isr.isr_ssid_len; ssidlen = isr.isr_meshid_len; } else { idp = cp + isr.isr_ie_off; ssidlen = isr.isr_ssid_len; } if (ssidlen > IEEE80211_NWID_LEN) ssidlen = IEEE80211_NWID_LEN; memcpy(ssid, idp, ssidlen); ssid[IEEE80211_NWID_LEN] = '\0'; (void)wlan_add_new_scan_result(wif, &isr, ssid); cp += isr.isr_len; argsize -= isr.isr_len; } while (argsize >= sizeof(struct ieee80211req_scan_result)); return (0); } int wlan_get_stats(struct wlan_iface *wif) { struct ifreq ifr; memset(&ifr, 0, sizeof(struct ifreq)); strlcpy(ifr.ifr_name, wif->wname, IFNAMSIZ); ifr.ifr_data = (caddr_t) &wif->stats; if (ioctl(sock, SIOCG80211STATS, &ifr) < 0) { syslog(LOG_ERR, "iface %s - ioctl(SIOCG80211STATS) failed: %s", wif->wname, strerror(errno)); return (-1); } return (0); } int wlan_get_wepmode(struct wlan_iface *wif) { int val = 0; size_t argsize = 0; if (wlan_ioctl(wif->wname, IEEE80211_IOC_WEP, &val, NULL, &argsize, 0) < 0 || val == IEEE80211_WEP_NOSUP) { wif->wepsupported = 0; /* XXX */ wif->wepmode = wlanWepMode_off; wif->weptxkey = 0; return (-1); } wif->wepsupported = 1; switch (val) { case IEEE80211_WEP_ON: wif->wepmode = wlanWepMode_on; break; case IEEE80211_WEP_MIXED: wif->wepmode = wlanWepMode_mixed; break; case IEEE80211_WEP_OFF: /* FALLTHROUGH */ default: wif->wepmode = wlanWepMode_off; break; } return (0); } int wlan_set_wepmode(struct wlan_iface *wif) { int val; size_t argsize = 0; if (!wif->wepsupported) return (-1); switch (wif->wepmode) { case wlanWepMode_off: val = IEEE80211_WEP_OFF; break; case wlanWepMode_on: val = IEEE80211_WEP_ON; break; case wlanWepMode_mixed: val = IEEE80211_WEP_MIXED; break; default: return (-1); } if (wlan_ioctl(wif->wname, IEEE80211_IOC_WEP, &val, NULL, &argsize, 1) < 0) return (-1); return (0); } int wlan_get_weptxkey(struct wlan_iface *wif) { int val; size_t argsize = 0; if (!wif->wepsupported) return (0); if (wlan_ioctl(wif->wname, IEEE80211_IOC_WEPTXKEY, &val, NULL, &argsize, 0) < 0) return (-1); if (val == IEEE80211_KEYIX_NONE) wif->weptxkey = 0; else wif->weptxkey = val + 1; return (0); } int wlan_set_weptxkey(struct wlan_iface *wif) { int val; size_t argsize = 0; if (!wif->wepsupported) return (0); if (wif->weptxkey >= IEEE80211_WEP_NKID) return (-1); if (wif->weptxkey == 0) val = IEEE80211_KEYIX_NONE; else val = wif->weptxkey - 1; if (wlan_ioctl(wif->wname, IEEE80211_IOC_WEPTXKEY, &val, NULL, &argsize, 1) < 0) return (-1); return (0); } int wlan_get_wepkeys(struct wlan_iface *wif __unused) { /* XXX: should they be visible via SNMP */ return (0); } int wlan_set_wepkeys(struct wlan_iface *wif __unused) { /* XXX: should they be configurable via SNMP */ return (0); } int wlan_get_mac_policy(struct wlan_iface *wif) { int val = IEEE80211_MACCMD_POLICY; size_t argsize = 0; struct ieee80211req ireq; memset(&ireq, 0, sizeof(struct ieee80211req)); strlcpy(ireq.i_name, wif->wname, IFNAMSIZ); ireq.i_type = IEEE80211_IOC_MACCMD; ireq.i_val = IEEE80211_MACCMD_POLICY; if (ioctl(sock, SIOCG80211, &ireq) < 0) { if (errno != EINVAL) { syslog(LOG_ERR, "iface %s - get param: ioctl(%d) " "failed: %s", wif->wname, ireq.i_type, strerror(errno)); wif->macsupported = 0; return (-1); } else { wif->macsupported = 1; wif->mac_policy = wlanMACAccessControlPolicy_open; return (0); } } wif->macsupported = 1; switch (val) { case IEEE80211_MACCMD_POLICY_ALLOW: wif->mac_policy = wlanMACAccessControlPolicy_allow; break; case IEEE80211_MACCMD_POLICY_DENY: wif->mac_policy = wlanMACAccessControlPolicy_deny; break; case IEEE80211_MACCMD_POLICY_RADIUS: wif->mac_policy = wlanMACAccessControlPolicy_radius; break; case IEEE80211_MACCMD_POLICY_OPEN: /* FALLTHROUGH */ default: wif->mac_policy = wlanMACAccessControlPolicy_open; break; } argsize = 0; val = IEEE80211_MACCMD_LIST; if (wlan_ioctl(wif->wname, IEEE80211_IOC_MACCMD, &val, NULL, &argsize, 0) < 0) return (-1); wif->mac_nacls = argsize / sizeof(struct ieee80211req_maclist *); return (0); } int wlan_set_mac_policy(struct wlan_iface *wif) { int val; size_t argsize = 0; if (!wif->macsupported) return (-1); switch (wif->mac_policy) { case wlanMACAccessControlPolicy_allow: val = IEEE80211_MACCMD_POLICY_ALLOW; break; case wlanMACAccessControlPolicy_deny: val = IEEE80211_MACCMD_POLICY_DENY; break; case wlanMACAccessControlPolicy_radius: val = IEEE80211_MACCMD_POLICY_RADIUS; break; case wlanMACAccessControlPolicy_open: val = IEEE80211_MACCMD_POLICY_OPEN; break; default: return (-1); } if (wlan_ioctl(wif->wname, IEEE80211_IOC_MACCMD, &val, NULL, &argsize, 1) < 0) return (-1); return (0); } int wlan_flush_mac_mac(struct wlan_iface *wif) { int val = IEEE80211_MACCMD_FLUSH; size_t argsize = 0; if (wlan_ioctl(wif->wname, IEEE80211_IOC_MACCMD, &val, NULL, &argsize, 1) < 0) return (-1); return (0); } static int wlan_add_mac_macinfo(struct wlan_iface *wif, const struct ieee80211req_maclist *ml) { struct wlan_mac_mac *mmac; if ((mmac = wlan_mac_new_mac(ml->ml_macaddr)) == NULL) return (-1); mmac->mac_status = RowStatus_active; if (wlan_mac_add_mac(wif, mmac) < 0) { wlan_mac_free_mac(mmac); return (-1); } return (0); } int wlan_get_mac_acl_macs(struct wlan_iface *wif) { int i, nacls, val = IEEE80211_MACCMD_LIST; size_t argsize = 0; uint8_t *data; struct ieee80211req ireq; const struct ieee80211req_maclist *acllist; if (wif->mac_policy == wlanMACAccessControlPolicy_radius) { wif->mac_nacls = 0; return (0); } memset(&ireq, 0, sizeof(struct ieee80211req)); strlcpy(ireq.i_name, wif->wname, IFNAMSIZ); ireq.i_type = IEEE80211_IOC_MACCMD; ireq.i_val = IEEE80211_MACCMD_LIST; if (ioctl(sock, SIOCG80211, &ireq) < 0) { if (errno != EINVAL) { syslog(LOG_ERR, "iface %s - get param: ioctl(%d) " "failed: %s", wif->wname, ireq.i_type, strerror(errno)); wif->macsupported = 0; return (-1); } } if (argsize == 0) { wif->mac_nacls = 0; return (0); } if ((data = (uint8_t *)malloc(argsize)) == NULL) return (-1); if (wlan_ioctl(wif->wname, IEEE80211_IOC_MACCMD, &val, data, &argsize, 0) < 0) return (-1); nacls = argsize / sizeof(*acllist); acllist = (struct ieee80211req_maclist *) data; for (i = 0; i < nacls; i++) (void)wlan_add_mac_macinfo(wif, acllist + i); wif->mac_nacls = nacls; return (0); } int wlan_add_mac_acl_mac(struct wlan_iface *wif, struct wlan_mac_mac *mmac) { int val = 0; size_t argsize = IEEE80211_ADDR_LEN; struct ieee80211req_mlme mlme; if (wlan_ioctl(wif->wname, IEEE80211_IOC_ADDMAC, &val, mmac->mac, &argsize, 1) < 0) return (-1); mmac->mac_status = RowStatus_active; /* If policy is deny, try to kick the station just in case. */ if (wif->mac_policy != wlanMACAccessControlPolicy_deny) return (0); memset(&mlme, 0, sizeof(mlme)); mlme.im_op = IEEE80211_MLME_DEAUTH; mlme.im_reason = IEEE80211_REASON_AUTH_EXPIRE; memcpy(mlme.im_macaddr, mmac->mac, IEEE80211_ADDR_LEN); argsize = sizeof(struct ieee80211req_mlme); if (wlan_ioctl(wif->wname, IEEE80211_IOC_MLME, &val, &mlme, &argsize, 1) < 0 && errno != ENOENT) return (-1); return (0); } int wlan_del_mac_acl_mac(struct wlan_iface *wif, struct wlan_mac_mac *mmac) { int val = 0; size_t argsize = IEEE80211_ADDR_LEN; struct ieee80211req_mlme mlme; if (wlan_ioctl(wif->wname, IEEE80211_IOC_DELMAC, &val, mmac->mac, &argsize, 1) < 0) return (-1); mmac->mac_status = RowStatus_active; /* If policy is allow, try to kick the station just in case. */ if (wif->mac_policy != wlanMACAccessControlPolicy_allow) return (0); memset(&mlme, 0, sizeof(mlme)); mlme.im_op = IEEE80211_MLME_DEAUTH; mlme.im_reason = IEEE80211_REASON_AUTH_EXPIRE; memcpy(mlme.im_macaddr, mmac->mac, IEEE80211_ADDR_LEN); argsize = sizeof(struct ieee80211req_mlme); if (wlan_ioctl(wif->wname, IEEE80211_IOC_MLME, &val, &mlme, &argsize, 1) < 0 && errno != ENOENT) return (-1); return (0); } int wlan_peer_set_vlan(struct wlan_iface *wif, struct wlan_peer *wip, int vlan) { int val = 0; size_t argsize; struct ieee80211req_sta_vlan vreq; memcpy(vreq.sv_macaddr, wip->pmac, IEEE80211_ADDR_LEN); vreq.sv_vlan = vlan; argsize = sizeof(struct ieee80211req_sta_vlan); if (wlan_ioctl(wif->wname, IEEE80211_IOC_STA_VLAN, &val, &vreq, &argsize, 1) < 0) return (-1); wip->vlan = vlan; return (0); } /* XXX */ #ifndef IEEE80211_NODE_AUTH #define IEEE80211_NODE_AUTH 0x000001 /* authorized for data */ #define IEEE80211_NODE_QOS 0x000002 /* QoS enabled */ #define IEEE80211_NODE_ERP 0x000004 /* ERP enabled */ #define IEEE80211_NODE_PWR_MGT 0x000010 /* power save mode enabled */ #define IEEE80211_NODE_AREF 0x000020 /* authentication ref held */ #define IEEE80211_NODE_HT 0x000040 /* HT enabled */ #define IEEE80211_NODE_HTCOMPAT 0x000080 /* HT setup w/ vendor OUI's */ #define IEEE80211_NODE_WPS 0x000100 /* WPS association */ #define IEEE80211_NODE_TSN 0x000200 /* TSN association */ #define IEEE80211_NODE_AMPDU_RX 0x000400 /* AMPDU rx enabled */ #define IEEE80211_NODE_AMPDU_TX 0x000800 /* AMPDU tx enabled */ #define IEEE80211_NODE_MIMO_PS 0x001000 /* MIMO power save enabled */ #define IEEE80211_NODE_MIMO_RTS 0x002000 /* send RTS in MIMO PS */ #define IEEE80211_NODE_RIFS 0x004000 /* RIFS enabled */ #define IEEE80211_NODE_SGI20 0x008000 /* Short GI in HT20 enabled */ #define IEEE80211_NODE_SGI40 0x010000 /* Short GI in HT40 enabled */ #define IEEE80211_NODE_ASSOCID 0x020000 /* xmit requires associd */ #define IEEE80211_NODE_AMSDU_RX 0x040000 /* AMSDU rx enabled */ #define IEEE80211_NODE_AMSDU_TX 0x080000 /* AMSDU tx enabled */ #endif static uint32_t wlan_peerstate_to_snmp(uint32_t pstate) { uint32_t sstate = 0; if ((pstate & IEEE80211_NODE_AUTH) != 0) sstate |= (0x1 << WlanIfacePeerFlagsType_authorizedForData); if ((pstate & IEEE80211_NODE_QOS) != 0) sstate |= (0x1 << WlanIfacePeerFlagsType_qosEnabled); if ((pstate & IEEE80211_NODE_ERP) != 0) sstate |= (0x1 << WlanIfacePeerFlagsType_erpEnabled); if ((pstate & IEEE80211_NODE_PWR_MGT) != 0) sstate |= (0x1 << WlanIfacePeerFlagsType_powerSaveMode); if ((pstate & IEEE80211_NODE_AREF) != 0) sstate |= (0x1 << WlanIfacePeerFlagsType_authRefHeld); if ((pstate & IEEE80211_NODE_HT) != 0) sstate |= (0x1 << WlanIfacePeerFlagsType_htEnabled); if ((pstate & IEEE80211_NODE_HTCOMPAT) != 0) sstate |= (0x1 << WlanIfacePeerFlagsType_htCompat); if ((pstate & IEEE80211_NODE_WPS) != 0) sstate |= (0x1 << WlanIfacePeerFlagsType_wpsAssoc); if ((pstate & IEEE80211_NODE_TSN) != 0) sstate |= (0x1 << WlanIfacePeerFlagsType_tsnAssoc); if ((pstate & IEEE80211_NODE_AMPDU_RX) != 0) sstate |= (0x1 << WlanIfacePeerFlagsType_ampduRx); if ((pstate & IEEE80211_NODE_AMPDU_TX) != 0) sstate |= (0x1 << WlanIfacePeerFlagsType_ampduTx); if ((pstate & IEEE80211_NODE_MIMO_PS) != 0) sstate |= (0x1 << WlanIfacePeerFlagsType_mimoPowerSave); if ((pstate & IEEE80211_NODE_MIMO_RTS) != 0) sstate |= (0x1 << WlanIfacePeerFlagsType_sendRts); if ((pstate & IEEE80211_NODE_RIFS) != 0) sstate |= (0x1 << WlanIfacePeerFlagsType_rifs); if ((pstate & IEEE80211_NODE_SGI20) != 0) sstate |= (0x1 << WlanIfacePeerFlagsType_shortGiHT20); if ((pstate & IEEE80211_NODE_SGI40) != 0) sstate |= (0x1 << WlanIfacePeerFlagsType_shortGiHT40); if ((pstate & IEEE80211_NODE_AMSDU_RX) != 0) sstate |= (0x1 << WlanIfacePeerFlagsType_amsduRx); if ((pstate & IEEE80211_NODE_AMSDU_TX) != 0) sstate |= (0x1 << WlanIfacePeerFlagsType_amsduTx); return (sstate); } static struct wlan_peer * wlan_add_peerinfo(const struct ieee80211req_sta_info *si) { struct wlan_peer *wip; if ((wip = wlan_new_peer(si->isi_macaddr))== NULL) return (NULL); wip->associd = IEEE80211_AID(si->isi_associd); wip->vlan = si->isi_vlan; wip->frequency = si->isi_freq; wip->fflags = si->isi_flags; wip->txrate = si->isi_txrate; wip->rssi = si->isi_rssi; wip->idle = si->isi_inact; wip->txseqs = si->isi_txseqs[0]; /* XXX */ wip->rxseqs = si->isi_rxseqs[0]; /* XXX */ wip->txpower = si->isi_txpower; wip->capinfo = wlan_peercaps_to_snmp(si->isi_capinfo); wip->state = wlan_peerstate_to_snmp(si->isi_state); wip->local_id = si->isi_localid; wip->peer_id = si->isi_peerid; return (wip); } int wlan_get_peerinfo(struct wlan_iface *wif) { union { struct ieee80211req_sta_req req; uint8_t buf[24 * 1024]; } u; const uint8_t *cp; int val = 0; size_t len; struct ieee80211req_sta_info si; struct wlan_peer *wip; /* Get all stations - broadcast address */ (void) memset(u.req.is_u.macaddr, 0xff, IEEE80211_ADDR_LEN); len = sizeof(u); if (wlan_ioctl(wif->wname, IEEE80211_IOC_STA_INFO, & val, &u, &len, 0) < 0) return (-1); if (len < sizeof(struct ieee80211req_sta_info)) return (-1); cp = (const uint8_t *) u.req.info; do { memcpy(&si, cp, sizeof(struct ieee80211req_sta_info)); if ((wip = wlan_add_peerinfo(&si)) != NULL && wlan_add_peer(wif, wip) < 0) wlan_free_peer(wip); cp += si.isi_len, len -= si.isi_len; } while (len >= sizeof(struct ieee80211req_sta_info)); return (0); } /************************************************************************ * Wireless MESH & HWMP sysctl config. */ const char wlan_sysctl_name[] = "net.wlan."; static const char *wlan_sysctl[] = { "mesh.retrytimeout", "mesh.holdingtimeout", "mesh.confirmtimeout", "mesh.maxretries", "hwmp.targetonly", "hwmp.replyforward", "hwmp.pathlifetime", "hwmp.roottimeout", "hwmp.rootint", "hwmp.rannint", "hwmp.inact", }; int32_t wlan_do_sysctl(struct wlan_config *cfg, enum wlan_syscl which, int set) { char mib_name[100]; int val, sval; size_t len, vlen; if (set) { vlen = sizeof(sval); switch (which) { case WLAN_MESH_RETRY_TO: sval = cfg->mesh_retryto; break; case WLAN_MESH_HOLDING_TO: sval = cfg->mesh_holdingto; break; case WLAN_MESH_CONFIRM_TO: sval = cfg->mesh_confirmto; break; case WLAN_MESH_MAX_RETRIES: sval = cfg->mesh_maxretries; break; case WLAN_HWMP_TARGET_ONLY: sval = cfg->hwmp_targetonly; break; case WLAN_HWMP_REPLY_FORWARD: sval = cfg->hwmp_replyforward; break; case WLAN_HWMP_PATH_LIFETIME: sval = cfg->hwmp_pathlifetime; break; case WLAN_HWMP_ROOT_TO: sval = cfg->hwmp_roottimeout; break; case WLAN_HWMP_ROOT_INT: sval = cfg->hwmp_rootint; break; case WLAN_HWMP_RANN_INT: sval = cfg->hwmp_rannint; break; case WLAN_HWMP_INACTIVITY_TO: sval = cfg->hwmp_inact; break; default: return (-1); } } else { if (which >= WLAN_SYSCTL_MAX) return (-1); vlen = 0; } strlcpy(mib_name, wlan_sysctl_name, sizeof(mib_name)); strlcat(mib_name, wlan_sysctl[which], sizeof(mib_name)); len = sizeof (val); if (sysctlbyname(mib_name, &val, &len, (set? &sval : NULL), vlen) < 0) { syslog(LOG_ERR, "sysctl(%s) failed - %s", mib_name, strerror(errno)); return (-1); } switch (which) { case WLAN_MESH_RETRY_TO: cfg->mesh_retryto = val; break; case WLAN_MESH_HOLDING_TO: cfg->mesh_holdingto = val; break; case WLAN_MESH_CONFIRM_TO: cfg->mesh_confirmto = val; break; case WLAN_MESH_MAX_RETRIES: cfg->mesh_maxretries = val; break; case WLAN_HWMP_TARGET_ONLY: cfg->hwmp_targetonly = val; break; case WLAN_HWMP_REPLY_FORWARD: cfg->hwmp_replyforward = val; break; case WLAN_HWMP_PATH_LIFETIME: cfg->hwmp_pathlifetime = val; break; case WLAN_HWMP_ROOT_TO: cfg->hwmp_roottimeout = val; break; case WLAN_HWMP_ROOT_INT: cfg->hwmp_rootint = val; break; case WLAN_HWMP_RANN_INT: cfg->hwmp_rannint = val; break; case WLAN_HWMP_INACTIVITY_TO: cfg->hwmp_inact = val; break; default: /* NOTREACHED */ abort(); } return (0); } int wlan_mesh_config_get(struct wlan_iface *wif, int which) { int op, val = 0; size_t argsize = 0; uint8_t data[32], *pd = NULL; switch (which) { case LEAF_wlanMeshTTL: op = IEEE80211_IOC_MESH_TTL; break; case LEAF_wlanMeshPeeringEnabled: op = IEEE80211_IOC_MESH_AP; break; case LEAF_wlanMeshForwardingEnabled: op = IEEE80211_IOC_MESH_FWRD; break; case LEAF_wlanMeshMetric: op = IEEE80211_IOC_MESH_PR_METRIC; pd = data; argsize = sizeof(data); break; case LEAF_wlanMeshPath: op = IEEE80211_IOC_MESH_PR_PATH; pd = data; argsize = sizeof(data); break; case LEAF_wlanMeshRoutesFlush: return (0); default: return (-1); } if (wlan_ioctl(wif->wname, op, &val, pd, &argsize, 0) < 0) return (-1); switch (which) { case LEAF_wlanMeshTTL: wif->mesh_ttl = val; break; case LEAF_wlanMeshPeeringEnabled: if (val) wif->mesh_peering = wlanMeshPeeringEnabled_true; else wif->mesh_peering = wlanMeshPeeringEnabled_false; break; case LEAF_wlanMeshForwardingEnabled: if (val) wif->mesh_forwarding = wlanMeshForwardingEnabled_true; else wif->mesh_forwarding = wlanMeshForwardingEnabled_false; break; case LEAF_wlanMeshMetric: data[argsize] = '\0'; if (strcmp(data, "AIRTIME") == 0) wif->mesh_metric = wlanMeshMetric_airtime; else wif->mesh_metric = wlanMeshMetric_unknown; break; case LEAF_wlanMeshPath: data[argsize] = '\0'; if (strcmp(data, "HWMP") == 0) wif->mesh_path = wlanMeshPath_hwmp; else wif->mesh_path = wlanMeshPath_unknown; } return (0); } int wlan_mesh_config_set(struct wlan_iface *wif, int which) { int op, val = 0; size_t argsize = 0; uint8_t data[32], *pd = NULL; switch (which) { case LEAF_wlanMeshTTL: op = IEEE80211_IOC_MESH_TTL; val = wif->mesh_ttl; break; case LEAF_wlanMeshPeeringEnabled: op = IEEE80211_IOC_MESH_AP; if (wif->mesh_peering == wlanMeshPeeringEnabled_true) val = 1; break; case LEAF_wlanMeshForwardingEnabled: if (wif->mesh_forwarding == wlanMeshForwardingEnabled_true) val = 1; op = IEEE80211_IOC_MESH_FWRD; break; case LEAF_wlanMeshMetric: op = IEEE80211_IOC_MESH_PR_METRIC; if (wif->mesh_metric == wlanMeshMetric_airtime) strcpy(data, "AIRTIME"); else return (-1); pd = data; argsize = sizeof(data); break; case LEAF_wlanMeshPath: op = IEEE80211_IOC_MESH_PR_PATH; if (wif->mesh_path == wlanMeshPath_hwmp) strcpy(data, "HWMP"); else return (-1); pd = data; argsize = sizeof(data); break; default: return (-1); } if (wlan_ioctl(wif->wname, op, &val, pd, &argsize, 1) < 0) return (-1); return(0); } int wlan_mesh_flush_routes(struct wlan_iface *wif) { int val = IEEE80211_MESH_RTCMD_FLUSH; size_t argsize = 0; if (wlan_ioctl(wif->wname, IEEE80211_IOC_MESH_RTCMD, &val, NULL, &argsize, 1) < 0) return (-1); return (0); } int wlan_mesh_add_route(struct wlan_iface *wif, struct wlan_mesh_route *wmr) { int val = IEEE80211_MESH_RTCMD_ADD; size_t argsize = IEEE80211_ADDR_LEN; if (wlan_ioctl(wif->wname, IEEE80211_IOC_MESH_RTCMD, &val, wmr->imroute.imr_dest, &argsize, 1) < 0) return (-1); wmr->mroute_status = RowStatus_active; return (0); } int wlan_mesh_del_route(struct wlan_iface *wif, struct wlan_mesh_route *wmr) { int val = IEEE80211_MESH_RTCMD_DELETE; size_t argsize = IEEE80211_ADDR_LEN; if (wlan_ioctl(wif->wname, IEEE80211_IOC_MESH_RTCMD, &val, wmr->imroute.imr_dest, &argsize, 1) < 0) return (-1); wmr->mroute_status = RowStatus_destroy; return (0); } int wlan_mesh_get_routelist(struct wlan_iface *wif) { int i, nroutes, val = IEEE80211_MESH_RTCMD_LIST; size_t argsize; struct ieee80211req_mesh_route routes[128]; struct ieee80211req_mesh_route *rt; struct wlan_mesh_route *wmr; argsize = sizeof(routes); if (wlan_ioctl(wif->wname, IEEE80211_IOC_MESH_RTCMD, &val, routes, &argsize, 0) < 0) /* XXX: ENOMEM? */ return (-1); nroutes = argsize / sizeof(*rt); for (i = 0; i < nroutes; i++) { rt = routes + i; if ((wmr = wlan_mesh_new_route(rt->imr_dest)) == NULL) return (-1); memcpy(&wmr->imroute, rt, sizeof(*rt)); wmr->mroute_status = RowStatus_active; if (wlan_mesh_add_rtentry(wif, wmr) < 0) wlan_mesh_free_route(wmr); } return (0); } int wlan_hwmp_config_get(struct wlan_iface *wif, int which) { int op, val = 0; size_t argsize = 0; switch (which) { case LEAF_wlanHWMPRootMode: op = IEEE80211_IOC_HWMP_ROOTMODE; break; case LEAF_wlanHWMPMaxHops: op = IEEE80211_IOC_HWMP_MAXHOPS; break; default: return (-1); } if (wlan_ioctl(wif->wname, op, &val, NULL, &argsize, 0) < 0) return (-1); switch (which) { case LEAF_wlanHWMPRootMode: switch (val) { case IEEE80211_HWMP_ROOTMODE_NORMAL: wif->hwmp_root_mode = wlanHWMPRootMode_normal; break; case IEEE80211_HWMP_ROOTMODE_PROACTIVE: wif->hwmp_root_mode = wlanHWMPRootMode_proactive; break; case IEEE80211_HWMP_ROOTMODE_RANN: wif->hwmp_root_mode = wlanHWMPRootMode_rann; break; case IEEE80211_HWMP_ROOTMODE_DISABLED: default: wif->hwmp_root_mode = wlanHWMPRootMode_disabled; break; } break; case LEAF_wlanHWMPMaxHops: wif->hwmp_max_hops = val; break; } return (0); } int wlan_hwmp_config_set(struct wlan_iface *wif, int which) { int op, val = 0; size_t argsize = 0; switch (which) { case LEAF_wlanHWMPRootMode: op = IEEE80211_IOC_HWMP_ROOTMODE; switch (wif->hwmp_root_mode) { case wlanHWMPRootMode_disabled: val = IEEE80211_HWMP_ROOTMODE_DISABLED; break; case wlanHWMPRootMode_normal: val = IEEE80211_HWMP_ROOTMODE_NORMAL; break; case wlanHWMPRootMode_proactive: val = IEEE80211_HWMP_ROOTMODE_PROACTIVE; break; case wlanHWMPRootMode_rann: val = IEEE80211_HWMP_ROOTMODE_RANN; break; default: return (-1); } break; case LEAF_wlanHWMPMaxHops: op = IEEE80211_IOC_HWMP_MAXHOPS; val = wif->hwmp_max_hops; break; default: return (-1); } if (wlan_ioctl(wif->wname, op, &val, NULL, &argsize, 1) < 0) return (-1); return (0); }