Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F109548615
D16654.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
83 KB
Referenced Files
None
Subscribers
None
D16654.diff
View Options
Index: head/contrib/bsnmp/gensnmptree/gensnmptree.1
===================================================================
--- head/contrib/bsnmp/gensnmptree/gensnmptree.1
+++ head/contrib/bsnmp/gensnmptree/gensnmptree.1
@@ -31,7 +31,7 @@
.\"
.\" $Begemot: gensnmptree.1 383 2006-05-30 07:40:49Z brandt_h $
.\"
-.Dd June 29, 2018
+.Dd April 2, 2019
.Dt GENSNMPTREE 1
.Os
.Sh NAME
@@ -100,25 +100,11 @@
is the last component of the OID.
.El
.It Fl F
-Together with
-.Fl E
-causes
-.Nm
-instead of the generation of enum definitions the generation of
-functions for checking a value to be one of the enumeration variants and
-for conversion between strings and the enum. The file is sent to standard
-output and is meant to be included into a C-file for compilation.
+emit definitions for C-functions includeable in a C-file that do some basic
+stuff on enums like value checking and conversion between value and strings.
.It Fl f
-This flag can be used together with
-.Fl E
-or when generating the tree files. It causes
-.Nm
-to emit static inline functions for checking a value to be one of the
-enumeration values and for conversion between strings and the enum.
-If used when generating the tree files, the preprocessor symbol
-.Ar SNMPTREE_TYPES
-must be defined when including the tree header file for these definitions
-to become visible.
+emit definitions for inline C-functions that do some basic
+stuff on enums like value checking and conversion between value and strings.
.It Fl h
Print a short help page.
.It Fl I Ar directory
@@ -136,36 +122,6 @@
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
Index: head/contrib/bsnmp/gensnmptree/gensnmptree.c
===================================================================
--- head/contrib/bsnmp/gensnmptree/gensnmptree.c
+++ head/contrib/bsnmp/gensnmptree/gensnmptree.c
@@ -110,7 +110,6 @@
static const char usgtxt[] = "\
Generate SNMP tables.\n\
-$Id$\n\
usage: gensnmptree [-dEeFfhlt] [-I directory] [-i infile] [-p prefix]\n\
[name]...\n\
options:\n\
@@ -127,6 +126,37 @@
-t generate a .def file\n\
";
+/**
+ * Program operation.
+ */
+enum op {
+ /** generate the tree */
+ OP_GEN,
+
+ /** extract OIDs */
+ OP_EXTRACT,
+
+ /** print the parsed tree */
+ OP_TREE,
+
+ /** extract enums */
+ OP_ENUMS,
+};
+
+/**
+ * Which functions to create.
+ */
+enum gen_funcs {
+ /** none */
+ GEN_FUNCS_NONE,
+
+ /** functions for header files */
+ GEN_FUNCS_H,
+
+ /** functions for C files */
+ GEN_FUNCS_C,
+};
+
/*
* A node in the OID tree
*/
@@ -162,15 +192,18 @@
uint32_t index; /* index for table entry */
char *func; /* function for tables */
struct node_list subs;
+ char *subtypes[SNMP_INDEXES_MAX];
} entry;
struct leaf {
enum snmp_syntax syntax; /* syntax for this leaf */
char *func; /* function name */
+ char *subtype; /* subtype */
} leaf;
struct column {
enum snmp_syntax syntax; /* syntax for this column */
+ char *subtype; /* subtype */
} column;
} u;
};
@@ -214,7 +247,7 @@
{
void *ptr;
- if ((ptr = malloc(size)) == NULL)
+ if ((ptr = calloc(1, size)) == NULL)
err(1, "allocing %zu bytes", size);
return (ptr);
@@ -710,12 +743,14 @@
* token.
*/
static u_int
-parse_type(enum tok *tok, struct type *t, const char *vname)
+parse_type(enum tok *tok, struct type *t, const char *vname, char **subtype)
{
u_int syntax;
struct enums *e;
syntax = val;
+ if (subtype != NULL)
+ *subtype = NULL;
if (*tok == TOK_ENUM || *tok == TOK_BITS) {
if (t == NULL && vname != NULL) {
@@ -759,6 +794,8 @@
if ((*tok = gettoken()) == '|') {
if (gettoken() != TOK_STR)
report("subtype expected after '|'");
+ if (subtype != NULL)
+ *subtype = savetok();
*tok = gettoken();
}
}
@@ -794,18 +831,21 @@
if ((tok = gettoken()) == TOK_TYPE || tok == TOK_DEFTYPE ||
tok == TOK_ENUM || tok == TOK_BITS) {
/* LEAF or COLUM */
- u_int syntax = parse_type(&tok, NULL, node->name);
+ char *subtype;
+ u_int syntax = parse_type(&tok, NULL, node->name, &subtype);
if (tok == TOK_STR) {
/* LEAF */
node->type = NODE_LEAF;
node->u.leaf.func = savetok();
node->u.leaf.syntax = syntax;
+ node->u.leaf.subtype = subtype;
tok = gettoken();
} else {
/* COLUMN */
node->type = NODE_COLUMN;
node->u.column.syntax = syntax;
+ node->u.column.subtype = subtype;
}
while (tok != ')') {
@@ -825,9 +865,12 @@
tok = gettoken();
while (tok == TOK_TYPE || tok == TOK_DEFTYPE ||
tok == TOK_ENUM || tok == TOK_BITS) {
- u_int syntax = parse_type(&tok, NULL, node->name);
- if (index_count++ == SNMP_INDEXES_MAX)
+ char *subtype;
+ u_int syntax = parse_type(&tok, NULL, node->name,
+ &subtype);
+ if (index_count == SNMP_INDEXES_MAX)
report("too many table indexes");
+ node->u.entry.subtypes[index_count++] = subtype;
node->u.entry.index |=
syntax << (SNMP_INDEX_SHIFT * index_count);
}
@@ -882,7 +925,8 @@
tok = gettoken();
t->is_enum = (tok == TOK_ENUM);
t->is_bits = (tok == TOK_BITS);
- t->syntax = parse_type(&tok, t, NULL);
+
+ t->syntax = parse_type(&tok, t, NULL, NULL);
pushback(tok);
return (NULL);
@@ -903,7 +947,7 @@
* Generate the C-code table part for one node.
*/
static void
-gen_node(FILE *fp, struct node *np, struct asn_oid *oid, u_int idx,
+gen_node(FILE *fp, const struct node *np, struct asn_oid *oid, u_int idx,
const char *func)
{
u_int n;
@@ -1008,7 +1052,7 @@
* Generate the header file with the function declarations.
*/
static void
-gen_header(FILE *fp, struct node *np, u_int oidlen, const char *func)
+gen_header(FILE *fp, const struct node *np, u_int oidlen, const char *func)
{
char f[MAXSTR + 4];
struct node *sub;
@@ -1058,7 +1102,7 @@
* Generate the OID table.
*/
static void
-gen_table(FILE *fp, struct node *node)
+gen_table(FILE *fp, const struct node *node)
{
struct asn_oid oid;
@@ -1067,7 +1111,6 @@
#ifdef HAVE_STDINT_H
fprintf(fp, "#include <stdint.h>\n");
#endif
- fprintf(fp, "#include <string.h>\n");
if (localincs) {
fprintf(fp, "#include \"asn1.h\"\n");
fprintf(fp, "#include \"snmp.h\"\n");
@@ -1118,6 +1161,8 @@
case NODE_LEAF:
print_syntax(np->u.leaf.syntax);
+ if (np->u.leaf.subtype != NULL)
+ printf(" | %s", np->u.leaf.subtype);
printf(" %s%s%s)\n", np->u.leaf.func,
(np->flags & FL_GET) ? " GET" : "",
(np->flags & FL_SET) ? " SET" : "");
@@ -1137,8 +1182,11 @@
case NODE_ENTRY:
printf(" :");
- for (i = 0; i < SNMP_INDEX_COUNT(np->u.entry.index); i++)
+ for (i = 0; i < SNMP_INDEX_COUNT(np->u.entry.index); i++) {
print_syntax(SNMP_INDEX(np->u.entry.index, i));
+ if (np->u.entry.subtypes[i] != NULL)
+ printf(" | %s", np->u.entry.subtypes[i]);
+ }
printf(" %s\n", np->u.entry.func);
TAILQ_FOREACH(sp, &np->u.entry.subs, link)
gen_tree(sp, level + 1);
@@ -1147,6 +1195,8 @@
case NODE_COLUMN:
print_syntax(np->u.column.syntax);
+ if (np->u.column.subtype != NULL)
+ printf(" | %s", np->u.column.subtype);
printf("%s%s)\n", (np->flags & FL_GET) ? " GET" : "",
(np->flags & FL_SET) ? " SET" : "");
break;
@@ -1194,15 +1244,6 @@
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)
{
@@ -1391,45 +1432,6 @@
}
/**
- * Generate a definition for the enum packed into a guard against multiple
- * definitions.
- *
- * \param fp file to write definition to
- * \param t type
- */
-static void
-gen_enum(FILE *fp, const struct type *t)
-{
- const struct enums *e;
- long min = LONG_MAX;
-
- fprintf(fp, "\n");
- fprintf(fp, "#ifndef %s_defined__\n", t->name);
- fprintf(fp, "#define %s_defined__\n", t->name);
- fprintf(fp, "/*\n");
- fprintf(fp, " * From %s:%u\n", t->from_fname, t->from_lno);
- fprintf(fp, " */\n");
- fprintf(fp, "enum %s {\n", t->name);
- TAILQ_FOREACH(e, &t->enums, link) {
- fprintf(fp, "\t%s_", t->name);
- unminus(fp, e->name);
- fprintf(fp, " = %ld,\n", e->value);
- if (e->value < min)
- min = e->value;
- }
- fprintf(fp, "};\n");
- fprintf(fp, "#define STROFF_%s %ld\n", t->name, min);
- fprintf(fp, "#define STRING_%s \\\n", t->name);
- TAILQ_FOREACH(e, &t->enums, link) {
- fprintf(fp, "\t[%ld] = \"%s_", e->value - min, t->name);
- unminus(fp, e->name);
- fprintf(fp, "\",\\\n");
- }
- fprintf(fp, "\n");
- fprintf(fp, "#endif /* %s_defined__ */\n", t->name);
-}
-
-/**
* Generate helper functions for an enum.
*
* We always generate a switch statement for the isok function. The compiler
@@ -1494,6 +1496,54 @@
}
/**
+ * Generate a definition for the enum packed into a guard against multiple
+ * definitions.
+ *
+ * \param fp file to write definition to
+ * \param t type
+ * \param dof generate functions too
+ */
+static void
+gen_enum(FILE *fp, const struct type *t, int dof)
+{
+ const struct enums *e;
+ long min = LONG_MAX;
+
+ fprintf(fp, "\n");
+ fprintf(fp, "#ifndef %s_defined__\n", t->name);
+ fprintf(fp, "#define %s_defined__\n", t->name);
+ fprintf(fp, "/*\n");
+ fprintf(fp, " * From %s:%u\n", t->from_fname, t->from_lno);
+ fprintf(fp, " */\n");
+ fprintf(fp, "enum %s {\n", t->name);
+ TAILQ_FOREACH(e, &t->enums, link) {
+ fprintf(fp, "\t%s_", t->name);
+ unminus(fp, e->name);
+ fprintf(fp, " = %ld,\n", e->value);
+ if (e->value < min)
+ min = e->value;
+ }
+ fprintf(fp, "};\n");
+ fprintf(fp, "#define STROFF_%s %ld\n", t->name, min);
+ fprintf(fp, "#define STRING_%s \\\n", t->name);
+ TAILQ_FOREACH(e, &t->enums, link) {
+ fprintf(fp, "\t[%ld] = \"%s_", e->value - min, t->name);
+ unminus(fp, e->name);
+ fprintf(fp, "\",\\\n");
+ }
+ fprintf(fp, "\n");
+ if (dof) {
+ fprintf(fp, "#ifdef SNMPENUM_FUNCS\n");
+ fprintf(fp, "\n");
+ gen_enum_funcs(fp, t, 0);
+ fprintf(fp, "\n");
+ fprintf(fp, "#endif\n");
+ fprintf(fp, "\n");
+ }
+ fprintf(fp, "#endif /* %s_defined__ */\n", t->name);
+}
+
+/**
* Generate helper functions for an enum. This generates code for a c file.
*
* \param fp file to write to
@@ -1529,6 +1579,16 @@
gen_enum_funcs(fp, t, ccode);
}
+static void
+gen_enums(FILE *fp, int dof)
+{
+ const struct type *t;
+
+ LIST_FOREACH(t, &types, link)
+ if (t->is_enum || t->is_bits)
+ gen_enum(fp, t, dof);
+}
+
/**
* Extract a given enum to the specified file and optionally generate static
* inline helper functions for them.
@@ -1546,9 +1606,7 @@
LIST_FOREACH(t, &types, link)
if ((t->is_enum || t->is_bits) && strcmp(t->name, name) == 0) {
- gen_enum(fp, t);
- if (gen_funcs)
- gen_enum_funcs(fp, t, 0);
+ gen_enum(fp, t, gen_funcs);
return (0);
}
return (-1);
@@ -1567,11 +1625,8 @@
const struct type *t;
LIST_FOREACH(t, &types, link)
- if (t->is_enum || t->is_bits) {
- gen_enum(fp, t);
- if (gen_funcs)
- gen_enum_funcs(fp, t, 0);
- }
+ if (t->is_enum || t->is_bits)
+ gen_enum(fp, t, gen_funcs);
}
/**
@@ -1579,13 +1634,12 @@
*
* \param argc number of arguments
* \param argv arguments (enum names)
- * \param gen_funcs_h generate functions into the header file
- * \param gen_funcs_c generate a .c file with functions
+ * \param gen_funcs which functions to generate
*/
static void
-make_enums(int argc, char *argv[], int gen_funcs_h, int gen_funcs_c)
+make_enums(int argc, char *argv[], enum gen_funcs gen_funcs)
{
- if (gen_funcs_c) {
+ if (gen_funcs == GEN_FUNCS_C) {
if (argc == 0)
gen_all_enum_funcs(stdout, 1);
else {
@@ -1595,30 +1649,58 @@
}
} else {
if (argc == 0)
- extract_all_enums(stdout, gen_funcs_h);
+ extract_all_enums(stdout, gen_funcs == GEN_FUNCS_H);
else {
for (int i = 0; i < argc; i++)
- if (extract_enum(stdout, argv[i], gen_funcs_h))
+ if (extract_enum(stdout, argv[i],
+ gen_funcs == GEN_FUNCS_H))
errx(1, "enum not found: %s", argv[i]);
}
}
}
+/**
+ * Produce the operation tables for the daemon or a module.
+ *
+ * \param root tree root
+ * \param gen_funcs generate enum funcs
+ */
+static void
+make_table(const struct node *root, int gen_funcs)
+{
+ FILE *fp;
+
+ char fname[MAXPATHLEN + 1];
+ sprintf(fname, "%stree.h", file_prefix);
+ if ((fp = fopen(fname, "w")) == NULL)
+ err(1, "%s: ", fname);
+ gen_header(fp, root, PREFIX_LEN, NULL);
+
+ fprintf(fp, "\n#ifdef SNMPTREE_TYPES\n");
+ gen_enums(fp, gen_funcs);
+ fprintf(fp, "\n#endif /* SNMPTREE_TYPES */\n\n");
+
+ fprintf(fp, "#define %sCTREE_SIZE %u\n", file_prefix, tree_size);
+ fprintf(fp, "extern const struct snmp_node %sctree[];\n", file_prefix);
+
+ fclose(fp);
+
+ sprintf(fname, "%stree.c", file_prefix);
+ if ((fp = fopen(fname, "w")) == NULL)
+ err(1, "%s: ", fname);
+ gen_table(fp, root);
+ fclose(fp);
+}
+
int
main(int argc, char *argv[])
{
- int do_extract = 0;
- int do_tree = 0;
- int do_enums = 0;
- int gen_funcs_h = 0;
- int gen_funcs_c = 0;
- int opt;
- struct node *root;
- char fname[MAXPATHLEN + 1];
- int tok;
- FILE *fp;
+ enum op op = OP_GEN;
+ enum gen_funcs gen_funcs = GEN_FUNCS_NONE;
+
char *infile = NULL;
+ int opt;
while ((opt = getopt(argc, argv, "dEeFfhI:i:lp:t")) != EOF)
switch (opt) {
@@ -1627,19 +1709,29 @@
break;
case 'E':
- do_enums = 1;
+ if (op != OP_GEN && op != OP_ENUMS)
+ errx(1, "-E conflicts with earlier options");
+ op = OP_ENUMS;
break;
case 'e':
- do_extract = 1;
+ if (op != OP_GEN && op != OP_EXTRACT)
+ errx(1, "-e conflicts with earlier options");
+ op = OP_EXTRACT;
break;
case 'F':
- gen_funcs_c = 1;
+ if (gen_funcs != GEN_FUNCS_NONE &&
+ gen_funcs != GEN_FUNCS_C)
+ errx(1, "-F conflicts with -f");
+ gen_funcs = GEN_FUNCS_C;
break;
case 'f':
- gen_funcs_h = 1;
+ if (gen_funcs != GEN_FUNCS_NONE &&
+ gen_funcs != GEN_FUNCS_H)
+ errx(1, "-f conflicts with -F");
+ gen_funcs = GEN_FUNCS_H;
break;
case 'h':
@@ -1666,75 +1758,61 @@
break;
case 't':
- do_tree = 1;
+ if (op != OP_GEN && op != OP_TREE)
+ errx(1, "-t conflicts with earlier options");
+ op = OP_TREE;
break;
}
- if (do_extract + do_tree + do_enums > 1)
- errx(1, "conflicting options -e/-t/-E");
- if (!do_extract && !do_enums && argc != optind)
- errx(1, "no arguments allowed");
- if (do_extract && argc == optind)
- errx(1, "no objects specified");
+ argc -= optind;
+ argv += optind;
- if ((gen_funcs_h || gen_funcs_c) && (do_extract || do_tree))
- errx(1, "-f and -F not allowed with -e or -t");
- if (gen_funcs_c && !do_enums)
- errx(1, "-F requires -E");
- if (gen_funcs_h && gen_funcs_c)
- errx(1, "-f and -F are mutually exclusive");
-
+ /* open input */
if (infile == NULL) {
input_new(stdin, NULL, "<stdin>");
} else {
+ FILE *fp;
if ((fp = fopen(infile, "r")) == NULL)
err(1, "%s", infile);
input_new(fp, NULL, infile);
}
- root = parse_top(gettoken());
+ /* parse and check input */
+ struct node *root = parse_top(gettoken());
+
+ int tok;
while ((tok = gettoken()) != TOK_EOF)
merge(&root, parse_top(tok));
if (root)
check_tree(root);
- if (do_extract) {
- while (optind < argc) {
- if (gen_extract(stdout, root, argv[optind]))
- errx(1, "object not found: %s", argv[optind]);
- optind++;
- }
+ /* do what the user has requested */
+ switch (op) {
+
+ case OP_EXTRACT:
+ if (argc == 0)
+ errx(1, "-e requires arguments");
+
+ for (int i = 0; i < argc; i++)
+ if (gen_extract(stdout, root, argv[i]))
+ errx(1, "object not found: %s", argv[i]);
return (0);
- }
- if (do_enums) {
- make_enums(argc - optind, argv + optind,
- gen_funcs_h, gen_funcs_c);
+
+ case OP_ENUMS:
+ make_enums(argc, argv, gen_funcs);
return (0);
- }
- if (do_tree) {
+
+ case OP_TREE:
+ if (argc != 0)
+ errx(1, "-t allows no arguments");
gen_tree(root, 0);
return (0);
- }
- sprintf(fname, "%stree.h", file_prefix);
- if ((fp = fopen(fname, "w")) == NULL)
- err(1, "%s: ", fname);
- gen_header(fp, root, PREFIX_LEN, NULL);
- fprintf(fp, "\n#ifdef SNMPTREE_TYPES\n");
- extract_all_enums(fp, gen_funcs_h);
- fprintf(fp, "\n#endif /* SNMPTREE_TYPES */\n\n");
-
- fprintf(fp, "#define %sCTREE_SIZE %u\n", file_prefix, tree_size);
- fprintf(fp, "extern const struct snmp_node %sctree[];\n", file_prefix);
-
- fclose(fp);
-
- sprintf(fname, "%stree.c", file_prefix);
- if ((fp = fopen(fname, "w")) == NULL)
- err(1, "%s: ", fname);
- gen_table(fp, root);
- fclose(fp);
-
- return (0);
+ case OP_GEN:
+ if (argc != 0)
+ errx(1, "tree generation allows no arguments");
+ make_table(root, gen_funcs == GEN_FUNCS_H);
+ return (0);
+ }
}
Index: head/contrib/bsnmp/lib/snmpclient.h
===================================================================
--- head/contrib/bsnmp/lib/snmpclient.h
+++ head/contrib/bsnmp/lib/snmpclient.h
@@ -49,6 +49,7 @@
#define SNMP_TRANS_UDP 0
#define SNMP_TRANS_LOC_DGRAM 1
#define SNMP_TRANS_LOC_STREAM 2
+#define SNMP_TRANS_UDP6 3
/* type of callback function for responses
* this callback function is responsible for free() any memory associated with
Index: head/contrib/bsnmp/lib/snmpclient.c
===================================================================
--- head/contrib/bsnmp/lib/snmpclient.c
+++ head/contrib/bsnmp/lib/snmpclient.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2004-2005
+ * Copyright (c) 2004-2005,2018
* Hartmut Brandt.
* All rights reserved.
* Copyright (c) 2001-2003
@@ -34,11 +34,13 @@
*
* Support functions for SNMP clients.
*/
-#include <sys/types.h>
+#include <sys/param.h>
#include <sys/time.h>
#include <sys/queue.h>
#include <sys/socket.h>
#include <sys/un.h>
+#include <net/if.h>
+#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
@@ -58,12 +60,16 @@
#include <err.h>
#endif
+#include <arpa/inet.h>
+
#include "support.h"
#include "asn1.h"
#include "snmp.h"
#include "snmpclient.h"
#include "snmppriv.h"
+#define DEBUG_PARSE 0
+
/* global context */
struct snmp_client snmp_client;
@@ -924,7 +930,8 @@
/* open connection */
memset(&hints, 0, sizeof(hints));
hints.ai_flags = AI_CANONNAME;
- hints.ai_family = AF_INET;
+ hints.ai_family = snmp_client.trans == SNMP_TRANS_UDP ? AF_INET:
+ AF_INET6;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_protocol = 0;
error = getaddrinfo(snmp_client.chost, snmp_client.cport, &hints, &res0);
@@ -1068,6 +1075,7 @@
switch (snmp_client.trans) {
case SNMP_TRANS_UDP:
+ case SNMP_TRANS_UDP6:
if (open_client_udp(host, port) != 0)
return (-1);
break;
@@ -1866,99 +1874,410 @@
return (0);
}
-/*
- * parse a server specification
+/**
+ * Try to get a transport identifier which is a leading alphanumeric string
+ * (starting with '_' or a letter and including also '_') terminated by
+ * a double colon. The string may not be empty. The transport identifier
+ * is optional.
*
- * [trans::][community@][server][:port]
+ * \param sc client struct to set errors
+ * \param strp possible start of transport; updated to point to
+ * the next character to parse
+ *
+ * \return end of transport; equals *strp if there is none; NULL if there
+ * was an error
*/
-int
-snmp_parse_server(struct snmp_client *sc, const char *str)
+static inline const char *
+get_transp(struct snmp_client *sc, const char **strp)
{
- const char *p, *s = str;
+ const char *p = *strp;
- /* look for a double colon */
- for (p = s; *p != '\0'; p++) {
- if (*p == '\\' && p[1] != '\0') {
+ if (isascii(*p) && (isalpha(*p) || *p == '_')) {
+ p++;
+ while (isascii(*p) && (isalnum(*p) || *p == '_'))
p++;
- continue;
+ if (p[0] == ':' && p[1] == ':') {
+ *strp = p + 2;
+ return (p);
}
- if (*p == ':' && p[1] == ':')
- break;
}
- if (*p != '\0') {
- if (p > s) {
- if (p - s == 3 && strncmp(s, "udp", 3) == 0)
- sc->trans = SNMP_TRANS_UDP;
- else if (p - s == 6 && strncmp(s, "stream", 6) == 0)
- sc->trans = SNMP_TRANS_LOC_STREAM;
- else if (p - s == 5 && strncmp(s, "dgram", 5) == 0)
- sc->trans = SNMP_TRANS_LOC_DGRAM;
- else {
- seterr(sc, "unknown SNMP transport '%.*s'",
- (int)(p - s), s);
- return (-1);
- }
- }
- s = p + 2;
+ if (p[0] == ':' && p[1] == ':') {
+ seterr(sc, "empty transport specifier");
+ return (NULL);
}
+ return (*strp);
+}
- /* look for a @ */
- for (p = s; *p != '\0'; p++) {
- if (*p == '\\' && p[1] != '\0') {
- p++;
- continue;
- }
- if (*p == '@')
- break;
+/**
+ * Try to get community string. Eat everything up to the last @ (if there is
+ * any) but only if it is not longer than SNMP_COMMUNITY_MAXLEN. Empty
+ * community strings are legal.
+ *
+ * \param sc client struct to set errors
+ * \param strp possible start of community; updated to the point to
+ * the next character to parse
+ *
+ * \return end of community; equals *strp if there is none; NULL if there
+ * was an error
+ */
+static inline const char *
+get_comm(struct snmp_client *sc, const char **strp)
+{
+ const char *p = strrchr(*strp, '@');
+
+ if (p == NULL)
+ /* no community string */
+ return (*strp);
+
+ if (p - *strp > SNMP_COMMUNITY_MAXLEN) {
+ seterr(sc, "community string too long '%.*s'",
+ p - *strp, *strp);
+ return (NULL);
}
- if (*p != '\0') {
- if (p - s > SNMP_COMMUNITY_MAXLEN) {
- seterr(sc, "community string too long");
- return (-1);
+ *strp = p + 1;
+ return (p);
+}
+
+/**
+ * Try to get an IPv6 address. This starts with an [ and should end with an ]
+ * and everything between should be not longer than INET6_ADDRSTRLEN and
+ * parseable by inet_pton().
+ *
+ * \param sc client struct to set errors
+ * \param strp possible start of IPv6 address (the '['); updated to point to
+ * the next character to parse (the one after the closing ']')
+ *
+ * \return end of address (equals *strp + 1 if there is none) or NULL
+ * on errors
+ */
+static inline const char *
+get_ipv6(struct snmp_client *sc, const char **strp)
+{
+ char str[INET6_ADDRSTRLEN + IF_NAMESIZE];
+ struct addrinfo hints, *res;
+ int error;
+
+ if (**strp != '[')
+ return (*strp + 1);
+
+ const char *p = *strp + 1;
+ while (*p != ']' ) {
+ if (*p == '\0') {
+ seterr(sc, "unterminated IPv6 address '%.*s'",
+ p - *strp, *strp);
+ return (NULL);
}
- strncpy(sc->read_community, s, p - s);
- sc->read_community[p - s] = '\0';
- strncpy(sc->write_community, s, p - s);
- sc->write_community[p - s] = '\0';
- s = p + 1;
+ p++;
}
- /* look for a colon */
- for (p = s; *p != '\0'; p++) {
- if (*p == '\\' && p[1] != '\0') {
- p++;
- continue;
- }
- if (*p == ':')
- break;
+ if (p - *strp > INET6_ADDRSTRLEN + IF_NAMESIZE) {
+ seterr(sc, "IPv6 address too long '%.*s'", p - *strp, *strp);
+ return (NULL);
}
- if (*p == ':') {
- if (p > s) {
- /* host:port */
- free(sc->chost);
- if ((sc->chost = malloc(p - s + 1)) == NULL) {
- seterr(sc, "%s", strerror(errno));
- return (-1);
- }
- strncpy(sc->chost, s, p - s);
- sc->chost[p - s] = '\0';
+ strncpy(str, *strp + 1, p - (*strp + 1));
+ str[p - (*strp + 1)] = '\0';
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_flags = AI_CANONNAME | AI_NUMERICHOST;
+ hints.ai_family = AF_INET6;
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_protocol = IPPROTO_UDP;
+ error = getaddrinfo(str, NULL, &hints, &res);
+ if (error != 0) {
+ seterr(sc, "%s: %s", str, gai_strerror(error));
+ return (NULL);
+ }
+ freeaddrinfo(res);
+ *strp = p + 1;
+ return (p);
+}
+
+/**
+ * Try to get an IPv4 address. This starts with a digit and consists of digits
+ * and dots, is not longer INET_ADDRSTRLEN and must be parseable by
+ * inet_aton().
+ *
+ * \param sc client struct to set errors
+ * \param strp possible start of IPv4 address; updated to point to the
+ * next character to parse
+ *
+ * \return end of address (equals *strp if there is none) or NULL
+ * on errors
+ */
+static inline const char *
+get_ipv4(struct snmp_client *sc, const char **strp)
+{
+ const char *p = *strp;
+
+ while (isascii(*p) && (isdigit(*p) || *p == '.'))
+ p++;
+
+ if (p - *strp > INET_ADDRSTRLEN) {
+ seterr(sc, "IPv4 address too long '%.*s'", p - *strp, *strp);
+ return (NULL);
+ }
+ if (*strp == p)
+ return *strp;
+
+ char str[INET_ADDRSTRLEN + 1];
+ strncpy(str, *strp, p - *strp);
+ str[p - *strp] = '\0';
+
+ struct in_addr addr;
+ if (inet_aton(str, &addr) != 1) {
+ seterr(sc, "illegal IPv4 address '%s'", str);
+ return (NULL);
+ }
+
+ *strp = p;
+ return (p);
+}
+
+/**
+ * Try to get a hostname. This includes everything up to but not including
+ * the last colon (if any). There is no length restriction.
+ *
+ * \param sc client struct to set errors
+ * \param strp possible start of hostname; updated to point to the next
+ * character to parse (the trailing NUL character or the last
+ * colon)
+ *
+ * \return end of address (equals *strp if there is none)
+ */
+static inline const char *
+get_host(struct snmp_client *sc __unused, const char **strp)
+{
+ const char *p = strrchr(*strp, ':');
+
+ if (p == NULL) {
+ *strp += strlen(*strp);
+ return (*strp);
+ }
+
+ *strp = p;
+ return (p);
+}
+
+/**
+ * Try to get a port number. This start with a colon and extends to the end
+ * of string. The port number must not be empty.
+ *
+ * \param sc client struct to set errors
+ * \param strp possible start of port specification; if this points to a
+ * colon there is a port specification
+ *
+ * \return end of port number (equals *strp if there is none); NULL
+ * if there is no port number
+ */
+static inline const char *
+get_port(struct snmp_client *sc, const char **strp)
+{
+ if (**strp != ':')
+ return (*strp + 1);
+
+ if ((*strp)[1] == '\0') {
+ seterr(sc, "empty port name");
+ return (NULL);
+ }
+
+ *strp += strlen(*strp);
+ return (*strp);
+}
+
+/**
+ * Save the string in the range given by two pointers.
+ *
+ * \param sc client struct to set errors
+ * \param s begin and end pointers
+ *
+ * \return freshly allocated copy of the string between s[0] and s[1]
+ */
+static inline char *
+save_str(struct snmp_client *sc, const char *const s[2])
+{
+ char *m;
+
+ if ((m = malloc(s[1] - s[0] + 1)) == NULL) {
+ seterr(sc, "%s: %s", __func__, strerror(errno));
+ return (NULL);
+ }
+ strncpy(m, s[0], s[1] - s[0]);
+ m[s[1] - s[0]] = '\0';
+
+ return (m);
+}
+
+/**
+ * Parse a server specification. All parts are optional:
+ *
+ * [<trans>::][<comm>@][<host-or-ip>][:<port>]
+ *
+ * The transport string consists of letters, digits or '_' and starts with
+ * a letter or digit. It is terminated by two colons and may not be empty.
+ *
+ * The community string is terminated by the last '@' and does not exceed
+ * SNMP_COMMUNITY_MAXLEN. It may be empty.
+ *
+ * The host or ip is either an IPv4 address (as parsed by inet_pton()), an
+ * IPv6 address in '[' and ']' and parseable by inet_aton() or a hostname
+ * terminated by the last colon or by the NUL character.
+ *
+ * The port number may be specified numerically or symbolically and starts
+ * with the last colon.
+ *
+ * The functions sets the chost, cport, trans, read_community and
+ * write_community fields on success and the error field on errors.
+ * The chost and cport fields are allocated by malloc(3), their previous
+ * content is deallocated by free(3).
+ *
+ * The function explicitly allows mismatches between the transport and
+ * the address type in order to support IPv4 in IPv6 addresses.
+ *
+ * \param sc client struct to fill
+ * \param str string to parse
+ *
+ * \return 0 on success and -1 on errors
+ */
+int
+snmp_parse_server(struct snmp_client *sc, const char *str)
+{
+#if DEBUG_PARSE
+ const char *const orig = str;
+#endif
+
+ const char *const trans_list[] = {
+ [SNMP_TRANS_UDP] = "udp",
+ [SNMP_TRANS_LOC_DGRAM] = "dgram",
+ [SNMP_TRANS_LOC_STREAM] = "stream",
+ [SNMP_TRANS_UDP6] = "udp6",
+ };
+
+ /* parse input */
+ const char *const transp[2] = {
+ str,
+ get_transp(sc, &str),
+ };
+ if (transp[1] == NULL)
+ return (-1);
+
+ const char *const comm[2] = {
+ str,
+ get_comm(sc, &str),
+ };
+ if (comm[1] == NULL)
+ return (-1);
+
+ const char *const ipv6[2] = {
+ str + 1,
+ get_ipv6(sc, &str),
+ };
+ if (ipv6[1] == NULL)
+ return (-1);
+
+ const char *ipv4[2] = {
+ str,
+ str,
+ };
+
+ const char *host[2] = {
+ str,
+ str,
+ };
+
+ if (ipv6[0] == ipv6[1]) {
+ ipv4[1] = get_ipv4(sc, &str);
+
+ if (ipv4[0] == ipv4[1])
+ host[1] = get_host(sc, &str);
+ }
+
+ const char *port[2] = {
+ str + 1,
+ get_port(sc, &str),
+ };
+ if (port[1] == NULL)
+ return (-1);
+
+ if (*str != '\0') {
+ seterr(sc, "junk at end of server specification '%s'", str);
+ return (-1);
+ }
+
+#if DEBUG_PARSE
+ printf("transp: %zu %zu\n", transp[0] - orig, transp[1] - orig);
+ printf("comm: %zu %zu\n", comm[0] - orig, comm[1] - orig);
+ printf("ipv6: %zu %zu\n", ipv6[0] - orig, ipv6[1] - orig);
+ printf("ipv4: %zu %zu\n", ipv4[0] - orig, ipv4[1] - orig);
+ printf("host: %zu %zu\n", host[0] - orig, host[1] - orig);
+ printf("port: %zu %zu\n", port[0] - orig, port[1] - orig);
+#endif
+
+ /* analyse and allocate */
+ int i = -1;
+ if (transp[0] != transp[1]) {
+ for (i = 0; i < (int)nitems(trans_list); i++) {
+ if (trans_list[i] != NULL &&
+ strlen(trans_list[i]) == (size_t)(transp[1] -
+ transp[0]) && !strncmp(trans_list[i], transp[0],
+ transp[1] - transp[0]))
+ break;
}
- /* port */
- free(sc->cport);
- if ((sc->cport = strdup(p + 1)) == NULL) {
- seterr(sc, "%s", strerror(errno));
+
+ if (i == (int)nitems(trans_list)) {
+ seterr(sc, "unknown transport specifier '%.*s'",
+ transp[1] - transp[0], transp[0]);
return (-1);
}
+ }
- } else if (p > s) {
- /* host */
- free(sc->chost);
- if ((sc->chost = strdup(s)) == NULL) {
- seterr(sc, "%s", strerror(errno));
+ char *chost;
+
+ if (ipv6[0] != ipv6[1]) {
+ if ((chost = save_str(sc, ipv6)) == NULL)
return (-1);
+ if (i == -1)
+ i = SNMP_TRANS_UDP6;
+ } else if (ipv4[0] != ipv4[1]) {
+ if ((chost = save_str(sc, ipv4)) == NULL)
+ return (-1);
+ if (i == -1)
+ i = SNMP_TRANS_UDP;
+ } else {
+ if ((chost = save_str(sc, host)) == NULL)
+ return (-1);
+
+ if (i == -1) {
+ /* Default transport is UDP unless the host contains
+ * a slash in which case we default to DGRAM. */
+ i = SNMP_TRANS_UDP;
+ for (const char *p = host[0]; p < host[1]; p++)
+ if (*p == '/') {
+ i = SNMP_TRANS_LOC_DGRAM;
+ break;
+ }
}
}
+
+ char *cport = save_str(sc, port);
+ if (cport == NULL) {
+ free(chost);
+ return (-1);
+ }
+
+ /* commit */
+ sc->trans = i;
+
+ strncpy(sc->read_community, comm[0], comm[1] - comm[0]);
+ sc->read_community[comm[1] - comm[0]] = '\0';
+ strncpy(sc->write_community, comm[0], comm[1] - comm[0]);
+ sc->write_community[comm[1] - comm[0]] = '\0';
+
+ free(sc->chost);
+ sc->chost = chost;
+ free(sc->cport);
+ sc->cport = cport;
+
return (0);
}
Index: head/contrib/bsnmp/lib/tc.def
===================================================================
--- head/contrib/bsnmp/lib/tc.def
+++ head/contrib/bsnmp/lib/tc.def
@@ -46,3 +46,11 @@
5 readOnly
)
+typedef InetAddressType ENUM (
+ 0 unknown
+ 1 ipv4
+ 2 ipv6
+ 3 ipv4z
+ 4 ipv6z
+ 16 dns
+)
Index: head/contrib/bsnmp/snmpd/BEGEMOT-SNMPD.txt
===================================================================
--- head/contrib/bsnmp/snmpd/BEGEMOT-SNMPD.txt
+++ head/contrib/bsnmp/snmpd/BEGEMOT-SNMPD.txt
@@ -3,6 +3,10 @@
-- Fraunhofer Institute for Open Communication Systems (FhG Fokus).
-- All rights reserved.
--
+-- Copyright (c) 2018
+-- Hartmut Brandt.
+-- All rights reserved.
+--
-- Author: Harti Brandt <harti@freebsd.org>
--
-- Redistribution and use in source and binary forms, with or without
@@ -34,17 +38,17 @@
IMPORTS
MODULE-IDENTITY, OBJECT-TYPE, OBJECT-IDENTITY, Counter32,
- Unsigned32, IpAddress
+ Integer32, Unsigned32, IpAddress
FROM SNMPv2-SMI
TEXTUAL-CONVENTION, TruthValue, RowStatus
FROM SNMPv2-TC
- MODULE-COMPLIANCE, OBJECT-GROUP
- FROM SNMPv2-CONF
+ InetAddressType, InetAddress, InetPortNumber
+ FROM INET-ADDRESS-MIB
begemot
FROM BEGEMOT-MIB;
begemotSnmpd MODULE-IDENTITY
- LAST-UPDATED "201801190000Z"
+ LAST-UPDATED "201808080000Z"
ORGANIZATION "Fraunhofer FOKUS, CATS"
CONTACT-INFO
" Hartmut Brandt
@@ -59,6 +63,9 @@
E-mail: harti@freebsd.org"
DESCRIPTION
"The MIB module for the Begemot SNMP daemon."
+ REVISION "201808080000Z"
+ DESCRIPTION
+ "Add the begemotSnmpdTransInetTable."
::= { begemot 1 }
begemotSnmpdObjects OBJECT IDENTIFIER ::= { begemotSnmpd 1 }
@@ -93,7 +100,7 @@
begemotSnmpdConfig OBJECT IDENTIFIER ::= { begemotSnmpdObjects 1 }
begemotSnmpdTransmitBuffer OBJECT-TYPE
- SYNTAX INTEGER (484..65535)
+ SYNTAX Integer32 (484..65535)
MAX-ACCESS read-write
STATUS current
DESCRIPTION
@@ -103,7 +110,7 @@
::= { begemotSnmpdConfig 1 }
begemotSnmpdReceiveBuffer OBJECT-TYPE
- SYNTAX INTEGER (484..65535)
+ SYNTAX Integer32 (484..65535)
MAX-ACCESS read-write
STATUS current
DESCRIPTION
@@ -181,7 +188,7 @@
::= { begemotTrapSinkEntry 1 }
begemotTrapSinkPort OBJECT-TYPE
- SYNTAX INTEGER (1..65535)
+ SYNTAX Integer32 (1..65535)
MAX-ACCESS not-accessible
STATUS current
DESCRIPTION
@@ -203,7 +210,7 @@
begemotSnmpdPortTable OBJECT-TYPE
SYNTAX SEQUENCE OF BegemotSnmpdPortEntry
MAX-ACCESS not-accessible
- STATUS current
+ STATUS deprecated
DESCRIPTION
"A table with descriptions of UDP ports to listen on
for SNMP messages."
@@ -212,7 +219,7 @@
begemotSnmpdPortEntry OBJECT-TYPE
SYNTAX BegemotSnmpdPortEntry
MAX-ACCESS not-accessible
- STATUS current
+ STATUS deprecated
DESCRIPTION
"An entry in the table with descriptions of UDP ports to
listen on for SNMP messages."
@@ -228,15 +235,15 @@
begemotSnmpdPortAddress OBJECT-TYPE
SYNTAX IpAddress
MAX-ACCESS not-accessible
- STATUS current
+ STATUS deprecated
DESCRIPTION
"The IP address to bind to."
::= { begemotSnmpdPortEntry 1 }
begemotSnmpdPortPort OBJECT-TYPE
- SYNTAX INTEGER (1..65535)
+ SYNTAX Integer32 (1..65535)
MAX-ACCESS not-accessible
- STATUS current
+ STATUS deprecated
DESCRIPTION
"The UDP port to listen on for SNMP messages."
::= { begemotSnmpdPortEntry 2 }
@@ -244,7 +251,7 @@
begemotSnmpdPortStatus OBJECT-TYPE
SYNTAX INTEGER { valid(1), invalid(2) }
MAX-ACCESS read-create
- STATUS current
+ STATUS deprecated
DESCRIPTION
"Set status to 1 to create entry, set it to 2 to delete it."
::= { begemotSnmpdPortEntry 3 }
@@ -275,7 +282,7 @@
begemotSnmpdCommunityIndex Unsigned32,
begemotSnmpdCommunityString OCTET STRING,
begemotSnmpdCommunityDescr OCTET STRING,
- begemotSnmpdCommunityPermission INTEGER
+ begemotSnmpdCommunityPermission Unsigned32
}
begemotSnmpdCommunityModule OBJECT-TYPE
@@ -312,7 +319,7 @@
::= { begemotSnmpdCommunityEntry 4 }
begemotSnmpdCommunityPermission OBJECT-TYPE
- SYNTAX INTEGER (1..4294967295)
+ SYNTAX Unsigned32 (1..4294967295)
MAX-ACCESS not-accessible
STATUS current
DESCRIPTION
@@ -449,7 +456,7 @@
::= { begemotSnmpdDebug 2 }
begemotSnmpdDebugSyslogPri OBJECT-TYPE
- SYNTAX INTEGER (0..8)
+ SYNTAX Integer32 (0..8)
MAX-ACCESS read-write
STATUS current
DESCRIPTION
@@ -570,10 +577,115 @@
"A pointer to the group with the transport-dependend stuff."
::= { begemotSnmpdTransportEntry 3 }
+-- ----------------------------------------------------------------------
--
+-- Internet port table.
+--
+BegemotSnmpdTransportProto ::= TEXTUAL-CONVENTION
+ STATUS current
+ DESCRIPTION
+ "A value that represents the type of protocol to be used for
+ listening on a socket. The following protocols are currently
+ used:
+
+ udp(1) Use UDP for IPv4 and IPv6 sockets."
+ SYNTAX INTEGER {
+ udp(1)
+ }
+
+begemotSnmpdTransInetTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF BegemotSnmpdTransInetEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This table contains all the ports the daemon should listen on.
+ Entries can be created at initialization time via the config
+ file or at run time via a SET. One row can map to several open
+ sockets in the case of InetAddressType::dns rows. These rows
+ open one socket for each address returned by getaddrinfo(3).
+ for SNMP messages."
+ ::= { begemotSnmpdObjects 11 }
+
+begemotSnmpdTransInetEntry OBJECT-TYPE
+ SYNTAX BegemotSnmpdTransInetEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "A row of the internet port table. Each row may map to one or
+ more listening sockets."
+ INDEX {
+ begemotSnmpdTransInetAddressType,
+ begemotSnmpdTransInetAddress,
+ begemotSnmpdTransInetPort,
+ begemotSnmpdTransInetProto
+ }
+ ::= { begemotSnmpdTransInetTable 1 }
+
+BegemotSnmpdTransInetEntry ::= SEQUENCE {
+ begemotSnmpdTransInetAddressType InetAddressType,
+ begemotSnmpdTransInetAddress InetAddress,
+ begemotSnmpdTransInetPort InetPortNumber,
+ begemotSnmpdTransInetProto BegemotSnmpdTransportProto,
+ begemotSnmpdTransInetStatus RowStatus
+}
+
+begemotSnmpdTransInetAddressType OBJECT-TYPE
+ SYNTAX InetAddressType
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "The type of the address. Only ipv4, ipv6, ipv6z and dns are
+ supported."
+ ::= { begemotSnmpdTransInetEntry 1 }
+
+begemotSnmpdTransInetAddress OBJECT-TYPE
+ SYNTAX InetAddress (SIZE (0..64))
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "The address. For ipv4 addresses the length must be 4, ipv6
+ addresses have a length of 16 and ipv6z addresses a length of
+ 20 where the last four bytes are the interface index in big
+ endian format. dns addresses may be of zero-length in which case
+ getaddrinfo() generates INADDR_ANY and its IPv6 equivalent. dns
+ addresses will open a socket for all addresses returned by
+ getaddrinfo()."
+ ::= { begemotSnmpdTransInetEntry 2 }
+
+begemotSnmpdTransInetPort OBJECT-TYPE
+ SYNTAX InetPortNumber (1..65535)
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "The port to listen on for SNMP messages."
+ ::= { begemotSnmpdTransInetEntry 3 }
+
+begemotSnmpdTransInetProto OBJECT-TYPE
+ SYNTAX BegemotSnmpdTransportProto
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "The protocol to use. Currently only the value udp(1) is supported."
+ ::= { begemotSnmpdTransInetEntry 4 }
+
+begemotSnmpdTransInetStatus OBJECT-TYPE
+ SYNTAX RowStatus
+ MAX-ACCESS read-create
+ STATUS current
+ DESCRIPTION
+ "The status of the conceptual row. A row may be created using
+ createAndGo(4) or createAndWait(5). An inactive row can be
+ activated writing active(1) and an active row can be inactivated
+ by writing notInService(2). Finally active or inactive rows can be
+ deleted by writing the value destroy(6). The value of this field
+ will never read as notReady(3)."
+ ::= { begemotSnmpdTransInetEntry 5 }
+
+--
-- XXX These should go into their own MIB
--
begemotSnmpdTransUdp OBJECT IDENTIFIER ::= { begemotSnmpdTransportMappings 2 }
begemotSnmpdTransLsock OBJECT IDENTIFIER ::= { begemotSnmpdTransportMappings 3 }
+begemotSnmpdTransInet OBJECT IDENTIFIER ::= { begemotSnmpdTransportMappings 4 }
END
Index: head/contrib/bsnmp/snmpd/main.c
===================================================================
--- head/contrib/bsnmp/snmpd/main.c
+++ head/contrib/bsnmp/snmpd/main.c
@@ -65,6 +65,8 @@
#include "tree.h"
#include "oid.h"
+#include "trans_inet.h"
+
#define PATH_PID "/var/run/%s.pid"
#define PATH_CONFIG "/etc/%s.config"
#define PATH_ENGINE "/var/%s.engine"
@@ -1038,7 +1040,7 @@
ssize_t ret, slen;
int32_t vi;
#ifdef USE_TCPWRAPPERS
- char client[16];
+ char client[INET6_ADDRSTRLEN];
#endif
ret = tport->transport->vtab->recv(tport, pi);
@@ -1184,8 +1186,12 @@
sndbuf, &sndlen, "SNMP", ierr, vi, NULL);
if (ferr == SNMPD_INPUT_OK) {
- slen = tport->transport->vtab->send(tport, sndbuf, sndlen,
- pi->peer, pi->peerlen);
+ if (tport->transport->vtab->send != NULL)
+ slen = tport->transport->vtab->send(tport, sndbuf,
+ sndlen, pi->peer, pi->peerlen);
+ else
+ slen = tport->transport->vtab->send2(tport, sndbuf,
+ sndlen, pi);
if (slen == -1)
syslog(LOG_ERR, "send*: %m");
else if ((size_t)slen != sndlen)
@@ -1201,7 +1207,8 @@
}
/*
- * Send a PDU to a given port
+ * Send a PDU to a given port. If this is a multi-socket port, use the
+ * first socket.
*/
void
snmp_send_port(void *targ, const struct asn_oid *port, struct snmp_pdu *pdu,
@@ -1224,7 +1231,10 @@
snmp_output(pdu, sndbuf, &sndlen, "SNMP PROXY");
- len = trans->vtab->send(tp, sndbuf, sndlen, addr, addrlen);
+ if (trans->vtab->send != NULL)
+ len = trans->vtab->send(tp, sndbuf, sndlen, addr, addrlen);
+ else
+ len = trans->vtab->send2(tp, sndbuf, sndlen, NULL);
if (len == -1)
syslog(LOG_ERR, "sendto: %m");
@@ -1238,19 +1248,40 @@
/*
* Close an input source
+ *
+ * \param pi input instance
*/
void
snmpd_input_close(struct port_input *pi)
{
- if (pi->id != NULL)
+ if (pi->id != NULL) {
fd_deselect(pi->id);
- if (pi->fd >= 0)
+ pi->id = NULL;
+ }
+ if (pi->fd >= 0) {
(void)close(pi->fd);
- if (pi->buf != NULL)
+ pi->fd = -1;
+ }
+ if (pi->buf != NULL) {
free(pi->buf);
+ pi->buf = NULL;
+ }
}
/*
+ * Initialize an input source.
+ *
+ * \param pi input instance
+ */
+void
+snmpd_input_init(struct port_input *pi)
+{
+ pi->id = NULL;
+ pi->fd = -1;
+ pi->buf = NULL;
+}
+
+/*
* Dump internal state.
*/
#ifdef USE_LIBBEGEMOT
@@ -1633,6 +1664,8 @@
syslog(LOG_WARNING, "cannot start UDP transport");
if (lsock_trans.start() != SNMP_ERR_NOERROR)
syslog(LOG_WARNING, "cannot start LSOCK transport");
+ if (inet_trans.start() != SNMP_ERR_NOERROR)
+ syslog(LOG_WARNING, "cannot start INET transport");
#ifdef USE_LIBBEGEMOT
if (debug.evdebug > 0)
Index: head/contrib/bsnmp/snmpd/snmpd.h
===================================================================
--- head/contrib/bsnmp/snmpd/snmpd.h
+++ head/contrib/bsnmp/snmpd/snmpd.h
@@ -174,8 +174,8 @@
int snmpd_input(struct port_input *, struct tport *);
void snmpd_input_close(struct port_input *);
+void snmpd_input_init(struct port_input *);
-
/*
* Transport domain
*/
@@ -194,6 +194,10 @@
ssize_t (*send)(struct tport *, const u_char *, size_t,
const struct sockaddr *, size_t);
ssize_t (*recv)(struct tport *, struct port_input *);
+
+ /** send via a multi-socket port */
+ ssize_t (*send2)(struct tport *, const u_char *, size_t,
+ struct port_input *);
};
struct transport {
struct asn_oid index; /* transport table index */
Index: head/contrib/bsnmp/snmpd/snmpd.config
===================================================================
--- head/contrib/bsnmp/snmpd/snmpd.config
+++ head/contrib/bsnmp/snmpd/snmpd.config
@@ -72,8 +72,18 @@
begemotSnmpdCommunityDisable = 1
# open standard SNMP ports
-begemotSnmpdPortStatus.[$(host)].161 = 1
-begemotSnmpdPortStatus.127.0.0.1.161 = 1
+# begemotSnmpdPortStatus.[$(host)].161 = 1
+# begemotSnmpdPortStatus.127.0.0.1.161 = 1
+
+# UDP over IPv4: 127.0.0.1:161
+begemotSnmpdTransInetStatus.1.4.127.0.0.1.161.1 = 4
+
+# UDP over IPv6: ::1:161
+begemotSnmpdTransInetStatus.2.16.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.1.161.1 = 4
+
+# Use domain name and IPv6 link-local address with scope zone id as address
+# begemotSnmpdTransInetStatus.16."localhost".161.1 = 4
+# begemotSnmpdTransInetStatus.16."fe80::1%em0".161.1 = 4
# open a unix domain socket
begemotSnmpdLocalPortStatus."/var/run/snmpd.sock" = 1
Index: head/contrib/bsnmp/snmpd/snmpmod.h
===================================================================
--- head/contrib/bsnmp/snmpd/snmpmod.h
+++ head/contrib/bsnmp/snmpd/snmpmod.h
@@ -56,6 +56,48 @@
* ordering can be done either on an integer/unsigned field, an asn_oid
* or an ordering function.
*/
+
+/*
+ * First set of macros is used when the link is embedded into sub-struct
+ * and links these sub-structs. The sub-struct must be the first field.
+ *
+ * The list is a list of the subfield types.
+ */
+#define INSERT_OBJECT_OID_LINK_INDEX_TYPE(PTR, LIST, LINK, INDEX, SUBF) do {\
+ typedef __typeof ((PTR)->SUBF) _subf_type; \
+ _subf_type *_lelem; \
+ \
+ TAILQ_FOREACH(_lelem, (LIST), LINK) \
+ if (asn_compare_oid(&_lelem->INDEX, &(PTR)->SUBF.INDEX) > 0)\
+ break; \
+ if (_lelem == NULL) \
+ TAILQ_INSERT_TAIL((LIST), &(PTR)->SUBF, LINK); \
+ else \
+ TAILQ_INSERT_BEFORE(_lelem, &(PTR)->SUBF, LINK); \
+ } while (0)
+
+#define NEXT_OBJECT_OID_LINK_INDEX_TYPE(LIST, OID, SUB, LINK, INDEX, TYPE) ({\
+ __typeof (TAILQ_FIRST((LIST))) _lelem; \
+ \
+ TAILQ_FOREACH(_lelem, (LIST), LINK) \
+ if (index_compare(OID, SUB, &_lelem->INDEX) < 0) \
+ break; \
+ (TYPE *)(_lelem); \
+ })
+
+#define FIND_OBJECT_OID_LINK_INDEX_TYPE(LIST, OID, SUB, LINK, INDEX, TYPE) ({\
+ __typeof (TAILQ_FIRST((LIST))) _lelem; \
+ \
+ TAILQ_FOREACH(_lelem, (LIST), LINK) \
+ if (index_compare(OID, SUB, &_lelem->INDEX) == 0) \
+ break; \
+ (TYPE *)(_lelem); \
+ })
+
+/*
+ * This set of macros allows specification of the link and index name.
+ * The index is an OID.
+ */
#define INSERT_OBJECT_OID_LINK_INDEX(PTR, LIST, LINK, INDEX) do { \
__typeof (PTR) _lelem; \
\
Index: head/contrib/bsnmp/snmpd/trans_inet.h
===================================================================
--- head/contrib/bsnmp/snmpd/trans_inet.h
+++ head/contrib/bsnmp/snmpd/trans_inet.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2018
+ * Hartmut Brandt.
+ * All rights reserved.
+ *
+ * Author: Harti Brandt <harti@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id$
+ */
+
+#ifndef trans_inet_h_1530971397
+#define trans_inet_h_1530971397
+
+/* transport declaration */
+extern const struct transport_def inet_trans;
+
+#endif
Index: head/contrib/bsnmp/snmpd/trans_inet.c
===================================================================
--- head/contrib/bsnmp/snmpd/trans_inet.c
+++ head/contrib/bsnmp/snmpd/trans_inet.c
@@ -0,0 +1,1342 @@
+/*
+ * Copyright (c) 2018
+ * Hartmut Brandt.
+ * All rights reserved.
+ *
+ * Author: Harti Brandt <harti@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Begemot: bsnmp/snmpd/trans_udp.c,v 1.5 2005/10/04 08:46:56 brandt_h Exp $
+ *
+ * Internet transport
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <netdb.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include <stdio.h>
+
+#include <arpa/inet.h>
+
+#include "asn1.h"
+#include "snmp.h"
+#include "snmpmod.h"
+
+#include "snmpd.h"
+
+#define SNMPTREE_TYPES
+#define SNMPENUM_FUNCS
+#include "tree.h"
+#include "oid.h"
+
+extern const struct transport_def inet_trans;
+
+struct inet_port;
+struct inet_port_params;
+struct port_sock;
+
+typedef int create_func(struct inet_port *, struct inet_port_params *);
+typedef void input_func(int, void *);
+typedef int activate_func(struct inet_port *);
+typedef void deactivate_func(struct inet_port *);
+typedef void parse_ctrl_func(struct port_sock *, const struct msghdr *);
+typedef void setsrc_func(struct port_sock *, struct msghdr *);
+
+static create_func ipv4_create;
+static input_func ipv4_input;
+static activate_func ipv4_activate;
+static deactivate_func ipv4_deactivate;
+static parse_ctrl_func ipv4_parse_ctrl;
+static setsrc_func ipv4_setsrc;
+
+static create_func ipv6_create;
+static input_func ipv6_input;
+static activate_func ipv6_activate;
+static deactivate_func ipv6_deactivate;
+static parse_ctrl_func ipv6_parse_ctrl;
+static setsrc_func ipv6_setsrc;
+
+static create_func ipv6z_create;
+
+static create_func dns_create;
+static activate_func dns_activate;
+static deactivate_func dns_deactivate;
+
+struct port_sock {
+ /* common input stuff; must be first */
+ struct port_input input;
+
+ /** link field */
+ TAILQ_ENTRY(port_sock) link;
+
+ /** pointer to parent */
+ struct inet_port *port;
+
+ /** bind address */
+ struct sockaddr_storage bind_addr;
+
+ /** reply destination */
+ struct sockaddr_storage ret_dest;
+
+ /** need to set source address in reply; set for INADDR_ANY */
+ bool set_ret_source;
+
+ /** address of the receive interface */
+ union {
+ /** IPv4 case */
+ struct in_addr a4;
+
+ /** IPv6 case */
+ struct in6_pktinfo a6;
+ } ret_source;
+
+ /** parse control message */
+ parse_ctrl_func *parse_ctrl;
+
+ /** set source address for a send() */
+ setsrc_func *setsrc;
+};
+static_assert(offsetof(struct port_sock, input) == 0,
+ "input not first in port_sock");
+
+/**
+ * Table row for the inet ports.
+ *
+ * When actived each row can have one or several open sockets. For ipv6,
+ * ipv4 and ipv6z addresses it is always one, for dns addresses more than
+ * one socket can be open.
+ */
+struct inet_port {
+ /** common i/o port stuff (must be first) */
+ struct tport tport;
+
+ /** transport protocol */
+ enum BegemotSnmpdTransportProto proto;
+
+ /** row status */
+ enum RowStatus row_status;
+
+ /** socket list */
+ TAILQ_HEAD(, port_sock) socks;
+
+ /** value for InetAddressType::dns */
+ char *dns_addr;
+
+ /** port number in dns case; network byte order */
+ uint16_t dns_port;
+
+ /** create a port */
+ create_func *create;
+
+ /** activate a port */
+ activate_func *activate;
+
+ /** deactivate port */
+ deactivate_func *deactivate;
+};
+static_assert(offsetof(struct inet_port, tport) == 0,
+ "tport not first in inet_port");
+
+/** to be used in bind_addr field */
+#define AF_DNS AF_VENDOR00
+
+/** registered transport */
+static struct transport *my_trans;
+
+/** set operation */
+enum {
+ SET_CREATED,
+ SET_ACTIVATED,
+ SET_DEACTIVATE,
+ SET_DESTROY,
+};
+
+/** length of the control data buffer */
+static const size_t RECV_CBUF_SIZE =
+ MAX(CMSG_SPACE(SOCKCREDSIZE(CMGROUP_MAX)) +
+ CMSG_SPACE(sizeof(struct in_addr)),
+ CMSG_SPACE(SOCKCREDSIZE(CMGROUP_MAX)) +
+ CMSG_SPACE(sizeof(struct in6_pktinfo)));
+
+/** length of the control data buffer */
+static const size_t XMIT_CBUF_SIZE =
+ MAX(CMSG_SPACE(sizeof(struct in_addr)),
+ CMSG_SPACE(sizeof(struct in6_pktinfo)));
+
+/**
+ * Start the transport. This registers the transport with the
+ * transport table.
+ *
+ * \return SNMP error code
+ */
+static int
+inet_start(void)
+{
+ return (trans_register(&inet_trans, &my_trans));
+}
+
+/**
+ * Stop the transport. This tries to unregister the transport which
+ * in turn fails if the list of ports is not empty.
+ *
+ * \return SNMP error code
+ */
+static int
+inet_stop(int force __unused)
+{
+ if (my_trans != NULL)
+ if (trans_unregister(my_trans) != 0)
+ return (SNMP_ERR_GENERR);
+ return (SNMP_ERR_NOERROR);
+}
+
+/**
+ * Deactivate SNMP port.
+ *
+ * \param tp port to close
+ */
+static void
+deactivate_port(struct inet_port *p)
+{
+ p->deactivate(p);
+}
+
+/*
+ * This function activates a port. For ports opened via the config files
+ * this is called just before entering the event loop. For ports create
+ * during runtime this is called when the RowStatus is set to Active or
+ * as second step for CreateAndGo.
+ *
+ * \param tp transport port
+ *
+ * \return SNMP error code
+ */
+static int
+inet_activate(struct tport *tp)
+{
+ struct inet_port *port = (struct inet_port *)tp;
+
+ return (port->activate(port));
+}
+
+/*
+ * Close the SNMP port if it is open and destroy it.
+ *
+ * \param tp port to close
+ */
+static void
+inet_destroy_port(struct tport *tp)
+{
+ struct inet_port *port = (struct inet_port *)tp;
+
+ deactivate_port(port);
+
+ trans_remove_port(tp);
+
+ free(port->dns_addr);
+ free(port);
+}
+
+/**
+ * If the input struct has no buffer allocated yet, do it now. If allocation
+ * fails, read the data into a local buffer and drop it.
+ *
+ * \param pi input struct
+ *
+ * \return -1 if allocation fails, 0 otherwise
+ */
+static int
+inet_alloc_buf(struct port_input *pi)
+{
+ char drop_buf[2000];
+
+ if (pi->buf == NULL) {
+ if ((pi->buf = buf_alloc(0)) == NULL) {
+ (void)recvfrom(pi->fd, drop_buf, sizeof(drop_buf),
+ 0, NULL, NULL);
+ return (-1);
+ }
+ pi->buflen = buf_size(0);
+ }
+ return (0);
+}
+
+/**
+ * Read message into input buffer. Get also the source address and any
+ * control data that is available. If the message is truncated, increment
+ * corresponding statistics.
+ *
+ * \param pi input object
+ * \param msg message object to fill
+ * \param cbuf control data buffer
+ *
+ * \return -1 when something goes wrong, 0 othersise
+ */
+static int
+inet_read_msg(struct port_input *pi, struct msghdr *msg, char *cbuf)
+{
+ struct iovec iov[1];
+
+ iov[0].iov_base = pi->buf;
+ iov[0].iov_len = pi->buflen;
+
+ msg->msg_name = pi->peer;
+ msg->msg_namelen = pi->peerlen;
+ msg->msg_iov = iov;
+ msg->msg_iovlen = 1;
+ msg->msg_control = cbuf;
+ msg->msg_controllen = RECV_CBUF_SIZE;
+ msg->msg_flags = 0;
+
+ memset(cbuf, 0, RECV_CBUF_SIZE);
+
+ const ssize_t len = recvmsg(pi->fd, msg, 0);
+
+ if (len == -1 || len == 0)
+ /* receive error */
+ return (-1);
+
+ if (msg->msg_flags & MSG_TRUNC) {
+ /* truncated - drop */
+ snmpd_stats.silentDrops++;
+ snmpd_stats.inTooLong++;
+ return (-1);
+ }
+
+ pi->length = (size_t)len;
+
+ return (0);
+}
+
+/*
+ * Input available on socket.
+ *
+ * \param tp transport port
+ * \param pi input struct
+ *
+ * \return number of bytes received
+ */
+static ssize_t
+inet_recv(struct tport *tp, struct port_input *pi)
+{
+ struct inet_port *port = __containerof(tp, struct inet_port, tport);
+ struct port_sock *sock = __containerof(pi, struct port_sock, input);
+
+ assert(port->proto == BegemotSnmpdTransportProto_udp);
+
+ if (inet_alloc_buf(pi) == -1)
+ return (-1);
+
+ char cbuf[RECV_CBUF_SIZE];
+ struct msghdr msg;
+
+ if (inet_read_msg(pi, &msg, cbuf) == -1)
+ return (-1);
+
+ sock->parse_ctrl(sock, &msg);
+
+ return (0);
+}
+
+/*
+ * Send message.
+ *
+ * \param tp port
+ * \param buf data to send
+ * \param len number of bytes to send
+ * \param addr destination address
+ * \param addlen destination address length
+ *
+ * \return number of bytes sent
+ */
+static ssize_t
+inet_send2(struct tport *tp, const u_char *buf, size_t len,
+ struct port_input *pi)
+{
+ struct inet_port *p = __containerof(tp, struct inet_port, tport);
+ struct port_sock *s = (pi == NULL) ? TAILQ_FIRST(&p->socks) :
+ __containerof(pi, struct port_sock, input);
+
+ struct iovec iov;
+
+ iov.iov_base = __DECONST(void*, buf);
+ iov.iov_len = len;
+
+ struct msghdr msg;
+
+ msg.msg_flags = 0;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_name = (void *)pi->peer;
+ msg.msg_namelen = pi->peerlen;
+
+ msg.msg_control = NULL;
+ msg.msg_controllen = 0;
+
+ char cbuf[XMIT_CBUF_SIZE];
+ if (s->set_ret_source) {
+ msg.msg_control = cbuf;
+ s->setsrc(s, &msg);
+ }
+
+ return (sendmsg(s->input.fd, &msg, 0));
+}
+
+/** exported to daemon */
+const struct transport_def inet_trans = {
+ "inet",
+ OIDX_begemotSnmpdTransInet,
+ inet_start,
+ inet_stop,
+ inet_destroy_port,
+ inet_activate,
+ NULL,
+ inet_recv,
+ inet_send2,
+};
+
+struct inet_port_params {
+ /** index oid */
+ struct asn_oid index;
+
+ /** internet address type */
+ enum InetAddressType type;
+
+ /** internet address */
+ u_char *addr;
+
+ /** length of address */
+ size_t addr_len;
+
+ /** port number */
+ uint32_t port;
+
+ /** protocol */
+ enum BegemotSnmpdTransportProto proto;
+};
+
+/**
+ * IPv4 creation stuff. Parse the index, fill socket address and creates
+ * a port_sock.
+ *
+ * \param port the port to create
+ * \param params parameters from the SNMP SET
+ *
+ * \return SNMP error
+ */
+static int
+ipv4_create(struct inet_port *port, struct inet_port_params *params)
+{
+ uint32_t ip;
+
+ if (params->addr_len != 4)
+ return (SNMP_ERR_INCONS_VALUE);
+
+ memcpy(&ip, params->addr, 4);
+ struct port_sock *sock = calloc(1, sizeof(struct port_sock));
+ if (sock == NULL)
+ return (SNMP_ERR_GENERR);
+
+ snmpd_input_init(&sock->input);
+
+ TAILQ_INSERT_HEAD(&port->socks, sock, link);
+
+ struct sockaddr_in *sin =
+ (struct sockaddr_in *)&sock->bind_addr;
+
+ sin->sin_len = sizeof(struct sockaddr_in);
+ sin->sin_family = AF_INET;
+ sin->sin_addr.s_addr = htonl(ip);
+ sin->sin_port = htons(params->port);
+
+ sock->port = port;
+
+ return (SNMP_ERR_NOERROR);
+}
+
+/*
+ * An IPv4 inet port is ready. Delegate to the generic code to read the data
+ * and react.
+ *
+ * \param fd file descriptor that is ready
+ * \param udata inet_port pointer
+ */
+static void
+ipv4_input(int fd __unused, void *udata)
+{
+ struct port_sock *sock = udata;
+
+ sock->input.peerlen = sizeof(struct sockaddr_in);
+ snmpd_input(&sock->input, &sock->port->tport);
+}
+
+/**
+ * Activate an IPv4 socket.
+ *
+ * \param sock thhe socket to activate
+ *
+ * \return error code
+ */
+static int
+ipv4_activate_sock(struct port_sock *sock)
+{
+ if ((sock->input.fd = socket(PF_INET, SOCK_DGRAM, 0)) == -1) {
+ syslog(LOG_ERR, "creating UDP4 socket: %m");
+ return (SNMP_ERR_RES_UNAVAIL);
+ }
+
+ const struct sockaddr_in *sin =
+ (const struct sockaddr_in *)&sock->bind_addr;
+
+ if (sin->sin_addr.s_addr == INADDR_ANY) {
+ /* need to know from which address to return */
+ static const int on = 1;
+
+ if (setsockopt(sock->input.fd, IPPROTO_IP, IP_RECVDSTADDR, &on,
+ sizeof(on)) == -1) {
+ syslog(LOG_ERR, "setsockopt(IP_RECVDSTADDR): %m");
+ (void)close(sock->input.fd);
+ sock->input.fd = -1;
+ return (SNMP_ERR_GENERR);
+ }
+ sock->set_ret_source = true;
+ }
+
+ if (bind(sock->input.fd, (const struct sockaddr *)sin, sizeof(*sin))) {
+ if (errno == EADDRNOTAVAIL) {
+ (void)close(sock->input.fd);
+ sock->input.fd = -1;
+ return (SNMP_ERR_INCONS_NAME);
+ }
+ syslog(LOG_ERR, "bind: %s:%u %m", inet_ntoa(sin->sin_addr),
+ ntohs(sin->sin_port));
+ (void)close(sock->input.fd);
+ sock->input.fd = -1;
+ return (SNMP_ERR_GENERR);
+ }
+
+ if ((sock->input.id = fd_select(sock->input.fd, ipv4_input,
+ sock, NULL)) == NULL) {
+ (void)close(sock->input.fd);
+ sock->input.fd = -1;
+ return (SNMP_ERR_GENERR);
+ }
+ sock->input.peer = (struct sockaddr *)&sock->ret_dest;
+
+ sock->parse_ctrl = ipv4_parse_ctrl;
+ sock->setsrc = ipv4_setsrc;
+
+ return (SNMP_ERR_NOERROR);
+}
+
+/**
+ * Open an IPv4 socket. Make the socket, bind it and put it on the select
+ * list. The socket struct has already been created during creation.
+ *
+ * \param p inet port
+ *
+ * \return SNMP error code
+ */
+static int
+ipv4_activate(struct inet_port *p)
+{
+ struct port_sock *sock = TAILQ_FIRST(&p->socks);
+ assert(sock);
+ assert(!TAILQ_NEXT(sock, link));
+
+ const int ret = ipv4_activate_sock(sock);
+ if (ret == SNMP_ERR_NOERROR)
+ p->row_status = RowStatus_active;
+
+ return (ret);
+}
+
+/**
+ * Close an IPv4 socket. Keep the sock object.
+ *
+ * \param p inet port
+ */
+static void
+ipv4_deactivate(struct inet_port *p)
+{
+ struct port_sock *sock = TAILQ_FIRST(&p->socks);
+ assert(sock);
+ assert(!TAILQ_NEXT(sock, link));
+
+ snmpd_input_close(&sock->input);
+
+ p->row_status = RowStatus_notInService;
+}
+
+/**
+ * Parse the control data received with a UDPv4 packet. This may contain
+ * credentials (for a local connection) and the address of the interface
+ * the message was received on. If there are credentials set the priv flag
+ * if the effective UID is zero.
+ *
+ * \param sock the sock object
+ * \param msg the received message
+ */
+static void
+ipv4_parse_ctrl(struct port_sock *sock, const struct msghdr *msg)
+{
+ struct sockcred *cred = NULL;
+
+ for (struct cmsghdr *cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL;
+ cmsg = CMSG_NXTHDR(msg, cmsg)) {
+
+ if (cmsg->cmsg_level == IPPROTO_IP &&
+ cmsg->cmsg_type == IP_RECVDSTADDR) {
+ memcpy(&sock->ret_source.a4, CMSG_DATA(cmsg),
+ sizeof(struct in_addr));
+
+ } else if (cmsg->cmsg_level == SOL_SOCKET &&
+ cmsg->cmsg_type == SCM_CREDS) {
+ cred = (struct sockcred *)(void *)CMSG_DATA(cmsg);
+ }
+ }
+
+ sock->input.priv = 0;
+ if (sock->input.cred && cred)
+ /* remote end has sent credentials */
+ sock->input.priv = (cred->sc_euid == 0);
+}
+
+/**
+ * Set the source address option for IPv4 sockets.
+ *
+ * \param sock socket object
+ * \param msg message
+ */
+static void
+ipv4_setsrc(struct port_sock *sock, struct msghdr *msg)
+{
+ struct cmsghdr *cmsg = CMSG_FIRSTHDR(msg);
+
+ /* select outgoing interface by setting source address */
+ cmsg->cmsg_level = IPPROTO_IP;
+ cmsg->cmsg_type = IP_SENDSRCADDR;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_addr));
+ memcpy(CMSG_DATA(cmsg), &sock->ret_source.a4,
+ sizeof(struct in_addr));
+
+ msg->msg_controllen = CMSG_SPACE(sizeof(struct in_addr));
+}
+
+/**
+ * Common part of IPv6 creation. This is used by both ipv6_create() and
+ * ipv6z_create().
+ *
+ * \param port the table row
+ * \param params creation parameters
+ * \param scope_id scope_id (0 or from index)
+ *
+ * \return SNMP_ERR_NOERROR if port has been created, error code otherwise
+ */
+static int
+ipv6_create_common(struct inet_port *port, struct inet_port_params *params,
+ u_int scope_id)
+{
+ struct port_sock *sock = calloc(1, sizeof(struct port_sock));
+
+ if (sock == NULL)
+ return (SNMP_ERR_GENERR);
+
+ struct sockaddr_in6 *sin = (struct sockaddr_in6 *)&sock->bind_addr;
+
+ sin->sin6_len = sizeof(struct sockaddr_in6);
+ sin->sin6_family = AF_INET6;
+ sin->sin6_port = htons(params->port);
+ sin->sin6_flowinfo = 0;
+ sin->sin6_scope_id = scope_id;
+
+ memcpy(sin->sin6_addr.s6_addr, params->addr, 16);
+
+ if (IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr) && scope_id == 0) {
+ char buf[INET6_ADDRSTRLEN];
+ syslog(LOG_INFO, "%s: link local address used without scope "
+ "index: %s", __func__, inet_ntop(AF_INET6,
+ &sin->sin6_addr, buf, sizeof(buf)));
+ free(sock);
+ return (SNMP_ERR_NO_CREATION);
+ }
+
+ sock->port = port;
+
+ snmpd_input_init(&sock->input);
+ TAILQ_INSERT_HEAD(&port->socks, sock, link);
+
+ return (SNMP_ERR_NOERROR);
+}
+
+/**
+ * IPv6 creation stuff. Parse the index, fill socket address and creates
+ * a port_sock.
+ *
+ * \param port the port to create
+ * \param params parameters from the SNMP SET
+ *
+ * \return SNMP error
+ */
+static int
+ipv6_create(struct inet_port *port, struct inet_port_params *params)
+{
+ if (params->addr_len != 16)
+ return (SNMP_ERR_INCONS_VALUE);
+
+ const int ret = ipv6_create_common(port, params, 0);
+ if (ret != SNMP_ERR_NOERROR)
+ return (ret);
+
+ return (SNMP_ERR_NOERROR);
+}
+
+/*
+ * An IPv6 inet port is ready. Delegate to the generic code to read the data
+ * and react.
+ *
+ * \param fd file descriptor that is ready
+ * \param udata inet_port pointer
+ */
+static void
+ipv6_input(int fd __unused, void *udata)
+{
+ struct port_sock *sock = udata;
+
+ sock->input.peerlen = sizeof(struct sockaddr_in6);
+ snmpd_input(&sock->input, &sock->port->tport);
+}
+
+/**
+ * Activate an IPv6 socket.
+ *
+ * \param sock thhe socket to activate
+ *
+ * \return error code
+ */
+static int
+ipv6_activate_sock(struct port_sock *sock)
+{
+ if ((sock->input.fd = socket(PF_INET6, SOCK_DGRAM, 0)) == -1) {
+ syslog(LOG_ERR, "creating UDP6 socket: %m");
+ return (SNMP_ERR_RES_UNAVAIL);
+ }
+
+ const struct sockaddr_in6 *sin =
+ (const struct sockaddr_in6 *)&sock->bind_addr;
+
+ if (memcmp(&sin->sin6_addr, &in6addr_any, sizeof(in6addr_any)) == 0) {
+ /* need to know from which address to return */
+ static const int on = 1;
+
+ if (setsockopt(sock->input.fd, IPPROTO_IPV6, IPV6_RECVPKTINFO,
+ &on, sizeof(on)) == -1) {
+ syslog(LOG_ERR, "setsockopt(IP6_PKTINFO): %m");
+ (void)close(sock->input.fd);
+ sock->input.fd = -1;
+ return (SNMP_ERR_GENERR);
+ }
+ sock->set_ret_source = true;
+ }
+
+ if (bind(sock->input.fd, (const struct sockaddr *)sin, sizeof(*sin))) {
+ if (community != COMM_INITIALIZE && errno == EADDRNOTAVAIL) {
+ (void)close(sock->input.fd);
+ sock->input.fd = -1;
+ return (SNMP_ERR_INCONS_NAME);
+ }
+ char buf[INET6_ADDRSTRLEN];
+ syslog(LOG_ERR, "bind: %s:%u:%u %m", inet_ntop(AF_INET6,
+ &sin->sin6_addr, buf, sizeof(buf)), sin->sin6_scope_id,
+ ntohs(sin->sin6_port));
+ (void)close(sock->input.fd);
+ sock->input.fd = -1;
+ return (SNMP_ERR_GENERR);
+ }
+ if ((sock->input.id = fd_select(sock->input.fd, ipv6_input,
+ sock, NULL)) == NULL) {
+ (void)close(sock->input.fd);
+ sock->input.fd = -1;
+ return (SNMP_ERR_GENERR);
+ }
+ sock->input.peer = (struct sockaddr *)&sock->ret_dest;
+
+ sock->parse_ctrl = ipv6_parse_ctrl;
+ sock->setsrc = ipv6_setsrc;
+
+ return (SNMP_ERR_NOERROR);
+}
+
+/**
+ * Open an IPv6 socket.
+ *
+ * \param port inet port
+ *
+ * \return SNMP error code
+ */
+static int
+ipv6_activate(struct inet_port *p)
+{
+ struct port_sock *sock = TAILQ_FIRST(&p->socks);
+ assert(sock);
+
+ const int ret = ipv6_activate_sock(sock);
+
+ if (ret == SNMP_ERR_NOERROR)
+ p->row_status = RowStatus_active;
+ return (ret);
+}
+
+/**
+ * Close an IPv6 socket. Keep the sock object.
+ *
+ * \param p inet port
+ */
+static void
+ipv6_deactivate(struct inet_port *p)
+{
+ struct port_sock *sock = TAILQ_FIRST(&p->socks);
+ assert(sock);
+ assert(!TAILQ_NEXT(sock, link));
+
+ snmpd_input_close(&sock->input);
+
+ p->row_status = RowStatus_notInService;
+}
+
+/**
+ * IPv6 specific part of message processing. The control data may contain
+ * credentials and packet info that contains the destination address of
+ * the packet.
+ *
+ * \param sock the sock object
+ * \param msg the received message
+ */
+static void
+ipv6_parse_ctrl(struct port_sock *sock, const struct msghdr *msg)
+{
+ struct sockcred *cred = NULL;
+
+ for (struct cmsghdr *cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL;
+ cmsg = CMSG_NXTHDR(msg, cmsg)) {
+
+ if (cmsg->cmsg_level == IPPROTO_IPV6 &&
+ cmsg->cmsg_type == IPV6_PKTINFO) {
+ const struct in6_pktinfo *info =
+ (const struct in6_pktinfo *)(const void *)
+ CMSG_DATA(cmsg);
+ sock->ret_source.a6.ipi6_addr = info->ipi6_addr;
+ sock->ret_source.a6.ipi6_ifindex =
+ !IN6_IS_ADDR_LINKLOCAL(&info->ipi6_addr) ? 0:
+ info->ipi6_ifindex;
+ } else if (cmsg->cmsg_level == SOL_SOCKET &&
+ cmsg->cmsg_type == SCM_CREDS) {
+ cred = (struct sockcred *)(void *)CMSG_DATA(cmsg);
+ }
+ }
+
+ sock->input.priv = 0;
+ if (sock->input.cred && cred)
+ /* remote end has sent credentials */
+ sock->input.priv = (cred->sc_euid == 0);
+}
+
+/**
+ * Set the source address option for IPv4 sockets.
+ *
+ * \param sock socket object
+ * \param msg message
+ */
+static void
+ipv6_setsrc(struct port_sock *sock, struct msghdr *msg)
+{
+ struct cmsghdr *cmsg = CMSG_FIRSTHDR(msg);
+
+ /* select outgoing interface by setting source address */
+ cmsg->cmsg_level = IPPROTO_IPV6;
+ cmsg->cmsg_type = IPV6_PKTINFO;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
+ memcpy(CMSG_DATA(cmsg), &sock->ret_source.a6,
+ sizeof(struct in6_pktinfo));
+
+ msg->msg_controllen = CMSG_SPACE(sizeof(struct in6_pktinfo));
+}
+
+/**
+ * IPv6z creation stuff. Parse the index, fill socket address and creates
+ * a port_sock.
+ *
+ * \param port the port to create
+ * \param params parameters from the SNMP SET
+ *
+ * \return SNMP error
+ */
+static int
+ipv6z_create(struct inet_port *port, struct inet_port_params *params)
+{
+ if (params->addr_len != 20)
+ return (SNMP_ERR_INCONS_VALUE);
+
+ u_int scope_id = 0;
+ for (u_int i = 16; i < 20; i++) {
+ scope_id <<= 8;
+ scope_id |= params->addr[i];
+ }
+
+ const int ret = ipv6_create_common(port, params, scope_id);
+ if (ret != SNMP_ERR_NOERROR)
+ return (ret);
+
+ return (SNMP_ERR_NOERROR);
+}
+
+/**
+ * DNS name creation stuff. Parse the index and save the string.
+ * This does not create a socket struct.
+ *
+ * \param port the port to create
+ * \param params parameters from the SNMP SET
+ *
+ * \return SNMP error
+ */
+static int
+dns_create(struct inet_port *port, struct inet_port_params *params)
+{
+ if (params->addr_len > 64)
+ return (SNMP_ERR_INCONS_VALUE);
+
+ if (strnlen(params->addr, params->addr_len) !=
+ params->addr_len)
+ return (SNMP_ERR_INCONS_VALUE);
+
+ if ((port->dns_addr = realloc(params->addr,
+ params->addr_len + 1)) == NULL)
+ return (SNMP_ERR_GENERR);
+
+ port->dns_addr[params->addr_len] = '\0';
+ params->addr = NULL;
+
+ port->dns_port = htons(params->port);
+
+ return (SNMP_ERR_NOERROR);
+}
+
+/**
+ * Open sockets. This loops through all the addresses returned by getaddrinfo
+ * and opens a socket for each of them.
+ *
+ * \param port inet port
+ *
+ * \return SNMP error code
+ */
+static int
+dns_activate(struct inet_port *port)
+{
+ struct addrinfo hints;
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_DGRAM; // XXX udp-only
+ hints.ai_flags = AI_ADDRCONFIG | AI_PASSIVE | AI_NUMERICSERV;
+
+ char portbuf[8];
+ sprintf(portbuf, "%hu", ntohs(port->dns_port));
+
+ struct addrinfo *res0;
+ int error = getaddrinfo(port->dns_addr[0] == '\0'
+ ? NULL : port->dns_addr,
+ portbuf, &hints, &res0);
+
+ if (error) {
+ syslog(LOG_ERR, "cannot resolve address '%s': %s",
+ port->dns_addr, gai_strerror(error));
+ return (SNMP_ERR_GENERR);
+ }
+
+ for (struct addrinfo *res = res0; res != NULL; res = res->ai_next) {
+ if (res->ai_family != AF_INET && res->ai_family != AF_INET6)
+ continue;
+
+ struct port_sock *sock = calloc(1, sizeof(struct port_sock));
+ if (sock == NULL)
+ return (SNMP_ERR_GENERR);
+
+ snmpd_input_init(&sock->input);
+ sock->port = port;
+
+ int ret = SNMP_ERR_NOERROR;
+
+ if (res->ai_family == AF_INET) {
+ *(struct sockaddr_in *)&sock->bind_addr =
+ *(struct sockaddr_in *)(void *)res->ai_addr;
+ ret = ipv4_activate_sock(sock);
+ } else {
+ *(struct sockaddr_in6 *)&sock->bind_addr =
+ *(struct sockaddr_in6 *)(void *)res->ai_addr;
+ ret = ipv6_activate_sock(sock);
+ }
+
+ if (ret != SNMP_ERR_NOERROR)
+ free(sock);
+ else
+ TAILQ_INSERT_HEAD(&port->socks, sock, link);
+ }
+
+ if (!TAILQ_EMPTY(&port->socks))
+ port->row_status = RowStatus_active;
+
+ freeaddrinfo(res0);
+ return (SNMP_ERR_GENERR);
+}
+
+/**
+ * Deactive the socket. Close all open sockets and delete all sock objects.
+ *
+ * \param port inet port
+ */
+static void
+dns_deactivate(struct inet_port *port)
+{
+ while (!TAILQ_EMPTY(&port->socks)) {
+ struct port_sock *sock = TAILQ_FIRST(&port->socks);
+ TAILQ_REMOVE(&port->socks, sock, link);
+ snmpd_input_close(&sock->input);
+ free(sock);
+ }
+ port->row_status = RowStatus_notInService;
+}
+
+static int
+inet_create(struct inet_port_params *params, struct inet_port **pp)
+{
+ int err = SNMP_ERR_NOERROR;
+ struct inet_port *port = NULL;
+
+ if (params->port > 0xffff) {
+ err = SNMP_ERR_NO_CREATION;
+ goto fail;
+ }
+
+ if ((port = malloc(sizeof(*port))) == NULL) {
+ err = SNMP_ERR_GENERR;
+ goto fail;
+ }
+ memset(port, 0, sizeof(*port));
+ TAILQ_INIT(&port->socks);
+
+ port->proto = params->proto;
+ port->tport.index = params->index;
+
+ switch (params->type) {
+
+ case InetAddressType_ipv4:
+ port->create = ipv4_create;
+ port->activate = ipv4_activate;
+ port->deactivate = ipv4_deactivate;
+ break;
+
+ case InetAddressType_ipv6:
+ port->create = ipv6_create;
+ port->activate = ipv6_activate;
+ port->deactivate = ipv6_deactivate;
+ break;
+
+ case InetAddressType_ipv6z:
+ port->create = ipv6z_create;
+ port->activate = ipv6_activate;
+ port->deactivate = ipv6_deactivate;
+ break;
+
+ case InetAddressType_dns:
+ port->create = dns_create;
+ port->activate = dns_activate;
+ port->deactivate = dns_deactivate;
+ break;
+
+ default:
+ err = SNMP_ERR_NO_CREATION;
+ goto fail;
+ }
+
+ if ((err = port->create(port, params)) != SNMP_ERR_NOERROR)
+ goto fail;
+
+ *pp = port;
+ trans_insert_port(my_trans, &port->tport);
+ return (err);
+
+fail:
+ free(port->dns_addr);
+ free(port);
+ return (err);
+}
+
+static int
+create_and_go(struct snmp_context *ctx, struct inet_port_params *params)
+{
+ int err;
+ struct inet_port *port;
+
+ if ((err = inet_create(params, &port)) != SNMP_ERR_NOERROR)
+ return (err);
+
+ port->row_status = RowStatus_createAndGo;
+ ctx->scratch->ptr1 = port;
+
+ if (community == COMM_INITIALIZE)
+ return (err);
+
+ return (inet_activate(&port->tport));
+}
+
+static int
+create_and_wait(struct snmp_context *ctx, struct inet_port_params *params)
+{
+ int err;
+ struct inet_port *port;
+
+ if ((err = inet_create(params, &port)) != SNMP_ERR_NOERROR)
+ return (err);
+
+ port->row_status = RowStatus_createAndWait;
+ ctx->scratch->ptr1 = port;
+
+ return (err);
+}
+
+/**
+ * This is called to set a RowStatus value in the port table during
+ * SET processing.
+ *
+ * When this is called during initialization time and the RowStatus is set
+ * to CreateAndGo, the port is actually not activated. This is done when
+ * the main code calls the init() for all ports later.
+ */
+static int
+inet_port_set(struct snmp_context *ctx, struct inet_port *port,
+ struct inet_port_params *params, enum RowStatus nstatus)
+{
+ switch (nstatus) {
+
+ case RowStatus_createAndGo:
+ if (port != NULL)
+ return (SNMP_ERR_INCONS_VALUE);
+ ctx->scratch->int1 = SET_CREATED;
+ return (create_and_go(ctx, params));
+
+ case RowStatus_createAndWait:
+ if (port != NULL)
+ return (SNMP_ERR_INCONS_VALUE);
+ ctx->scratch->int1 = SET_CREATED;
+ return (create_and_wait(ctx, params));
+
+ case RowStatus_active:
+ if (port == NULL)
+ return (SNMP_ERR_INCONS_VALUE);
+
+ switch (port->row_status) {
+
+ case RowStatus_notReady:
+ /* this can not happend */
+ abort();
+
+ case RowStatus_notInService:
+ ctx->scratch->int1 = SET_ACTIVATED;
+ return (inet_activate(&port->tport));
+
+ case RowStatus_active:
+ return (SNMP_ERR_NOERROR);
+
+ case RowStatus_createAndGo:
+ case RowStatus_createAndWait:
+ case RowStatus_destroy:
+ abort();
+ }
+ break;
+
+ case RowStatus_notInService:
+ if (port == NULL)
+ return (SNMP_ERR_INCONS_VALUE);
+
+ switch (port->row_status) {
+
+ case RowStatus_notReady:
+ /* this can not happend */
+ abort();
+
+ case RowStatus_notInService:
+ return (SNMP_ERR_NOERROR);
+
+ case RowStatus_active:
+ /* this is done during commit */
+ ctx->scratch->int1 = SET_DEACTIVATE;
+ return (SNMP_ERR_NOERROR);
+
+ case RowStatus_createAndGo:
+ case RowStatus_createAndWait:
+ case RowStatus_destroy:
+ abort();
+ }
+ break;
+
+ case RowStatus_destroy:
+ /* this is done during commit */
+ ctx->scratch->int1 = SET_DESTROY;
+ return (SNMP_ERR_NOERROR);
+
+ case RowStatus_notReady:
+ return (SNMP_ERR_WRONG_VALUE);
+ }
+ abort();
+}
+
+/*
+ * Port table
+ */
+int
+op_snmp_trans_inet(struct snmp_context *ctx, struct snmp_value *value,
+ u_int sub, u_int iidx __unused, enum snmp_op op)
+{
+ asn_subid_t which = value->var.subs[sub - 1];
+ struct inet_port *port;
+ struct inet_port_params params;
+ int ret;
+
+ switch (op) {
+
+ case SNMP_OP_GETNEXT:
+ if ((port = (struct inet_port *)trans_next_port(my_trans,
+ &value->var, sub)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ index_append(&value->var, sub, &port->tport.index);
+ goto fetch;
+
+ case SNMP_OP_GET:
+ if ((port = (struct inet_port *)trans_find_port(my_trans,
+ &value->var, sub)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ goto fetch;
+
+ case SNMP_OP_SET:
+ port = (struct inet_port *)trans_find_port(my_trans,
+ &value->var, sub);
+
+ if (which != LEAF_begemotSnmpdTransInetStatus)
+ abort();
+ if (!isok_RowStatus(value->v.integer))
+ return (SNMP_ERR_WRONG_VALUE);
+
+ if (index_decode(&value->var, sub, iidx, ¶ms.type,
+ ¶ms.addr, ¶ms.addr_len, ¶ms.port,
+ ¶ms.proto))
+ return (SNMP_ERR_NO_CREATION);
+
+ asn_slice_oid(¶ms.index, &value->var, sub, value->var.len);
+
+ ret = inet_port_set(ctx, port, ¶ms,
+ (enum RowStatus)value->v.integer);
+
+ free(params.addr);
+ return (ret);
+
+ case SNMP_OP_ROLLBACK:
+ if ((port = (struct inet_port *)trans_find_port(my_trans,
+ &value->var, sub)) == NULL)
+ /* cannot happen */
+ abort();
+
+ switch (ctx->scratch->int1) {
+
+ case SET_CREATED:
+ /* newly created */
+ assert(port != NULL);
+ inet_destroy_port(&port->tport);
+ return (SNMP_ERR_NOERROR);
+
+ case SET_DESTROY:
+ /* do it now */
+ assert(port != NULL);
+ return (SNMP_ERR_NOERROR);
+
+ case SET_ACTIVATED:
+ deactivate_port(port);
+ return (SNMP_ERR_NOERROR);
+
+ case SET_DEACTIVATE:
+ return (SNMP_ERR_NOERROR);
+ }
+ abort();
+
+ case SNMP_OP_COMMIT:
+ if ((port = (struct inet_port *)trans_find_port(my_trans,
+ &value->var, sub)) == NULL)
+ /* cannot happen */
+ abort();
+
+ switch (ctx->scratch->int1) {
+
+ case SET_CREATED:
+ /* newly created */
+ assert(port != NULL);
+ return (SNMP_ERR_NOERROR);
+
+ case SET_DESTROY:
+ /* do it now */
+ assert(port != NULL);
+ inet_destroy_port(&port->tport);
+ return (SNMP_ERR_NOERROR);
+
+ case SET_ACTIVATED:
+ return (SNMP_ERR_NOERROR);
+
+ case SET_DEACTIVATE:
+ deactivate_port(port);
+ return (SNMP_ERR_NOERROR);
+ }
+ abort();
+ }
+ abort();
+
+ fetch:
+ switch (which) {
+
+ case LEAF_begemotSnmpdTransInetStatus:
+ value->v.integer = port->row_status;
+ break;
+
+ default:
+ abort();
+ }
+
+ return (SNMP_ERR_NOERROR);
+}
Index: head/contrib/bsnmp/snmpd/trans_lsock.c
===================================================================
--- head/contrib/bsnmp/snmpd/trans_lsock.c
+++ head/contrib/bsnmp/snmpd/trans_lsock.c
@@ -70,7 +70,8 @@
lsock_close_port,
lsock_init_port,
lsock_send,
- lsock_recv
+ lsock_recv,
+ NULL
};
static struct transport *my_trans;
Index: head/contrib/bsnmp/snmpd/trans_udp.c
===================================================================
--- head/contrib/bsnmp/snmpd/trans_udp.c
+++ head/contrib/bsnmp/snmpd/trans_udp.c
@@ -67,7 +67,8 @@
udp_close_port,
udp_init_port,
udp_send,
- udp_recv
+ udp_recv,
+ NULL
};
static struct transport *my_trans;
Index: head/contrib/bsnmp/snmpd/tree.def
===================================================================
--- head/contrib/bsnmp/snmpd/tree.def
+++ head/contrib/bsnmp/snmpd/tree.def
@@ -3,6 +3,10 @@
# Fraunhofer Institute for Open Communication Systems (FhG Fokus).
# All rights reserved.
#
+# Copyright (c) 2018
+# Hartmut Brandt.
+# All rights reserved.
+#
# Author: Harti Brandt <harti@freebsd.org>
#
# Redistribution and use in source and binary forms, with or without
@@ -33,6 +37,10 @@
include "tc.def"
+typedef BegemotSnmpdTransportProto ENUM (
+ 1 udp
+)
+
(1 internet
(2 mgmt
(1 mibII
@@ -172,13 +180,24 @@
))
(2 begemotSnmpdTransUdp OID op_transport_dummy)
(3 begemotSnmpdTransLsock OID op_transport_dummy)
+ (4 begemotSnmpdTransInet OID op_transport_dummy)
)
+ (11 begemotSnmpdTransInetTable
+ (1 begemotSnmpdTransInetEntry : INTEGER OCTETSTRING INTEGER INTEGER op_snmp_trans_inet
+ (1 begemotSnmpdTransInetAddressType InetAddressType)
+ (2 begemotSnmpdTransInetAddress OCTETSTRING)
+ (3 begemotSnmpdTransInetPort INTEGER)
+ (4 begemotSnmpdTransInetProto BegemotSnmpdTransportProto)
+ (5 begemotSnmpdTransInetStatus RowStatus GET SET)
+
+ ))
)
(2 begemotSnmpdDefs
(1 begemotSnmpdAgent
(1 begemotSnmpdAgentFreeBSD OID op_dummy)
)
)
+ (3 begemotSnmpdCompliance)
)
))
)
Index: head/lib/libbsnmp/libbsnmp/Makefile
===================================================================
--- head/lib/libbsnmp/libbsnmp/Makefile
+++ head/lib/libbsnmp/libbsnmp/Makefile
@@ -129,4 +129,8 @@
MLINKS+= bsnmplib.3 snmp_value_free.3
MLINKS+= bsnmplib.3 snmp_value_parse.3
+FILESGROUPS+= DEFS
+DEFS= tc.def
+DEFSDIR?= ${SHAREDIR}/snmp/defs
+
.include <bsd.lib.mk>
Index: head/usr.sbin/bsnmpd/bsnmpd/Makefile
===================================================================
--- head/usr.sbin/bsnmpd/bsnmpd/Makefile
+++ head/usr.sbin/bsnmpd/bsnmpd/Makefile
@@ -11,11 +11,11 @@
CONFSMODE= 600
PROG= bsnmpd
SRCS= main.c action.c config.c export.c trap.c trans_udp.c trans_lsock.c
-SRCS+= oid.h tree.c tree.h
+SRCS+= trans_inet.c oid.h tree.c tree.h
XSYM= snmpMIB begemotSnmpdModuleTable begemotSnmpd begemotTrapSinkTable \
sysUpTime snmpTrapOID coldStart authenticationFailure \
begemotSnmpdTransUdp begemotSnmpdTransLsock begemotSnmpdLocalPortTable \
- freeBSD freeBSDVersion
+ freeBSD freeBSDVersion begemotSnmpdTransInet
CLEANFILES= oid.h tree.c tree.h
MAN= bsnmpd.1 snmpmod.3
Index: head/usr.sbin/bsnmpd/bsnmpd/snmpd.config
===================================================================
--- head/usr.sbin/bsnmpd/bsnmpd/snmpd.config
+++ head/usr.sbin/bsnmpd/bsnmpd/snmpd.config
@@ -92,10 +92,22 @@
#
begemotSnmpdCommunityString.0.1 = $(read)
# begemotSnmpdCommunityString.0.2 = $(write)
+# begemotSnmpdCommunityString.0.3 = "otherPublic"
begemotSnmpdCommunityDisable = 1
# open standard SNMP ports
-begemotSnmpdPortStatus.0.0.0.0.161 = 1
+begemotSnmpdTransInetStatus.1.4.0.0.0.0.161.1 = 4
+begemotSnmpdTransInetStatus.2.16.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.161.1 = 4
+
+# UDP over IPv4: 127.0.0.1:161
+# begemotSnmpdTransInetStatus.1.4.127.0.0.1.161.1 = 4
+
+# UDP over IPv6: ::1:161
+# begemotSnmpdTransInetStatus.2.16.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.1.161.1 = 4
+
+# Use domain name and IPv6 link-local address with scope zone id as address
+# begemotSnmpdTransInetStatus.16."localhost".161.1 = 4
+# begemotSnmpdTransInetStatus.16."fe80::1%em0".161.1 = 4
# open a unix domain socket
begemotSnmpdLocalPortStatus."/var/run/snmpd.sock" = 1
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Fri, Feb 7, 1:44 PM (20 h, 3 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
16511206
Default Alt Text
D16654.diff (83 KB)
Attached To
Mode
D16654: IPv6 transport for bsnmp
Attached
Detach File
Event Timeline
Log In to Comment