aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/osmocom/gsm/tlv.h3
-rw-r--r--src/gsm/libosmogsm.map1
-rw-r--r--src/gsm/tlv_parser.c92
-rw-r--r--tests/tlv/tlv_test.c11
4 files changed, 83 insertions, 24 deletions
diff --git a/include/osmocom/gsm/tlv.h b/include/osmocom/gsm/tlv.h
index 8654893b..996f6aac 100644
--- a/include/osmocom/gsm/tlv.h
+++ b/include/osmocom/gsm/tlv.h
@@ -433,6 +433,9 @@ int tlv_parse_one(uint8_t *o_tag, uint16_t *o_len, const uint8_t **o_val,
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 d99121e2..4d009e04 100644
--- a/src/gsm/libosmogsm.map
+++ b/src/gsm/libosmogsm.map
@@ -406,6 +406,7 @@ rxlev_stat_reset;
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 f6939713..6e089f7f 100644
--- a/src/gsm/tlv_parser.c
+++ b/src/gsm/tlv_parser.c
@@ -220,7 +220,12 @@ tlv: /* GSM TS 04.07 11.2.4: Type 4 TLV */
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 @@ 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)
{
+ 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 @@ int tlv_parse(struct tlv_parsed *dec, const struct tlv_definition *def,
&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 39732756..e2065b00 100644
--- a/tests/tlv/tlv_test.c
+++ b/tests/tlv/tlv_test.c
@@ -254,6 +254,7 @@ static void test_tlv_repeated_ie()
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 @@ static void test_tlv_repeated_ie()
/* 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)