<p>neels has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.osmocom.org/c/libosmocore/+/26459">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">move tlv.h, tlv_parser.c from libosmogsm to libosmocore<br><br>TLV decoding and encoding has turned out useful outside of GSM, so move<br>it to libosmocore.<br><br>The current specific reason to move it is the implementation of<br>osmo-upf, which depends on libosmocore, but does not need libosmogsm,<br>besides the TLV parser/encoder for PFCP message coding.<br><br>Related: SYS#5599<br>Change-Id: I3cda0143af9eaf90dc73f9deccd651117fdf8845<br>---<br>M TODO-RELEASE<br>M include/Makefile.am<br>A include/osmocom/core/tlv.h<br>M include/osmocom/gsm/tlv.h<br>M src/Makefile.am<br>M src/gsm/Makefile.am<br>R src/tlv_parser.c<br>M tests/Makefile.am<br>M tests/tlv/tlv_test.c<br>9 files changed, 728 insertions(+), 709 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://gerrit.osmocom.org:29418/libosmocore refs/changes/59/26459/1</pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/TODO-RELEASE b/TODO-RELEASE</span><br><span>index 8ccfa49..f8ba440 100644</span><br><span>--- a/TODO-RELEASE</span><br><span>+++ b/TODO-RELEASE</span><br><span>@@ -7,3 +7,4 @@</span><br><span> # If any interfaces have been added since the last public release: c:r:a + 1.</span><br><span> # If any interfaces have been removed or changed since the last public release: c:r:0.</span><br><span> #library what description / commit summary line</span><br><span style="color: hsl(120, 100%, 40%);">+libosmogsm osmocom/gsm/tlv.h gsm/tlv.h and gsm/tlv_parser.c moved to libosmocore</span><br><span>diff --git a/include/Makefile.am b/include/Makefile.am</span><br><span>index e3246cf..948cc70 100644</span><br><span>--- a/include/Makefile.am</span><br><span>+++ b/include/Makefile.am</span><br><span>@@ -62,6 +62,7 @@</span><br><span> osmocom/core/sockaddr_str.h \</span><br><span> osmocom/core/time_cc.h \</span><br><span> osmocom/core/use_count.h \</span><br><span style="color: hsl(120, 100%, 40%);">+ osmocom/core/tlv.h \</span><br><span> osmocom/crypt/auth.h \</span><br><span> osmocom/crypt/gprs_cipher.h \</span><br><span> osmocom/crypt/kdf.h \</span><br><span>diff --git a/include/osmocom/core/tlv.h b/include/osmocom/core/tlv.h</span><br><span>new file mode 100644</span><br><span>index 0000000..7e6dfb5</span><br><span>--- /dev/null</span><br><span>+++ b/include/osmocom/core/tlv.h</span><br><span>@@ -0,0 +1,708 @@</span><br><span style="color: hsl(120, 100%, 40%);">+#pragma once</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <stdint.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <string.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/msgb.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/byteswap.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/bit16gen.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/bit32gen.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \defgroup tlv TLV parser</span><br><span style="color: hsl(120, 100%, 40%);">+ * @{</span><br><span style="color: hsl(120, 100%, 40%);">+ * \file tlv.h */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* Terminology / wording</span><br><span style="color: hsl(120, 100%, 40%);">+ tag length value (in bits)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ V - - 8</span><br><span style="color: hsl(120, 100%, 40%);">+ LV - 8 N * 8</span><br><span style="color: hsl(120, 100%, 40%);">+ TLV 8 8 N * 8</span><br><span style="color: hsl(120, 100%, 40%);">+ TL16V 8 16 N * 8</span><br><span style="color: hsl(120, 100%, 40%);">+ TLV16 8 8 N * 16</span><br><span style="color: hsl(120, 100%, 40%);">+ TvLV 8 8/16 N * 8</span><br><span style="color: hsl(120, 100%, 40%);">+ vTvLV 8/16 8/16 N * 8</span><br><span style="color: hsl(120, 100%, 40%);">+ T16LV 16 8 N * 8</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%);">+/*! gross length of a LV type field */</span><br><span style="color: hsl(120, 100%, 40%);">+#define LV_GROSS_LEN(x) (x+1)</span><br><span style="color: hsl(120, 100%, 40%);">+/*! gross length of a TLV type field */</span><br><span style="color: hsl(120, 100%, 40%);">+#define TLV_GROSS_LEN(x) (x+2)</span><br><span style="color: hsl(120, 100%, 40%);">+/*! gross length of a TLV16 type field */</span><br><span style="color: hsl(120, 100%, 40%);">+#define TLV16_GROSS_LEN(x) ((2*x)+2)</span><br><span style="color: hsl(120, 100%, 40%);">+/*! gross length of a TL16V type field */</span><br><span style="color: hsl(120, 100%, 40%);">+#define TL16V_GROSS_LEN(x) (x+3)</span><br><span style="color: hsl(120, 100%, 40%);">+/*! gross length of a L16TV type field */</span><br><span style="color: hsl(120, 100%, 40%);">+#define L16TV_GROSS_LEN(x) (x+3)</span><br><span style="color: hsl(120, 100%, 40%);">+/*! gross length of a T16LV type field */</span><br><span style="color: hsl(120, 100%, 40%);">+#define T16LV_GROSS_LEN(x) (x+3)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! maximum length of TLV of one byte length */</span><br><span style="color: hsl(120, 100%, 40%);">+#define TVLV_MAX_ONEBYTE 0x7f</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! error return codes of various TLV parser functions */</span><br><span style="color: hsl(120, 100%, 40%);">+enum osmo_tlv_parser_error {</span><br><span style="color: hsl(120, 100%, 40%);">+ OSMO_TLVP_ERR_OFS_BEYOND_BUFFER = -1,</span><br><span style="color: hsl(120, 100%, 40%);">+ OSMO_TLVP_ERR_OFS_LEN_BEYOND_BUFFER = -2,</span><br><span style="color: hsl(120, 100%, 40%);">+ OSMO_TLVP_ERR_UNKNOWN_TLV_TYPE = -3,</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ OSMO_TLVP_ERR_MAND_IE_MISSING = -50,</span><br><span style="color: hsl(120, 100%, 40%);">+ OSMO_TLVP_ERR_IE_TOO_SHORT = -51,</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%);">+/*! gross length of a TVLV type field */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint16_t TVLV_GROSS_LEN(uint16_t len)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ if (len <= TVLV_MAX_ONEBYTE)</span><br><span style="color: hsl(120, 100%, 40%);">+ return TLV_GROSS_LEN(len);</span><br><span style="color: hsl(120, 100%, 40%);">+ else</span><br><span style="color: hsl(120, 100%, 40%);">+ return TL16V_GROSS_LEN(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%);">+/*! gross length of vTvL header (tag+len) */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint16_t VTVL_GAN_GROSS_LEN(uint16_t tag, uint16_t len)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ uint16_t ret = 2;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (tag > TVLV_MAX_ONEBYTE)</span><br><span style="color: hsl(120, 100%, 40%);">+ ret++;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (len > TVLV_MAX_ONEBYTE)</span><br><span style="color: hsl(120, 100%, 40%);">+ ret++;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return ret;</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%);">+/*! gross length of vTvLV (tag+len+val) */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint16_t VTVLV_GAN_GROSS_LEN(uint16_t tag, uint16_t len)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ uint16_t ret;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (len <= TVLV_MAX_ONEBYTE)</span><br><span style="color: hsl(120, 100%, 40%);">+ ret = TLV_GROSS_LEN(len);</span><br><span style="color: hsl(120, 100%, 40%);">+ else</span><br><span style="color: hsl(120, 100%, 40%);">+ ret = TL16V_GROSS_LEN(len);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (tag > TVLV_MAX_ONEBYTE)</span><br><span style="color: hsl(120, 100%, 40%);">+ ret += 1;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return ret;</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%);">+/* TLV generation */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! put (append) a LV field */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *lv_put(uint8_t *buf, uint8_t len,</span><br><span style="color: hsl(120, 100%, 40%);">+ const uint8_t *val)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ *buf++ = len;</span><br><span style="color: hsl(120, 100%, 40%);">+ memcpy(buf, val, len);</span><br><span style="color: hsl(120, 100%, 40%);">+ return buf + 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%);">+/*! Append a TLV field, a Tag-Length-Value field.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[out] buf Location in a buffer to append TLV at.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] tag Tag id to write.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] len Length field to write and amount of bytes to append.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] val Pointer to data to append, or NULL to append zero data.</span><br><span style="color: hsl(120, 100%, 40%);">+ * Always append tag and length. Append \a len bytes read from \a val. If val is NULL, append \a len zero</span><br><span style="color: hsl(120, 100%, 40%);">+ * bytes instead. If \a len is zero, do not append any data apart from tag and length. */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *tlv_put(uint8_t *buf, uint8_t tag, uint8_t len,</span><br><span style="color: hsl(120, 100%, 40%);">+ const uint8_t *val)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ *buf++ = tag;</span><br><span style="color: hsl(120, 100%, 40%);">+ *buf++ = len;</span><br><span style="color: hsl(120, 100%, 40%);">+ if (len) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (val)</span><br><span style="color: hsl(120, 100%, 40%);">+ memcpy(buf, val, len);</span><br><span style="color: hsl(120, 100%, 40%);">+ else</span><br><span style="color: hsl(120, 100%, 40%);">+ memset(buf, 0, len);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ return buf + 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%);">+/*! put (append) a TL field (a TLV field but omitting the value part). */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *tl_put(uint8_t *buf, uint8_t tag, uint8_t len)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ *buf++ = tag;</span><br><span style="color: hsl(120, 100%, 40%);">+ *buf++ = len;</span><br><span style="color: hsl(120, 100%, 40%);">+ return buf;</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%);">+/*! put (append) a TLV16 field */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *tlv16_put(uint8_t *buf, uint8_t tag, uint8_t len,</span><br><span style="color: hsl(120, 100%, 40%);">+ const uint16_t *val)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ *buf++ = tag;</span><br><span style="color: hsl(120, 100%, 40%);">+ *buf++ = len;</span><br><span style="color: hsl(120, 100%, 40%);">+ memcpy(buf, val, len*2);</span><br><span style="color: hsl(120, 100%, 40%);">+ return buf + len*2;</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%);">+/*! put (append) a TL16V field */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *tl16v_put(uint8_t *buf, uint8_t tag, uint16_t len,</span><br><span style="color: hsl(120, 100%, 40%);">+ const uint8_t *val)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ *buf++ = tag;</span><br><span style="color: hsl(120, 100%, 40%);">+ *buf++ = len >> 8;</span><br><span style="color: hsl(120, 100%, 40%);">+ *buf++ = len & 0xff;</span><br><span style="color: hsl(120, 100%, 40%);">+ memcpy(buf, val, len);</span><br><span style="color: hsl(120, 100%, 40%);">+ return buf + 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%);">+/*! put (append) a TL16 field. */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *tl16_put(uint8_t *buf, uint8_t tag, uint16_t len)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ *buf++ = tag;</span><br><span style="color: hsl(120, 100%, 40%);">+ *buf++ = len >> 8;</span><br><span style="color: hsl(120, 100%, 40%);">+ *buf++ = len & 0xff;</span><br><span style="color: hsl(120, 100%, 40%);">+ return buf;</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%);">+/*! put (append) a TL16V field */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *t16lv_put(uint8_t *buf, uint16_t tag, uint8_t len,</span><br><span style="color: hsl(120, 100%, 40%);">+ const uint8_t *val)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ *buf++ = tag >> 8;</span><br><span style="color: hsl(120, 100%, 40%);">+ *buf++ = tag & 0xff;</span><br><span style="color: hsl(120, 100%, 40%);">+ *buf++ = len;</span><br><span style="color: hsl(120, 100%, 40%);">+ memcpy(buf, val, len);</span><br><span style="color: hsl(120, 100%, 40%);">+ return buf + 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%);">+/*! put (append) a TvLV field */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *tvlv_put(uint8_t *buf, uint8_t tag, uint16_t len,</span><br><span style="color: hsl(120, 100%, 40%);">+ const uint8_t *val)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ uint8_t *ret;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (len <= TVLV_MAX_ONEBYTE) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ret = tlv_put(buf, tag, len, val);</span><br><span style="color: hsl(120, 100%, 40%);">+ buf[1] |= 0x80;</span><br><span style="color: hsl(120, 100%, 40%);">+ } else</span><br><span style="color: hsl(120, 100%, 40%);">+ ret = tl16v_put(buf, tag, len, val);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return ret;</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%);">+/*! put (append) a TvL field (a TvLV with variable-size length, where the value part's length is already known, but will</span><br><span style="color: hsl(120, 100%, 40%);">+ * be put() later).</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns pointer to the value's start position.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *tvl_put(uint8_t *buf, uint8_t tag, uint16_t len)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ uint8_t *ret;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (len <= TVLV_MAX_ONEBYTE) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ret = tl_put(buf, tag, len);</span><br><span style="color: hsl(120, 100%, 40%);">+ buf[1] |= 0x80;</span><br><span style="color: hsl(120, 100%, 40%);">+ } else</span><br><span style="color: hsl(120, 100%, 40%);">+ ret = tl16_put(buf, tag, len);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return ret;</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%);">+/*! put (append) a variable-length tag or variable-length length * */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *vt_gan_put(uint8_t *buf, uint16_t tag)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ if (tag > TVLV_MAX_ONEBYTE) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* two-byte TAG */</span><br><span style="color: hsl(120, 100%, 40%);">+ *buf++ = 0x80 | (tag >> 8);</span><br><span style="color: hsl(120, 100%, 40%);">+ *buf++ = (tag & 0xff);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else</span><br><span style="color: hsl(120, 100%, 40%);">+ *buf++ = tag;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return buf;</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%);">+/* put (append) vTvL (GAN) field (tag + length)*/</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *vtvl_gan_put(uint8_t *buf, uint16_t tag, uint16_t len)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ uint8_t *ret;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ret = vt_gan_put(buf, tag);</span><br><span style="color: hsl(120, 100%, 40%);">+ return vt_gan_put(ret, 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%);">+/* put (append) vTvLV (GAN) field (tag + length + val) */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *vtvlv_gan_put(uint8_t *buf, uint16_t tag, uint16_t len,</span><br><span style="color: hsl(120, 100%, 40%);">+ const uint8_t *val)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ uint8_t *ret;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ret = vtvl_gan_put(buf, tag, len );</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ memcpy(ret, val, len);</span><br><span style="color: hsl(120, 100%, 40%);">+ ret = buf + len;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return ret;</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%);">+/*! put (append) a TLV16 field to \ref msgb */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *msgb_tlv16_put(struct msgb *msg, uint8_t tag, uint8_t len, const uint16_t *val)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ uint8_t *buf = msgb_put(msg, TLV16_GROSS_LEN(len));</span><br><span style="color: hsl(120, 100%, 40%);">+ return tlv16_put(buf, tag, len, val);</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%);">+/*! put (append) a TL16V field to \ref msgb */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *msgb_tl16v_put(struct msgb *msg, uint8_t tag, uint16_t len,</span><br><span style="color: hsl(120, 100%, 40%);">+ const uint8_t *val)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ uint8_t *buf = msgb_put(msg, TL16V_GROSS_LEN(len));</span><br><span style="color: hsl(120, 100%, 40%);">+ return tl16v_put(buf, tag, len, val);</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%);">+static inline uint8_t *msgb_t16lv_put(struct msgb *msg, uint16_t tag, uint8_t len, const uint8_t *val)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ uint8_t *buf = msgb_put(msg, T16LV_GROSS_LEN(len));</span><br><span style="color: hsl(120, 100%, 40%);">+ return t16lv_put(buf, tag, len, val);</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%);">+/*! put (append) a TvL field to \ref msgb, i.e. a TvLV with variable-size length, where the value's length is already</span><br><span style="color: hsl(120, 100%, 40%);">+ * known, but will be put() later. The value section is not yet reserved, only tag and variable-length are put in the</span><br><span style="color: hsl(120, 100%, 40%);">+ * msgb.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns pointer to the value's start position and end of the msgb.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *msgb_tvl_put(struct msgb *msg, uint8_t tag, uint16_t len)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ uint8_t *buf = msgb_put(msg, TVLV_GROSS_LEN(len));</span><br><span style="color: hsl(120, 100%, 40%);">+ return tvl_put(buf, tag, 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%);">+/*! put (append) a TvLV field to \ref msgb */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *msgb_tvlv_put(struct msgb *msg, uint8_t tag, uint16_t len,</span><br><span style="color: hsl(120, 100%, 40%);">+ const uint8_t *val)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ uint8_t *buf = msgb_put(msg, TVLV_GROSS_LEN(len));</span><br><span style="color: hsl(120, 100%, 40%);">+ return tvlv_put(buf, tag, len, val);</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%);">+/*! put (append) a TvLV field containing a big-endian 16bit value to msgb. */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *msgb_tvlv_put_16be(struct msgb *msg, uint8_t tag, uint16_t val)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ uint16_t val_be = osmo_htons(val);</span><br><span style="color: hsl(120, 100%, 40%);">+ return msgb_tvlv_put(msg, tag, 2, (const uint8_t *)&val_be);</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%);">+/*! put (append) a TvLV field containing a big-endian 16bit value to msgb. */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *msgb_tvlv_put_32be(struct msgb *msg, uint8_t tag, uint32_t val)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ uint32_t val_be = osmo_htonl(val);</span><br><span style="color: hsl(120, 100%, 40%);">+ return msgb_tvlv_put(msg, tag, 4, (const uint8_t *)&val_be);</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%);">+/*! put (append) a vTvLV field to \ref msgb */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *msgb_vtvlv_gan_put(struct msgb *msg, uint16_t tag,</span><br><span style="color: hsl(120, 100%, 40%);">+ uint16_t len, const uint8_t *val)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ uint8_t *buf = msgb_put(msg, VTVLV_GAN_GROSS_LEN(tag, len));</span><br><span style="color: hsl(120, 100%, 40%);">+ return vtvlv_gan_put(buf, tag, len, val);</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%);">+/*! put (append) a L16TV field to \ref msgb */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *msgb_l16tv_put(struct msgb *msg, uint16_t len, uint8_t tag,</span><br><span style="color: hsl(120, 100%, 40%);">+ const uint8_t *val)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ uint8_t *buf = msgb_put(msg, L16TV_GROSS_LEN(len));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ *buf++ = len >> 8;</span><br><span style="color: hsl(120, 100%, 40%);">+ *buf++ = len & 0xff;</span><br><span style="color: hsl(120, 100%, 40%);">+ *buf++ = tag;</span><br><span style="color: hsl(120, 100%, 40%);">+ memcpy(buf, val, len);</span><br><span style="color: hsl(120, 100%, 40%);">+ return buf + 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%);">+/*! put (append) a V field */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *v_put(uint8_t *buf, uint8_t val)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ *buf++ = val;</span><br><span style="color: hsl(120, 100%, 40%);">+ return buf;</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%);">+/*! put (append) a TV field */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *tv_put(uint8_t *buf, uint8_t tag, </span><br><span style="color: hsl(120, 100%, 40%);">+ uint8_t val)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ *buf++ = tag;</span><br><span style="color: hsl(120, 100%, 40%);">+ *buf++ = val;</span><br><span style="color: hsl(120, 100%, 40%);">+ return buf;</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%);">+/*! put (append) a TVfixed field */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *tv_fixed_put(uint8_t *buf, 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%);">+ *buf++ = tag;</span><br><span style="color: hsl(120, 100%, 40%);">+ memcpy(buf, val, len);</span><br><span style="color: hsl(120, 100%, 40%);">+ return buf + 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%);">+/*! put (append) a TV16 field</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in,out] buf data buffer</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] tag Tag value</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] val Value (in host byte order!)</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *tv16_put(uint8_t *buf, uint8_t tag, </span><br><span style="color: hsl(120, 100%, 40%);">+ uint16_t val)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ *buf++ = tag;</span><br><span style="color: hsl(120, 100%, 40%);">+ *buf++ = val >> 8;</span><br><span style="color: hsl(120, 100%, 40%);">+ *buf++ = val & 0xff;</span><br><span style="color: hsl(120, 100%, 40%);">+ return buf;</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%);">+/*! put (append) a LV field to a \ref msgb</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns pointer to first byte after newly-put information */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *msgb_lv_put(struct msgb *msg, uint8_t len, const uint8_t *val)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ uint8_t *buf = msgb_put(msg, LV_GROSS_LEN(len));</span><br><span style="color: hsl(120, 100%, 40%);">+ return lv_put(buf, len, val);</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%);">+/*! put (append) a TLV field to a \ref msgb</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns pointer to first byte after newly-put information */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *msgb_tlv_put(struct msgb *msg, uint8_t tag, uint8_t len, const uint8_t *val)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ uint8_t *buf = msgb_put(msg, TLV_GROSS_LEN(len));</span><br><span style="color: hsl(120, 100%, 40%);">+ return tlv_put(buf, tag, len, val);</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%);">+/*! put (append) a TV field to a \ref msgb</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns pointer to first byte after newly-put information */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *msgb_tv_put(struct msgb *msg, uint8_t tag, uint8_t val)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ uint8_t *buf = msgb_put(msg, 2);</span><br><span style="color: hsl(120, 100%, 40%);">+ return tv_put(buf, tag, val);</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%);">+/*! put (append) a TVfixed field to a \ref msgb</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns pointer to first byte after newly-put information */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *msgb_tv_fixed_put(struct msgb *msg, 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%);">+ uint8_t *buf = msgb_put(msg, 1+len);</span><br><span style="color: hsl(120, 100%, 40%);">+ return tv_fixed_put(buf, tag, len, val);</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%);">+/*! put (append) a V field to a \ref msgb</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns pointer to first byte after newly-put information */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *msgb_v_put(struct msgb *msg, uint8_t val)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ uint8_t *buf = msgb_put(msg, 1);</span><br><span style="color: hsl(120, 100%, 40%);">+ return v_put(buf, val);</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%);">+/*! put (append) a TL fields to a \ref msgb</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns pointer to the length field so it can be updated after adding new information under specified tag */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *msgb_tl_put(struct msgb *msg, uint8_t tag)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ uint8_t *len = msgb_v_put(msg, tag);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* reserve space for length, len points to this reserved space already */</span><br><span style="color: hsl(120, 100%, 40%);">+ msgb_v_put(msg, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return 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%);">+/*! put (append) a TV16 field (network order) to the given msgb</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns pointer to first byte after newly-put information */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *msgb_tv16_put(struct msgb *msg, uint8_t tag, uint16_t val)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ uint8_t *buf = msgb_put(msg, 3);</span><br><span style="color: hsl(120, 100%, 40%);">+ return tv16_put(buf, tag, val);</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%);">+/*! put (append) a TV32 field (network order) to the given msgb</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns pointer to first byte after newly-put information */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *msgb_tv32_put(struct msgb *msg, uint8_t tag, uint32_t val)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ uint8_t *buf = msgb_put(msg, 1 + 4);</span><br><span style="color: hsl(120, 100%, 40%);">+ *buf++ = tag;</span><br><span style="color: hsl(120, 100%, 40%);">+ osmo_store32be(val, buf);</span><br><span style="color: hsl(120, 100%, 40%);">+ return msg->tail;</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%);">+/*! push (prepend) a TLV field to a \ref msgb</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns pointer to first byte of newly-pushed information */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *msgb_tlv_push(struct msgb *msg, uint8_t tag, uint8_t len, const uint8_t *val)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ uint8_t *buf = msgb_push(msg, TLV_GROSS_LEN(len));</span><br><span style="color: hsl(120, 100%, 40%);">+ tlv_put(buf, tag, len, val);</span><br><span style="color: hsl(120, 100%, 40%);">+ return buf;</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%);">+/*! push 1-byte tagged value */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *msgb_tlv1_push(struct msgb *msg, uint8_t tag, uint8_t val)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ return msgb_tlv_push(msg, tag, 1, &val);</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%);">+/*! push (prepend) a TV field to a \ref msgb</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns pointer to first byte of newly-pushed information */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *msgb_tv_push(struct msgb *msg, uint8_t tag, uint8_t val)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ uint8_t *buf = msgb_push(msg, 2);</span><br><span style="color: hsl(120, 100%, 40%);">+ tv_put(buf, tag, val);</span><br><span style="color: hsl(120, 100%, 40%);">+ return buf;</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%);">+/*! push (prepend) a TV16 field to a \ref msgb</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns pointer to first byte of newly-pushed information */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *msgb_tv16_push(struct msgb *msg, uint8_t tag, uint16_t val)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ uint8_t *buf = msgb_push(msg, 3);</span><br><span style="color: hsl(120, 100%, 40%);">+ tv16_put(buf, tag, val);</span><br><span style="color: hsl(120, 100%, 40%);">+ return buf;</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%);">+/*! push (prepend) a TvLV field to a \ref msgb</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns pointer to first byte of newly-pushed information */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *msgb_tvlv_push(struct msgb *msg, uint8_t tag, uint16_t len,</span><br><span style="color: hsl(120, 100%, 40%);">+ const uint8_t *val)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ uint8_t *buf = msgb_push(msg, TVLV_GROSS_LEN(len));</span><br><span style="color: hsl(120, 100%, 40%);">+ tvlv_put(buf, tag, len, val);</span><br><span style="color: hsl(120, 100%, 40%);">+ return buf;</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%);">+/* push (prepend) a vTvL header to a \ref msgb</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *msgb_vtvl_gan_push(struct msgb *msg, uint16_t tag,</span><br><span style="color: hsl(120, 100%, 40%);">+ uint16_t len)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ uint8_t *buf = msgb_push(msg, VTVL_GAN_GROSS_LEN(tag, len));</span><br><span style="color: hsl(120, 100%, 40%);">+ vtvl_gan_put(buf, tag, len);</span><br><span style="color: hsl(120, 100%, 40%);">+ return buf;</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%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *msgb_vtvlv_gan_push(struct msgb *msg, uint16_t tag,</span><br><span style="color: hsl(120, 100%, 40%);">+ uint16_t len, const uint8_t *val)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ uint8_t *buf = msgb_push(msg, VTVLV_GAN_GROSS_LEN(tag, len));</span><br><span style="color: hsl(120, 100%, 40%);">+ vtvlv_gan_put(buf, tag, len, val);</span><br><span style="color: hsl(120, 100%, 40%);">+ return buf;</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%);">+/* TLV parsing */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Entry in a TLV parser array */</span><br><span style="color: hsl(120, 100%, 40%);">+struct tlv_p_entry {</span><br><span style="color: hsl(120, 100%, 40%);">+ uint16_t len; /*!< length */</span><br><span style="color: hsl(120, 100%, 40%);">+ const uint8_t *val; /*!< pointer to value */</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%);">+/*! TLV type */</span><br><span style="color: hsl(120, 100%, 40%);">+enum tlv_type {</span><br><span style="color: hsl(120, 100%, 40%);">+ TLV_TYPE_NONE, /*!< no type */</span><br><span style="color: hsl(120, 100%, 40%);">+ TLV_TYPE_FIXED, /*!< fixed-length value-only */</span><br><span style="color: hsl(120, 100%, 40%);">+ TLV_TYPE_T, /*!< tag-only */</span><br><span style="color: hsl(120, 100%, 40%);">+ TLV_TYPE_TV, /*!< tag-value (8bit) */</span><br><span style="color: hsl(120, 100%, 40%);">+ TLV_TYPE_TLV, /*!< tag-length-value */</span><br><span style="color: hsl(120, 100%, 40%);">+ TLV_TYPE_TL16V, /*!< tag, 16 bit length, value */</span><br><span style="color: hsl(120, 100%, 40%);">+ TLV_TYPE_TvLV, /*!< tag, variable length, value */</span><br><span style="color: hsl(120, 100%, 40%);">+ TLV_TYPE_SINGLE_TV, /*!< tag and value (both 4 bit) in 1 byte */</span><br><span style="color: hsl(120, 100%, 40%);">+ TLV_TYPE_vTvLV_GAN, /*!< variable-length tag, variable-length length */</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%);">+/*! Definition of a single IE (Information Element) */</span><br><span style="color: hsl(120, 100%, 40%);">+struct tlv_def {</span><br><span style="color: hsl(120, 100%, 40%);">+ enum tlv_type type; /*!< TLV type */</span><br><span style="color: hsl(120, 100%, 40%);">+ uint8_t fixed_len; /*!< length in case of \ref TLV_TYPE_FIXED */</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%);">+/*! Definition of All 256 IE / TLV */</span><br><span style="color: hsl(120, 100%, 40%);">+struct tlv_definition {</span><br><span style="color: hsl(120, 100%, 40%);">+ struct tlv_def def[256];</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%);">+/*! result of the TLV parser */</span><br><span style="color: hsl(120, 100%, 40%);">+struct tlv_parsed {</span><br><span style="color: hsl(120, 100%, 40%);">+ struct tlv_p_entry lv[256];</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%);">+extern struct tlv_definition tvlv_att_def;</span><br><span style="color: hsl(120, 100%, 40%);">+extern struct tlv_definition vtvlv_gan_att_def;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int tlv_parse_one(uint8_t *o_tag, uint16_t *o_len, const uint8_t **o_val,</span><br><span style="color: hsl(120, 100%, 40%);">+ const struct tlv_definition *def,</span><br><span style="color: hsl(120, 100%, 40%);">+ const uint8_t *buf, int buf_len);</span><br><span style="color: hsl(120, 100%, 40%);">+int tlv_parse(struct tlv_parsed *dec, const struct tlv_definition *def,</span><br><span style="color: hsl(120, 100%, 40%);">+ const uint8_t *buf, int buf_len, uint8_t lv_tag, uint8_t lv_tag2);</span><br><span style="color: hsl(120, 100%, 40%);">+int tlv_parse2(struct tlv_parsed *dec, int dec_multiples,</span><br><span style="color: hsl(120, 100%, 40%);">+ const struct tlv_definition *def, const uint8_t *buf, int buf_len,</span><br><span style="color: hsl(120, 100%, 40%);">+ uint8_t lv_tag, uint8_t lv_tag2);</span><br><span style="color: hsl(120, 100%, 40%);">+/* take a master (src) tlv def and fill up all empty slots in 'dst' */</span><br><span style="color: hsl(120, 100%, 40%);">+void tlv_def_patch(struct tlv_definition *dst, const struct tlv_definition *src);</span><br><span style="color: hsl(120, 100%, 40%);">+</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 style="color: hsl(120, 100%, 40%);">+#define TLVP_PRESENT(x, y) ((x)->lv[y].val)</span><br><span style="color: hsl(120, 100%, 40%);">+#define TLVP_LEN(x, y) (x)->lv[y].len</span><br><span style="color: hsl(120, 100%, 40%);">+#define TLVP_VAL(x, y) (x)->lv[y].val</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define TLVP_PRES_LEN(tp, tag, min_len) \</span><br><span style="color: hsl(120, 100%, 40%);">+ (TLVP_PRESENT(tp, tag) && TLVP_LEN(tp, tag) >= min_len)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Return pointer to a TLV element if it is present.</span><br><span style="color: hsl(120, 100%, 40%);">+ * Usage:</span><br><span style="color: hsl(120, 100%, 40%);">+ * struct tlv_p_entry *e = TVLP_GET(&tp, TAG);</span><br><span style="color: hsl(120, 100%, 40%);">+ * if (!e)</span><br><span style="color: hsl(120, 100%, 40%);">+ * return -ENOENT;</span><br><span style="color: hsl(120, 100%, 40%);">+ * hexdump(e->val, e->len);</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] _tp pointer to \ref tlv_parsed.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] tag IE tag to return.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns struct tlv_p_entry pointer, or NULL if not present.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+#define TLVP_GET(_tp, tag) (TLVP_PRESENT(_tp, tag)? &(_tp)->lv[tag] : NULL)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Like TLVP_GET(), but enforcing a minimum val length.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] _tp pointer to \ref tlv_parsed.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] tag IE tag to return.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] min_len Minimum value length in bytes.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns struct tlv_p_entry pointer, or NULL if not present or too short.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+#define TLVP_GET_MINLEN(_tp, tag, min_len) \</span><br><span style="color: hsl(120, 100%, 40%);">+ (TLVP_PRES_LEN(_tp, tag, min_len)? &(_tp)->lv[tag] : NULL)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Like TLVP_VAL(), but enforcing a minimum val length.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] _tp pointer to \ref tlv_parsed.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] tag IE tag to return.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] min_len Minimum value length in bytes.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns const uint8_t pointer to value, or NULL if not present or too short.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+#define TLVP_VAL_MINLEN(_tp, tag, min_len) \</span><br><span style="color: hsl(120, 100%, 40%);">+ (TLVP_PRES_LEN(_tp, tag, min_len)? (_tp)->lv[tag].val : NULL)</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%);">+/*! Obtain 1-byte TLV element.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] tp pointer to \ref tlv_parsed</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] tag the Tag to look for</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] default_val default value to use if tag not available</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns the 1st byte of value with a given tag or default_val if tag was not found</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t tlvp_val8(const struct tlv_parsed *tp, uint8_t tag, uint8_t default_val)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ const uint8_t *res = TLVP_VAL_MINLEN(tp, tag, 1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (res)</span><br><span style="color: hsl(120, 100%, 40%);">+ return res[0];</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return default_val;</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%);">+/*! Align given TLV element with 16 bit value to an even address</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] tp pointer to \ref tlv_parsed</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] pos element to return</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns aligned 16 bit value</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint16_t tlvp_val16_unal(const struct tlv_parsed *tp, int pos)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ uint16_t res;</span><br><span style="color: hsl(120, 100%, 40%);">+ memcpy(&res, TLVP_VAL(tp, pos), sizeof(res));</span><br><span style="color: hsl(120, 100%, 40%);">+ return res;</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%);">+/*! Align given TLV element with 32 bit value to an address that is a multiple of 4</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] tp pointer to \ref tlv_parsed</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] pos element to return</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns aligned 32 bit value</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint32_t tlvp_val32_unal(const struct tlv_parsed *tp, int pos)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ uint32_t res;</span><br><span style="color: hsl(120, 100%, 40%);">+ memcpy(&res, TLVP_VAL(tp, pos), sizeof(res));</span><br><span style="color: hsl(120, 100%, 40%);">+ return res;</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%);">+/*! Retrieve (possibly unaligned) TLV element and convert to host byte order</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] tp pointer to \ref tlv_parsed</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] pos element to return</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns aligned 16 bit value in host byte order</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint16_t tlvp_val16be(const struct tlv_parsed *tp, int pos)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ return osmo_load16be(TLVP_VAL(tp, pos));</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%);">+/*! Retrieve (possibly unaligned) TLV element and convert to host byte order</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] tp pointer to \ref tlv_parsed</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] pos element to return</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns aligned 32 bit value in host byte order</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint32_t tlvp_val32be(const struct tlv_parsed *tp, int pos)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ return osmo_load32be(TLVP_VAL(tp, pos));</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%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct tlv_parsed *osmo_tlvp_copy(const struct tlv_parsed *tp_orig, void *ctx);</span><br><span style="color: hsl(120, 100%, 40%);">+int osmo_tlvp_merge(struct tlv_parsed *dst, const struct tlv_parsed *src);</span><br><span style="color: hsl(120, 100%, 40%);">+int osmo_shift_v_fixed(uint8_t **data, size_t *data_len,</span><br><span style="color: hsl(120, 100%, 40%);">+ size_t len, uint8_t **value);</span><br><span style="color: hsl(120, 100%, 40%);">+int osmo_match_shift_tv_fixed(uint8_t **data, size_t *data_len,</span><br><span style="color: hsl(120, 100%, 40%);">+ uint8_t tag, size_t len, uint8_t **value);</span><br><span style="color: hsl(120, 100%, 40%);">+int osmo_shift_tlv(uint8_t **data, size_t *data_len,</span><br><span style="color: hsl(120, 100%, 40%);">+ uint8_t *tag, uint8_t **value, size_t *value_len);</span><br><span style="color: hsl(120, 100%, 40%);">+int osmo_match_shift_tlv(uint8_t **data, size_t *data_len,</span><br><span style="color: hsl(120, 100%, 40%);">+ uint8_t tag, uint8_t **value, size_t *value_len);</span><br><span style="color: hsl(120, 100%, 40%);">+int osmo_shift_lv(uint8_t **data, size_t *data_len,</span><br><span style="color: hsl(120, 100%, 40%);">+ uint8_t **value, size_t *value_len);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define MSG_DEF(name, mand_ies, flags) { name, mand_ies, ARRAY_SIZE(mand_ies), flags }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct osmo_tlv_prot_msg_def {</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! human-readable name of message type (optional) */</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *name;</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! array of mandatory IEs */</span><br><span style="color: hsl(120, 100%, 40%);">+ const uint8_t *mand_ies;</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! number of entries in 'mand_ies' above */</span><br><span style="color: hsl(120, 100%, 40%);">+ uint8_t mand_count;</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! user-defined flags (like uplink/downlink/...) */</span><br><span style="color: hsl(120, 100%, 40%);">+ uint32_t flags;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+struct osmo_tlv_prot_ie_def {</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! minimum length of IE value part, in octets */</span><br><span style="color: hsl(120, 100%, 40%);">+ uint16_t min_len;</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! huamn-readable name (optional) */</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *name;</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%);">+/*! Osmocom TLV protocol definition */</span><br><span style="color: hsl(120, 100%, 40%);">+struct osmo_tlv_prot_def {</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! human-readable name of protocol */</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *name;</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! TLV parser definition (optional) */</span><br><span style="color: hsl(120, 100%, 40%);">+ const struct tlv_definition *tlv_def;</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! definition of each message (8-bit message type) */</span><br><span style="color: hsl(120, 100%, 40%);">+ struct osmo_tlv_prot_msg_def msg_def[256];</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! definition of IE for each 8-bit tag */</span><br><span style="color: hsl(120, 100%, 40%);">+ struct osmo_tlv_prot_ie_def ie_def[256];</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! value_string array of message type names (legacy, if not populated in msg_def) */</span><br><span style="color: hsl(120, 100%, 40%);">+ const struct value_string *msgt_names;</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%);">+const char *osmo_tlv_prot_msg_name(const struct osmo_tlv_prot_def *pdef, uint8_t msg_type);</span><br><span style="color: hsl(120, 100%, 40%);">+const char *osmo_tlv_prot_ie_name(const struct osmo_tlv_prot_def *pdef, uint8_t iei);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int osmo_tlv_prot_validate_tp(const struct osmo_tlv_prot_def *pdef, uint8_t msg_type,</span><br><span style="color: hsl(120, 100%, 40%);">+ const struct tlv_parsed *tp, int log_subsys, const char *log_pfx);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int osmo_tlv_prot_parse(const struct osmo_tlv_prot_def *pdef,</span><br><span style="color: hsl(120, 100%, 40%);">+ struct tlv_parsed *dec, unsigned int dec_multiples, uint8_t msg_type,</span><br><span style="color: hsl(120, 100%, 40%);">+ const uint8_t *buf, unsigned int buf_len, uint8_t lv_tag, uint8_t lv_tag2,</span><br><span style="color: hsl(120, 100%, 40%);">+ int log_subsys, const char *log_pfx);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint32_t osmo_tlv_prot_msgt_flags(const struct osmo_tlv_prot_def *pdef, uint8_t msg_type)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ return pdef->msg_def[msg_type].flags;</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%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! @} */</span><br><span>diff --git a/include/osmocom/gsm/tlv.h b/include/osmocom/gsm/tlv.h</span><br><span>index 7e6dfb5..05a7261 100644</span><br><span>--- a/include/osmocom/gsm/tlv.h</span><br><span>+++ b/include/osmocom/gsm/tlv.h</span><br><span>@@ -1,708 +1,8 @@</span><br><span> #pragma once</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-#include <stdint.h></span><br><span style="color: hsl(0, 100%, 40%);">-#include <string.h></span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-#include <osmocom/core/msgb.h></span><br><span style="color: hsl(0, 100%, 40%);">-#include <osmocom/core/byteswap.h></span><br><span style="color: hsl(0, 100%, 40%);">-#include <osmocom/core/bit16gen.h></span><br><span style="color: hsl(0, 100%, 40%);">-#include <osmocom/core/bit32gen.h></span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! \defgroup tlv TLV parser</span><br><span style="color: hsl(0, 100%, 40%);">- * @{</span><br><span style="color: hsl(0, 100%, 40%);">- * \file tlv.h */</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/* Terminology / wording</span><br><span style="color: hsl(0, 100%, 40%);">- tag length value (in bits)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- V - - 8</span><br><span style="color: hsl(0, 100%, 40%);">- LV - 8 N * 8</span><br><span style="color: hsl(0, 100%, 40%);">- TLV 8 8 N * 8</span><br><span style="color: hsl(0, 100%, 40%);">- TL16V 8 16 N * 8</span><br><span style="color: hsl(0, 100%, 40%);">- TLV16 8 8 N * 16</span><br><span style="color: hsl(0, 100%, 40%);">- TvLV 8 8/16 N * 8</span><br><span style="color: hsl(0, 100%, 40%);">- vTvLV 8/16 8/16 N * 8</span><br><span style="color: hsl(0, 100%, 40%);">- T16LV 16 8 N * 8</span><br><span style="color: hsl(0, 100%, 40%);">-*/</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! gross length of a LV type field */</span><br><span style="color: hsl(0, 100%, 40%);">-#define LV_GROSS_LEN(x) (x+1)</span><br><span style="color: hsl(0, 100%, 40%);">-/*! gross length of a TLV type field */</span><br><span style="color: hsl(0, 100%, 40%);">-#define TLV_GROSS_LEN(x) (x+2)</span><br><span style="color: hsl(0, 100%, 40%);">-/*! gross length of a TLV16 type field */</span><br><span style="color: hsl(0, 100%, 40%);">-#define TLV16_GROSS_LEN(x) ((2*x)+2)</span><br><span style="color: hsl(0, 100%, 40%);">-/*! gross length of a TL16V type field */</span><br><span style="color: hsl(0, 100%, 40%);">-#define TL16V_GROSS_LEN(x) (x+3)</span><br><span style="color: hsl(0, 100%, 40%);">-/*! gross length of a L16TV type field */</span><br><span style="color: hsl(0, 100%, 40%);">-#define L16TV_GROSS_LEN(x) (x+3)</span><br><span style="color: hsl(0, 100%, 40%);">-/*! gross length of a T16LV type field */</span><br><span style="color: hsl(0, 100%, 40%);">-#define T16LV_GROSS_LEN(x) (x+3)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! maximum length of TLV of one byte length */</span><br><span style="color: hsl(0, 100%, 40%);">-#define TVLV_MAX_ONEBYTE 0x7f</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! error return codes of various TLV parser functions */</span><br><span style="color: hsl(0, 100%, 40%);">-enum osmo_tlv_parser_error {</span><br><span style="color: hsl(0, 100%, 40%);">- OSMO_TLVP_ERR_OFS_BEYOND_BUFFER = -1,</span><br><span style="color: hsl(0, 100%, 40%);">- OSMO_TLVP_ERR_OFS_LEN_BEYOND_BUFFER = -2,</span><br><span style="color: hsl(0, 100%, 40%);">- OSMO_TLVP_ERR_UNKNOWN_TLV_TYPE = -3,</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- OSMO_TLVP_ERR_MAND_IE_MISSING = -50,</span><br><span style="color: hsl(0, 100%, 40%);">- OSMO_TLVP_ERR_IE_TOO_SHORT = -51,</span><br><span style="color: hsl(0, 100%, 40%);">-};</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! gross length of a TVLV type field */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint16_t TVLV_GROSS_LEN(uint16_t len)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">- if (len <= TVLV_MAX_ONEBYTE)</span><br><span style="color: hsl(0, 100%, 40%);">- return TLV_GROSS_LEN(len);</span><br><span style="color: hsl(0, 100%, 40%);">- else</span><br><span style="color: hsl(0, 100%, 40%);">- return TL16V_GROSS_LEN(len);</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! gross length of vTvL header (tag+len) */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint16_t VTVL_GAN_GROSS_LEN(uint16_t tag, uint16_t len)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">- uint16_t ret = 2;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- if (tag > TVLV_MAX_ONEBYTE)</span><br><span style="color: hsl(0, 100%, 40%);">- ret++;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- if (len > TVLV_MAX_ONEBYTE)</span><br><span style="color: hsl(0, 100%, 40%);">- ret++;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- return ret;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! gross length of vTvLV (tag+len+val) */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint16_t VTVLV_GAN_GROSS_LEN(uint16_t tag, uint16_t len)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">- uint16_t ret;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- if (len <= TVLV_MAX_ONEBYTE)</span><br><span style="color: hsl(0, 100%, 40%);">- ret = TLV_GROSS_LEN(len);</span><br><span style="color: hsl(0, 100%, 40%);">- else</span><br><span style="color: hsl(0, 100%, 40%);">- ret = TL16V_GROSS_LEN(len);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- if (tag > TVLV_MAX_ONEBYTE)</span><br><span style="color: hsl(0, 100%, 40%);">- ret += 1;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- return ret;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/* TLV generation */</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! put (append) a LV field */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *lv_put(uint8_t *buf, uint8_t len,</span><br><span style="color: hsl(0, 100%, 40%);">- const uint8_t *val)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">- *buf++ = len;</span><br><span style="color: hsl(0, 100%, 40%);">- memcpy(buf, val, len);</span><br><span style="color: hsl(0, 100%, 40%);">- return buf + len;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! Append a TLV field, a Tag-Length-Value field.</span><br><span style="color: hsl(0, 100%, 40%);">- * \param[out] buf Location in a buffer to append TLV at.</span><br><span style="color: hsl(0, 100%, 40%);">- * \param[in] tag Tag id to write.</span><br><span style="color: hsl(0, 100%, 40%);">- * \param[in] len Length field to write and amount of bytes to append.</span><br><span style="color: hsl(0, 100%, 40%);">- * \param[in] val Pointer to data to append, or NULL to append zero data.</span><br><span style="color: hsl(0, 100%, 40%);">- * Always append tag and length. Append \a len bytes read from \a val. If val is NULL, append \a len zero</span><br><span style="color: hsl(0, 100%, 40%);">- * bytes instead. If \a len is zero, do not append any data apart from tag and length. */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *tlv_put(uint8_t *buf, uint8_t tag, uint8_t len,</span><br><span style="color: hsl(0, 100%, 40%);">- const uint8_t *val)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">- *buf++ = tag;</span><br><span style="color: hsl(0, 100%, 40%);">- *buf++ = len;</span><br><span style="color: hsl(0, 100%, 40%);">- if (len) {</span><br><span style="color: hsl(0, 100%, 40%);">- if (val)</span><br><span style="color: hsl(0, 100%, 40%);">- memcpy(buf, val, len);</span><br><span style="color: hsl(0, 100%, 40%);">- else</span><br><span style="color: hsl(0, 100%, 40%);">- memset(buf, 0, len);</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(0, 100%, 40%);">- return buf + len;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! put (append) a TL field (a TLV field but omitting the value part). */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *tl_put(uint8_t *buf, uint8_t tag, uint8_t len)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">- *buf++ = tag;</span><br><span style="color: hsl(0, 100%, 40%);">- *buf++ = len;</span><br><span style="color: hsl(0, 100%, 40%);">- return buf;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! put (append) a TLV16 field */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *tlv16_put(uint8_t *buf, uint8_t tag, uint8_t len,</span><br><span style="color: hsl(0, 100%, 40%);">- const uint16_t *val)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">- *buf++ = tag;</span><br><span style="color: hsl(0, 100%, 40%);">- *buf++ = len;</span><br><span style="color: hsl(0, 100%, 40%);">- memcpy(buf, val, len*2);</span><br><span style="color: hsl(0, 100%, 40%);">- return buf + len*2;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! put (append) a TL16V field */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *tl16v_put(uint8_t *buf, uint8_t tag, uint16_t len,</span><br><span style="color: hsl(0, 100%, 40%);">- const uint8_t *val)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">- *buf++ = tag;</span><br><span style="color: hsl(0, 100%, 40%);">- *buf++ = len >> 8;</span><br><span style="color: hsl(0, 100%, 40%);">- *buf++ = len & 0xff;</span><br><span style="color: hsl(0, 100%, 40%);">- memcpy(buf, val, len);</span><br><span style="color: hsl(0, 100%, 40%);">- return buf + len;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! put (append) a TL16 field. */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *tl16_put(uint8_t *buf, uint8_t tag, uint16_t len)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">- *buf++ = tag;</span><br><span style="color: hsl(0, 100%, 40%);">- *buf++ = len >> 8;</span><br><span style="color: hsl(0, 100%, 40%);">- *buf++ = len & 0xff;</span><br><span style="color: hsl(0, 100%, 40%);">- return buf;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! put (append) a TL16V field */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *t16lv_put(uint8_t *buf, uint16_t tag, uint8_t len,</span><br><span style="color: hsl(0, 100%, 40%);">- const uint8_t *val)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">- *buf++ = tag >> 8;</span><br><span style="color: hsl(0, 100%, 40%);">- *buf++ = tag & 0xff;</span><br><span style="color: hsl(0, 100%, 40%);">- *buf++ = len;</span><br><span style="color: hsl(0, 100%, 40%);">- memcpy(buf, val, len);</span><br><span style="color: hsl(0, 100%, 40%);">- return buf + len;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! put (append) a TvLV field */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *tvlv_put(uint8_t *buf, uint8_t tag, uint16_t len,</span><br><span style="color: hsl(0, 100%, 40%);">- const uint8_t *val)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">- uint8_t *ret;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- if (len <= TVLV_MAX_ONEBYTE) {</span><br><span style="color: hsl(0, 100%, 40%);">- ret = tlv_put(buf, tag, len, val);</span><br><span style="color: hsl(0, 100%, 40%);">- buf[1] |= 0x80;</span><br><span style="color: hsl(0, 100%, 40%);">- } else</span><br><span style="color: hsl(0, 100%, 40%);">- ret = tl16v_put(buf, tag, len, val);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- return ret;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! put (append) a TvL field (a TvLV with variable-size length, where the value part's length is already known, but will</span><br><span style="color: hsl(0, 100%, 40%);">- * be put() later).</span><br><span style="color: hsl(0, 100%, 40%);">- * \returns pointer to the value's start position.</span><br><span style="color: hsl(120, 100%, 40%);">+/* Up to libosmocore v1.6.0, tlv.h lived in osmocom/gsm/tlv.h.</span><br><span style="color: hsl(120, 100%, 40%);">+ * The file was moved to osmocom/core/tlv.h, because TLV turned out to be useful outside of GSM.</span><br><span style="color: hsl(120, 100%, 40%);">+ * For easy compatibility, this file remains as redirection to core/tlv.h.</span><br><span> */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *tvl_put(uint8_t *buf, uint8_t tag, uint16_t len)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">- uint8_t *ret;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- if (len <= TVLV_MAX_ONEBYTE) {</span><br><span style="color: hsl(0, 100%, 40%);">- ret = tl_put(buf, tag, len);</span><br><span style="color: hsl(0, 100%, 40%);">- buf[1] |= 0x80;</span><br><span style="color: hsl(0, 100%, 40%);">- } else</span><br><span style="color: hsl(0, 100%, 40%);">- ret = tl16_put(buf, tag, len);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- return ret;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! put (append) a variable-length tag or variable-length length * */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *vt_gan_put(uint8_t *buf, uint16_t tag)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">- if (tag > TVLV_MAX_ONEBYTE) {</span><br><span style="color: hsl(0, 100%, 40%);">- /* two-byte TAG */</span><br><span style="color: hsl(0, 100%, 40%);">- *buf++ = 0x80 | (tag >> 8);</span><br><span style="color: hsl(0, 100%, 40%);">- *buf++ = (tag & 0xff);</span><br><span style="color: hsl(0, 100%, 40%);">- } else</span><br><span style="color: hsl(0, 100%, 40%);">- *buf++ = tag;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- return buf;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/* put (append) vTvL (GAN) field (tag + length)*/</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *vtvl_gan_put(uint8_t *buf, uint16_t tag, uint16_t len)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">- uint8_t *ret;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- ret = vt_gan_put(buf, tag);</span><br><span style="color: hsl(0, 100%, 40%);">- return vt_gan_put(ret, len);</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/* put (append) vTvLV (GAN) field (tag + length + val) */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *vtvlv_gan_put(uint8_t *buf, uint16_t tag, uint16_t len,</span><br><span style="color: hsl(0, 100%, 40%);">- const uint8_t *val)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">- uint8_t *ret;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- ret = vtvl_gan_put(buf, tag, len );</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- memcpy(ret, val, len);</span><br><span style="color: hsl(0, 100%, 40%);">- ret = buf + len;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- return ret;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! put (append) a TLV16 field to \ref msgb */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *msgb_tlv16_put(struct msgb *msg, uint8_t tag, uint8_t len, const uint16_t *val)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">- uint8_t *buf = msgb_put(msg, TLV16_GROSS_LEN(len));</span><br><span style="color: hsl(0, 100%, 40%);">- return tlv16_put(buf, tag, len, val);</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! put (append) a TL16V field to \ref msgb */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *msgb_tl16v_put(struct msgb *msg, uint8_t tag, uint16_t len,</span><br><span style="color: hsl(0, 100%, 40%);">- const uint8_t *val)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">- uint8_t *buf = msgb_put(msg, TL16V_GROSS_LEN(len));</span><br><span style="color: hsl(0, 100%, 40%);">- return tl16v_put(buf, tag, len, val);</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *msgb_t16lv_put(struct msgb *msg, uint16_t tag, uint8_t len, const uint8_t *val)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">- uint8_t *buf = msgb_put(msg, T16LV_GROSS_LEN(len));</span><br><span style="color: hsl(0, 100%, 40%);">- return t16lv_put(buf, tag, len, val);</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! put (append) a TvL field to \ref msgb, i.e. a TvLV with variable-size length, where the value's length is already</span><br><span style="color: hsl(0, 100%, 40%);">- * known, but will be put() later. The value section is not yet reserved, only tag and variable-length are put in the</span><br><span style="color: hsl(0, 100%, 40%);">- * msgb.</span><br><span style="color: hsl(0, 100%, 40%);">- * \returns pointer to the value's start position and end of the msgb.</span><br><span style="color: hsl(0, 100%, 40%);">- */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *msgb_tvl_put(struct msgb *msg, uint8_t tag, uint16_t len)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">- uint8_t *buf = msgb_put(msg, TVLV_GROSS_LEN(len));</span><br><span style="color: hsl(0, 100%, 40%);">- return tvl_put(buf, tag, len);</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! put (append) a TvLV field to \ref msgb */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *msgb_tvlv_put(struct msgb *msg, uint8_t tag, uint16_t len,</span><br><span style="color: hsl(0, 100%, 40%);">- const uint8_t *val)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">- uint8_t *buf = msgb_put(msg, TVLV_GROSS_LEN(len));</span><br><span style="color: hsl(0, 100%, 40%);">- return tvlv_put(buf, tag, len, val);</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! put (append) a TvLV field containing a big-endian 16bit value to msgb. */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *msgb_tvlv_put_16be(struct msgb *msg, uint8_t tag, uint16_t val)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">- uint16_t val_be = osmo_htons(val);</span><br><span style="color: hsl(0, 100%, 40%);">- return msgb_tvlv_put(msg, tag, 2, (const uint8_t *)&val_be);</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! put (append) a TvLV field containing a big-endian 16bit value to msgb. */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *msgb_tvlv_put_32be(struct msgb *msg, uint8_t tag, uint32_t val)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">- uint32_t val_be = osmo_htonl(val);</span><br><span style="color: hsl(0, 100%, 40%);">- return msgb_tvlv_put(msg, tag, 4, (const uint8_t *)&val_be);</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! put (append) a vTvLV field to \ref msgb */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *msgb_vtvlv_gan_put(struct msgb *msg, uint16_t tag,</span><br><span style="color: hsl(0, 100%, 40%);">- uint16_t len, const uint8_t *val)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">- uint8_t *buf = msgb_put(msg, VTVLV_GAN_GROSS_LEN(tag, len));</span><br><span style="color: hsl(0, 100%, 40%);">- return vtvlv_gan_put(buf, tag, len, val);</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! put (append) a L16TV field to \ref msgb */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *msgb_l16tv_put(struct msgb *msg, uint16_t len, uint8_t tag,</span><br><span style="color: hsl(0, 100%, 40%);">- const uint8_t *val)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">- uint8_t *buf = msgb_put(msg, L16TV_GROSS_LEN(len));</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- *buf++ = len >> 8;</span><br><span style="color: hsl(0, 100%, 40%);">- *buf++ = len & 0xff;</span><br><span style="color: hsl(0, 100%, 40%);">- *buf++ = tag;</span><br><span style="color: hsl(0, 100%, 40%);">- memcpy(buf, val, len);</span><br><span style="color: hsl(0, 100%, 40%);">- return buf + len;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! put (append) a V field */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *v_put(uint8_t *buf, uint8_t val)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">- *buf++ = val;</span><br><span style="color: hsl(0, 100%, 40%);">- return buf;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! put (append) a TV field */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *tv_put(uint8_t *buf, uint8_t tag, </span><br><span style="color: hsl(0, 100%, 40%);">- uint8_t val)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">- *buf++ = tag;</span><br><span style="color: hsl(0, 100%, 40%);">- *buf++ = val;</span><br><span style="color: hsl(0, 100%, 40%);">- return buf;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! put (append) a TVfixed field */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *tv_fixed_put(uint8_t *buf, uint8_t tag,</span><br><span style="color: hsl(0, 100%, 40%);">- unsigned int len, const uint8_t *val)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">- *buf++ = tag;</span><br><span style="color: hsl(0, 100%, 40%);">- memcpy(buf, val, len);</span><br><span style="color: hsl(0, 100%, 40%);">- return buf + len;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! put (append) a TV16 field</span><br><span style="color: hsl(0, 100%, 40%);">- * \param[in,out] buf data buffer</span><br><span style="color: hsl(0, 100%, 40%);">- * \param[in] tag Tag value</span><br><span style="color: hsl(0, 100%, 40%);">- * \param[in] val Value (in host byte order!)</span><br><span style="color: hsl(0, 100%, 40%);">- */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *tv16_put(uint8_t *buf, uint8_t tag, </span><br><span style="color: hsl(0, 100%, 40%);">- uint16_t val)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">- *buf++ = tag;</span><br><span style="color: hsl(0, 100%, 40%);">- *buf++ = val >> 8;</span><br><span style="color: hsl(0, 100%, 40%);">- *buf++ = val & 0xff;</span><br><span style="color: hsl(0, 100%, 40%);">- return buf;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! put (append) a LV field to a \ref msgb</span><br><span style="color: hsl(0, 100%, 40%);">- * \returns pointer to first byte after newly-put information */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *msgb_lv_put(struct msgb *msg, uint8_t len, const uint8_t *val)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">- uint8_t *buf = msgb_put(msg, LV_GROSS_LEN(len));</span><br><span style="color: hsl(0, 100%, 40%);">- return lv_put(buf, len, val);</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! put (append) a TLV field to a \ref msgb</span><br><span style="color: hsl(0, 100%, 40%);">- * \returns pointer to first byte after newly-put information */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *msgb_tlv_put(struct msgb *msg, uint8_t tag, uint8_t len, const uint8_t *val)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">- uint8_t *buf = msgb_put(msg, TLV_GROSS_LEN(len));</span><br><span style="color: hsl(0, 100%, 40%);">- return tlv_put(buf, tag, len, val);</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! put (append) a TV field to a \ref msgb</span><br><span style="color: hsl(0, 100%, 40%);">- * \returns pointer to first byte after newly-put information */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *msgb_tv_put(struct msgb *msg, uint8_t tag, uint8_t val)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">- uint8_t *buf = msgb_put(msg, 2);</span><br><span style="color: hsl(0, 100%, 40%);">- return tv_put(buf, tag, val);</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! put (append) a TVfixed field to a \ref msgb</span><br><span style="color: hsl(0, 100%, 40%);">- * \returns pointer to first byte after newly-put information */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *msgb_tv_fixed_put(struct msgb *msg, uint8_t tag,</span><br><span style="color: hsl(0, 100%, 40%);">- unsigned int len, const uint8_t *val)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">- uint8_t *buf = msgb_put(msg, 1+len);</span><br><span style="color: hsl(0, 100%, 40%);">- return tv_fixed_put(buf, tag, len, val);</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! put (append) a V field to a \ref msgb</span><br><span style="color: hsl(0, 100%, 40%);">- * \returns pointer to first byte after newly-put information */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *msgb_v_put(struct msgb *msg, uint8_t val)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">- uint8_t *buf = msgb_put(msg, 1);</span><br><span style="color: hsl(0, 100%, 40%);">- return v_put(buf, val);</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! put (append) a TL fields to a \ref msgb</span><br><span style="color: hsl(0, 100%, 40%);">- * \returns pointer to the length field so it can be updated after adding new information under specified tag */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *msgb_tl_put(struct msgb *msg, uint8_t tag)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">- uint8_t *len = msgb_v_put(msg, tag);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- /* reserve space for length, len points to this reserved space already */</span><br><span style="color: hsl(0, 100%, 40%);">- msgb_v_put(msg, 0);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- return len;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! put (append) a TV16 field (network order) to the given msgb</span><br><span style="color: hsl(0, 100%, 40%);">- * \returns pointer to first byte after newly-put information */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *msgb_tv16_put(struct msgb *msg, uint8_t tag, uint16_t val)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">- uint8_t *buf = msgb_put(msg, 3);</span><br><span style="color: hsl(0, 100%, 40%);">- return tv16_put(buf, tag, val);</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! put (append) a TV32 field (network order) to the given msgb</span><br><span style="color: hsl(0, 100%, 40%);">- * \returns pointer to first byte after newly-put information */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *msgb_tv32_put(struct msgb *msg, uint8_t tag, uint32_t val)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">- uint8_t *buf = msgb_put(msg, 1 + 4);</span><br><span style="color: hsl(0, 100%, 40%);">- *buf++ = tag;</span><br><span style="color: hsl(0, 100%, 40%);">- osmo_store32be(val, buf);</span><br><span style="color: hsl(0, 100%, 40%);">- return msg->tail;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! push (prepend) a TLV field to a \ref msgb</span><br><span style="color: hsl(0, 100%, 40%);">- * \returns pointer to first byte of newly-pushed information */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *msgb_tlv_push(struct msgb *msg, uint8_t tag, uint8_t len, const uint8_t *val)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">- uint8_t *buf = msgb_push(msg, TLV_GROSS_LEN(len));</span><br><span style="color: hsl(0, 100%, 40%);">- tlv_put(buf, tag, len, val);</span><br><span style="color: hsl(0, 100%, 40%);">- return buf;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! push 1-byte tagged value */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *msgb_tlv1_push(struct msgb *msg, uint8_t tag, uint8_t val)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">- return msgb_tlv_push(msg, tag, 1, &val);</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! push (prepend) a TV field to a \ref msgb</span><br><span style="color: hsl(0, 100%, 40%);">- * \returns pointer to first byte of newly-pushed information */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *msgb_tv_push(struct msgb *msg, uint8_t tag, uint8_t val)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">- uint8_t *buf = msgb_push(msg, 2);</span><br><span style="color: hsl(0, 100%, 40%);">- tv_put(buf, tag, val);</span><br><span style="color: hsl(0, 100%, 40%);">- return buf;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! push (prepend) a TV16 field to a \ref msgb</span><br><span style="color: hsl(0, 100%, 40%);">- * \returns pointer to first byte of newly-pushed information */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *msgb_tv16_push(struct msgb *msg, uint8_t tag, uint16_t val)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">- uint8_t *buf = msgb_push(msg, 3);</span><br><span style="color: hsl(0, 100%, 40%);">- tv16_put(buf, tag, val);</span><br><span style="color: hsl(0, 100%, 40%);">- return buf;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! push (prepend) a TvLV field to a \ref msgb</span><br><span style="color: hsl(0, 100%, 40%);">- * \returns pointer to first byte of newly-pushed information */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *msgb_tvlv_push(struct msgb *msg, uint8_t tag, uint16_t len,</span><br><span style="color: hsl(0, 100%, 40%);">- const uint8_t *val)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">- uint8_t *buf = msgb_push(msg, TVLV_GROSS_LEN(len));</span><br><span style="color: hsl(0, 100%, 40%);">- tvlv_put(buf, tag, len, val);</span><br><span style="color: hsl(0, 100%, 40%);">- return buf;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/* push (prepend) a vTvL header to a \ref msgb</span><br><span style="color: hsl(0, 100%, 40%);">- */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *msgb_vtvl_gan_push(struct msgb *msg, uint16_t tag,</span><br><span style="color: hsl(0, 100%, 40%);">- uint16_t len)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">- uint8_t *buf = msgb_push(msg, VTVL_GAN_GROSS_LEN(tag, len));</span><br><span style="color: hsl(0, 100%, 40%);">- vtvl_gan_put(buf, tag, len);</span><br><span style="color: hsl(0, 100%, 40%);">- return buf;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *msgb_vtvlv_gan_push(struct msgb *msg, uint16_t tag,</span><br><span style="color: hsl(0, 100%, 40%);">- uint16_t len, const uint8_t *val)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">- uint8_t *buf = msgb_push(msg, VTVLV_GAN_GROSS_LEN(tag, len));</span><br><span style="color: hsl(0, 100%, 40%);">- vtvlv_gan_put(buf, tag, len, val);</span><br><span style="color: hsl(0, 100%, 40%);">- return buf;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/* TLV parsing */</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! Entry in a TLV parser array */</span><br><span style="color: hsl(0, 100%, 40%);">-struct tlv_p_entry {</span><br><span style="color: hsl(0, 100%, 40%);">- uint16_t len; /*!< length */</span><br><span style="color: hsl(0, 100%, 40%);">- const uint8_t *val; /*!< pointer to value */</span><br><span style="color: hsl(0, 100%, 40%);">-};</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! TLV type */</span><br><span style="color: hsl(0, 100%, 40%);">-enum tlv_type {</span><br><span style="color: hsl(0, 100%, 40%);">- TLV_TYPE_NONE, /*!< no type */</span><br><span style="color: hsl(0, 100%, 40%);">- TLV_TYPE_FIXED, /*!< fixed-length value-only */</span><br><span style="color: hsl(0, 100%, 40%);">- TLV_TYPE_T, /*!< tag-only */</span><br><span style="color: hsl(0, 100%, 40%);">- TLV_TYPE_TV, /*!< tag-value (8bit) */</span><br><span style="color: hsl(0, 100%, 40%);">- TLV_TYPE_TLV, /*!< tag-length-value */</span><br><span style="color: hsl(0, 100%, 40%);">- TLV_TYPE_TL16V, /*!< tag, 16 bit length, value */</span><br><span style="color: hsl(0, 100%, 40%);">- TLV_TYPE_TvLV, /*!< tag, variable length, value */</span><br><span style="color: hsl(0, 100%, 40%);">- TLV_TYPE_SINGLE_TV, /*!< tag and value (both 4 bit) in 1 byte */</span><br><span style="color: hsl(0, 100%, 40%);">- TLV_TYPE_vTvLV_GAN, /*!< variable-length tag, variable-length length */</span><br><span style="color: hsl(0, 100%, 40%);">-};</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! Definition of a single IE (Information Element) */</span><br><span style="color: hsl(0, 100%, 40%);">-struct tlv_def {</span><br><span style="color: hsl(0, 100%, 40%);">- enum tlv_type type; /*!< TLV type */</span><br><span style="color: hsl(0, 100%, 40%);">- uint8_t fixed_len; /*!< length in case of \ref TLV_TYPE_FIXED */</span><br><span style="color: hsl(0, 100%, 40%);">-};</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! Definition of All 256 IE / TLV */</span><br><span style="color: hsl(0, 100%, 40%);">-struct tlv_definition {</span><br><span style="color: hsl(0, 100%, 40%);">- struct tlv_def def[256];</span><br><span style="color: hsl(0, 100%, 40%);">-};</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! result of the TLV parser */</span><br><span style="color: hsl(0, 100%, 40%);">-struct tlv_parsed {</span><br><span style="color: hsl(0, 100%, 40%);">- struct tlv_p_entry lv[256];</span><br><span style="color: hsl(0, 100%, 40%);">-};</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-extern struct tlv_definition tvlv_att_def;</span><br><span style="color: hsl(0, 100%, 40%);">-extern struct tlv_definition vtvlv_gan_att_def;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-int tlv_parse_one(uint8_t *o_tag, uint16_t *o_len, const uint8_t **o_val,</span><br><span style="color: hsl(0, 100%, 40%);">- const struct tlv_definition *def,</span><br><span style="color: hsl(0, 100%, 40%);">- const uint8_t *buf, int buf_len);</span><br><span style="color: hsl(0, 100%, 40%);">-int tlv_parse(struct tlv_parsed *dec, const struct tlv_definition *def,</span><br><span style="color: hsl(0, 100%, 40%);">- const uint8_t *buf, int buf_len, uint8_t lv_tag, uint8_t lv_tag2);</span><br><span style="color: hsl(0, 100%, 40%);">-int tlv_parse2(struct tlv_parsed *dec, int dec_multiples,</span><br><span style="color: hsl(0, 100%, 40%);">- const struct tlv_definition *def, const uint8_t *buf, int buf_len,</span><br><span style="color: hsl(0, 100%, 40%);">- uint8_t lv_tag, uint8_t lv_tag2);</span><br><span style="color: hsl(0, 100%, 40%);">-/* take a master (src) tlv def and fill up all empty slots in 'dst' */</span><br><span style="color: hsl(0, 100%, 40%);">-void tlv_def_patch(struct tlv_definition *dst, const struct tlv_definition *src);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-int tlv_encode_one(struct msgb *msg, enum tlv_type type, uint8_t tag,</span><br><span style="color: hsl(0, 100%, 40%);">- unsigned int len, const uint8_t *val);</span><br><span style="color: hsl(0, 100%, 40%);">-int tlv_encode(struct msgb *msg, const struct tlv_definition *def, const struct tlv_parsed *tp);</span><br><span style="color: hsl(0, 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(0, 100%, 40%);">- const uint8_t *tag_order, unsigned int tag_order_len);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-#define TLVP_PRESENT(x, y) ((x)->lv[y].val)</span><br><span style="color: hsl(0, 100%, 40%);">-#define TLVP_LEN(x, y) (x)->lv[y].len</span><br><span style="color: hsl(0, 100%, 40%);">-#define TLVP_VAL(x, y) (x)->lv[y].val</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-#define TLVP_PRES_LEN(tp, tag, min_len) \</span><br><span style="color: hsl(0, 100%, 40%);">- (TLVP_PRESENT(tp, tag) && TLVP_LEN(tp, tag) >= min_len)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! Return pointer to a TLV element if it is present.</span><br><span style="color: hsl(0, 100%, 40%);">- * Usage:</span><br><span style="color: hsl(0, 100%, 40%);">- * struct tlv_p_entry *e = TVLP_GET(&tp, TAG);</span><br><span style="color: hsl(0, 100%, 40%);">- * if (!e)</span><br><span style="color: hsl(0, 100%, 40%);">- * return -ENOENT;</span><br><span style="color: hsl(0, 100%, 40%);">- * hexdump(e->val, e->len);</span><br><span style="color: hsl(0, 100%, 40%);">- * \param[in] _tp pointer to \ref tlv_parsed.</span><br><span style="color: hsl(0, 100%, 40%);">- * \param[in] tag IE tag to return.</span><br><span style="color: hsl(0, 100%, 40%);">- * \returns struct tlv_p_entry pointer, or NULL if not present.</span><br><span style="color: hsl(0, 100%, 40%);">- */</span><br><span style="color: hsl(0, 100%, 40%);">-#define TLVP_GET(_tp, tag) (TLVP_PRESENT(_tp, tag)? &(_tp)->lv[tag] : NULL)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! Like TLVP_GET(), but enforcing a minimum val length.</span><br><span style="color: hsl(0, 100%, 40%);">- * \param[in] _tp pointer to \ref tlv_parsed.</span><br><span style="color: hsl(0, 100%, 40%);">- * \param[in] tag IE tag to return.</span><br><span style="color: hsl(0, 100%, 40%);">- * \param[in] min_len Minimum value length in bytes.</span><br><span style="color: hsl(0, 100%, 40%);">- * \returns struct tlv_p_entry pointer, or NULL if not present or too short.</span><br><span style="color: hsl(0, 100%, 40%);">- */</span><br><span style="color: hsl(0, 100%, 40%);">-#define TLVP_GET_MINLEN(_tp, tag, min_len) \</span><br><span style="color: hsl(0, 100%, 40%);">- (TLVP_PRES_LEN(_tp, tag, min_len)? &(_tp)->lv[tag] : NULL)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! Like TLVP_VAL(), but enforcing a minimum val length.</span><br><span style="color: hsl(0, 100%, 40%);">- * \param[in] _tp pointer to \ref tlv_parsed.</span><br><span style="color: hsl(0, 100%, 40%);">- * \param[in] tag IE tag to return.</span><br><span style="color: hsl(0, 100%, 40%);">- * \param[in] min_len Minimum value length in bytes.</span><br><span style="color: hsl(0, 100%, 40%);">- * \returns const uint8_t pointer to value, or NULL if not present or too short.</span><br><span style="color: hsl(0, 100%, 40%);">- */</span><br><span style="color: hsl(0, 100%, 40%);">-#define TLVP_VAL_MINLEN(_tp, tag, min_len) \</span><br><span style="color: hsl(0, 100%, 40%);">- (TLVP_PRES_LEN(_tp, tag, min_len)? (_tp)->lv[tag].val : NULL)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! Obtain 1-byte TLV element.</span><br><span style="color: hsl(0, 100%, 40%);">- * \param[in] tp pointer to \ref tlv_parsed</span><br><span style="color: hsl(0, 100%, 40%);">- * \param[in] tag the Tag to look for</span><br><span style="color: hsl(0, 100%, 40%);">- * \param[in] default_val default value to use if tag not available</span><br><span style="color: hsl(0, 100%, 40%);">- * \returns the 1st byte of value with a given tag or default_val if tag was not found</span><br><span style="color: hsl(0, 100%, 40%);">- */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t tlvp_val8(const struct tlv_parsed *tp, uint8_t tag, uint8_t default_val)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">- const uint8_t *res = TLVP_VAL_MINLEN(tp, tag, 1);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- if (res)</span><br><span style="color: hsl(0, 100%, 40%);">- return res[0];</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- return default_val;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! Align given TLV element with 16 bit value to an even address</span><br><span style="color: hsl(0, 100%, 40%);">- * \param[in] tp pointer to \ref tlv_parsed</span><br><span style="color: hsl(0, 100%, 40%);">- * \param[in] pos element to return</span><br><span style="color: hsl(0, 100%, 40%);">- * \returns aligned 16 bit value</span><br><span style="color: hsl(0, 100%, 40%);">- */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint16_t tlvp_val16_unal(const struct tlv_parsed *tp, int pos)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">- uint16_t res;</span><br><span style="color: hsl(0, 100%, 40%);">- memcpy(&res, TLVP_VAL(tp, pos), sizeof(res));</span><br><span style="color: hsl(0, 100%, 40%);">- return res;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! Align given TLV element with 32 bit value to an address that is a multiple of 4</span><br><span style="color: hsl(0, 100%, 40%);">- * \param[in] tp pointer to \ref tlv_parsed</span><br><span style="color: hsl(0, 100%, 40%);">- * \param[in] pos element to return</span><br><span style="color: hsl(0, 100%, 40%);">- * \returns aligned 32 bit value</span><br><span style="color: hsl(0, 100%, 40%);">- */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint32_t tlvp_val32_unal(const struct tlv_parsed *tp, int pos)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">- uint32_t res;</span><br><span style="color: hsl(0, 100%, 40%);">- memcpy(&res, TLVP_VAL(tp, pos), sizeof(res));</span><br><span style="color: hsl(0, 100%, 40%);">- return res;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! Retrieve (possibly unaligned) TLV element and convert to host byte order</span><br><span style="color: hsl(0, 100%, 40%);">- * \param[in] tp pointer to \ref tlv_parsed</span><br><span style="color: hsl(0, 100%, 40%);">- * \param[in] pos element to return</span><br><span style="color: hsl(0, 100%, 40%);">- * \returns aligned 16 bit value in host byte order</span><br><span style="color: hsl(0, 100%, 40%);">- */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint16_t tlvp_val16be(const struct tlv_parsed *tp, int pos)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">- return osmo_load16be(TLVP_VAL(tp, pos));</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! Retrieve (possibly unaligned) TLV element and convert to host byte order</span><br><span style="color: hsl(0, 100%, 40%);">- * \param[in] tp pointer to \ref tlv_parsed</span><br><span style="color: hsl(0, 100%, 40%);">- * \param[in] pos element to return</span><br><span style="color: hsl(0, 100%, 40%);">- * \returns aligned 32 bit value in host byte order</span><br><span style="color: hsl(0, 100%, 40%);">- */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint32_t tlvp_val32be(const struct tlv_parsed *tp, int pos)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">- return osmo_load32be(TLVP_VAL(tp, pos));</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-struct tlv_parsed *osmo_tlvp_copy(const struct tlv_parsed *tp_orig, void *ctx);</span><br><span style="color: hsl(0, 100%, 40%);">-int osmo_tlvp_merge(struct tlv_parsed *dst, const struct tlv_parsed *src);</span><br><span style="color: hsl(0, 100%, 40%);">-int osmo_shift_v_fixed(uint8_t **data, size_t *data_len,</span><br><span style="color: hsl(0, 100%, 40%);">- size_t len, uint8_t **value);</span><br><span style="color: hsl(0, 100%, 40%);">-int osmo_match_shift_tv_fixed(uint8_t **data, size_t *data_len,</span><br><span style="color: hsl(0, 100%, 40%);">- uint8_t tag, size_t len, uint8_t **value);</span><br><span style="color: hsl(0, 100%, 40%);">-int osmo_shift_tlv(uint8_t **data, size_t *data_len,</span><br><span style="color: hsl(0, 100%, 40%);">- uint8_t *tag, uint8_t **value, size_t *value_len);</span><br><span style="color: hsl(0, 100%, 40%);">-int osmo_match_shift_tlv(uint8_t **data, size_t *data_len,</span><br><span style="color: hsl(0, 100%, 40%);">- uint8_t tag, uint8_t **value, size_t *value_len);</span><br><span style="color: hsl(0, 100%, 40%);">-int osmo_shift_lv(uint8_t **data, size_t *data_len,</span><br><span style="color: hsl(0, 100%, 40%);">- uint8_t **value, size_t *value_len);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-#define MSG_DEF(name, mand_ies, flags) { name, mand_ies, ARRAY_SIZE(mand_ies), flags }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-struct osmo_tlv_prot_msg_def {</span><br><span style="color: hsl(0, 100%, 40%);">- /*! human-readable name of message type (optional) */</span><br><span style="color: hsl(0, 100%, 40%);">- const char *name;</span><br><span style="color: hsl(0, 100%, 40%);">- /*! array of mandatory IEs */</span><br><span style="color: hsl(0, 100%, 40%);">- const uint8_t *mand_ies;</span><br><span style="color: hsl(0, 100%, 40%);">- /*! number of entries in 'mand_ies' above */</span><br><span style="color: hsl(0, 100%, 40%);">- uint8_t mand_count;</span><br><span style="color: hsl(0, 100%, 40%);">- /*! user-defined flags (like uplink/downlink/...) */</span><br><span style="color: hsl(0, 100%, 40%);">- uint32_t flags;</span><br><span style="color: hsl(0, 100%, 40%);">-};</span><br><span style="color: hsl(0, 100%, 40%);">-struct osmo_tlv_prot_ie_def {</span><br><span style="color: hsl(0, 100%, 40%);">- /*! minimum length of IE value part, in octets */</span><br><span style="color: hsl(0, 100%, 40%);">- uint16_t min_len;</span><br><span style="color: hsl(0, 100%, 40%);">- /*! huamn-readable name (optional) */</span><br><span style="color: hsl(0, 100%, 40%);">- const char *name;</span><br><span style="color: hsl(0, 100%, 40%);">-};</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! Osmocom TLV protocol definition */</span><br><span style="color: hsl(0, 100%, 40%);">-struct osmo_tlv_prot_def {</span><br><span style="color: hsl(0, 100%, 40%);">- /*! human-readable name of protocol */</span><br><span style="color: hsl(0, 100%, 40%);">- const char *name;</span><br><span style="color: hsl(0, 100%, 40%);">- /*! TLV parser definition (optional) */</span><br><span style="color: hsl(0, 100%, 40%);">- const struct tlv_definition *tlv_def;</span><br><span style="color: hsl(0, 100%, 40%);">- /*! definition of each message (8-bit message type) */</span><br><span style="color: hsl(0, 100%, 40%);">- struct osmo_tlv_prot_msg_def msg_def[256];</span><br><span style="color: hsl(0, 100%, 40%);">- /*! definition of IE for each 8-bit tag */</span><br><span style="color: hsl(0, 100%, 40%);">- struct osmo_tlv_prot_ie_def ie_def[256];</span><br><span style="color: hsl(0, 100%, 40%);">- /*! value_string array of message type names (legacy, if not populated in msg_def) */</span><br><span style="color: hsl(0, 100%, 40%);">- const struct value_string *msgt_names;</span><br><span style="color: hsl(0, 100%, 40%);">-};</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-const char *osmo_tlv_prot_msg_name(const struct osmo_tlv_prot_def *pdef, uint8_t msg_type);</span><br><span style="color: hsl(0, 100%, 40%);">-const char *osmo_tlv_prot_ie_name(const struct osmo_tlv_prot_def *pdef, uint8_t iei);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-int osmo_tlv_prot_validate_tp(const struct osmo_tlv_prot_def *pdef, uint8_t msg_type,</span><br><span style="color: hsl(0, 100%, 40%);">- const struct tlv_parsed *tp, int log_subsys, const char *log_pfx);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-int osmo_tlv_prot_parse(const struct osmo_tlv_prot_def *pdef,</span><br><span style="color: hsl(0, 100%, 40%);">- struct tlv_parsed *dec, unsigned int dec_multiples, uint8_t msg_type,</span><br><span style="color: hsl(0, 100%, 40%);">- const uint8_t *buf, unsigned int buf_len, uint8_t lv_tag, uint8_t lv_tag2,</span><br><span style="color: hsl(0, 100%, 40%);">- int log_subsys, const char *log_pfx);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint32_t osmo_tlv_prot_msgt_flags(const struct osmo_tlv_prot_def *pdef, uint8_t msg_type)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">- return pdef->msg_def[msg_type].flags;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! @} */</span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/tlv.h></span><br><span>diff --git a/src/Makefile.am b/src/Makefile.am</span><br><span>index 4bda456..953b53b 100644</span><br><span>--- a/src/Makefile.am</span><br><span>+++ b/src/Makefile.am</span><br><span>@@ -33,6 +33,7 @@</span><br><span> it_q.c \</span><br><span> probes.d \</span><br><span> base64.c \</span><br><span style="color: hsl(120, 100%, 40%);">+ tlv_parser.c \</span><br><span> $(NULL)</span><br><span> </span><br><span> if HAVE_SSSE3</span><br><span>diff --git a/src/gsm/Makefile.am b/src/gsm/Makefile.am</span><br><span>index 580e397..8b19a5e 100644</span><br><span>--- a/src/gsm/Makefile.am</span><br><span>+++ b/src/gsm/Makefile.am</span><br><span>@@ -20,7 +20,7 @@</span><br><span> </span><br><span> BUILT_SOURCES = gsm0503_conv.c</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-libgsmint_la_SOURCES = a5.c rxlev_stat.c tlv_parser.c comp128.c comp128v23.c \</span><br><span style="color: hsl(120, 100%, 40%);">+libgsmint_la_SOURCES = a5.c rxlev_stat.c comp128.c comp128v23.c \</span><br><span> gsm_utils.c rsl.c gsm48.c gsm48_arfcn_range_encode.c \</span><br><span> gsm48_ie.c gsm0808.c sysinfo.c \</span><br><span> gprs_cipher_core.c gprs_rlc.c gsm0480.c abis_nm.c gsm0502.c \</span><br><span>diff --git a/src/gsm/tlv_parser.c b/src/tlv_parser.c</span><br><span>similarity index 100%</span><br><span>rename from src/gsm/tlv_parser.c</span><br><span>rename to src/tlv_parser.c</span><br><span>diff --git a/tests/Makefile.am b/tests/Makefile.am</span><br><span>index a6f6017..176532e 100644</span><br><span>--- a/tests/Makefile.am</span><br><span>+++ b/tests/Makefile.am</span><br><span>@@ -240,7 +240,7 @@</span><br><span> $(top_builddir)/src/gsm/libosmogsm.la</span><br><span> </span><br><span> tlv_tlv_test_SOURCES = tlv/tlv_test.c</span><br><span style="color: hsl(0, 100%, 40%);">-tlv_tlv_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libosmogsm.la</span><br><span style="color: hsl(120, 100%, 40%);">+tlv_tlv_test_LDADD = $(LDADD)</span><br><span> </span><br><span> gsup_gsup_test_SOURCES = gsup/gsup_test.c</span><br><span> gsup_gsup_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libosmogsm.la</span><br><span>diff --git a/tests/tlv/tlv_test.c b/tests/tlv/tlv_test.c</span><br><span>index fdd15ab..f30f635 100644</span><br><span>--- a/tests/tlv/tlv_test.c</span><br><span>+++ b/tests/tlv/tlv_test.c</span><br><span>@@ -288,6 +288,14 @@</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 const struct tlv_definition test_tlvdef = {</span><br><span style="color: hsl(120, 100%, 40%);">+ .def = {</span><br><span style="color: hsl(120, 100%, 40%);">+ [0x17] = { TLV_TYPE_TLV },</span><br><span style="color: hsl(120, 100%, 40%);">+ [0x2c] = { TLV_TYPE_TV },</span><br><span style="color: hsl(120, 100%, 40%);">+ [0x40] = { TLV_TYPE_TV },</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%);">+</span><br><span> static void test_tlv_encoder()</span><br><span> {</span><br><span> const uint8_t enc_ies[] = {</span><br><span>@@ -312,11 +320,11 @@</span><br><span> OSMO_ASSERT(msg);</span><br><span> </span><br><span> /* decode BSSAP IEs specified above */</span><br><span style="color: hsl(0, 100%, 40%);">- rc = osmo_bssap_tlv_parse(&tp, enc_ies, ARRAY_SIZE(enc_ies));</span><br><span style="color: hsl(120, 100%, 40%);">+ rc = tlv_parse(&tp, &test_tlvdef, enc_ies, ARRAY_SIZE(enc_ies), 0, 0);</span><br><span> OSMO_ASSERT(rc == 3);</span><br><span> </span><br><span> /* re-encode it */</span><br><span style="color: hsl(0, 100%, 40%);">- rc = tlv_encode(msg, gsm0808_att_tlvdef(), &tp);</span><br><span style="color: hsl(120, 100%, 40%);">+ rc = tlv_encode(msg, &test_tlvdef, &tp);</span><br><span> OSMO_ASSERT(rc == ARRAY_SIZE(enc_ies));</span><br><span> OSMO_ASSERT(!memcmp(msgb_data(msg), enc_ies, ARRAY_SIZE(enc_ies)));</span><br><span> </span><br><span>@@ -325,7 +333,7 @@</span><br><span> printf("Testing TLV encoder with IE ordering\n");</span><br><span> </span><br><span> /* re-encodei in different order */</span><br><span style="color: hsl(0, 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%);">+ rc = tlv_encode_ordered(msg, &test_tlvdef, &tp, ie_order, ARRAY_SIZE(ie_order));</span><br><span> OSMO_ASSERT(rc == ARRAY_SIZE(enc_ies));</span><br><span> OSMO_ASSERT(!memcmp(msgb_data(msg), enc_ies_reordered, ARRAY_SIZE(enc_ies_reordered)));</span><br><span> </span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.osmocom.org/c/libosmocore/+/26459">change 26459</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/c/libosmocore/+/26459"/><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-Change-Id: I3cda0143af9eaf90dc73f9deccd651117fdf8845 </div>
<div style="display:none"> Gerrit-Change-Number: 26459 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: neels <nhofmeyr@sysmocom.de> </div>
<div style="display:none"> Gerrit-MessageType: newchange </div>