Page MenuHomeFreeBSD

D16654.diff
No OneTemporary

D16654.diff

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, &params.type,
+ &params.addr, &params.addr_len, &params.port,
+ &params.proto))
+ return (SNMP_ERR_NO_CREATION);
+
+ asn_slice_oid(&params.index, &value->var, sub, value->var.len);
+
+ ret = inet_port_set(ctx, port, &params,
+ (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

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)

Event Timeline