<p>laforge <strong>submitted</strong> this change.</p><p><a href="https://gerrit.osmocom.org/c/libosmocore/+/21534">View Change</a></p><div style="white-space:pre-wrap">Approvals:
  fixeria: Looks good to me, approved
  pespin: Looks good to me, but someone else must approve
  Jenkins Builder: Verified

</div><pre style="font-family: monospace,monospace; white-space: pre-wrap;">Introduce 'osmo_tlv_prot' abstraction for validation of TLV protocols<br><br>This extends our existing TLV parser with the ability to<br>* validate that mandatory IEs of a given message are present<br>* validate that all present IEs are of required minimum length<br><br>Introducing this generic layer will help us to reduce open-coded<br>imperative verification across virtually all the protocols we<br>implement, as well as add validation to those protocols where we<br>don't properly perform related input validation yet.<br><br>Change-Id: If1e1d9adfa141ca86001dbd62a6a339f9bf9a912<br>---<br>M include/osmocom/gsm/tlv.h<br>M src/gsm/libosmogsm.map<br>M src/gsm/tlv_parser.c<br>3 files changed, 159 insertions(+), 1 deletion(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/include/osmocom/gsm/tlv.h b/include/osmocom/gsm/tlv.h</span><br><span>index 254c21b..a307b3d 100644</span><br><span>--- a/include/osmocom/gsm/tlv.h</span><br><span>+++ b/include/osmocom/gsm/tlv.h</span><br><span>@@ -620,4 +620,54 @@</span><br><span> int osmo_shift_lv(uint8_t **data, size_t *data_len,</span><br><span>                  uint8_t **value, size_t *value_len);</span><br><span> </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> /*! @} */</span><br><span>diff --git a/src/gsm/libosmogsm.map b/src/gsm/libosmogsm.map</span><br><span>index f339120..bcded1c 100644</span><br><span>--- a/src/gsm/libosmogsm.map</span><br><span>+++ b/src/gsm/libosmogsm.map</span><br><span>@@ -580,6 +580,11 @@</span><br><span> osmo_match_shift_tlv;</span><br><span> osmo_shift_lv;</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tlv_prot_msg_name;</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tlv_prot_ie_name;</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tlv_prot_validate_tp;</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_tlv_prot_parse;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> gan_msgt_vals;</span><br><span> gan_pdisc_vals;</span><br><span> </span><br><span>diff --git a/src/gsm/tlv_parser.c b/src/gsm/tlv_parser.c</span><br><span>index 159b42b..24edd0c 100644</span><br><span>--- a/src/gsm/tlv_parser.c</span><br><span>+++ b/src/gsm/tlv_parser.c</span><br><span>@@ -1,4 +1,4 @@</span><br><span style="color: hsl(0, 100%, 40%);">-/* (C) 2008-2017 by Harald Welte <laforge@gnumonks.org></span><br><span style="color: hsl(120, 100%, 40%);">+/* (C) 2008-2020 by Harald Welte <laforge@gnumonks.org></span><br><span>  * (C) 2016-2017 by sysmocom - s.f.m.c. GmbH</span><br><span>  *</span><br><span>  * All Rights Reserved</span><br><span>@@ -24,6 +24,7 @@</span><br><span> #include <stdint.h></span><br><span> #include <errno.h></span><br><span> #include <osmocom/core/utils.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/logging.h></span><br><span> #include <osmocom/gsm/tlv.h></span><br><span> </span><br><span> /*! \addtogroup tlv</span><br><span>@@ -627,4 +628,106 @@</span><br><span>     return -1;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static __thread char ienamebuf[32];</span><br><span style="color: hsl(120, 100%, 40%);">+static __thread char msgnamebuf[32];</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! get the message name for given msg_type in protocol pdef */</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%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      if (pdef->msg_def[msg_type].name) {</span><br><span style="color: hsl(120, 100%, 40%);">+                return pdef->msg_def[msg_type].name;</span><br><span style="color: hsl(120, 100%, 40%);">+       } else if (pdef->msgt_names) {</span><br><span style="color: hsl(120, 100%, 40%);">+             return get_value_string(pdef->msgt_names, msg_type);</span><br><span style="color: hsl(120, 100%, 40%);">+       } else {</span><br><span style="color: hsl(120, 100%, 40%);">+              snprintf(msgnamebuf, sizeof(msgnamebuf), "Unknown msg_type 0x%02x", msg_type);</span><br><span style="color: hsl(120, 100%, 40%);">+              return msgnamebuf;</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%);">+/*! get the IE name for given IEI in protocol pdef */</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%);">+        if (pdef->ie_def[iei].name) {</span><br><span style="color: hsl(120, 100%, 40%);">+              return pdef->ie_def[iei].name;</span><br><span style="color: hsl(120, 100%, 40%);">+     } else {</span><br><span style="color: hsl(120, 100%, 40%);">+              snprintf(ienamebuf, sizeof(ienamebuf), "Unknown IEI 0x%02x", iei);</span><br><span style="color: hsl(120, 100%, 40%);">+          return ienamebuf;</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%);">+/*! Validate an already TLV-decoded message against the protocol definition.</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] pdef protocol definition of given protocol</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] msg_type message type of the parsed message</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] tp TLV parser result</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] log_subsys logging sub-system for log messages</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] log_pfx prefix for log messages</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \returns 0 in case of success; negative osmo_tlv_parser_error in case of error</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%);">+    const struct osmo_tlv_prot_msg_def *msg_def= &pdef->msg_def[msg_type];</span><br><span style="color: hsl(120, 100%, 40%);">+ unsigned int num_err = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     unsigned int i;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     if (msg_def->mand_ies) {</span><br><span style="color: hsl(120, 100%, 40%);">+           for (i = 0; i < msg_def->mand_count; i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+                     uint8_t iei = msg_def->mand_ies[i];</span><br><span style="color: hsl(120, 100%, 40%);">+                        if (!TLVP_PRESENT(tp, iei)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                         LOGP(log_subsys, LOGL_ERROR, "%s %s %s: Missing Mandatory IE: %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                                     log_pfx, pdef->name, osmo_tlv_prot_msg_name(pdef, msg_type),</span><br><span style="color: hsl(120, 100%, 40%);">+                               osmo_tlv_prot_ie_name(pdef, iei));</span><br><span style="color: hsl(120, 100%, 40%);">+                               num_err++;</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 style="color: hsl(120, 100%, 40%);">+   for (i = 0; i < ARRAY_SIZE(tp->lv); i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+              uint16_t min_len;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           if (!TLVP_PRESENT(tp, i))</span><br><span style="color: hsl(120, 100%, 40%);">+                     continue;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           min_len = pdef->ie_def[i].min_len;</span><br><span style="color: hsl(120, 100%, 40%);">+         if (TLVP_LEN(tp, i) < min_len) {</span><br><span style="color: hsl(120, 100%, 40%);">+                   LOGP(log_subsys, LOGL_ERROR, "%s %s %s: Short IE %s: %u < %u\n", log_pfx,</span><br><span style="color: hsl(120, 100%, 40%);">+                             pdef->name, osmo_tlv_prot_msg_name(pdef, msg_type),</span><br><span style="color: hsl(120, 100%, 40%);">+                        osmo_tlv_prot_ie_name(pdef, i), TLVP_LEN(tp, i), min_len);</span><br><span style="color: hsl(120, 100%, 40%);">+                       num_err++;</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%);">+   return -num_err;</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%);">+/*! Parse + Validate a TLV-encoded message against the protocol definition.</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] pdef protocol definition of given protocol</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[out] dec caller-allocated pointer to \ref tlv_parsed</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] dec_multiples length of the tlv_parsed[] in \a dec.</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] msg_type message type of the parsed message</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] buf the input data buffer to be parsed</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] buf_len length of the input data buffer</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] lv_tag an initial LV tag at the start of the buffer</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] lv_tag2 a second initial LV tag following the \a lv_tag</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] log_subsys logging sub-system for log messages</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] log_pfx prefix for log messages</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \returns 0 in case of success; negative osmo_tlv_parser_error in case of error</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%);">+       int rc;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     rc = tlv_parse2(dec, dec_multiples, pdef->tlv_def, buf, buf_len, lv_tag, lv_tag2);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (rc < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+              LOGP(log_subsys, LOGL_ERROR, "%s %s %s: TLV parser error %d\n", log_pfx,</span><br><span style="color: hsl(120, 100%, 40%);">+                 pdef->name, osmo_tlv_prot_msg_name(pdef, msg_type), rc);</span><br><span style="color: hsl(120, 100%, 40%);">+              return rc;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return osmo_tlv_prot_validate_tp(pdef, msg_type, dec, log_subsys, log_pfx);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /*! @} */</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.osmocom.org/c/libosmocore/+/21534">change 21534</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/+/21534"/><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: If1e1d9adfa141ca86001dbd62a6a339f9bf9a912 </div>
<div style="display:none"> Gerrit-Change-Number: 21534 </div>
<div style="display:none"> Gerrit-PatchSet: 10 </div>
<div style="display:none"> Gerrit-Owner: laforge <laforge@osmocom.org> </div>
<div style="display:none"> Gerrit-Reviewer: Jenkins Builder </div>
<div style="display:none"> Gerrit-Reviewer: fixeria <vyanitskiy@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: laforge <laforge@osmocom.org> </div>
<div style="display:none"> Gerrit-Reviewer: pespin <pespin@sysmocom.de> </div>
<div style="display:none"> Gerrit-MessageType: merged </div>