SNMP frontend: Generalized type handling for set functions

SPlit scalar_handler into get and set subfunctions
This commit is contained in:
Olof hagsand 2022-05-20 15:02:58 +02:00
parent 34fe439f53
commit ea03363c3a
8 changed files with 285 additions and 398 deletions

View file

@ -118,6 +118,143 @@ done:
return retval; return retval;
} }
/*! Scalar handler, set a value to clixon
* get xpath: see yang2api_path_fmt / api_path2xpath
*/
static int
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;
u_char *snmpval = NULL;
size_t snmplen;
int ret;
if (xml_nsctx_yang(ys, &nsc) < 0)
goto done;
if (yang2xpath(ys, &xpath) < 0)
goto done;
if (clicon_rpc_get(h, xpath, nsc, CONTENT_ALL, -1, &xt) < 0)
goto done;
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);
}
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)
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 <--
*/
/* see snmplib/snmp_client.c */
if (snmp_set_var_value(requestvb,
snmpval,
snmplen) != 0){
clicon_err(OE_SNMP, 0, "snmp_set_var_value");
goto done;
}
ok:
retval = 0;
done:
if (snmpval)
free(snmpval);
if (xt)
xml_free(xt);
if (xpath)
free(xpath);
if (nsc)
xml_nsctx_free(nsc);
return retval;
}
/*! Scalar handler, get a value from clixon
*/
static int
snmp_scalar_set(clicon_handle h,
yang_stmt *ys,
netsnmp_variable_list *requestvb,
netsnmp_agent_request_info *reqinfo,
netsnmp_request_info *requests)
{
int retval = -1;
char *api_path = NULL;
cxobj *xtop = NULL;
cxobj *xbot = NULL;
cxobj *xb;
yang_stmt *yspec;
int ret;
char *valstr = NULL;
cbuf *cb = NULL;
if ((yspec = clicon_dbspec_yang(h)) == NULL){
clicon_err(OE_FATAL, 0, "No DB_SPEC");
goto done;
}
if ((xtop = xml_new(NETCONF_INPUT_CONFIG, NULL, CX_ELMNT)) == NULL)
goto done;
if (yang2api_path_fmt(ys, 0, &api_path) < 0)
goto done;
if ((ret = api_path2xml(api_path, yspec, xtop, YC_DATANODE, 1, &xbot, NULL, NULL)) < 0)
goto done;
if (ret == 0){
clicon_err(OE_XML, 0, "api_path2xml %s invalid", api_path);
goto done;
}
if ((xb = xml_new("body", xbot, CX_BODY)) == NULL)
goto done;
if ((ret = type_snmp2yang(requestvb, reqinfo, requests, &valstr)) < 0)
goto done;
if (ret == 0)
goto ok;
if (xml_value_set(xb, valstr) < 0)
goto done;
if ((cb = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new");
goto done;
}
if (clicon_xml2cbuf(cb, xtop, 0, 0, -1) < 0)
goto done;
if (clicon_rpc_edit_config(h, "candidate", OP_MERGE, cbuf_get(cb)) < 0)
goto done;
ok:
retval = 0;
done:
if (cb)
cbuf_free(cb);
if (xtop)
xml_free(xtop);
if (valstr)
free(valstr);
return retval;
}
/*! SNMP Scalar operation handler /*! SNMP Scalar operation handler
* Calls order: READ:160, * Calls order: READ:160,
@ -131,24 +268,12 @@ snmp_scalar_handler(netsnmp_mib_handler *handler,
netsnmp_agent_request_info *reqinfo, netsnmp_agent_request_info *reqinfo,
netsnmp_request_info *requests) netsnmp_request_info *requests)
{ {
int retval = -1; int retval = -1;
clixon_snmp_handle *sh; clixon_snmp_handle *sh;
yang_stmt *ys; yang_stmt *ys;
clicon_handle h; int asn1_type;
cg_var *cv = NULL;
cxobj *xt = NULL;
cxobj *xerr;
cvec *nsc = NULL;
cxobj *x;
char *xpath = NULL;
int asn1_type;
enum cv_type cvtype;
char *valstr;
u_char *snmpval = NULL;
size_t snmplen;
int ret;
netsnmp_variable_list *requestvb; /* sub of requests */ netsnmp_variable_list *requestvb; /* sub of requests */
cbuf *cb = NULL; 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
@ -174,7 +299,6 @@ snmp_scalar_handler(netsnmp_mib_handler *handler,
requests->inclusive); requests->inclusive);
sh = (clixon_snmp_handle*)nhreg->my_reg_void; sh = (clixon_snmp_handle*)nhreg->my_reg_void;
ys = sh->sh_ys; ys = sh->sh_ys;
h = sh->sh_h;
// fprint_objid(stderr, nhreg->rootoid, nhreg->rootoid_len); // fprint_objid(stderr, nhreg->rootoid, nhreg->rootoid_len);
assert(sh->sh_oidlen == requestvb->name_length); assert(sh->sh_oidlen == requestvb->name_length);
assert(requestvb->name_length == nhreg->rootoid_len); assert(requestvb->name_length == nhreg->rootoid_len);
@ -188,123 +312,43 @@ 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) if (yang2snmp_types(ys, &asn1_type, &cvtype) < 0)
goto done; 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 requestvb->type = asn1_type; // ASN_NULL on input
if (snmp_scalar_get(sh->sh_h, ys, requestvb, sh->sh_default, cvtype, reqinfo, requests) < 0)
/* get xpath: see yang2api_path_fmt / api_path2xpath
New fn: yang2xpath?
clicon_rpc_get()
<rpc $DEFAULTNS><get><filter type=\"xpath\" select=\"/if:interfaces\" xmlns:if=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\" /></get></rpc>"
*/
if (xml_nsctx_yang(ys, &nsc) < 0)
goto done; goto done;
if (yang2xpath(ys, &xpath) < 0)
goto done;
if (clicon_rpc_get(h, xpath, nsc, CONTENT_ALL, -1, &xt) < 0)
goto done;
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);
}
else if ((valstr = sh->sh_default) != NULL)
;
else{
netsnmp_set_request_error(reqinfo, requests, SNMP_NOSUCHINSTANCE);
goto ok;
}
if ((ret = type_yang2snmp(valstr, cvtype, reqinfo, requests, &snmpval, &snmplen)) < 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 <--
*/
/* see snmplib/snmp_client.c */
if (snmp_set_var_value(requestvb,
snmpval,
snmplen) != 0){
clicon_err(OE_SNMP, 0, "snmp_set_var_value");
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 */
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);
break; break;
case MODE_SET_RESERVE2: /* 1 */
case MODE_SET_RESERVE2: // 1
break; break;
case MODE_SET_ACTION: /* 2 */
case MODE_SET_ACTION: // 2 if (snmp_scalar_set(sh->sh_h, ys, requestvb, reqinfo, requests) < 0)
/*
* update current
*/
/* yang2xpath -> xpath2xml
* accesses = *(requestvb->val.integer);
* rpc edit-config
<data></data>
*/
if ((cb = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new");
goto done; goto done;
} break;
/*! XXX only int */ case MODE_SET_UNDO: /* 5 */
cprintf(cb, "<config><NET-SNMP-EXAMPLES-MIB xmlns=\"urn:ietf:params:xml:ns:yang:smiv2:NET-SNMP-EXAMPLES-MIB\"><netSnmpExampleScalars><netSnmpExampleInteger>%ld</netSnmpExampleInteger></netSnmpExampleScalars></NET-SNMP-EXAMPLES-MIB></config>", *requestvb->val.integer); if (clicon_rpc_discard_changes(sh->sh_h) < 0)
if (clicon_rpc_edit_config(h, "candidate", OP_MERGE, cbuf_get(cb)) < 0)
goto done; goto done;
break; break;
case MODE_SET_UNDO: // 5 case MODE_SET_COMMIT: /* 3 */
if (clicon_rpc_discard_changes(h) < 0) if (clicon_rpc_commit(sh->sh_h) < 0)
goto done;
break;
case MODE_SET_COMMIT: // 3
if (clicon_rpc_commit(h) < 0)
goto done; goto done;
break; break;
case MODE_SET_FREE: // 4 case MODE_SET_FREE: /* 4 */
/*
* nothing to do
*/
break; break;
} }
ok:
retval = SNMP_ERR_NOERROR; retval = SNMP_ERR_NOERROR;
done: done:
if (snmpval)
free(snmpval);
if (cb)
cbuf_free(cb);
if (xpath)
free(xpath);
if (xt)
xml_free(xt);
if (nsc)
xml_nsctx_free(nsc);
if (cv)
cv_free(cv);
return retval; return retval;
} }

