diff --git a/apps/snmp/snmp_handler.c b/apps/snmp/snmp_handler.c index c4723a4b..f3cd9216 100644 --- a/apps/snmp/snmp_handler.c +++ b/apps/snmp/snmp_handler.c @@ -71,50 +71,120 @@ snmp_common_handler(netsnmp_mib_handler *handler, { int retval = -1; netsnmp_variable_list *requestvb; /* sub of requests */ - char oidstr0[MAX_OID_LEN*2] = {0,}; - char oidstr1[MAX_OID_LEN*2] = {0,}; - char oidstr2[MAX_OID_LEN*2] = {0,}; + cbuf *cb; if (requests == NULL || shp == NULL){ clicon_err(OE_XML, EINVAL, "requests or shp is null"); goto done; } requestvb = requests->requestvb; - if (snprint_objid(oidstr0, sizeof(oidstr0), - requestvb->name, requestvb->name_length) < 0){ - clicon_err(OE_XML, 0, "snprint_objid buffer too small"); - goto done; - } if ((*shp = (clixon_snmp_handle*)handler->myvoid) == NULL){ clicon_err(OE_XML, 0, "No myvoid handler"); goto done; } - if (snprint_objid(oidstr1, sizeof(oidstr1), - nhreg->rootoid, nhreg->rootoid_len) < 0){ - clicon_err(OE_XML, 0, "snprint_objid buffer too small"); + if ((cb = cbuf_new()) == NULL){ + clicon_err(OE_UNIX, errno, "cbuf_new"); goto done; } - if (snprint_objid(oidstr2, sizeof(oidstr2), - (*shp)->sh_oid, (*shp)->sh_oidlen) < 0){ - clicon_err(OE_XML, 0, "snprint_objid buffer too small"); - goto done; - } - if (strcmp(oidstr0, oidstr2) == 0) + oid_cbuf(cb, (*shp)->sh_oid, (*shp)->sh_oidlen); + if (oid_eq(requestvb->name, requestvb->name_length, + (*shp)->sh_oid, (*shp)->sh_oidlen) == 0){ /* equal */ clicon_debug(1, "%s \"%s\" %s inclusive:%d %s", __FUNCTION__, - oidstr2, + cbuf_get(cb), snmp_msg_int2str(reqinfo->mode), requests->inclusive, tablehandler?"table":"scalar"); - else - clicon_debug(1, "%s \"%s\"/\"%s\" %s inclusive:%d %s", __FUNCTION__, - oidstr2, oidstr0, + } + else{ /* not equal */ + cprintf(cb, " ("); + oid_cbuf(cb, requestvb->name, requestvb->name_length); + cprintf(cb, ")"); + // nhreg->rootoid same as shp + clicon_debug(1, "%s \"%s\" %s inclusive:%d %s", __FUNCTION__, + cbuf_get(cb), snmp_msg_int2str(reqinfo->mode), requests->inclusive, tablehandler?"table":"scalar"); - + } retval = 0; done: + if (cb) + cbuf_free(cb); return retval; } +#ifdef SNMP_TABLE_DYNAMIC +/*! + */ +static int +snmp_scalar_return(cxobj *xs, + yang_stmt *ys, + oid *oidc, + size_t oidclen, + netsnmp_agent_request_info *reqinfo, + netsnmp_request_info *requests) +{ + int retval = -1; + int asn1type; + char *xmlstr = NULL; + char *defaultval = NULL; + u_char *snmpval = NULL; + size_t snmplen = 0; + char *reason = NULL; + netsnmp_variable_list *requestvb = requests->requestvb; + int ret; + + /* SMI default value, How is this different from yang defaults? + */ + if (yang_extension_value(ys, "defval", IETF_YANG_SMIV2_NS, NULL, &defaultval) < 0) + goto done; + if (xs != NULL){ + if ((ret = type_xml2snmp_pre(xml_body(xs), ys, &xmlstr)) < 0) + goto done; + if (ret == 0){ + netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_WRONGVALUE); + goto ok; + } + } + else if (defaultval != NULL){ + if ((xmlstr = strdup(defaultval)) == NULL){ + clicon_err(OE_UNIX, errno, "strdup"); + goto done; + } + } + else{ + netsnmp_set_request_error(reqinfo, requests, SNMP_NOSUCHINSTANCE); + goto ok; + } + if (type_yang2asn1(ys, &asn1type, 1) < 0) + goto done; + if ((ret = type_xml2snmp(xmlstr, &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 + */ + if ((ret = snmp_set_var_typed_value(requestvb, asn1type, snmpval, snmplen)) != SNMPERR_SUCCESS){ + clicon_err(OE_SNMP, ret, "snmp_set_var_typed_value"); + goto done; + } + if ((ret = snmp_set_var_objid(requestvb, oidc, oidclen)) != SNMPERR_SUCCESS){ + clicon_err(OE_SNMP, ret, "snmp_set_var_objid"); + goto done; + } + ok: + retval = 0; + done: + if (xmlstr) + free(xmlstr); + if (snmpval) + free(snmpval); + if (reason) + free(reason); + return retval; +} +#endif /* SNMP_TABLE_DYNAMIC */ /*! Scalar handler, set a value to clixon * get xpath: see yang2api_path_fmt / api_path2xpath @@ -125,12 +195,13 @@ snmp_common_handler(netsnmp_mib_handler *handler, * @param[in] defaultval * @param[in] reqinfo * @param[in] requests + * @retval 0 OK + * @retval -1 Error */ static int snmp_scalar_get(clicon_handle h, yang_stmt *ys, cvec *cvk, - netsnmp_variable_list *requestvb, char *defaultval, netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests) @@ -147,13 +218,14 @@ snmp_scalar_get(clicon_handle h, int ret; int asn1type; char *reason = NULL; + netsnmp_variable_list *requestvb = requests->requestvb; clicon_debug(1, "%s", __FUNCTION__); /* 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, cvk, &xpath) < 0) + if (snmp_yang2xpath(ys, cvk, &xpath) < 0) goto done; /* Do the backend call */ if (clicon_rpc_get(h, xpath, nsc, CONTENT_ALL, -1, &xt) < 0) @@ -198,9 +270,8 @@ snmp_scalar_get(clicon_handle h, } /* see snmplib/snmp_client. somewhat indirect */ - requestvb->type = asn1type; // ASN_NULL on input - if ((ret = snmp_set_var_value(requestvb, snmpval, snmplen)) != SNMPERR_SUCCESS){ - clicon_err(OE_SNMP, ret, "snmp_set_var_value"); + if ((ret = snmp_set_var_typed_value(requestvb, asn1type, snmpval, snmplen)) != SNMPERR_SUCCESS){ + clicon_err(OE_SNMP, ret, "snmp_set_var_typed_value"); goto done; } ok: @@ -226,7 +297,6 @@ snmp_scalar_get(clicon_handle h, static int snmp_scalar_set(clicon_handle h, yang_stmt *ys, - netsnmp_variable_list *requestvb, netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests) { @@ -239,7 +309,8 @@ snmp_scalar_set(clicon_handle h, int ret; char *valstr = NULL; cbuf *cb = NULL; - + netsnmp_variable_list *requestvb = requests->requestvb; + if ((yspec = clicon_dbspec_yang(h)) == NULL){ clicon_err(OE_FATAL, 0, "No DB_SPEC"); goto done; @@ -250,7 +321,6 @@ snmp_scalar_set(clicon_handle h, 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; @@ -309,7 +379,7 @@ clixon_snmp_scalar_handler(netsnmp_mib_handler *handler, switch (reqinfo->mode) { case MODE_GET: /* 160 */ if (snmp_scalar_get(sh->sh_h, sh->sh_ys, sh->sh_cvk_orig, - requestvb, sh->sh_default, reqinfo, requests) < 0) + sh->sh_default, reqinfo, requests) < 0) goto done; break; case MODE_GETNEXT: /* 161 */ @@ -328,7 +398,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, sh->sh_ys, requestvb, reqinfo, requests) < 0) + if (snmp_scalar_set(sh->sh_h, sh->sh_ys, reqinfo, requests) < 0) goto done; break; case MODE_SET_UNDO: /* 5 */ @@ -348,6 +418,227 @@ clixon_snmp_scalar_handler(netsnmp_mib_handler *handler, return retval; } +#ifdef SNMP_TABLE_DYNAMIC +/*! Create xpath from YANG table OID + 1 + n + cvk/key = requestvb->name + * Get yang of leaf from first part of OID + * Create xpath with right keys from later part of OID + * Query clixon if object exists, if so return value + * @param[in] h Clixon handle + * @param[in] yt Yang of table (of list type) + * @param[in] oids OID of ultimate scalar value + * @param[in] oidslen OID length of scalar + * @param[in] reginfo + * @param[in] requests + * @retval -1 Error + * @retval 0 Object not found + * @retval 1 OK + */ +static int +snmp_table_get(clicon_handle h, + yang_stmt *yt, + oid *oids, + size_t oidslen, + netsnmp_agent_request_info *reqinfo, + netsnmp_request_info *requests) +{ + int retval = -1; + oid oidt[MAX_OID_LEN] = {0,}; /* Table / list oid */ + size_t oidtlen = MAX_OID_LEN; + oid oidleaf[MAX_OID_LEN] = {0,}; /* Leaf */ + size_t oidleaflen = MAX_OID_LEN; + oid *oidi; + size_t oidilen; + yang_stmt *ys; + yang_stmt *yk; + char *xpath = NULL; + cvec *cvk_orig; + cvec *cvk_val; + int i; + cg_var *cv; + char *defaultval = NULL; + int ret; + + /* Get OID from table /list */ + if ((ret = yangext_oid_get(yt, oidt, &oidtlen, NULL)) < 0) + goto done; + if (ret == 0) + goto done; + /* Get yang of leaf from first part of OID */ + ys = NULL; + while ((ys = yn_each(yt, ys)) != NULL) { + if (yang_keyword_get(ys) != Y_LEAF) + continue; + if ((ret = yangext_oid_get(ys, oidleaf, &oidleaflen, NULL)) < 0) + goto done; + if (ret == 0) + goto done; + assert(oidtlen + 1 == oidleaflen); + if (oids[oidleaflen-1] == oidleaf[oidleaflen-1]) + break; + } + if (ys == NULL){ + /* No leaf with matching OID */ + goto fail; + } + /* SMI default value, How is this different from yang defaults? + */ + if (yang_extension_value(ys, "defval", IETF_YANG_SMIV2_NS, NULL, &defaultval) < 0) + goto done; + + /* Create xpath with right keys from later part of OID + * Inverse of snmp_str2oid + */ + if ((cvk_orig = yang_cvec_get(yt)) == NULL){ + clicon_err(OE_YANG, 0, "No keys"); + goto done; + } + if ((cvk_val = cvec_dup(cvk_orig)) == NULL){ + clicon_err(OE_UNIX, errno, "cvec_dup"); + goto done; + } + oidilen = oidslen-(oidtlen+1); + oidi = oids+oidtlen+1; + /* Add keys */ + for (i=0; i 0){ + found++; /* return this */ + break; + } + } /* while xcol */ + if (found) + break; + } /* while xrow */ + } + if (found){ + if (snmp_scalar_return(xcol, ycol, oidc, oidclen, reqinfo, requests) < 0) + goto done; + } + retval = 0; + done: + if (xpath) + free(xpath); + if (xt) + xml_free(xt); + if (nsc) + xml_nsctx_free(nsc); + return retval; +} +#endif /* SNMP_TABLE_DYNAMIC */ + /*! 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 @@ -374,7 +665,10 @@ clixon_snmp_table_handler(netsnmp_mib_handler *handler, cxobj *xt = NULL; cbuf *cb = NULL; int ret; - +#ifdef SNMP_TABLE_DYNAMIC + netsnmp_variable_list *requestvb; +#endif + clicon_debug(2, "%s", __FUNCTION__); if ((ret = snmp_common_handler(handler, nhreg, reqinfo, requests, &sh, 1)) < 0) goto done; @@ -382,30 +676,28 @@ clixon_snmp_table_handler(netsnmp_mib_handler *handler, clicon_debug(1, "%s Error table not registered", __FUNCTION__); goto ok; } +#ifdef SNMP_TABLE_DYNAMIC + requestvb = requests->requestvb; +#endif 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) + /* Create xpath from YANG table OID + 1 + n + cvk/key = requestvb->name + */ + if ((ret = snmp_table_get(sh->sh_h, sh->sh_ys, + requestvb->name, requestvb->name_length, + reqinfo, requests)) < 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; - } - } + if (ret == 0) + netsnmp_set_request_error(reqinfo, requests, SNMP_NOSUCHINSTANCE); #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) + if (snmp_table_getnext(sh->sh_h, sh->sh_ys, + requestvb->name, requestvb->name_length, + reqinfo, requests) < 0) goto done; #endif break; diff --git a/apps/snmp/snmp_lib.c b/apps/snmp/snmp_lib.c index a739f1a1..360dc01c 100644 --- a/apps/snmp/snmp_lib.c +++ b/apps/snmp/snmp_lib.c @@ -156,7 +156,16 @@ snmp_msg_int2str(int msg) { return clicon_int2str(snmp_msg_map, msg); } -/*! Should be netsnmp lib function, cant find it + +/*! Check equality of two OIDs + * + * @param[in] objid0 First OID vector + * @param[in] objid0len Length of first OID vector + * @param[in] objid1 Second OID vector + * @param[in] objid1len Length of second OID vector + * @retval 0 Equal + * @retval !=0 Not equal, see man memcmp + * (Should be netsnmp lib function, cant find it) */ int oid_eq(const oid *objid0, @@ -164,9 +173,183 @@ oid_eq(const oid *objid0, const oid *objid1, size_t objid1len) { - if (objid0len != objid1len) - return 0; - return memcmp(objid0, objid1, objid0len*sizeof(*objid0)); + if (objid0len < objid1len) + return -1; + else if (objid0len > objid1len) + return 1; + else + return memcmp(objid0, objid1, objid0len*sizeof(*objid0)); +} + +/*! Append a second OID to a first + * @param[in,out] objid0 First OID vector + * @param[in,out] objid0len Length of first OID vector + * @param[in] objid1 Second OID vector + * @param[in] objid1len Length of second OID vector + * @retval 0 OK + * @retval -1 Error + * Assume objid0 is allocated with MAX_OID_LEN > oid0len+oid1len + */ +int +oid_append(const oid *objid0, + size_t *objid0len, + const oid *objid1, + size_t objid1len) +{ + void *dst; + + dst = (void*)objid0; + dst += (*objid0len)*sizeof(*objid0); + if (memcpy(dst, objid1, objid1len*sizeof(*objid0)) < 0){ + clicon_err(OE_UNIX, errno, "memcpy"); + return -1; + } + *objid0len += objid1len; + return 0; +} + +/*! Print objid to file + * @see fprint_objid but prints symbolic + */ +int +oid_cbuf(cbuf *cb, + const oid *objid, + size_t objidlen) +{ + size_t i; + + for (i=0; i oidlen + * @param[out] objidlen Length of OID vector on return + * @param[out] objidstrp Pointer to string (direct not malloced) optional + * @retval 1 OK + * @retval 0 Invalid, not found + * @retval -1 Error + */ +int +yangext_oid_get(yang_stmt *yn, + oid *objid, + size_t *objidlen, + char **objidstrp) +{ + int retval = -1; + int exist = 0; + char *oidstr = NULL; + yang_stmt *yref = NULL; + + if (yang_keyword_get(yn) == Y_LEAF){ + if (snmp_yang_type_get(yn, &yref, NULL, NULL, NULL) < 0) + goto done; + } + else + yref = yn; + /* Get OID from table /list */ + if (yang_extension_value(yref, "oid", IETF_YANG_SMIV2_NS, &exist, &oidstr) < 0) + goto done; + if (exist == 0 || oidstr == NULL){ + clicon_debug(1, "OID not found as SMIv2 yang extension of %s", yang_argument_get(yref)); + goto fail; + } + if (snmp_parse_oid(oidstr, objid, objidlen) == NULL){ + clicon_err(OE_XML, errno, "snmp_parse_oid"); + goto done; + } + if (objidstrp) + *objidstrp = oidstr; + retval = 1; + done: + return retval; + fail: + retval = 0; + goto done; } /*! Duplicate clixon snmp handler struct @@ -191,11 +374,6 @@ snmp_handle_clone(void *arg) clicon_err(OE_UNIX, errno, "cvec_dup"); return NULL; } - if (sh0->sh_cvk_oid && - (sh1->sh_cvk_oid = cvec_dup(sh0->sh_cvk_oid)) == NULL){ - clicon_err(OE_UNIX, errno, "cvec_dup"); - return NULL; - } return (void*)sh1; } @@ -211,8 +389,6 @@ snmp_handle_free(void *arg) if (sh != NULL){ if (sh->sh_cvk_orig) cvec_free(sh->sh_cvk_orig); - if (sh->sh_cvk_oid) - cvec_free(sh->sh_cvk_oid); if (sh->sh_table_info){ if (sh->sh_table_info->indexes){ snmp_free_varbind(sh->sh_table_info->indexes); @@ -240,33 +416,13 @@ type_yang2asn1(yang_stmt *ys, int extended) { int retval = -1; - yang_stmt *yrestype; /* resolved type */ char *restype; /* resolved type */ char *origtype = NULL; /* original type */ int at; - yang_stmt *ypath; - yang_stmt *yref; - /* Get yang type of leaf and trasnslate to ASN.1 */ - if (yang_type_get(ys, &origtype, &yrestype, NULL, NULL, NULL, NULL, NULL) < 0) + /* Get yang type of leaf and translate to ASN.1 */ + if (snmp_yang_type_get(ys, NULL, &origtype, NULL, &restype) < 0) goto done; - restype = yrestype?yang_argument_get(yrestype):NULL; - /* Special case: leafref, find original type */ - if (strcmp(restype, "leafref")==0){ - if ((ypath = yang_find(yrestype, Y_PATH, NULL)) == NULL){ - clicon_err(OE_YANG, 0, "No path in leafref"); - goto done; - } - if (yang_path_arg(ys, yang_argument_get(ypath), &yref) < 0) - goto done; - if (origtype){ - free(origtype); - origtype = NULL; - } - if (yang_type_get(yref, &origtype, &yrestype, NULL, NULL, NULL, NULL, NULL) < 0) - goto done; - restype = yrestype?yang_argument_get(yrestype):NULL; - } /* Translate to asn.1 * First try original type, first type */ @@ -312,9 +468,8 @@ type_snmp2xml(yang_stmt *ys, char *cvstr; enum cv_type cvtype; cg_var *cv = NULL; - yang_stmt *yrestype; /* resolved type */ - char *restype; /* resolved type */ - char *origtype = NULL; /* original type */ + char *restype = NULL; /* resolved type */ + yang_stmt *yrestype = NULL; clicon_debug(1, "%s", __FUNCTION__); if (valstr == NULL){ @@ -323,9 +478,8 @@ type_snmp2xml(yang_stmt *ys, } cvstr = (char*)clicon_int2str(snmp_type_map, requestvb->type); /* Get yang type of leaf and trasnslate to ASN.1 */ - if (yang_type_get(ys, &origtype, &yrestype, NULL, NULL, NULL, NULL, NULL) < 0) + if (snmp_yang_type_get(ys, NULL, NULL, &yrestype, &restype) < 0) goto done; - restype = yrestype?yang_argument_get(yrestype):NULL; /* special case for enum */ if (strcmp(cvstr, "int32")==0 && strcmp(restype, "enumeration") == 0) cvstr = "string"; @@ -399,8 +553,6 @@ type_snmp2xml(yang_stmt *ys, clicon_debug(2, "%s %d", __FUNCTION__, retval); if (cv) cv_free(cv); - if (origtype) - free(origtype); return retval; fail: retval = 0; @@ -427,8 +579,7 @@ type_xml2snmp_pre(char *xmlstr0, { int retval = -1; yang_stmt *yrestype; /* resolved type */ - char *restype; /* resolved type */ - char *origtype = NULL; /* original type */ + char *restype = NULL; /* resolved type */ char *str = NULL; int ret; @@ -437,9 +588,8 @@ type_xml2snmp_pre(char *xmlstr0, 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) + if (snmp_yang_type_get(ys, NULL, NULL, &yrestype, &restype) < 0) // XXX yrestype goto done; - restype = yrestype?yang_argument_get(yrestype):NULL; if (strcmp(restype, "enumeration") == 0){ /* special case for enum */ if ((ret = yang_enum2valstr(yrestype, xmlstr0, &str)) < 0) goto done; @@ -469,8 +619,6 @@ type_xml2snmp_pre(char *xmlstr0, retval = 1; done: clicon_debug(2, "%s %d", __FUNCTION__, retval); - if (origtype) - free(origtype); return retval; fail: retval = 0; @@ -608,17 +756,17 @@ type_xml2snmp(char *snmpstr, /*! Construct an xpath from yang statement, internal fn using cb * Recursively construct it to the top. - * @param[in] ys Yang statement - * @param[in] keyvec Array of [name,val]s as a cvec of key name and values - * @param[out] cb xpath as cbuf - * @retval 0 OK - * @retval -1 Error + * @param[in] ys Yang statement + * @param[in] keyvec Cvec of key values + * @param[out] cb xpath as cbuf + * @retval 0 OK + * @retval -1 Error * @see yang2xpath */ static int -yang2xpath_cb(yang_stmt *ys, - cvec *keyvec, - cbuf *cb) +snmp_yang2xpath_cb(yang_stmt *ys, + cvec *keyvec, + cbuf *cb) { yang_stmt *yp; /* parent */ int i; @@ -633,7 +781,7 @@ yang2xpath_cb(yang_stmt *ys, if (yp != NULL && /* XXX rm */ yang_keyword_get(yp) != Y_MODULE && yang_keyword_get(yp) != Y_SUBMODULE){ - if (yang2xpath_cb(yp, keyvec, cb) < 0) /* recursive call */ + if (snmp_yang2xpath_cb(yp, keyvec, cb) < 0) /* recursive call */ goto done; if (yang_keyword_get(yp) != Y_CHOICE && yang_keyword_get(yp) != Y_CASE){ cprintf(cb, "/"); @@ -670,10 +818,10 @@ yang2xpath_cb(yang_stmt *ys, return retval; } -/*! Construct an xpath from yang statement +/*! Construct an xpath from yang statement, limited to SNMP table translations * Recursively construct it to the top. * @param[in] ys Yang statement - * @param[in] keyvec Array of [name,val]s as a cvec of key name and values + * @param[in] keyvec Cvec of key values * @param[out] xpath Malloced xpath string, use free() after use * @retval 0 OK * @retval -1 Error @@ -682,9 +830,9 @@ yang2xpath_cb(yang_stmt *ys, * 2. It is far from complete so maybe keep it here as a special case */ int -yang2xpath(yang_stmt *ys, - cvec *keyvec, - char **xpath) +snmp_yang2xpath(yang_stmt *ys, + cvec *keyvec, + char **xpath) { int retval = -1; cbuf *cb = NULL; @@ -693,7 +841,7 @@ yang2xpath(yang_stmt *ys, clicon_err(OE_UNIX, errno, "cbuf_new"); goto done; } - if (yang2xpath_cb(ys, keyvec, cb) < 0) + if (snmp_yang2xpath_cb(ys, keyvec, cb) < 0) goto done; if (xpath && (*xpath = strdup(cbuf_get(cb))) == NULL){ clicon_err(OE_UNIX, errno, "strdup"); @@ -706,27 +854,27 @@ yang2xpath(yang_stmt *ys, return retval; } -/*! Translate from xml body to OID +/*! Translate from xml body string to SMI OID representation * For ints this is one to one, eg 42 -> 42 - * But for eg strings this is more comples, eg foo -> 3.6.22.22 (or something,...) + * But for eg strings this is more complex, eg foo -> 3.6.22.22 (or something,...) + * @param[in] str XML body string + * @param[in] yi Yang statement + * @param[out] objid OID vector + * @param[out] objidlen Length of OID vector */ int -snmp_body2oid(cxobj *xi, - cg_var *cv) +snmp_str2oid(char *str, + yang_stmt *yi, + oid *objid, + size_t *objidlen) { int retval = -1; - yang_stmt *yi; int asn1_type; - char *body; - size_t len; - cbuf *enc = NULL; int i; + int j = 0; - if ((yi = xml_spec(xi)) == NULL) - goto ok; if (type_yang2asn1(yi, &asn1_type, 0) < 0) goto done; - body = xml_body(xi); switch (asn1_type){ case ASN_INTEGER: case ASN_GAUGE: @@ -734,30 +882,76 @@ snmp_body2oid(cxobj *xi, case ASN_COUNTER64: case ASN_COUNTER: case ASN_IPADDRESS: - if (cv_string_set(cv, body) < 0){ - clicon_err(OE_UNIX, errno, "cv_string_set"); - goto done; - } + objid[j++] = atoi(str); break; case ASN_OCTET_STR:{ /* encode to N.c.c.c.c */ - if ((enc = cbuf_new()) == NULL){ - clicon_err(OE_UNIX, errno, "cbuf_new"); - goto done; - } - len = strlen(body); - cprintf(enc, "%zu", len); - for (i=0; i 42 + * But for eg strings this is more comples, eg foo -> 3.6.22.22 (or something,...) + * @param[in,out] oidi ObjID vector + * @param[in,out] oidilen Length of ObjID vector + * @param[in] yk Yang statement of key + * @param[out] cv CLIgen variable string notation as "x.y.z" + */ +int +snmp_oid2str(oid **oidi, + size_t *oidilen, + yang_stmt *yk, + cg_var *cv) +{ + int retval = -1; + int asn1_type; + int i = 0; + cbuf *enc = NULL; + size_t len; + + if (type_yang2asn1(yk, &asn1_type, 0) < 0) + goto done; + if ((enc = cbuf_new()) == NULL){ + clicon_err(OE_UNIX, errno, "cbuf_new"); + goto done; + } + switch (asn1_type){ + case ASN_INTEGER: + case ASN_GAUGE: + case ASN_TIMETICKS: + case ASN_COUNTER64: + case ASN_COUNTER: + case ASN_IPADDRESS: + cprintf(enc, "%lu", (*oidi)[i++]); + if (cv_string_set(cv, cbuf_get(enc)) < 0){ + clicon_err(OE_UNIX, errno, "cv_string_set"); + goto done; + } + break; + case ASN_OCTET_STR: /* decode from N.c.c.c.c */ + len = (*oidi)[i++]; + for (; i1a... + * where i and j are list keys (table indexes) + * Return two vectors: + * - cvk_val: A vector of key values: [1,a] + * - cvk_oid: A vector of OIDs: [1, 1.97] + * + * @param[in] xentry XML list entry + * @param[in] cvk_name Vector of list keys + * @param[out] cvk_val Vector of XML key values + * @param[out] objidk OID key part, to be appended to node OID + * @retval -1 Error + * @retval 0 Invalid (not all indexes present) + * @retval 1 OK + * Both cvk_val and cvk_oid can be re-used in successive calls but need to be freed w cvec_free after use + */ +int +snmp_xmlkey2val_oid(cxobj *xentry, + cvec *cvk_name, + cvec **cvk_val, + oid *objidk, + size_t *objidklen) +{ + int retval = -1; + cxobj *xi; + int i; + cg_var *cv; + cg_var *cv0; + oid objid[MAX_OID_LEN] = {0,}; + size_t objidlen = MAX_OID_LEN; + + *objidklen = 0; + if (cvk_val){ + if (*cvk_val){ + cvec_free(*cvk_val); + if ((*cvk_val = cvec_dup(cvk_name)) == NULL){ + clicon_err(OE_UNIX, errno, "cvec_dup"); + goto done; + } + } + else if ((*cvk_val = cvec_dup(cvk_name)) == NULL){ + clicon_err(OE_UNIX, errno, "cvec_dup"); + goto done; + } + } + for (i=0; iname_a, tree1->namelen)){ - fprintf(stderr, "%s EQUAL==================\n", __FUNCTION__); + oid_eq(oid0, oid0len, tree1->name_a, tree1->namelen)==0){ retval = 1; } else @@ -868,3 +1134,4 @@ clixon_snmp_api_oid_find(oid *oid0, // done: return retval; } + diff --git a/apps/snmp/snmp_lib.h b/apps/snmp/snmp_lib.h index 8b53a907..ae840ae9 100644 --- a/apps/snmp/snmp_lib.h +++ b/apps/snmp/snmp_lib.h @@ -47,6 +47,8 @@ extern "C" { /* Need some way to multiplex SNMP_ and MIB errors on OE_SNMP error handler */ #define CLIXON_ERR_SNMP_MIB 0x1000 +#define IETF_YANG_SMIV2_NS "urn:ietf:params:xml:ns:yang:ietf-yang-smiv2" + /* * Types */ @@ -58,17 +60,21 @@ struct clixon_snmp_handle { oid sh_oid[MAX_OID_LEN]; /* OID for debug, may be removed? */ size_t sh_oidlen; char *sh_default; /* MIB default value leaf only */ - cvec *sh_cvk_orig; /* Index/Key variables (original) */ - cvec *sh_cvk_oid; /* Index/Key variables (OID translated) */ - netsnmp_table_registration_info *sh_table_info; /* To mimic table-handler in libnetsnmp code*/ + cvec *sh_cvk_orig; /* Index/Key variable values (original) */ + netsnmp_table_registration_info *sh_table_info; /* To mimic table-handler in libnetsnmp code + * save only to free properly */ + }; 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 oid_append(const oid *objid0, size_t *objid0len, const oid *objid1, size_t objid1len); +int oid_cbuf(cbuf *cb, const oid *objid, size_t objidlen); +int oid_print(FILE *f, const oid *objid, size_t objidlen); +int yangext_oid_get(yang_stmt *yn, oid *objid, size_t *objidlen, char **objidstr); int snmp_access_str2int(char *modes_str); const char *snmp_msg_int2str(int msg); void *snmp_handle_clone(void *arg); @@ -81,9 +87,11 @@ int type_snmp2xml(yang_stmt *ys, char **valstr); 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_yang2xpath(yang_stmt *ys, cvec *keyvec, char **xpath); +int snmp_str2oid(char *str, yang_stmt *yi, oid *objid, size_t *objidlen); +int snmp_oid2str(oid **oidi, size_t *oidilen, yang_stmt *yi, cg_var *cv); int clixon_snmp_err_cb(void *handle, int suberr, cbuf *cb); +int snmp_xmlkey2val_oid(cxobj *xrow, cvec *cvk_name, cvec **cvk_orig, oid *objidk, size_t *objidklen); /*========== libnetsnmp-specific code =============== */ int clixon_snmp_api_agent_check(void); diff --git a/apps/snmp/snmp_register.c b/apps/snmp/snmp_register.c index b4e86b37..d9338a30 100644 --- a/apps/snmp/snmp_register.c +++ b/apps/snmp/snmp_register.c @@ -80,9 +80,6 @@ #include "snmp_register.h" #include "snmp_handler.h" -#define IETF_YANG_SMIV2_NS "urn:ietf:params:xml:ns:yang:ietf-yang-smiv2" - - /*! Parse smiv2 extensions for YANG leaf * Typical leaf: * smiv2:oid "1.3.6.1.4.1.8072.2.1.1"; @@ -91,8 +88,8 @@ * @param[in] h Clixon handle * @param[in] ys Mib-Yang node * @param[in] cvk_orig Vector of untranslated key/index values (eg "foo") - * @param[in] cvk_oid Vector of translated to OID key/index values. (eg "3.6.22.22") - + * @param[in] oidk Part of OID thatrepresents key + * @param[in] oidklen Length of oidk * @retval 0 OK * @retval -1 Error * netsnmp_subtree_find(oid1,sz1, 0, 0) @@ -100,8 +97,9 @@ static int mibyang_leaf_register(clicon_handle h, yang_stmt *ys, - cvec *cvk_orig, - cvec *cvk_oid) + cvec *cvk_val, + oid *oidk, + size_t oidklen) { int retval = -1; netsnmp_handler_registration *nhreg = NULL; @@ -109,35 +107,23 @@ mibyang_leaf_register(clicon_handle h, int ret; char *modes_str = NULL; char *default_str = NULL; - char *oidstr = NULL; oid oid1[MAX_OID_LEN] = {0,}; size_t oid1len = MAX_OID_LEN; int modes; char *name; clixon_snmp_handle *sh; - cg_var *cvi; cbuf *cboid = NULL; - /* Get OID from leaf */ - if (yang_extension_value(ys, "oid", IETF_YANG_SMIV2_NS, NULL, &oidstr) < 0) - goto done; - if (oidstr == NULL) - goto ok; - /* Append sub-keys to original oidstr, use cligen-buf - */ if ((cboid = cbuf_new()) == NULL){ clicon_err(OE_UNIX, errno, "cbuf_new"); goto done; } - cprintf(cboid, "%s", oidstr); - 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, &oid1len) == NULL){ - clicon_err(OE_XML, 0, "snmp_parse_oid(%s)", cbuf_get(cboid)); - // goto done; - goto ok; // XXX skip - } + if ((ret = yangext_oid_get(ys, oid1, &oid1len, NULL)) < 0) + goto done; + if (ret == 0) + goto ok; + if (oid_append(oid1, &oid1len, oidk, oidklen) < 0) + goto done; /* Check if already registered */ if (clixon_snmp_api_oid_find(oid1, oid1len) == 1) goto ok; @@ -178,13 +164,8 @@ mibyang_leaf_register(clicon_handle h, memcpy(sh->sh_oid, oid1, sizeof(oid1)); sh->sh_oidlen = oid1len; sh->sh_default = default_str; - if (cvk_orig && - (sh->sh_cvk_orig = cvec_dup(cvk_orig)) == NULL){ - clicon_err(OE_UNIX, errno, "cvec_dup"); - goto done; - } - if (cvk_oid && - (sh->sh_cvk_oid = cvec_dup(cvk_oid)) == NULL){ + if (cvk_val && + (sh->sh_cvk_orig = cvec_dup(cvk_val)) == NULL){ clicon_err(OE_UNIX, errno, "cvec_dup"); goto done; } @@ -209,7 +190,8 @@ mibyang_leaf_register(clicon_handle h, clicon_err(OE_SNMP, ret-CLIXON_ERR_SNMP_MIB, "netsnmp_register_instance"); goto done; } - clicon_debug(1, "%s %s registered", __FUNCTION__, cbuf_get(cboid)); + oid_cbuf(cboid, oid1, oid1len); + clicon_debug(1, "%s register: %s %s", __FUNCTION__, name, cbuf_get(cboid)); ok: retval = 0; done: @@ -262,14 +244,10 @@ mibyang_table_register(clicon_handle h, goto done; } /* Get OID from parent container */ - if (yang_extension_value(ys, "oid", IETF_YANG_SMIV2_NS, NULL, &oidstr) < 0) + if ((ret = yangext_oid_get(ys, oid1, &oid1len, &oidstr)) < 0) goto done; - if (oidstr == NULL) + if (ret == 0) goto ok; - if (snmp_parse_oid(oidstr, oid1, &oid1len) == NULL){ - clicon_err(OE_XML, errno, "snmp_parse_oid"); - goto done; - } name = yang_argument_get(ys); /* Userdata to pass around in netsmp callbacks @@ -347,8 +325,8 @@ mibyang_table_register(clicon_handle h, clicon_err(OE_SNMP, ret, "netsnmp_register_table"); goto done; } - sh->sh_table_info = table_info; - clicon_debug(1, "%s %s registered", __FUNCTION__, oidstr); + sh->sh_table_info = table_info; /* Keep to free at exit */ + clicon_debug(1, "%s register: %s %s", __FUNCTION__, name, oidstr); ok: retval = 0; done: @@ -381,13 +359,11 @@ mibyang_table_poll(clicon_handle h, cxobj *xcol; yang_stmt *y; cvec *cvk_name; - cg_var *cv0; - cvec *cvk_orig = NULL; /* vector of index keys: original index */ - cvec *cvk_oid = NULL; /* vector of index keys: translated to OID */ - cg_var *cv; - int i; - cxobj *xi; + cvec *cvk_val = NULL; /* vector of index keys: original index */ yang_stmt *ys; + int ret; + oid oidk[MAX_OID_LEN] = {0,}; + size_t oidklen = MAX_OID_LEN; clicon_debug(1, "%s", __FUNCTION__); if ((ys = yang_parent_get(ylist)) == NULL || @@ -397,7 +373,7 @@ mibyang_table_poll(clicon_handle h, } if (xml_nsctx_yang(ys, &nsc) < 0) goto done; - if (yang2xpath(ys, NULL, &xpath) < 0) + if (snmp_yang2xpath(ys, NULL, &xpath) < 0) goto done; if (clicon_rpc_get(h, xpath, nsc, CONTENT_ALL, -1, &xt) < 0) goto done; @@ -413,42 +389,15 @@ mibyang_table_poll(clicon_handle h, } xrow = NULL; while ((xrow = xml_child_each(xtable, xrow, CX_ELMNT)) != NULL) { - if (cvk_orig){ - cvec_free(cvk_orig); - cvk_orig = NULL; - } - if ((cvk_orig = cvec_dup(cvk_name)) == NULL){ - clicon_err(OE_UNIX, errno, "cvec_dup"); + if ((ret = snmp_xmlkey2val_oid(xrow, cvk_name, &cvk_val, oidk, &oidklen)) < 0) goto done; - } - if (cvk_oid){ - cvec_free(cvk_oid); - cvk_oid = NULL; - } - if ((cvk_oid = cvec_dup(cvk_name)) == NULL){ - clicon_err(OE_UNIX, errno, "cvec_dup"); - goto done; - } - for (i=0; i