neels has submitted this change. (
https://gerrit.osmocom.org/c/osmo-upf/+/27529 )
Change subject: libosmo-gtlv: add TLIV capability
......................................................................
libosmo-gtlv: add TLIV capability
During code review, it was indicated that some TLV protocols that we
will likely deal with in the near future also employ an I, and instance
value of a tag. Add TLIV support.
A usage example for a manually implemented TLIV structure is found in
tests/libosmo-gtlv/gtlv_test.c.
A usage example for a generated TLIV protocol is found in
tests/libosmo-gtlv/test_tliv/.
Related: SYS#5599
Change-Id: I0a076e54dfba6038cc779cb7c8f3967d212226aa
---
M configure.ac
M include/osmocom/gtlv/gtlv.h
M include/osmocom/gtlv/gtlv_dec_enc.h
M include/osmocom/gtlv/gtlv_gen.h
M src/libosmo-gtlv/gtlv.c
M src/libosmo-gtlv/gtlv_dec_enc.c
M src/libosmo-gtlv/gtlv_gen.c
M tests/libosmo-gtlv/Makefile.am
M tests/libosmo-gtlv/gtlv_dec_enc_test.c
M tests/libosmo-gtlv/gtlv_test.c
M tests/libosmo-gtlv/gtlv_test.ok
A tests/libosmo-gtlv/test_tliv/Makefile.am
A tests/libosmo-gtlv/test_tliv/gen__myproto_ies_auto.c
A tests/libosmo-gtlv/test_tliv/myproto_ies_custom.c
A tests/libosmo-gtlv/test_tliv/myproto_ies_custom.h
A tests/libosmo-gtlv/test_tliv/tliv_test.c
A tests/libosmo-gtlv/test_tliv/tliv_test.ok
M tests/testsuite.at
18 files changed, 938 insertions(+), 172 deletions(-)
Approvals:
Jenkins Builder: Verified
pespin: Looks good to me, but someone else must approve
laforge: Looks good to me, but someone else must approve
neels: Looks good to me, approved
diff --git a/configure.ac b/configure.ac
index fe3d6e9..ad97ab0 100644
--- a/configure.ac
+++ b/configure.ac
@@ -206,6 +206,7 @@
tests/atlocal
tests/libosmo-gtlv/Makefile
tests/libosmo-gtlv/test_gtlv_gen/Makefile
+ tests/libosmo-gtlv/test_tliv/Makefile
doc/Makefile
doc/examples/Makefile
doc/manuals/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 @@
/*! 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 @@
* \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 @@
} 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 @@
}
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 @@
/* 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 @@
* 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 @@
#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 @@
/*! 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 @@
* 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 @@
/* 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 @@
/* 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 @@
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 @@
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_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_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, >lv->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 @@
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 @@
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 @@
#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 @@
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 @@
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,
>lv->ti);
+ iec++);
/* No such IE coding found. */
if (osmo_gtlv_coding_end(iec))
break;
@@ -128,7 +139,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
(*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, >lv->ti)) {
/* Next tag is a different IE, end the repetition. */
break;
}
@@ -387,7 +400,7 @@
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 @@
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 @@
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 @@
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 @@
}
/* 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 @@
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 @@
/* 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 @@
$(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 @@
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 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 @@
.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 @@
.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 @@
.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 @@
for (ie = ies; ie->val; ie++) {
/* put header without knowing length yet */
- OSMO_ASSERT(osmo_gtlv_put_tl(>lv, ie->tag, 0) == 0);
+ OSMO_ASSERT(osmo_gtlv_put_tli(>lv, &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 @@
/* 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, >lv.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 @@
ie = ies;
while (1) {
int rc;
- int next_tag = osmo_gtlv_load_peek_tag(>lv);
- if (next_tag == -ENOENT)
- printf(" peek T=-ENOENT\n");
- else
- printf(" peek T=%d\n", next_tag);
+ struct osmo_gtlv_tag_inst next_tag;
+ rc = osmo_gtlv_load_peek_tag(>lv, &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 && next_tag != ie->tag) {
- printf(" ERROR peeking tag: expected tag %d, got tag %d\n", ie->tag,
next_tag);
+ 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 @@
for (ie = last_ie; ie >= ies; ie--) {
/* each time, look from the beginning */
osmo_gtlv_load_start(>lv);
- rc = osmo_gtlv_load_next_by_tag(>lv, ie->tag);
+ rc = osmo_gtlv_load_next_by_tag_inst(>lv, &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(>lv.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, >lv.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(>lv, 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, >lv.ti, NULL),
+ gtlv.len,
+ osmo_hexdump_nospc(gtlv.val, gtlv.len),
+ osmo_gtlv_tag_inst_to_str_c(ctx, >lv.ti, NULL));
+
+ rc = osmo_gtlv_load_next_by_tag_inst(>lv, &ie->ti);
if (rc || !gtlv.val) {
printf(" ERROR val not found\n");
exit(1);
@@ -184,7 +198,9 @@
}
}
}
- 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, >lv.ti, NULL),
+ gtlv.len, osmo_hexdump_nospc(gtlv.val, gtlv.len));
}
printf("- decoding every second tag:\n");
@@ -196,25 +212,32 @@
if (!ie->val)
break;
- rc = osmo_gtlv_load_next_by_tag(>lv, ie->tag);
+ rc = osmo_gtlv_load_next_by_tag_inst(>lv, &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(>lv.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, >lv.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(>lv, 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, >lv.ti, NULL),
+ gtlv.len,
+ osmo_hexdump_nospc(gtlv.val, gtlv.len),
+ osmo_gtlv_tag_inst_to_str_c(ctx, >lv.ti, NULL));
+
+ rc = osmo_gtlv_load_next_by_tag_inst(>lv, &ie->ti);
if (rc || !gtlv.val) {
printf(" ERROR val not found\n");
exit(1);
@@ -224,14 +247,16 @@
}
}
}
- 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, >lv.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(>lv, ie->tag);
- printf(" osmo_gtlv_load_next_by_tag(%d) rc=", ie->tag);
+ rc = osmo_gtlv_load_next_by_tag_inst(>lv, &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 @@
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 @@
},
/* 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 @@
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 @@
},
/* 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 @@
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 @@
},
/* 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 @@
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 @@
}
/* 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 @@
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 @@
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(a)sysmocom.de>
+ * All Rights Reserved.
+ *
+ * Author: Neels Janosch Hofmeyr <nhofmeyr(a)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(a)sysmocom.de>
+ * All Rights Reserved.
+ *
+ * Author: Neels Janosch Hofmeyr <nhofmeyr(a)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(a)sysmocom.de>
+ * All Rights Reserved.
+ *
+ * Author: Neels Janosch Hofmeyr <nhofmeyr(a)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(a)sysmocom.de>
+ * All Rights Reserved.
+ *
+ * Author: Neels Janosch Hofmeyr <nhofmeyr(a)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(>lv, (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, >lv, 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 @@
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
--
To view, visit
https://gerrit.osmocom.org/c/osmo-upf/+/27529
To unsubscribe, or for help writing mail filters, visit
https://gerrit.osmocom.org/settings
Gerrit-Project: osmo-upf
Gerrit-Branch: master
Gerrit-Change-Id: I0a076e54dfba6038cc779cb7c8f3967d212226aa
Gerrit-Change-Number: 27529
Gerrit-PatchSet: 6
Gerrit-Owner: neels <nhofmeyr(a)sysmocom.de>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: laforge <laforge(a)osmocom.org>
Gerrit-Reviewer: neels <nhofmeyr(a)sysmocom.de>
Gerrit-Reviewer: pespin <pespin(a)sysmocom.de>
Gerrit-MessageType: merged