[PATCH 3/4] tlv_parser: Split the tlv into 'parse loop' and 'parse single value'

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/OpenBSC@lists.osmocom.org/.

Sylvain Munaut 246tnt at gmail.com
Sun Oct 25 16:56:43 UTC 2009


From: Sylvain Munaut <tnt at 246tNt.com>

This is needed when you need to manually parse TLV blocks
that don't follow the logic supported by tlv_parse but you
still want to rely on working code and not fiddle with details.

Signed-off-by: Sylvain Munaut <tnt at 246tNt.com>
---
 openbsc/include/openbsc/tlv.h |    3 +
 openbsc/src/tlv_parser.c      |  173 ++++++++++++++++++++++++-----------------
 2 files changed, 104 insertions(+), 72 deletions(-)

diff --git a/openbsc/include/openbsc/tlv.h b/openbsc/include/openbsc/tlv.h
index 6da4fb1..0cf4388 100644
--- a/openbsc/include/openbsc/tlv.h
+++ b/openbsc/include/openbsc/tlv.h
@@ -207,6 +207,9 @@ struct tlv_parsed {
 
 extern struct tlv_definition tvlv_att_def;
 
+int tlv_parse_one(u_int8_t *o_tag, u_int16_t *o_len, const u_int8_t **o_val,
+                  const struct tlv_definition *def,
+                  const u_int8_t *buf, int buf_len);
 int tlv_parse(struct tlv_parsed *dec, const struct tlv_definition *def,
 	      const u_int8_t *buf, int buf_len, u_int8_t lv_tag, u_int8_t lv_tag2);
 
diff --git a/openbsc/src/tlv_parser.c b/openbsc/src/tlv_parser.c
index 8321b88..fd0045f 100644
--- a/openbsc/src/tlv_parser.c
+++ b/openbsc/src/tlv_parser.c
@@ -16,6 +16,82 @@ int tlv_dump(struct tlv_parsed *dec)
 	return 0;
 }
 