View file

@ -54,8 +54,9 @@
#include <pwd.h> #include <pwd.h>
#include <syslog.h> #include <syslog.h>
#include <errno.h> #include <errno.h>
#include <sys/types.h>
#include <signal.h> #include <signal.h>
#include <assert.h>
#include <sys/types.h>
/* net-snmp */ /* net-snmp */
#include <net-snmp/net-snmp-config.h> #include <net-snmp/net-snmp-config.h>
@ -91,16 +92,19 @@ static const map_str2int snmp_access_map[] = {
/* Map between clixon and ASN.1 types. /* Map between clixon and ASN.1 types.
* @see net-snmp/library/asn1.h * @see net-snmp/library/asn1.h
* @see union netsnmp_vardata in net-snmp/types.h * @see union netsnmp_vardata in net-snmp/types.h
* XXX not complete
* XXX TimeTicks
*/ */
static const map_str2int snmp_type_map[] = { static const map_str2int snmp_type_map[] = {
{"bool", ASN_BOOLEAN},
{"int32", ASN_INTEGER}, {"int32", ASN_INTEGER},
{"bits", ASN_BIT_STR},
{"string", ASN_OCTET_STR}, {"string", ASN_OCTET_STR},
{"empty", ASN_NULL}, // {"bool", ASN_BOOLEAN},
//XXX {"", ASN_OBJECT_ID}, // {"empty", ASN_NULL},
// XXX {"", ASN_SEQUENCE}, // {"bits", ASN_BIT_STR},
// XXX {"", ASN_SET}, // {"", ASN_OBJECT_ID},
// {"", ASN_SEQUENCE},
// {"", ASN_SET},
{NULL, -1} {NULL, -1}
}; };
@ -130,7 +134,13 @@ 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)
*
* @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
*/ */
int int
yang2snmp_types(yang_stmt *ys, yang2snmp_types(yang_stmt *ys,
@ -141,31 +151,38 @@ yang2snmp_types(yang_stmt *ys,
yang_stmt *yrestype; /* resolved type */ yang_stmt *yrestype; /* resolved type */
char *restype; /* resolved type */ char *restype; /* resolved type */
char *origtype=NULL; /* original type */ char *origtype=NULL; /* original type */
int at;
/* Get yang type of leaf and trasnslate to ASN.1 */ /* Get yang type of leaf and trasnslate to ASN.1 */
if (yang_type_get(ys, &origtype, &yrestype, NULL, NULL, NULL, NULL, NULL) < 0) if (yang_type_get(ys, &origtype, &yrestype, NULL, NULL, NULL, NULL, NULL) < 0)
goto done; goto done;
restype = yrestype?yang_argument_get(yrestype):NULL; restype = yrestype?yang_argument_get(yrestype):NULL;
if (clicon_type2cv(origtype, restype, ys, cvtype) < 0)
goto done;
/* translate to asn.1 */ /* translate to asn.1 */
*asn1_type = clicon_str2int(snmp_type_map, restype); 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;
}
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); clicon_debug(1, "%s type:%s", __FUNCTION__, restype);
retval = 0; retval = 0;
done: done:
return retval; return retval;
} }
/*! Translate from yang/xml/clixon to SNMP/ASN.1 /*! Translate from yang/xml/clixon to SNMP/ASN.1
** *
* The tran
* @param[in] valstr Clixon/yang/xml string value * @param[in] valstr Clixon/yang/xml string value
* @param[in] cvtype Type of clixon type * @param[in] cvtype Type of clixon type
* @param[in] reqinfo snmpd API struct for error
* @param[in] requests snmpd API struct for error
* @param[out] snmpval Malloc:ed snmp type * @param[out] snmpval Malloc:ed snmp type
* @param[out] snmplen Length of snmp type * @param[out] snmplen Length of snmp type
* @retval 1 OK * @retval 1 OK
* @retval 0 Invalid value * @retval 0 Invalid value or type
* @retval -1 Error * @retval -1 Error
*/ */
int int
@ -216,6 +233,7 @@ type_yang2snmp(char *valstr,
break; break;
} }
default: default:
assert(0); // XXX
clicon_debug(1, "%s %s not supported", __FUNCTION__, cv_type2str(cvtype)); clicon_debug(1, "%s %s not supported", __FUNCTION__, cv_type2str(cvtype));
netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_WRONGTYPE); netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_WRONGTYPE);
goto fail; goto fail;
@ -231,6 +249,66 @@ type_yang2snmp(char *valstr,
retval = 0; retval = 0;
goto done; goto done;
} }
/*! Translate from yang/xml/clixon to SNMP/ASN.1
*
* @param[in] snmpval Malloc:ed snmp type
* @param[in] snmplen Length of snmp type
* @param[in] reqinfo snmpd API struct for error
* @param[in] requests snmpd API struct for error
* @param[out] valstr Clixon/yang/xml string value, free after use)
* @retval 1 OK, and valstr set
* @retval 0 Invalid value or type
* @retval -1 Error
*/
int
type_snmp2yang(netsnmp_variable_list *requestvb,
netsnmp_agent_request_info *reqinfo,
netsnmp_request_info *requests,
char **valstr)
{
int retval = -1;
char *cvtypestr;
enum cv_type cvtype;
cg_var *cv;
clicon_debug(1, "%s", __FUNCTION__);
if (valstr == NULL){
clicon_err(OE_UNIX, EINVAL, "valstr is NULL");
goto done;
}
cvtypestr = (char*)clicon_int2str(snmp_type_map, requestvb->type);
cvtype = cv_str2type(cvtypestr);
if ((cv = cv_new(cvtype)) == NULL){
clicon_err(OE_UNIX, errno, "cv_new");
goto done;
}
switch (requestvb->type){
case ASN_BOOLEAN:
case ASN_INTEGER:
cv_int32_set(cv, *requestvb->val.integer);
break;
case ASN_OCTET_STR:
cv_string_set(cv, (char*)requestvb->val.string);
break;
default:
assert(0); // XXX
clicon_debug(1, "%s %s not supported", __FUNCTION__, cv_type2str(cvtype));
netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_WRONGTYPE);
goto fail;
break;
}
if ((*valstr = cv2str_dup(cv)) == NULL){
clicon_err(OE_UNIX, errno, "cv2str_dup");
goto done;
}
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.
@ -263,9 +341,10 @@ yang2xpath_cb(yang_stmt *ys,
cprintf(cb, "/"); cprintf(cb, "/");
} }
} }
cprintf(cb, "%s:", yang_find_myprefix(ys)); if (yang_keyword_get(ys) != Y_CHOICE && yang_keyword_get(ys) != Y_CASE){
if (yang_keyword_get(ys) != Y_CHOICE && yang_keyword_get(ys) != Y_CASE) cprintf(cb, "%s:", yang_find_myprefix(ys));
cprintf(cb, "%s", yang_argument_get(ys)); cprintf(cb, "%s", yang_argument_get(ys));
}
switch (yang_keyword_get(ys)){ switch (yang_keyword_get(ys)){
case Y_LIST: // XXX not xpaths case Y_LIST: // XXX not xpaths
cvk = yang_cvec_get(ys); /* Use Y_LIST cache, see ys_populate_list() */ cvk = yang_cvec_get(ys); /* Use Y_LIST cache, see ys_populate_list() */
@ -290,7 +369,6 @@ yang2xpath_cb(yang_stmt *ys,
} }
/*! Construct an xpath from yang statement /*! Construct an xpath from yang statement
* Recursively construct it to the top. * Recursively construct it to the top.
* @param[in] ys Yang statement * @param[in] ys Yang statement
* @param[out] xpath Malloced xpath string, use free() after use * @param[out] xpath Malloced xpath string, use free() after use

View file

@ -64,6 +64,10 @@ int yang2snmp_types(yang_stmt *ys, int *asn1_type, enum cv_type *cvtype);
int type_yang2snmp(char *valstr, enum cv_type cvtype, int type_yang2snmp(char *valstr, enum cv_type cvtype,
netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests, netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests,
u_char **snmpval, size_t *snmplen); 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 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

@ -448,8 +448,8 @@ main(int argc,
/* Init snmp as subagent */ /* Init snmp as subagent */
if (clixon_snmp_subagent(h, logdst) < 0) if (clixon_snmp_subagent(h, logdst) < 0)
goto done; goto done;
/* Init mib-translated yangs and register callbacks */ /* Init and traverse mib-translated yangs and register callbacks */
if (clixon_snmp_mib_yangs(h) < 0) if (clixon_snmp_traverse_mibyangs(h) < 0)
goto done; goto done;
if (dbg) if (dbg)

View file

@ -81,258 +81,6 @@
#define IETF_YANG_SMIV2_NS "urn:ietf:params:xml:ns:yang:ietf-yang-smiv2" #define IETF_YANG_SMIV2_NS "urn:ietf:params:xml:ns:yang:ietf-yang-smiv2"
#if 0
/*! SNMP table operation handlre
* Callorder: 161,160,.... 0, 1,2,3, 160,161,...
* see https://net-snmp.sourceforge.io/dev/agent/data_set_8c-example.html#_a0
*/
int
snmp_table_handler(netsnmp_mib_handler *handler,
netsnmp_handler_registration *nhreg,
netsnmp_agent_request_info *reqinfo,
netsnmp_request_info *requests)
{
int retval = SNMP_ERR_GENERR;
clixon_snmp_handle *sh;
netsnmp_table_data_set *table;
yang_stmt *ys;
clicon_handle h;
yang_stmt *ylist;
cvec *nsc = NULL;
cxobj *xt = NULL;
cbuf *cb = NULL;
clicon_debug(1, "%s %s %s", __FUNCTION__,
handler->handler_name,
snmp_msg_int2str(reqinfo->mode));
sh = (clixon_snmp_handle*)nhreg->my_reg_void;
ys = sh->sh_ys;
h = sh->sh_h;
table = sh->sh_table;
if ((ylist = yang_find(ys, Y_LIST, NULL)) == NULL)
goto ok;
if (clixon_table_create(table, ys, h) < 0)
goto done;
switch(reqinfo->mode){
case MODE_GETNEXT: // 160
break;
case MODE_GET: // 160
case MODE_SET_RESERVE1:
case MODE_SET_RESERVE2:
case MODE_SET_ACTION:
case MODE_SET_COMMIT:
break;
}
ok:
retval = SNMP_ERR_NOERROR;
done:
if (xt)
xml_free(xt);
if (cb)
cbuf_free(cb);
if (nsc)
xml_nsctx_free(nsc);
return retval;
}
/*! SNMP Scalar operation handler
* Calls order: READ:160,
* WRITE: 0, 1, 2, 3,
* MODE_SET_RESERVE1, MODE_SET_RESERVE2, MODE_SET_ACTION, MODE_SET_COMMIT
*
*/
int
snmp_scalar_handler(netsnmp_mib_handler *handler,
netsnmp_handler_registration *nhreg,
netsnmp_agent_request_info *reqinfo,
netsnmp_request_info *requests)
{
int retval = -1;
clixon_snmp_handle *sh;
yang_stmt *ys;
clicon_handle h;
cg_var *cv = NULL;
cxobj *xt = NULL;
cxobj *xerr;
cvec *nsc = NULL;
cxobj *x;
char *xpath = NULL;
int asn1_type;
enum cv_type cvtype;
char *valstr;
u_char *snmpval = NULL;
size_t snmplen;
int ret;
netsnmp_variable_list *requestvb; /* sub of requests */
cbuf *cb = NULL;
/*
* can be used to pass information on a per-pdu basis from a
* helper to the later handlers
netsnmp_agent_request_info *reqinfo,
netsnmp_data_list *agent_data;
netsnmp_free_agent_data_set()
*/
requestvb = requests->requestvb;
if (0)
fprintf(stderr, "%s %s %s\n", __FUNCTION__,
handler->handler_name,
snmp_msg_int2str(reqinfo->mode)
);
if (0)
fprintf(stderr, "inclusive:%d\n",
requests->inclusive
);
clicon_debug(1, "%s %s %s %d", __FUNCTION__,
handler->handler_name,
snmp_msg_int2str(reqinfo->mode),
requests->inclusive);
sh = (clixon_snmp_handle*)nhreg->my_reg_void;
ys = sh->sh_ys;
h = sh->sh_h;
// fprint_objid(stderr, nhreg->rootoid, nhreg->rootoid_len);
assert(sh->sh_oidlen == requestvb->name_length);
assert(requestvb->name_length == nhreg->rootoid_len);
assert(snmp_oid_compare(sh->sh_oid, sh->sh_oidlen,
requestvb->name, requestvb->name_length) == 0);
assert(snmp_oid_compare(requestvb->name, requestvb->name_length,
nhreg->rootoid, nhreg->rootoid_len) == 0);
#if 0 /* If oid match fails */
netsnmp_set_request_error(reqinfo, requests,
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
/* get xpath: see yang2api_path_fmt / api_path2xpath
New fn: yang2xpath?
clicon_rpc_get()
<rpc $DEFAULTNS><get><filter type=\"xpath\" select=\"/if:interfaces\" xmlns:if=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\" /></get></rpc>"
*/
if (xml_nsctx_yang(ys, &nsc) < 0)
goto done;
if (yang2xpath(ys, &xpath) < 0)
goto done;
if (clicon_rpc_get(h, xpath, nsc, CONTENT_ALL, -1, &xt) < 0)
goto done;
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);
}
else if ((valstr = sh->sh_default) != NULL)
;
else{
netsnmp_set_request_error(reqinfo, requests, SNMP_NOSUCHINSTANCE);
goto ok;
}
if ((ret = type_yang2snmp(valstr, cvtype, reqinfo, requests, &snmpval, &snmplen)) < 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 <--
*/
/* see snmplib/snmp_client.c */
if (snmp_set_var_value(requestvb,
snmpval,
snmplen) != 0){
clicon_err(OE_SNMP, 0, "snmp_set_var_value");
goto done;
}
break;
case MODE_GETNEXT: // 161
assert(0); // Not seen?
break;
case MODE_SET_RESERVE1: // 0
if (requestvb->type != asn1_type)
netsnmp_set_request_error(reqinfo, requests,
SNMP_ERR_WRONGTYPE);
break;
case MODE_SET_RESERVE2: // 1
break;
case MODE_SET_ACTION: // 2
/*
* update current
*/
/* yang2xpath -> xpath2xml
* accesses = *(requestvb->val.integer);
* rpc edit-config
<data></data>
*/
if ((cb = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new");
goto done;
}
/*! XXX only int */
cprintf(cb, "<config><NET-SNMP-EXAMPLES-MIB xmlns=\"urn:ietf:params:xml:ns:yang:smiv2:NET-SNMP-EXAMPLES-MIB\"><netSnmpExampleScalars><netSnmpExampleInteger>%ld</netSnmpExampleInteger></netSnmpExampleScalars></NET-SNMP-EXAMPLES-MIB></config>", *requestvb->val.integer);
if (clicon_rpc_edit_config(h, "candidate", OP_MERGE, cbuf_get(cb)) < 0)
goto done;
break;
case MODE_SET_UNDO: // 5
if (clicon_rpc_discard_changes(h) < 0)
goto done;
break;
case MODE_SET_COMMIT: // 3
if (clicon_rpc_commit(h) < 0)
goto done;
break;
case MODE_SET_FREE: // 4
/*
* nothing to do
*/
break;
}
ok:
retval = SNMP_ERR_NOERROR;
done:
if (snmpval)
free(snmpval);
if (cb)
cbuf_free(cb);
if (xpath)
free(xpath);
if (xt)
xml_free(xt);
if (nsc)
xml_nsctx_free(nsc);
if (cv)
cv_free(cv);
return retval;
}
#endif
/*! Parse smiv2 extensions for YANG container/list /*! Parse smiv2 extensions for YANG container/list
* *
* Typical table: * Typical table:
@ -457,7 +205,10 @@ 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 (yang2snmp_types(ys, NULL, NULL) < 0)
goto done;
#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
*/ */
if (modes_str == NULL) if (modes_str == NULL)
@ -489,10 +240,8 @@ mib_yang_leaf(clicon_handle h,
memcpy(sh->sh_oid, oid1, sizeof(oid1)); memcpy(sh->sh_oid, oid1, sizeof(oid1));
sh->sh_oidlen = sz1; sh->sh_oidlen = sz1;
sh->sh_default = default_str; sh->sh_default = default_str;
if ((nhreg = netsnmp_handler_registration_create(name, if ((nhreg = netsnmp_handler_registration_create(name, handler,
handler, oid1, sz1,
oid1,
sz1,
modes)) == NULL){ modes)) == NULL){
clicon_err(OE_SNMP, errno, "netsnmp_handler_registration_create"); clicon_err(OE_SNMP, errno, "netsnmp_handler_registration_create");
netsnmp_handler_free(handler); netsnmp_handler_free(handler);
@ -587,7 +336,7 @@ mib_traverse(clicon_handle h,
* @retval -1 Error * @retval -1 Error
*/ */
int int
clixon_snmp_mib_yangs(clicon_handle h) clixon_snmp_traverse_mibyangs(clicon_handle h)
{ {
int retval = -1; int retval = -1;
char *modname; char *modname;

View file

@ -43,7 +43,7 @@ extern "C" {
/* /*
* Prototypes * Prototypes
*/ */
int clixon_snmp_mib_yangs(clicon_handle h); int clixon_snmp_traverse_mibyangs(clicon_handle h);
#endif /* _SNMP_REGISTER_H_ */ #endif /* _SNMP_REGISTER_H_ */

View file

@ -386,6 +386,8 @@ yang2api_path_fmt_1(yang_stmt *ys,
* @param[in] ys Yang statement * @param[in] ys Yang statement
* @param[in] inclkey If set include key leaf (eg last leaf d in ex) * @param[in] inclkey If set include key leaf (eg last leaf d in ex)
* @param[out] api_path_fmt XML api path. Needs to be freed after use. * @param[out] api_path_fmt XML api path. Needs to be freed after use.
* @retval 0 OK
* @retval -1 Error
* "api-path" is "URI-encoded path expression" definition in RFC8040 3.5.3 * "api-path" is "URI-encoded path expression" definition in RFC8040 3.5.3
*/ */
int int
@ -902,7 +904,7 @@ api_path2xpath(char *api_path,
* @param[in] nodeclass Set to schema nodes, data nodes, etc * @param[in] nodeclass Set to schema nodes, data nodes, etc
* @param[in] strict Break if api-path is not "complete" otherwise ignore and continue * @param[in] strict Break if api-path is not "complete" otherwise ignore and continue
* @param[out] xbotp Resulting xml tree * @param[out] xbotp Resulting xml tree
* @param[out] ybotp Yang spec matching xpathp * @param[out] ybotp Yang spec matching xpathp
* @param[out] xerr Netconf error message (if retval=0) * @param[out] xerr Netconf error message (if retval=0)
* @retval 1 OK * @retval 1 OK
* @retval 0 Invalid api_path or associated XML, netconf error * @retval 0 Invalid api_path or associated XML, netconf error
@ -1144,7 +1146,7 @@ api_path2xml_vec(char **vec,
* @param[in,out] xtop Incoming XML tree * @param[in,out] xtop Incoming XML tree
* @param[in] nodeclass Set to schema nodes, data nodes, etc * @param[in] nodeclass Set to schema nodes, data nodes, etc
* @param[in] strict Break if api-path is not "complete" otherwise ignore and continue * @param[in] strict Break if api-path is not "complete" otherwise ignore and continue
* @param[out] xbotp Resulting xml tree (end of xpath) * @param[out] xbotp Resulting xml tree (end of xpath) (optional)
* @param[out] ybotp Yang spec matching xbotp * @param[out] ybotp Yang spec matching xbotp
* @param[out] xerr Netconf error message (if retval=0) * @param[out] xerr Netconf error message (if retval=0)
* @retval 1 OK * @retval 1 OK

View file

@ -104,16 +104,17 @@ testinit
# NET-SNMP-EXAMPLES-MIB::netSnmpExamples # NET-SNMP-EXAMPLES-MIB::netSnmpExamples
MIB=".1.3.6.1.4.1.8072.2" MIB=".1.3.6.1.4.1.8072.2"
OID="${MIB}.1.1" # netSnmpExampleInteger OID1="${MIB}.1.1" # netSnmpExampleInteger
OID3="${MIB}.1.3" # netSnmpExampleString
new "Test SNMP get for default value" new "Test SNMP get for default value"
expectpart "$($snmpget $OID)" 0 "$OID = INTEGER: 42" expectpart "$($snmpget $OID1)" 0 "$OID1 = INTEGER: 42"
new "Set new value to OID" new "Set new value to OID1"
expectpart "$($snmpset $OID i 1234)" 0 "$OID = INTEGER: 1234" expectpart "$($snmpset $OID1 i 1234)" 0 "$OID1 = INTEGER: 1234"
new "Get new value" new "Get new value"
expectpart "$($snmpget $OID)" 0 "$OID = INTEGER: 1234" expectpart "$($snmpget $OID1)" 0 "$OID1 = INTEGER: 1234"
new "Set new value via NETCONF" new "Set new value via NETCONF"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><edit-config><default-operation>none</default-operation><target><candidate/></target><config><NET-SNMP-EXAMPLES-MIB xmlns=\"urn:ietf:params:xml:ns:yang:smiv2:NET-SNMP-EXAMPLES-MIB\"><netSnmpExampleScalars><netSnmpExampleInteger>999</netSnmpExampleInteger></netSnmpExampleScalars></NET-SNMP-EXAMPLES-MIB></config></edit-config></rpc>" "" "<rpc-reply $DEFAULTNS><ok/></rpc-reply>" expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><edit-config><default-operation>none</default-operation><target><candidate/></target><config><NET-SNMP-EXAMPLES-MIB xmlns=\"urn:ietf:params:xml:ns:yang:smiv2:NET-SNMP-EXAMPLES-MIB\"><netSnmpExampleScalars><netSnmpExampleInteger>999</netSnmpExampleInteger></netSnmpExampleScalars></NET-SNMP-EXAMPLES-MIB></config></edit-config></rpc>" "" "<rpc-reply $DEFAULTNS><ok/></rpc-reply>"
@ -122,7 +123,16 @@ new "netconf commit"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><commit/></rpc>" "" "<rpc-reply $DEFAULTNS><ok/></rpc-reply>" expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><commit/></rpc>" "" "<rpc-reply $DEFAULTNS><ok/></rpc-reply>"
new "Get new value" new "Get new value"
expectpart "$($snmpget $OID)" 0 "$OID = INTEGER: 999" expectpart "$($snmpget $OID1)" 0 "$OID1 = INTEGER: 999"
new "Test SNMP get string for default value"
expectpart "$($snmpget $OID3)" 0 "$OID3 = STRING: So long, and thanks for all the fish!."
new "Set new string value to OID3"
expectpart "$($snmpset $OID3 s foobar)" 0 "$OID3 = STRING: foobar"
new "Get new value"
expectpart "$($snmpget $OID3)" 0 "$OID3 = STRING: foobar"
new "Cleaning up" new "Cleaning up"
testexit testexit