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
parent 31fea9e6c8
commit aa95ead1cc
6 changed files with 252 additions and 72 deletions

View file

@ -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);

View file

@ -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

View file

@ -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);

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)
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

View file

@ -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);

View file

@ -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
*