From 62a32faf9c853b7101d6834b63f77eebc99a147b Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Sat, 14 May 2022 17:13:10 +0200 Subject: [PATCH] SNMP frontend. New snmp_lib.[ch] and rearranged code into lib Test: renamed WITH_SNMP -> ENABLE_SNMP --- apps/snmp/Makefile.in | 1 + apps/snmp/snmp_lib.c | 276 ++++++++++++++++++++++++++++++++++++++ apps/snmp/snmp_lib.h | 58 ++++++++ apps/snmp/snmp_mib_yang.c | 218 ++---------------------------- test/config.sh.in | 2 +- test/lib.sh | 2 +- test/test_snmp.sh | 2 +- test/test_snmp_set.sh | 2 +- test/test_snmp_table.sh | 2 +- 9 files changed, 349 insertions(+), 214 deletions(-) create mode 100644 apps/snmp/snmp_lib.c create mode 100644 apps/snmp/snmp_lib.h diff --git a/apps/snmp/Makefile.in b/apps/snmp/Makefile.in index a5a0cdab..4b4cea81 100644 --- a/apps/snmp/Makefile.in +++ b/apps/snmp/Makefile.in @@ -85,6 +85,7 @@ APPL = clixon_snmp APPSRC = APPSRC += snmp_main.c APPSRC += snmp_mib_yang.c +APPSRC += snmp_lib.c APPOBJ = $(APPSRC:.c=.o) diff --git a/apps/snmp/snmp_lib.c b/apps/snmp/snmp_lib.c new file mode 100644 index 00000000..3f862e3a --- /dev/null +++ b/apps/snmp/snmp_lib.c @@ -0,0 +1,276 @@ +/* + * + ***** 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 ***** + * See RFC 6643 + * 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 +#include +#include +#include +#include +#include +#include + +/* net-snmp */ +#include +#include +#include + +/* cligen */ +#include + +/* clicon */ +#include + +#include "snmp_lib.h" + +/* + * 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 snmp_access_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} +}; + +/* Map between clixon and ASN.1 types. + * @see net-snmp/library/asn1.h + * @see union netsnmp_vardata in net-snmp/types.h + */ +static const map_str2int snmp_type_map[] = { + {"bool", ASN_BOOLEAN}, + {"int32", ASN_INTEGER}, + {"bits", ASN_BIT_STR}, + {"string", ASN_OCTET_STR}, + {"empty", ASN_NULL}, + //XXX {"", ASN_OBJECT_ID}, + // XXX {"", ASN_SEQUENCE}, + // XXX {"", ASN_SET}, + {NULL, -1} +}; + + +/*! Translate from snmp string to int representation + * @note Internal snmpd, maybe find something in netsnmpd? + */ +int +snmp_modes_str2int(char *modes_str) +{ + return clicon_str2int(snmp_access_map, modes_str); +} + +/*! + */ +int +yang2snmp_types(yang_stmt *ys, + int *asn1_type, + enum cv_type *cvtype) +{ + int retval = -1; + yang_stmt *yrestype; /* resolved type */ + char *restype; /* resolved type */ + char *origtype=NULL; /* original type */ + + /* Get yang type of leaf and trasnslate to ASN.1 */ + if (yang_type_get(ys, &origtype, &yrestype, NULL, NULL, NULL, NULL, NULL) < 0) + goto done; + restype = yrestype?yang_argument_get(yrestype):NULL; + if (clicon_type2cv(origtype, restype, ys, cvtype) < 0) + goto done; + /* translate to asn.1 */ + *asn1_type = clicon_str2int(snmp_type_map, restype); + clicon_debug(1, "%s type:%s", __FUNCTION__, restype); + retval = 0; + done: + return retval; +} + + +/*! Translate from yang/xml/clixon to SNMP/ASN.1 +** +* The tran + * @param[in] valstr Clixon/yang/xml string value + * @param[in] cvtype Type of clixon type + * @param[out] snmpval Malloc:ed snmp type + * @param[out] snmplen Length of snmp type + * @retval 1 OK + * @retval 0 Invalid value + * @retval -1 Error + */ +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 retval = -1; + int ret; + char *reason = NULL; + size_t cvlen; + cg_var *cv; + + clicon_debug(1, "%s", __FUNCTION__); + if (snmpval == NULL || snmplen == NULL){ + clicon_err(OE_UNIX, EINVAL, "snmpval or snmplen is NULL"); + goto done; + } + if ((cv = cv_new(cvtype)) == NULL){ + clicon_err(OE_UNIX, errno, "cv_new"); + goto done; + } + if ((ret = cv_parse1(valstr, cv, &reason)) < 0) + goto done; + if (ret == 0){ + clicon_debug(1, "%s %s", __FUNCTION__, reason); + netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_WRONGTYPE); + goto fail; + } + cvlen = cv_len(cv); + if ((*snmpval = malloc(cvlen)) == NULL){ + clicon_err(OE_UNIX, errno, "malloc"); + goto done; + } + switch (cvtype){ + case CGV_INT32:{ + int i = cv_int32_get(cv); + memcpy(*snmpval, &i, cvlen); + *snmplen = cvlen; + break; + } + case CGV_STRING:{ + strcpy((char*)*snmpval, cv_string_get(cv)); + *snmplen = cvlen; + break; + } + default: + clicon_debug(1, "%s %s not supported", __FUNCTION__, cv_type2str(cvtype)); + netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_WRONGTYPE); + goto fail; + break; + } + retval = 1; + done: + clicon_debug(1, "%s %d", __FUNCTION__, retval); + if (reason) + free(reason); + return retval; + fail: + retval = 0; + goto done; +} + +/*! Construct an xpath from yang statement + * Recursively construct it to the top. + * @param[in] ys Yang statement + * @param[out] cb xpath as cbuf + * @retval 0 OK + * @retval -1 Error + * @note + * 1. This should really be in a core .c file, like clixon_yang, BUT + * 2. It is far from complete so maybe keep it here as a special case + */ +int +yang2xpath(yang_stmt *ys, + cbuf *cb) +{ + yang_stmt *yp; /* parent */ + int i; + cvec *cvk = NULL; /* vector of index keys */ + int retval = -1; + + if ((yp = yang_parent_get(ys)) == NULL){ + clicon_err(OE_YANG, EINVAL, "yang expected parent %s", yang_argument_get(ys)); + goto done; + } + if (yp != NULL && /* XXX rm */ + yang_keyword_get(yp) != Y_MODULE && + yang_keyword_get(yp) != Y_SUBMODULE){ + + if (yang2xpath(yp, cb) < 0) /* recursive call */ + goto done; + if (yang_keyword_get(yp) != Y_CHOICE && yang_keyword_get(yp) != Y_CASE){ + cprintf(cb, "/"); + } + } + cprintf(cb, "/%s:", yang_find_myprefix(ys)); + if (yang_keyword_get(ys) != Y_CHOICE && yang_keyword_get(ys) != Y_CASE) + cprintf(cb, "%s", yang_argument_get(ys)); + switch (yang_keyword_get(ys)){ + case Y_LIST: // XXX not xpaths + cvk = yang_cvec_get(ys); /* Use Y_LIST cache, see ys_populate_list() */ + if (cvec_len(cvk)) + cprintf(cb, "="); + /* Iterate over individual keys */ + for (i=0; i +#include "snmp_lib.h" #include "snmp_mib_yang.h" #define IETF_YANG_SMIV2_NS "urn:ietf:params:xml:ns:yang:ietf-yang-smiv2" @@ -84,67 +85,6 @@ struct clixon_snmp_handle { }; typedef struct clixon_snmp_handle clixon_snmp_handle; -/* - * 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 snmp_access_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} -}; - -/* Map between clixon and ASN.1 types. - * @see net-snmp/library/asn1.h - * @see union netsnmp_vardata in net-snmp/types.h - */ -static const map_str2int snmp_type_map[] = { - {"bool", ASN_BOOLEAN}, - {"int32", ASN_INTEGER}, - {"bits", ASN_BIT_STR}, - {"string", ASN_OCTET_STR}, - {"empty", ASN_NULL}, - //XXX {"", ASN_OBJECT_ID}, - // XXX {"", ASN_SEQUENCE}, - // XXX {"", ASN_SET}, - {NULL, -1} -}; - - -static int -yang2snmp_types(yang_stmt *ys, - int *asn1_type, - enum cv_type *cvtype) -{ - int retval = -1; - yang_stmt *yrestype; /* resolved type */ - char *restype; /* resolved type */ - char *origtype=NULL; /* original type */ - - /* Get yang type of leaf and trasnslate to ASN.1 */ - if (yang_type_get(ys, &origtype, &yrestype, NULL, NULL, NULL, NULL, NULL) < 0) - goto done; - restype = yrestype?yang_argument_get(yrestype):NULL; - if (clicon_type2cv(origtype, restype, ys, cvtype) < 0) - goto done; - /* translate to asn.1 */ - *asn1_type = clicon_str2int(snmp_type_map, restype); - clicon_debug(1, "%s type:%s", __FUNCTION__, restype); - retval = 0; - done: - return retval; -} - -#if 1 /* table example */ - /* * https://net-snmp.sourceforge.io/dev/agent/data_set_8c-example.html#_a0 */ @@ -195,139 +135,6 @@ init_testtable(void) netsnmp_register_table_data_set(handler, table, NULL); netsnmp_register_auto_data_table(table, NULL); } -#endif /* table example */ - -/*! Construct an xpath from yang statement - * Recursively construct it to the top. - * @param[in] ys Yang statement - * @param[out] cb xpath as cbuf - * @retval 0 OK - * @retval -1 Error - * XXX Not lists - */ -static int -yang2xpath(yang_stmt *ys, - cbuf *cb) -{ - yang_stmt *yp; /* parent */ - int i; - cvec *cvk = NULL; /* vector of index keys */ - int retval = -1; - - if ((yp = yang_parent_get(ys)) == NULL){ - clicon_err(OE_YANG, EINVAL, "yang expected parent %s", yang_argument_get(ys)); - goto done; - } - if (yp != NULL && /* XXX rm */ - yang_keyword_get(yp) != Y_MODULE && - yang_keyword_get(yp) != Y_SUBMODULE){ - - if (yang2xpath(yp, cb) < 0) /* recursive call */ - goto done; - if (yang_keyword_get(yp) != Y_CHOICE && yang_keyword_get(yp) != Y_CASE){ - cprintf(cb, "/"); - } - } - cprintf(cb, "/%s:", yang_find_myprefix(ys)); - if (yang_keyword_get(ys) != Y_CHOICE && yang_keyword_get(ys) != Y_CASE) - cprintf(cb, "%s", yang_argument_get(ys)); - switch (yang_keyword_get(ys)){ - case Y_LIST: // XXX not xpaths - cvk = yang_cvec_get(ys); /* Use Y_LIST cache, see ys_populate_list() */ - if (cvec_len(cvk)) - cprintf(cb, "="); - /* Iterate over individual keys */ - for (i=0; i /dev/null if [ $? != 0 ]; then echo -e "\e[31m\nenable-netsnmp set but snmpd not running, start with:" diff --git a/test/test_snmp.sh b/test/test_snmp.sh index d513d2a2..6d1d16fd 100755 --- a/test/test_snmp.sh +++ b/test/test_snmp.sh @@ -6,7 +6,7 @@ s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi APPNAME=example -if [ ${WITH_NETSNMP} != "yes" ]; then +if [ ${ENABLE_NETSNMP} != "yes" ]; then echo "Skipping test, Net-SNMP support not enabled." if [ "$s" = $0 ]; then exit 0; else return 0; fi fi diff --git a/test/test_snmp_set.sh b/test/test_snmp_set.sh index c56231bd..e558bd64 100755 --- a/test/test_snmp_set.sh +++ b/test/test_snmp_set.sh @@ -8,7 +8,7 @@ s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi APPNAME=snmp # XXX skip for now -if [ ${WITH_NETSNMP} != "yes" ]; then +if [ ${ENABLE_NETSNMP} != "yes" ]; then echo "Skipping test, Net-SNMP support not enabled." if [ "$s" = $0 ]; then exit 0; else return 0; fi fi diff --git a/test/test_snmp_table.sh b/test/test_snmp_table.sh index cc3e304d..ada176bd 100755 --- a/test/test_snmp_table.sh +++ b/test/test_snmp_table.sh @@ -7,7 +7,7 @@ s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi APPNAME=snmp -if [ ${WITH_NETSNMP} != "yes" ]; then +if [ ${ENABLE_NETSNMP} != "yes" ]; then echo "Skipping test, Net-SNMP support not enabled." if [ "$s" = $0 ]; then exit 0; else return 0; fi fi