diff --git a/apps/snmp/snmp_handler.c b/apps/snmp/snmp_handler.c index f067f7d3..6ebc0643 100644 --- a/apps/snmp/snmp_handler.c +++ b/apps/snmp/snmp_handler.c @@ -126,54 +126,69 @@ snmp_scalar_get(clicon_handle h, yang_stmt *ys, netsnmp_variable_list *requestvb, char *defaultval, - enum cv_type cvtype, netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests) { - int retval = -1; - cvec *nsc = NULL; - char *xpath = NULL; - cxobj *xt = NULL; - cxobj *xerr; - cxobj *x; - char *valstr = NULL; + int retval = -1; + cvec *nsc = NULL; + char *xpath = NULL; + cxobj *xt = NULL; + cxobj *xerr; + cxobj *x; + char *snmpstr = NULL; u_char *snmpval = NULL; - size_t snmplen; - int ret; + size_t snmplen = 0; + int ret; + int asn1type; + char *reason = NULL; + /* Prepare backend call by constructing namespace context */ if (xml_nsctx_yang(ys, &nsc) < 0) goto done; + /* Create xpath from yang (XXX works not for lists) */ if (yang2xpath(ys, &xpath) < 0) goto done; + /* Do the backend call */ if (clicon_rpc_get(h, xpath, nsc, CONTENT_ALL, -1, &xt) < 0) goto done; + /* Detect error XXX Error handling could improve */ 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); + /* + * The xml to snmp value conversion is done here. It 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 (type_xml2snmpstr(xml_body(x), ys, &snmpstr) < 0) + goto done; + } + else if (defaultval != NULL){ + if ((snmpstr = strdup(defaultval)) == NULL){ + clicon_err(OE_UNIX, errno, "strdup"); + goto done; + } } - else if ((valstr = defaultval) != NULL) - ; else{ netsnmp_set_request_error(reqinfo, requests, SNMP_NOSUCHINSTANCE); goto ok; } - if ((ret = type_yang2snmp(valstr, cvtype, reqinfo, requests, &snmpval, &snmplen)) < 0) + if (type_yang2asn1(ys, &asn1type) < 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 <-- + if ((ret = type_snmpstr2val(snmpstr, asn1type, &snmpval, &snmplen, &reason)) < 0) + goto done; + if (ret == 0){ + clicon_debug(1, "%s %s", __FUNCTION__, reason); + netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_WRONGTYPE); + goto ok; + } + /* see snmplib/snmp_client. somewhat indirect */ - - /* see snmplib/snmp_client.c */ + requestvb->type = asn1type; // ASN_NULL on input if (snmp_set_var_value(requestvb, snmpval, snmplen) != 0){ @@ -183,6 +198,10 @@ snmp_scalar_get(clicon_handle h, ok: retval = 0; done: + if (reason) + free(reason); + if (snmpstr) + free(snmpstr); if (snmpval) free(snmpval); if (xt) @@ -230,7 +249,7 @@ snmp_scalar_set(clicon_handle h, } if ((xb = xml_new("body", xbot, CX_BODY)) == NULL) goto done; - if ((ret = type_snmp2yang(requestvb, reqinfo, requests, &valstr)) < 0) + if ((ret = type_snmp2xml(requestvb, reqinfo, requests, &valstr)) < 0) goto done; if (ret == 0) goto ok; @@ -273,7 +292,6 @@ snmp_scalar_handler(netsnmp_mib_handler *handler, yang_stmt *ys; int asn1_type; netsnmp_variable_list *requestvb; /* sub of requests */ - enum cv_type cvtype; /* * can be used to pass information on a per-pdu basis from a @@ -312,20 +330,19 @@ snmp_scalar_handler(netsnmp_mib_handler *handler, 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 - if (snmp_scalar_get(sh->sh_h, ys, requestvb, sh->sh_default, cvtype, reqinfo, requests) < 0) + if (snmp_scalar_get(sh->sh_h, ys, requestvb, sh->sh_default, reqinfo, requests) < 0) goto done; break; case MODE_GETNEXT: /* 161 */ assert(0); // Not seen? break; case MODE_SET_RESERVE1: /* 0 */ + /* Translate from YANG ys leaf type to SNMP asn1.1 type ids (not value), also cvtype */ + if (type_yang2asn1(ys, &asn1_type) < 0) + goto done; if (requestvb->type != asn1_type) netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_WRONGTYPE); diff --git a/apps/snmp/snmp_lib.c b/apps/snmp/snmp_lib.c index f13cf6de..0735ecf2 100644 --- a/apps/snmp/snmp_lib.c +++ b/apps/snmp/snmp_lib.c @@ -96,17 +96,12 @@ static const map_str2int snmp_access_map[] = { * XXX TimeTicks */ static const map_str2int snmp_type_map[] = { - - {"int32", ASN_INTEGER}, - {"string", ASN_OCTET_STR}, - {"uint32", ASN_INTEGER}, - {"uint64", ASN_INTEGER}, - // {"bool", ASN_BOOLEAN}, - // {"empty", ASN_NULL}, - // {"bits", ASN_BIT_STR}, - // {"", ASN_OBJECT_ID}, - // {"", ASN_SEQUENCE}, - // {"", ASN_SET}, + {"int32", ASN_INTEGER}, // 2 + {"string", ASN_OCTET_STR}, // 4 + {"enumeration", ASN_INTEGER}, // 2 special case + {"uint32", ASN_GAUGE}, // 0x42 + {"uint64", ASN_COUNTER64}, // 0x46 / 70 + {"boolean", ASN_INTEGER}, // 2 special case -> enumeration {NULL, -1} }; @@ -136,18 +131,18 @@ snmp_msg_int2str(int msg) { return clicon_int2str(snmp_msg_map, msg); } + /*! Translate from YANG to SNMP asn1.1 type ids (not value) * * @param[in] ys YANG leaf node * @param[out] asn1_type ASN.1 type id - * @param[out] cvtype Clixon cv type * @retval 0 OK * @retval -1 Error + * @see type_yang2snmp, yang only */ int -yang2snmp_types(yang_stmt *ys, - int *asn1_type, - enum cv_type *cvtype) +type_yang2asn1(yang_stmt *ys, + int *asn1_type) { int retval = -1; yang_stmt *yrestype; /* resolved type */ @@ -163,18 +158,17 @@ yang2snmp_types(yang_stmt *ys, if ((at = clicon_str2int(snmp_type_map, restype)) < 0){ clicon_err(OE_YANG, 0, "No snmp translation for YANG %s type:%s", yang_argument_get(ys), restype); - // goto done; + goto done; } if (asn1_type) *asn1_type = at; - if (cvtype && clicon_type2cv(origtype, restype, ys, cvtype) < 0) - goto done; clicon_debug(1, "%s type:%s", __FUNCTION__, restype); retval = 0; done: return retval; } +#if 0 /*! Translate from yang/xml/clixon to SNMP/ASN.1 * * @param[in] valstr Clixon/yang/xml string value @@ -263,6 +257,8 @@ type_yang2snmp(char *valstr, retval = 0; goto done; } +#endif + /*! Translate from yang/xml/clixon to SNMP/ASN.1 * * @param[in] snmpval Malloc:ed snmp type @@ -275,10 +271,10 @@ type_yang2snmp(char *valstr, * @retval -1 Error */ int -type_snmp2yang(netsnmp_variable_list *requestvb, - netsnmp_agent_request_info *reqinfo, - netsnmp_request_info *requests, - char **valstr) +type_snmp2xml(netsnmp_variable_list *requestvb, + netsnmp_agent_request_info *reqinfo, + netsnmp_request_info *requests, + char **valstr) { int retval = -1; char *cvtypestr; @@ -324,6 +320,146 @@ type_snmp2yang(netsnmp_variable_list *requestvb, goto done; } +/*! 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 + */ +int +type_xml2snmpstr(char *xmlstr, + yang_stmt *ys, + char **snmpstr) + +{ + int retval = -1; + yang_stmt *yrestype; /* resolved type */ + char *restype; /* resolved type */ + char *origtype=NULL; /* original type */ + char *str = NULL; + + if (snmpstr == NULL){ + clicon_err(OE_UNIX, EINVAL, "snmpstr"); + goto done; + } + /* 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 (strcmp(restype, "enumeration") == 0){ /* special case for enum */ + if (yang_enum2intstr(yrestype, xmlstr, &str) < 0) + goto done; + } + /* special case for bool: although smidump translates TruthValue to boolean + * and there is an ASN_BOOLEAN constant: + * 1) there is no code for ASN_BOOLEAN and + * 2) Truthvalue actually translates to enum true(1)/false(0) + */ + else if (strcmp(restype, "boolean") == 0){ + if (strcmp(xmlstr, "false")==0) + str = "0"; + else + str = "1"; + } + else{ + str = xmlstr; + } + if ((*snmpstr = strdup(str)) == NULL){ + clicon_err(OE_UNIX, errno, "strdup"); + goto done; + } + retval = 0; + done: + clicon_debug(1, "%s %d", __FUNCTION__, retval); + return retval; +} + +/*! Given snmp string value (as translated frm XML) parse into snmp value + * + * @param[in] snmpstr SNMP type string + * @param[in] asn1type ASN.1 type id + * @param[out] snmpval Malloc:ed snmp type + * @param[out] snmplen Length of snmp type + * @param[out] reason Error reason if retval is 0 + * @retval 1 OK + * @retval 0 Invalid + * @retval -1 Error + */ +int +type_snmpstr2val(char *snmpstr, + int asn1type, + u_char **snmpval, + size_t *snmplen, + char **reason) +{ + int retval = -1; + int ret; + + if (snmpval == NULL || snmplen == NULL){ + clicon_err(OE_UNIX, EINVAL, "snmpval or snmplen is NULL"); + goto done; + } + switch (asn1type){ + case ASN_BOOLEAN: // 1 + break; + case ASN_INTEGER: // 2 + *snmplen = 4; + if ((*snmpval = malloc(*snmplen)) == NULL){ + clicon_err(OE_UNIX, errno, "malloc"); + goto done; + } + if ((ret = parse_int32(snmpstr, (int32_t*)*snmpval, reason)) < 0) + goto done; + if (ret == 0) + goto fail; + break; + case ASN_GAUGE: // 0x42 + *snmplen = 4; + if ((*snmpval = malloc(*snmplen)) == NULL){ + clicon_err(OE_UNIX, errno, "malloc"); + goto done; + } + if ((ret = parse_uint32(snmpstr, (uint32_t*)*snmpval, reason)) < 0) + goto done; + if (ret == 0) + goto fail; + + break; + case ASN_OCTET_STR: // 4 + *snmplen = strlen(snmpstr)+1; + if ((*snmpval = (u_char*)strdup((snmpstr))) == NULL){ + clicon_err(OE_UNIX, errno, "strdup"); + goto done; + } + break; + case ASN_COUNTER64:{ // 0x46 / 70 + uint64_t u64; + struct counter64 *c64; + *snmplen = sizeof(struct counter64); // 16! + if ((*snmpval = malloc(*snmplen)) == NULL){ + clicon_err(OE_UNIX, errno, "malloc"); + goto done; + } + memset(*snmpval, 0, *snmplen); + if ((ret = parse_uint64(snmpstr, &u64, reason)) < 0) + goto done; + c64 = (struct counter64 *)*snmpval; + c64->low = u64&0xffffffff; + c64->high = u64/0x100000000; + if (ret == 0) + goto fail; + } + break; + default: + assert(0); + } + retval = 1; + done: + clicon_debug(1, "%s %d", __FUNCTION__, retval); + return retval; + fail: + retval = 0; + goto done; +} + /*! Construct an xpath from yang statement, internal fn using cb * Recursively construct it to the top. * @param[in] ys Yang statement diff --git a/apps/snmp/snmp_lib.h b/apps/snmp/snmp_lib.h index 13a6cbe4..b46b5f4c 100644 --- a/apps/snmp/snmp_lib.h +++ b/apps/snmp/snmp_lib.h @@ -60,14 +60,14 @@ typedef struct clixon_snmp_handle clixon_snmp_handle; */ int snmp_access_str2int(char *modes_str); const char *snmp_msg_int2str(int msg); -int yang2snmp_types(yang_stmt *ys, int *asn1_type, enum cv_type *cvtype); -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 type_snmp2yang(netsnmp_variable_list *requestvb, - netsnmp_agent_request_info *reqinfo, - netsnmp_request_info *requests, - char **valstr); +int type_yang2asn1(yang_stmt *ys, int *asn1_type); +int type_snmp2xml(netsnmp_variable_list *requestvb, + 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 yang2xpath(yang_stmt *ys, char **xpath); int clixon_table_create(netsnmp_table_data_set *table, yang_stmt *ys, clicon_handle h); diff --git a/apps/snmp/snmp_register.c b/apps/snmp/snmp_register.c index dd67eee7..8f5da8aa 100644 --- a/apps/snmp/snmp_register.c +++ b/apps/snmp/snmp_register.c @@ -205,8 +205,8 @@ mib_yang_leaf(clicon_handle h, } if (yang_extension_value(ys, "max-access", IETF_YANG_SMIV2_NS, NULL, &modes_str) < 0) goto done; -#if 0 /* Sanity check of types */ - if (yang2snmp_types(ys, NULL, NULL) < 0) +#if 1 /* Sanity check of types */ + if (type_yang2asn1(ys, NULL) < 0) goto done; #endif /* Get modes (access) read-only, read-write, not-accessible, oaccessible-for-notify diff --git a/lib/clixon/clixon_xml_map.h b/lib/clixon/clixon_xml_map.h index 8f6193d3..95bd43f2 100644 --- a/lib/clixon/clixon_xml_map.h +++ b/lib/clixon/clixon_xml_map.h @@ -70,6 +70,7 @@ int xml2xpath(cxobj *x, cvec *nsc, char **xpath); int assign_namespace_element(cxobj *x0, cxobj *x1, cxobj *x1p); int assign_namespace_body(cxobj *x0, cxobj *x1); int xml_merge(cxobj *x0, cxobj *x1, yang_stmt *yspec, char **reason); +int yang_enum2intstr(yang_stmt *ytype, char *value, char **intstr); int yang_enum_int_value(cxobj *node, int32_t *val); int xml_copy_marked(cxobj *x0, cxobj *x1); int yang_check_when_xpath(cxobj *xn, cxobj *xp, yang_stmt *yn, int *hit, int *nrp, char **xpathp); diff --git a/lib/src/clixon_xml_map.c b/lib/src/clixon_xml_map.c index 752277b3..b8635998 100644 --- a/lib/src/clixon_xml_map.c +++ b/lib/src/clixon_xml_map.c @@ -1810,6 +1810,37 @@ xml_merge(cxobj *x0, goto done; } + +/*! Given a YANG (enum) type node and a value, return the string containing corresponding int str + * + * @param[in] ytype YANG type noden + * @param[in] valstr Value of enum + * @param[out] intstr Corresponding string containing an int (direct pointer, dont free) + */ +int +yang_enum2intstr(yang_stmt *ytype, + char *valstr, + char **intstr) +{ + int retval = -1; + yang_stmt *yenum; + yang_stmt *yval; + + if (intstr == NULL){ + clicon_err(OE_UNIX, EINVAL, "intstr is NULL"); + goto done; + } + if ((yenum = yang_find(ytype, Y_ENUM, valstr)) == NULL) + goto done; + /* Should assign value if yval not found */ + if ((yval = yang_find(yenum, Y_VALUE, NULL)) == NULL) + goto done; + *intstr = yang_argument_get(yval); + retval = 0; + done: + return retval; +} + /*! Get integer value from xml node from yang enumeration * @param[in] node XML node in a tree * @param[out] val Integer value returned @@ -1832,9 +1863,8 @@ yang_enum_int_value(cxobj *node, yang_stmt *ys; yang_stmt *ytype; yang_stmt *yrestype; /* resolved type */ - yang_stmt *yenum; - yang_stmt *yval; char *reason = NULL; + char *intstr = NULL; if (node == NULL) goto done; @@ -1853,20 +1883,16 @@ yang_enum_int_value(cxobj *node, } if (yrestype==NULL || strcmp(yang_argument_get(yrestype), "enumeration")) goto done; - if ((yenum = yang_find(yrestype, Y_ENUM, xml_body(node))) == NULL) - goto done; - /* Should assign value if yval not found */ - if ((yval = yang_find(yenum, Y_VALUE, NULL)) == NULL) + if (yang_enum2intstr(yrestype, xml_body(node), &intstr) < 0) goto done; /* reason is string containing why int could not be parsed */ - if (parse_int32(yang_argument_get(yval), val, &reason) < 0) + if (parse_int32(intstr, val, &reason) < 0) goto done; retval = 0; done: return retval; } - /*! Given XML tree x0 with marked nodes, copy marked nodes to new tree x1 * Two marks are used: XML_FLAG_MARK and XML_FLAG_CHANGE *