neels has uploaded this change for review. ( https://gerrit.osmocom.org/c/osmo-upf/+/27217 )
Change subject: libosmo-tlv: add versatile TLV de- and encoder ......................................................................
libosmo-tlv: add versatile TLV de- and encoder
An all new TLV parser supporting:
- Any size of T and L (determined by callback function), - "Grouped IEs", so that an IE payload is a nested IE structure, - optional/mandatory/multi-occurence IEs.
Will be used for PFCP message decoding and encoding, a T16L16V protocol which requires above features.
Previously, the way we deal with TLVs causes a lot of code re-implementation. The TL decoding is taken care of by the API, but for encoding, we essentially re-implement the TL encoding for each protocol and each encoded message. This API is an improvement in that we only once implement the TL coding (or just use osmo_t8l8v_cfg / osmo_t16l16v_cfg), get symmetric de- and encoding of the TL, and only need to deal with the value part of each IE.
The common pattern of - store TL preliminarily, - write V data and - update L after V is complete is conveniently done by osmo_tlv_put_update_tl().
Related: SYS#5599 Change-Id: Ib0fd00d9f288ffe13b7e67701f3e47073587404a --- M configure.ac M include/osmocom/Makefile.am A include/osmocom/tlv/Makefile.am A include/osmocom/tlv/tlv.h M src/Makefile.am A src/libosmo-tlv/Makefile.am A src/libosmo-tlv/tlv.c M tests/Makefile.am A tests/libosmo-tlv/Makefile.am A tests/libosmo-tlv/tlv_test.c A tests/libosmo-tlv/tlv_test.ok M tests/testsuite.at 12 files changed, 1,260 insertions(+), 0 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/osmo-upf refs/changes/17/27217/1
diff --git a/configure.ac b/configure.ac index 4995146..70879e9 100644 --- a/configure.ac +++ b/configure.ac @@ -197,11 +197,14 @@ AC_OUTPUT( include/Makefile include/osmocom/Makefile + include/osmocom/tlv/Makefile include/osmocom/upf/Makefile src/Makefile + src/libosmo-tlv/Makefile src/osmo-upf/Makefile tests/Makefile tests/atlocal + tests/libosmo-tlv/Makefile doc/Makefile doc/examples/Makefile doc/manuals/Makefile diff --git a/include/osmocom/Makefile.am b/include/osmocom/Makefile.am index 3f929f1..4d56df0 100644 --- a/include/osmocom/Makefile.am +++ b/include/osmocom/Makefile.am @@ -1,3 +1,4 @@ SUBDIRS = \ + tlv \ upf \ $(NULL) diff --git a/include/osmocom/tlv/Makefile.am b/include/osmocom/tlv/Makefile.am new file mode 100644 index 0000000..f819a41 --- /dev/null +++ b/include/osmocom/tlv/Makefile.am @@ -0,0 +1,5 @@ +tlv_HEADERS = \ + tlv.h \ + $(NULL) + +tlvdir = $(includedir)/osmocom/tlv diff --git a/include/osmocom/tlv/tlv.h b/include/osmocom/tlv/tlv.h new file mode 100644 index 0000000..404bbba --- /dev/null +++ b/include/osmocom/tlv/tlv.h @@ -0,0 +1,139 @@ +/* + * (C) 2021-2022 by sysmocom - s.f.m.c. GmbH info@sysmocom.de + * All Rights Reserved. + * + * Author: Neels Janosch Hofmeyr nhofmeyr@sysmocom.de + * + * SPDX-License-Identifier: GPL-2.0+ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ +#pragma once + +#include <stdint.h> +#include <stdio.h> + +struct msgb; +struct osmo_tlv_load; +struct osmo_tlv_put; + +/*! TL configuration for osmo_tlv_load*() and osmo_tlv_put*(). Depending on these implementations provided by the caller, + * osmo_tlv can load any sizes of tag and length fields (that don't surpass the value range of unsigned int and size_t, + * respectively), as well as TV (fixed-length) or TvLV (variable-sized length). + * + * See osmo_t8l8v_cfg and osmo_t16l16v_cfg, ready implementations for plain 8bit and 16bit TLV protocols. + * + * libosmo-pfcp serves as example for using this entire TLV API, uncluding de/encoding to structs and generating parts + * of the TLV parsing code based on message definitions. It uses osmo_t16l16v_cfg. + */ +struct osmo_tlv_cfg { + /*! The length in bytes of the shortest possible TL header (e.g. 4 for T16L16V, or 1 for 8bit tags where TV IEs + * without a length exist). A src_data_len passed to store_tl() below is guaranteed to be >= this value. If at + * any point there is remaining message data smaller than this value, a parsing error is returned. + */ + size_t tl_min_size; + + /*! Read one TL from the start of src_data. + * \param tag Return the T value read from src_data in *tag. + * \param len Return the L value read from src_data in *len. + * \param val Return the position just after the TL in *val. If there is V data, point at the start of the V + * data in src_data. If there is no V data, point at the byte just after the TL part in src_data. + * \param src_data Part of raw message being decoded. + * \param src_data_len Remaining message data length at src_data. + * \param tlv Backpointer to the osmo_tlv_load struct. + * \return 0 on success, negative on error. + */ + int (*load_tl)(unsigned int *tag, size_t *len, const uint8_t **val, + const uint8_t *src_data, size_t src_data_len, struct osmo_tlv_load *tlv); + + /*! Write a TL to dst_data, and return the size of the TL written. + * This is also invoked by osmo_tlv_put_update_tl() to overwrite a previous TL header. If the TL part's size + * can be different than the first time (e.g. due to a large L value in a TvLV protocol), an implementation can + * use the 'tlv' arg to figure out how to memmove the message data: + * When invoked by osmo_tlv_put_tl(), dst_data == tlv->dst->tail and dst_data_avail == msgb_tailroom(). + * When invoked by osmo_tlv_put_update_tl(), dst_data < tlv->dst->tail, dst_data points at the start of the + * TL section written earlier by osmo_tlv_put_tl() and dst_data_avail == the size of the TL written earlier. + * + * \param dst_data Write TL data to the start of this buffer. + * \param dst_data_avail Remaining available space in dst_data. + * \param tag The T value to store in dst_data. + * \param len The L value to store in dst_data. + * \param tlv Backpointer to the osmo_tlv_put struct, including tlv->dst, the underlying msgb. + * \return the size of the TL part in bytes on success, -EINVAL if tag is invalid, -EMSGSIZE if len is too large + * or dst_data_avail is too small for the TL. + */ + int (*store_tl)(uint8_t *dst_data, size_t dst_data_avail, unsigned int tag, size_t len, struct osmo_tlv_put *tlv); +}; + +/*! Configuration that allows parsing an 8bit tag and 8bit length TLV. */ +extern const struct osmo_tlv_cfg osmo_t8l8v_cfg; + +/*! Configuration that allows parsing a 16bit tag and 16bit length TLV (see for example PFCP). */ +extern const struct osmo_tlv_cfg osmo_t16l16v_cfg; + +/*! State for loading a TLV structure from raw data. */ +struct osmo_tlv_load { + /*! Caller-defined context pointer available for use by load_tl() and store_tl() implementations. */ + void *priv; + + /*! Definition of tag and length sizes (by function pointers). */ + const struct osmo_tlv_cfg *cfg; + + /*! Overall message buffer being parsed. */ + struct { + const uint8_t *data; + size_t len; + } src; + + /*! Return value from last invocation of osmo_tlv_load_next*(): tag value of parsed IE. */ + unsigned int tag; + /*! Return value from last invocation of osmo_tlv_load_next*(): Start of the IE's payload data (after tag and + * length). If the end of the src buffer is reached, val == NULL. If a TLV contained no value part, len == 0, + * but this still points just after the TL. */ + const uint8_t *val; + /*! Return value from last invocation of osmo_tlv_load_next*(): Length of the IE's payload data (without tag and + * length) */ + size_t len; +}; + +/* Start or restart the tlv from the first IE in the overall TLV data. */ +static inline void osmo_tlv_load_start(struct osmo_tlv_load *tlv) +{ + tlv->val = NULL; +} + +int osmo_tlv_load_next(struct osmo_tlv_load *tlv); +int osmo_tlv_load_peek_tag(const struct osmo_tlv_load *tlv); +int osmo_tlv_load_next_by_tag(struct osmo_tlv_load *tlv, unsigned int tag); + +/* State for storing a TLV structure into a msgb. */ +struct osmo_tlv_put { + /*! Caller-defined context pointer available for use by load_tl() and store_tl() implementations. */ + void *priv; + + /* Definition of tag and length sizes (by function pointers). */ + const struct osmo_tlv_cfg *cfg; + + /* msgb to append new TL to */ + struct msgb *dst; + /* Where was the last TL head written */ + unsigned int last_tag; + uint8_t *last_tl; + uint8_t *last_val; +}; + +int osmo_tlv_put_tl(struct osmo_tlv_put *tlv, unsigned int tag, size_t len); +int osmo_tlv_put_update_tl(struct osmo_tlv_put *tlv); diff --git a/src/Makefile.am b/src/Makefile.am index a8ba763..0e44dcc 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,3 +1,4 @@ SUBDIRS = \ + libosmo-tlv \ osmo-upf \ $(NULL) diff --git a/src/libosmo-tlv/Makefile.am b/src/libosmo-tlv/Makefile.am new file mode 100644 index 0000000..0f848d3 --- /dev/null +++ b/src/libosmo-tlv/Makefile.am @@ -0,0 +1,25 @@ +AM_CPPFLAGS = \ + $(all_includes) \ + -I$(top_srcdir)/include \ + -I$(top_builddir) \ + -I$(builddir) \ + $(NULL) + +AM_CFLAGS = \ + -Wall \ + $(LIBOSMOCORE_CFLAGS) \ + $(COVERAGE_CFLAGS) \ + $(NULL) + +AM_LDFLAGS = \ + $(LIBOSMOCORE_LIBS) \ + $(COVERAGE_LDFLAGS) \ + $(NULL) + +noinst_LIBRARIES = \ + libosmo-tlv.a \ + $(NULL) + +libosmo_tlv_a_SOURCES = \ + tlv.c \ + $(NULL) diff --git a/src/libosmo-tlv/tlv.c b/src/libosmo-tlv/tlv.c new file mode 100644 index 0000000..3426597 --- /dev/null +++ b/src/libosmo-tlv/tlv.c @@ -0,0 +1,284 @@ +/* + * (C) 2021-2022 by sysmocom - s.f.m.c. GmbH info@sysmocom.de + * All Rights Reserved. + * + * Author: Neels Janosch Hofmeyr nhofmeyr@sysmocom.de + * + * SPDX-License-Identifier: GPL-2.0+ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#include <errno.h> + +#include <osmocom/core/bits.h> +#include <osmocom/core/msgb.h> +#include <osmocom/tlv/tlv.h> + +static int next_tl_valid(const struct osmo_tlv_load *tlv, const uint8_t **ie_start_p, size_t *buflen_left_p) +{ + const uint8_t *ie_start; + size_t buflen_left; + + /* Start of next IE, or first IE for first invocation. */ + if (!tlv->val) + ie_start = tlv->src.data; + else + ie_start = tlv->val + tlv->len; + + /* Sanity */ + if (ie_start < tlv->src.data || ie_start > tlv->src.data + tlv->src.len) + return -ENOSPC; + + buflen_left = tlv->src.len - (ie_start - tlv->src.data); + + /* Too short for parsing an IE? Check also against integer overflow. */ + if (buflen_left && ((buflen_left < tlv->cfg->tl_min_size) || (buflen_left > tlv->src.len))) + return -EBADMSG; + + *ie_start_p = ie_start; + *buflen_left_p = buflen_left; + return 0; +} + +/* Return a TLV IE from a message buffer. + * + * Return the first or next TLV data found in the data buffer, based on the state of the tlv parameter. + * When tlv->val is NULL, return the first IE in the data buffer. + * Otherwise assume that tlv points at a valid IE in the data structure, and return the subsequent IE. + * + * Usage example: + * + * struct osmo_tlv tlv = { + * .cfg = osmo_t16l16v_cfg, + * .src = { .data = msgb_l3(msg), .len = msgb_l3len(msg) }, + * }; + * for (;;) { + * if (osmo_tlv_next(&tlv)) { + * printf("Error\n"); + * break; + * } + * if (!tlv.val) { + * printf("End\n"); + * break; + * } + * printf("Tag %u: %zu octets: %s\n", tlv.tag, tlv.len, osmo_hexdump(tlv.val, tlv.len)); + * } + * + * \param[inout] tlv Buffer to return the IE data, and state for TLV parsing position. tlv->msg should indicate the + * overall message buffer. The other tlv members should be zero initialized before the first call, and + * remain unchanged between invocations of this function. + * \returns 0 on success, negative on TLV parsing error. The IE data is returned in tlv->tag, tlv->len and tlv->val; + * tlv->val == NULL if no more IEs remain in the buffer. + */ +int osmo_tlv_load_next(struct osmo_tlv_load *tlv) +{ + const uint8_t *ie_start; + const uint8_t *ie_end; + size_t buflen_left; + int rc; + + rc = next_tl_valid(tlv, &ie_start, &buflen_left); + if (rc) + return rc; + + /* No more IEs? */ + if (!buflen_left) { + tlv->val = NULL; + return 0; + } + + /* Locate next IE */ + OSMO_ASSERT(tlv->cfg->load_tl); + rc = tlv->cfg->load_tl(&tlv->tag, &tlv->len, &tlv->val, ie_start, buflen_left, tlv); + if (rc) + return rc; + + /* Sanity */ + ie_end = tlv->val + tlv->len; + if (ie_end < tlv->src.data || ie_end > tlv->src.data + tlv->src.len) + return -EBADMSG; + + return 0; +} + +/* Return the tag of the IE that osmo_tlv_next() would yield, do not change the tlv state. + * + * \param[inout] tlv state for TLV parsing position. + * \returns the tag number on success, negative on TLV parsing error, -ENOENT when no more tags + * follow. + */ +int osmo_tlv_load_peek_tag(const struct osmo_tlv_load *tlv) +{ + const uint8_t *ie_start; + size_t buflen_left; + int rc; + unsigned int tag; + size_t len; + const uint8_t *val; + /* Guard against modification by load_tl(). */ + struct osmo_tlv_load mtlv = *tlv; + + rc = next_tl_valid(&mtlv, &ie_start, &buflen_left); + if (rc) + return rc; + + if (!buflen_left) + return -ENOENT; + + /* Return next IE tag*/ + OSMO_ASSERT(mtlv.cfg->load_tl); + rc = tlv->cfg->load_tl(&tag, &len, &val, ie_start, buflen_left, &mtlv); + if (rc) + return -EBADMSG; + return tag; +} + +/* Same as osmo_tlv_next(), but skip any IEs until the given tag is reached. Change the tlv state only when + * success is returned. + * \return 0 when the tag is found. Return -ENOENT when no such tag follows and keep the tlv unchanged. */ +int osmo_tlv_load_next_by_tag(struct osmo_tlv_load *tlv, unsigned int tag) +{ + struct osmo_tlv_load work = *tlv; + for (;;) { + int rc = osmo_tlv_load_next(&work); + if (rc) + return rc; + if (!work.val) + return -ENOENT; + if (work.tag == tag) { + *tlv = work; + return 0; + } + } +} + +/* Put tag header and length at the end of the msgb. + * If the length is not known yet, it can be passed as 0 at first, and osmo_tlv_put_update_tl() can determine the + * resulting length after the value part was put into the msgb. + * + * Usage example: + * + * struct msgb *msg = msgb_alloc(1024, "foo"), + * struct osmo_tlv_put tlv = { + * .cfg = osmo_t16l16v_cfg, + * .dst = msg, + * } + * + * osmo_tlv_put_tl(tlv, 23, 0); // tag 23, length 0 = not known yet + * + * msgb_put(msg, 42); + * ... + * msgb_put(msg, 42); + * ... + * msgb_put(msg, 42); + * + * osmo_tlv_put_update_tl(tlv); + * + * Return 0 on success, -EINVAL if the tag value is invalid, -EMSGSIZE if len is too large. + */ +int osmo_tlv_put_tl(struct osmo_tlv_put *tlv, unsigned int tag, size_t len) +{ + int rc; + uint8_t *last_tl; + OSMO_ASSERT(tlv->cfg->store_tl); + last_tl = tlv->dst->tail; + rc = tlv->cfg->store_tl(tlv->dst->tail, msgb_tailroom(tlv->dst), tag, len, tlv); + if (rc < 0) + return rc; + if (rc > 0) + msgb_put(tlv->dst, rc); + tlv->last_tag = tag; + tlv->last_tl = last_tl; + tlv->last_val = tlv->dst->tail; + return 0; +} + +/* Update the length of the last put IE header (last call to osmo_tlv_put_tl()) to match with the current + * tlv->dst->tail. + * Return 0 on success, -EMSGSIZE if the amount of data written since osmo_tlv_put_tl() is too large. + */ +int osmo_tlv_put_update_tl(struct osmo_tlv_put *tlv) +{ + size_t len = tlv->dst->tail - tlv->last_val; + int rc = tlv->cfg->store_tl(tlv->last_tl, tlv->last_val - tlv->last_tl, tlv->last_tag, len, tlv); + if (rc < 0) + return rc; + /* In case the TL has changed in size, hopefully the implementation has moved the msgb data. Make sure last_val + * points at the right place now. */ + tlv->last_val = tlv->last_tl + rc; + return 0; +} + +static int t8l8v_load_tl(unsigned int *tag, size_t *len, const uint8_t **val, + const uint8_t *src_data, size_t src_data_len, struct osmo_tlv_load *tlv) +{ + /* already validated in next_tl_valid(): src_data_len >= cfg->tl_min_size == 2. */ + *tag = src_data[0]; + *len = src_data[1]; + *val = src_data + 2; + return 0; +} + +static int t8l8v_store_tl(uint8_t *dst_data, size_t dst_data_avail, unsigned int tag, size_t len, + struct osmo_tlv_put *tlv) +{ + if (tag > UINT8_MAX) + return -EINVAL; + if (len > UINT8_MAX) + return -EMSGSIZE; + if (dst_data_avail < 2) + return -ENOSPC; + dst_data[0] = tag; + dst_data[1] = len; + return 2; +} + +const struct osmo_tlv_cfg osmo_t8l8v_cfg = { + .tl_min_size = 2, + .load_tl = t8l8v_load_tl, + .store_tl = t8l8v_store_tl, +}; + +static int t16l16v_load_tl(unsigned int *tag, size_t *len, const uint8_t **val, + const uint8_t *src_data, size_t src_data_len, struct osmo_tlv_load *tlv) +{ + /* already validated in next_tl_valid(): src_data_len >= cfg->tl_min_size == 4. */ + *tag = osmo_load16be(src_data); + *len = osmo_load16be(src_data + 2); + *val = src_data + 4; + return 0; +} + +static int t16l16v_store_tl(uint8_t *dst_data, size_t dst_data_avail, unsigned int tag, size_t len, + struct osmo_tlv_put *tlv) +{ + if (tag > UINT16_MAX) + return -EINVAL; + if (len > UINT16_MAX) + return -EMSGSIZE; + if (dst_data_avail < 4) + return -ENOSPC; + osmo_store16be(tag, dst_data); + osmo_store16be(len, dst_data + 2); + return 4; +} + +const struct osmo_tlv_cfg osmo_t16l16v_cfg = { + .tl_min_size = 4, + .load_tl = t16l16v_load_tl, + .store_tl = t16l16v_store_tl, +}; diff --git a/tests/Makefile.am b/tests/Makefile.am index 6d14cc0..fb1680e 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1,4 +1,5 @@ SUBDIRS = \ + libosmo-tlv \ $(NULL)
# The `:;' works around a Bash 3.2 bug when the output is not writeable. diff --git a/tests/libosmo-tlv/Makefile.am b/tests/libosmo-tlv/Makefile.am new file mode 100644 index 0000000..250dc5e --- /dev/null +++ b/tests/libosmo-tlv/Makefile.am @@ -0,0 +1,30 @@ +AM_CPPFLAGS = \ + $(all_includes) \ + -I$(top_srcdir)/include \ + $(NULL) + +AM_CFLAGS = \ + -Wall \ + $(LIBOSMOCORE_CFLAGS) \ + $(NULL) + +noinst_PROGRAMS = \ + tlv_test \ + $(NULL) + +EXTRA_DIST = \ + tlv_test.ok \ + $(NULL) + +tlv_test_SOURCES = \ + tlv_test.c \ + $(NULL) + +tlv_test_LDADD = \ + $(top_builddir)/src/libosmo-tlv/libosmo-tlv.a \ + $(LIBOSMOCORE_LIBS) \ + $(NULL) + +.PHONY: update_exp +update_exp: + $(builddir)/tlv_test >$(srcdir)/tlv_test.ok diff --git a/tests/libosmo-tlv/tlv_test.c b/tests/libosmo-tlv/tlv_test.c new file mode 100644 index 0000000..ed3c12b --- /dev/null +++ b/tests/libosmo-tlv/tlv_test.c @@ -0,0 +1,509 @@ +/* (C) 2022 by sysmocom - s.f.m.c. GmbH info@sysmocom.de + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +#include <stdio.h> +#include <errno.h> + +#include <osmocom/core/application.h> +#include <osmocom/core/utils.h> +#include <osmocom/core/msgb.h> + +#include <osmocom/tlv/tlv.h> + +void *ctx; + +struct ie { + int tag; + const char *val; +}; + +/* write all IEs to a msgb */ +struct msgb *test_tlv_enc(const struct osmo_tlv_cfg *cfg, const struct ie *ies) +{ + const struct ie *ie; + struct osmo_tlv_put tlv = { + .cfg = cfg, + .dst = msgb_alloc(1024, __func__), + }; + + for (ie = ies; ie->val; ie++) { + /* put header without knowing length yet */ + OSMO_ASSERT( osmo_tlv_put_tl(&tlv, ie->tag, 0) == 0); + /* put value data, as much as desired */ + msgb_put(tlv.dst, osmo_hexparse(ie->val, tlv.dst->tail, msgb_tailroom(tlv.dst))); + /* update header len from amount of written data */ + OSMO_ASSERT( osmo_tlv_put_update_tl(&tlv) == 0); + } + + printf("- encoded: %s.\n", osmo_hexdump(tlv.dst->data, tlv.dst->len)); + return tlv.dst; +} + +/* read all IEs from the msgb, and verify that it matches the given list of IEs */ +void test_tlv_dec(const struct osmo_tlv_cfg *cfg, const struct ie *ies, struct msgb *msg) +{ + const struct ie *ie; + struct osmo_tlv_load tlv = { + .cfg = cfg, + .src = { msg->data, msg->len }, + }; + + printf("- decoding:\n"); + osmo_tlv_load_start(&tlv); + + for (ie = ies; ie->val; ie++) { + int rc = osmo_tlv_load_next(&tlv); + if (rc) { + printf(" ERROR loading TLV structure: osmo_tlv_load_next() rc = %d\n", rc); + exit(1); + } + /* end of TLV structure? */ + if (!tlv.val) + break; + printf(" T=%d L=%zu v=%s\n", tlv.tag, tlv.len, osmo_hexdump_nospc(tlv.val, tlv.len)); + if (tlv.tag != ie->tag) { + printf(" ERROR loading TLV structure: expected tag %d, got tag %d\n", ie->tag, tlv.tag); + exit(1); + } + if (strcmp(ie->val, osmo_hexdump_nospc(tlv.val, tlv.len))) { + printf(" ERROR loading TLV structure: expected val %s, got val %s\n", ie->val, + osmo_hexdump_nospc(tlv.val, tlv.len)); + exit(1); + } + } +} + +void test_tlv_peek(const struct osmo_tlv_cfg *cfg, const struct ie *ies, struct msgb *msg) +{ + const struct ie *ie; + struct osmo_tlv_load tlv = { + .cfg = cfg, + .src = { msg->data, msg->len }, + }; + + printf("- peeking:\n"); + osmo_tlv_load_start(&tlv); + + ie = ies; + while (1) { + int rc; + int next_tag = osmo_tlv_load_peek_tag(&tlv); + if (next_tag == -ENOENT) + printf(" peek T=-ENOENT\n"); + else + printf(" peek T=%d\n", next_tag); + + if (ie->val && next_tag != ie->tag) { + printf(" ERROR peeking tag: expected tag %d, got tag %d\n", ie->tag, next_tag); + exit(1); + } + if (!ie->val && next_tag != -ENOENT) { + printf(" ERROR peeking tag: expected -ENOENT, got tag %d\n", next_tag); + exit(1); + } + + if (next_tag == -ENOENT) + break; + + /* go to the next TLV */ + rc = osmo_tlv_load_next(&tlv); + if (rc) { + printf(" ERROR loading TLV structure: osmo_tlv_load_next() rc = %d\n", rc); + exit(1); + } + if (ie->val) + ie++; + } +} + +/* Decode TLV in random order, each time searching for a tag in the raw data */ +void test_tlv_dec_by_tag(const struct osmo_tlv_cfg *cfg, const struct ie *ies, struct msgb *msg) +{ + const struct ie *last_ie; + const struct ie *ie; + int rc; + struct osmo_tlv_load tlv = { + .cfg = cfg, + .src = { msg->data, msg->len }, + }; + + printf("- decoding in reverse order:\n"); + + last_ie = ies; + while (last_ie->val) last_ie++; + last_ie--; + + for (ie = last_ie; ie >= ies; ie--) { + /* each time, look from the beginning */ + osmo_tlv_load_start(&tlv); + rc = osmo_tlv_load_next_by_tag(&tlv, ie->tag); + if (rc) { + printf(" ERROR loading TLV structure: osmo_tlv_load_next_by_tag(%d) rc = %d\n", ie->tag, rc); + exit(1); + } + if (!tlv.val) { + printf(" ERROR loading TLV structure: osmo_tlv_load_next_by_tag(%d) returned NULL val\n", + ie->tag); + exit(1); + } + if (tlv.tag != ie->tag) { + printf(" ERROR loading TLV structure: expected tag %d, got tag %d\n", ie->tag, tlv.tag); + exit(1); + } + if (strcmp(ie->val, osmo_hexdump_nospc(tlv.val, tlv.len))) { + while (1) { + printf(" (mismatch: T=%d L=%zu v=%s, checking for another occurence of T=%d)\n", + tlv.tag, tlv.len, osmo_hexdump_nospc(tlv.val, tlv.len), tlv.tag); + rc = osmo_tlv_load_next_by_tag(&tlv, ie->tag); + if (rc || !tlv.val) { + printf(" ERROR val not found\n"); + exit(1); + } + if (strcmp(ie->val, osmo_hexdump_nospc(tlv.val, tlv.len)) == 0) { + break; + } + } + } + printf(" T=%d L=%zu v=%s\n", tlv.tag, tlv.len, osmo_hexdump_nospc(tlv.val, tlv.len)); + } + + printf("- decoding every second tag:\n"); + + osmo_tlv_load_start(&tlv); + for (ie = ies; ie->val; ie++) { + /* skip one tag */ + ie++; + if (!ie->val) + break; + + rc = osmo_tlv_load_next_by_tag(&tlv, ie->tag); + if (rc) { + printf(" ERROR loading TLV structure: osmo_tlv_load_next_by_tag(%d) rc = %d\n", ie->tag, rc); + exit(1); + } + if (!tlv.val) { + printf(" ERROR loading TLV structure: osmo_tlv_load_next_by_tag(%d) returned NULL val\n", + ie->tag); + exit(1); + } + if (tlv.tag != ie->tag) { + printf(" ERROR loading TLV structure: expected tag %d, got tag %d\n", ie->tag, tlv.tag); + exit(1); + } + if (strcmp(ie->val, osmo_hexdump_nospc(tlv.val, tlv.len))) { + while (1) { + printf(" (mismatch: T=%d L=%zu v=%s, checking for another occurence of T=%d)\n", + tlv.tag, tlv.len, osmo_hexdump_nospc(tlv.val, tlv.len), tlv.tag); + rc = osmo_tlv_load_next_by_tag(&tlv, ie->tag); + if (rc || !tlv.val) { + printf(" ERROR val not found\n"); + exit(1); + } + if (strcmp(ie->val, osmo_hexdump_nospc(tlv.val, tlv.len)) == 0) { + break; + } + } + } + printf(" T=%d L=%zu v=%s\n", tlv.tag, tlv.len, osmo_hexdump_nospc(tlv.val, tlv.len)); + } + + printf("- enforcing order: without restart, a past tag is not parsed again:\n"); + /* Try to read the first tag, expect that it isn't found because we're already halfway in the message data */ + ie = ies; + rc = osmo_tlv_load_next_by_tag(&tlv, ie->tag); + printf(" osmo_tlv_load_next_by_tag(%d) rc=", ie->tag); + if (rc == -ENOENT) { + printf("-ENOENT\n"); + } else { + printf("%d\n", rc); + printf(" ERROR: expected -ENOENT\n"); + exit(1); + } +} + +void test_tlv(const char *label, struct ie *tests[], size_t tests_len, const struct osmo_tlv_cfg *cfg) +{ + int i; + for (i = 0; i < tests_len; i++) { + const struct ie *ies = tests[i]; + struct msgb *msg; + printf("\n=== start: %s[%d]\n", label, i); + + msg = test_tlv_enc(cfg, ies); + test_tlv_dec(cfg, ies, msg); + test_tlv_peek(cfg, ies, msg); + test_tlv_dec_by_tag(cfg, ies, msg); + + msgb_free(msg); + + printf("=== end: %s[%d]\n", label, i); + } +} + +struct ie t8l8v_test1[] = { + /* smallest T */ + { 0, "2342" }, + /* largest T */ + { 255, "2342" }, + + /* smallest V (no V data) */ + { 1, "" }, + /* largest V, 255 bytes is the largest that an 8bit size length can express. */ + { 123, "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + }, + + /* arbitrary test data */ + { 101, "11" }, + { 102, "2222" }, + { 103, "333333" }, + {} +}; + +struct ie t8l8v_test_multi[] = { + { 42, "42" }, + { 2, "0101" }, + { 2, "2222" }, + { 3, "11" }, + { 3, "2222" }, + { 3, "333333" }, + { 23, "23" }, + { 42, "666f72747974776f" }, + { 23, "7477656e74797468726565" }, + {} +}; + +struct ie *t8l8v_tests[] = { + t8l8v_test1, + t8l8v_test_multi, +}; + +void test_t8l8v() +{ + test_tlv("t8l8v_tests", t8l8v_tests, ARRAY_SIZE(t8l8v_tests), &osmo_t8l8v_cfg); +} + +struct ie t16l16v_test1[] = { + /* smallest T */ + { 0, "2342" }, + /* largest T */ + { 65535, "2342" }, + + /* smallest V (no V data) */ + { 1, "" }, + /* 256 bytes is one more than an 8bit size length can express. */ + { 123, "0000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000" + }, + + /* arbitrary test data */ + { 1001, "11" }, + { 1002, "2222" }, + { 1003, "333333" }, + {} +}; + +struct ie t16l16v_test_multi[] = { + { 1042, "42" }, + { 102, "0101" }, + { 102, "2222" }, + { 103, "11" }, + { 103, "2222" }, + { 103, "333333" }, + { 1023, "23" }, + { 1042, "666f72747974776f" }, + { 1023, "7477656e74797468726565" }, + {} +}; + +struct ie *t16l16v_tests[] = { + t16l16v_test1, + t16l16v_test_multi, +}; + +void test_t16l16v() +{ + test_tlv("t16l16v_tests", t16l16v_tests, ARRAY_SIZE(t16l16v_tests), &osmo_t16l16v_cfg); +} + +struct ie txlxv_test1[] = { + /* smallest T */ + { 0, "2342" }, + /* largest T that still fits in one encoded octet (highest bit serves as flag) */ + { 0x7f, "2342" }, + /* smallest T that needs two octets to be encoded (first octet = 0x80 flag + 0, second octet = 0x1) */ + { 0x80, "2342" }, + /* largest T that can be encoded in 16bit - one flag bit. */ + { 0x7fff, "2342" }, + + /* smallest V (no V data) */ + { 1, "" }, + /* 256 bytes is one more than an 8bit size length can express. */ + { 123, "0000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000" + }, + + /* arbitrary test data */ + { 1002, "2222" }, + { 1003, "333333" }, + {} +}; + +struct ie txlxv_test_multi[] = { + { 1042, "42" }, + { 1002, "0101" }, + { 1002, "2222" }, + { 103, "11" }, + { 103, "2222" }, + { 103, "333333" }, + { 1023, "23" }, + { 1042, "666f72747974776f" }, + { 1023, "7477656e74797468726565" }, + {} +}; + +struct ie *txlxv_tests[] = { + txlxv_test1, + txlxv_test_multi, +}; + +/* Example of defining a variable TL, where size of T and L depend on the actual tag and length values: load. */ +int txlxv_load_tl(unsigned int *tag, size_t *len, const uint8_t **val, const uint8_t *src_data, size_t src_data_len, + struct osmo_tlv_load *tlv) +{ + const uint8_t *pos = src_data; + const uint8_t *end = src_data + src_data_len; + if (pos[0] & 0x80) { + if (pos + 2 > end) + return -EINVAL; + *tag = (((int)pos[1]) << 7) + (pos[0] & 0x7f); + pos += 2; + } else { + *tag = pos[0]; + pos++; + } + + switch (*tag) { + case 1002: + /* fixed-length IE */ + *len = 2; + break; + case 123: + /* 16bit length IE */ + if (pos + 2 > end) + return -EINVAL; + *len = osmo_load16be(pos); + pos += 2; + break; + default: + /* 8bit length IE */ + if (pos + 1 > end) + return -EINVAL; + *len = *pos; + pos++; + break; + } + *val = pos; + return 0; +} + +/* Example of defining a variable TL, where size of T and L depend on the actual tag and length values: store. */ +int txlxv_store_tl(uint8_t *dst_data, size_t dst_data_avail, unsigned int tag, size_t len, struct osmo_tlv_put *tlv) +{ + uint8_t *pos = dst_data; + uint8_t *end = dst_data + dst_data_avail; + if (tag < 0x80) { + if (pos + 1 > end) + return -ENOSPC; + pos[0] = tag; + pos++; + } else { + if (pos + 2 > end) + return -ENOSPC; + pos[0] = 0x80 + (tag & 0x7f); + pos[1] = tag >> 7; + pos += 2; + } + + switch (tag) { + case 1002: + /* fixed-length IE, write no len */ + break; + case 123: + /* 16bit length IE */ + if (len > UINT16_MAX) + return -ERANGE; + if (pos + 2 > end) + return -ENOSPC; + osmo_store16be(len, pos); + pos += 2; + break; + default: + /* 8bit length IE */ + if (len > UINT8_MAX) + return -ERANGE; + if (pos + 1 > end) + return -ENOSPC; + pos[0] = len; + pos++; + break; + } + return pos - dst_data; +} + +const struct osmo_tlv_cfg txlxv_cfg = { + .tl_min_size = 1, + .load_tl = txlxv_load_tl, + .store_tl = txlxv_store_tl, +}; + +void test_txlxv() +{ + test_tlv("txlxv_tests", txlxv_tests, ARRAY_SIZE(txlxv_tests), &txlxv_cfg); +} + +int main() +{ + ctx = talloc_named_const(NULL, 0, "tlv_test"); + msgb_talloc_ctx_init(ctx, 0); + + test_t8l8v(); + test_t16l16v(); + test_txlxv(); + + talloc_free(ctx); + return 0; +} diff --git a/tests/libosmo-tlv/tlv_test.ok b/tests/libosmo-tlv/tlv_test.ok new file mode 100644 index 0000000..6ad7e7a --- /dev/null +++ b/tests/libosmo-tlv/tlv_test.ok @@ -0,0 +1,256 @@ + +=== start: t8l8v_tests[0] +- encoded: 00 02 23 42 ff 02 23 42 01 00 7b ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 65 01 11 66 02 22 22 67 03 33 33 33 . +- decoding: + T=0 L=2 v=2342 + T=255 L=2 v=2342 + T=1 L=0 v= + T=123 L=255 v=ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + T=101 L=1 v=11 + T=102 L=2 v=2222 + T=103 L=3 v=333333 +- peeking: + peek T=0 + peek T=255 + peek T=1 + peek T=123 + peek T=101 + peek T=102 + peek T=103 + peek T=-ENOENT +- decoding in reverse order: + T=103 L=3 v=333333 + T=102 L=2 v=2222 + T=101 L=1 v=11 + T=123 L=255 v=ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + T=1 L=0 v= + T=255 L=2 v=2342 + T=0 L=2 v=2342 +- decoding every second tag: + T=255 L=2 v=2342 + T=123 L=255 v=ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + T=102 L=2 v=2222 +- enforcing order: without restart, a past tag is not parsed again: + osmo_tlv_load_next_by_tag(0) rc=-ENOENT +=== end: t8l8v_tests[0] + +=== start: t8l8v_tests[1] +- encoded: 2a 01 42 02 02 01 01 02 02 22 22 03 01 11 03 02 22 22 03 03 33 33 33 17 01 23 2a 08 66 6f 72 74 79 74 77 6f 17 0b 74 77 65 6e 74 79 74 68 72 65 65 . +- decoding: + T=42 L=1 v=42 + T=2 L=2 v=0101 + T=2 L=2 v=2222 + T=3 L=1 v=11 + T=3 L=2 v=2222 + T=3 L=3 v=333333 + T=23 L=1 v=23 + T=42 L=8 v=666f72747974776f + T=23 L=11 v=7477656e74797468726565 +- peeking: + peek T=42 + peek T=2 + peek T=2 + peek T=3 + peek T=3 + peek T=3 + peek T=23 + peek T=42 + peek T=23 + peek T=-ENOENT +- decoding in reverse order: + (mismatch: T=23 L=1 v=23, checking for another occurence of T=23) + T=23 L=11 v=7477656e74797468726565 + (mismatch: T=42 L=1 v=42, checking for another occurence of T=42) + T=42 L=8 v=666f72747974776f + T=23 L=1 v=23 + (mismatch: T=3 L=1 v=11, checking for another occurence of T=3) + (mismatch: T=3 L=2 v=2222, checking for another occurence of T=3) + T=3 L=3 v=333333 + (mismatch: T=3 L=1 v=11, checking for another occurence of T=3) + T=3 L=2 v=2222 + T=3 L=1 v=11 + (mismatch: T=2 L=2 v=0101, checking for another occurence of T=2) + T=2 L=2 v=2222 + T=2 L=2 v=0101 + T=42 L=1 v=42 +- decoding every second tag: + T=2 L=2 v=0101 + T=3 L=1 v=11 + (mismatch: T=3 L=2 v=2222, checking for another occurence of T=3) + T=3 L=3 v=333333 + T=42 L=8 v=666f72747974776f +- enforcing order: without restart, a past tag is not parsed again: + osmo_tlv_load_next_by_tag(42) rc=-ENOENT +=== end: t8l8v_tests[1] + +=== start: t16l16v_tests[0] +- encoded: 00 00 00 02 23 42 ff ff 00 02 23 42 00 01 00 00 00 7b 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 03 e9 00 01 11 03 ea 00 02 22 22 03 eb 00 03 33 33 33 . +- decoding: + T=0 L=2 v=2342 + T=65535 L=2 v=2342 + T=1 L=0 v= + T=123 L=256 v=00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + T=1001 L=1 v=11 + T=1002 L=2 v=2222 + T=1003 L=3 v=333333 +- peeking: + peek T=0 + peek T=65535 + peek T=1 + peek T=123 + peek T=1001 + peek T=1002 + peek T=1003 + peek T=-ENOENT +- decoding in reverse order: + T=1003 L=3 v=333333 + T=1002 L=2 v=2222 + T=1001 L=1 v=11 + T=123 L=256 v=00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + T=1 L=0 v= + T=65535 L=2 v=2342 + T=0 L=2 v=2342 +- decoding every second tag: + T=65535 L=2 v=2342 + T=123 L=256 v=00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + T=1002 L=2 v=2222 +- enforcing order: without restart, a past tag is not parsed again: + osmo_tlv_load_next_by_tag(0) rc=-ENOENT +=== end: t16l16v_tests[0] + +=== start: t16l16v_tests[1] +- encoded: 04 12 00 01 42 00 66 00 02 01 01 00 66 00 02 22 22 00 67 00 01 11 00 67 00 02 22 22 00 67 00 03 33 33 33 03 ff 00 01 23 04 12 00 08 66 6f 72 74 79 74 77 6f 03 ff 00 0b 74 77 65 6e 74 79 74 68 72 65 65 . +- decoding: + T=1042 L=1 v=42 + T=102 L=2 v=0101 + T=102 L=2 v=2222 + T=103 L=1 v=11 + T=103 L=2 v=2222 + T=103 L=3 v=333333 + T=1023 L=1 v=23 + T=1042 L=8 v=666f72747974776f + T=1023 L=11 v=7477656e74797468726565 +- peeking: + peek T=1042 + peek T=102 + peek T=102 + peek T=103 + peek T=103 + peek T=103 + peek T=1023 + peek T=1042 + peek T=1023 + peek T=-ENOENT +- decoding in reverse order: + (mismatch: T=1023 L=1 v=23, checking for another occurence of T=1023) + T=1023 L=11 v=7477656e74797468726565 + (mismatch: T=1042 L=1 v=42, checking for another occurence of T=1042) + T=1042 L=8 v=666f72747974776f + T=1023 L=1 v=23 + (mismatch: T=103 L=1 v=11, checking for another occurence of T=103) + (mismatch: T=103 L=2 v=2222, checking for another occurence of T=103) + T=103 L=3 v=333333 + (mismatch: T=103 L=1 v=11, checking for another occurence of T=103) + T=103 L=2 v=2222 + T=103 L=1 v=11 + (mismatch: T=102 L=2 v=0101, checking for another occurence of T=102) + T=102 L=2 v=2222 + T=102 L=2 v=0101 + T=1042 L=1 v=42 +- decoding every second tag: + T=102 L=2 v=0101 + T=103 L=1 v=11 + (mismatch: T=103 L=2 v=2222, checking for another occurence of T=103) + T=103 L=3 v=333333 + T=1042 L=8 v=666f72747974776f +- enforcing order: without restart, a past tag is not parsed again: + osmo_tlv_load_next_by_tag(1042) rc=-ENOENT +=== end: t16l16v_tests[1] + +=== start: txlxv_tests[0] +- encoded: 00 02 23 42 7f 02 23 42 80 01 02 23 42 ff ff 02 23 42 01 00 7b 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ea 07 22 22 eb 07 03 33 33 33 . +- decoding: + T=0 L=2 v=2342 + T=127 L=2 v=2342 + T=128 L=2 v=2342 + T=32767 L=2 v=2342 + T=1 L=0 v= + T=123 L=256 v=00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + T=1002 L=2 v=2222 + T=1003 L=3 v=333333 +- peeking: + peek T=0 + peek T=127 + peek T=128 + peek T=32767 + peek T=1 + peek T=123 + peek T=1002 + peek T=1003 + peek T=-ENOENT +- decoding in reverse order: + T=1003 L=3 v=333333 + T=1002 L=2 v=2222 + T=123 L=256 v=00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + T=1 L=0 v= + T=32767 L=2 v=2342 + T=128 L=2 v=2342 + T=127 L=2 v=2342 + T=0 L=2 v=2342 +- decoding every second tag: + T=127 L=2 v=2342 + T=32767 L=2 v=2342 + T=123 L=256 v=00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + T=1003 L=3 v=333333 +- enforcing order: without restart, a past tag is not parsed again: + osmo_tlv_load_next_by_tag(0) rc=-ENOENT +=== end: txlxv_tests[0] + +=== start: txlxv_tests[1] +- encoded: 92 08 01 42 ea 07 01 01 ea 07 22 22 67 01 11 67 02 22 22 67 03 33 33 33 ff 07 01 23 92 08 08 66 6f 72 74 79 74 77 6f ff 07 0b 74 77 65 6e 74 79 74 68 72 65 65 . +- decoding: + T=1042 L=1 v=42 + T=1002 L=2 v=0101 + T=1002 L=2 v=2222 + T=103 L=1 v=11 + T=103 L=2 v=2222 + T=103 L=3 v=333333 + T=1023 L=1 v=23 + T=1042 L=8 v=666f72747974776f + T=1023 L=11 v=7477656e74797468726565 +- peeking: + peek T=1042 + peek T=1002 + peek T=1002 + peek T=103 + peek T=103 + peek T=103 + peek T=1023 + peek T=1042 + peek T=1023 + peek T=-ENOENT +- decoding in reverse order: + (mismatch: T=1023 L=1 v=23, checking for another occurence of T=1023) + T=1023 L=11 v=7477656e74797468726565 + (mismatch: T=1042 L=1 v=42, checking for another occurence of T=1042) + T=1042 L=8 v=666f72747974776f + T=1023 L=1 v=23 + (mismatch: T=103 L=1 v=11, checking for another occurence of T=103) + (mismatch: T=103 L=2 v=2222, checking for another occurence of T=103) + T=103 L=3 v=333333 + (mismatch: T=103 L=1 v=11, checking for another occurence of T=103) + T=103 L=2 v=2222 + T=103 L=1 v=11 + (mismatch: T=1002 L=2 v=0101, checking for another occurence of T=1002) + T=1002 L=2 v=2222 + T=1002 L=2 v=0101 + T=1042 L=1 v=42 +- decoding every second tag: + T=1002 L=2 v=0101 + T=103 L=1 v=11 + (mismatch: T=103 L=2 v=2222, checking for another occurence of T=103) + T=103 L=3 v=333333 + T=1042 L=8 v=666f72747974776f +- enforcing order: without restart, a past tag is not parsed again: + osmo_tlv_load_next_by_tag(1042) rc=-ENOENT +=== end: txlxv_tests[1] diff --git a/tests/testsuite.at b/tests/testsuite.at index 09a77c3..86ef4d3 100644 --- a/tests/testsuite.at +++ b/tests/testsuite.at @@ -1,2 +1,8 @@ AT_INIT AT_BANNER([Regression tests.]) + +AT_SETUP([tlv]) +AT_KEYWORDS([tlv]) +cat $abs_srcdir/libosmo-tlv/tlv_test.ok > expout +AT_CHECK([$abs_top_builddir/tests/libosmo-tlv/tlv_test], [], [expout], [ignore]) +AT_CLEANUP