Harald Welte has submitted this change and it was merged. ( 
https://gerrit.osmocom.org/12900 )

Change subject: TLV: Add one-shot TLV encoder
......................................................................

TLV: Add one-shot TLV encoder

So far, the TLV code contained two types of functions
* tlp_parse() to parse all TLVs according to definition into tlvp_parsed
* various helper functions to encode individual TLVs during message
  generation

This patch implements the inverse of tlv_parse(): tlv_encode(), which
takes a full 'struct tlv_pared' and encodes all IEs found in it.  The
order of IEs is in numerically ascending order of the tag.

As many protocols have different IE/TLV ordering requirements, let's add
a tlv_encode_ordered() function where the caller can specify the TLV
ordering during the one-shot encode.

Change-Id: I761a30bf20355a9f80a4a8e0c60b0b0f78515efe
---
M include/osmocom/gsm/tlv.h
M src/gsm/libosmogsm.map
M src/gsm/tlv_parser.c
M tests/tlv/tlv_test.c
M tests/tlv/tlv_test.ok
5 files changed, 155 insertions(+), 0 deletions(-)

Approvals:
  Harald Welte: Looks good to me, approved
  Jenkins Builder: Verified



diff --git a/include/osmocom/gsm/tlv.h b/include/osmocom/gsm/tlv.h
index d0c9552..bb0e8fc 100644
--- a/include/osmocom/gsm/tlv.h
+++ b/include/osmocom/gsm/tlv.h
@@ -457,6 +457,12 @@
 /* take a master (src) tlv def and fill up all empty slots in 'dst' */
 void tlv_def_patch(struct tlv_definition *dst, const struct tlv_definition 
*src);

+int tlv_encode_one(struct msgb *msg, enum tlv_type type, uint8_t tag,
+                  unsigned int len, const uint8_t *val);
+int tlv_encode(struct msgb *msg, const struct tlv_definition *def, const 
struct tlv_parsed *tp);
+int tlv_encode_ordered(struct msgb *msg, const struct tlv_definition *def, 
const struct tlv_parsed *tp,
+                       const uint8_t *tag_order, unsigned int tag_order_len);
+
 #define TLVP_PRESENT(x, y)     ((x)->lv[y].val)
 #define TLVP_LEN(x, y)         (x)->lv[y].len
 #define TLVP_VAL(x, y)         (x)->lv[y].val
diff --git a/src/gsm/libosmogsm.map b/src/gsm/libosmogsm.map
index 0f4a0db..299504e 100644
--- a/src/gsm/libosmogsm.map
+++ b/src/gsm/libosmogsm.map
@@ -537,6 +537,9 @@
 tlv_parse;
 tlv_parse2;
 tlv_parse_one;
+tlv_encode;
+tlv_encode_ordered;
+tlv_encode_one;
 tvlv_att_def;
 vtvlv_gan_att_def;

diff --git a/src/gsm/tlv_parser.c b/src/gsm/tlv_parser.c
index 6e089f7..159b42b 100644
--- a/src/gsm/tlv_parser.c
+++ b/src/gsm/tlv_parser.c
@@ -120,6 +120,103 @@
        return 0;
 }

