SNMP: Step towards dynamic tables

This commit is contained in:
Olof hagsand 2022-06-10 09:36:15 +02:00
parent 5d1c78ead5
commit 94c00a2584
5 changed files with 186 additions and 114 deletions

View file

@ -103,75 +103,18 @@ snmp_common_handler(netsnmp_mib_handler *handler,
clicon_debug(1, "%s \"%s\" %s inclusive:%d %s", __FUNCTION__,
oidstr2,
snmp_msg_int2str(reqinfo->mode),
requests->inclusive, tablehandler?"table":"");
requests->inclusive, tablehandler?"table":"scalar");
else
clicon_debug(1, "%s \"%s\"/\"%s\" %s inclusive:%d %s", __FUNCTION__,
oidstr2, oidstr0,
snmp_msg_int2str(reqinfo->mode),
requests->inclusive, tablehandler?"table":"");
requests->inclusive, tablehandler?"table":"scalar");
retval = 0;
done:
return retval;
}
/*! SNMP table operation handler
* Callorder: 161,160,.... 0, 1,2,3, 160,161,...
* see https://net-snmp.sourceforge.io/dev/agent/data_set_8c-example.html#_a0
*
* see table_array.[ch] simplify the task of
* writing a table handler for the net-snmp agent when the data being
* accessed is in an oid sorted form and must be accessed externally.
*
* netsnmp_table_build_oid_from_index()
*
* table_container.[ch]
*
* build_new_oid
*/
int
clixon_snmp_table_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 = NULL;
cvec *nsc = NULL;
cxobj *xt = NULL;
cbuf *cb = NULL;
int ret;
clicon_debug(1, "%s", __FUNCTION__);
if ((ret = snmp_common_handler(handler, nhreg, reqinfo, requests, &sh, 1)) < 0)
goto done;
switch(reqinfo->mode){
case MODE_GETNEXT: // 160
#ifdef SNMP_TABLE_DYNAMIC
/* Register table sub-oid:s of existing entries in clixon */
if (mibyang_table_poll(sh->sh_h, sh->sh_ys) < 0)
goto done;
#endif
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;
}
/*! Scalar handler, set a value to clixon
* get xpath: see yang2api_path_fmt / api_path2xpath
@ -356,18 +299,16 @@ clixon_snmp_scalar_handler(netsnmp_mib_handler *handler,
{
int retval = -1;
clixon_snmp_handle *sh = NULL;
yang_stmt *ys;
int asn1_type;
netsnmp_variable_list *requestvb = requests->requestvb;
clicon_debug(2, "%s", __FUNCTION__);
if (snmp_common_handler(handler, nhreg, reqinfo, requests, &sh, 0) < 0)
goto done;
ys = sh->sh_ys;
/* see net-snmp/agent/snmp_agent.h / net-snmp/library/snmp.h */
switch (reqinfo->mode) {
case MODE_GET: /* 160 */
if (snmp_scalar_get(sh->sh_h, ys, sh->sh_cvk_orig,
if (snmp_scalar_get(sh->sh_h, sh->sh_ys, sh->sh_cvk_orig,
requestvb, sh->sh_default, reqinfo, requests) < 0)
goto done;
break;
@ -376,7 +317,7 @@ clixon_snmp_scalar_handler(netsnmp_mib_handler *handler,
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) < 0)
if (type_yang2asn1(sh->sh_ys, &asn1_type, 0) < 0)
goto done;
if (requestvb->type != asn1_type){
clicon_debug(1, "%s Expected type:%d, got: %d", __FUNCTION__, requestvb->type, asn1_type);
@ -387,7 +328,7 @@ clixon_snmp_scalar_handler(netsnmp_mib_handler *handler,
case MODE_SET_RESERVE2: /* 1 */
break;
case MODE_SET_ACTION: /* 2 */
if (snmp_scalar_set(sh->sh_h, ys, requestvb, reqinfo, requests) < 0)
if (snmp_scalar_set(sh->sh_h, sh->sh_ys, requestvb, reqinfo, requests) < 0)
goto done;
break;
case MODE_SET_UNDO: /* 5 */
@ -406,3 +347,83 @@ clixon_snmp_scalar_handler(netsnmp_mib_handler *handler,
done:
return retval;
}
/*! SNMP table operation handler
* Callorder: 161,160,.... 0, 1,2,3, 160,161,...
* see https://net-snmp.sourceforge.io/dev/agent/data_set_8c-example.html#_a0
*
* see table_array.[ch] simplify the task of
* writing a table handler for the net-snmp agent when the data being
* accessed is in an oid sorted form and must be accessed externally.
*
* netsnmp_table_build_oid_from_index()
*
* table_container.[ch]
*
* build_new_oid
*/
int
clixon_snmp_table_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 = NULL;
cvec *nsc = NULL;
cxobj *xt = NULL;
cbuf *cb = NULL;
int ret;
clicon_debug(2, "%s", __FUNCTION__);
if ((ret = snmp_common_handler(handler, nhreg, reqinfo, requests, &sh, 1)) < 0)
goto done;
if (sh->sh_ys == NULL){
clicon_debug(1, "%s Error table not registered", __FUNCTION__);
goto ok;
}
switch(reqinfo->mode){
case MODE_GET: // 160
#ifdef SNMP_TABLE_DYNAMIC
/* Register table sub-oid:s of existing entries in clixon */
if (mibyang_table_poll(sh->sh_h, sh->sh_ys) < 0)
goto done;
#if 1
{
if ((ret = netsnmp_call_next_handler(handler, nhreg, reqinfo, requests)) < 0){
clicon_err(OE_SNMP, ret, "netsnmp_call_next_handler");
goto done;
}
}
#endif
// Wrong sh, need to make another call
// if (snmp_scalar_get(sh->sh_h, sh->sh_ys, sh->sh_cvk_orig,
// requestvb, sh->sh_default, reqinfo, requests) < 0)
#endif
// Then try and get actual scalar
break;
case MODE_GETNEXT: // 161
#ifdef SNMP_TABLE_DYNAMIC
/* Register table sub-oid:s of existing entries in clixon */
if (mibyang_table_poll(sh->sh_h, sh->sh_ys) < 0)
goto done;
#endif
break;
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;
}

View file

@ -156,6 +156,18 @@ snmp_msg_int2str(int msg)
{
return clicon_int2str(snmp_msg_map, msg);
}
/*! Should be netsnmp lib function, cant find it
*/
int
oid_eq(const oid *objid0,
size_t objid0len,
const oid *objid1,
size_t objid1len)
{
if (objid0len != objid1len)
return 0;
return memcmp(objid0, objid1, objid0len*sizeof(*objid0));
}
/*! Duplicate clixon snmp handler struct
* Use signature of libnetsnmp data_clone field of netsnmp_mib_handler in agent_handler.h
@ -753,45 +765,17 @@ snmp_body2oid(cxobj *xi,
return retval;
}
/*========== libnetsnmp-specific code ===============
* Peeks into internal lib global variables, may be sensitive to library change
*/
/*! Check if netsnmp is connected
* @retval 1 yes, running
* @retval 0 No, not running
* XXX: this peeks into the "main_session" global variable in agent/snmp_agent.c
* Tried to find API function but failed
*/
int
snmp_agent_check(void)
{
extern netsnmp_session *main_session;
return (main_session != NULL) ? 1 : 0;
}
/*! Cleanup remaining libnetsnmb memory
* XXX: this peeks into the "tclist" global variable in snmplib/parse.c
* Tried to find API function but failed
*/
int
snmp_agent_cleanup(void)
{
extern void *tclist;
if (tclist)
free(tclist);
return 0;
}
/* Specialized SNMP error category log/err callback
/*! Specialized SNMP error category log/err callback
*
* This function displays all negative SNMP errors on the form SNMPERR_* that are not SNMPERR_SUCCESS(=0)
* There are also positive SNMP errors on the form SNMP_ERR_* which are not properly handled below
* This function displays all negative SNMP errors on the form SNMPERR_* that are not
* SNMPERR_SUCCESS(=0)
* There are also positive SNMP errors on the form SNMP_ERR_* which are not properly handled
* below
* @param[in] handle Application-specific handle
* @param[in] suberr Application-specific handle, points to SNMP_ERR_* unless < -0x1000 in which
case they are MIB_* errors defined in agent_registry.h
* @param[out] cb Read log/error string into this buffer
* @param[in] suberr Application-specific handle, points to SNMP_ERR_* unless
< CLIXON_ERR_SNMP_MIB in which case they are MIB_* errors defined
in agent_registry.h
* @param[out] cb Read log/error string into this buffer
* @note Some SNMP API functions sometimes returns NULL/ptr or other return values that do not fall into
* this category, then OE_SNMP should NOT be used.
*/
@ -827,3 +811,60 @@ clixon_snmp_err_cb(void *handle,
}
return 0;
}
/*========== libnetsnmp-specific code ===============
* Peeks into internal lib global variables, may be sensitive to library change
*/
/*! Check if netsnmp is connected
* @retval 1 yes, running
* @retval 0 No, not running
* XXX: this peeks into the "main_session" global variable in agent/snmp_agent.c
* Tried to find API function but failed
*/
int
clixon_snmp_api_agent_check(void)
{
extern netsnmp_session *main_session;
return (main_session != NULL) ? 1 : 0;
}
/*! Cleanup remaining libnetsnmb memory
* XXX: this peeks into the "tclist" global variable in snmplib/parse.c
* Tried to find API function but failed
*/
int
clixon_snmp_api_agent_cleanup(void)
{
extern void *tclist;
if (tclist)
free(tclist);
return 0;
}
/*! See if oid is registered
* This is good enough for add,
* But for delete a more advanced function is needed
* @see netsnmp_subtree_load
* @retval -1 Error
* @retval 0 Not found
* @retval 1 Found
*/
int
clixon_snmp_api_oid_find(oid *oid0,
size_t oid0len)
{
int retval = -1;
netsnmp_subtree *tree1 = NULL;
if ((tree1 = netsnmp_subtree_find(oid0, oid0len, NULL, "")) != NULL &&
oid_eq(oid0, oid0len, tree1->name_a, tree1->namelen)){
fprintf(stderr, "%s EQUAL==================\n", __FUNCTION__);
retval = 1;
}
else
retval = 0;
// done:
return retval;
}

View file

@ -67,6 +67,8 @@ typedef struct clixon_snmp_handle clixon_snmp_handle;
/*
* Prototypes
*/
int oid_eq(const oid * objid0, size_t objid0len, const oid * objid1, size_t objid1len);
int snmp_access_str2int(char *modes_str);
const char *snmp_msg_int2str(int msg);
void *snmp_handle_clone(void *arg);
@ -81,10 +83,13 @@ 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);
int snmp_agent_check(void);
int snmp_agent_cleanup(void);
int clixon_snmp_err_cb(void *handle, int suberr, cbuf *cb);
/*========== libnetsnmp-specific code =============== */
int clixon_snmp_api_agent_check(void);
int clixon_snmp_api_agent_cleanup(void);
int clixon_snmp_api_oid_find(oid *oid1, size_t oidlen);
#endif /* _SNMP_LIB_H_ */
#ifdef __cplusplus

View file

@ -110,7 +110,7 @@ snmp_terminate(clicon_handle h)
snmp_shutdown(__FUNCTION__);
shutdown_agent();
snmp_agent_cleanup();
clixon_snmp_api_agent_cleanup();
clicon_rpc_close_session(h);
if ((yspec = clicon_dbspec_yang(h)) != NULL)
ys_free(yspec);
@ -267,7 +267,7 @@ clixon_snmp_init_subagent(clicon_handle h,
/* example-demon will be used to read example-demon.conf files. */
init_snmp(__PROGRAM__);
if (!snmp_agent_check()){
if (!clixon_snmp_api_agent_check()){
clicon_err(OE_DAEMON, 0, "Connection to SNMP agent failed");
goto done;
}

View file

@ -95,6 +95,7 @@
* @retval 0 OK
* @retval -1 Error
* netsnmp_subtree_find(oid1,sz1, 0, 0)
*/
static int
mibyang_leaf_register(clicon_handle h,
@ -110,7 +111,7 @@ mibyang_leaf_register(clicon_handle h,
char *default_str = NULL;
char *oidstr = NULL;
oid oid1[MAX_OID_LEN] = {0,};
size_t sz1 = MAX_OID_LEN;
size_t oid1len = MAX_OID_LEN;
int modes;
char *name;
clixon_snmp_handle *sh;
@ -132,11 +133,14 @@ mibyang_leaf_register(clicon_handle h,
cvi = NULL;
while ((cvi = cvec_each(cvk_oid, cvi)) != NULL)
cprintf(cboid, ".%s", cv_string_get(cvi));
if (snmp_parse_oid(cbuf_get(cboid), oid1, &sz1) == NULL){
if (snmp_parse_oid(cbuf_get(cboid), oid1, &oid1len) == NULL){
clicon_err(OE_XML, 0, "snmp_parse_oid(%s)", cbuf_get(cboid));
// goto done;
goto ok; // XXX skip
}
/* Check if already registered */
if (clixon_snmp_api_oid_find(oid1, oid1len) == 1)
goto ok;
if (yang_extension_value(ys, "max-access", IETF_YANG_SMIV2_NS, NULL, &modes_str) < 0)
goto done;
/* Only for sanity check of types initially to fail early */
@ -155,7 +159,7 @@ mibyang_leaf_register(clicon_handle h,
goto done;
name = yang_argument_get(ys);
/* Stateless function, just returns ptr */
if ((handler = netsnmp_create_handler(name, clixon_snmp_scalar_handler)) == NULL){
clicon_err(OE_XML, errno, "netsnmp_create_handler");
goto done;
@ -172,7 +176,7 @@ mibyang_leaf_register(clicon_handle h,
sh->sh_h = h;
sh->sh_ys = ys;
memcpy(sh->sh_oid, oid1, sizeof(oid1));
sh->sh_oidlen = sz1;
sh->sh_oidlen = oid1len;
sh->sh_default = default_str;
if (cvk_orig &&
(sh->sh_cvk_orig = cvec_dup(cvk_orig)) == NULL){
@ -184,8 +188,9 @@ mibyang_leaf_register(clicon_handle h,
clicon_err(OE_UNIX, errno, "cvec_dup");
goto done;
}
/* Stateless function, just returns ptr */
if ((nhreg = netsnmp_handler_registration_create(name, handler,
oid1, sz1,
oid1, oid1len,
modes)) == NULL){
clicon_err(OE_XML, errno, "netsnmp_handler_registration_create");
netsnmp_handler_free(handler);
@ -238,7 +243,7 @@ mibyang_table_register(clicon_handle h,
netsnmp_handler_registration *nhreg;
char *oidstr = NULL;
oid oid1[MAX_OID_LEN] = {0,};
size_t sz1 = MAX_OID_LEN;
size_t oid1len = MAX_OID_LEN;
char *name;
clixon_snmp_handle *sh;
int ret;
@ -261,7 +266,7 @@ mibyang_table_register(clicon_handle h,
goto done;
if (oidstr == NULL)
goto ok;
if (snmp_parse_oid(oidstr, oid1, &sz1) == NULL){
if (snmp_parse_oid(oidstr, oid1, &oid1len) == NULL){
clicon_err(OE_XML, errno, "snmp_parse_oid");
goto done;
}
@ -279,14 +284,14 @@ mibyang_table_register(clicon_handle h,
sh->sh_ys = ylist;
memcpy(sh->sh_oid, oid1, sizeof(oid1));
sh->sh_oidlen = sz1;
sh->sh_oidlen = oid1len;
if ((handler = netsnmp_create_handler(name, clixon_snmp_table_handler)) == NULL){
clicon_err(OE_XML, errno, "netsnmp_create_handler");
goto done;
}
if ((nhreg = netsnmp_handler_registration_create(name, handler,
oid1, sz1,
oid1, oid1len,
HANDLER_CAN_RWRITE)) == NULL){
clicon_err(OE_XML, errno, "netsnmp_handler_registration_create");
netsnmp_handler_free(handler);