<p>Harald Welte <strong>merged</strong> this change.</p><p><a href="https://gerrit.osmocom.org/12900">View Change</a></p><div style="white-space:pre-wrap">Approvals:
  Harald Welte: Looks good to me, approved
  Jenkins Builder: Verified

</div><pre style="font-family: monospace,monospace; white-space: pre-wrap;">TLV: Add one-shot TLV encoder<br><br>So far, the TLV code contained two types of functions<br>* tlp_parse() to parse all TLVs according to definition into tlvp_parsed<br>* various helper functions to encode individual TLVs during message<br>  generation<br><br>This patch implements the inverse of tlv_parse(): tlv_encode(), which<br>takes a full 'struct tlv_pared' and encodes all IEs found in it.  The<br>order of IEs is in numerically ascending order of the tag.<br><br>As many protocols have different IE/TLV ordering requirements, let's add<br>a tlv_encode_ordered() function where the caller can specify the TLV<br>ordering during the one-shot encode.<br><br>Change-Id: I761a30bf20355a9f80a4a8e0c60b0b0f78515efe<br>---<br>M include/osmocom/gsm/tlv.h<br>M src/gsm/libosmogsm.map<br>M src/gsm/tlv_parser.c<br>M tests/tlv/tlv_test.c<br>M tests/tlv/tlv_test.ok<br>5 files changed, 155 insertions(+), 0 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/include/osmocom/gsm/tlv.h b/include/osmocom/gsm/tlv.h</span><br><span>index d0c9552..bb0e8fc 100644</span><br><span>--- a/include/osmocom/gsm/tlv.h</span><br><span>+++ b/include/osmocom/gsm/tlv.h</span><br><span>@@ -457,6 +457,12 @@</span><br><span> /* take a master (src) tlv def and fill up all empty slots in 'dst' */</span><br><span> void tlv_def_patch(struct tlv_definition *dst, const struct tlv_definition *src);</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+int tlv_encode_one(struct msgb *msg, enum tlv_type type, uint8_t tag,</span><br><span style="color: hsl(120, 100%, 40%);">+                  unsigned int len, const uint8_t *val);</span><br><span style="color: hsl(120, 100%, 40%);">+int tlv_encode(struct msgb *msg, const struct tlv_definition *def, const struct tlv_parsed *tp);</span><br><span style="color: hsl(120, 100%, 40%);">+int tlv_encode_ordered(struct msgb *msg, const struct tlv_definition *def, const struct tlv_parsed *tp,</span><br><span style="color: hsl(120, 100%, 40%);">+                  const uint8_t *tag_order, unsigned int tag_order_len);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> #define TLVP_PRESENT(x, y)  ((x)->lv[y].val)</span><br><span> #define TLVP_LEN(x, y)           (x)->lv[y].len</span><br><span> #define TLVP_VAL(x, y)             (x)->lv[y].val</span><br><span>diff --git a/src/gsm/libosmogsm.map b/src/gsm/libosmogsm.map</span><br><span>index 0f4a0db..299504e 100644</span><br><span>--- a/src/gsm/libosmogsm.map</span><br><span>+++ b/src/gsm/libosmogsm.map</span><br><span>@@ -537,6 +537,9 @@</span><br><span> tlv_parse;</span><br><span> tlv_parse2;</span><br><span> tlv_parse_one;</span><br><span style="color: hsl(120, 100%, 40%);">+tlv_encode;</span><br><span style="color: hsl(120, 100%, 40%);">+tlv_encode_ordered;</span><br><span style="color: hsl(120, 100%, 40%);">+tlv_encode_one;</span><br><span> tvlv_att_def;</span><br><span> vtvlv_gan_att_def;</span><br><span> </span><br><span>diff --git a/src/gsm/tlv_parser.c b/src/gsm/tlv_parser.c</span><br><span>index 6e089f7..159b42b 100644</span><br><span>--- a/src/gsm/tlv_parser.c</span><br><span>+++ b/src/gsm/tlv_parser.c</span><br><span>@@ -120,6 +120,103 @@</span><br><span>       return 0;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Encode a single TLV into given message buffer</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[inout] msg Caller-allocated message buffer with sufficient tailroom</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] type TLV type/format to use during encode</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] tag Tag of TLV to be encoded</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \parma[in] len Length of TLV to be encoded</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] val Value part of TLV to be encoded</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \returns 0 on success; negative in case of error */</span><br><span style="color: hsl(120, 100%, 40%);">+int tlv_encode_one(struct msgb *msg, enum tlv_type type, uint8_t tag,</span><br><span style="color: hsl(120, 100%, 40%);">+               unsigned int len, const uint8_t *val)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   switch (type) {</span><br><span style="color: hsl(120, 100%, 40%);">+       case TLV_TYPE_NONE:</span><br><span style="color: hsl(120, 100%, 40%);">+           break;</span><br><span style="color: hsl(120, 100%, 40%);">+        case TLV_TYPE_FIXED:</span><br><span style="color: hsl(120, 100%, 40%);">+          msgb_tv_fixed_put(msg, tag, len, val);</span><br><span style="color: hsl(120, 100%, 40%);">+                break;</span><br><span style="color: hsl(120, 100%, 40%);">+        case TLV_TYPE_T:</span><br><span style="color: hsl(120, 100%, 40%);">+              msgb_v_put(msg, tag);</span><br><span style="color: hsl(120, 100%, 40%);">+         break;</span><br><span style="color: hsl(120, 100%, 40%);">+        case TLV_TYPE_TV:</span><br><span style="color: hsl(120, 100%, 40%);">+             msgb_tv_put(msg, tag, val[0]);</span><br><span style="color: hsl(120, 100%, 40%);">+                break;</span><br><span style="color: hsl(120, 100%, 40%);">+        case TLV_TYPE_TLV:</span><br><span style="color: hsl(120, 100%, 40%);">+            msgb_tlv_put(msg, tag, len, val);</span><br><span style="color: hsl(120, 100%, 40%);">+             break;</span><br><span style="color: hsl(120, 100%, 40%);">+        case TLV_TYPE_TL16V:</span><br><span style="color: hsl(120, 100%, 40%);">+          msgb_tl16v_put(msg, tag, len, val);</span><br><span style="color: hsl(120, 100%, 40%);">+           break;</span><br><span style="color: hsl(120, 100%, 40%);">+        case TLV_TYPE_TvLV:</span><br><span style="color: hsl(120, 100%, 40%);">+           msgb_tvlv_put(msg, tag, len, val);</span><br><span style="color: hsl(120, 100%, 40%);">+            break;</span><br><span style="color: hsl(120, 100%, 40%);">+        case TLV_TYPE_SINGLE_TV:</span><br><span style="color: hsl(120, 100%, 40%);">+              msgb_v_put(msg, (tag << 4) | (val[0] & 0xf));</span><br><span style="color: hsl(120, 100%, 40%);">+               break;</span><br><span style="color: hsl(120, 100%, 40%);">+        case TLV_TYPE_vTvLV_GAN:</span><br><span style="color: hsl(120, 100%, 40%);">+              msgb_vtvlv_gan_put(msg, tag, len, val);</span><br><span style="color: hsl(120, 100%, 40%);">+               break;</span><br><span style="color: hsl(120, 100%, 40%);">+        default:</span><br><span style="color: hsl(120, 100%, 40%);">+              return -EINVAL;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+     return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Encode a set of decoded TLVs according to a given definition into a message buffer</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[inout] msg Caller-allocated message buffer with sufficient tailroom</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] def structure defining the valid TLV tags / configurations</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] tp decoded values to be encoded</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \returns number of bytes consumed in msg; negative in case of error */</span><br><span style="color: hsl(120, 100%, 40%);">+int tlv_encode(struct msgb *msg, const struct tlv_definition *def, const struct tlv_parsed *tp)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     unsigned int tailroom_before = msgb_tailroom(msg);</span><br><span style="color: hsl(120, 100%, 40%);">+    unsigned int i;</span><br><span style="color: hsl(120, 100%, 40%);">+       int rc;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     for (i = 0; i < ARRAY_SIZE(tp->lv); i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+              /* skip entries in the array that aren't used/filled */</span><br><span style="color: hsl(120, 100%, 40%);">+           if (!TLVP_PRESENT(tp, i))</span><br><span style="color: hsl(120, 100%, 40%);">+                     continue;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           rc = tlv_encode_one(msg, def->def[i].type, i, TLVP_LEN(tp, i), TLVP_VAL(tp, i));</span><br><span style="color: hsl(120, 100%, 40%);">+           if (rc < 0)</span><br><span style="color: hsl(120, 100%, 40%);">+                        return rc;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return tailroom_before - msgb_tailroom(msg);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Encode a set of decoded TLVs according to a given definition and IE order into a message buffer</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[inout] msg Caller-allocated message buffer with sufficient tailroom</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] def structure defining the valid TLV tags / configurations</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] tp decoded values to be encoded</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] tag_order array of tags determining the IE encoding order</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] tag_order_len length of tag_order</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \returns number of bytes consumed in msg; negative in case of error */</span><br><span style="color: hsl(120, 100%, 40%);">+int tlv_encode_ordered(struct msgb *msg, const struct tlv_definition *def, const struct tlv_parsed *tp,</span><br><span style="color: hsl(120, 100%, 40%);">+                    const uint8_t *tag_order, unsigned int tag_order_len)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    unsigned int tailroom_before = msgb_tailroom(msg);</span><br><span style="color: hsl(120, 100%, 40%);">+    unsigned int i;</span><br><span style="color: hsl(120, 100%, 40%);">+       int rc;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     for (i = 0; i < tag_order_len; i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+              uint8_t tag = tag_order[i];</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+         /* skip entries in the array that aren't used/filled */</span><br><span style="color: hsl(120, 100%, 40%);">+           if (!TLVP_PRESENT(tp, tag))</span><br><span style="color: hsl(120, 100%, 40%);">+                   continue;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           rc = tlv_encode_one(msg, def->def[tag].type, tag, TLVP_LEN(tp, tag), TLVP_VAL(tp, tag));</span><br><span style="color: hsl(120, 100%, 40%);">+           if (rc < 0)</span><br><span style="color: hsl(120, 100%, 40%);">+                        return rc;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+     return tailroom_before - msgb_tailroom(msg);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /*! Parse a single TLV encoded IE</span><br><span>  *  \param[out] o_tag the tag of the IE that was found</span><br><span>  *  \param[out] o_len length of the IE that was found</span><br><span>diff --git a/tests/tlv/tlv_test.c b/tests/tlv/tlv_test.c</span><br><span>index e2065b0..925d762 100644</span><br><span>--- a/tests/tlv/tlv_test.c</span><br><span>+++ b/tests/tlv/tlv_test.c</span><br><span>@@ -1,4 +1,6 @@</span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/msgb.h></span><br><span> #include <osmocom/gsm/tlv.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/gsm/gsm0808.h></span><br><span> </span><br><span> static void check_tlv_parse(uint8_t **data, size_t *data_len,</span><br><span>                        uint8_t exp_tag, size_t exp_len, const uint8_t *exp_val)</span><br><span>@@ -286,12 +288,57 @@</span><br><span>         OSMO_ASSERT(dec3[2].lv[tag].val == &test_data[2 + 3 + 3]);</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static void test_tlv_encoder()</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    const uint8_t enc_ies[] = {</span><br><span style="color: hsl(120, 100%, 40%);">+           0x17, 0x14,     0x06, 0x2b, 0x12, 0x2b, 0x0b, 0x40, 0x2b, 0xb7, 0x05, 0xd0, 0x63, 0x82, 0x95, 0x03, 0x05, 0x40,</span><br><span style="color: hsl(120, 100%, 40%);">+                               0x07, 0x08, 0x43, 0x90,</span><br><span style="color: hsl(120, 100%, 40%);">+               0x2c,           0x04,</span><br><span style="color: hsl(120, 100%, 40%);">+         0x40,           0x42,</span><br><span style="color: hsl(120, 100%, 40%);">+ };</span><br><span style="color: hsl(120, 100%, 40%);">+    const uint8_t ie_order[] = { 0x2c, 0x40, 0x17 };</span><br><span style="color: hsl(120, 100%, 40%);">+      const uint8_t enc_ies_reordered[] = {</span><br><span style="color: hsl(120, 100%, 40%);">+         0x2c,           0x04,</span><br><span style="color: hsl(120, 100%, 40%);">+         0x40,           0x42,</span><br><span style="color: hsl(120, 100%, 40%);">+         0x17, 0x14,     0x06, 0x2b, 0x12, 0x2b, 0x0b, 0x40, 0x2b, 0xb7, 0x05, 0xd0, 0x63, 0x82, 0x95, 0x03, 0x05, 0x40,</span><br><span style="color: hsl(120, 100%, 40%);">+                               0x07, 0x08, 0x43, 0x90,</span><br><span style="color: hsl(120, 100%, 40%);">+       };</span><br><span style="color: hsl(120, 100%, 40%);">+    struct tlv_parsed tp;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct msgb *msg = msgb_alloc(1024, __func__);</span><br><span style="color: hsl(120, 100%, 40%);">+        int rc;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     printf("Testing TLV encoder by decoding + re-encoding binary\n");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ OSMO_ASSERT(msg);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* decode BSSAP IEs specified above */</span><br><span style="color: hsl(120, 100%, 40%);">+        rc = osmo_bssap_tlv_parse(&tp, enc_ies, ARRAY_SIZE(enc_ies));</span><br><span style="color: hsl(120, 100%, 40%);">+     OSMO_ASSERT(rc == 3);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       /* re-encode it */</span><br><span style="color: hsl(120, 100%, 40%);">+    rc = tlv_encode(msg, gsm0808_att_tlvdef(), &tp);</span><br><span style="color: hsl(120, 100%, 40%);">+  OSMO_ASSERT(rc == ARRAY_SIZE(enc_ies));</span><br><span style="color: hsl(120, 100%, 40%);">+       OSMO_ASSERT(!memcmp(msgb_data(msg), enc_ies, ARRAY_SIZE(enc_ies)));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ msgb_reset(msg);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    printf("Testing TLV encoder with IE ordering\n");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* re-encodei in different order */</span><br><span style="color: hsl(120, 100%, 40%);">+   rc = tlv_encode_ordered(msg, gsm0808_att_tlvdef(), &tp, ie_order, ARRAY_SIZE(ie_order));</span><br><span style="color: hsl(120, 100%, 40%);">+  OSMO_ASSERT(rc == ARRAY_SIZE(enc_ies));</span><br><span style="color: hsl(120, 100%, 40%);">+       OSMO_ASSERT(!memcmp(msgb_data(msg), enc_ies_reordered, ARRAY_SIZE(enc_ies_reordered)));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     msgb_free(msg);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> int main(int argc, char **argv)</span><br><span> {</span><br><span>         //osmo_init_logging2(ctx, &info);</span><br><span> </span><br><span>    test_tlv_shift_functions();</span><br><span>  test_tlv_repeated_ie();</span><br><span style="color: hsl(120, 100%, 40%);">+       test_tlv_encoder();</span><br><span> </span><br><span>      printf("Done.\n");</span><br><span>         return EXIT_SUCCESS;</span><br><span>diff --git a/tests/tlv/tlv_test.ok b/tests/tlv/tlv_test.ok</span><br><span>index de159bf..f3f0fd4 100644</span><br><span>--- a/tests/tlv/tlv_test.ok</span><br><span>+++ b/tests/tlv/tlv_test.ok</span><br><span>@@ -1,2 +1,4 @@</span><br><span> Test shift functions</span><br><span style="color: hsl(120, 100%, 40%);">+Testing TLV encoder by decoding + re-encoding binary</span><br><span style="color: hsl(120, 100%, 40%);">+Testing TLV encoder with IE ordering</span><br><span> Done.</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.osmocom.org/12900">change 12900</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.osmocom.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.osmocom.org/12900"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: libosmocore </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-MessageType: merged </div>
<div style="display:none"> Gerrit-Change-Id: I761a30bf20355a9f80a4a8e0c60b0b0f78515efe </div>
<div style="display:none"> Gerrit-Change-Number: 12900 </div>
<div style="display:none"> Gerrit-PatchSet: 8 </div>
<div style="display:none"> Gerrit-Owner: Harald Welte <laforge@gnumonks.org> </div>
<div style="display:none"> Gerrit-Reviewer: Harald Welte <laforge@gnumonks.org> </div>
<div style="display:none"> Gerrit-Reviewer: Jenkins Builder (1000002) </div>
<div style="display:none"> Gerrit-Reviewer: Pau Espin Pedrol <pespin@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: Vadim Yanitskiy <axilirator@gmail.com> </div>