aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--configure.ac1
-rw-r--r--include/osmocom/gtlv/gtlv.h28
-rw-r--r--include/osmocom/gtlv/gtlv_dec_enc.h4
-rw-r--r--include/osmocom/gtlv/gtlv_gen.h12
-rw-r--r--src/libosmo-gtlv/gtlv.c94
-rw-r--r--src/libosmo-gtlv/gtlv_dec_enc.c69
-rw-r--r--src/libosmo-gtlv/gtlv_gen.c13
-rw-r--r--tests/libosmo-gtlv/Makefile.am2
-rw-r--r--tests/libosmo-gtlv/gtlv_dec_enc_test.c18
-rw-r--r--tests/libosmo-gtlv/gtlv_test.c300
-rw-r--r--tests/libosmo-gtlv/gtlv_test.ok67
-rw-r--r--tests/libosmo-gtlv/test_tliv/Makefile.am60
-rw-r--r--tests/libosmo-gtlv/test_tliv/gen__myproto_ies_auto.c71
-rw-r--r--tests/libosmo-gtlv/test_tliv/myproto_ies_custom.c68
-rw-r--r--tests/libosmo-gtlv/test_tliv/myproto_ies_custom.h50
-rw-r--r--tests/libosmo-gtlv/test_tliv/tliv_test.c217
-rw-r--r--tests/libosmo-gtlv/test_tliv/tliv_test.ok32
-rw-r--r--tests/testsuite.at6
18 files changed, 939 insertions, 173 deletions
diff --git a/configure.ac b/configure.ac
index 90170e1..24bd5e4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -96,6 +96,7 @@ AC_OUTPUT(
tests/atlocal
tests/libosmo-gtlv/Makefile
tests/libosmo-gtlv/test_gtlv_gen/Makefile
+ tests/libosmo-gtlv/test_tliv/Makefile
doc/Makefile
contrib/Makefile
Makefile)
diff --git a/include/osmocom/gtlv/gtlv.h b/include/osmocom/gtlv/gtlv.h
index 7acf558..d800f71 100644
--- a/include/osmocom/gtlv/gtlv.h
+++ b/include/osmocom/gtlv/gtlv.h
@@ -25,10 +25,25 @@
#include <stdint.h>
#include <stdio.h>
+#include <stdbool.h>
struct msgb;
struct osmo_gtlv_load;
struct osmo_gtlv_put;
+struct value_string;
+
+struct osmo_gtlv_tag_inst {
+ unsigned int tag;
+ bool instance_present;
+ unsigned int instance;
+};
+
+int osmo_gtlv_tag_inst_cmp(const struct osmo_gtlv_tag_inst *a, const struct osmo_gtlv_tag_inst *b);
+
+int osmo_gtlv_tag_inst_to_str_buf(char *buf, size_t buflen, const struct osmo_gtlv_tag_inst *ti,
+ const struct value_string *tag_names);
+char *osmo_gtlv_tag_inst_to_str_c(void *ctx, const struct osmo_gtlv_tag_inst *ti,
+ const struct value_string *tag_names);
/*! TL configuration for osmo_gtlv_load*() and osmo_gtlv_put*(). Depending on these implementations provided by the caller,
* osmo_gtlv can load any sizes of tag and length fields (that don't surpass the value range of unsigned int and size_t,
@@ -49,6 +64,7 @@ struct osmo_gtlv_cfg {
/*! Read one TL from the start of src_data.
* \param gtlv Return the T (tag) value read from src_data in gtlv->tag.
* Return the L (length) value read from src_data in gtlv->len.
+ * Return the I (instance) value read from src_data in gtlv->len; ignore if there is no I.
* Return the position just after the TL in gtlv->*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.
@@ -68,12 +84,14 @@ struct osmo_gtlv_cfg {
* \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 instance The I value to store in dst_data (if this tag is a TLIV); ignore when not a TLIV.
* \param len The L value to store in dst_data.
* \param gtlv Backpointer to the osmo_gtlv_put struct, including gtlv->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_gtlv_put *gtlv);
+ int (*store_tl)(uint8_t *dst_data, size_t dst_data_avail, const struct osmo_gtlv_tag_inst *ti, size_t len,
+ struct osmo_gtlv_put *gtlv);
};
/*! Configuration that allows parsing an 8bit tag and 8bit length TLV. */
@@ -97,7 +115,7 @@ struct osmo_gtlv_load {
} src;
/*! Return value from last invocation of osmo_gtlv_load_next*(): tag value of parsed IE. */
- unsigned int tag;
+ struct osmo_gtlv_tag_inst ti;
/*! Return value from last invocation of osmo_gtlv_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. */
@@ -114,8 +132,9 @@ static inline void osmo_gtlv_load_start(struct osmo_gtlv_load *gtlv)
}
int osmo_gtlv_load_next(struct osmo_gtlv_load *gtlv);
-int osmo_gtlv_load_peek_tag(const struct osmo_gtlv_load *gtlv);
+int osmo_gtlv_load_peek_tag(const struct osmo_gtlv_load *gtlv, struct osmo_gtlv_tag_inst *ti);
int osmo_gtlv_load_next_by_tag(struct osmo_gtlv_load *gtlv, unsigned int tag);
+int osmo_gtlv_load_next_by_tag_inst(struct osmo_gtlv_load *gtlv, const struct osmo_gtlv_tag_inst *ti);
/* State for storing a TLV structure into a msgb. */
struct osmo_gtlv_put {
@@ -128,10 +147,11 @@ struct osmo_gtlv_put {
/* msgb to append new TL to */
struct msgb *dst;
/* What was the last TL written and where are its TL and V */
- unsigned int last_tag;
+ struct osmo_gtlv_tag_inst last_ti;
uint8_t *last_tl;
uint8_t *last_val;
};
int osmo_gtlv_put_tl(struct osmo_gtlv_put *gtlv, unsigned int tag, size_t len);
+int osmo_gtlv_put_tli(struct osmo_gtlv_put *gtlv, const struct osmo_gtlv_tag_inst *ti, size_t len);
int osmo_gtlv_put_update_tl(struct osmo_gtlv_put *gtlv);
diff --git a/include/osmocom/gtlv/gtlv_dec_enc.h b/include/osmocom/gtlv/gtlv_dec_enc.h
index b861129..132239f 100644
--- a/include/osmocom/gtlv/gtlv_dec_enc.h
+++ b/include/osmocom/gtlv/gtlv_dec_enc.h
@@ -78,8 +78,8 @@ enum osmo_gtlv_coding_nested_ies_ordered {
* that the decoded structs to match the IEs are also generated at the same time and thus always match the message
* definitions. For an example, see tests/libosmo-gtlv/test_gtlv_gen/. */
struct osmo_gtlv_coding {
- /*! the IEI value */
- unsigned int tag;
+ /*! the IEI discriminator, and optional instance number */
+ struct osmo_gtlv_tag_inst ti;
/*! Decoding function callback. Invoked for each defined and present IE encountered in the message.
* Return 0 on success, negative on failure. */
diff --git a/include/osmocom/gtlv/gtlv_gen.h b/include/osmocom/gtlv/gtlv_gen.h
index a2f088b..71cec92 100644
--- a/include/osmocom/gtlv/gtlv_gen.h
+++ b/include/osmocom/gtlv/gtlv_gen.h
@@ -37,6 +37,10 @@ struct osmo_gtlv_gen_ie;
#define OSMO_GTLV_GEN_O_MULTI(MAX, TLV_GEN_IE, MEMB_NAME) { MEMB_NAME, .multi = MAX, .ie = &(TLV_GEN_IE) }
#define OSMO_GTLV_GEN_M_MULTI(MAX, MAND_COUNT, TLV_GEN_IE, MEMB_NAME) \
{ MEMB_NAME, .multi = MAX, .multi_mandatory = MAND_COUNT, .ie = &(TLV_GEN_IE) }
+#define OSMO_GTLV_GEN_O_INST(INSTANCE, TLV_GEN_IE, MEMB_NAME) { MEMB_NAME, .optional = true, .instance = INSTANCE, .ie = &TLV_GEN_IE }
+#define OSMO_GTLV_GEN_M_INST(INSTANCE, TLV_GEN_IE, MEMB_NAME) { MEMB_NAME, .instance = INSTANCE, .ie = &(TLV_GEN_IE) }
+
+#define OSMO_GTLV_GEN_NO_INSTANCE INT_MAX
/*! osmo_gtlv_gen_ie with all members == NULL, so that all are derived from the member name. */
extern const struct osmo_gtlv_gen_ie osmo_gtlv_gen_ie_auto;
@@ -81,6 +85,10 @@ struct osmo_gtlv_gen_ie_o {
/*! Number of mandatory occurences of the IE, only has an effect if .multi > 0. */
unsigned int multi_mandatory;
+ /* If any, the instance nr to match, in C that yields an unsigned int.
+ * e.g. "1" or "MYPROTO_FOO_INST_ONE". */
+ const char *instance;
+
/*! IE decoding / encoding instructions. If NULL, the entire IE definition is derived from .name.
* 'MYPROTO_IEI_NAME', 'myproto_dec_name()', 'myproto_enc_name()', 'myproto_enc_to_str_name()'.
* Your myproto_ies_custom.h needs to define an enum value MYPROTO_IEI_NAME and*/
@@ -96,7 +104,9 @@ struct osmo_gtlv_gen_ie {
* When there are no nested IEs, the type needs to be defined manually by a myproto_ies_custom.h. */
const char *decoded_type;
- /*! C name of this tag value, e.g. "MYPROTO_IEI_FOO". If NULL, take "MYPROTO_IEI_"+upper(name) instead. */
+ /*! C name of this tag value, e.g. "foo" to use tag "MYPROTO_IEI_FOO".
+ * If NULL, take "MYPROTO_IEI_"+upper(memb_name) instead, where memb_name comes from the osmo_gtlv_gen_ie_o.
+ * decoded_type and/or dec_enc may be derived from this, if they are NULL. */
const char *tag_name;
/*! Name suffix of the dec/enc functions. "foo" -> myproto_dec_foo(), myproto_enc_foo(),
diff --git a/src/libosmo-gtlv/gtlv.c b/src/libosmo-gtlv/gtlv.c
index 997a67a..9bdbe75 100644
--- a/src/libosmo-gtlv/gtlv.c
+++ b/src/libosmo-gtlv/gtlv.c
@@ -27,6 +27,45 @@
#include <osmocom/core/msgb.h>
#include <osmocom/gtlv/gtlv.h>
+int osmo_gtlv_tag_inst_cmp(const struct osmo_gtlv_tag_inst *a, const struct osmo_gtlv_tag_inst *b)
+{
+ int cmp;
+ if (a == b)
+ return 0;
+ if (!a)
+ return -1;
+ if (!b)
+ return 1;
+ cmp = OSMO_CMP(a->tag, b->tag);
+ if (cmp)
+ return cmp;
+ cmp = OSMO_CMP(a->instance_present ? 1 : 0, b->instance_present ? 1 : 0);
+ if (cmp)
+ return cmp;
+ if (a->instance_present)
+ return OSMO_CMP(a->instance, b->instance);
+ return 0;
+}
+
+int osmo_gtlv_tag_inst_to_str_buf(char *buf, size_t buflen, const struct osmo_gtlv_tag_inst *ti,
+ const struct value_string *tag_names)
+{
+ struct osmo_strbuf sb = { .buf = buf, .len = buflen };
+ if (!tag_names)
+ OSMO_STRBUF_PRINTF(sb, "%u", ti->tag);
+ else
+ OSMO_STRBUF_PRINTF(sb, "%s", get_value_string(tag_names, ti->tag));
+ if (ti->instance_present)
+ OSMO_STRBUF_PRINTF(sb, "[%u]", ti->instance);
+ return sb.chars_needed;
+}
+
+char *osmo_gtlv_tag_inst_to_str_c(void *ctx, const struct osmo_gtlv_tag_inst *ti,
+ const struct value_string *tag_names)
+{
+ OSMO_NAME_C_IMPL(ctx, 64, "ERROR", osmo_gtlv_tag_inst_to_str_buf, ti, tag_names)
+}
+
static int next_tl_valid(const struct osmo_gtlv_load *gtlv, const uint8_t **ie_start_p, size_t *buflen_left_p)
{
const uint8_t *ie_start;
@@ -102,6 +141,7 @@ int osmo_gtlv_load_next(struct osmo_gtlv_load *gtlv)
/* Locate next IE */
OSMO_ASSERT(gtlv->cfg->load_tl);
+ gtlv->ti = (struct osmo_gtlv_tag_inst){};
rc = gtlv->cfg->load_tl(gtlv, ie_start, buflen_left);
if (rc)
return rc;
@@ -117,16 +157,19 @@ int osmo_gtlv_load_next(struct osmo_gtlv_load *gtlv)
/* Return the tag of the IE that osmo_gtlv_next() would yield, do not change the gtlv state.
*
* \param[in] gtlv state for TLV parsing position; is not modified.
- * \returns the tag number on success, negative on TLV parsing error, -ENOENT when no more tags
- * follow.
+ * \param[out] tag the tag number on success, if NULL don't return the tag.
+ * \param[out] instance the instance number or OSMO_GTLV_NO_INSTANCE if there is no instance value,
+ * if NULL don't return the instance value.
+ * \returns 0 on success, negative on TLV parsing error, -ENOENT when no more tags follow.
*/
-int osmo_gtlv_load_peek_tag(const struct osmo_gtlv_load *gtlv)
+int osmo_gtlv_load_peek_tag(const struct osmo_gtlv_load *gtlv, struct osmo_gtlv_tag_inst *ti)
{
const uint8_t *ie_start;
size_t buflen_left;
int rc;
/* Guard against modification by load_tl(). */
struct osmo_gtlv_load mtlv = *gtlv;
+ mtlv.ti = (struct osmo_gtlv_tag_inst){};
rc = next_tl_valid(&mtlv, &ie_start, &buflen_left);
if (rc)
@@ -140,16 +183,26 @@ int osmo_gtlv_load_peek_tag(const struct osmo_gtlv_load *gtlv)
rc = gtlv->cfg->load_tl(&mtlv, ie_start, buflen_left);
if (rc)
return -EBADMSG;
- return mtlv.tag;
+ if (ti)
+ *ti = mtlv.ti;
+ return 0;
}
/* Same as osmo_gtlv_load_next(), but skip any IEs until the given tag is reached. Change the gtlv state only when success
* is returned.
* \param[out] gtlv Return the next IE's TLV info.
* \param[in] tag Tag value to match.
+ * \param[in] instance Instance value to match; For IEs that have no instance value (no TLIV), pass
+ * OSMO_GTLV_NO_INSTANCE.
* \return 0 when the tag is found. Return -ENOENT when no such tag follows and keep the gtlv unchanged. */
int osmo_gtlv_load_next_by_tag(struct osmo_gtlv_load *gtlv, unsigned int tag)
{
+ struct osmo_gtlv_tag_inst ti = { .tag = tag };
+ return osmo_gtlv_load_next_by_tag_inst(gtlv, &ti);
+}
+
+int osmo_gtlv_load_next_by_tag_inst(struct osmo_gtlv_load *gtlv, const struct osmo_gtlv_tag_inst *ti)
+{
struct osmo_gtlv_load work = *gtlv;
for (;;) {
int rc = osmo_gtlv_load_next(&work);
@@ -157,7 +210,7 @@ int osmo_gtlv_load_next_by_tag(struct osmo_gtlv_load *gtlv, unsigned int tag)
return rc;
if (!work.val)
return -ENOENT;
- if (work.tag == tag) {
+ if (!osmo_gtlv_tag_inst_cmp(&work.ti, ti)) {
*gtlv = work;
return 0;
}
@@ -190,16 +243,25 @@ int osmo_gtlv_load_next_by_tag(struct osmo_gtlv_load *gtlv, unsigned int tag)
*/
int osmo_gtlv_put_tl(struct osmo_gtlv_put *gtlv, unsigned int tag, size_t len)
{
+ struct osmo_gtlv_tag_inst ti = { .tag = tag };
+ return osmo_gtlv_put_tli(gtlv, &ti, len);
+}
+
+/* Put tag header, instance value and length at the end of the msgb, according to gtlv->cfg->store_tl().
+ * This is the same as osmo_gtlv_put_tl(), only osmo_gtlv_put_tl() passes instance = 0.
+ */
+int osmo_gtlv_put_tli(struct osmo_gtlv_put *gtlv, const struct osmo_gtlv_tag_inst *ti, size_t len)
+{
int rc;
uint8_t *last_tl;
OSMO_ASSERT(gtlv->cfg->store_tl);
last_tl = gtlv->dst->tail;
- rc = gtlv->cfg->store_tl(gtlv->dst->tail, msgb_tailroom(gtlv->dst), tag, len, gtlv);
+ rc = gtlv->cfg->store_tl(gtlv->dst->tail, msgb_tailroom(gtlv->dst), ti, len, gtlv);
if (rc < 0)
return rc;
if (rc > 0)
msgb_put(gtlv->dst, rc);
- gtlv->last_tag = tag;
+ gtlv->last_ti = *ti;
gtlv->last_tl = last_tl;
gtlv->last_val = gtlv->dst->tail;
return 0;
@@ -212,7 +274,7 @@ int osmo_gtlv_put_tl(struct osmo_gtlv_put *gtlv, unsigned int tag, size_t len)
int osmo_gtlv_put_update_tl(struct osmo_gtlv_put *gtlv)
{
size_t len = gtlv->dst->tail - gtlv->last_val;
- int rc = gtlv->cfg->store_tl(gtlv->last_tl, gtlv->last_val - gtlv->last_tl, gtlv->last_tag, len, gtlv);
+ int rc = gtlv->cfg->store_tl(gtlv->last_tl, gtlv->last_val - gtlv->last_tl, &gtlv->last_ti, len, gtlv);
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
@@ -224,22 +286,22 @@ int osmo_gtlv_put_update_tl(struct osmo_gtlv_put *gtlv)
static int t8l8v_load_tl(struct osmo_gtlv_load *gtlv, const uint8_t *src_data, size_t src_data_len)
{
/* already validated in next_tl_valid(): src_data_len >= cfg->tl_min_size == 2. */
- gtlv->tag = src_data[0];
+ gtlv->ti.tag = src_data[0];
gtlv->len = src_data[1];
gtlv->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,
+static int t8l8v_store_tl(uint8_t *dst_data, size_t dst_data_avail, const struct osmo_gtlv_tag_inst *ti, size_t len,
struct osmo_gtlv_put *gtlv)
{
- if (tag > UINT8_MAX)
+ if (ti->tag > UINT8_MAX)
return -EINVAL;
if (len > UINT8_MAX)
return -EMSGSIZE;
if (dst_data_avail < 2)
return -ENOSPC;
- dst_data[0] = tag;
+ dst_data[0] = ti->tag;
dst_data[1] = len;
return 2;
}
@@ -253,22 +315,22 @@ const struct osmo_gtlv_cfg osmo_t8l8v_cfg = {
static int t16l16v_load_tl(struct osmo_gtlv_load *gtlv, const uint8_t *src_data, size_t src_data_len)
{
/* already validated in next_tl_valid(): src_data_len >= cfg->tl_min_size == 4. */
- gtlv->tag = osmo_load16be(src_data);
+ gtlv->ti.tag = osmo_load16be(src_data);
gtlv->len = osmo_load16be(src_data + 2);
gtlv->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,
+static int t16l16v_store_tl(uint8_t *dst_data, size_t dst_data_avail, const struct osmo_gtlv_tag_inst *ti, size_t len,
struct osmo_gtlv_put *gtlv)
{
- if (tag > UINT16_MAX)
+ if (ti->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(ti->tag, dst_data);
osmo_store16be(len, dst_data + 2);
return 4;
}
diff --git a/src/libosmo-gtlv/gtlv_dec_enc.c b/src/libosmo-gtlv/gtlv_dec_enc.c
index 28ee493..2e5509a 100644
--- a/src/libosmo-gtlv/gtlv_dec_enc.c
+++ b/src/libosmo-gtlv/gtlv_dec_enc.c
@@ -31,12 +31,21 @@
/* Reverse offsetof(): return the address of the struct member for a given osmo_gtlv_msg and member ofs_foo value. */
#define MEMB(M, MEMB_OFS) ((void *)((char *)(M) + (MEMB_OFS)))
-#define RETURN_ERROR(RC, IEI, FMT, ARGS...) \
+#define RETURN_ERROR(RC, TAG_INST, FMT, ARGS...) \
do {\
- if (err_cb) \
- err_cb(err_cb_data, (void *)decoded_struct, __FILE__, __LINE__, \
- "IE '%s' (0x%x): " FMT " (%d: %s)\n", \
- get_value_string(iei_strs, IEI), IEI, ##ARGS, RC, strerror((RC) > 0 ? (RC) : -(RC))); \
+ if (err_cb) { \
+ if ((TAG_INST).instance_present) \
+ err_cb(err_cb_data, (void *)decoded_struct, __FILE__, __LINE__, \
+ "tag 0x%x = %s instance %u: " FMT " (%d: %s)\n", \
+ (TAG_INST).tag, get_value_string(iei_strs, (TAG_INST).tag), \
+ (TAG_INST).instance, ##ARGS, \
+ RC, strerror((RC) > 0 ? (RC) : -(RC))); \
+ else \
+ err_cb(err_cb_data, (void *)decoded_struct, __FILE__, __LINE__, \
+ "tag 0x%x = %s: " FMT " (%d: %s)\n", \
+ (TAG_INST).tag, get_value_string(iei_strs, (TAG_INST).tag), ##ARGS, \
+ RC, strerror((RC) > 0 ? (RC) : -(RC))); \
+ } \
return RC; \
} while (0)
@@ -72,7 +81,7 @@ static int osmo_gtlvs_decode_unordered(void *decoded_struct, unsigned int obj_of
#define CHECK_SEEN(IEC) do { \
unsigned int ie_coding_idx = (IEC) - ie_coding; \
if (ie_coding_idx >= ARRAY_SIZE(seen_ie_coding_entries)) \
- RETURN_ERROR(-ENOTSUP, gtlv->tag, \
+ RETURN_ERROR(-ENOTSUP, gtlv->ti, \
"Too many IE definitions for decoding an unordered TLV structure"); \
seen_p = &seen_ie_coding_entries[ie_coding_idx]; \
} while (0)
@@ -91,7 +100,7 @@ static int osmo_gtlvs_decode_unordered(void *decoded_struct, unsigned int obj_of
rc = osmo_gtlv_load_next(gtlv);
if (rc)
- RETURN_ERROR(rc, gtlv->tag, "Decoding IEs failed on or after this tag");
+ RETURN_ERROR(rc, gtlv->ti, "Decoding IEs failed on or after this tag");
if (!gtlv->val) {
/* End of the TLV structure */
break;
@@ -103,7 +112,9 @@ static int osmo_gtlvs_decode_unordered(void *decoded_struct, unsigned int obj_of
do {
/* Find the IE coding for this tag */
- for (iec = ie_coding; !osmo_gtlv_coding_end(iec) && iec->tag != gtlv->tag; iec++);
+ for (iec = ie_coding;
+ !osmo_gtlv_coding_end(iec) && osmo_gtlv_tag_inst_cmp(&iec->ti, &gtlv->ti);
+ iec++);
/* No such IE coding found. */
if (osmo_gtlv_coding_end(iec))
break;
@@ -128,7 +139,7 @@ static int osmo_gtlvs_decode_unordered(void *decoded_struct, unsigned int obj_of
if (ie_max_allowed_count) {
/* There have been IE definitions for this IEI, but all slots to decode it are already
* filled. */
- RETURN_ERROR(-ENOTSUP, gtlv->tag, "Only %u instances of this IE are supported per message",
+ RETURN_ERROR(-ENOTSUP, gtlv->ti, "Only %u instances of this IE are supported per message",
ie_max_allowed_count);
}
/* No such IE defined in ie_coding, just skip the TLV. */
@@ -166,14 +177,14 @@ static int osmo_gtlvs_decode_unordered(void *decoded_struct, unsigned int obj_of
rc = osmo_gtlvs_decode(decoded_struct, obj_ofs + memb_ofs, &inner_tlv, ordered, iec->nested_ies,
err_cb, err_cb_data, iei_strs);
if (rc)
- RETURN_ERROR(rc, gtlv->tag, "Error while decoding TLV structure nested inside this IE");
+ RETURN_ERROR(rc, gtlv->ti, "Error while decoding TLV structure nested inside this IE");
} else {
/* Normal IE, decode the specific IE data. */
if (!iec->dec_func)
- RETURN_ERROR(-EIO, gtlv->tag, "IE definition lacks a dec_func()");
+ RETURN_ERROR(-EIO, gtlv->ti, "IE definition lacks a dec_func()");
rc = iec->dec_func(decoded_struct, MEMB(obj, memb_ofs), gtlv);
if (rc)
- RETURN_ERROR(rc, gtlv->tag, "Error while decoding this IE");
+ RETURN_ERROR(rc, gtlv->ti, "Error while decoding this IE");
}
if (multi_count_p) {
@@ -197,14 +208,14 @@ static int osmo_gtlvs_decode_unordered(void *decoded_struct, unsigned int obj_of
multi_count_p = iec->has_count ? MEMB(obj, iec->count_ofs) : NULL;
if (multi_count_p) {
if (*multi_count_p < iec->count_mandatory)
- RETURN_ERROR(-EINVAL, iec->tag, "Error while decoding: %u instances of this IE are mandatory, got %u",
+ RETURN_ERROR(-EINVAL, iec->ti, "%u instances of this IE are mandatory, got %u",
iec->count_mandatory, *multi_count_p);
continue;
}
/* Neither an optional nor a multi member, hence it must be mandatory. */
CHECK_SEEN(iec);
if (!*seen_p)
- RETURN_ERROR(-EINVAL, iec->tag, "Missing mandatory IE");
+ RETURN_ERROR(-EINVAL, iec->ti, "Missing mandatory IE");
}
return 0;
}
@@ -235,19 +246,20 @@ static int osmo_gtlvs_decode_ordered(void *decoded_struct, unsigned int obj_ofs,
int rc;
bool *presence_flag = ie_coding->has_presence_flag ? MEMB(obj, ie_coding->presence_flag_ofs) : NULL;
unsigned int *multi_count = ie_coding->has_count ? MEMB(obj, ie_coding->count_ofs) : NULL;
+ struct osmo_gtlv_tag_inst peek_ti;
- rc = osmo_gtlv_load_next_by_tag(gtlv, ie_coding->tag);
+ rc = osmo_gtlv_load_next_by_tag_inst(gtlv, &ie_coding->ti);
switch (rc) {
case 0:
break;
case -ENOENT:
if (!presence_flag && (!multi_count || *multi_count < ie_coding->count_mandatory))
- RETURN_ERROR(rc, ie_coding->tag, "Missing mandatory IE");
+ RETURN_ERROR(rc, ie_coding->ti, "Missing mandatory IE");
if (presence_flag)
*presence_flag = false;
continue;
default:
- RETURN_ERROR(rc, ie_coding->tag, "Error in TLV structure");
+ RETURN_ERROR(rc, ie_coding->ti, "Error in TLV structure");
}
for (;;) {
@@ -257,7 +269,7 @@ static int osmo_gtlvs_decode_ordered(void *decoded_struct, unsigned int obj_ofs,
unsigned int memb_ofs = ie_coding->memb_ofs + memb_next_array_idx * ie_coding->memb_array_pitch;
if (multi_count && memb_next_array_idx >= ie_coding->count_max)
- RETURN_ERROR(-ENOTSUP, ie_coding->tag, "Only %u instances of this IE are supported per message",
+ RETURN_ERROR(-ENOTSUP, ie_coding->ti, "Only %u instances of this IE are supported per message",
ie_coding->count_max);
/* Decode IE value part */
@@ -286,15 +298,15 @@ static int osmo_gtlvs_decode_ordered(void *decoded_struct, unsigned int obj_ofs,
rc = osmo_gtlvs_decode(decoded_struct, obj_ofs + memb_ofs, &inner_tlv, ordered,
ie_coding->nested_ies, err_cb, err_cb_data, iei_strs);
if (rc)
- RETURN_ERROR(rc, ie_coding->tag,
+ RETURN_ERROR(rc, ie_coding->ti,
"Error while decoding TLV structure nested inside this IE");
} else {
/* Normal IE, decode the specific IE data. */
if (!ie_coding->dec_func)
- RETURN_ERROR(-EIO, ie_coding->tag, "IE definition lacks a dec_func()");
+ RETURN_ERROR(-EIO, ie_coding->ti, "IE definition lacks a dec_func()");
rc = ie_coding->dec_func(decoded_struct, MEMB(obj, memb_ofs), gtlv);
if (rc)
- RETURN_ERROR(rc, ie_coding->tag, "Error while decoding this IE");
+ RETURN_ERROR(rc, ie_coding->ti, "Error while decoding this IE");
}
if (presence_flag)
@@ -312,7 +324,8 @@ static int osmo_gtlvs_decode_ordered(void *decoded_struct, unsigned int obj_ofs,
(*multi_count)++;
/* Does another one of these IEs follow? */
- if (osmo_gtlv_load_peek_tag(gtlv) != gtlv->tag) {
+ if (osmo_gtlv_load_peek_tag(gtlv, &peek_ti)
+ || osmo_gtlv_tag_inst_cmp(&peek_ti, &gtlv->ti)) {
/* Next tag is a different IE, end the repetition. */
break;
}
@@ -387,7 +400,7 @@ int osmo_gtlvs_encode(struct osmo_gtlv_put *gtlv, const void *decoded_struct, un
if (multi_count_p) {
n = *multi_count_p;
if (!ie_coding->memb_array_pitch)
- RETURN_ERROR(-EFAULT, ie_coding->tag,
+ RETURN_ERROR(-EFAULT, ie_coding->ti,
"Error in protocol definition: The ie_coding lacks a memb_array_pitch"
" value, cannot be used as multi-IE\n");
} else {
@@ -397,11 +410,11 @@ int osmo_gtlvs_encode(struct osmo_gtlv_put *gtlv, const void *decoded_struct, un
for (i = 0; i < n; i++) {
unsigned int memb_ofs;
- osmo_gtlv_put_tl(gtlv, ie_coding->tag, 0);
+ osmo_gtlv_put_tli(gtlv, &ie_coding->ti, 0);
/* If this is a repeated IE, encode from the correct array index */
if (multi_count_p && i >= ie_coding->count_max)
- RETURN_ERROR(-ENOTSUP, ie_coding->tag,
+ RETURN_ERROR(-ENOTSUP, ie_coding->ti,
"Only %u instances of this IE are supported per message", ie_coding->count_max);
memb_ofs = ie_coding->memb_ofs + i * ie_coding->memb_array_pitch;
@@ -413,12 +426,12 @@ int osmo_gtlvs_encode(struct osmo_gtlv_put *gtlv, const void *decoded_struct, un
rc = osmo_gtlvs_encode(&nested_tlv, decoded_struct, obj_ofs + memb_ofs,
ie_coding->nested_ies, err_cb, err_cb_data, iei_strs);
if (rc)
- RETURN_ERROR(rc, ie_coding->tag,
+ RETURN_ERROR(rc, ie_coding->ti,
"Error while encoding TLV structure nested inside this IE");
} else {
rc = ie_coding->enc_func(gtlv, decoded_struct, MEMB(obj, memb_ofs));
if (rc)
- RETURN_ERROR(rc, ie_coding->tag, "Error while encoding this IE");
+ RETURN_ERROR(rc, ie_coding->ti, "Error while encoding this IE");
}
osmo_gtlv_put_update_tl(gtlv);
@@ -467,7 +480,7 @@ int osmo_gtlvs_encode_to_str_buf(char *buf, size_t buflen, const void *decoded_s
if (!n)
continue;
- OSMO_STRBUF_PRINTF(sb, " '%s'=", get_value_string(iei_strs, ie_coding->tag));
+ OSMO_STRBUF_PRINTF(sb, " '%s'=", get_value_string(iei_strs, ie_coding->ti.tag));
if (multi_count_p)
OSMO_STRBUF_PRINTF(sb, "{ ");
diff --git a/src/libosmo-gtlv/gtlv_gen.c b/src/libosmo-gtlv/gtlv_gen.c
index 3f499f7..fd3fbd9 100644
--- a/src/libosmo-gtlv/gtlv_gen.c
+++ b/src/libosmo-gtlv/gtlv_gen.c
@@ -249,7 +249,7 @@ static void write_extern_dec_enc(const struct osmo_gtlv_gen_ie_o *ies)
}
/* For a nested IE, write the struct osmo_gtlv_coding array of the inner IEs.
- * { MYPROTO_IEI_BAR,
+ * { { MYPROTO_IEI_BAR },
* .memb_ofs = offsetof(struct myproto_foo, bar),
* .dec_func = myproto_dec_bar,
* .enc_func = myproto_enc_bar,
@@ -263,10 +263,13 @@ static void write_ies_array(const char *indent, const struct osmo_gtlv_gen_ie_o
for (ie_o = ies; ie_o->ie; ie_o++) {
const struct osmo_gtlv_gen_ie *ie = ie_o->ie;
const char *tag_name = (ie && ie->tag_name) ? ie->tag_name : ie_o->name;
- printi("{ %s%s,\n", g_cfg->tag_prefix, osmo_str_toupper(tag_name));
+ printi("{ { %s%s", g_cfg->tag_prefix, osmo_str_toupper(tag_name));
+ if (ie_o->instance)
+ printf(", true, %s", ie_o->instance);
+ printf(" },\n");
printi(" .memb_ofs = offsetof(%s, %s%s),\n", obj_type, substruct, ie_o->name);
- if (ie && ie->nested_ies) {
- printi(" .nested_ies = ies_in_%s,\n", ie->tag_name ? : ie_o->name);
+ if (ie->nested_ies) {
+ printi(" .nested_ies = ies_in_%s,\n", tag_name);
} else {
const char *dec_enc = ie->dec_enc ? : (ie->tag_name ? : ie_o->name);
printi(" .dec_func = %s_dec_%s,\n", g_cfg->proto_name, dec_enc);
@@ -291,7 +294,7 @@ static void write_ies_array(const char *indent, const struct osmo_gtlv_gen_ie_o
/* For a nested IE, write the struct osmo_gtlv_coding array of the inner IEs.
* static const struct osmo_gtlv_coding ies_in_foo[] = {
- * { MYPROTO_IEI_BAR,
+ * { {MYPROTO_IEI_BAR},
* .memb_ofs = offsetof(struct myproto_foo, bar),
* .dec_func = myproto_dec_bar,
* .enc_func = myproto_enc_bar,
diff --git a/tests/libosmo-gtlv/Makefile.am b/tests/libosmo-gtlv/Makefile.am
index 392a73e..45d6ec2 100644
--- a/tests/libosmo-gtlv/Makefile.am
+++ b/tests/libosmo-gtlv/Makefile.am
@@ -1,5 +1,6 @@
SUBDIRS = \
test_gtlv_gen \
+ test_tliv \
$(NULL)
AM_CPPFLAGS = \
@@ -45,3 +46,4 @@ update_exp:
$(builddir)/gtlv_test >$(srcdir)/gtlv_test.ok
$(builddir)/gtlv_dec_enc_test >$(srcdir)/gtlv_dec_enc_test.ok
$(MAKE) -C test_gtlv_gen update_exp
+ $(MAKE) -C test_tliv update_exp
diff --git a/tests/libosmo-gtlv/gtlv_dec_enc_test.c b/tests/libosmo-gtlv/gtlv_dec_enc_test.c
index 89b4011..e02a2e5 100644
--- a/tests/libosmo-gtlv/gtlv_dec_enc_test.c
+++ b/tests/libosmo-gtlv/gtlv_dec_enc_test.c
@@ -205,21 +205,21 @@ int enc_to_str_repeat_struct(char *buf, size_t buflen, const void *encode_from)
struct osmo_gtlv_coding nested_inner_msg_ies[] = {
{
- .tag = TAG_FOO,
+ .ti = { TAG_FOO },
.dec_func = dec_u16,
.enc_func = enc_u16,
.enc_to_str_func = enc_to_str_u16,
.memb_ofs = offsetof(struct nested_inner_msg, foo),
},
{
- .tag = TAG_BAR,
+ .ti = { TAG_BAR },
.dec_func = dec_bar,
.enc_func = enc_bar,
.enc_to_str_func = enc_to_str_bar,
.memb_ofs = offsetof(struct nested_inner_msg, bar),
},
{
- .tag = TAG_BAZ,
+ .ti = { TAG_BAZ },
.dec_func = dec_baz,
.enc_func = enc_baz,
.enc_to_str_func = enc_to_str_baz,
@@ -230,21 +230,21 @@ struct osmo_gtlv_coding nested_inner_msg_ies[] = {
struct osmo_gtlv_coding msg_ie_coding[] = {
{
- .tag = TAG_FOO,
+ .ti = { TAG_FOO },
.dec_func = dec_u16,
.enc_func = enc_u16,
.enc_to_str_func = enc_to_str_u16,
.memb_ofs = offsetof(struct decoded_msg, foo),
},
{
- .tag = TAG_BAR,
+ .ti = { TAG_BAR },
.dec_func = dec_bar,
.enc_func = enc_bar,
.enc_to_str_func = enc_to_str_bar,
.memb_ofs = offsetof(struct decoded_msg, bar),
},
{
- .tag = TAG_BAZ,
+ .ti = { TAG_BAZ },
.dec_func = dec_baz,
.enc_func = enc_baz,
.enc_to_str_func = enc_to_str_baz,
@@ -253,7 +253,7 @@ struct osmo_gtlv_coding msg_ie_coding[] = {
.presence_flag_ofs = offsetof(struct decoded_msg, baz_present),
},
{
- .tag = TAG_REPEAT_INT,
+ .ti = { TAG_REPEAT_INT },
.dec_func = dec_u16,
.enc_func = enc_u16,
.enc_to_str_func = enc_to_str_u16,
@@ -264,7 +264,7 @@ struct osmo_gtlv_coding msg_ie_coding[] = {
.count_max = ARRAY_SIZE(((struct decoded_msg *)0)->repeat_int),
},
{
- .tag = TAG_REPEAT_STRUCT,
+ .ti = { TAG_REPEAT_STRUCT },
.dec_func = dec_repeat_struct,
.enc_func = enc_repeat_struct,
.enc_to_str_func = enc_to_str_repeat_struct,
@@ -275,7 +275,7 @@ struct osmo_gtlv_coding msg_ie_coding[] = {
.count_max = ARRAY_SIZE(((struct decoded_msg *)0)->repeat_struct),
},
{
- .tag = TAG_NEST,
+ .ti = { TAG_NEST },
.memb_ofs = offsetof(struct decoded_msg, nest),
.nested_ies = nested_inner_msg_ies,
.has_presence_flag = true,
diff --git a/tests/libosmo-gtlv/gtlv_test.c b/tests/libosmo-gtlv/gtlv_test.c
index c009850..8023699 100644
--- a/tests/libosmo-gtlv/gtlv_test.c
+++ b/tests/libosmo-gtlv/gtlv_test.c
@@ -33,7 +33,7 @@
void *ctx;
struct ie {
- int tag;
+ struct osmo_gtlv_tag_inst ti;
const char *val;
};
@@ -48,7 +48,7 @@ struct msgb *test_tlv_enc(const struct osmo_gtlv_cfg *cfg, const struct ie *ies)
for (ie = ies; ie->val; ie++) {
/* put header without knowing length yet */
- OSMO_ASSERT(osmo_gtlv_put_tl(&gtlv, ie->tag, 0) == 0);
+ OSMO_ASSERT(osmo_gtlv_put_tli(&gtlv, &ie->ti, 0) == 0);
/* put value data, as much as desired */
msgb_put(gtlv.dst, osmo_hexparse(ie->val, gtlv.dst->tail, msgb_tailroom(gtlv.dst)));
/* update header len from amount of written data */
@@ -80,9 +80,10 @@ void test_tlv_dec(const struct osmo_gtlv_cfg *cfg, const struct ie *ies, struct
/* end of TLV structure? */
if (!gtlv.val)
break;
- printf(" T=%d L=%zu v=%s\n", gtlv.tag, gtlv.len, osmo_hexdump_nospc(gtlv.val, gtlv.len));
- if (gtlv.tag != ie->tag) {
- printf(" ERROR loading TLV structure: expected tag %d, got tag %d\n", ie->tag, gtlv.tag);
+ printf(" T=%s L=%zu", osmo_gtlv_tag_inst_to_str_c(ctx, &gtlv.ti, NULL), gtlv.len);
+ printf(" v=%s\n", osmo_hexdump_nospc(gtlv.val, gtlv.len));
+ if (gtlv.ti.tag != ie->ti.tag) {
+ printf(" ERROR loading TLV structure: expected tag %u, got tag %u\n", ie->ti.tag, gtlv.ti.tag);
exit(1);
}
if (strcmp(ie->val, osmo_hexdump_nospc(gtlv.val, gtlv.len))) {
@@ -107,22 +108,28 @@ void test_tlv_peek(const struct osmo_gtlv_cfg *cfg, const struct ie *ies, struct
ie = ies;
while (1) {
int rc;
- int next_tag = osmo_gtlv_load_peek_tag(&gtlv);
- 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);
+ struct osmo_gtlv_tag_inst next_tag;
+ rc = osmo_gtlv_load_peek_tag(&gtlv, &next_tag);
+ if (rc == -ENOENT) {
+ printf(" peek rc=-ENOENT\n");
+ } else {
+ printf(" peek T=%s", osmo_gtlv_tag_inst_to_str_c(ctx, &next_tag, NULL));
+ printf("\n");
+ }
+
+ if (ie->val && osmo_gtlv_tag_inst_cmp(&next_tag, &ie->ti)) {
+ printf(" ERROR peeking tag: expected tag %s, got tag %s\n",
+ osmo_gtlv_tag_inst_to_str_c(ctx, &ie->ti, NULL),
+ osmo_gtlv_tag_inst_to_str_c(ctx, &next_tag, NULL));
exit(1);
}
- if (!ie->val && next_tag != -ENOENT) {
- printf(" ERROR peeking tag: expected -ENOENT, got tag %d\n", next_tag);
+ if (!ie->val && rc != -ENOENT) {
+ printf(" ERROR peeking tag: expected -ENOENT, got rc=%d, tag %s\n", rc,
+ osmo_gtlv_tag_inst_to_str_c(ctx, &next_tag, NULL));
exit(1);
}
- if (next_tag == -ENOENT)
+ if (rc == -ENOENT)
break;
/* go to the next TLV */
@@ -156,25 +163,32 @@ void test_tlv_dec_by_tag(const struct osmo_gtlv_cfg *cfg, const struct ie *ies,
for (ie = last_ie; ie >= ies; ie--) {
/* each time, look from the beginning */
osmo_gtlv_load_start(&gtlv);
- rc = osmo_gtlv_load_next_by_tag(&gtlv, ie->tag);
+ rc = osmo_gtlv_load_next_by_tag_inst(&gtlv, &ie->ti);
if (rc) {
- printf(" ERROR loading TLV structure: osmo_gtlv_load_next_by_tag(%d) rc = %d\n", ie->tag, rc);
+ printf(" ERROR loading TLV structure: osmo_gtlv_load_next_by_tag_inst(%s) rc = %d\n",
+ osmo_gtlv_tag_inst_to_str_c(ctx, &ie->ti, NULL), rc);
exit(1);
}
if (!gtlv.val) {
- printf(" ERROR loading TLV structure: osmo_gtlv_load_next_by_tag(%d) returned NULL val\n",
- ie->tag);
+ printf(" ERROR loading TLV structure: osmo_gtlv_load_next_by_tag_inst(%s) returned NULL val\n",
+ osmo_gtlv_tag_inst_to_str_c(ctx, &ie->ti, NULL));
exit(1);
}
- if (gtlv.tag != ie->tag) {
- printf(" ERROR loading TLV structure: expected tag %d, got tag %d\n", ie->tag, gtlv.tag);
+ if (osmo_gtlv_tag_inst_cmp(&gtlv.ti, &ie->ti)) {
+ printf(" ERROR loading TLV structure: expected tag %s, got tag %s\n",
+ osmo_gtlv_tag_inst_to_str_c(ctx, &ie->ti, NULL),
+ osmo_gtlv_tag_inst_to_str_c(ctx, &gtlv.ti, NULL));
exit(1);
}
if (strcmp(ie->val, osmo_hexdump_nospc(gtlv.val, gtlv.len))) {
while (1) {
- printf(" (mismatch: T=%d L=%zu v=%s, checking for another occurrence of T=%d)\n",
- gtlv.tag, gtlv.len, osmo_hexdump_nospc(gtlv.val, gtlv.len), gtlv.tag);
- rc = osmo_gtlv_load_next_by_tag(&gtlv, ie->tag);
+ printf(" (mismatch: T=%s L=%zu v=%s, checking for another occurrence of T=%s)\n",
+ osmo_gtlv_tag_inst_to_str_c(ctx, &gtlv.ti, NULL),
+ gtlv.len,
+ osmo_hexdump_nospc(gtlv.val, gtlv.len),
+ osmo_gtlv_tag_inst_to_str_c(ctx, &gtlv.ti, NULL));
+
+ rc = osmo_gtlv_load_next_by_tag_inst(&gtlv, &ie->ti);
if (rc || !gtlv.val) {
printf(" ERROR val not found\n");
exit(1);
@@ -184,7 +198,9 @@ void test_tlv_dec_by_tag(const struct osmo_gtlv_cfg *cfg, const struct ie *ies,
}
}
}
- printf(" T=%d L=%zu v=%s\n", gtlv.tag, gtlv.len, osmo_hexdump_nospc(gtlv.val, gtlv.len));
+ printf(" T=%s L=%zu v=%s\n",
+ osmo_gtlv_tag_inst_to_str_c(ctx, &gtlv.ti, NULL),
+ gtlv.len, osmo_hexdump_nospc(gtlv.val, gtlv.len));
}
printf("- decoding every second tag:\n");
@@ -196,25 +212,32 @@ void test_tlv_dec_by_tag(const struct osmo_gtlv_cfg *cfg, const struct ie *ies,
if (!ie->val)
break;
- rc = osmo_gtlv_load_next_by_tag(&gtlv, ie->tag);
+ rc = osmo_gtlv_load_next_by_tag_inst(&gtlv, &ie->ti);
if (rc) {
- printf(" ERROR loading TLV structure: osmo_gtlv_load_next_by_tag(%d) rc = %d\n", ie->tag, rc);
+ printf(" ERROR loading TLV structure: osmo_gtlv_load_next_by_tag_inst(%s) rc = %d\n",
+ osmo_gtlv_tag_inst_to_str_c(ctx, &ie->ti, NULL), rc);
exit(1);
}
if (!gtlv.val) {
- printf(" ERROR loading TLV structure: osmo_gtlv_load_next_by_tag(%d) returned NULL val\n",
- ie->tag);
+ printf(" ERROR loading TLV structure: osmo_gtlv_load_next_by_tag_inst(%s) returned NULL val\n",
+ osmo_gtlv_tag_inst_to_str_c(ctx, &ie->ti, NULL));
exit(1);
}
- if (gtlv.tag != ie->tag) {
- printf(" ERROR loading TLV structure: expected tag %d, got tag %d\n", ie->tag, gtlv.tag);
+ if (osmo_gtlv_tag_inst_cmp(&gtlv.ti, &ie->ti)) {
+ printf(" ERROR loading TLV structure: expected tag %s, got tag %s\n",
+ osmo_gtlv_tag_inst_to_str_c(ctx, &ie->ti, NULL),
+ osmo_gtlv_tag_inst_to_str_c(ctx, &gtlv.ti, NULL));
exit(1);
}
if (strcmp(ie->val, osmo_hexdump_nospc(gtlv.val, gtlv.len))) {
while (1) {
- printf(" (mismatch: T=%d L=%zu v=%s, checking for another occurrence of T=%d)\n",
- gtlv.tag, gtlv.len, osmo_hexdump_nospc(gtlv.val, gtlv.len), gtlv.tag);
- rc = osmo_gtlv_load_next_by_tag(&gtlv, ie->tag);
+ printf(" (mismatch: T=%s L=%zu v=%s, checking for another occurrence of T=%s)\n",
+ osmo_gtlv_tag_inst_to_str_c(ctx, &gtlv.ti, NULL),
+ gtlv.len,
+ osmo_hexdump_nospc(gtlv.val, gtlv.len),
+ osmo_gtlv_tag_inst_to_str_c(ctx, &gtlv.ti, NULL));
+
+ rc = osmo_gtlv_load_next_by_tag_inst(&gtlv, &ie->ti);
if (rc || !gtlv.val) {
printf(" ERROR val not found\n");
exit(1);
@@ -224,14 +247,16 @@ void test_tlv_dec_by_tag(const struct osmo_gtlv_cfg *cfg, const struct ie *ies,
}
}
}
- printf(" T=%d L=%zu v=%s\n", gtlv.tag, gtlv.len, osmo_hexdump_nospc(gtlv.val, gtlv.len));
+ printf(" T=%s L=%zu v=%s\n",
+ osmo_gtlv_tag_inst_to_str_c(ctx, &gtlv.ti, NULL),
+ gtlv.len, osmo_hexdump_nospc(gtlv.val, gtlv.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_gtlv_load_next_by_tag(&gtlv, ie->tag);
- printf(" osmo_gtlv_load_next_by_tag(%d) rc=", ie->tag);
+ rc = osmo_gtlv_load_next_by_tag_inst(&gtlv, &ie->ti);
+ printf(" osmo_gtlv_load_next_by_tag_inst(%s) rc=", osmo_gtlv_tag_inst_to_str_c(ctx, &ie->ti, NULL));
if (rc == -ENOENT) {
printf("-ENOENT\n");
} else {
@@ -262,14 +287,14 @@ void test_tlv(const char *label, struct ie *tests[], size_t tests_len, const str
struct ie t8l8v_test1[] = {
/* smallest T */
- { 0, "2342" },
+ { {}, "2342" },
/* largest T */
- { 255, "2342" },
+ { {255}, "2342" },
/* smallest V (no V data) */
- { 1, "" },
+ { {1}, "" },
/* largest V, 255 bytes is the largest that an 8bit size length can express. */
- { 123, "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ { {123}, "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
@@ -280,22 +305,22 @@ struct ie t8l8v_test1[] = {
},
/* arbitrary test data */
- { 101, "11" },
- { 102, "2222" },
- { 103, "333333" },
+ { {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" },
+ { {42}, "42" },
+ { {2}, "0101" },
+ { {2}, "2222" },
+ { {3}, "11" },
+ { {3}, "2222" },
+ { {3}, "333333" },
+ { {23}, "23" },
+ { {42}, "666f72747974776f" },
+ { {23}, "7477656e74797468726565" },
{}
};
@@ -311,14 +336,14 @@ void test_t8l8v()
struct ie t16l16v_test1[] = {
/* smallest T */
- { 0, "2342" },
+ { {}, "2342" },
/* largest T */
- { 65535, "2342" },
+ { {65535}, "2342" },
/* smallest V (no V data) */
- { 1, "" },
+ { {1}, "" },
/* 256 bytes is one more than an 8bit size length can express. */
- { 123, "0000000000000000000000000000000000000000000000000000000000000000"
+ { {123}, "0000000000000000000000000000000000000000000000000000000000000000"
"0000000000000000000000000000000000000000000000000000000000000000"
"0000000000000000000000000000000000000000000000000000000000000000"
"0000000000000000000000000000000000000000000000000000000000000000"
@@ -329,22 +354,22 @@ struct ie t16l16v_test1[] = {
},
/* arbitrary test data */
- { 1001, "11" },
- { 1002, "2222" },
- { 1003, "333333" },
+ { {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" },
+ { {1042}, "42" },
+ { {102}, "0101" },
+ { {102}, "2222" },
+ { {103}, "11" },
+ { {103}, "2222" },
+ { {103}, "333333" },
+ { {1023}, "23" },
+ { {1042}, "666f72747974776f" },
+ { {1023}, "7477656e74797468726565" },
{}
};
@@ -360,18 +385,18 @@ void test_t16l16v()
struct ie txlxv_test1[] = {
/* smallest T */
- { 0, "2342" },
+ { {}, "2342" },
/* largest T that still fits in one encoded octet (highest bit serves as flag) */
- { 0x7f, "2342" },
+ { {0x7f}, "2342" },
/* smallest T that needs two octets to be encoded (first octet = 0x80 flag + 0, second octet = 0x1) */
- { 0x80, "2342" },
+ { {0x80}, "2342" },
/* largest T that can be encoded in 16bit - one flag bit. */
- { 0x7fff, "2342" },
+ { {0x7fff}, "2342" },
/* smallest V (no V data) */
- { 1, "" },
+ { {1}, "" },
/* 256 bytes is one more than an 8bit size length can express. */
- { 123, "0000000000000000000000000000000000000000000000000000000000000000"
+ { {123}, "0000000000000000000000000000000000000000000000000000000000000000"
"0000000000000000000000000000000000000000000000000000000000000000"
"0000000000000000000000000000000000000000000000000000000000000000"
"0000000000000000000000000000000000000000000000000000000000000000"
@@ -382,21 +407,21 @@ struct ie txlxv_test1[] = {
},
/* arbitrary test data */
- { 1002, "2222" },
- { 1003, "333333" },
+ { {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" },
+ { {1042}, "42" },
+ { {1002}, "0101" },
+ { {1002}, "2222" },
+ { {103}, "11" },
+ { {103}, "2222" },
+ { {103}, "333333" },
+ { {1023}, "23" },
+ { {1042}, "666f72747974776f" },
+ { {1023}, "7477656e74797468726565" },
{}
};
@@ -413,14 +438,14 @@ int txlxv_load_tl(struct osmo_gtlv_load *gtlv, const uint8_t *src_data, size_t s
if (pos[0] & 0x80) {
if (pos + 2 > end)
return -EINVAL;
- gtlv->tag = (((int)pos[1]) << 7) + (pos[0] & 0x7f);
+ gtlv->ti.tag = (((int)pos[1]) << 7) + (pos[0] & 0x7f);
pos += 2;
} else {
- gtlv->tag = pos[0];
+ gtlv->ti.tag = pos[0];
pos++;
}
- switch (gtlv->tag) {
+ switch (gtlv->ti.tag) {
case 1002:
/* fixed-length IE */
gtlv->len = 2;
@@ -445,10 +470,12 @@ int txlxv_load_tl(struct osmo_gtlv_load *gtlv, const uint8_t *src_data, size_t s
}
/* 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_gtlv_put *gtlv)
+int txlxv_store_tl(uint8_t *dst_data, size_t dst_data_avail, const struct osmo_gtlv_tag_inst *ti, size_t len,
+ struct osmo_gtlv_put *gtlv)
{
uint8_t *pos = dst_data;
uint8_t *end = dst_data + dst_data_avail;
+ unsigned int tag = ti->tag;
if (tag < 0x80) {
if (pos + 1 > end)
return -ENOSPC;
@@ -496,7 +523,95 @@ const struct osmo_gtlv_cfg txlxv_cfg = {
void test_txlxv()
{
- test_tlv("txlxv_tests", txlxv_tests, ARRAY_SIZE(txlxv_tests), &txlxv_cfg);
+ test_tlv(__func__, txlxv_tests, ARRAY_SIZE(txlxv_tests), &txlxv_cfg);
+}
+
+/* Example of defining a TLI, with an instance indicator */
+static int tliv_load_tl(struct osmo_gtlv_load *gtlv, const uint8_t *src_data, size_t src_data_len)
+{
+ /* already validated in next_tl_valid(): src_data_len >= cfg->tl_min_size == 2. */
+ gtlv->ti.tag = src_data[0];
+ gtlv->len = src_data[1];
+
+ switch (gtlv->ti.tag) {
+ /* All tags that are TLIV go here */
+ case 5:
+ case 7:
+ case 9:
+ if (src_data_len < 3)
+ return -ENOSPC;
+ gtlv->ti.instance_present = true;
+ gtlv->ti.instance = src_data[2];
+ gtlv->val = src_data + 3;
+ return 0;
+ default:
+ gtlv->val = src_data + 2;
+ return 0;
+ }
+}
+
+static int tliv_store_tl(uint8_t *dst_data, size_t dst_data_avail, const struct osmo_gtlv_tag_inst *ti, size_t len,
+ struct osmo_gtlv_put *gtlv)
+{
+ if (ti->tag > UINT8_MAX)
+ return -EINVAL;
+ if (len > UINT8_MAX)
+ return -EMSGSIZE;
+ if (dst_data_avail < 2)
+ return -ENOSPC;
+
+ dst_data[0] = ti->tag;
+ dst_data[1] = len;
+
+ switch (ti->tag) {
+ /* All tags that are TLIV go here */
+ case 5:
+ case 7:
+ case 9:
+ if (dst_data_avail < 3)
+ return -ENOSPC;
+ if (!ti->instance_present)
+ return -EINVAL;
+ if (ti->instance > UINT8_MAX)
+ return -EINVAL;
+ dst_data[2] = ti->instance;
+ return 3;
+ default:
+ return 2;
+ }
+}
+
+const struct osmo_gtlv_cfg osmo_tliv_cfg = {
+ .tl_min_size = 2,
+ .load_tl = tliv_load_tl,
+ .store_tl = tliv_store_tl,
+};
+
+struct ie tliv_test1[] = {
+ /* TLV */
+ { {1}, "0002" },
+ /* TLIV */
+ { {5, true, 1}, "0017" },
+ /* TLIV */
+ { {5, true, 2}, "0018" },
+ /* TLIV */
+ { {5, true, 3}, "0019" },
+ /* TLV */
+ { {6}, "001a" },
+ /* TLIV */
+ { {7, true, 1}, "001b" },
+ /* TLIV */
+ { {9, true, 1}, "001c" },
+ {}
+};
+
+struct ie *tliv_tests[] = {
+ tliv_test1,
+};
+
+void test_tliv()
+{
+ test_tlv(__func__, tliv_tests, ARRAY_SIZE(tliv_tests), &osmo_tliv_cfg);
}
int main()
@@ -507,6 +622,7 @@ int main()
test_t8l8v();
test_t16l16v();
test_txlxv();
+ test_tliv();
talloc_free(ctx);
return 0;
diff --git a/tests/libosmo-gtlv/gtlv_test.ok b/tests/libosmo-gtlv/gtlv_test.ok
index 3cebd7e..edc6ff3 100644
--- a/tests/libosmo-gtlv/gtlv_test.ok
+++ b/tests/libosmo-gtlv/gtlv_test.ok
@@ -17,7 +17,7 @@
peek T=101
peek T=102
peek T=103
- peek T=-ENOENT
+ peek rc=-ENOENT
- decoding in reverse order:
T=103 L=3 v=333333
T=102 L=2 v=2222
@@ -31,7 +31,7 @@
T=123 L=255 v=ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
T=102 L=2 v=2222
- enforcing order: without restart, a past tag is not parsed again:
- osmo_gtlv_load_next_by_tag(0) rc=-ENOENT
+ osmo_gtlv_load_next_by_tag_inst(0) rc=-ENOENT
=== end: test_t8l8v[0]
=== start: test_t8l8v[1]
@@ -56,7 +56,7 @@
peek T=23
peek T=42
peek T=23
- peek T=-ENOENT
+ peek rc=-ENOENT
- decoding in reverse order:
(mismatch: T=23 L=1 v=23, checking for another occurrence of T=23)
T=23 L=11 v=7477656e74797468726565
@@ -80,7 +80,7 @@
T=3 L=3 v=333333
T=42 L=8 v=666f72747974776f
- enforcing order: without restart, a past tag is not parsed again:
- osmo_gtlv_load_next_by_tag(42) rc=-ENOENT
+ osmo_gtlv_load_next_by_tag_inst(42) rc=-ENOENT
=== end: test_t8l8v[1]
=== start: test_t16l16v[0]
@@ -101,7 +101,7 @@
peek T=1001
peek T=1002
peek T=1003
- peek T=-ENOENT
+ peek rc=-ENOENT
- decoding in reverse order:
T=1003 L=3 v=333333
T=1002 L=2 v=2222
@@ -115,7 +115,7 @@
T=123 L=256 v=00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
T=1002 L=2 v=2222
- enforcing order: without restart, a past tag is not parsed again:
- osmo_gtlv_load_next_by_tag(0) rc=-ENOENT
+ osmo_gtlv_load_next_by_tag_inst(0) rc=-ENOENT
=== end: test_t16l16v[0]
=== start: test_t16l16v[1]
@@ -140,7 +140,7 @@
peek T=1023
peek T=1042
peek T=1023
- peek T=-ENOENT
+ peek rc=-ENOENT
- decoding in reverse order:
(mismatch: T=1023 L=1 v=23, checking for another occurrence of T=1023)
T=1023 L=11 v=7477656e74797468726565
@@ -164,10 +164,10 @@
T=103 L=3 v=333333
T=1042 L=8 v=666f72747974776f
- enforcing order: without restart, a past tag is not parsed again:
- osmo_gtlv_load_next_by_tag(1042) rc=-ENOENT
+ osmo_gtlv_load_next_by_tag_inst(1042) rc=-ENOENT
=== end: test_t16l16v[1]
-=== start: txlxv_tests[0]
+=== start: test_txlxv[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
@@ -187,7 +187,7 @@
peek T=123
peek T=1002
peek T=1003
- peek T=-ENOENT
+ peek rc=-ENOENT
- decoding in reverse order:
T=1003 L=3 v=333333
T=1002 L=2 v=2222
@@ -203,10 +203,10 @@
T=123 L=256 v=00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
T=1003 L=3 v=333333
- enforcing order: without restart, a past tag is not parsed again:
- osmo_gtlv_load_next_by_tag(0) rc=-ENOENT
-=== end: txlxv_tests[0]
+ osmo_gtlv_load_next_by_tag_inst(0) rc=-ENOENT
+=== end: test_txlxv[0]
-=== start: txlxv_tests[1]
+=== start: test_txlxv[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
@@ -228,7 +228,7 @@
peek T=1023
peek T=1042
peek T=1023
- peek T=-ENOENT
+ peek rc=-ENOENT
- decoding in reverse order:
(mismatch: T=1023 L=1 v=23, checking for another occurrence of T=1023)
T=1023 L=11 v=7477656e74797468726565
@@ -252,5 +252,40 @@
T=103 L=3 v=333333
T=1042 L=8 v=666f72747974776f
- enforcing order: without restart, a past tag is not parsed again:
- osmo_gtlv_load_next_by_tag(1042) rc=-ENOENT
-=== end: txlxv_tests[1]
+ osmo_gtlv_load_next_by_tag_inst(1042) rc=-ENOENT
+=== end: test_txlxv[1]
+
+=== start: test_tliv[0]
+- encoded: 01 02 00 02 05 02 01 00 17 05 02 02 00 18 05 02 03 00 19 06 02 00 1a 07 02 01 00 1b 09 02 01 00 1c .
+- decoding:
+ T=1 L=2 v=0002
+ T=5[1] L=2 v=0017
+ T=5[2] L=2 v=0018
+ T=5[3] L=2 v=0019
+ T=6 L=2 v=001a
+ T=7[1] L=2 v=001b
+ T=9[1] L=2 v=001c
+- peeking:
+ peek T=1
+ peek T=5[1]
+ peek T=5[2]
+ peek T=5[3]
+ peek T=6
+ peek T=7[1]
+ peek T=9[1]
+ peek rc=-ENOENT
+- decoding in reverse order:
+ T=9[1] L=2 v=001c
+ T=7[1] L=2 v=001b
+ T=6 L=2 v=001a
+ T=5[3] L=2 v=0019
+ T=5[2] L=2 v=0018
+ T=5[1] L=2 v=0017
+ T=1 L=2 v=0002
+- decoding every second tag:
+ T=5[1] L=2 v=0017
+ T=5[3] L=2 v=0019
+ T=7[1] L=2 v=001b
+- enforcing order: without restart, a past tag is not parsed again:
+ osmo_gtlv_load_next_by_tag_inst(1) rc=-ENOENT
+=== end: test_tliv[0]
diff --git a/tests/libosmo-gtlv/test_tliv/Makefile.am b/tests/libosmo-gtlv/test_tliv/Makefile.am
new file mode 100644
index 0000000..eb95e12
--- /dev/null
+++ b/tests/libosmo-gtlv/test_tliv/Makefile.am
@@ -0,0 +1,60 @@
+AM_CPPFLAGS = \
+ $(all_includes) \
+ -I$(top_srcdir)/include \
+ -I$(bulddir) \
+ $(NULL)
+
+AM_CFLAGS = \
+ -Wall \
+ $(LIBOSMOCORE_CFLAGS) \
+ $(NULL)
+
+noinst_PROGRAMS = \
+ gen__myproto_ies_auto \
+ tliv_test \
+ $(NULL)
+
+EXTRA_DIST = \
+ myproto_ies_custom.h \
+ tliv_test.ok \
+ $(NULL)
+
+BUILT_SOURCES = \
+ myproto_ies_auto.h \
+ myproto_ies_auto.c \
+ $(NULL)
+
+CLEANFILES = \
+ myproto_ies_auto.h \
+ myproto_ies_auto.c \
+ $(NULL)
+
+gen__myproto_ies_auto_SOURCES = \
+ gen__myproto_ies_auto.c \
+ myproto_ies_custom.c \
+ $(NULL)
+
+gen__myproto_ies_auto_LDADD = \
+ $(top_builddir)/src/libosmo-gtlv/libosmo-gtlv.a \
+ $(LIBOSMOCORE_LIBS) \
+ $(NULL)
+
+myproto_ies_auto.h: $(builddir)/gen__myproto_ies_auto
+ $(builddir)/gen__myproto_ies_auto h > $(builddir)/myproto_ies_auto.h
+myproto_ies_auto.c: $(builddir)/gen__myproto_ies_auto
+ $(builddir)/gen__myproto_ies_auto c > $(builddir)/myproto_ies_auto.c
+
+tliv_test_SOURCES = \
+ tliv_test.c \
+ myproto_ies_custom.c \
+ myproto_ies_auto.c \
+ $(NULL)
+
+tliv_test_LDADD = \
+ $(top_builddir)/src/libosmo-gtlv/libosmo-gtlv.a \
+ $(LIBOSMOCORE_LIBS) \
+ $(NULL)
+
+.PHONY: update_exp
+update_exp:
+ $(builddir)/tliv_test >$(srcdir)/tliv_test.ok
diff --git a/tests/libosmo-gtlv/test_tliv/gen__myproto_ies_auto.c b/tests/libosmo-gtlv/test_tliv/gen__myproto_ies_auto.c
new file mode 100644
index 0000000..21e70d9
--- /dev/null
+++ b/tests/libosmo-gtlv/test_tliv/gen__myproto_ies_auto.c
@@ -0,0 +1,71 @@
+/*
+ * (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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/gtlv/gtlv_gen.h>
+
+#define O OSMO_GTLV_GEN_O
+#define M OSMO_GTLV_GEN_M
+#define O_MULTI OSMO_GTLV_GEN_O_MULTI
+#define M_MULTI OSMO_GTLV_GEN_M_MULTI
+#define O_INST OSMO_GTLV_GEN_O_INST
+#define M_INST OSMO_GTLV_GEN_M_INST
+
+#define AUTO osmo_gtlv_gen_ie_auto
+
+static const struct osmo_gtlv_gen_ie bar = {
+ .tag_name = "bar",
+};
+
+static const struct osmo_gtlv_gen_ie_o ies_in_moo_msg[] = {
+ M_INST("MYPROTO_IEI_BAR_ALPHA", bar, "bar_alpha"),
+ O_INST("MYPROTO_IEI_BAR_BETA", bar, "bar_beta"),
+ M_INST("MYPROTO_IEI_BAR_GAMMA", bar, "bar_gamma"),
+ {}
+};
+
+static const struct osmo_gtlv_gen_msg msg_defs[] = {
+ { "moo", ies_in_moo_msg },
+ {}
+};
+
+int main(int argc, const char **argv)
+{
+ struct osmo_gtlv_gen_cfg cfg = {
+ .proto_name = "myproto",
+ .message_type_enum = "enum myproto_msg_type",
+ .message_type_prefix = "MYPROTO_MSGT_",
+ .tag_enum = "enum myproto_iei",
+ .tag_prefix = "MYPROTO_IEI_",
+ .decoded_type_prefix = "struct myproto_ie_",
+ .h_header = "#include \"myproto_ies_custom.h\"",
+ .c_header = "#include <myproto_ies_auto.h>",
+ .msg_defs = msg_defs,
+ .add_enc_to_str = true,
+ };
+ return osmo_gtlv_gen_main(&cfg, argc, argv);
+}
diff --git a/tests/libosmo-gtlv/test_tliv/myproto_ies_custom.c b/tests/libosmo-gtlv/test_tliv/myproto_ies_custom.c
new file mode 100644
index 0000000..54164ce
--- /dev/null
+++ b/tests/libosmo-gtlv/test_tliv/myproto_ies_custom.c
@@ -0,0 +1,68 @@
+/* Example for defining custom IES for gtlv_gen. */
+/*
+ * (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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <errno.h>
+#include <inttypes.h>
+
+#include <osmocom/core/bits.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/gtlv/gtlv.h>
+
+#include <myproto_ies_custom.h>
+
+int myproto_dec_bar(void *decoded_struct, void *decode_to, const struct osmo_gtlv_load *gtlv)
+{
+ struct myproto_ie_bar *bar = decode_to;
+ if (gtlv->len < 2)
+ return -EINVAL;
+ *bar = (struct myproto_ie_bar){
+ .a = gtlv->val[0],
+ .b = (gtlv->val[1] == 1),
+ };
+ return 0;
+}
+
+int myproto_enc_bar(struct osmo_gtlv_put *gtlv, void *decoded_struct, void *encode_from)
+{
+ struct myproto_ie_bar *bar = encode_from;
+ msgb_put_u8(gtlv->dst, bar->a);
+ msgb_put_u8(gtlv->dst, bar->b ? 1 : 0);
+ return 0;
+}
+
+int myproto_enc_to_str_bar(char *buf, size_t buflen, void *encode_from)
+{
+ struct myproto_ie_bar *bar = encode_from;
+ return snprintf(buf, buflen, "%d,%s", bar->a, bar->b ? "true" : "false");
+}
+
+const struct value_string myproto_msg_type_names[] = {
+ { MYPROTO_MSGT_MOO, "MOO" },
+ {}
+};
+
+const struct value_string myproto_iei_names[] = {
+ { MYPROTO_IEI_BAR, "BAR" },
+ {}
+};
diff --git a/tests/libosmo-gtlv/test_tliv/myproto_ies_custom.h b/tests/libosmo-gtlv/test_tliv/myproto_ies_custom.h
new file mode 100644
index 0000000..64e06ff
--- /dev/null
+++ b/tests/libosmo-gtlv/test_tliv/myproto_ies_custom.h
@@ -0,0 +1,50 @@
+/* Definitions for decoded message IEs, to be used by the auto-generated myproto_ies_auto.c. */
+/*
+ * (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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#pragma once
+
+#include <osmocom/core/utils.h>
+
+enum myproto_msg_type {
+ MYPROTO_MSGT_MOO = 1,
+};
+
+extern const struct value_string myproto_msg_type_names[];
+
+enum myproto_iei {
+ MYPROTO_IEI_BAR = 1,
+};
+
+enum myproto_iei_bar_inst {
+ MYPROTO_IEI_BAR_ALPHA = 2,
+ MYPROTO_IEI_BAR_BETA = 3,
+ MYPROTO_IEI_BAR_GAMMA = 5,
+};
+
+extern const struct value_string myproto_iei_names[];
+
+struct myproto_ie_bar {
+ int a;
+ bool b;
+};
diff --git a/tests/libosmo-gtlv/test_tliv/tliv_test.c b/tests/libosmo-gtlv/test_tliv/tliv_test.c
new file mode 100644
index 0000000..fd4e310
--- /dev/null
+++ b/tests/libosmo-gtlv/test_tliv/tliv_test.c
@@ -0,0 +1,217 @@
+/*
+ * (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, 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/gtlv/gtlv.h>
+
+#include <myproto_ies_auto.h>
+
+struct myproto_msg {
+ enum myproto_msg_type type;
+ union myproto_ies ies;
+};
+
+static void err_cb(void *data, void *decoded_struct, const char *file, int line, const char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ //printf("ERR: %s:%d ", file, line);
+ printf("ERR: ");
+ vprintf(fmt, args);
+ va_end(args);
+}
+
+static int myproto_msg_enc(struct msgb *dst, const struct myproto_msg *msg, const struct osmo_gtlv_cfg *cfg)
+{
+ struct osmo_gtlv_put gtlv = {
+ .cfg = cfg,
+ .dst = dst,
+ };
+
+ msgb_put_u8(gtlv.dst, msg->type);
+ return myproto_ies_encode(&gtlv, (void *)&msg->ies, msg->type, err_cb, NULL, myproto_iei_names);
+}
+
+static int myproto_msg_dec(struct myproto_msg *msg, const uint8_t *data, size_t data_len,
+ const struct osmo_gtlv_cfg *cfg, bool ordered)
+{
+ struct osmo_gtlv_load gtlv;
+ if (data_len < 1)
+ return -EINVAL;
+ msg->type = data[0];
+ gtlv = (struct osmo_gtlv_load){
+ .cfg = cfg,
+ .src = { data + 1, data_len - 1 },
+ };
+ return myproto_ies_decode(&msg->ies, &gtlv, ordered, msg->type, err_cb, NULL, myproto_iei_names);
+}
+
+void *ctx;
+
+struct myproto_msg tests[] = {
+ {
+ MYPROTO_MSGT_MOO,
+ {
+ .moo = {
+ .bar_alpha = { 23, true },
+ .bar_gamma = { 42, false },
+ },
+ },
+ },
+ {
+ MYPROTO_MSGT_MOO,
+ {
+ .moo = {
+ .bar_alpha = { 11, true },
+ .bar_beta_present = true,
+ .bar_beta = { 22, false },
+ .bar_gamma = { 33, true },
+ },
+ },
+ },
+};
+
+int myproto_msg_to_str_buf(char *buf, size_t buflen, const struct myproto_msg *m)
+{
+ struct osmo_strbuf sb = { .buf = buf, .len = buflen };
+ OSMO_STRBUF_PRINTF(sb, "%s={", get_value_string(myproto_msg_type_names, m->type));
+ OSMO_STRBUF_APPEND(sb, osmo_gtlvs_encode_to_str_buf, &m->ies, 0, myproto_get_msg_coding(m->type),
+ myproto_iei_names);
+ OSMO_STRBUF_PRINTF(sb, " }");
+ return sb.chars_needed;
+
+}
+
+char *myproto_msg_to_str(const struct myproto_msg *m)
+{
+ OSMO_NAME_C_IMPL(ctx, 256, "ERROR", myproto_msg_to_str_buf, m)
+}
+
+void test_enc_dec(const char *label, const struct osmo_gtlv_cfg *cfg, bool ordered)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(tests); i++) {
+ int rc;
+ const struct myproto_msg *orig = &tests[i];
+ struct myproto_msg parsed = {};
+ struct msgb *msg;
+
+ printf("\n=== start %s %s[%d]\n", label, __func__, i);
+ printf("encoded: %s\n", myproto_msg_to_str(orig));
+
+ msg = msgb_alloc(1024, __func__);
+ rc = myproto_msg_enc(msg, orig, cfg);
+ printf("myproto_msg_enc() rc = %d\n", rc);
+ printf("%s.\n", osmo_hexdump(msg->data, msg->len));
+
+ rc = myproto_msg_dec(&parsed, msg->data, msg->len, cfg, ordered);
+ printf("myproto_msg_dec() rc = %d\n", rc);
+ printf("decoded: %s\n", myproto_msg_to_str(&parsed));
+ if (strcmp(myproto_msg_to_str(orig), myproto_msg_to_str(&parsed))) {
+ printf(" ERROR: parsed != orig\n");
+ exit(1);
+ }
+
+ msgb_free(msg);
+ printf("=== end %s %s[%d]\n", label, __func__, i);
+ }
+}
+
+/* Example of defining a TLI, with an instance indicator */
+static int tliv_load_tl(struct osmo_gtlv_load *gtlv, const uint8_t *src_data, size_t src_data_len)
+{
+ /* already validated in next_tl_valid(): src_data_len >= cfg->tl_min_size == 2. */
+ gtlv->ti.tag = src_data[0];
+ gtlv->len = src_data[1];
+
+ switch (gtlv->ti.tag) {
+ /* All tags that are TLIV go here */
+ case MYPROTO_IEI_BAR:
+ if (src_data_len < 3)
+ return -ENOSPC;
+ gtlv->ti.instance_present = true;
+ gtlv->ti.instance = src_data[2];
+ gtlv->val = src_data + 3;
+ /* In this example, the I is part of the len */
+ gtlv->len--;
+ return 0;
+ default:
+ gtlv->val = src_data + 2;
+ return 0;
+ }
+}
+
+static int tliv_store_tl(uint8_t *dst_data, size_t dst_data_avail, const struct osmo_gtlv_tag_inst *ti, size_t len,
+ struct osmo_gtlv_put *gtlv)
+{
+ if (ti->tag > UINT8_MAX)
+ return -EINVAL;
+ if (len > UINT8_MAX)
+ return -EMSGSIZE;
+ if (dst_data_avail < 2)
+ return -ENOSPC;
+
+ dst_data[0] = ti->tag;
+
+ switch (ti->tag) {
+ /* All tags that are TLIV go here */
+ case MYPROTO_IEI_BAR:
+ if (dst_data_avail < 3)
+ return -ENOSPC;
+ if (!ti->instance_present)
+ return -EINVAL;
+ if (ti->instance > UINT8_MAX)
+ return -EINVAL;
+ /* here, I is part of the len in L; the passed len reflects only the value, so add 1 for I */
+ dst_data[1] = len + 1;
+ dst_data[2] = ti->instance;
+ return 3;
+ default:
+ dst_data[1] = len;
+ return 2;
+ }
+}
+
+const struct osmo_gtlv_cfg osmo_tliv_cfg = {
+ .tl_min_size = 2,
+ .load_tl = tliv_load_tl,
+ .store_tl = tliv_store_tl,
+};
+
+int main()
+{
+ ctx = talloc_named_const(NULL, 0, "test_gen_tlv");
+ msgb_talloc_ctx_init(ctx, 0);
+
+ test_enc_dec("tliv ordered", &osmo_tliv_cfg, true);
+ test_enc_dec("tliv unordered", &osmo_tliv_cfg, false);
+
+ talloc_free(ctx);
+ return 0;
+}
diff --git a/tests/libosmo-gtlv/test_tliv/tliv_test.ok b/tests/libosmo-gtlv/test_tliv/tliv_test.ok
new file mode 100644
index 0000000..184ffa6
--- /dev/null
+++ b/tests/libosmo-gtlv/test_tliv/tliv_test.ok
@@ -0,0 +1,32 @@
+
+=== start tliv ordered test_enc_dec[0]
+encoded: MOO={ 'BAR'=23,true 'BAR'=42,false }
+myproto_msg_enc() rc = 0
+01 01 03 02 17 01 01 03 05 2a 00 .
+myproto_msg_dec() rc = 0
+decoded: MOO={ 'BAR'=23,true 'BAR'=42,false }
+=== end tliv ordered test_enc_dec[0]
+
+=== start tliv ordered test_enc_dec[1]
+encoded: MOO={ 'BAR'=11,true 'BAR'=22,false 'BAR'=33,true }
+myproto_msg_enc() rc = 0
+01 01 03 02 0b 01 01 03 03 16 00 01 03 05 21 01 .
+myproto_msg_dec() rc = 0
+decoded: MOO={ 'BAR'=11,true 'BAR'=22,false 'BAR'=33,true }
+=== end tliv ordered test_enc_dec[1]
+
+=== start tliv unordered test_enc_dec[0]
+encoded: MOO={ 'BAR'=23,true 'BAR'=42,false }
+myproto_msg_enc() rc = 0
+01 01 03 02 17 01 01 03 05 2a 00 .
+myproto_msg_dec() rc = 0
+decoded: MOO={ 'BAR'=23,true 'BAR'=42,false }
+=== end tliv unordered test_enc_dec[0]
+
+=== start tliv unordered test_enc_dec[1]
+encoded: MOO={ 'BAR'=11,true 'BAR'=22,false 'BAR'=33,true }
+myproto_msg_enc() rc = 0
+01 01 03 02 0b 01 01 03 03 16 00 01 03 05 21 01 .
+myproto_msg_dec() rc = 0
+decoded: MOO={ 'BAR'=11,true 'BAR'=22,false 'BAR'=33,true }
+=== end tliv unordered test_enc_dec[1]
diff --git a/tests/testsuite.at b/tests/testsuite.at
index 7cc652e..0d17305 100644
--- a/tests/testsuite.at
+++ b/tests/testsuite.at
@@ -18,3 +18,9 @@ AT_KEYWORDS([gtlv_gen])
cat $abs_srcdir/libosmo-gtlv/test_gtlv_gen/gtlv_gen_test.ok > expout
AT_CHECK([$abs_top_builddir/tests/libosmo-gtlv/test_gtlv_gen/gtlv_gen_test], [], [expout], [ignore])
AT_CLEANUP
+
+AT_SETUP([tliv])
+AT_KEYWORDS([tliv])
+cat $abs_srcdir/libosmo-gtlv/test_tliv/tliv_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/libosmo-gtlv/test_tliv/tliv_test], [], [expout], [ignore])
+AT_CLEANUP