[MERGED] libosmocore[master]: add tlv_parse2(), capable of multiple instances of the same IE

This is merely a historical archive of years 2008-2021, before the migration to mailman3.

A maintained and still updated list archive can be found at https://lists.osmocom.org/hyperkitty/list/gerrit-log@lists.osmocom.org/.

Harald Welte gerrit-no-reply at lists.osmocom.org
Sat Apr 14 11:36:46 UTC 2018


Harald Welte has submitted this change and it was merged.

Change subject: add tlv_parse2(), capable of multiple instances of the same IE
......................................................................


add tlv_parse2(), capable of multiple instances of the same IE

Allow passing multiple struct tlv_parsed in an array, to allow parsing as many
repeated IEs as are expected by the caller.

>From tlv_parse(), call tlv_parse2() with dec_multiple = 1 to yield the previous
behavior. tlv_parse() remains valid API.

An example of multiple IEs is the BSSMAP Handover Request, containing Cell
Identifier (Serving) and Cell Identifier (Target), both defined by 3GPP TS
48.008 3.2.2.17 with identical IE tags; both are mandatory.

Related: OS#2283 (inter-BSC HO, BSC side)
Change-Id: Id04008eaf0a1cafdbdc11b7efc556e3035b1c84d
---
M include/osmocom/gsm/tlv.h
M src/gsm/libosmogsm.map
M src/gsm/tlv_parser.c
M tests/tlv/tlv_test.c
4 files changed, 83 insertions(+), 24 deletions(-)

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



diff --git a/include/osmocom/gsm/tlv.h b/include/osmocom/gsm/tlv.h
index 8654893..996f6aa 100644
--- a/include/osmocom/gsm/tlv.h
+++ b/include/osmocom/gsm/tlv.h
@@ -433,6 +433,9 @@
                   const uint8_t *buf, int buf_len);
 int tlv_parse(struct tlv_parsed *dec, const struct tlv_definition *def,
 	      const uint8_t *buf, int buf_len, uint8_t lv_tag, uint8_t lv_tag2);
+int tlv_parse2(struct tlv_parsed *dec, int dec_multiples,
+	       const struct tlv_definition *def, const uint8_t *buf, int buf_len,
+	       uint8_t lv_tag, uint8_t lv_tag2);
 /* take a master (src) tlv def and fill up all empty slots in 'dst' */
 void tlv_def_patch(struct tlv_definition *dst, const struct tlv_definition *src);
 
diff --git a/src/gsm/libosmogsm.map b/src/gsm/libosmogsm.map
index d99121e..4d009e0 100644
--- a/src/gsm/libosmogsm.map
+++ b/src/gsm/libosmogsm.map
@@ -406,6 +406,7 @@
 tlv_def_patch;
 tlv_dump;
 tlv_parse;
+tlv_parse2;
 tlv_parse_one;
 tvlv_att_def;
 vtvlv_gan_att_def;
diff --git a/src/gsm/tlv_parser.c b/src/gsm/tlv_parser.c
index f693971..6e089f7 100644
--- a/src/gsm/tlv_parser.c
+++ b/src/gsm/tlv_parser.c
@@ -220,7 +220,12 @@
 	return len;
 }
 