+/* o_tag:  output: tag found
+ * o_len:  output: length of the data
+ * o_val:  output: pointer to the data
+ * def:     input: a structure defining the valid TLV tags / configurations
+ * buf:     input: the input data buffer to be parsed
+ * buf_len: input: the length of the input data buffer
+ *
+ * Also, returns the number of bytes consumed by the TLV entry
+ */
+int tlv_parse_one(u_int8_t *o_tag, u_int16_t *o_len, const u_int8_t **o_val,
+		  const struct tlv_definition *def,
+		  const u_int8_t *buf, int buf_len)
+{
+	u_int8_t tag;
+	int len;
+
+	tag = *buf;
+	*o_tag = tag;
+
+	/* FIXME: use tables for knwon IEI */
+	switch (def->def[tag].type) {
+	case TLV_TYPE_T:
+		/* GSM TS 04.07 11.2.4: Type 1 TV or Type 2 T */
+		*o_val = buf;
+		*o_len = 0;
+		len = 1;
+		break;
+	case TLV_TYPE_TV:
+		*o_val = buf+1;
+		*o_len = 1;
+		len = 2;
+		break;
+	case TLV_TYPE_FIXED:
+		*o_val = buf+1;
+		*o_len = def->def[tag].fixed_len;
+		len = def->def[tag].fixed_len + 1;
+		break;
+	case TLV_TYPE_TLV:
+		/* GSM TS 04.07 11.2.4: Type 4 TLV */
+		if (buf + 1 > buf + buf_len)
+			return -1;
+		*o_val = buf+2;
+		*o_len = *(buf+1);
+		len = *o_len + 2;
+		if (len > buf_len)
+			return -2;
+		break;
+	case TLV_TYPE_TvLV:
+		if (*(buf+1) & 0x80) {
+			/* like TLV, but without highest bit of len */
+			if (buf + 1 > buf + buf_len)
+				return -1;
+			*o_val = buf+2;
+			*o_len = *(buf+1) & 0x7f;
+			len = *o_len + 2;
+			if (len > buf_len)
+				return -2;
+			break;
+		}
+		/* like TL16V, fallthrough */
+	case TLV_TYPE_TL16V:
+		if (2 > buf_len)
+			return -1;
+		*o_val = buf+3;
+		*o_len = *(buf+1) << 8 | *(buf+2);
+		len = *o_len + 3;
+		if (len > buf_len)
+			return -2;
+		break;
+	default:
+		return -3;
+	}
+
+	return len;
+}
+
 /* dec:    output: a caller-allocated pointer to a struct tlv_parsed,
  * def:     input: a structure defining the valid TLV tags / configurations
  * buf:     input: the input data buffer to be parsed
@@ -27,94 +103,47 @@ int tlv_parse(struct tlv_parsed *dec, const struct tlv_definition *def,
 	      const u_int8_t *buf, int buf_len, u_int8_t lv_tag,
 	      u_int8_t lv_tag2)
 {
-	u_int8_t tag, len = 1;
-	const u_int8_t *pos = buf;
-	int num_parsed = 0;
+	int ofs = 0, num_parsed = 0;
+	u_int16_t len;
 
 	memset(dec, 0, sizeof(*dec));
 
 	if (lv_tag) {
-		if (pos > buf + buf_len)
+		if (ofs > buf_len)
 			return -1;
-		dec->lv[lv_tag].val = pos+1;
-		dec->lv[lv_tag].len = *pos;
+		dec->lv[lv_tag].val = &buf[ofs+1];
+		dec->lv[lv_tag].len = buf[ofs];
 		len = dec->lv[lv_tag].len + 1;
-		if (pos + len > buf + buf_len)
+		if (ofs + len > buf_len)
 			return -2;
 		num_parsed++;
-		pos += len;
+		ofs += len;
 	}
 	if (lv_tag2) {
-		if (pos > buf + buf_len)
+		if (ofs > buf_len)
 			return -1;
-		dec->lv[lv_tag2].val = pos+1;
-		dec->lv[lv_tag2].len = *pos;
+		dec->lv[lv_tag2].val = &buf[ofs+1];
+		dec->lv[lv_tag2].len = buf[ofs];
 		len = dec->lv[lv_tag2].len + 1;
-		if (pos + len > buf + buf_len)
+		if (ofs + len > buf_len)
 			return -2;
 		num_parsed++;
-		pos += len;
+		ofs += len;
 	}
 
-	for (; pos < buf+buf_len; pos += len) {
-		tag = *pos;
-		/* FIXME: use tables for knwon IEI */
-		switch (def->def[tag].type) {
-		case TLV_TYPE_T:
-			/* GSM TS 04.07 11.2.4: Type 1 TV or Type 2 T */
-			dec->lv[tag].val = pos;
-			dec->lv[tag].len = 0;
-			len = 1;
-			num_parsed++;
-			break;
-		case TLV_TYPE_TV:
-			dec->lv[tag].val = pos+1;
-			dec->lv[tag].len = 1;
-			len = 2;
-			num_parsed++;
-			break;
-		case TLV_TYPE_FIXED:
-			dec->lv[tag].val = pos+1;
-			dec->lv[tag].len = def->def[tag].fixed_len;
-			len = def->def[tag].fixed_len + 1;
-			num_parsed++;
-			break;
-		case TLV_TYPE_TLV:
-			/* GSM TS 04.07 11.2.4: Type 4 TLV */
-			if (pos + 1 > buf + buf_len)
-				return -1;
-			dec->lv[tag].val = pos+2;
-			dec->lv[tag].len = *(pos+1);
-			len = dec->lv[tag].len + 2;
-			if (pos + len > buf + buf_len)
-				return -2;
-			num_parsed++;
-			break;
-		case TLV_TYPE_TvLV:
-			if (*(pos+1) & 0x80) {
-				/* like TLV, but without highest bit of len */
-				if (pos + 1 > buf + buf_len)
-					return -1;
-				dec->lv[tag].val = pos+2;
-				dec->lv[tag].len = *(pos+1) & 0x7f;
-				len = dec->lv[tag].len + 2;
-				if (pos + len > buf + buf_len)
-					return -2;
-				num_parsed++;
-				break;
-			}
-			/* like TL16V, fallthrough */
-		case TLV_TYPE_TL16V:
-			if (pos + 2 > buf + buf_len)
-				return -1;
-			dec->lv[tag].val = pos+3;
-			dec->lv[tag].len = *(pos+1) << 8 | *(pos+2);
-			len = dec->lv[tag].len + 3;
-			if (pos + len > buf + buf_len)
-				return -2;
-			num_parsed++;
-			break;
-		}
+	while (ofs < buf_len) {
+		int rv;
+		u_int8_t tag;
+		const u_int8_t *val;
+
+		rv = tlv_parse_one(&tag, &len, &val, def,
+		                   &buf[ofs], buf_len-ofs);
+		if (rv < 0)
+			return rv;
+		dec->lv[tag].val = val;
+		dec->lv[tag].len = len;
+		ofs += rv;
+		num_parsed++;
 	}
 	//tlv_dump(dec);
 	return num_parsed;
-- 
1.6.4





More information about the OpenBSC mailing list