diff --git a/lib/clixon/clixon_xml_map.h b/lib/clixon/clixon_xml_map.h index 12ace1e7..4a051a0a 100644 --- a/lib/clixon/clixon_xml_map.h +++ b/lib/clixon/clixon_xml_map.h @@ -40,6 +40,15 @@ #ifndef _CLIXON_XML_MAP_H_ #define _CLIXON_XML_MAP_H_ +/* + * Maximum number of supported bit pisitions in YANG "bits" data + * type. As defined in RFC7950 (section 9.7.4.2.) the position + * value must be in the range 0 to 4294967295. But who needs + * that much bit positions? (To set bit 4'294'967'295 it would be + * necessary to tranfer 4294967295/8 = 536'870'911 bytes!) + */ +#define CLIXON_BITS_POS_MAX 1024 + /* * Types */ @@ -68,6 +77,8 @@ 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_valstr2enum(yang_stmt *ytype, char *valstr, char **enumstr); +int yang_bitsstr2val(yang_stmt *ytype, char *bitsstr, unsigned char **snmpval, size_t *snmplen); +int yang_val2bitsstr(yang_stmt *ytype, unsigned char *snmpval, size_t snmplen, cbuf *cb); int yang_enum2valstr(yang_stmt *ytype, char *enumstr, char **valstr); int yang_enum2int(yang_stmt *ytype, char *enumstr, int32_t *val); int yang_enum_int_value(cxobj *node, int32_t *val); diff --git a/lib/src/clixon_xml_map.c b/lib/src/clixon_xml_map.c index 463c3974..e61178e3 100644 --- a/lib/src/clixon_xml_map.c +++ b/lib/src/clixon_xml_map.c @@ -1588,6 +1588,226 @@ yang_enum2int(yang_stmt *ytype, return retval; } +/*! Given a YANG (bits) type node and a bit string, return the bit position. + * Example: + * + * type bits { + * bit stateA { + * position "0"; << This one + * } + * bit stateB { + * position "1"; << And this one + * } + * } + * + * If the position is not specified, it will be automatically assigned as defined + * in RFC7950, section 9.7.4.2 + * + * @param[in] ytype YANG type noden + * @param[in] bitstr bit (flag) string + * @param[out] bitpos position for the given bit (flag) + * @retval 1 OK, result in flagpos + * @retval 0 Invalid, not found + * @retval -1 Error + */ +int +yang_bits_pos(yang_stmt *ytype, + char *bitstr, + uint32_t *bitpos) +{ + int retval = 0; + int ret = 0; + int is_first = 1; + char *reason; + + yang_stmt *yprev = NULL; + yang_stmt *ypos = NULL; + while ((yprev = yn_each(ytype, yprev)) != NULL){ + // Check for the given bit name (flag) + if (yang_keyword_get(yprev) == Y_BIT){ + + // Use position from Y_POSITION statement if defined + if ((ypos = yang_find(yprev, Y_POSITION, NULL)) != NULL){ + if ((ret = parse_uint32(yang_argument_get(ypos), bitpos, &reason)) < 0){ + clixon_err(OE_UNIX, EINVAL, "cannot parse bit position val: %s", reason); + goto done; + } + if (ret == 0) + goto fail; + } else { + // Position not defined. Use last known position + 1 (skip first node to start with 0) + if (is_first == 0) (*bitpos)++; + } + + if (strcmp(bitstr, yang_argument_get(yprev)) == 0){ + retval = 1; + goto done; + } + + is_first = 0; + } + } + + clixon_debug(CLIXON_DBG_DEFAULT, "flag %s not found", bitstr); + goto fail; + + done: + return retval; + fail: + retval = 0; + goto done; +} + +/*! Given a YANG (bits) type node and string value, return the + * SNMP value for all bits (flags) that are set. + * + * @param[in] ytype YANG type noden + * @param[in] bitsstr Value of bits as space separated string + * @param[out] snmpval SNMP value with all bits set for given bitsstr + * @param[out] snmplen length of snmpval + * @retval 1 OK, result in snmpval + * @retval 0 Invalid, not found + * @retval -1 Error + * @see yang_val2bitsstr + */ +int +yang_bitsstr2val(yang_stmt *ytype, + char *bitsstr, + unsigned char **snmpval, + size_t *snmplen) +{ + int retval = -1; + int i = 0; + int byte = 0; + char **vec = NULL; + char *v; + char *buffer = NULL; + int nvec; + int ret = 0; + uint32_t bitpos; + + *snmplen = 0; + + if ((buffer = calloc(CLIXON_BITS_POS_MAX / 8, sizeof(unsigned char))) == NULL){ + clixon_err(OE_UNIX, errno, "calloc"); + goto done; + } + + if ((vec = clicon_strsep(bitsstr, " ", &nvec)) == NULL){ + clixon_err(OE_UNIX, EINVAL, "split string failed"); + goto done; + } + + // Go over all set flags in given bitstring + for (i=0; i 0) { + if ((ret = yang_bits_pos(ytype, v, &bitpos)) < 0) + goto done; + if (ret == 0) + goto fail; + + // Set bit at correct byte and bit position + byte = bitpos / 8; + buffer[byte] = buffer[byte] | (1 << (7 - (bitpos % 8))); + *snmplen = byte + 1; + + if (*snmplen >= CLIXON_BITS_POS_MAX) { + clixon_err(OE_UNIX, EINVAL, "bit position %zu out of range. (max. allowed %d)", + *snmplen, CLIXON_BITS_POS_MAX); + goto done; + } + } + } + + if ((*snmpval = malloc(*snmplen)) == NULL){ + clixon_err(OE_UNIX, errno, "calloc"); + goto done; + } + + memcpy(*snmpval, buffer, *snmplen); + retval = 1; + + done: + if (buffer) + free(buffer); + if (vec) + free(vec); + return retval; + + fail: + retval = 0; + goto done; +} + +/*! Given a YANG (bits) type node and SNMP value, return the string value for all bits (flags) that are set. + * + * @param[in] ytype YANG type noden + * @param[in] snmpval SNMP value + * @param[in] snmplen length of snmpval + * @param[out] cb space separated string with bit labes for all bits that are set in snmpval + * @retval 1 OK, result in cb + * @retval 0 Invalid, not found + * @retval -1 Error + * @see yang_bitsstr2val + */ +int +yang_val2bitsstr(yang_stmt *ytype, + unsigned char *snmpval, + size_t snmplen, + cbuf *cb) +{ + int retval = -1; + int is_first = 1; + int ret = 0; + int byte = 0; + char *reason; + yang_stmt *yprev = NULL; + yang_stmt *ypos; + uint32_t bitpos = 0; + + if (cb == NULL){ + clixon_err(OE_UNIX, EINVAL, "cb is NULL"); + goto done; + } + + // Go over all defined bits and check if it is seet in intval + while ((yprev = yn_each(ytype, yprev)) != NULL && byte < snmplen){ + if (yang_keyword_get(yprev) == Y_BIT) { + // Use position from Y_POSITION statement if defined + if ((ypos = yang_find(yprev, Y_POSITION, NULL)) != NULL){ + if ((ret = parse_uint32(yang_argument_get(ypos), &bitpos, &reason)) < 0){ + clixon_err(OE_UNIX, EINVAL, "cannot parse bit position val: %s", reason); + goto done; + } + if (ret == 0) + goto fail; + } else { + // Position not defined. Use last known position + 1 (skip first node to start with 0) + if (is_first == 0) bitpos++; + } + + byte = bitpos / 8; + if (snmpval[byte] & (1 << (7 - (bitpos % 8)))){ + if (is_first == 0) cbuf_append_str(cb, " "); + cbuf_append_str(cb, yang_argument_get(yprev)); + } + + is_first = 0; + } + } + // append space if no flag is set to indicate that + if (cbuf_len(cb) == 0) + cbuf_append_str(cb, " "); + retval = 1; + + done: + return retval; + fail: + retval = 0; + goto done; +} + /*! Get integer value from xml node from yang enumeration * * @param[in] node XML node in a tree