Clixon SNMP frontend

Added two new config options to clixon-config.yang: `CLICON_HTTP_DATA_ROOT`
Added new files: apps/snmp/snmp_mib_yang.[ch] for generic MIB/YANG handling
Test: killall quiet
Test: added specific MIB for generic code: <CLICON_SNMP_MIB>NET-SNMP-EXAMPLES-MIB</CLICON_SNMP_MIB>
This commit is contained in:
Olof hagsand 2022-05-04 15:03:08 +02:00
parent ee06652e86
commit df687f7180
12 changed files with 561 additions and 243 deletions

View file

@ -41,8 +41,9 @@
* Support of SNMP for retreiving and setting values via net-snmp using a MIB-YANG mapping defined in RFC6643. * Support of SNMP for retreiving and setting values via net-snmp using a MIB-YANG mapping defined in RFC6643.
* For details, see [SNMP section of user manual](https://clixon-docs.readthedocs.io/en/latest/snmp.html) * For details, see [SNMP section of user manual](https://clixon-docs.readthedocs.io/en/latest/snmp.html)
* YANG `clixon-config@2022-03-21.yang` changes: * YANG `clixon-config@2022-03-21.yang` changes:
* Added option: * Added options:
* `CLICON_SNMP_AGENT_SOCK` * `CLICON_SNMP_AGENT_SOCK`
* `CLICON_SNMP_MIB`
* New configure options: * New configure options:
* `--enable-netsnmp` * `--enable-netsnmp`
* `--with-mib-generated-yang-dir=DIR` * `--with-mib-generated-yang-dir=DIR`

View file

@ -861,7 +861,7 @@ cli_show_options(clicon_handle h,
else else
fprintf(stdout, "%s: NULL\n", keys[i]); fprintf(stdout, "%s: NULL\n", keys[i]);
} }
/* Next print CLICON_FEATURE and CLICON_YANG_DIR from config tree /* Next print CLICON_FEATURE, CLICON_YANG_DIR and CLICON_SNMP_MIB from config tree
* Since they are lists they are placed in the config tree. * Since they are lists they are placed in the config tree.
*/ */
x = NULL; x = NULL;
@ -876,6 +876,12 @@ cli_show_options(clicon_handle h,
continue; continue;
fprintf(stdout, "%s: \"%s\"\n", xml_name(x), xml_body(x)); fprintf(stdout, "%s: \"%s\"\n", xml_name(x), xml_body(x));
} }
x = NULL;
while ((x = xml_child_each(clicon_conf_xml(h), x, CX_ELMNT)) != NULL) {
if (strcmp(xml_name(x), "CLICON_SNMP_MIB") != 0)
continue;
fprintf(stdout, "%s: \"%s\"\n", xml_name(x), xml_body(x));
}
retval = 0; retval = 0;
done: done:
if (keys) if (keys)

View file

@ -84,6 +84,7 @@ APPL = clixon_snmp
# Common source - not accessible from plugin - independent of restconf package (fcgi|native) # Common source - not accessible from plugin - independent of restconf package (fcgi|native)
APPSRC = APPSRC =
APPSRC += snmp_main.c APPSRC += snmp_main.c
APPSRC += snmp_mib_yang.c
APPOBJ = $(APPSRC:.c=.o) APPOBJ = $(APPSRC:.c=.o)

View file