+
+/*! Encode a single TLV into given message buffer
+ *  \param[inout] msg Caller-allocated message buffer with sufficient tailroom
+ *  \param[in] type TLV type/format to use during encode
+ *  \param[in] tag Tag of TLV to be encoded
+ *  \parma[in] len Length of TLV to be encoded
+ *  \param[in] val Value part of TLV to be encoded
+ *  \returns 0 on success; negative in case of error */
+int tlv_encode_one(struct msgb *msg, enum tlv_type type, uint8_t tag,
+                  unsigned int len, const uint8_t *val)
+{
+       switch (type) {
+       case TLV_TYPE_NONE:
+               break;
+       case TLV_TYPE_FIXED:
+               msgb_tv_fixed_put(msg, tag, len, val);
+               break;
+       case TLV_TYPE_T:
+               msgb_v_put(msg, tag);
+               break;
+       case TLV_TYPE_TV:
+               msgb_tv_put(msg, tag, val[0]);
+               break;
+       case TLV_TYPE_TLV:
+               msgb_tlv_put(msg, tag, len, val);
+               break;
+       case TLV_TYPE_TL16V:
+               msgb_tl16v_put(msg, tag, len, val);
+               break;
+       case TLV_TYPE_TvLV:
+               msgb_tvlv_put(msg, tag, len, val);
+               break;
+       case TLV_TYPE_SINGLE_TV:
+               msgb_v_put(msg, (tag << 4) | (val[0] & 0xf));
+               break;
+       case TLV_TYPE_vTvLV_GAN:
+               msgb_vtvlv_gan_put(msg, tag, len, val);
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+/*! Encode a set of decoded TLVs according to a given definition into a 
message buffer
+ *  \param[inout] msg Caller-allocated message buffer with sufficient tailroom
+ *  \param[in] def structure defining the valid TLV tags / configurations
+ *  \param[in] tp decoded values to be encoded
+ *  \returns number of bytes consumed in msg; negative in case of error */
+int tlv_encode(struct msgb *msg, const struct tlv_definition *def, const 
struct tlv_parsed *tp)
+{
+       unsigned int tailroom_before = msgb_tailroom(msg);
+       unsigned int i;
+       int rc;
+
+       for (i = 0; i < ARRAY_SIZE(tp->lv); i++) {
+               /* skip entries in the array that aren't used/filled */
+               if (!TLVP_PRESENT(tp, i))
+                       continue;
+
+               rc = tlv_encode_one(msg, def->def[i].type, i, TLVP_LEN(tp, i), 
TLVP_VAL(tp, i));
+               if (rc < 0)
+                       return rc;
+       }
+
+       return tailroom_before - msgb_tailroom(msg);
+}
+
+/*! Encode a set of decoded TLVs according to a given definition and IE order 
into a message buffer
+ *  \param[inout] msg Caller-allocated message buffer with sufficient tailroom
+ *  \param[in] def structure defining the valid TLV tags / configurations
+ *  \param[in] tp decoded values to be encoded
+ *  \param[in] tag_order array of tags determining the IE encoding order
+ *  \param[in] tag_order_len length of tag_order
+ *  \returns number of bytes consumed in msg; negative in case of error */
+int tlv_encode_ordered(struct msgb *msg, const struct tlv_definition *def, 
const struct tlv_parsed *tp,
+                       const uint8_t *tag_order, unsigned int tag_order_len)
+{
+
+       unsigned int tailroom_before = msgb_tailroom(msg);
+       unsigned int i;
+       int rc;
+
+       for (i = 0; i < tag_order_len; i++) {
+               uint8_t tag = tag_order[i];
+
+               /* skip entries in the array that aren't used/filled */
+               if (!TLVP_PRESENT(tp, tag))
+                       continue;
+
+               rc = tlv_encode_one(msg, def->def[tag].type, tag, TLVP_LEN(tp, 
tag), TLVP_VAL(tp, tag));
+               if (rc < 0)
+                       return rc;
+       }
+       return tailroom_before - msgb_tailroom(msg);
+}
+
 /*! Parse a single TLV encoded IE
  *  \param[out] o_tag the tag of the IE that was found
  *  \param[out] o_len length of the IE that was found
diff --git a/tests/tlv/tlv_test.c b/tests/tlv/tlv_test.c
index e2065b0..925d762 100644
--- a/tests/tlv/tlv_test.c
+++ b/tests/tlv/tlv_test.c
@@ -1,4 +1,6 @@
+#include <osmocom/core/msgb.h>
 #include <osmocom/gsm/tlv.h>
+#include <osmocom/gsm/gsm0808.h>

 static void check_tlv_parse(uint8_t **data, size_t *data_len,
                            uint8_t exp_tag, size_t exp_len, const uint8_t 
*exp_val)
@@ -286,12 +288,57 @@
        OSMO_ASSERT(dec3[2].lv[tag].val == &test_data[2 + 3 + 3]);
 }

+static void test_tlv_encoder()
+{
+       const uint8_t enc_ies[] = {
+               0x17, 0x14,     0x06, 0x2b, 0x12, 0x2b, 0x0b, 0x40, 0x2b, 0xb7, 
0x05, 0xd0, 0x63, 0x82, 0x95, 0x03, 0x05, 0x40,
+                               0x07, 0x08, 0x43, 0x90,
+               0x2c,           0x04,
+               0x40,           0x42,
+       };
+       const uint8_t ie_order[] = { 0x2c, 0x40, 0x17 };
+       const uint8_t enc_ies_reordered[] = {
+               0x2c,           0x04,
+               0x40,           0x42,
+               0x17, 0x14,     0x06, 0x2b, 0x12, 0x2b, 0x0b, 0x40, 0x2b, 0xb7, 
0x05, 0xd0, 0x63, 0x82, 0x95, 0x03, 0x05, 0x40,
+                               0x07, 0x08, 0x43, 0x90,
+       };
+       struct tlv_parsed tp;
+       struct msgb *msg = msgb_alloc(1024, __func__);
+       int rc;
+
+       printf("Testing TLV encoder by decoding + re-encoding binary\n");
+
+       OSMO_ASSERT(msg);
+
+       /* decode BSSAP IEs specified above */
+       rc = osmo_bssap_tlv_parse(&tp, enc_ies, ARRAY_SIZE(enc_ies));
+       OSMO_ASSERT(rc == 3);
+
+       /* re-encode it */
+       rc = tlv_encode(msg, gsm0808_att_tlvdef(), &tp);
+       OSMO_ASSERT(rc == ARRAY_SIZE(enc_ies));
+       OSMO_ASSERT(!memcmp(msgb_data(msg), enc_ies, ARRAY_SIZE(enc_ies)));
+
+       msgb_reset(msg);
+
+       printf("Testing TLV encoder with IE ordering\n");
+
+       /* re-encodei in different order */
+       rc = tlv_encode_ordered(msg, gsm0808_att_tlvdef(), &tp, ie_order, 
ARRAY_SIZE(ie_order));
+       OSMO_ASSERT(rc == ARRAY_SIZE(enc_ies));
+       OSMO_ASSERT(!memcmp(msgb_data(msg), enc_ies_reordered, 
ARRAY_SIZE(enc_ies_reordered)));
+
+       msgb_free(msg);
+}
+
 int main(int argc, char **argv)
 {
        //osmo_init_logging2(ctx, &info);

        test_tlv_shift_functions();
        test_tlv_repeated_ie();
+       test_tlv_encoder();

        printf("Done.\n");
        return EXIT_SUCCESS;
diff --git a/tests/tlv/tlv_test.ok b/tests/tlv/tlv_test.ok
index de159bf..f3f0fd4 100644
--- a/tests/tlv/tlv_test.ok
+++ b/tests/tlv/tlv_test.ok
@@ -1,2 +1,4 @@
 Test shift functions
+Testing TLV encoder by decoding + re-encoding binary
+Testing TLV encoder with IE ordering
 Done.

--
To view, visit https://gerrit.osmocom.org/12900
To unsubscribe, or for help writing mail filters, visit 
https://gerrit.osmocom.org/settings

Gerrit-Project: libosmocore
Gerrit-Branch: master
Gerrit-MessageType: merged
Gerrit-Change-Id: I761a30bf20355a9f80a4a8e0c60b0b0f78515efe
Gerrit-Change-Number: 12900
Gerrit-PatchSet: 8
Gerrit-Owner: Harald Welte <[email protected]>
Gerrit-Reviewer: Harald Welte <[email protected]>
Gerrit-Reviewer: Jenkins Builder (1000002)
Gerrit-Reviewer: Pau Espin Pedrol <[email protected]>
Gerrit-Reviewer: Vadim Yanitskiy <[email protected]>

Reply via email to