diff --git a/apps/snmp/Makefile.in b/apps/snmp/Makefile.in index 4b4cea81..b39fe187 100644 --- a/apps/snmp/Makefile.in +++ b/apps/snmp/Makefile.in @@ -84,7 +84,8 @@ APPL = clixon_snmp # Common source - not accessible from plugin - independent of restconf package (fcgi|native) APPSRC = APPSRC += snmp_main.c -APPSRC += snmp_mib_yang.c +APPSRC += snmp_register.c +APPSRC += snmp_handler.c APPSRC += snmp_lib.c APPOBJ = $(APPSRC:.c=.o) diff --git a/apps/snmp/snmp_handler.c b/apps/snmp/snmp_handler.c new file mode 100644 index 00000000..2d77213c --- /dev/null +++ b/apps/snmp/snmp_handler.c @@ -0,0 +1,310 @@ +/* + * + ***** 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 HAVE_CONFIG_H +#include "clixon_config.h" /* generated by config & autoconf */ +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +/* net-snmp */ +#include +#include +#include + +/* cligen */ +#include + +/* clicon */ +#include + +#include "snmp_lib.h" +#include "snmp_handler.h" + +/*! SNMP table operation handlre + + * Callorder: 161,160,.... 0, 1,2,3, 160,161,... + * see https://net-snmp.sourceforge.io/dev/agent/data_set_8c-example.html#_a0 + */ +int +snmp_table_handler(netsnmp_mib_handler *handler, + netsnmp_handler_registration *nhreg, + netsnmp_agent_request_info *reqinfo, + netsnmp_request_info *requests) +{ + int retval = SNMP_ERR_GENERR; + clixon_snmp_handle *sh; + netsnmp_table_data_set *table; + yang_stmt *ys; + clicon_handle h; + yang_stmt *ylist; + cvec *nsc = NULL; + cxobj *xt = NULL; + cbuf *cb = NULL; + + clicon_debug(1, "%s %s %s", __FUNCTION__, + handler->handler_name, + snmp_msg_int2str(reqinfo->mode)); + sh = (clixon_snmp_handle*)nhreg->my_reg_void; + ys = sh->sh_ys; + h = sh->sh_h; + table = sh->sh_table; + + if ((ylist = yang_find(ys, Y_LIST, NULL)) == NULL) + goto ok; + + if (clixon_table_create(table, ys, h) < 0) + goto done; + + switch(reqinfo->mode){ + case MODE_GETNEXT: // 160 + break; + case MODE_GET: // 160 + case MODE_SET_RESERVE1: + case MODE_SET_RESERVE2: + case MODE_SET_ACTION: + case MODE_SET_COMMIT: + break; + + } +ok: + retval = SNMP_ERR_NOERROR; + +done: + if (xt) + xml_free(xt); + if (cb) + cbuf_free(cb); + if (nsc) + xml_nsctx_free(nsc); + return retval; +} + + +/*! SNMP Scalar operation handler + * Calls order: READ:160, + * WRITE: 0, 1, 2, 3, + * MODE_SET_RESERVE1, MODE_SET_RESERVE2, MODE_SET_ACTION, MODE_SET_COMMIT + * + */ +int +snmp_scalar_handler(netsnmp_mib_handler *handler, + netsnmp_handler_registration *nhreg, + netsnmp_agent_request_info *reqinfo, + netsnmp_request_info *requests) +{ + int retval = -1; + clixon_snmp_handle *sh; + yang_stmt *ys; + clicon_handle h; + cg_var *cv = NULL; + cxobj *xt = NULL; + cxobj *xerr; + cvec *nsc = NULL; + cxobj *x; + char *xpath = NULL; + int asn1_type; + enum cv_type cvtype; + char *valstr; + u_char *snmpval = NULL; + size_t snmplen; + int ret; + netsnmp_variable_list *requestvb; /* sub of requests */ + cbuf *cb = NULL; + + /* + * can be used to pass information on a per-pdu basis from a + * helper to the later handlers + netsnmp_agent_request_info *reqinfo, + netsnmp_data_list *agent_data; + netsnmp_free_agent_data_set() + */ + requestvb = requests->requestvb; + if (0) + fprintf(stderr, "%s %s %s\n", __FUNCTION__, + handler->handler_name, + snmp_msg_int2str(reqinfo->mode) + ); + + if (0) + fprintf(stderr, "inclusive:%d\n", + requests->inclusive + ); + clicon_debug(1, "%s %s %s %d", __FUNCTION__, + handler->handler_name, + snmp_msg_int2str(reqinfo->mode), + requests->inclusive); + sh = (clixon_snmp_handle*)nhreg->my_reg_void; + ys = sh->sh_ys; + h = sh->sh_h; + // fprint_objid(stderr, nhreg->rootoid, nhreg->rootoid_len); + assert(sh->sh_oidlen == requestvb->name_length); + assert(requestvb->name_length == nhreg->rootoid_len); + assert(snmp_oid_compare(sh->sh_oid, sh->sh_oidlen, + requestvb->name, requestvb->name_length) == 0); + assert(snmp_oid_compare(requestvb->name, requestvb->name_length, + nhreg->rootoid, nhreg->rootoid_len) == 0); + +#if 0 /* If oid match fails */ + netsnmp_set_request_error(reqinfo, requests, + SNMP_NOSUCHOBJECT); + return SNMP_ERR_NOERROR; +#endif + + if (yang2snmp_types(ys, &asn1_type, &cvtype) < 0) + goto done; + + /* see net-snmp/agent/snmp_agent.h / net-snmp/library/snmp.h */ + switch (reqinfo->mode) { + case MODE_GET: // 160 + requestvb->type = asn1_type; // ASN_NULL on input + + /* get xpath: see yang2api_path_fmt / api_path2xpath + New fn: yang2xpath? + clicon_rpc_get() + " + + */ + + if (xml_nsctx_yang(ys, &nsc) < 0) + goto done; + if (yang2xpath(ys, &xpath) < 0) + goto done; + if (clicon_rpc_get(h, xpath, nsc, CONTENT_ALL, -1, &xt) < 0) + goto done; + if ((xerr = xpath_first(xt, NULL, "/rpc-error")) != NULL){ + clixon_netconf_error(xerr, "clicon_rpc_get", NULL); + goto done; + } + /* Get value, either from xml, or smiv2 default */ + if ((x = xpath_first(xt, nsc, "%s", xpath)) != NULL) { + valstr = xml_body(x); + } + else if ((valstr = sh->sh_default) != NULL) + ; + else{ + netsnmp_set_request_error(reqinfo, requests, SNMP_NOSUCHINSTANCE); + goto ok; + } + if ((ret = type_yang2snmp(valstr, cvtype, reqinfo, requests, &snmpval, &snmplen)) < 0) + goto done; + if (ret == 0) + goto ok; + + /* 1. use cligen object and get rwa buf / size from that, OR + * + have parse function from YANG + * - does not have + * 2. use union netsnmp_vardata and pass that here? + * 3. Make cv2asn1 conversion function <-- + */ + + /* see snmplib/snmp_client.c */ + if (snmp_set_var_value(requestvb, + snmpval, + snmplen) != 0){ + clicon_err(OE_SNMP, 0, "snmp_set_var_value"); + goto done; + } + break; + case MODE_GETNEXT: // 161 + assert(0); // Not seen? + break; + case MODE_SET_RESERVE1: // 0 + if (requestvb->type != asn1_type) + netsnmp_set_request_error(reqinfo, requests, + SNMP_ERR_WRONGTYPE); + break; + + case MODE_SET_RESERVE2: // 1 + break; + + case MODE_SET_ACTION: // 2 + /* + * update current + */ + /* yang2xpath -> xpath2xml + * accesses = *(requestvb->val.integer); + * rpc edit-config + + */ + + if ((cb = cbuf_new()) == NULL){ + clicon_err(OE_UNIX, errno, "cbuf_new"); + goto done; + } + /*! XXX only int */ + cprintf(cb, "%ld", *requestvb->val.integer); + if (clicon_rpc_edit_config(h, "candidate", OP_MERGE, cbuf_get(cb)) < 0) + goto done; + break; + + case MODE_SET_UNDO: // 5 + if (clicon_rpc_discard_changes(h) < 0) + goto done; + break; + + case MODE_SET_COMMIT: // 3 + if (clicon_rpc_commit(h) < 0) + goto done; + break; + case MODE_SET_FREE: // 4 + /* + * nothing to do + */ + break; + } + ok: + retval = SNMP_ERR_NOERROR; + done: + if (snmpval) + free(snmpval); + if (cb) + cbuf_free(cb); + if (xpath) + free(xpath); + if (xt) + xml_free(xt); + if (nsc) + xml_nsctx_free(nsc); + if (cv) + cv_free(cv); + return retval; +} diff --git a/apps/snmp/snmp_handler.h b/apps/snmp/snmp_handler.h new file mode 100644 index 00000000..e5df6ad3 --- /dev/null +++ b/apps/snmp/snmp_handler.h @@ -0,0 +1,60 @@ +/* + * + ***** 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_HANDLER_H_ +#define _SNMP_HANDLER_H_ + +/* + * Prototypes + */ +int snmp_table_handler(netsnmp_mib_handler *handler, + netsnmp_handler_registration *nhreg, + netsnmp_agent_request_info *reqinfo, + netsnmp_request_info *requests); +int snmp_scalar_handler(netsnmp_mib_handler *handler, + netsnmp_handler_registration *nhreg, + netsnmp_agent_request_info *reqinfo, + netsnmp_request_info *requests); + +#endif /* _SNMP_HANDLER_H_ */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + diff --git a/apps/snmp/snmp_lib.c b/apps/snmp/snmp_lib.c index c88c8263..6c21db6f 100644 --- a/apps/snmp/snmp_lib.c +++ b/apps/snmp/snmp_lib.c @@ -323,3 +323,70 @@ yang2xpath(yang_stmt *ys, cbuf_free(cb); return retval; } + +int +clixon_table_create(netsnmp_table_data_set *table, yang_stmt *ys, clicon_handle h) +{ + cvec *nsc = NULL; + cxobj *xt = NULL; + cxobj *xerr; + char *xpath; + cxobj *xtable; + cxobj *xe; + cxobj *xleaf; + int i; + char *valstr; + netsnmp_table_row *row, *tmprow; + int retval = -1; + + if (xml_nsctx_yang(ys, &nsc) < 0) + goto done; + + if (yang2xpath(ys, &xpath) < 0) + goto done; + + if (clicon_rpc_get(h, xpath, nsc, CONTENT_ALL, -1, &xt) < 0) + goto done; + + if ((xerr = xpath_first(xt, NULL, "/rpc-error")) != NULL){ + clixon_netconf_error(xerr, "clicon_rpc_get", NULL); + goto done; + } + + netsnmp_table_dataset_add_index(table, ASN_OCTET_STR); + netsnmp_table_set_multi_add_default_row(table, 2, ASN_OCTET_STR, 1, NULL, 0, 3, ASN_OCTET_STR, 1, NULL, 0, 0); + + if ((xtable = xpath_first(xt, nsc, "%s", xpath)) != NULL) { + for (tmprow = table->table->first_row; tmprow; tmprow = tmprow->next) + netsnmp_table_dataset_remove_and_delete_row(table, tmprow); + + xe = NULL; /* Loop thru entries in table */ + while ((xe = xml_child_each(xtable, xe, CX_ELMNT)) != NULL) { + row = netsnmp_create_table_data_row(); + xleaf = NULL; /* Loop thru leafs in entry */ + i = 1; /* tableindex start at 1 */ + while ((xleaf = xml_child_each(xe, xleaf, CX_ELMNT)) != NULL) { + valstr = xml_body(xleaf); + if (i == 1) // Assume first netry is key XXX should check YANG + netsnmp_table_row_add_index(row, ASN_OCTET_STR, valstr, strlen(valstr)); + else{ + netsnmp_set_row_column(row, i, ASN_OCTET_STR, valstr, strlen(valstr)); + netsnmp_mark_row_column_writable(row, i, 1); + } + i++; + } + + netsnmp_table_dataset_add_row(table, row); + } + } + + retval = 1; + +done: + if (xt) + xml_free(xt); + if (nsc) + xml_nsctx_free(nsc); + + return retval; +} diff --git a/apps/snmp/snmp_lib.h b/apps/snmp/snmp_lib.h index e5ed35f2..ed572180 100644 --- a/apps/snmp/snmp_lib.h +++ b/apps/snmp/snmp_lib.h @@ -40,6 +40,21 @@ extern "C" { #ifndef _SNMP_LIB_H_ #define _SNMP_LIB_H_ +/* + * Types + */ +/* Userdata to pass around in netsmp callbacks + */ +struct clixon_snmp_handle { + clicon_handle sh_h; + yang_stmt *sh_ys; + oid sh_oid[MAX_OID_LEN]; /* OID for debug, may be removed? */ + size_t sh_oidlen; + char *sh_default; /* MIB default value leaf only */ + netsnmp_table_data_set *sh_table; /* table struct, table only */ +}; +typedef struct clixon_snmp_handle clixon_snmp_handle; + /* * Prototypes */ @@ -50,6 +65,7 @@ int type_yang2snmp(char *valstr, enum cv_type cvtype, netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests, u_char **snmpval, size_t *snmplen); int yang2xpath(yang_stmt *ys, char **xpath); +int clixon_table_create(netsnmp_table_data_set *table, yang_stmt *ys, clicon_handle h); #endif /* _SNMP_LIB_H_ */ diff --git a/apps/snmp/snmp_main.c b/apps/snmp/snmp_main.c index 8aed4c92..66ba4cae 100644 --- a/apps/snmp/snmp_main.c +++ b/apps/snmp/snmp_main.c @@ -56,7 +56,7 @@ /* clicon */ #include -#include "snmp_mib_yang.h" +#include "snmp_register.h" /* Command line options to be passed to getopt(3) */ #define SNMP_OPTS "hD:f:l:o:z" diff --git a/apps/snmp/snmp_mib_yang.c b/apps/snmp/snmp_register.c similarity index 86% rename from apps/snmp/snmp_mib_yang.c rename to apps/snmp/snmp_register.c index 60c373f6..4082e2b7 100644 --- a/apps/snmp/snmp_mib_yang.c +++ b/apps/snmp/snmp_register.c @@ -76,91 +76,12 @@ #include #include "snmp_lib.h" -#include "snmp_mib_yang.h" +#include "snmp_register.h" +#include "snmp_handler.h" #define IETF_YANG_SMIV2_NS "urn:ietf:params:xml:ns:yang:ietf-yang-smiv2" -/* - * Local Types - */ -/* Userdata to pass around in netsmp callbacks - */ -struct clixon_snmp_handle { - clicon_handle sh_h; - yang_stmt *sh_ys; - oid sh_oid[MAX_OID_LEN]; /* OID for debug, may be removed? */ - size_t sh_oidlen; - char *sh_default; /* MIB default value leaf only */ - netsnmp_table_data_set *sh_table; /* table struct, table only */ -}; -typedef struct clixon_snmp_handle clixon_snmp_handle; - -int clixon_table_create(netsnmp_table_data_set *table, yang_stmt *ys, clicon_handle h) -{ - cvec *nsc = NULL; - cxobj *xt = NULL; - cxobj *xerr; - char *xpath; - cxobj *xtable; - cxobj *xe; - cxobj *xleaf; - int i; - char *valstr; - netsnmp_table_row *row, *tmprow; - int retval = -1; - - if (xml_nsctx_yang(ys, &nsc) < 0) - goto done; - - if (yang2xpath(ys, &xpath) < 0) - goto done; - - if (clicon_rpc_get(h, xpath, nsc, CONTENT_ALL, -1, &xt) < 0) - goto done; - - if ((xerr = xpath_first(xt, NULL, "/rpc-error")) != NULL){ - clixon_netconf_error(xerr, "clicon_rpc_get", NULL); - goto done; - } - - netsnmp_table_dataset_add_index(table, ASN_OCTET_STR); - netsnmp_table_set_multi_add_default_row(table, 2, ASN_OCTET_STR, 1, NULL, 0, 3, ASN_OCTET_STR, 1, NULL, 0, 0); - - if ((xtable = xpath_first(xt, nsc, "%s", xpath)) != NULL) { - for (tmprow = table->table->first_row; tmprow; tmprow = tmprow->next) - netsnmp_table_dataset_remove_and_delete_row(table, tmprow); - - xe = NULL; /* Loop thru entries in table */ - while ((xe = xml_child_each(xtable, xe, CX_ELMNT)) != NULL) { - row = netsnmp_create_table_data_row(); - xleaf = NULL; /* Loop thru leafs in entry */ - i = 1; /* tableindex start at 1 */ - while ((xleaf = xml_child_each(xe, xleaf, CX_ELMNT)) != NULL) { - valstr = xml_body(xleaf); - if (i == 1) // Assume first netry is key XXX should check YANG - netsnmp_table_row_add_index(row, ASN_OCTET_STR, valstr, strlen(valstr)); - else{ - netsnmp_set_row_column(row, i, ASN_OCTET_STR, valstr, strlen(valstr)); - netsnmp_mark_row_column_writable(row, i, 1); - } - i++; - } - - netsnmp_table_dataset_add_row(table, row); - } - } - - retval = 1; - -done: - if (xt) - xml_free(xt); - if (nsc) - xml_nsctx_free(nsc); - - return retval; -} - +#if 0 /*! SNMP table operation handlre * Callorder: 161,160,.... 0, 1,2,3, 160,161,... @@ -410,6 +331,7 @@ snmp_scalar_handler(netsnmp_mib_handler *handler, cv_free(cv); return retval; } +#endif /*! Parse smiv2 extensions for YANG container/list * diff --git a/apps/snmp/snmp_mib_yang.h b/apps/snmp/snmp_register.h similarity index 95% rename from apps/snmp/snmp_mib_yang.h rename to apps/snmp/snmp_register.h index 6befc7f0..17a3ac5d 100644 --- a/apps/snmp/snmp_mib_yang.h +++ b/apps/snmp/snmp_register.h @@ -37,15 +37,15 @@ extern "C" { #endif -#ifndef _SNMP_MIB_YANG_H_ -#define _SNMP_MIB_YANG_H_ +#ifndef _SNMP_REGISTER_H_ +#define _SNMP_REGISTER_H_ /* * Prototypes */ int clixon_snmp_mib_yangs(clicon_handle h); -#endif /* _SNMP_MIB_YANG_H_ */ +#endif /* _SNMP_REGISTER_H_ */ #ifdef __cplusplus } /* extern "C" */