@ -55,229 +55,11 @@
/* clicon */ /* clicon */
#include <clixon/clixon.h> #include <clixon/clixon.h>
#include "snmp_mib_yang.h"
/* Command line options to be passed to getopt(3) */ /* Command line options to be passed to getopt(3) */
#define SNMP_OPTS "hD:f:l:o:" #define SNMP_OPTS "hD:f:l:o:"
#if 1 /* scalar example */
#define TESTHANDLER_SET_NAME "my_test"
int
my_test_instance_handler(netsnmp_mib_handler *handler,
netsnmp_handler_registration *reginfo,
netsnmp_agent_request_info *reqinfo,
netsnmp_request_info *requests)
{
static int accesses = 42;
u_long *accesses_cache = NULL;
clicon_debug(1, "%s", __FUNCTION__);
switch (reqinfo->mode) {
case MODE_GET:
snmp_set_var_typed_value(requests->requestvb, ASN_INTEGER,
(u_char *) & accesses, sizeof(accesses));
break;
case MODE_SET_RESERVE1:
if (requests->requestvb->type != ASN_INTEGER)
netsnmp_set_request_error(reqinfo, requests,
SNMP_ERR_WRONGTYPE);
break;
case MODE_SET_RESERVE2:
/*
* store old info for undo later
*/
accesses_cache = netsnmp_memdup(&accesses, sizeof(accesses));
if (accesses_cache == NULL) {
netsnmp_set_request_error(reqinfo, requests,
SNMP_ERR_RESOURCEUNAVAILABLE);
return SNMP_ERR_NOERROR;
}
netsnmp_request_add_list_data(requests,
netsnmp_create_data_list
(TESTHANDLER_SET_NAME,
accesses_cache, free));
break;
case MODE_SET_ACTION:
/*
* update current
*/
accesses = *(requests->requestvb->val.integer);
DEBUGMSGTL(("testhandler", "updated accesses -> %d\n", accesses));
break;
case MODE_SET_UNDO:
accesses =
*((u_long *) netsnmp_request_get_list_data(requests,
TESTHANDLER_SET_NAME));
break;
case MODE_SET_COMMIT:
case MODE_SET_FREE:
/*
* nothing to do
*/
break;
}
return SNMP_ERR_NOERROR;
}
static void
init_testscalar(void)
{
oid my_oid[] = { 1, 3, 6, 1, 4, 1, 8072, 2, 1, 1 };
clicon_debug(1, "%s", __FUNCTION__);
/*
* instance handler test
*/
netsnmp_register_instance(netsnmp_create_handler_registration
("netSnmpExampleInteger", my_test_instance_handler,
my_oid, OID_LENGTH(my_oid),
HANDLER_CAN_RWRITE));
}
#endif
#if 1 /* table example */
static netsnmp_table_data_set *table_set;
/*
* https://net-snmp.sourceforge.io/dev/agent/data_set_8c-example.html#_a0
*/
static void
init_testtable(void)
{
netsnmp_table_row *row;
/*
* the OID we want to register our integer at. This should be the
* * OID node for the entire table. In our case this is the
* * netSnmpIETFWGTable oid definition
*/
oid my_oid[] = { 1, 3, 6, 1, 4, 1, 8072, 2, 2, 1 };
/*
* a debugging statement. Run the agent with -Dexample_data_set to see
* * the output of this debugging statement.
*/
DEBUGMSGTL(("example_data_set",
"Initalizing example dataset table\n"));
/*
* It's going to be the "working group chairs" table, since I'm
* * sitting at an IETF convention while I'm writing this.
* *
* * column 1 = index = string = WG name
* * column 2 = string = chair #1
* * column 3 = string = chair #2 (most WGs have 2 chairs now)
*/
table_set = netsnmp_create_table_data_set("netSnmpIETFWGTable");
/*
* allow the creation of new rows via SNMP SETs
*/
table_set->allow_creation = 1;
/*
* set up what a row "should" look like, starting with the index
*/
netsnmp_table_dataset_add_index(table_set, ASN_OCTET_STR);
/*
* define what the columns should look like. both are octet strings here
*/
netsnmp_table_set_multi_add_default_row(table_set,
/*
* column 2 = OCTET STRING,
* writable = 1,
* default value = NULL,
* default value len = 0
*/
2, ASN_OCTET_STR, 1, NULL, 0,
/*
* similar
*/
3, ASN_OCTET_STR, 1, NULL, 0,
0 /* done */ );
/*
* register the table
*/
/*
* if we wanted to handle specific data in a specific way, or note
* * when requests came in we could change the NULL below to a valid
* * handler method in which we could over ride the default
* * behaviour of the table_dataset helper
*/
netsnmp_register_table_data_set(netsnmp_create_handler_registration
("netSnmpIETFWGTable", NULL,
my_oid, OID_LENGTH(my_oid),
HANDLER_CAN_RWRITE), table_set, NULL);
/*
* create the a row for the table, and add the data
*/
row = netsnmp_create_table_data_row();
/*
* set the index to the IETF WG name "snmpv3"
*/
netsnmp_table_row_add_index(row, ASN_OCTET_STR, "snmpv3",
strlen("snmpv3"));
/*
* set column 2 to be the WG chair name "Russ Mundy"
*/
netsnmp_set_row_column(row, 2, ASN_OCTET_STR,
"Russ Mundy", strlen("Russ Mundy"));
netsnmp_mark_row_column_writable(row, 2, 1); /* make writable via SETs */
/*
* set column 3 to be the WG chair name "David Harrington"
*/
netsnmp_set_row_column(row, 3, ASN_OCTET_STR, "David Harrington",
strlen("David Harrington"));
netsnmp_mark_row_column_writable(row, 3, 1); /* make writable via SETs */
/*
* add the row to the table
*/
netsnmp_table_dataset_add_row(table_set, row);
/*
* add the data, for the second row
*/
row = netsnmp_create_table_data_row();
netsnmp_table_row_add_index(row, ASN_OCTET_STR, "snmpconf",
strlen("snmpconf"));
netsnmp_set_row_column(row, 2, ASN_OCTET_STR, "David Partain",
strlen("David Partain"));
netsnmp_mark_row_column_writable(row, 2, 1); /* make writable */
netsnmp_set_row_column(row, 3, ASN_OCTET_STR, "Jon Saperia",
strlen("Jon Saperia"));
netsnmp_mark_row_column_writable(row, 3, 1); /* make writable */
netsnmp_table_dataset_add_row(table_set, row);
/*
* Finally, this actually allows the "add_row" token it the
* * snmpd.conf file to add rows to this table.
* * Example snmpd.conf line:
* * add_row netSnmpIETFWGTable eos "Glenn Waters" "Dale Francisco"
*/
netsnmp_register_auto_data_table(table_set, NULL);
DEBUGMSGTL(("example_data_set", "Done initializing.\n"));
}
#endif /* table example */
/*! Signal terminates process /*! Signal terminates process
* Just set exit flag for proper exit in event loop * Just set exit flag for proper exit in event loop
*/ */
@ -357,8 +139,8 @@ clixon_snmp_fdset_register(clicon_handle h)
* @see snmp_terminate * @see snmp_terminate
*/ */
static int static int
clixon_snmp_init(clicon_handle h, clixon_snmp_subagent(clicon_handle h,
int logdst) int logdst)
{ {
int retval = -1; int retval = -1;
char *sockpath = NULL; char *sockpath = NULL;
@ -378,13 +160,10 @@ clixon_snmp_init(clicon_handle h,
/* XXX: This should be configurable. */ /* XXX: This should be configurable. */
netsnmp_ds_set_string(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_X_SOCKET, sockpath); netsnmp_ds_set_string(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_X_SOCKET, sockpath);
/* initialize the agent library */ /* initialize the agent library */
init_agent(__PROGRAM__); init_agent(__PROGRAM__);
/* XXX Hardcoded, replace this with generic MIB */
init_testscalar();
init_testtable();
/* example-demon will be used to read example-demon.conf files. */ /* example-demon will be used to read example-demon.conf files. */
init_snmp(__PROGRAM__); init_snmp(__PROGRAM__);
@ -397,7 +176,7 @@ clixon_snmp_init(clicon_handle h,
goto done; goto done;
} }
if (set_signal(SIGPIPE, SIG_IGN, NULL) < 0){ if (set_signal(SIGPIPE, SIG_IGN, NULL) < 0){
clicon_err(OE_UNIX, errno, "Setting DIGPIPE signal"); clicon_err(OE_UNIX, errno, "Setting SIGPIPE signal");
goto done; goto done;
} }
/* Workaround for netsnmps API use of fdset:s instead of sockets */ /* Workaround for netsnmps API use of fdset:s instead of sockets */
@ -633,7 +412,10 @@ main(int argc,
clicon_session_id_set(h, id); clicon_session_id_set(h, id);
/* Init snmp as subagent */ /* Init snmp as subagent */
if (clixon_snmp_init(h, logdst) < 0) if (clixon_snmp_subagent(h, logdst) < 0)
goto done;
/* Init mib-translated yangs and register callbacks */
if (clixon_snmp_mib_yangs(h) < 0)
goto done; goto done;
if (dbg) if (dbg)

453
apps/snmp/snmp_mib_yang.c Normal file
View file

@ -0,0 +1,453 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2022 Olof Hagsand and Kristofer Hallin
This file is part of CLIXON.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Alternatively, the contents of this file may be used under the terms of
the GNU General Public License Version 3 or later (the "GPL"),
in which case the provisions of the GPL are applicable instead
of those above. If you wish to allow use of your version of this file only
under the terms of the GPL, and not to allow others to
use your version of this file under the terms of Apache License version 2,
indicate your decision by deleting the provisions above and replace them with
the notice and other provisions required by the GPL. If you do not delete
the provisions above, a recipient may use your version of this file under
the terms of any one of the Apache License version 2 or the GPL.
***** END LICENSE BLOCK *****
* Extensions are grouped in some categories, the one I have seen are, example:
* 1. leaf
* smiv2:max-access "read-write";
* smiv2:oid "1.3.6.1.4.1.8072.2.1.1";
* smiv2:defval "42"; (not always)
* 2. container, list
* smiv2:oid "1.3.6.1.4.1.8072.2.1";
* 3. module level
* smiv2:alias "netSnmpExamples" {
* smiv2:oid "1.3.6.1.4.1.8072.2";
*
*/
#ifdef HAVE_CONFIG_H
#include "clixon_config.h" /* generated by config & autoconf */
#endif
#include <stdio.h>
#include <unistd.h>
#include <pwd.h>
#include <syslog.h>
#include <errno.h>
#include <sys/types.h>
#include <signal.h>
/* net-snmp */
#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
#include <net-snmp/agent/net-snmp-agent-includes.h>
/* cligen */
#include <cligen/cligen.h>
/* clicon */
#include <clixon/clixon.h>
#include "snmp_mib_yang.h"
#define IETF_YANG_SMIV2_NS "urn:ietf:params:xml:ns:yang:ietf-yang-smiv2"
/*
* Local variables
*/
/* Mapping between yang keyword string <--> clixon constants
* Here is also the place where doc on some types store variables (cv)
*/
/* Mapping between smiv2 yang extension access string string <--> netsnmp handler codes (agent_handler.h)
* Here is also the place where doc on some types store variables (cv)
* see netsnmp_handler_registration_create()
*/
static const map_str2int acc_map[] = {
{"read-only", HANDLER_CAN_RONLY}, /* HANDLER_CAN_GETANDGETNEXT */
{"read-write", HANDLER_CAN_RWRITE}, /* HANDLER_CAN_GETANDGETNEXT | HANDLER_CAN_SET */
{"not-accessible", 0}, // XXX
{"accessible-for-notify", 0}, // XXX
{NULL, -1}
};
#if 1 /* table example */
static netsnmp_table_data_set *table_set;
/*
* https://net-snmp.sourceforge.io/dev/agent/data_set_8c-example.html#_a0
*/
static void
init_testtable(void)
{
netsnmp_table_row *row;
/*
* the OID we want to register our integer at. This should be the
* * OID node for the entire table. In our case this is the
* * netSnmpIETFWGTable oid definition
*/
oid my_oid[] = { 1, 3, 6, 1, 4, 1, 8072, 2, 2, 1 };
/*
* a debugging statement. Run the agent with -Dexample_data_set to see
* * the output of this debugging statement.
*/
DEBUGMSGTL(("example_data_set",
"Initalizing example dataset table\n"));
/*
* It's going to be the "working group chairs" table, since I'm
* * sitting at an IETF convention while I'm writing this.
* *
* * column 1 = index = string = WG name
* * column 2 = string = chair #1
* * column 3 = string = chair #2 (most WGs have 2 chairs now)
*/
table_set = netsnmp_create_table_data_set("netSnmpIETFWGTable");
/*
* allow the creation of new rows via SNMP SETs
*/
table_set->allow_creation = 1;
/*
* set up what a row "should" look like, starting with the index
*/
netsnmp_table_dataset_add_index(table_set, ASN_OCTET_STR);
/*
* define what the columns should look like. both are octet strings here
*/
netsnmp_table_set_multi_add_default_row(table_set,
/*
* column 2 = OCTET STRING,
* writable = 1,
* default value = NULL,
* default value len = 0
*/
2, ASN_OCTET_STR, 1, NULL, 0,
/*
* similar
*/
3, ASN_OCTET_STR, 1, NULL, 0,
0 /* done */ );
/*
* register the table
*/
/*
* if we wanted to handle specific data in a specific way, or note
* * when requests came in we could change the NULL below to a valid
* * handler method in which we could over ride the default
* * behaviour of the table_dataset helper
*/
netsnmp_register_table_data_set(netsnmp_create_handler_registration
("netSnmpIETFWGTable", NULL,
my_oid, OID_LENGTH(my_oid),
HANDLER_CAN_RWRITE), table_set, NULL);
/*
* create the a row for the table, and add the data
*/
row = netsnmp_create_table_data_row();
/*
* set the index to the IETF WG name "snmpv3"
*/
netsnmp_table_row_add_index(row, ASN_OCTET_STR, "snmpv3",
strlen("snmpv3"));
/*
* set column 2 to be the WG chair name "Russ Mundy"
*/
netsnmp_set_row_column(row, 2, ASN_OCTET_STR,
"Russ Mundy", strlen("Russ Mundy"));
netsnmp_mark_row_column_writable(row, 2, 1); /* make writable via SETs */
/*
* set column 3 to be the WG chair name "David Harrington"
*/
netsnmp_set_row_column(row, 3, ASN_OCTET_STR, "David Harrington",
strlen("David Harrington"));
netsnmp_mark_row_column_writable(row, 3, 1); /* make writable via SETs */
/*
* add the row to the table
*/
netsnmp_table_dataset_add_row(table_set, row);
/*
* add the data, for the second row
*/
row = netsnmp_create_table_data_row();
netsnmp_table_row_add_index(row, ASN_OCTET_STR, "snmpconf",
strlen("snmpconf"));
netsnmp_set_row_column(row, 2, ASN_OCTET_STR, "David Partain",
strlen("David Partain"));
netsnmp_mark_row_column_writable(row, 2, 1); /* make writable */
netsnmp_set_row_column(row, 3, ASN_OCTET_STR, "Jon Saperia",
strlen("Jon Saperia"));
netsnmp_mark_row_column_writable(row, 3, 1); /* make writable */
netsnmp_table_dataset_add_row(table_set, row);
/*
* Finally, this actually allows the "add_row" token it the
* * snmpd.conf file to add rows to this table.
* * Example snmpd.conf line:
* * add_row netSnmpIETFWGTable eos "Glenn Waters" "Dale Francisco"
*/
netsnmp_register_auto_data_table(table_set, NULL);
DEBUGMSGTL(("example_data_set", "Done initializing.\n"));
}
#endif /* table example */
#if 1 /* scalar example */
#define TESTHANDLER_SET_NAME "my_test"
int
my_test_instance_handler(netsnmp_mib_handler *handler,
netsnmp_handler_registration *reginfo,
netsnmp_agent_request_info *reqinfo,
netsnmp_request_info *requests)
{
static int accesses = 42;
u_long *accesses_cache = NULL;
// clicon_handle h = (clicon_handle)handler->myvoid;
clicon_debug(1, "%s", __FUNCTION__);
clicon_debug(1, "%s %p", __FUNCTION__, reginfo);
switch (reqinfo->mode) {
case MODE_GET:
snmp_set_var_typed_value(requests->requestvb, ASN_INTEGER,
(u_char *) & accesses, sizeof(accesses));
break;
case MODE_SET_RESERVE1:
if (requests->requestvb->type != ASN_INTEGER)
netsnmp_set_request_error(reqinfo, requests,
SNMP_ERR_WRONGTYPE);
break;
case MODE_SET_RESERVE2:
/*
* store old info for undo later
*/
accesses_cache = netsnmp_memdup(&accesses, sizeof(accesses));
if (accesses_cache == NULL) {
netsnmp_set_request_error(reqinfo, requests,
SNMP_ERR_RESOURCEUNAVAILABLE);
return SNMP_ERR_NOERROR;
}
netsnmp_request_add_list_data(requests,
netsnmp_create_data_list
(TESTHANDLER_SET_NAME,
accesses_cache, free));
break;
case MODE_SET_ACTION:
/*
* update current
*/
accesses = *(requests->requestvb->val.integer);
DEBUGMSGTL(("testhandler", "updated accesses -> %d\n", accesses));
break;
case MODE_SET_UNDO:
accesses =
*((u_long *) netsnmp_request_get_list_data(requests,
TESTHANDLER_SET_NAME));
break;
case MODE_SET_COMMIT:
case MODE_SET_FREE:
/*
* nothing to do
*/
break;
}
return SNMP_ERR_NOERROR;
}
#endif
/*! Parse smiv2 extensions for YANG leaf
* Typical leaf:
* smiv2:oid "1.3.6.1.4.1.8072.2.1.1";
* smiv2:max-access "read-write";
* smiv2:defval "42"; (optional)
*/
static int
mib_yang_leaf(clicon_handle h,
yang_stmt *ys)
{
int retval = -1;
netsnmp_handler_registration *nh = NULL;
netsnmp_mib_handler *handler;
int ret;
char *oidstr = NULL;
char *modes_str = NULL;
oid oid1[MAX_OID_LEN] = {0,};
size_t sz1 = MAX_OID_LEN;
int modes;
yang_stmt *yrestype; /* resolved type */
// char *restype; /* resolved type */
char *origtype=NULL; /* original type */
char *name;
clicon_debug(1, "%s %s", __FUNCTION__, oidstr);
if (yang_extension_value(ys, "oid", IETF_YANG_SMIV2_NS, NULL, &oidstr) < 0)
goto done;
if (oidstr == NULL)
goto ok;
if (snmp_parse_oid(oidstr, oid1, &sz1) == NULL){
clicon_err(OE_SNMP, 0, "snmp_parse_oid");
goto done;
}
if (yang_extension_value(ys, "max-access", IETF_YANG_SMIV2_NS, NULL, &modes_str) < 0)
goto done;
/* read-only, read-write, not-accessible, oaccessible-for-notify
*/
if (modes_str == NULL)
goto ok;
modes = clicon_str2int(acc_map, modes_str);
/* Default value, XXX How is this different from yang defaults?
*/
// if (yang_extension_value(ys, "defval", IETF_YANG_SMIV2_NS, NULL, &modes_str) < 0)
// goto done;
/* get yang type of leaf */
if (yang_type_get(ys, &origtype, &yrestype, NULL, NULL, NULL, NULL, NULL) < 0)
goto done;
// restype = yrestype?yang_argument_get(yrestype):NULL;
name = yang_argument_get(ys);
if ((handler = netsnmp_create_handler(name, my_test_instance_handler)) == NULL){
clicon_err(OE_SNMP, errno, "netsnmp_create_handler");
goto done;
}
// handler->myvoid = h;
handler->myvoid =(void*)99;
if ((nh = netsnmp_handler_registration_create(name,
handler,
oid1,
sz1,
modes)) == NULL){
clicon_err(OE_SNMP, errno, "netsnmp_handler_registration_create");
netsnmp_handler_free(handler);
goto done;
}
clicon_debug(1, "%s %p", __FUNCTION__, nh);
if ((ret = netsnmp_register_instance(nh)) < 0){
/* XXX Failures are MIB_REGISTRATION_FAILED and MIB_DUPLICATE_REGISTRATION. */
clicon_err(OE_SNMP, ret, "netsnmp_register_instance");
goto done;
}
ok:
retval = 0;
done:
return retval;
}
/*! Check smiv2 extensions for
*
* Called for each node in a mib-yang
* Algorithm is to find smiv2:oid, then its associated parent type (eg leaf, container, list)
* and then register callbacks.
* @param[in] ys Yang node, of type unknown
* @param[in] arg Argument, in effect clicon_handle
* @retval -1 Error
* @retval 0 OK
* @see yang_extension_value
* @see ys_populate_unknown
*/
static int
mib_yang_extension(yang_stmt *ys,
void *arg)
{
int retval = -1;
clicon_handle h = (clicon_handle)arg;
switch(yang_keyword_get(ys)){
case Y_LEAF:
if (mib_yang_leaf(h, ys) < 0)
goto done;
break;
case Y_CONTAINER: // XXX
break;
case Y_LIST: // XXX
break;
default:
break;
}
retval = 0;
done:
return retval;
}
int
clixon_snmp_mib_yangs(clicon_handle h)
{
int retval = -1;
char *modname;
cxobj *x;
yang_stmt *yspec;
yang_stmt *ymod;
/* XXX Hardcoded, replace this with generic MIB */
init_testtable(); // XXX
if ((yspec = clicon_dbspec_yang(h)) == NULL){
clicon_err(OE_FATAL, 0, "No DB_SPEC");
goto done;
}
x = NULL;
while ((x = xml_child_each(clicon_conf_xml(h), x, CX_ELMNT)) != NULL) {
if (strcmp(xml_name(x), "CLICON_SNMP_MIB") != 0)
continue;
if ((modname = xml_body(x)) == NULL)
continue;
clicon_debug(1, "%s %s: \"%s\"", __FUNCTION__, xml_name(x), modname);
/* Note, here we assume the Yang is loaded by some other mechanism and
* error if it not found.
* Alternatively, that YANG could be loaded.
* Problem is, if clixon_snmp has not loaded it, has backend done it?
* What happens if backend has not loaded it?
*/
if ((ymod = yang_find(yspec, Y_MODULE, modname)) == NULL){
clicon_err(OE_YANG, 0, "Mib-translated-yang %s not loaded", modname);
goto done;
}
/* Recursively traverse the mib-yang to find extensions */
if (yang_apply(ymod, -1, mib_yang_extension, 1, h) < 0)
goto done;
}
retval = 0;
done:
return retval;
}

53
apps/snmp/snmp_mib_yang.h Normal file
View file

@ -0,0 +1,53 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2022 Olof Hagsand and Kristofer Hallin
This file is part of CLIXON.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Alternatively, the contents of this file may be used under the terms of
the GNU General Public License Version 3 or later (the "GPL"),
in which case the provisions of the GPL are applicable instead
of those above. If you wish to allow use of your version of this file only
under the terms of the GPL, and not to allow others to
use your version of this file under the terms of Apache License version 2,
indicate your decision by deleting the provisions above and replace them with
the notice and other provisions required by the GPL. If you do not delete
the provisions above, a recipient may use your version of this file under
the terms of any one of the Apache License version 2 or the GPL.
***** END LICENSE BLOCK *****
*/
#ifdef __cplusplus
extern "C" {
#endif
#ifndef _SNMP_MIB_YANG_H_
#define _SNMP_MIB_YANG_H_
/*
* Prototypes
*/
int clixon_snmp_mib_yangs(clicon_handle h);
#endif /* _SNMP_MIB_YANG_H_ */
#ifdef __cplusplus
} /* extern "C" */
#endif

View file

@ -133,7 +133,7 @@ static const map_str2int yang_regexp_map[] = {
* @param[in] dbglevel Debug level * @param[in] dbglevel Debug level
* @retval 0 OK * @retval 0 OK
* @retval -1 Error * @retval -1 Error
* @note CLICON_FEATURE and CLICON_YANG_DIR are treated specially since they are lists * @note CLICON_FEATURE, CLICON_YANG_DIR and CLICON_SNMP_MIB are treated specially since they are lists
*/ */
int int
clicon_option_dump(clicon_handle h, clicon_option_dump(clicon_handle h,
@ -161,7 +161,7 @@ clicon_option_dump(clicon_handle h,
else else
clicon_debug(dbglevel, "%s = NULL", keys[i]); clicon_debug(dbglevel, "%s = NULL", keys[i]);
} }
/* Next print CLICON_FEATURE and CLICON_YANG_DIR from config tree /* Next print CLICON_FEATURE, CLICON_YANG_DIR and CLICON_SNMP_DIR from config tree
* Since they are lists they are placed in the config tree. * Since they are lists they are placed in the config tree.
*/ */
x = NULL; x = NULL;
@ -176,6 +176,12 @@ clicon_option_dump(clicon_handle h,
continue; continue;
clicon_debug(dbglevel, "%s =\t \"%s\"", xml_name(x), xml_body(x)); clicon_debug(dbglevel, "%s =\t \"%s\"", xml_name(x), xml_body(x));
} }
x = NULL;
while ((x = xml_child_each(clicon_conf_xml(h), x, CX_ELMNT)) != NULL) {
if (strcmp(xml_name(x), "CLICON_SNMP_MIB") != 0)
continue;
clicon_debug(dbglevel, "%s =\t \"%s\"", xml_name(x), xml_body(x));
}
retval = 0; retval = 0;
done: done:
if (keys) if (keys)
@ -333,7 +339,8 @@ parse_configfile(clicon_handle h,
continue; continue;
/* List options for configure options that are lists or leaf-lists: append to main */ /* List options for configure options that are lists or leaf-lists: append to main */
if (strcmp(name,"CLICON_FEATURE")==0 || if (strcmp(name,"CLICON_FEATURE")==0 ||
strcmp(name,"CLICON_YANG_DIR")==0){ strcmp(name,"CLICON_YANG_DIR")==0 ||
strcmp(name,"CLICON_SNMP_MIB")==0){
if (xml_addsub(xt, xec) < 0) if (xml_addsub(xt, xec) < 0)
goto done; goto done;
continue; continue;
@ -381,6 +388,8 @@ parse_configfile(clicon_handle h,
continue; continue;
if (strcmp(name,"CLICON_YANG_DIR")==0) if (strcmp(name,"CLICON_YANG_DIR")==0)
continue; continue;
if (strcmp(name,"CLICON_SNMP_MIB")==0)
continue;
if (clicon_hash_add(copt, if (clicon_hash_add(copt,
name, name,
body, body,
@ -424,7 +433,8 @@ clicon_option_add(clicon_handle h,
cxobj *x; cxobj *x;
if (strcmp(name, "CLICON_FEATURE")==0 || if (strcmp(name, "CLICON_FEATURE")==0 ||
strcmp(name, "CLICON_YANG_DIR")==0){ strcmp(name, "CLICON_YANG_DIR")==0 ||
strcmp(name, "CLICON_SNMP_MIB")==0){
if ((x = clicon_conf_xml(h)) == NULL){ if ((x = clicon_conf_xml(h)) == NULL){
clicon_err(OE_UNIX, ENOENT, "option %s not found (clicon_conf_xml_set has not been called?)", name); clicon_err(OE_UNIX, ENOENT, "option %s not found (clicon_conf_xml_set has not been called?)", name);
goto done; goto done;

View file

@ -1364,7 +1364,7 @@ yang_find_prefix_by_namespace(yang_stmt *ys,
goto done; goto done;
} }
/*! Given a yang statement and local prefi valid in module , find namespace /*! Given a yang statement and local prefix valid in module, find namespace
* *
* @param[in] ys Yang statement in module tree (or module itself) * @param[in] ys Yang statement in module tree (or module itself)
* @param[in] prefix Local prefix to access module with (direct pointer) * @param[in] prefix Local prefix to access module with (direct pointer)
@ -3661,7 +3661,7 @@ yang_anydata_add(yang_stmt *yp,
/*! Find extension argument and return if extension exists and its argument value /*! Find extension argument and return if extension exists and its argument value
* *
* @param[in] ys Yang statement where unknown statement may occur referncing to extension * @param[in] ys Yang statement where unknown statement may occur referencing to extension
* @param[in] name Name of the extension * @param[in] name Name of the extension
* @param[in] ns The namespace of the module where the extension is defined * @param[in] ns The namespace of the module where the extension is defined
* @param[out] exist The extension exists. * @param[out] exist The extension exists.
@ -3678,7 +3678,7 @@ yang_anydata_add(yang_stmt *yp,
* // use extension value * // use extension value
* } * }
* @endcode * @endcode
* @see ys_populate_unknown Called when parsing YANGo * @see ys_populate_unknown Called when parsing YANG
*/ */
int int
yang_extension_value(yang_stmt *ys, yang_extension_value(yang_stmt *ys,

View file

@ -440,7 +440,7 @@ function chunked_framing()
function start_snmp(){ function start_snmp(){
cfg=$1 cfg=$1
$clixon_snmp -f $cfg -D $DBG -l s & $clixon_snmp -f $cfg -D $DBG &
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
err err
@ -456,7 +456,7 @@ function stop_snmp(){
if [ $valgrindtest != 0 ]; then if [ $valgrindtest != 0 ]; then
kill `ps aux | grep [v]algrind | awk '{print $2}' | tail -n1` kill `ps aux | grep [v]algrind | awk '{print $2}' | tail -n1`
else else
killall clixon_snmp killall -q clixon_snmp
fi fi
} }

View file

@ -31,6 +31,7 @@ cat <<EOF > $cfg
<CLICON_BACKEND_PIDFILE>/var/tmp/$APPNAME.pidfile</CLICON_BACKEND_PIDFILE> <CLICON_BACKEND_PIDFILE>/var/tmp/$APPNAME.pidfile</CLICON_BACKEND_PIDFILE>
<CLICON_XMLDB_DIR>$dir</CLICON_XMLDB_DIR> <CLICON_XMLDB_DIR>$dir</CLICON_XMLDB_DIR>
<CLICON_SNMP_AGENT_SOCK>unix:$SOCK</CLICON_SNMP_AGENT_SOCK> <CLICON_SNMP_AGENT_SOCK>unix:$SOCK</CLICON_SNMP_AGENT_SOCK>
<CLICON_SNMP_MIB>NET-SNMP-EXAMPLES-MIB</CLICON_SNMP_MIB>
</clixon-config> </clixon-config>
EOF EOF
@ -61,7 +62,7 @@ function testinit(){
# Kill old clixon_snmp, if any # Kill old clixon_snmp, if any
new "Terminating any old clixon_snmp processes" new "Terminating any old clixon_snmp processes"
sudo killall clixon_snmp sudo killall -q clixon_snmp
new "Starting clixon_snmp" new "Starting clixon_snmp"
start_snmp $cfg & start_snmp $cfg &
@ -71,7 +72,7 @@ function testinit(){
} }
function testexit(){ function testexit(){
sudo killall clixon_snmp stop_snmp
} }
new "SNMP tests" new "SNMP tests"

View file

@ -41,6 +41,7 @@ cat <<EOF > $cfg
<CLICON_BACKEND_PIDFILE>/var/tmp/$APPNAME.pidfile</CLICON_BACKEND_PIDFILE> <CLICON_BACKEND_PIDFILE>/var/tmp/$APPNAME.pidfile</CLICON_BACKEND_PIDFILE>
<CLICON_XMLDB_DIR>$dir</CLICON_XMLDB_DIR> <CLICON_XMLDB_DIR>$dir</CLICON_XMLDB_DIR>
<CLICON_SNMP_AGENT_SOCK>unix:$SOCK</CLICON_SNMP_AGENT_SOCK> <CLICON_SNMP_AGENT_SOCK>unix:$SOCK</CLICON_SNMP_AGENT_SOCK>
<CLICON_SNMP_MIB>NET-SNMP-EXAMPLES-MIB</CLICON_SNMP_MIB>
</clixon-config> </clixon-config>
EOF EOF
@ -71,7 +72,7 @@ function testinit(){
# Kill old clixon_snmp, if any # Kill old clixon_snmp, if any
new "Terminating any old clixon_snmp processes" new "Terminating any old clixon_snmp processes"
sudo killall clixon_snmp sudo killall -q clixon_snmp
new "Starting clixon_snmp" new "Starting clixon_snmp"
start_snmp $cfg & start_snmp $cfg &
@ -81,7 +82,7 @@ function testinit(){
} }
function testexit(){ function testexit(){
sudo killall clixon_snmp stop_snmp
} }
new "SNMP table tests" new "SNMP table tests"

View file

@ -1132,6 +1132,16 @@ module clixon-config {
0 means no limit"; 0 means no limit";
} }
leaf-list CLICON_SNMP_MIB {
description
"Names of MIBs that are used by clixon_snmp.
For each MIB M, a YANG file M.yang is expected to be found.
If not found, an error is genereated.
The YANG file M.yang is typically generated from the source MIB but can also
be handcrafted. An example of such a script is scripts/mib_to_yang.sh.
A list of these options should be in the configuration.";
type string;
}
leaf CLICON_SNMP_AGENT_SOCK { leaf CLICON_SNMP_AGENT_SOCK {
type string; type string;
default "unix:/tmp/clixon_snmp.sock"; default "unix:/tmp/clixon_snmp.sock";