diff options
author | Neels Hofmeyr <neels@hofmeyr.de> | 2018-04-13 03:36:49 +0200 |
---|---|---|
committer | Neels Hofmeyr <neels@hofmeyr.de> | 2018-04-13 05:28:09 +0200 |
commit | a78b22ba203d9674bb797623b9c1dd985b523663 (patch) | |
tree | 77453b0de925087e818ff474c6ac2a6bb02421f2 | |
parent | 74663d97c6677d92ba9ed4338c190b8dc42b537e (diff) |
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
-rw-r--r-- | include/osmocom/gsm/tlv.h | 3 | ||||
-rw-r--r-- | src/gsm/libosmogsm.map | 1 | ||||
-rw-r--r-- | src/gsm/tlv_parser.c | 92 | ||||
-rw-r--r-- | tests/tlv/tlv_test.c | 11 |
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) |