-/*! Parse an entire buffer of TLV encoded Information Elements
+/*! Parse an entire buffer of TLV encoded Information Elements.
+ * In case of multiple occurences of an IE, keep only the first occurence.
+ * Most GSM related protocols clearly indicate that in case of duplicate
+ * IEs, only the first occurrence shall be used, while any further occurrences
+ * shall be ignored.  See e.g. 3GPP TS 24.008 Section 8.6.3.
+ * For multiple occurences, use tlv_parse2().
  *  \param[out] dec caller-allocated pointer to \ref tlv_parsed
  *  \param[in] def structure defining the valid TLV tags / configurations
  *  \param[in] buf the input data buffer to be parsed
@@ -233,38 +238,77 @@
 	      const uint8_t *buf, int buf_len, uint8_t lv_tag,
 	      uint8_t lv_tag2)
 {
+	return tlv_parse2(dec, 1, def, buf, buf_len, lv_tag, lv_tag2);
+}
+
+/*! Like tlv_parse(), but capable of decoding multiple occurences of the same IE.
+ * Parse an entire buffer of TLV encoded Information Elements.
+ * To decode multiple occurences of IEs, provide in dec an _array_ of tlv_parsed, and
+ * pass the size of that array in dec_multiples. The first occurence of each IE
+ * is stored in dec[0], the second in dec[1] and so forth. If there are more
+ * occurences than the array length given in dec_multiples, the remaining
+ * occurences are dropped.
+ *  \param[out] dec caller-allocated pointer to \ref tlv_parsed
+ *  \param[in] dec_multiples length of the tlv_parsed[] in \a dec.
+ *  \param[in] def structure defining the valid TLV tags / configurations
+ *  \param[in] buf the input data buffer to be parsed
+ *  \param[in] buf_len length of the input data buffer
+ *  \param[in] lv_tag an initial LV tag at the start of the buffer
+ *  \param[in] lv_tag2 a second initial LV tag following the \a lv_tag
+ *  \returns number of TLV entries parsed; negative in case of error
+ */
+int tlv_parse2(struct tlv_parsed *dec, int dec_multiples,
+	       const struct tlv_definition *def, const uint8_t *buf, int buf_len,
+	       uint8_t lv_tag, uint8_t lv_tag2)
+{
 	int ofs = 0, num_parsed = 0;
 	uint16_t len;
+	int dec_i;
 
-	memset(dec, 0, sizeof(*dec));
+	for (dec_i = 0; dec_i < dec_multiples; dec_i++)
+		memset(&dec[dec_i], 0, sizeof(*dec));
 
 	if (lv_tag) {
+		const uint8_t *val;
+		uint16_t parsed_len;
 		if (ofs > buf_len)
 			return -1;
-		dec->lv[lv_tag].val = &buf[ofs+1];
-		dec->lv[lv_tag].len = buf[ofs];
-		len = dec->lv[lv_tag].len + 1;
-		if (ofs + len > buf_len) {
-			dec->lv[lv_tag].val = NULL;
-			dec->lv[lv_tag].len = 0;
+		val = &buf[ofs+1];
+		len = buf[ofs];
+		parsed_len = len + 1;
+		if (ofs + parsed_len > buf_len)
 			return -2;
-		}
 		num_parsed++;
-		ofs += len;
+		ofs += parsed_len;
+		/* store the resulting val and len */
+		for (dec_i = 0; dec_i < dec_multiples; dec_i++) {
+			if (dec[dec_i].lv[lv_tag].val != NULL)
+				continue;
+			dec->lv[lv_tag].val = val;
+			dec->lv[lv_tag].len = len;
+			break;
+		}
 	}
 	if (lv_tag2) {
+		const uint8_t *val;
+		uint16_t parsed_len;
 		if (ofs > buf_len)
 			return -1;
-		dec->lv[lv_tag2].val = &buf[ofs+1];
-		dec->lv[lv_tag2].len = buf[ofs];
-		len = dec->lv[lv_tag2].len + 1;
-		if (ofs + len > buf_len) {
-			dec->lv[lv_tag2].val = NULL;
-			dec->lv[lv_tag2].len = 0;
+		val = &buf[ofs+1];
+		len = buf[ofs];
+		parsed_len = len + 1;
+		if (ofs + parsed_len > buf_len)
 			return -2;
-		}
 		num_parsed++;
-		ofs += len;
+		ofs += parsed_len;
+		/* store the resulting val and len */
+		for (dec_i = 0; dec_i < dec_multiples; dec_i++) {
+			if (dec[dec_i].lv[lv_tag2].val != NULL)
+				continue;
+			dec->lv[lv_tag2].val = val;
+			dec->lv[lv_tag2].len = len;
+			break;
+		}
 	}
 
 	while (ofs < buf_len) {
@@ -276,12 +320,12 @@
 		                   &buf[ofs], buf_len-ofs);
 		if (rv < 0)
 			return rv;
-		/* Most GSM related protocols clearly indicate that in case of duplicate
-		 * IEs, only the first occurrence shall be used, while any further occurrences
-		 * shall be ignored.  See e.g. 3GPP TS 24.008 Section 8.6.3 */
-		if (dec->lv[tag].val == NULL) {
-			dec->lv[tag].val = val;
-			dec->lv[tag].len = len;
+		for (dec_i = 0; dec_i < dec_multiples; dec_i++) {
+			if (dec[dec_i].lv[tag].val != NULL)
+				continue;
+			dec[dec_i].lv[tag].val = val;
+			dec[dec_i].lv[tag].len = len;
+			break;
 		}
 		ofs += rv;
 		num_parsed++;
diff --git a/tests/tlv/tlv_test.c b/tests/tlv/tlv_test.c
index 3973275..e2065b0 100644
--- a/tests/tlv/tlv_test.c
+++ b/tests/tlv/tlv_test.c
@@ -254,6 +254,7 @@
 	int i, rc;
 	const uint8_t tag = 0x1a;
 	struct tlv_parsed dec;
+	struct tlv_parsed dec3[3];
 	struct tlv_definition def;
 
 	memset(&def, 0, sizeof(def));
@@ -273,6 +274,16 @@
 	/* Value pointer should point at first value in test data array. */
 	OSMO_ASSERT(dec.lv[tag].val == &test_data[2]);
 	OSMO_ASSERT(*dec.lv[tag].val == test_data[2]);
+
+	/* Accept three decodings, pointing at first, second and third val */
+	rc = tlv_parse2(dec3, 3, &def, &test_data[1], sizeof(test_data) - 1, tag, 0);
+	OSMO_ASSERT(rc == i/3);
+	OSMO_ASSERT(dec3[0].lv[tag].len == 1);
+	OSMO_ASSERT(dec3[0].lv[tag].val == &test_data[2]);
+	OSMO_ASSERT(dec3[1].lv[tag].len == 1);
+	OSMO_ASSERT(dec3[1].lv[tag].val == &test_data[2 + 3]);
+	OSMO_ASSERT(dec3[2].lv[tag].len == 1);
+	OSMO_ASSERT(dec3[2].lv[tag].val == &test_data[2 + 3 + 3]);
 }
 
 int main(int argc, char **argv)

-- 
To view, visit https://gerrit.osmocom.org/7788
To unsubscribe, visit https://gerrit.osmocom.org/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: Id04008eaf0a1cafdbdc11b7efc556e3035b1c84d
Gerrit-PatchSet: 2
Gerrit-Project: libosmocore
Gerrit-Branch: master
Gerrit-Owner: Neels Hofmeyr <nhofmeyr at sysmocom.de>
Gerrit-Reviewer: Harald Welte <laforge at gnumonks.org>
Gerrit-Reviewer: Jenkins Builder



More information about the gerrit-log mailing list