From f149770f32c8d52b42b4ed5303d647e2a8864182 Mon Sep 17 00:00:00 2001 From: Neels Hofmeyr Date: Fri, 25 Feb 2022 01:39:32 +0100 Subject: [PATCH] tliv wip Change-Id: I76c0e3e60eff61354e580cb70ef8645f09a29ec8 --- configure.ac | 1 + include/osmocom/tlv/tlv.h | 31 +++- include/osmocom/tlv/tlv_dec_enc.h | 4 +- include/osmocom/tlv/tlv_gen.h | 9 + src/libosmo-pfcp/pfcp_msg.c | 4 +- src/libosmo-tlv/tlv.c | 83 +++++++-- src/libosmo-tlv/tlv_dec_enc.c | 68 ++++--- src/libosmo-tlv/tlv_gen.c | 17 +- tests/libosmo-tlv/Makefile.am | 1 + tests/libosmo-tlv/test_tliv/Makefile.am | 60 +++++++ .../test_tliv/gen__myproto_ies_auto.c | 75 ++++++++ .../test_tliv/myproto_ies_custom.c | 69 ++++++++ .../test_tliv/myproto_ies_custom.h | 42 +++++ tests/libosmo-tlv/test_tliv/tliv_test.c | 142 +++++++++++++++ tests/libosmo-tlv/test_tliv/tliv_test.ok | 160 +++++++++++++++++ tests/libosmo-tlv/tlv_dec_enc_test.c | 18 +- tests/libosmo-tlv/tlv_test.c | 167 +++++++++++++++--- 17 files changed, 854 insertions(+), 97 deletions(-) create mode 100644 tests/libosmo-tlv/test_tliv/Makefile.am create mode 100644 tests/libosmo-tlv/test_tliv/gen__myproto_ies_auto.c create mode 100644 tests/libosmo-tlv/test_tliv/myproto_ies_custom.c create mode 100644 tests/libosmo-tlv/test_tliv/myproto_ies_custom.h create mode 100644 tests/libosmo-tlv/test_tliv/tliv_test.c create mode 100644 tests/libosmo-tlv/test_tliv/tliv_test.ok diff --git a/configure.ac b/configure.ac index c11e595..239f807 100644 --- a/configure.ac +++ b/configure.ac @@ -212,6 +212,7 @@ AC_OUTPUT( tests/atlocal tests/libosmo-tlv/Makefile tests/libosmo-tlv/test_tlv_gen/Makefile + tests/libosmo-tlv/test_tliv/Makefile doc/Makefile doc/examples/Makefile doc/manuals/Makefile diff --git a/include/osmocom/tlv/tlv.h b/include/osmocom/tlv/tlv.h index 17f0d24..9a80f37 100644 --- a/include/osmocom/tlv/tlv.h +++ b/include/osmocom/tlv/tlv.h @@ -24,11 +24,23 @@ #include #include +#include +#include struct msgb; struct osmo_tlv_load; struct osmo_tlv_put; +struct osmo_tlv_tag_inst { + unsigned int tag; + bool instance_present; + unsigned int instance; +}; + +int osmo_tlv_tag_inst_cmp(const struct osmo_tlv_tag_inst *a, const struct osmo_tlv_tag_inst *b); +char *osmo_tlv_tag_inst_to_str_buf( asdf + + /*! TL configuration for osmo_tlv_load*() and osmo_tlv_put*(). Depending on these implementations provided by the caller, * osmo_tlv can load any sizes of tag and length fields (that don't surpass the value range of unsigned int and size_t, * respectively), as well as TV (fixed-length) or TvLV (variable-sized length). @@ -46,8 +58,9 @@ struct osmo_tlv_cfg { size_t tl_min_size; /*! Read one TL from the start of src_data. - * \param tlv Return the T value read from src_data in tlv->tag. - * Return the L value read from src_data in tlv->len. + * \param tlv Return the T (tag) value read from src_data in tlv->tag. + * Return the L (length) value read from src_data in tlv->len. + * Return the I (instance) value read from src_data in tlv->len; ignore if there is no I. * Return the position just after the TL in tlv->*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. @@ -67,12 +80,14 @@ struct osmo_tlv_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 tlv Backpointer to the osmo_tlv_put struct, including tlv->dst, the underlying msgb. * \return the size of the TL part in bytes on success, -EINVAL if tag is invalid, -EMSGSIZE if len is too large * or dst_data_avail is too small for the TL. */ - int (*store_tl)(uint8_t *dst_data, size_t dst_data_avail, unsigned int tag, size_t len, struct osmo_tlv_put *tlv); + int (*store_tl)(uint8_t *dst_data, size_t dst_data_avail, const struct osmo_tlv_tag_inst *ti, size_t len, + struct osmo_tlv_put *tlv); }; /*! Configuration that allows parsing an 8bit tag and 8bit length TLV. */ @@ -96,7 +111,7 @@ struct osmo_tlv_load { } src; /*! Return value from last invocation of osmo_tlv_load_next*(): tag value of parsed IE. */ - unsigned int tag; + struct osmo_tlv_tag_inst ti; /*! Return value from last invocation of osmo_tlv_load_next*(): Start of the IE's payload data (after tag and * length). If the end of the src buffer is reached, val == NULL. If a TLV contained no value part, len == 0, * but this still points just after the TL. */ @@ -113,8 +128,9 @@ static inline void osmo_tlv_load_start(struct osmo_tlv_load *tlv) } int osmo_tlv_load_next(struct osmo_tlv_load *tlv); -int osmo_tlv_load_peek_tag(const struct osmo_tlv_load *tlv); +int osmo_tlv_load_peek_tag(const struct osmo_tlv_load *tlv, struct osmo_tlv_tag_inst *ti); int osmo_tlv_load_next_by_tag(struct osmo_tlv_load *tlv, unsigned int tag); +int osmo_tlv_load_next_by_tag_inst(struct osmo_tlv_load *tlv, const struct osmo_tlv_tag_inst *ti); /* State for storing a TLV structure into a msgb. */ struct osmo_tlv_put { @@ -126,11 +142,12 @@ struct osmo_tlv_put { /* msgb to append new TL to */ struct msgb *dst; - /* Where was the last TL head written */ - unsigned int last_tag; + /* What was the last TL written and where are its TL and V */ + struct osmo_tlv_tag_inst last_ti; uint8_t *last_tl; uint8_t *last_val; }; int osmo_tlv_put_tl(struct osmo_tlv_put *tlv, unsigned int tag, size_t len); +int osmo_tlv_put_tli(struct osmo_tlv_put *tlv, const struct osmo_tlv_tag_inst *ti, size_t len); int osmo_tlv_put_update_tl(struct osmo_tlv_put *tlv); diff --git a/include/osmocom/tlv/tlv_dec_enc.h b/include/osmocom/tlv/tlv_dec_enc.h index 552b86f..511fa05 100644 --- a/include/osmocom/tlv/tlv_dec_enc.h +++ b/include/osmocom/tlv/tlv_dec_enc.h @@ -77,8 +77,8 @@ enum osmo_tlv_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-tlv/test_tlv_gen/. */ struct osmo_tlv_coding { - /*! the IEI value */ - unsigned int tag; + /*! the IEI discriminator, and optional instance number */ + struct osmo_tlv_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/tlv/tlv_gen.h b/include/osmocom/tlv/tlv_gen.h index 8f97381..e608f3c 100644 --- a/include/osmocom/tlv/tlv_gen.h +++ b/include/osmocom/tlv/tlv_gen.h @@ -6,6 +6,8 @@ struct osmo_tlv_gen_ie; +#define OSMO_TLV_GEN_NO_INSTANCE INT_MAX + /*! Modifier for Mandatory/Optional/Multiple around an osmo_tlv_gen_ie. */ struct osmo_tlv_gen_ie_o { /*! Whether to add a bool foo_present, and to skip the coding if false. */ @@ -15,6 +17,11 @@ struct osmo_tlv_gen_ie_o { unsigned int multi; /*! Number of mandatory occurences of the IE */ 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 */ const struct osmo_tlv_gen_ie *ie; }; @@ -24,6 +31,8 @@ struct osmo_tlv_gen_ie_o { #define OSMO_TLV_GEN_O_MULTI(MAX, TLV_GEN_IE) { .multi = MAX, .ie = &(TLV_GEN_IE) } #define OSMO_TLV_GEN_M_MULTI(MAX, MAND_COUNT, TLV_GEN_IE) \ { .multi = MAX, .multi_mandatory = MAND_COUNT, .ie = &(TLV_GEN_IE) } +#define OSMO_TLV_GEN_O_INST(INSTANCE, TLV_GEN_IE) { .optional = true, .instance = INSTANCE, .ie = &TLV_GEN_IE } +#define OSMO_TLV_GEN_M_INST(INSTANCE, TLV_GEN_IE) { .instance = INSTANCE, .ie = &(TLV_GEN_IE) } /*! Define decoding and encoding of a single IE, i.e. one full TLV. */ struct osmo_tlv_gen_ie { diff --git a/src/libosmo-pfcp/pfcp_msg.c b/src/libosmo-pfcp/pfcp_msg.c index 81748da..c0cc706 100644 --- a/src/libosmo-pfcp/pfcp_msg.c +++ b/src/libosmo-pfcp/pfcp_msg.c @@ -261,9 +261,9 @@ static void osmo_pfcp_msg_set_memb_ofs(struct osmo_pfcp_msg *m) if (!mc) return; for (; !osmo_tlv_coding_end(mc) && (m->ofs_cause == 0 || m->ofs_node_id == 0); mc++) { - if (mc->tag == OSMO_PFCP_IEI_CAUSE) + if (mc->ti.tag == OSMO_PFCP_IEI_CAUSE) m->ofs_cause = offsetof(struct osmo_pfcp_msg, ies) + mc->memb_ofs; - if (mc->tag == OSMO_PFCP_IEI_NODE_ID) + if (mc->ti.tag == OSMO_PFCP_IEI_NODE_ID) m->ofs_node_id = offsetof(struct osmo_pfcp_msg, ies) + mc->memb_ofs; } diff --git a/src/libosmo-tlv/tlv.c b/src/libosmo-tlv/tlv.c index 5d62195..cdf7b87 100644 --- a/src/libosmo-tlv/tlv.c +++ b/src/libosmo-tlv/tlv.c @@ -27,6 +27,26 @@ #include #include +int osmo_tlv_tag_inst_cmp(const struct osmo_tlv_tag_inst *a, const struct osmo_tlv_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; +} + static int next_tl_valid(const struct osmo_tlv_load *tlv, const uint8_t **ie_start_p, size_t *buflen_left_p) { const uint8_t *ie_start; @@ -102,6 +122,7 @@ int osmo_tlv_load_next(struct osmo_tlv_load *tlv) /* Locate next IE */ OSMO_ASSERT(tlv->cfg->load_tl); + tlv->ti = (struct osmo_tlv_tag_inst){0}; rc = tlv->cfg->load_tl(tlv, ie_start, buflen_left); if (rc) return rc; @@ -116,17 +137,20 @@ int osmo_tlv_load_next(struct osmo_tlv_load *tlv) /* Return the tag of the IE that osmo_tlv_next() would yield, do not change the tlv state. * - * \param[inout] tlv state for TLV parsing position. - * \returns the tag number on success, negative on TLV parsing error, -ENOENT when no more tags - * follow. + * \param[in] tlv state for TLV parsing position; is not modified. + * \param[out] tag the tag number on success, if NULL don't return the tag. + * \param[out] instance the instance number or OSMO_TLV_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_tlv_load_peek_tag(const struct osmo_tlv_load *tlv) +int osmo_tlv_load_peek_tag(const struct osmo_tlv_load *tlv, struct osmo_tlv_tag_inst *ti) { const uint8_t *ie_start; size_t buflen_left; int rc; /* Guard against modification by load_tl(). */ struct osmo_tlv_load mtlv = *tlv; + mtlv.ti = (struct osmo_tlv_tag_inst){0}; rc = next_tl_valid(&mtlv, &ie_start, &buflen_left); if (rc) @@ -140,13 +164,25 @@ int osmo_tlv_load_peek_tag(const struct osmo_tlv_load *tlv) rc = tlv->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_tlv_next(), but skip any IEs until the given tag is reached. Change the tlv state only when - * success is returned. +/* Same as osmo_tlv_load_next(), but skip any IEs until the given tag is reached. Change the tlv state only when success + * is returned. + * \param[out] tlv 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_TLV_NO_INSTANCE. * \return 0 when the tag is found. Return -ENOENT when no such tag follows and keep the tlv unchanged. */ int osmo_tlv_load_next_by_tag(struct osmo_tlv_load *tlv, unsigned int tag) +{ + struct osmo_tlv_tag_inst ti = { .tag = tag }; + return osmo_tlv_load_next_by_tag_inst(tlv, &ti); +} + +int osmo_tlv_load_next_by_tag_inst(struct osmo_tlv_load *tlv, const struct osmo_tlv_tag_inst *ti) { struct osmo_tlv_load work = *tlv; for (;;) { @@ -155,7 +191,7 @@ int osmo_tlv_load_next_by_tag(struct osmo_tlv_load *tlv, unsigned int tag) return rc; if (!work.val) return -ENOENT; - if (work.tag == tag) { + if (!osmo_tlv_tag_inst_cmp(&work.ti, ti)) { *tlv = work; return 0; } @@ -187,17 +223,26 @@ int osmo_tlv_load_next_by_tag(struct osmo_tlv_load *tlv, unsigned int tag) * Return 0 on success, -EINVAL if the tag value is invalid, -EMSGSIZE if len is too large. */ int osmo_tlv_put_tl(struct osmo_tlv_put *tlv, unsigned int tag, size_t len) +{ + struct osmo_tlv_tag_inst ti = { .tag = tag }; + return osmo_tlv_put_tli(tlv, &ti, len); +} + +/* Put tag header, instance value and length at the end of the msgb, according to tlv->cfg->store_tl(). + * This is the same as osmo_tlv_put_tl(), only osmo_tlv_put_tl() passes instance = 0. + */ +int osmo_tlv_put_tli(struct osmo_tlv_put *tlv, const struct osmo_tlv_tag_inst *ti, size_t len) { int rc; uint8_t *last_tl; OSMO_ASSERT(tlv->cfg->store_tl); last_tl = tlv->dst->tail; - rc = tlv->cfg->store_tl(tlv->dst->tail, msgb_tailroom(tlv->dst), tag, len, tlv); + rc = tlv->cfg->store_tl(tlv->dst->tail, msgb_tailroom(tlv->dst), ti, len, tlv); if (rc < 0) return rc; if (rc > 0) msgb_put(tlv->dst, rc); - tlv->last_tag = tag; + tlv->last_ti = *ti; tlv->last_tl = last_tl; tlv->last_val = tlv->dst->tail; return 0; @@ -210,7 +255,7 @@ int osmo_tlv_put_tl(struct osmo_tlv_put *tlv, unsigned int tag, size_t len) int osmo_tlv_put_update_tl(struct osmo_tlv_put *tlv) { size_t len = tlv->dst->tail - tlv->last_val; - int rc = tlv->cfg->store_tl(tlv->last_tl, tlv->last_val - tlv->last_tl, tlv->last_tag, len, tlv); + int rc = tlv->cfg->store_tl(tlv->last_tl, tlv->last_val - tlv->last_tl, &tlv->last_ti, len, tlv); if (rc < 0) return rc; /* In case the TL has changed in size, hopefully the implementation has moved the msgb data. Make sure last_val @@ -222,22 +267,22 @@ int osmo_tlv_put_update_tl(struct osmo_tlv_put *tlv) static int t8l8v_load_tl(struct osmo_tlv_load *tlv, const uint8_t *src_data, size_t src_data_len) { /* already validated in next_tl_valid(): src_data_len >= cfg->tl_min_size == 2. */ - tlv->tag = src_data[0]; + tlv->ti.tag = src_data[0]; tlv->len = src_data[1]; tlv->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_tlv_tag_inst *ti, size_t len, struct osmo_tlv_put *tlv) { - 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; } @@ -251,22 +296,22 @@ const struct osmo_tlv_cfg osmo_t8l8v_cfg = { static int t16l16v_load_tl(struct osmo_tlv_load *tlv, const uint8_t *src_data, size_t src_data_len) { /* already validated in next_tl_valid(): src_data_len >= cfg->tl_min_size == 4. */ - tlv->tag = osmo_load16be(src_data); + tlv->ti.tag = osmo_load16be(src_data); tlv->len = osmo_load16be(src_data + 2); tlv->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_tlv_tag_inst *ti, size_t len, struct osmo_tlv_put *tlv) { - 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-tlv/tlv_dec_enc.c b/src/libosmo-tlv/tlv_dec_enc.c index 74fadf4..17d1819 100644 --- a/src/libosmo-tlv/tlv_dec_enc.c +++ b/src/libosmo-tlv/tlv_dec_enc.c @@ -31,11 +31,21 @@ /* Reverse offsetof(): return the address of the struct member for a given osmo_tlv_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(decoded_struct, __FILE__, __LINE__, "tag 0x%x = %s: " FMT " (%d: %s)\n", IEI, \ - get_value_string(iei_strs, IEI), ##ARGS, RC, strerror((RC) > 0 ? (RC) : -(RC))); \ + if (err_cb) { \ + if ((TAG_INST).instance_present) \ + err_cb(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(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) @@ -70,7 +80,7 @@ static int osmo_tlvs_decode_unordered(void *decoded_struct, unsigned int obj_ofs #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, tlv->tag, \ + RETURN_ERROR(-ENOTSUP, tlv->ti, \ "Too many IE definitions for decoding an unordered TLV structure"); \ seen_p = &seen_ie_coding_entries[ie_coding_idx]; \ } while (0) @@ -89,7 +99,7 @@ static int osmo_tlvs_decode_unordered(void *decoded_struct, unsigned int obj_ofs rc = osmo_tlv_load_next(tlv); if (rc) - RETURN_ERROR(rc, tlv->tag, "Decoding IEs failed on or after this tag"); + RETURN_ERROR(rc, tlv->ti, "Decoding IEs failed on or after this tag"); if (!tlv->val) { /* End of the TLV structure */ break; @@ -101,7 +111,9 @@ static int osmo_tlvs_decode_unordered(void *decoded_struct, unsigned int obj_ofs do { /* Find the IE coding for this tag */ - for (iec = ie_coding; !osmo_tlv_coding_end(iec) && iec->tag != tlv->tag; iec++); + for (iec = ie_coding; + !osmo_tlv_coding_end(iec) && osmo_tlv_tag_inst_cmp(&iec->ti, &tlv->ti); + iec++); /* No such IE coding found. */ if (osmo_tlv_coding_end(iec)) break; @@ -126,7 +138,7 @@ static int osmo_tlvs_decode_unordered(void *decoded_struct, unsigned int obj_ofs 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, tlv->tag, "Only %u instances of this IE are supported per message", + RETURN_ERROR(-ENOTSUP, tlv->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. */ @@ -164,14 +176,14 @@ static int osmo_tlvs_decode_unordered(void *decoded_struct, unsigned int obj_ofs rc = osmo_tlvs_decode(decoded_struct, obj_ofs + memb_ofs, &inner_tlv, ordered, iec->nested_ies, err_cb, iei_strs); if (rc) - RETURN_ERROR(rc, tlv->tag, "Error while decoding TLV structure nested inside this IE"); + RETURN_ERROR(rc, tlv->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, tlv->tag, "IE definition lacks a dec_func()"); + RETURN_ERROR(-EIO, tlv->ti, "IE definition lacks a dec_func()"); rc = iec->dec_func(decoded_struct, MEMB(obj, memb_ofs), tlv); if (rc) - RETURN_ERROR(rc, tlv->tag, "Error while decoding this IE"); + RETURN_ERROR(rc, tlv->ti, "Error while decoding this IE"); } if (multi_count_p) { @@ -195,14 +207,14 @@ static int osmo_tlvs_decode_unordered(void *decoded_struct, unsigned int obj_ofs multi_count_p = iec->has_count ? MEMB(obj, iec->count_ofs) : NULL; if (multi_count_p) { if (*multi_count_p < iec->count_madatory) - RETURN_ERROR(-EINVAL, iec->tag, "%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_madatory, *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; } @@ -232,19 +244,20 @@ static int osmo_tlvs_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_tlv_tag_inst peek_ti; - rc = osmo_tlv_load_next_by_tag(tlv, ie_coding->tag); + rc = osmo_tlv_load_next_by_tag_inst(tlv, &ie_coding->ti); switch (rc) { case 0: break; case -ENOENT: if (!presence_flag && (!multi_count || *multi_count < ie_coding->count_madatory)) - 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 (;;) { @@ -254,7 +267,7 @@ static int osmo_tlvs_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 */ @@ -283,15 +296,15 @@ static int osmo_tlvs_decode_ordered(void *decoded_struct, unsigned int obj_ofs, rc = osmo_tlvs_decode(decoded_struct, obj_ofs + memb_ofs, &inner_tlv, ordered, ie_coding->nested_ies, err_cb, 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), tlv); 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) @@ -309,7 +322,8 @@ static int osmo_tlvs_decode_ordered(void *decoded_struct, unsigned int obj_ofs, (*multi_count)++; /* Does another one of these IEs follow? */ - if (osmo_tlv_load_peek_tag(tlv) != tlv->tag) { + if (osmo_tlv_load_peek_tag(tlv, &peek_ti) + || osmo_tlv_tag_inst_cmp(&peek_ti, &tlv->ti)) { /* Next tag is a different IE, end the repetition. */ break; } @@ -381,7 +395,7 @@ int osmo_tlvs_encode(struct osmo_tlv_put *tlv, void *decoded_struct, unsigned in 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 { @@ -391,11 +405,11 @@ int osmo_tlvs_encode(struct osmo_tlv_put *tlv, void *decoded_struct, unsigned in for (i = 0; i < n; i++) { unsigned int memb_ofs; - osmo_tlv_put_tl(tlv, ie_coding->tag, 0); + osmo_tlv_put_tli(tlv, &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; @@ -407,12 +421,12 @@ int osmo_tlvs_encode(struct osmo_tlv_put *tlv, void *decoded_struct, unsigned in rc = osmo_tlvs_encode(&nested_tlv, decoded_struct, obj_ofs + memb_ofs, ie_coding->nested_ies, err_cb, 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(tlv, 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_tlv_put_update_tl(tlv); @@ -461,7 +475,7 @@ int osmo_tlvs_encode_to_str_buf(char *buf, size_t buflen, const void *decoded_st 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-tlv/tlv_gen.c b/src/libosmo-tlv/tlv_gen.c index ad082e2..1a6306f 100644 --- a/src/libosmo-tlv/tlv_gen.c +++ b/src/libosmo-tlv/tlv_gen.c @@ -227,7 +227,7 @@ static void write_extern_dec_enc(const struct osmo_tlv_gen_ie_o *ies) } /* For a nested IE, write the struct osmo_tlv_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, @@ -241,7 +241,10 @@ static void write_ies_array(const char *indent, const struct osmo_tlv_gen_ie_o * for (ie_o = ies; ie_o->ie; ie_o++) { const struct osmo_tlv_gen_ie *ie = ie_o->ie; const char *tag_name = ie->tag_name ? : ie->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) + printi(", true, %s", ie_o->instance); + printi(" },\n"); printi(" .memb_ofs = offsetof(%s, %s%s),\n", obj_type, substruct, ie->name); if (ie->nested_ies) { printi(" .nested_ies = ies_in_%s,\n", ie->name); @@ -263,13 +266,17 @@ static void write_ies_array(const char *indent, const struct osmo_tlv_gen_ie_o * printi(" .has_presence_flag = true,\n"); printi(" .presence_flag_ofs = offsetof(%s, %s%s_present),\n", obj_type, substruct, ie->name); } + if (ie_o->instance) { + printi(" .has_instance = true,\n"); + printi(" .instance = %s,\n", ie_o->instance); + } printi("},\n"); } } /* For a nested IE, write the struct osmo_tlv_coding array of the inner IEs. * static const struct osmo_tlv_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, @@ -291,7 +298,7 @@ static void write_nested_ies_array(const struct osmo_tlv_gen_ie_o *ies) printf("\nstatic const struct osmo_tlv_coding ies_in_%s[] = {\n", ie->name); write_ies_array(indent, ie->nested_ies, decoded_type(ie), ""); - printi("{0}\n"); + printi("{}\n"); printf("};\n"); } } @@ -329,7 +336,7 @@ static void write_c() char *substruct = talloc_asprintf(NULL, "%s.", gen_msg->name); printf("\nstatic const struct osmo_tlv_coding ies_in_msg_%s[] = {\n", gen_msg->name); write_ies_array("\t", gen_msg->ies, obj_type, substruct); - printf("\t{0}\n};\n"); + printf("\t{}\n};\n"); talloc_free(substruct); talloc_free(obj_type); } diff --git a/tests/libosmo-tlv/Makefile.am b/tests/libosmo-tlv/Makefile.am index 64e3bdd..6baa854 100644 --- a/tests/libosmo-tlv/Makefile.am +++ b/tests/libosmo-tlv/Makefile.am @@ -1,5 +1,6 @@ SUBDIRS = \ test_tlv_gen \ + test_tliv \ $(NULL) AM_CPPFLAGS = \ diff --git a/tests/libosmo-tlv/test_tliv/Makefile.am b/tests/libosmo-tlv/test_tliv/Makefile.am new file mode 100644 index 0000000..3b94619 --- /dev/null +++ b/tests/libosmo-tlv/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-tlv/libosmo-tlv.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-tlv/libosmo-tlv.a \ + $(LIBOSMOCORE_LIBS) \ + $(NULL) + +.PHONY: update_exp +update_exp: + $(builddir)/tliv_test >$(srcdir)/tliv_test.ok diff --git a/tests/libosmo-tlv/test_tliv/gen__myproto_ies_auto.c b/tests/libosmo-tlv/test_tliv/gen__myproto_ies_auto.c new file mode 100644 index 0000000..64cca8f --- /dev/null +++ b/tests/libosmo-tlv/test_tliv/gen__myproto_ies_auto.c @@ -0,0 +1,75 @@ +/* + * (C) 2021-2022 by sysmocom - s.f.m.c. GmbH + * All Rights Reserved. + * + * Author: Neels Janosch Hofmeyr + * + * SPDX-License-Identifier: GPL-2.0+ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include + +#define O OSMO_TLV_GEN_O +#define M OSMO_TLV_GEN_M +#define O_MULTI OSMO_TLV_GEN_O_MULTI +#define M_MULTI OSMO_TLV_GEN_M_MULTI +#define O_INST OSMO_TLV_GEN_O_INST +#define M_INST OSMO_TLV_GEN_M_INST + +static const struct osmo_tlv_gen_ie bar = { + .name = "bar", + /* uses 'struct ie_bar bar;' and dec_bar()/enc_bar() */ + .to_str = "bar", /* uses myproto_enc_to_str_bar() */ +}; + +static const struct osmo_tlv_gen_ie_o ies_in_moo_msg[] = { + M_INST("5", bar), + {0} +}; + +static const struct osmo_tlv_gen_ie_o ies_in_goo_msg[] = { + O_INST("3", bar), + {0} +}; + +static const struct osmo_tlv_gen_msg msg_defs[] = { + { "moo", ies_in_moo_msg }, + { "goo", ies_in_goo_msg }, + {0} +}; + +int main(int argc, const char **argv) +{ + struct osmo_tlv_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 ", + .msg_defs = msg_defs, + }; + return osmo_tlv_gen_main(&cfg, argc, argv); +} diff --git a/tests/libosmo-tlv/test_tliv/myproto_ies_custom.c b/tests/libosmo-tlv/test_tliv/myproto_ies_custom.c new file mode 100644 index 0000000..1039ec5 --- /dev/null +++ b/tests/libosmo-tlv/test_tliv/myproto_ies_custom.c @@ -0,0 +1,69 @@ +/* Example for defining custom IES for tlv_gen. + * + * (C) 2021-2022 by sysmocom - s.f.m.c. GmbH + * All Rights Reserved. + * + * Author: Neels Janosch Hofmeyr + * + * 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 Affero General Public License + * along with this program. If not, see . + * + */ +#include +#include + +#include +#include +#include + +#include + +int myproto_dec_bar(void *decoded_struct, void *decode_to, const struct osmo_tlv_load *tlv) +{ + struct myproto_ie_bar *bar = decode_to; + if (tlv->len < 2) + return -EINVAL; + *bar = (struct myproto_ie_bar){ + .a = tlv->val[0], + .b = (tlv->val[1] == 1), + }; + return 0; +} + +int myproto_enc_bar(struct osmo_tlv_put *tlv, void *decoded_struct, void *encode_from) +{ + struct myproto_ie_bar *bar = encode_from; + msgb_put_u8(tlv->dst, bar->a); + msgb_put_u8(tlv->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" }, + { MYPROTO_MSGT_GOO, "GOO" }, + {} +}; + +const struct value_string myproto_iei_names[] = { + { MYPROTO_IEI_FOO, "FOO" }, + { MYPROTO_IEI_BAR, "BAR" }, + {} +}; diff --git a/tests/libosmo-tlv/test_tliv/myproto_ies_custom.h b/tests/libosmo-tlv/test_tliv/myproto_ies_custom.h new file mode 100644 index 0000000..5206c6a --- /dev/null +++ b/tests/libosmo-tlv/test_tliv/myproto_ies_custom.h @@ -0,0 +1,42 @@ +/* Definitions for decoded message IEs, to be used by the auto-generated tlv_gen_test_tlv.c. */ +#pragma once + +#include + +enum myproto_msg_type { + MYPROTO_MSGT_MOO = 1, + MYPROTO_MSGT_GOO = 7, +}; + +extern const struct value_string myproto_msg_type_names[]; + +enum myproto_iei { + MYPROTO_IEI_FOO = 1, + MYPROTO_IEI_BAR, +}; + +extern const struct value_string myproto_iei_names[]; + +struct myproto_ie_bar { + int a; + bool b; +}; + +union myproto_ie_bar_instances { + struct { + struct myproto_ie_bar bar[5]; + bool bar_present[5]; + } arr; + struct { + struct myproto_ie_bar bar_one; + struct myproto_ie_bar bar_two; + struct myproto_ie_bar different_bar; + struct myproto_ie_bar fallback_bar; + struct myproto_ie_bar final_bar; + bool bar_one_present; + bool bar_two_present; + bool different_bar_present; + bool fallback_bar_present; + bool final_bar_present; + } inst; +}; diff --git a/tests/libosmo-tlv/test_tliv/tliv_test.c b/tests/libosmo-tlv/test_tliv/tliv_test.c new file mode 100644 index 0000000..e7182b4 --- /dev/null +++ b/tests/libosmo-tlv/test_tliv/tliv_test.c @@ -0,0 +1,142 @@ +/* (C) 2022 by sysmocom - s.f.m.c. GmbH + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include + +#include +#include +#include + +#include + +#include + +struct myproto_msg { + enum myproto_msg_type type; + union myproto_ies ies; +}; + +static void err_cb(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_tlv_cfg *cfg) +{ + struct osmo_tlv_put tlv = { + .cfg = cfg, + .dst = dst, + }; + + msgb_put_u8(tlv.dst, msg->type); + return myproto_ies_encode(&tlv, (void*)&msg->ies, msg->type, err_cb, myproto_iei_names); +} + +static int myproto_msg_dec(struct myproto_msg *msg, const uint8_t *data, size_t data_len, + const struct osmo_tlv_cfg *cfg, bool ordered) +{ + struct osmo_tlv_load tlv; + if (data_len < 1) + return -EINVAL; + msg->type = data[0]; + tlv = (struct osmo_tlv_load){ + .cfg = cfg, + .src = { data + 1, data_len - 1 }, + }; + return myproto_ies_decode(&msg->ies, &tlv, ordered, msg->type, err_cb, myproto_iei_names); +} + +void *ctx; + +struct myproto_msg tests[] = { + { + MYPROTO_MSGT_MOO, + { + .moo = { + .bar = { 23, 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_tlvs_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_tlv_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 = {0}; + 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); + } +} + +int main() +{ + ctx = talloc_named_const(NULL, 0, "test_gen_tlv"); + msgb_talloc_ctx_init(ctx, 0); + + test_enc_dec("t8l8v ordered", &osmo_t8l8v_cfg, true); + test_enc_dec("t8l8v unordered", &osmo_t8l8v_cfg, false); + + test_enc_dec("t16l16v ordered", &osmo_t16l16v_cfg, true); + test_enc_dec("t16l16v unordered", &osmo_t16l16v_cfg, false); + + talloc_free(ctx); + return 0; +} diff --git a/tests/libosmo-tlv/test_tliv/tliv_test.ok b/tests/libosmo-tlv/test_tliv/tliv_test.ok new file mode 100644 index 0000000..9974d67 --- /dev/null +++ b/tests/libosmo-tlv/test_tliv/tliv_test.ok @@ -0,0 +1,160 @@ + +=== start t8l8v ordered test_enc_dec[0] +encoded: MOO={ FOO=23 BAR="twentythree" } +myproto_msg_enc() rc = 0 +01 01 02 00 17 02 0b 74 77 65 6e 74 79 74 68 72 65 65 . +myproto_msg_dec() rc = 0 +decoded: MOO={ FOO=23 BAR="twentythree" } +=== end t8l8v ordered test_enc_dec[0] + +=== start t8l8v ordered test_enc_dec[1] +encoded: MOO={ FOO=23 BAR="twentythree" BAZ={2323,true} } +myproto_msg_enc() rc = 0 +01 01 02 00 17 02 0b 74 77 65 6e 74 79 74 68 72 65 65 03 02 89 13 . +myproto_msg_dec() rc = 0 +decoded: MOO={ FOO=23 BAR="twentythree" BAZ={2323,true} } +=== end t8l8v ordered test_enc_dec[1] + +=== start t8l8v ordered test_enc_dec[2] +encoded: MOO={ FOO=23 BAR="twentythree" BAZ={2323,true} REPEAT_INT={ 1, 2, 32767 } } +myproto_msg_enc() rc = 0 +01 01 02 00 17 02 0b 74 77 65 6e 74 79 74 68 72 65 65 03 02 89 13 04 02 00 01 04 02 00 02 04 02 7f ff . +myproto_msg_dec() rc = 0 +decoded: MOO={ FOO=23 BAR="twentythree" BAZ={2323,true} REPEAT_INT={ 1, 2, 32767 } } +=== end t8l8v ordered test_enc_dec[2] + +=== start t8l8v ordered test_enc_dec[3] +encoded: MOO={ FOO=23 BAR="twentythree" BAZ={2323,true} REPEAT_INT={ 1, 2, 32767 } REPEAT_STRUCT={ {1001,true,R_A}, {1002,false,R_B} } MOO_NEST={ FOO=42 BAR="fortytwo" BAZ={4242,false} } } +myproto_msg_enc() rc = 0 +01 01 02 00 17 02 0b 74 77 65 6e 74 79 74 68 72 65 65 03 02 89 13 04 02 00 01 04 02 00 02 04 02 7f ff 05 03 03 e9 80 05 03 03 ea 01 06 12 01 02 00 2a 02 08 66 6f 72 74 79 74 77 6f 03 02 10 92 . +myproto_msg_dec() rc = 0 +decoded: MOO={ FOO=23 BAR="twentythree" BAZ={2323,true} REPEAT_INT={ 1, 2, 32767 } REPEAT_STRUCT={ {1001,true,R_A}, {1002,false,R_B} } MOO_NEST={ FOO=42 BAR="fortytwo" BAZ={4242,false} } } +=== end t8l8v ordered test_enc_dec[3] + +=== start t8l8v ordered test_enc_dec[4] +encoded: GOO={ FOO=17 BAR="gooei" GOO_NEST={ { VAL=0x123456789abcdef MOO_NEST={ FOO=11 BAR="eleven" BAZ={1111,true} } }, { MOO_NEST={ FOO=12 BAR="twelve" BAZ={1212,false} } } } } +myproto_msg_enc() rc = 0 +07 01 02 00 11 02 05 67 6f 6f 65 69 08 1c 07 08 01 23 45 67 89 ab cd ef 06 10 01 02 00 0b 02 06 65 6c 65 76 65 6e 03 02 84 57 08 12 06 10 01 02 00 0c 02 06 74 77 65 6c 76 65 03 02 04 bc . +myproto_msg_dec() rc = 0 +decoded: GOO={ FOO=17 BAR="gooei" GOO_NEST={ { VAL=0x123456789abcdef MOO_NEST={ FOO=11 BAR="eleven" BAZ={1111,true} } }, { MOO_NEST={ FOO=12 BAR="twelve" BAZ={1212,false} } } } } +=== end t8l8v ordered test_enc_dec[4] + +=== start t8l8v unordered test_enc_dec[0] +encoded: MOO={ FOO=23 BAR="twentythree" } +myproto_msg_enc() rc = 0 +01 01 02 00 17 02 0b 74 77 65 6e 74 79 74 68 72 65 65 . +myproto_msg_dec() rc = 0 +decoded: MOO={ FOO=23 BAR="twentythree" } +=== end t8l8v unordered test_enc_dec[0] + +=== start t8l8v unordered test_enc_dec[1] +encoded: MOO={ FOO=23 BAR="twentythree" BAZ={2323,true} } +myproto_msg_enc() rc = 0 +01 01 02 00 17 02 0b 74 77 65 6e 74 79 74 68 72 65 65 03 02 89 13 . +myproto_msg_dec() rc = 0 +decoded: MOO={ FOO=23 BAR="twentythree" BAZ={2323,true} } +=== end t8l8v unordered test_enc_dec[1] + +=== start t8l8v unordered test_enc_dec[2] +encoded: MOO={ FOO=23 BAR="twentythree" BAZ={2323,true} REPEAT_INT={ 1, 2, 32767 } } +myproto_msg_enc() rc = 0 +01 01 02 00 17 02 0b 74 77 65 6e 74 79 74 68 72 65 65 03 02 89 13 04 02 00 01 04 02 00 02 04 02 7f ff . +myproto_msg_dec() rc = 0 +decoded: MOO={ FOO=23 BAR="twentythree" BAZ={2323,true} REPEAT_INT={ 1, 2, 32767 } } +=== end t8l8v unordered test_enc_dec[2] + +=== start t8l8v unordered test_enc_dec[3] +encoded: MOO={ FOO=23 BAR="twentythree" BAZ={2323,true} REPEAT_INT={ 1, 2, 32767 } REPEAT_STRUCT={ {1001,true,R_A}, {1002,false,R_B} } MOO_NEST={ FOO=42 BAR="fortytwo" BAZ={4242,false} } } +myproto_msg_enc() rc = 0 +01 01 02 00 17 02 0b 74 77 65 6e 74 79 74 68 72 65 65 03 02 89 13 04 02 00 01 04 02 00 02 04 02 7f ff 05 03 03 e9 80 05 03 03 ea 01 06 12 01 02 00 2a 02 08 66 6f 72 74 79 74 77 6f 03 02 10 92 . +myproto_msg_dec() rc = 0 +decoded: MOO={ FOO=23 BAR="twentythree" BAZ={2323,true} REPEAT_INT={ 1, 2, 32767 } REPEAT_STRUCT={ {1001,true,R_A}, {1002,false,R_B} } MOO_NEST={ FOO=42 BAR="fortytwo" BAZ={4242,false} } } +=== end t8l8v unordered test_enc_dec[3] + +=== start t8l8v unordered test_enc_dec[4] +encoded: GOO={ FOO=17 BAR="gooei" GOO_NEST={ { VAL=0x123456789abcdef MOO_NEST={ FOO=11 BAR="eleven" BAZ={1111,true} } }, { MOO_NEST={ FOO=12 BAR="twelve" BAZ={1212,false} } } } } +myproto_msg_enc() rc = 0 +07 01 02 00 11 02 05 67 6f 6f 65 69 08 1c 07 08 01 23 45 67 89 ab cd ef 06 10 01 02 00 0b 02 06 65 6c 65 76 65 6e 03 02 84 57 08 12 06 10 01 02 00 0c 02 06 74 77 65 6c 76 65 03 02 04 bc . +myproto_msg_dec() rc = 0 +decoded: GOO={ FOO=17 BAR="gooei" GOO_NEST={ { VAL=0x123456789abcdef MOO_NEST={ FOO=11 BAR="eleven" BAZ={1111,true} } }, { MOO_NEST={ FOO=12 BAR="twelve" BAZ={1212,false} } } } } +=== end t8l8v unordered test_enc_dec[4] + +=== start t16l16v ordered test_enc_dec[0] +encoded: MOO={ FOO=23 BAR="twentythree" } +myproto_msg_enc() rc = 0 +01 00 01 00 02 00 17 00 02 00 0b 74 77 65 6e 74 79 74 68 72 65 65 . +myproto_msg_dec() rc = 0 +decoded: MOO={ FOO=23 BAR="twentythree" } +=== end t16l16v ordered test_enc_dec[0] + +=== start t16l16v ordered test_enc_dec[1] +encoded: MOO={ FOO=23 BAR="twentythree" BAZ={2323,true} } +myproto_msg_enc() rc = 0 +01 00 01 00 02 00 17 00 02 00 0b 74 77 65 6e 74 79 74 68 72 65 65 00 03 00 02 89 13 . +myproto_msg_dec() rc = 0 +decoded: MOO={ FOO=23 BAR="twentythree" BAZ={2323,true} } +=== end t16l16v ordered test_enc_dec[1] + +=== start t16l16v ordered test_enc_dec[2] +encoded: MOO={ FOO=23 BAR="twentythree" BAZ={2323,true} REPEAT_INT={ 1, 2, 32767 } } +myproto_msg_enc() rc = 0 +01 00 01 00 02 00 17 00 02 00 0b 74 77 65 6e 74 79 74 68 72 65 65 00 03 00 02 89 13 00 04 00 02 00 01 00 04 00 02 00 02 00 04 00 02 7f ff . +myproto_msg_dec() rc = 0 +decoded: MOO={ FOO=23 BAR="twentythree" BAZ={2323,true} REPEAT_INT={ 1, 2, 32767 } } +=== end t16l16v ordered test_enc_dec[2] + +=== start t16l16v ordered test_enc_dec[3] +encoded: MOO={ FOO=23 BAR="twentythree" BAZ={2323,true} REPEAT_INT={ 1, 2, 32767 } REPEAT_STRUCT={ {1001,true,R_A}, {1002,false,R_B} } MOO_NEST={ FOO=42 BAR="fortytwo" BAZ={4242,false} } } +myproto_msg_enc() rc = 0 +01 00 01 00 02 00 17 00 02 00 0b 74 77 65 6e 74 79 74 68 72 65 65 00 03 00 02 89 13 00 04 00 02 00 01 00 04 00 02 00 02 00 04 00 02 7f ff 00 05 00 03 03 e9 80 00 05 00 03 03 ea 01 00 06 00 18 00 01 00 02 00 2a 00 02 00 08 66 6f 72 74 79 74 77 6f 00 03 00 02 10 92 . +myproto_msg_dec() rc = 0 +decoded: MOO={ FOO=23 BAR="twentythree" BAZ={2323,true} REPEAT_INT={ 1, 2, 32767 } REPEAT_STRUCT={ {1001,true,R_A}, {1002,false,R_B} } MOO_NEST={ FOO=42 BAR="fortytwo" BAZ={4242,false} } } +=== end t16l16v ordered test_enc_dec[3] + +=== start t16l16v ordered test_enc_dec[4] +encoded: GOO={ FOO=17 BAR="gooei" GOO_NEST={ { VAL=0x123456789abcdef MOO_NEST={ FOO=11 BAR="eleven" BAZ={1111,true} } }, { MOO_NEST={ FOO=12 BAR="twelve" BAZ={1212,false} } } } } +myproto_msg_enc() rc = 0 +07 00 01 00 02 00 11 00 02 00 05 67 6f 6f 65 69 00 08 00 26 00 07 00 08 01 23 45 67 89 ab cd ef 00 06 00 16 00 01 00 02 00 0b 00 02 00 06 65 6c 65 76 65 6e 00 03 00 02 84 57 00 08 00 1a 00 06 00 16 00 01 00 02 00 0c 00 02 00 06 74 77 65 6c 76 65 00 03 00 02 04 bc . +myproto_msg_dec() rc = 0 +decoded: GOO={ FOO=17 BAR="gooei" GOO_NEST={ { VAL=0x123456789abcdef MOO_NEST={ FOO=11 BAR="eleven" BAZ={1111,true} } }, { MOO_NEST={ FOO=12 BAR="twelve" BAZ={1212,false} } } } } +=== end t16l16v ordered test_enc_dec[4] + +=== start t16l16v unordered test_enc_dec[0] +encoded: MOO={ FOO=23 BAR="twentythree" } +myproto_msg_enc() rc = 0 +01 00 01 00 02 00 17 00 02 00 0b 74 77 65 6e 74 79 74 68 72 65 65 . +myproto_msg_dec() rc = 0 +decoded: MOO={ FOO=23 BAR="twentythree" } +=== end t16l16v unordered test_enc_dec[0] + +=== start t16l16v unordered test_enc_dec[1] +encoded: MOO={ FOO=23 BAR="twentythree" BAZ={2323,true} } +myproto_msg_enc() rc = 0 +01 00 01 00 02 00 17 00 02 00 0b 74 77 65 6e 74 79 74 68 72 65 65 00 03 00 02 89 13 . +myproto_msg_dec() rc = 0 +decoded: MOO={ FOO=23 BAR="twentythree" BAZ={2323,true} } +=== end t16l16v unordered test_enc_dec[1] + +=== start t16l16v unordered test_enc_dec[2] +encoded: MOO={ FOO=23 BAR="twentythree" BAZ={2323,true} REPEAT_INT={ 1, 2, 32767 } } +myproto_msg_enc() rc = 0 +01 00 01 00 02 00 17 00 02 00 0b 74 77 65 6e 74 79 74 68 72 65 65 00 03 00 02 89 13 00 04 00 02 00 01 00 04 00 02 00 02 00 04 00 02 7f ff . +myproto_msg_dec() rc = 0 +decoded: MOO={ FOO=23 BAR="twentythree" BAZ={2323,true} REPEAT_INT={ 1, 2, 32767 } } +=== end t16l16v unordered test_enc_dec[2] + +=== start t16l16v unordered test_enc_dec[3] +encoded: MOO={ FOO=23 BAR="twentythree" BAZ={2323,true} REPEAT_INT={ 1, 2, 32767 } REPEAT_STRUCT={ {1001,true,R_A}, {1002,false,R_B} } MOO_NEST={ FOO=42 BAR="fortytwo" BAZ={4242,false} } } +myproto_msg_enc() rc = 0 +01 00 01 00 02 00 17 00 02 00 0b 74 77 65 6e 74 79 74 68 72 65 65 00 03 00 02 89 13 00 04 00 02 00 01 00 04 00 02 00 02 00 04 00 02 7f ff 00 05 00 03 03 e9 80 00 05 00 03 03 ea 01 00 06 00 18 00 01 00 02 00 2a 00 02 00 08 66 6f 72 74 79 74 77 6f 00 03 00 02 10 92 . +myproto_msg_dec() rc = 0 +decoded: MOO={ FOO=23 BAR="twentythree" BAZ={2323,true} REPEAT_INT={ 1, 2, 32767 } REPEAT_STRUCT={ {1001,true,R_A}, {1002,false,R_B} } MOO_NEST={ FOO=42 BAR="fortytwo" BAZ={4242,false} } } +=== end t16l16v unordered test_enc_dec[3] + +=== start t16l16v unordered test_enc_dec[4] +encoded: GOO={ FOO=17 BAR="gooei" GOO_NEST={ { VAL=0x123456789abcdef MOO_NEST={ FOO=11 BAR="eleven" BAZ={1111,true} } }, { MOO_NEST={ FOO=12 BAR="twelve" BAZ={1212,false} } } } } +myproto_msg_enc() rc = 0 +07 00 01 00 02 00 11 00 02 00 05 67 6f 6f 65 69 00 08 00 26 00 07 00 08 01 23 45 67 89 ab cd ef 00 06 00 16 00 01 00 02 00 0b 00 02 00 06 65 6c 65 76 65 6e 00 03 00 02 84 57 00 08 00 1a 00 06 00 16 00 01 00 02 00 0c 00 02 00 06 74 77 65 6c 76 65 00 03 00 02 04 bc . +myproto_msg_dec() rc = 0 +decoded: GOO={ FOO=17 BAR="gooei" GOO_NEST={ { VAL=0x123456789abcdef MOO_NEST={ FOO=11 BAR="eleven" BAZ={1111,true} } }, { MOO_NEST={ FOO=12 BAR="twelve" BAZ={1212,false} } } } } +=== end t16l16v unordered test_enc_dec[4] diff --git a/tests/libosmo-tlv/tlv_dec_enc_test.c b/tests/libosmo-tlv/tlv_dec_enc_test.c index b7daad7..11564a3 100644 --- a/tests/libosmo-tlv/tlv_dec_enc_test.c +++ b/tests/libosmo-tlv/tlv_dec_enc_test.c @@ -199,21 +199,21 @@ int enc_to_str_repeat_struct(char *buf, size_t buflen, void *encode_from) struct osmo_tlv_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, @@ -224,21 +224,21 @@ struct osmo_tlv_coding nested_inner_msg_ies[] = { struct osmo_tlv_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, @@ -247,7 +247,7 @@ struct osmo_tlv_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, @@ -258,7 +258,7 @@ struct osmo_tlv_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, @@ -269,7 +269,7 @@ struct osmo_tlv_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-tlv/tlv_test.c b/tests/libosmo-tlv/tlv_test.c index 20934db..4bbcf59 100644 --- a/tests/libosmo-tlv/tlv_test.c +++ b/tests/libosmo-tlv/tlv_test.c @@ -28,7 +28,7 @@ void *ctx; struct ie { - int tag; + struct osmo_tlv_tag_inst ti; const char *val; }; @@ -43,7 +43,7 @@ struct msgb *test_tlv_enc(const struct osmo_tlv_cfg *cfg, const struct ie *ies) for (ie = ies; ie->val; ie++) { /* put header without knowing length yet */ - OSMO_ASSERT(osmo_tlv_put_tl(&tlv, ie->tag, 0) == 0); + OSMO_ASSERT(osmo_tlv_put_tli(&tlv, &ie->ti, 0) == 0); /* put value data, as much as desired */ msgb_put(tlv.dst, osmo_hexparse(ie->val, tlv.dst->tail, msgb_tailroom(tlv.dst))); /* update header len from amount of written data */ @@ -75,7 +75,10 @@ void test_tlv_dec(const struct osmo_tlv_cfg *cfg, const struct ie *ies, struct m /* end of TLV structure? */ if (!tlv.val) break; - printf(" T=%d L=%zu v=%s\n", tlv.tag, tlv.len, osmo_hexdump_nospc(tlv.val, tlv.len)); + printf(" T=%d L=%zu", tlv.tag, tlv.len); + if (tlv.instance != OSMO_TLV_NO_INSTANCE) + printf(" I=%u", tlv.instance); + printf(" v=%s\n", osmo_hexdump_nospc(tlv.val, tlv.len)); if (tlv.tag != ie->tag) { printf(" ERROR loading TLV structure: expected tag %d, got tag %d\n", ie->tag, tlv.tag); exit(1); @@ -102,22 +105,33 @@ void test_tlv_peek(const struct osmo_tlv_cfg *cfg, const struct ie *ies, struct ie = ies; while (1) { int rc; - int next_tag = osmo_tlv_load_peek_tag(&tlv); - if (next_tag == -ENOENT) - printf(" peek T=-ENOENT\n"); - else - printf(" peek T=%d\n", next_tag); + unsigned int next_tag; + unsigned int next_instance; + rc = osmo_tlv_load_peek_tag(&tlv, &next_tag, &next_instance); + if (rc == -ENOENT) { + printf(" peek rc=-ENOENT\n"); + } else { + printf(" peek T=%u", next_tag); + if (next_instance != OSMO_TLV_NO_INSTANCE) + printf(" I=%u", next_instance); + printf("\n"); + } if (ie->val && next_tag != ie->tag) { - printf(" ERROR peeking tag: expected tag %d, got tag %d\n", ie->tag, next_tag); + printf(" ERROR peeking tag: expected tag %u, got tag %u\n", ie->tag, next_tag); exit(1); } - if (!ie->val && next_tag != -ENOENT) { - printf(" ERROR peeking tag: expected -ENOENT, got tag %d\n", next_tag); + if (ie->val && ie->instance && next_instance != ie->instance) { + printf(" ERROR peeking tag: expected instance %u, got instance %u\n", ie->instance, + next_instance); + exit(1); + } + if (!ie->val && rc != -ENOENT) { + printf(" ERROR peeking tag: expected -ENOENT, got rc=%d, tag %u\n", rc, next_tag); exit(1); } - if (next_tag == -ENOENT) + if (rc == -ENOENT) break; /* go to the next TLV */ @@ -151,7 +165,7 @@ void test_tlv_dec_by_tag(const struct osmo_tlv_cfg *cfg, const struct ie *ies, s for (ie = last_ie; ie >= ies; ie--) { /* each time, look from the beginning */ osmo_tlv_load_start(&tlv); - rc = osmo_tlv_load_next_by_tag(&tlv, ie->tag); + rc = osmo_tlv_load_next_by_tag(&tlv, ie->tag, ie->instance ? : OSMO_TLV_NO_INSTANCE); if (rc) { printf(" ERROR loading TLV structure: osmo_tlv_load_next_by_tag(%d) rc = %d\n", ie->tag, rc); exit(1); @@ -167,9 +181,13 @@ void test_tlv_dec_by_tag(const struct osmo_tlv_cfg *cfg, const struct ie *ies, s } if (strcmp(ie->val, osmo_hexdump_nospc(tlv.val, tlv.len))) { while (1) { - printf(" (mismatch: T=%d L=%zu v=%s, checking for another occurrence of T=%d)\n", - tlv.tag, tlv.len, osmo_hexdump_nospc(tlv.val, tlv.len), tlv.tag); - rc = osmo_tlv_load_next_by_tag(&tlv, ie->tag); + printf(" (mismatch: T=%u L=%zu", tlv.tag, tlv.len); + if (tlv.instance != OSMO_TLV_NO_INSTANCE) + printf(" I=%u", tlv.instance); + printf(" v=%s, checking for another occurrence of T=%d)\n", + osmo_hexdump_nospc(tlv.val, tlv.len), tlv.tag); + + rc = osmo_tlv_load_next_by_tag(&tlv, ie->tag, ie->instance ? : OSMO_TLV_NO_INSTANCE); if (rc || !tlv.val) { printf(" ERROR val not found\n"); exit(1); @@ -179,7 +197,10 @@ void test_tlv_dec_by_tag(const struct osmo_tlv_cfg *cfg, const struct ie *ies, s } } } - printf(" T=%d L=%zu v=%s\n", tlv.tag, tlv.len, osmo_hexdump_nospc(tlv.val, tlv.len)); + printf(" T=%d L=%zu", tlv.tag, tlv.len); + if (tlv.instance != OSMO_TLV_NO_INSTANCE) + printf(" I=%u", tlv.instance); + printf(" v=%s\n", osmo_hexdump_nospc(tlv.val, tlv.len)); } printf("- decoding every second tag:\n"); @@ -191,7 +212,7 @@ void test_tlv_dec_by_tag(const struct osmo_tlv_cfg *cfg, const struct ie *ies, s if (!ie->val) break; - rc = osmo_tlv_load_next_by_tag(&tlv, ie->tag); + rc = osmo_tlv_load_next_by_tag(&tlv, ie->tag, ie->instance ? : OSMO_TLV_NO_INSTANCE); if (rc) { printf(" ERROR loading TLV structure: osmo_tlv_load_next_by_tag(%d) rc = %d\n", ie->tag, rc); exit(1); @@ -207,9 +228,13 @@ void test_tlv_dec_by_tag(const struct osmo_tlv_cfg *cfg, const struct ie *ies, s } if (strcmp(ie->val, osmo_hexdump_nospc(tlv.val, tlv.len))) { while (1) { - printf(" (mismatch: T=%d L=%zu v=%s, checking for another occurrence of T=%d)\n", - tlv.tag, tlv.len, osmo_hexdump_nospc(tlv.val, tlv.len), tlv.tag); - rc = osmo_tlv_load_next_by_tag(&tlv, ie->tag); + printf(" (mismatch: T=%u L=%zu", tlv.tag, tlv.len); + if (tlv.instance != OSMO_TLV_NO_INSTANCE) + printf(" I=%u", tlv.instance); + printf(" v=%s, checking for another occurrence of T=%d)\n", + osmo_hexdump_nospc(tlv.val, tlv.len), tlv.tag); + + rc = osmo_tlv_load_next_by_tag(&tlv, ie->tag, ie->instance ? : OSMO_TLV_NO_INSTANCE); if (rc || !tlv.val) { printf(" ERROR val not found\n"); exit(1); @@ -219,14 +244,17 @@ void test_tlv_dec_by_tag(const struct osmo_tlv_cfg *cfg, const struct ie *ies, s } } } - printf(" T=%d L=%zu v=%s\n", tlv.tag, tlv.len, osmo_hexdump_nospc(tlv.val, tlv.len)); + printf(" T=%d L=%zu", tlv.tag, tlv.len); + if (tlv.instance != OSMO_TLV_NO_INSTANCE) + printf(" I=%u", tlv.instance); + printf(" v=%s\n", osmo_hexdump_nospc(tlv.val, tlv.len)); } printf("- enforcing order: without restart, a past tag is not parsed again:\n"); /* Try to read the first tag, expect that it isn't found because we're already halfway in the message data */ ie = ies; - rc = osmo_tlv_load_next_by_tag(&tlv, ie->tag); - printf(" osmo_tlv_load_next_by_tag(%d) rc=", ie->tag); + rc = osmo_tlv_load_next_by_tag(&tlv, ie->tag, ie->instance ? : OSMO_TLV_NO_INSTANCE); + printf(" osmo_tlv_load_next_by_tag(%u, %u) rc=%d", ie->tag, ie->instance ? : OSMO_TLV_NO_INSTANCE, rc); if (rc == -ENOENT) { printf("-ENOENT\n"); } else { @@ -440,7 +468,8 @@ int txlxv_load_tl(struct osmo_tlv_load *tlv, const uint8_t *src_data, size_t src } /* Example of defining a variable TL, where size of T and L depend on the actual tag and length values: store. */ -int txlxv_store_tl(uint8_t *dst_data, size_t dst_data_avail, unsigned int tag, size_t len, struct osmo_tlv_put *tlv) +int txlxv_store_tl(uint8_t *dst_data, size_t dst_data_avail, unsigned int tag, unsigned int instance, size_t len, + struct osmo_tlv_put *tlv) { uint8_t *pos = dst_data; uint8_t *end = dst_data + dst_data_avail; @@ -491,7 +520,92 @@ const struct osmo_tlv_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_tlv_load *tlv, const uint8_t *src_data, size_t src_data_len) +{ + /* already validated in next_tl_valid(): src_data_len >= cfg->tl_min_size == 2. */ + tlv->tag = src_data[0]; + tlv->len = src_data[1]; + + switch (tlv->tag) { + /* All tags that are TLIV go here */ + case 5: + case 7: + case 9: + if (src_data_len < 3) + return -ENOSPC; + tlv->instance = src_data[2]; + tlv->val = src_data + 3; + return 0; + default: + tlv->val = src_data + 2; + return 0; + } +} + +static int tliv_store_tl(uint8_t *dst_data, size_t dst_data_avail, unsigned int tag, unsigned int instance, size_t len, + struct osmo_tlv_put *tlv) +{ + if (tag > UINT8_MAX) + return -EINVAL; + if (len > UINT8_MAX) + return -EMSGSIZE; + if (dst_data_avail < 2) + return -ENOSPC; + + dst_data[0] = tag; + dst_data[1] = len; + + switch (tag) { + /* All tags that are TLIV go here */ + case 5: + case 7: + case 9: + if (dst_data_avail < 3) + return -ENOSPC; + if (instance > UINT8_MAX) + return -EINVAL; + dst_data[2] = instance; + return 3; + default: + return 2; + } +} + +const struct osmo_tlv_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, "0017", 1 }, + /* TLIV */ + { 5, "0018", 2 }, + /* TLIV */ + { 5, "0019", 3 }, + /* TLV */ + { 6, "001a" }, + /* TLIV */ + { 7, "001b", 1 }, + /* TLIV */ + { 9, "001c", 1 }, + {} +}; + +struct ie *tliv_tests[] = { + tliv_test1, +}; + +void test_tliv() +{ + test_tlv(__func__, tliv_tests, ARRAY_SIZE(tliv_tests), &osmo_tliv_cfg); } int main() @@ -502,6 +616,7 @@ int main() test_t8l8v(); test_t16l16v(); test_txlxv(); + test_tliv(); talloc_free(ctx); return 0;