aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--openbsc/include/openbsc/tlv.h3
-rw-r--r--openbsc/src/tlv_parser.c173
2 files changed, 104 insertions, 72 deletions
diff --git a/openbsc/include/openbsc/tlv.h b/openbsc/include/openbsc/tlv.h
index 6da4fb16a..0cf4388d8 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 8321b880e..fd0045f97 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;