SNMP frontend: Reworked types for GET types translation

YANG enum type, split up enum to int into two functions
This commit is contained in:
Olof hagsand 2022-05-23 09:09:02 +02:00 committed by Kristofer Hallin
parent e44d76cc3a
commit 7a1dc5da50
6 changed files with 252 additions and 72 deletions

View file

@ -126,54 +126,69 @@ snmp_scalar_get(clicon_handle h,
yang_stmt *ys, yang_stmt *ys,
netsnmp_variable_list *requestvb, netsnmp_variable_list *requestvb,
char *defaultval, char *defaultval,
enum cv_type cvtype,
netsnmp_agent_request_info *reqinfo, netsnmp_agent_request_info *reqinfo,
netsnmp_request_info *requests) netsnmp_request_info *requests)
{ {
int retval = -1; int retval = -1;
cvec *nsc = NULL; cvec *nsc = NULL;
char *xpath = NULL; char *xpath = NULL;
cxobj *xt = NULL; cxobj *xt = NULL;
cxobj *xerr; cxobj *xerr;
cxobj *x; cxobj *x;
char *valstr = NULL; char *snmpstr = NULL;
u_char *snmpval = NULL; u_char *snmpval = NULL;
size_t snmplen; size_t snmplen = 0;
int ret; int ret;
int asn1type;
char *reason = NULL;
/* Prepare backend call by constructing namespace context */
if (xml_nsctx_yang(ys, &nsc) < 0) if (xml_nsctx_yang(ys, &nsc) < 0)
goto done; goto done;
/* Create xpath from yang (XXX works not for lists) */
if (yang2xpath(ys, &xpath) < 0) if (yang2xpath(ys, &xpath) < 0)
goto done; goto done;
/* Do the backend call */
if (clicon_rpc_get(h, xpath, nsc, CONTENT_ALL, -1, &xt) < 0) if (clicon_rpc_get(h, xpath, nsc, CONTENT_ALL, -1, &xt) < 0)
goto done; goto done;
/* Detect error XXX Error handling could improve */
if ((xerr = xpath_first(xt, NULL, "/rpc-error")) != NULL){ if ((xerr = xpath_first(xt, NULL, "/rpc-error")) != NULL){
clixon_netconf_error(xerr, "clicon_rpc_get", NULL); clixon_netconf_error(xerr, "clicon_rpc_get", NULL);
goto done; goto done;
} }
/* Get value, either from xml, or smiv2 default */ /*
if ((x = xpath_first(xt, nsc, "%s", xpath)) != NULL) { * The xml to snmp value conversion is done here. It is done in two steps:
valstr = xml_body(x); * 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{ else{
netsnmp_set_request_error(reqinfo, requests, SNMP_NOSUCHINSTANCE); netsnmp_set_request_error(reqinfo, requests, SNMP_NOSUCHINSTANCE);
goto ok; goto ok;
} }
if ((ret = type_yang2snmp(valstr, cvtype, reqinfo, requests, &snmpval, &snmplen)) < 0) if (type_yang2asn1(ys, &asn1type) < 0)
goto done; goto done;
if (ret == 0)
goto ok;
/* 1. use cligen object and get rwa buf / size from that, OR if ((ret = type_snmpstr2val(snmpstr, asn1type, &snmpval, &snmplen, &reason)) < 0)
* + have parse function from YANG goto done;
* - does not have if (ret == 0){
* 2. use union netsnmp_vardata and pass that here? clicon_debug(1, "%s %s", __FUNCTION__, reason);
* 3. Make cv2asn1 conversion function <-- netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_WRONGTYPE);
goto ok;
}
/* see snmplib/snmp_client. somewhat indirect
*/ */
requestvb->type = asn1type; // ASN_NULL on input
/* see snmplib/snmp_client.c */
if (snmp_set_var_value(requestvb, if (snmp_set_var_value(requestvb,
snmpval, snmpval,
snmplen) != 0){ snmplen) != 0){
@ -183,6 +198,10 @@ snmp_scalar_get(clicon_handle h,
ok: ok:
retval = 0; retval = 0;
done: done:
if (reason)
free(reason);
if (snmpstr)
free(snmpstr);
if (snmpval) if (snmpval)
free(snmpval); free(snmpval);
if (xt) if (xt)
@ -230,7 +249,7 @@ snmp_scalar_set(clicon_handle h,
} }
if ((xb = xml_new("body", xbot, CX_BODY)) == NULL) if ((xb = xml_new("body", xbot, CX_BODY)) == NULL)
goto done; goto done;
if ((ret = type_snmp2yang(requestvb, reqinfo, requests, &valstr)) < 0) if ((ret = type_snmp2xml(requestvb, reqinfo, requests, &valstr)) < 0)
goto done; goto done;
if (ret == 0) if (ret == 0)
goto ok; goto ok;
@ -273,7 +292,6 @@ snmp_scalar_handler(netsnmp_mib_handler *handler,
yang_stmt *ys; yang_stmt *ys;
int asn1_type; int asn1_type;
netsnmp_variable_list *requestvb; /* sub of requests */ netsnmp_variable_list *requestvb; /* sub of requests */
enum cv_type cvtype;
/* /*
* can be used to pass information on a per-pdu basis from a * 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); SNMP_NOSUCHOBJECT);
return SNMP_ERR_NOERROR; return SNMP_ERR_NOERROR;
#endif #endif
if (yang2snmp_types(ys, &asn1_type, &cvtype) < 0)
goto done;
/* see net-snmp/agent/snmp_agent.h / net-snmp/library/snmp.h */ /* see net-snmp/agent/snmp_agent.h / net-snmp/library/snmp.h */
switch (reqinfo->mode) { switch (reqinfo->mode) {
case MODE_GET: /* 160 */ case MODE_GET: /* 160 */
requestvb->type = asn1_type; // ASN_NULL on input if (snmp_scalar_get(sh->sh_h, ys, requestvb, sh->sh_default, reqinfo, requests) < 0)
if (snmp_scalar_get(sh->sh_h, ys, requestvb, sh->sh_default, cvtype, reqinfo, requests) < 0)
goto done; goto done;
break; break;
case MODE_GETNEXT: /* 161 */ case MODE_GETNEXT: /* 161 */
assert(0); // Not seen? assert(0); // Not seen?
break; break;
case MODE_SET_RESERVE1: /* 0 */ 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) if (requestvb->type != asn1_type)
netsnmp_set_request_error(reqinfo, requests, netsnmp_set_request_error(reqinfo, requests,
SNMP_ERR_WRONGTYPE); SNMP_ERR_WRONGTYPE);

View file

@ -96,17 +96,12 @@ static const map_str2int snmp_access_map[] = {
* XXX TimeTicks * XXX TimeTicks
*/ */
static const map_str2int snmp_type_map[] = { static const map_str2int snmp_type_map[] = {
{"int32", ASN_INTEGER}, // 2
{"int32", ASN_INTEGER}, {"string", ASN_OCTET_STR}, // 4
{"string", ASN_OCTET_STR}, {"enumeration", ASN_INTEGER}, // 2 special case
{"uint32", ASN_INTEGER}, {"uint32", ASN_GAUGE}, // 0x42
{"uint64", ASN_INTEGER}, {"uint64", ASN_COUNTER64}, // 0x46 / 70
// {"bool", ASN_BOOLEAN}, {"boolean", ASN_INTEGER}, // 2 special case -> enumeration
// {"empty", ASN_NULL},
// {"bits", ASN_BIT_STR},
// {"", ASN_OBJECT_ID},
// {"", ASN_SEQUENCE},
// {"", ASN_SET},
{NULL, -1} {NULL, -1}
}; };
@ -136,18 +131,18 @@ snmp_msg_int2str(int msg)
{ {
return clicon_int2str(snmp_msg_map, msg); return clicon_int2str(snmp_msg_map, msg);
} }
/*! Translate from YANG to SNMP asn1.1 type ids (not value) /*! Translate from YANG to SNMP asn1.1 type ids (not value)
* *
* @param[in] ys YANG leaf node * @param[in] ys YANG leaf node
* @param[out] asn1_type ASN.1 type id * @param[out] asn1_type ASN.1 type id
* @param[out] cvtype Clixon cv type
* @retval 0 OK * @retval 0 OK
* @retval -1 Error * @retval -1 Error
* @see type_yang2snmp, yang only
*/ */
int int
yang2snmp_types(yang_stmt *ys, type_yang2asn1(yang_stmt *ys,
int *asn1_type, int *asn1_type)
enum cv_type *cvtype)
{ {
int retval = -1; int retval = -1;
yang_stmt *yrestype; /* resolved type */ yang_stmt *yrestype; /* resolved type */
@ -163,18 +158,17 @@ yang2snmp_types(yang_stmt *ys,
if ((at = clicon_str2int(snmp_type_map, restype)) < 0){ if ((at = clicon_str2int(snmp_type_map, restype)) < 0){
clicon_err(OE_YANG, 0, "No snmp translation for YANG %s type:%s", clicon_err(OE_YANG, 0, "No snmp translation for YANG %s type:%s",
yang_argument_get(ys), restype); yang_argument_get(ys), restype);
// goto done; goto done;
} }
if (asn1_type) if (asn1_type)
*asn1_type = at; *asn1_type = at;
if (cvtype && clicon_type2cv(origtype, restype, ys, cvtype) < 0)
goto done;
clicon_debug(1, "%s type:%s", __FUNCTION__, restype); clicon_debug(1, "%s type:%s", __FUNCTION__, restype);
retval = 0; retval = 0;
done: done:
return retval; return retval;
} }
#if 0
/*! Translate from yang/xml/clixon to SNMP/ASN.1 /*! Translate from yang/xml/clixon to SNMP/ASN.1
* *
* @param[in] valstr Clixon/yang/xml string value * @param[in] valstr Clixon/yang/xml string value
@ -263,6 +257,8 @@ type_yang2snmp(char *valstr,
retval = 0; retval = 0;
goto done; goto done;
} }
#endif
/*! Translate from yang/xml/clixon to SNMP/ASN.1 /*! Translate from yang/xml/clixon to SNMP/ASN.1
* *
* @param[in] snmpval Malloc:ed snmp type * @param[in] snmpval Malloc:ed snmp type
@ -275,10 +271,10 @@ type_yang2snmp(char *valstr,
* @retval -1 Error * @retval -1 Error
*/ */
int int
type_snmp2yang(netsnmp_variable_list *requestvb, type_snmp2xml(netsnmp_variable_list *requestvb,
netsnmp_agent_request_info *reqinfo, netsnmp_agent_request_info *reqinfo,
netsnmp_request_info *requests, netsnmp_request_info *requests,
char **valstr) char **valstr)
{ {
int retval = -1; int retval = -1;
char *cvtypestr; char *cvtypestr;
@ -324,6 +320,146 @@ type_snmp2yang(netsnmp_variable_list *requestvb,
goto done; 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 /*! Construct an xpath from yang statement, internal fn using cb
* Recursively construct it to the top. * Recursively construct it to the top.
* @param[in] ys Yang statement * @param[in] ys Yang statement

View file

@ -60,14 +60,14 @@ typedef struct clixon_snmp_handle clixon_snmp_handle;
*/ */
int snmp_access_str2int(char *modes_str); int snmp_access_str2int(char *modes_str);
const char *snmp_msg_int2str(int msg); const char *snmp_msg_int2str(int msg);
int yang2snmp_types(yang_stmt *ys, int *asn1_type, enum cv_type *cvtype); int type_yang2asn1(yang_stmt *ys, int *asn1_type);
int type_yang2snmp(char *valstr, enum cv_type cvtype, int type_snmp2xml(netsnmp_variable_list *requestvb,
netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests, netsnmp_agent_request_info *reqinfo,
u_char **snmpval, size_t *snmplen); netsnmp_request_info *requests,
int type_snmp2yang(netsnmp_variable_list *requestvb, char **valstr);
netsnmp_agent_request_info *reqinfo, int type_xml2snmpstr(char *xmlstr, yang_stmt *ys, char **snmpstr);
netsnmp_request_info *requests,
char **valstr); int type_snmpstr2val(char *snmpstr, int asn1type, u_char **snmpval, size_t *snmplen, char **reason);
int yang2xpath(yang_stmt *ys, char **xpath); int yang2xpath(yang_stmt *ys, char **xpath);
int clixon_table_create(netsnmp_table_data_set *table, yang_stmt *ys, clicon_handle h); int clixon_table_create(netsnmp_table_data_set *table, yang_stmt *ys, clicon_handle h);

View file

@ -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) if (yang_extension_value(ys, "max-access", IETF_YANG_SMIV2_NS, NULL, &modes_str) < 0)
goto done; goto done;
#if 0 /* Sanity check of types */ #if 1 /* Sanity check of types */
if (yang2snmp_types(ys, NULL, NULL) < 0) if (type_yang2asn1(ys, NULL) < 0)
goto done; goto done;
#endif #endif
/* Get modes (access) read-only, read-write, not-accessible, oaccessible-for-notify /* Get modes (access) read-only, read-write, not-accessible, oaccessible-for-notify

View file

@ -72,6 +72,7 @@ int xml2xpath(cxobj *x, cvec *nsc, char **xpath);
int assign_namespace_element(cxobj *x0, cxobj *x1, cxobj *x1p); int assign_namespace_element(cxobj *x0, cxobj *x1, cxobj *x1p);
int assign_namespace_body(cxobj *x0, cxobj *x1); int assign_namespace_body(cxobj *x0, cxobj *x1);
int xml_merge(cxobj *x0, cxobj *x1, yang_stmt *yspec, char **reason); 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 yang_enum_int_value(cxobj *node, int32_t *val);
int xml_copy_marked(cxobj *x0, cxobj *x1); 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); int yang_check_when_xpath(cxobj *xn, cxobj *xp, yang_stmt *yn, int *hit, int *nrp, char **xpathp);

View file

@ -1922,6 +1922,37 @@ xml_merge(cxobj *x0,
goto done; 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 /*! Get integer value from xml node from yang enumeration
* @param[in] node XML node in a tree * @param[in] node XML node in a tree
* @param[out] val Integer value returned * @param[out] val Integer value returned
@ -1944,9 +1975,8 @@ yang_enum_int_value(cxobj *node,
yang_stmt *ys; yang_stmt *ys;
yang_stmt *ytype; yang_stmt *ytype;
yang_stmt *yrestype; /* resolved type */ yang_stmt *yrestype; /* resolved type */
yang_stmt *yenum;
yang_stmt *yval;
char *reason = NULL; char *reason = NULL;
char *intstr = NULL;
if (node == NULL) if (node == NULL)
goto done; goto done;
@ -1965,20 +1995,16 @@ yang_enum_int_value(cxobj *node,
} }
if (yrestype==NULL || strcmp(yang_argument_get(yrestype), "enumeration")) if (yrestype==NULL || strcmp(yang_argument_get(yrestype), "enumeration"))
goto done; goto done;
if ((yenum = yang_find(yrestype, Y_ENUM, xml_body(node))) == NULL) if (yang_enum2intstr(yrestype, xml_body(node), &intstr) < 0)
goto done;
/* Should assign value if yval not found */
if ((yval = yang_find(yenum, Y_VALUE, NULL)) == NULL)
goto done; goto done;
/* reason is string containing why int could not be parsed */ /* 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; goto done;
retval = 0; retval = 0;
done: done:
return retval; return retval;
} }
/*! Given XML tree x0 with marked nodes, copy marked nodes to new tree x1 /*! 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 * Two marks are used: XML_FLAG_MARK and XML_FLAG_CHANGE
* *