From ff52cad3a869518759fb707913a991575efa28d4 Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Mon, 30 May 2022 11:23:52 +0200 Subject: [PATCH] SNMP frontend: Types and indexes Added timestamp and inet address First go at index names --- apps/snmp/snmp_handler.c | 14 +-- apps/snmp/snmp_lib.c | 208 ++++++++++++++------------------------ apps/snmp/snmp_lib.h | 5 +- apps/snmp/snmp_register.c | 54 +++++----- test/test_snmp_set.sh | 8 +- test/test_snmp_types.sh | 28 ++--- 6 files changed, 137 insertions(+), 180 deletions(-) diff --git a/apps/snmp/snmp_handler.c b/apps/snmp/snmp_handler.c index 5914efe7..62fc41d1 100644 --- a/apps/snmp/snmp_handler.c +++ b/apps/snmp/snmp_handler.c @@ -208,7 +208,7 @@ snmp_scalar_get(clicon_handle h, cxobj *xt = NULL; cxobj *xerr; cxobj *x; - char *snmpstr = NULL; + char *xmlstr = NULL; u_char *snmpval = NULL; size_t snmplen = 0; int ret; @@ -230,13 +230,13 @@ snmp_scalar_get(clicon_handle h, goto done; } /* - * The xml to snmp value conversion is done here. It is done in two steps: + * The xml to snmp value conversion is done in two steps: * 1. From XML to SNMP string, there is a special case for enumeration, and for default value * 2. From SNMP string to SNMP binary value which invloves parsing */ if ((x = xpath_first(xt, nsc, "%s", xpath)) != NULL){ assert(xml_spec(x) == ys); - if ((ret = type_xml2snmpstr(xml_body(x), ys, &snmpstr)) < 0) + if ((ret = type_xml2snmp_pre(xml_body(x), ys, &xmlstr)) < 0) goto done; if (ret == 0){ netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_WRONGVALUE); @@ -244,7 +244,7 @@ snmp_scalar_get(clicon_handle h, } } else if (defaultval != NULL){ - if ((snmpstr = strdup(defaultval)) == NULL){ + if ((xmlstr = strdup(defaultval)) == NULL){ clicon_err(OE_UNIX, errno, "strdup"); goto done; } @@ -255,7 +255,7 @@ snmp_scalar_get(clicon_handle h, } if (type_yang2asn1(ys, &asn1type, 1) < 0) goto done; - if ((ret = type_snmpstr2val(snmpstr, &asn1type, &snmpval, &snmplen, &reason)) < 0) + if ((ret = type_xml2snmp(xmlstr, &asn1type, &snmpval, &snmplen, &reason)) < 0) goto done; if (ret == 0){ clicon_debug(1, "%s %s", __FUNCTION__, reason); @@ -274,8 +274,8 @@ snmp_scalar_get(clicon_handle h, done: if (reason) free(reason); - if (snmpstr) - free(snmpstr); + if (xmlstr) + free(xmlstr); if (snmpval) free(snmpval); if (xt) diff --git a/apps/snmp/snmp_lib.c b/apps/snmp/snmp_lib.c index fbc9f83f..b4a00b6f 100644 --- a/apps/snmp/snmp_lib.c +++ b/apps/snmp/snmp_lib.c @@ -201,7 +201,13 @@ type_yang2asn1(yang_stmt *ys, at = ASN_OCTET_STR; } else if (strcmp(origtype, "timeticks")==0){ - at = ASN_TIMETICKS; /* Clixon extended string type */ + at = ASN_TIMETICKS; + } + else if (strcmp(origtype, "timestamp")==0){ + at = ASN_TIMETICKS; + } + else if (strcmp(origtype, "InetAddress")==0){ + at = ASN_IPADDRESS; } else if (extended && strcmp(origtype, "phys-address")==0){ at = CLIXON_ASN_PHYS_ADDR; /* Clixon extended string type */ @@ -335,17 +341,21 @@ type_snmp2xml(yang_stmt *ys, } /*! Given xml value and YANG,m return corresponding malloced snmp string - * There is a special case for enumeration which is integer in snmp, string in YANG - * @param[in] xmlstr - * @retval 1 OK - * @retval 0 Invalid type - * @retval -1 Error + * + * For special cases to prepare for proper xml2snmp translation. This includes translating + * from string values to numeric values for enumeration and boolean. + * @param[in] xmlstr0 XML string pre + * @param[in] ys Yang node + * @param[out] xmlstr1 XML string ready for translation + * @retval 1 OK + * @retval 0 Invalid type + * @retval -1 Error * @see type_snmp2xml for snmpset */ int -type_xml2snmpstr(char *xmlstr, - yang_stmt *ys, - char **snmpstr) +type_xml2snmp_pre(char *xmlstr0, + yang_stmt *ys, + char **xmlstr1) { int retval = -1; @@ -355,8 +365,8 @@ type_xml2snmpstr(char *xmlstr, char *str = NULL; int ret; - if (snmpstr == NULL){ - clicon_err(OE_UNIX, EINVAL, "snmpstr"); + if (xmlstr1 == NULL){ + clicon_err(OE_UNIX, EINVAL, "xmlstr1"); goto done; } /* Get yang type of leaf and trasnslate to ASN.1 */ @@ -364,10 +374,10 @@ type_xml2snmpstr(char *xmlstr, goto done; restype = yrestype?yang_argument_get(yrestype):NULL; if (strcmp(restype, "enumeration") == 0){ /* special case for enum */ - if ((ret = yang_enum2valstr(yrestype, xmlstr, &str)) < 0) + if ((ret = yang_enum2valstr(yrestype, xmlstr0, &str)) < 0) goto done; if (ret == 0){ - clicon_debug(1, "Invalid enum valstr %s", xmlstr); + clicon_debug(1, "Invalid enum valstr %s", xmlstr0); goto fail; } } @@ -377,15 +387,15 @@ type_xml2snmpstr(char *xmlstr, * 2) Truthvalue actually translates to enum true(1)/false(0) */ else if (strcmp(restype, "boolean") == 0){ - if (strcmp(xmlstr, "false")==0) + if (strcmp(xmlstr0, "false")==0) str = "0"; else str = "1"; } else{ - str = xmlstr; + str = xmlstr0; } - if ((*snmpstr = strdup(str)) == NULL){ + if ((*xmlstr1 = strdup(str)) == NULL){ clicon_err(OE_UNIX, errno, "strdup"); goto done; } @@ -409,14 +419,14 @@ type_xml2snmpstr(char *xmlstr, * @retval 0 Invalid * @retval -1 Error * @note asn1type can be rewritten from CLIXON_ASN_ to ASN_ - * XXX See sprint_realloc_timeticks + * @see type_xml2snmp_pre for some pre-condition XML special cases (eg enums and bool) */ int -type_snmpstr2val(char *snmpstr, - int *asn1type, - u_char **snmpval, - size_t *snmplen, - char **reason) +type_xml2snmp(char *snmpstr, + int *asn1type, + u_char **snmpval, + size_t *snmplen, + char **reason) { int retval = -1; int ret; @@ -627,123 +637,59 @@ yang2xpath(yang_stmt *ys, return retval; } -#ifdef NOTUSED /*! - * @param[in] ys Yang statement - * @param[out] xpath Malloced xpath string, use free() after use - * @retval 0 OK - * @retval -1 Error */ int -clixon_table_create(netsnmp_table_data_set *table0, - yang_stmt *ys, - clicon_handle h) +snmp_body2oid(cxobj *xi, + cg_var *cv) { - int retval = -1; - cvec *nsc = NULL; - cxobj *xt = NULL; - cxobj *xerr; - char *xpath; - cxobj *xtable; - cxobj *xe; - cxobj *xleaf; - char *valstr; - netsnmp_table_data *table; - netsnmp_table_row *row; - netsnmp_table_row *tmprow; - int i; - int ret; - cvec *keyvec = NULL; + int retval = -1; + yang_stmt *yi; + int asn1_type; + char *body; + size_t len; + cbuf *enc = NULL; + int i; - if (table0 == NULL || (table = table0->table) == NULL){ - clicon_err(OE_UNIX, EINVAL, "table0 /->table is NULL"); + if ((yi = xml_spec(xi)) == NULL) + goto ok; + if (type_yang2asn1(yi, &asn1_type, 0) < 0) goto done; + body = xml_body(xi); + switch (asn1_type){ + case ASN_INTEGER: + case ASN_GAUGE: + case ASN_TIMETICKS: + case ASN_COUNTER64: + case ASN_COUNTER: + case ASN_IPADDRESS: + if (cv_string_set(cv, body) < 0){ + clicon_err(OE_UNIX,errno, "cv_string_set"); + goto done; + } + break; + case ASN_OCTET_STR:{ /* encode to N.c.c.c.c */ + if ((enc = cbuf_new()) == NULL){ + clicon_err(OE_UNIX, errno, "cbuf_new"); + goto done; + } + len = strlen(body); + cprintf(enc, "%zu", len); + for (i=0; iindexes_template, - NULL, - 0, - ASN_OCTET_STR, - NULL, - 0) == NULL){ - clicon_err(OE_XML, errno, "snmp_varlist_add_variable"); - goto done; - } - if ((ret = netsnmp_table_set_add_default_row(table0, 2, ASN_OCTET_STR, 1, NULL, 0)) != SNMPERR_SUCCESS){ - clicon_err(OE_SNMP, ret, "netsnmp_table_set_add_default_row"); - goto done; - } - if ((ret = netsnmp_table_set_add_default_row(table0, 3, ASN_OCTET_STR, 1, NULL, 0)) != SNMPERR_SUCCESS){ - clicon_err(OE_SNMP, ret, "netsnmp_table_set_add_default_row"); - goto done; - } -#else - netsnmp_table_dataset_add_index(table, ASN_OCTET_STR); - netsnmp_table_set_multi_add_default_row(table0, 2, ASN_OCTET_STR, 1, NULL, 0, 3, ASN_OCTET_STR, 1, NULL, 0, 0); -#endif - if ((xtable = xpath_first(xt, nsc, "%s", xpath)) != NULL) { - for (tmprow = table->first_row; tmprow; tmprow = tmprow->next) - netsnmp_table_dataset_remove_and_delete_row(table0, tmprow); - - xe = NULL; /* Loop thru entries in table */ - while ((xe = xml_child_each(xtable, xe, CX_ELMNT)) != NULL) { - if ((row = netsnmp_create_table_data_row()) == NULL){ - clicon_err(OE_UNIX, errno, "netsnmp_create_table_data_row"); - goto done; - } - 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 entry is key XXX should check YANG -#if 1 - if ((snmp_varlist_add_variable(&row->indexes, NULL, 0, ASN_OCTET_STR, (const u_char *)valstr, strlen(valstr))) == NULL){ - clicon_err(OE_XML, errno, "snmp_varlist_add_variable"); - goto done; - } -#else - netsnmp_table_row_add_index(row, ASN_OCTET_STR, valstr, strlen(valstr)); -#endif - } - else{ - if ((ret = netsnmp_set_row_column(row, i, ASN_OCTET_STR, valstr, strlen(valstr))) != SNMPERR_SUCCESS){ - clicon_err(OE_SNMP, ret, "netsnmp_set_row_column"); - goto done; - } - if ((ret = netsnmp_mark_row_column_writable(row, i, 1)) != SNMPERR_SUCCESS){ - clicon_err(OE_SNMP, ret, "netsnmp_set_row_column"); - goto done; - } - } - i++; - } - - netsnmp_table_dataset_add_row(table0, row); - } - } - + ok: retval = 0; - -done: - if (xt) - xml_free(xt); - if (nsc) - xml_nsctx_free(nsc); + done: + if (enc) + cbuf_free(enc); return retval; } -#endif diff --git a/apps/snmp/snmp_lib.h b/apps/snmp/snmp_lib.h index 5c02cccd..d2c94d64 100644 --- a/apps/snmp/snmp_lib.h +++ b/apps/snmp/snmp_lib.h @@ -67,9 +67,10 @@ int type_snmp2xml(yang_stmt *ys, netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests, char **valstr); -int type_xml2snmpstr(char *xmlstr, yang_stmt *ys, char **snmpstr); -int type_snmpstr2val(char *snmpstr, int *asn1type, u_char **snmpval, size_t *snmplen, char **reason); +int type_xml2snmp_pre(char *xmlstr, yang_stmt *ys, char **snmpstr); +int type_xml2snmp(char *snmpstr, int *asn1type, u_char **snmpval, size_t *snmplen, char **reason); int yang2xpath(yang_stmt *ys, cvec *keyvec, char **xpath); +int snmp_body2oid(cxobj *xi, cg_var *cv); #endif /* _SNMP_LIB_H_ */ diff --git a/apps/snmp/snmp_register.c b/apps/snmp/snmp_register.c index a3409c01..848d28cd 100644 --- a/apps/snmp/snmp_register.c +++ b/apps/snmp/snmp_register.c @@ -94,9 +94,9 @@ * @retval -1 Error */ static int -mib_yang_leaf(clicon_handle h, - yang_stmt *ys, - cvec *cvk) +mibyang_leaf_register(clicon_handle h, + yang_stmt *ys, + cvec *cvk) { int retval = -1; netsnmp_handler_registration *nhreg = NULL; @@ -135,7 +135,7 @@ mib_yang_leaf(clicon_handle h, } if (yang_extension_value(ys, "max-access", IETF_YANG_SMIV2_NS, NULL, &modes_str) < 0) goto done; - /* Sanity check of types */ + /* Only for sanity check of types initially to fail early */ if (type_yang2asn1(ys, NULL, 0) < 0) goto done; @@ -200,7 +200,9 @@ mib_yang_leaf(clicon_handle h, return retval; } -/*! Parse smiv2 extensions for YANG container/list +/*! Register table entry handler itself (not column/row leafs) + * + * Parse smiv2 extensions for YANG container/list * * Typical table: * container x { @@ -216,9 +218,9 @@ mib_yang_leaf(clicon_handle h, * @retval -1 Error */ static int -mib_yang_table(clicon_handle h, - yang_stmt *ys, - yang_stmt *ylist) +mibyang_table_register(clicon_handle h, + yang_stmt *ys, + yang_stmt *ylist) { int retval = -1; netsnmp_handler_registration *nhreg; @@ -325,7 +327,7 @@ mib_yang_table(clicon_handle h, return retval; } -/*! Register table sub-oid:s +/*! Register table sub-oid:s of existing entries in clixon * This assumes a table contains a set of keys and a list of leafs only * The function makes a query to the datastore and registers all table entries that * currently exists. This means it registers for a static table. If new rows or columns @@ -338,9 +340,9 @@ mib_yang_table(clicon_handle h, * @retval -1 Error */ static int -mib_traverse_table(clicon_handle h, - yang_stmt *ys, - yang_stmt *ylist) +mibyang_table_traverse_static(clicon_handle h, + yang_stmt *ys, + yang_stmt *ylist) { int retval = -1; cvec *nsc = NULL; @@ -358,6 +360,7 @@ mib_traverse_table(clicon_handle h, int i; cxobj *xi; + clicon_debug(1, "%s %s", __FUNCTION__, yang_argument_get(ys)); if (xml_nsctx_yang(ys, &nsc) < 0) goto done; if (yang2xpath(ys, NULL, &xpath) < 0) @@ -389,7 +392,8 @@ mib_traverse_table(clicon_handle h, cv = cvec_i(cvk, i); if ((xi = xml_find_type(xrow, NULL, cv_string_get(cv0), CX_ELMNT)) == NULL) break; - cv_string_set(cv, xml_body(xi)); + if (snmp_body2oid(xi, cv) < 0) + goto done; } if (i identify as table */ yp = yang_parent_get(yn); if (yang_keyword_get(yp) == Y_CONTAINER){ - /* Specialize table traversal */ - if (mib_yang_table(h, yp, yn) < 0) + /* Register table entry handler itself (not column/row leafs) */ + if (mibyang_table_register(h, yp, yn) < 0) goto done; - if (mib_traverse_table(h, yp, yn) < 0) + /* Register table sub-oid:s of existing entries in clixon */ + if (mibyang_table_traverse_static(h, yp, yn) < 0) goto done; goto ok; } @@ -469,7 +475,7 @@ mib_traverse(clicon_handle h, while ((ys = yn_each(yn, ys)) != NULL) { if (!yang_schemanode(ys)) continue; - if ((ret = mib_traverse(h, ys)) < 0) + if ((ret = mibyang_traverse(h, ys)) < 0) goto done; if (ret > 0){ retval = ret; @@ -497,11 +503,13 @@ clixon_snmp_traverse_mibyangs(clicon_handle h) yang_stmt *yspec; yang_stmt *ymod; - /* XXX Hardcoded, replace this with generic MIB */ if ((yspec = clicon_dbspec_yang(h)) == NULL){ clicon_err(OE_FATAL, 0, "No DB_SPEC"); goto done; } + /* Loop over clixon configuration file to find all CLICON_SNMP_MIB, and + * then loop over all those MIBs to register OIDs with netsnmp + */ x = NULL; while ((x = xml_child_each(clicon_conf_xml(h), x, CX_ELMNT)) != NULL) { if (strcmp(xml_name(x), "CLICON_SNMP_MIB") != 0) @@ -520,7 +528,7 @@ clixon_snmp_traverse_mibyangs(clicon_handle h) goto done; } /* Recursively traverse the mib-yang to find extensions */ - if (mib_traverse(h, ymod) < 0) + if (mibyang_traverse(h, ymod) < 0) goto done; } retval = 0; diff --git a/test/test_snmp_set.sh b/test/test_snmp_set.sh index a5fc5ae1..d8234867 100755 --- a/test/test_snmp_set.sh +++ b/test/test_snmp_set.sh @@ -219,13 +219,15 @@ expectpart "$($snmpget $OID)" 0 "$OID = $TYPE: $VALUE" NAME=ifCounterDiscontinuityTime OID=$OID11 VALUE=1234567890 -TYPE=Gauge32 # TimeStamp +#TYPE=Gauge32 # TimeStamp +TYPE=Timeticks new "Set $NAME $VALUE" -expectpart "$($snmpset $OID u $VALUE)" 0 "$OID = $TYPE: $VALUE" +echo "$snmpset $OID t $VALUE" +expectpart "$($snmpset $OID t $VALUE)" 0 "$OID = $TYPE: ($VALUE) 142 days, 21:21:18.90" new "Get $NAME $VALUE" -expectpart "$($snmpget $OID)" 0 "$OID = $TYPE: $VALUE" +expectpart "$($snmpget $OID)" 0 "$OID = $TYPE: ($VALUE) 142 days, 21:21:18.90" NAME=ifStackStatus OID=$OID12 diff --git a/test/test_snmp_types.sh b/test/test_snmp_types.sh index 51135676..48729057 100755 --- a/test/test_snmp_types.sh +++ b/test/test_snmp_types.sh @@ -167,7 +167,7 @@ new "Get $NAME $VALUE" expectpart "$($snmpget $OID)" 0 "$OID = $TYPE: $VALUE" new "Test SNMP getnext netSnmpExampleInteger" -expectpart "$($snmpgetnext $OID1)" 0 "$OID2 = INTEGER: -1" +expectpart "$($snmpgetnext $OID)" 0 "$OID2 = INTEGER: -1" NAME=netSnmpExampleSleeper OID=$OID2 @@ -178,7 +178,7 @@ new "Get $NAME $VALUE" expectpart "$($snmpget $OID)" 0 "$OID = $TYPE: $VALUE" new "Test SNMP getnext netSnmpExampleSleeper" -expectpart "$($snmpgetnext $OID2)" 0 "$OID3 = STRING: This is not default" +expectpart "$($snmpgetnext $OID)" 0 "$OID3 = STRING: This is not default" NAME=netSnmpExampleString OID=$OID3 @@ -189,7 +189,7 @@ new "Get $NAME $VALUE" expectpart "$($snmpget $OID)" 0 "$OID = $TYPE: $VALUE" new "Test SNMP getnext netSnmpExampleString" -expectpart "$($snmpgetnext $OID3)" 0 "$OID4 = Timeticks: (12345) 0:02:03.45" +expectpart "$($snmpgetnext $OID)" 0 "$OID4 = Timeticks: (12345) 0:02:03.45" NAME=ifTableLastChange OID=$OID4 @@ -200,7 +200,7 @@ new "Get $NAME $VALUE" expectpart "$($snmpget $OID)" 0 "$OID = $TYPE: $VALUE" new "Test SNMP getnext ipTableLastChange" -expectpart "$($snmpgetnext $OID4)" 0 "$OID5 = INTEGER: 48" +expectpart "$($snmpgetnext $OID)" 0 "$OID5 = INTEGER: 48" NAME=ifType OID=$OID5 @@ -211,7 +211,7 @@ new "Get $NAME $VALUE" expectpart "$($snmpget $OID)" 0 "$OID = $TYPE: $VALUE" new "Test SNMP getnext ifType" -expectpart "$($snmpgetnext $OID5)" 0 "$OID6 = Gauge32: 123123123" +expectpart "$($snmpgetnext $OID)" 0 "$OID6 = Gauge32: 123123123" NAME=ifSpeed OID=$OID6 @@ -222,7 +222,7 @@ new "Get $NAME $VALUE" expectpart "$($snmpget $OID)" 0 "$OID = $TYPE: $VALUE" new "Test SNMP getnext ifSpeed" -expectpart "$($snmpgetnext $OID6)" 0 "$OID7 = INTEGER: 3" +expectpart "$($snmpgetnext $OID)" 0 "$OID7 = INTEGER: 3" NAME=ifAdminStatus OID=$OID7 @@ -233,7 +233,7 @@ new "Get $NAME $VALUE" expectpart "$($snmpget $OID)" 0 "$OID = $TYPE: $VALUE" new "Test SNMP getnext ifAdminStatus" -expectpart "$($snmpgetnext $OID7)" 0 "$OID8 = Counter32: 123456" +expectpart "$($snmpgetnext $OID)" 0 "$OID8 = Counter32: 123456" NAME=ifInOctets OID=$OID8 @@ -244,7 +244,7 @@ new "Get $NAME $VALUE" expectpart "$($snmpget $OID)" 0 "$OID = $TYPE: $VALUE" new "Test SNMP getnext ifInOctets" -expectpart "$($snmpgetnext $OID8)" 0 "$OID9 = Counter64: 4294967296" +expectpart "$($snmpgetnext $OID)" 0 "$OID9 = Counter64: 4294967296" NAME=ifHCInOctets OID=$OID9 @@ -255,7 +255,7 @@ new "Get $NAME $VALUE" expectpart "$($snmpget $OID)" 0 "$OID = $TYPE: $VALUE" new "Test SNMP getnext ifHCInOctets" -expectpart "$($snmpgetnext $OID9)" 0 "$OID10 = INTEGER: 1" +expectpart "$($snmpgetnext $OID)" 0 "$OID10 = INTEGER: 1" NAME=ifPromiscuousMode OID=$OID10 @@ -266,18 +266,18 @@ new "Get $NAME $VALUE" expectpart "$($snmpget $OID)" 0 "$OID = $TYPE: $VALUE" new "Test SNMP getnext ifPromiscuousMode" -expectpart "$($snmpgetnext $OID10)" 0 "$OID11 = Gauge32: 1234567890" +expectpart "$($snmpgetnext $OID)" 0 "$OID11 = Timeticks: (1234567890) 142 days, 21:21:18.90" NAME=ifCounterDiscontinuityTime OID=$OID11 -VALUE=1234567890 -TYPE=Gauge32 # timestamp +VALUE="(1234567890) 142 days, 21:21:18.90" +TYPE=TimeTicks new "Get $NAME $VALUE" expectpart "$($snmpget $OID)" 0 "$OID = $TYPE: $VALUE" new "Test SNMP getnext ifCounterDiscontinuityTime" -expectpart "$($snmpgetnext $OID11)" 0 "$OID12 = INTEGER: 1" +expectpart "$($snmpgetnext $OID)" 0 "$OID12 = INTEGER: 1" NAME=ifStackStatus OID=$OID12 @@ -288,7 +288,7 @@ new "Get $NAME $VALUE" expectpart "$($snmpget $OID)" 0 "$OID = $TYPE: $VALUE" new "Test SNMP getnext ifStackStatus" -expectpart "$($snmpgetnext $OID12)" 0 "" # XXX table OID +#expectpart "$($snmpgetnext $OID)" 0 "" # XXX table OID should it work? #----------------- table