diff options
author | Harald Welte <laforge@gnumonks.org> | 2009-05-23 06:39:58 +0000 |
---|---|---|
committer | Harald Welte <laforge@gnumonks.org> | 2009-05-23 06:39:58 +0000 |
commit | a4d49e96ab45b12fe581d6ab3cecd0727bccb317 (patch) | |
tree | dbeebd5e255e0ef1b07c1be083f8691027e46afa | |
parent | e7b452a7a07b75f880fadeeb650efe2bac931c7b (diff) |
Some messages have one or two length-value information elements. The is
no IE type included in the message. These information elements are
mandatory, so their actual IE type is known. The improved parse_tlv()
function allows to parse zero, one, or two length-value elements.
(Andreas Eversberg)
-rw-r--r-- | include/openbsc/tlv.h | 29 | ||||
-rw-r--r-- | src/abis_nm.c | 2 | ||||
-rw-r--r-- | src/abis_rsl.c | 3 | ||||
-rw-r--r-- | src/gsm_04_08.c | 2 | ||||
-rw-r--r-- | src/tlv_parser.c | 37 |
5 files changed, 66 insertions, 7 deletions
diff --git a/include/openbsc/tlv.h b/include/openbsc/tlv.h index 453f1d0a0..ae88e6ed3 100644 --- a/include/openbsc/tlv.h +++ b/include/openbsc/tlv.h @@ -6,12 +6,21 @@ #include <openbsc/msgb.h> +#define LV_GROSS_LEN(x) (x+1) #define TLV_GROSS_LEN(x) (x+2) #define TLV16_GROSS_LEN(x) ((2*x)+2) #define TL16V_GROSS_LEN(x) (x+3) /* TLV generation */ +static inline u_int8_t *lv_put(u_int8_t *buf, u_int8_t len, + const u_int8_t *val) +{ + *buf++ = len; + memcpy(buf, val, len); + return buf + len; +} + static inline u_int8_t *tlv_put(u_int8_t *buf, u_int8_t tag, u_int8_t len, const u_int8_t *val) { @@ -53,6 +62,12 @@ static inline u_int8_t *msgb_tl16v_put(struct msgb *msg, u_int8_t tag, u_int16_t return tl16v_put(buf, tag, len, val); } +static inline u_int8_t *v_put(u_int8_t *buf, u_int8_t val) +{ + *buf++ = val; + return buf; +} + static inline u_int8_t *tv_put(u_int8_t *buf, u_int8_t tag, u_int8_t val) { @@ -70,6 +85,12 @@ static inline u_int8_t *tv16_put(u_int8_t *buf, u_int8_t tag, return buf; } +static inline u_int8_t *msgb_lv_put(struct msgb *msg, u_int8_t len, const u_int8_t *val) +{ + u_int8_t *buf = msgb_put(msg, LV_GROSS_LEN(len)); + return lv_put(buf, len, val); +} + static inline u_int8_t *msgb_tlv_put(struct msgb *msg, u_int8_t tag, u_int8_t len, const u_int8_t *val) { u_int8_t *buf = msgb_put(msg, TLV_GROSS_LEN(len)); @@ -82,6 +103,12 @@ static inline u_int8_t *msgb_tv_put(struct msgb *msg, u_int8_t tag, u_int8_t val return tv_put(buf, tag, val); } +static inline u_int8_t *msgb_v_put(struct msgb *msg, u_int8_t val) +{ + u_int8_t *buf = msgb_put(msg, 1); + return v_put(buf, val); +} + static inline u_int8_t *msgb_tv16_put(struct msgb *msg, u_int8_t tag, u_int16_t val) { u_int8_t *buf = msgb_put(msg, 3); @@ -135,7 +162,7 @@ struct tlv_parsed { }; int tlv_parse(struct tlv_parsed *dec, const struct tlv_definition *def, - const u_int8_t *buf, int buf_len); + const u_int8_t *buf, int buf_len, u_int8_t lv_tag, u_int8_t lv_tag2); #define TLVP_PRESENT(x, y) ((x)->lv[y].val) #define TLVP_LEN(x, y) (x)->lv[y].len diff --git a/src/abis_nm.c b/src/abis_nm.c index 6909f7828..0107c294e 100644 --- a/src/abis_nm.c +++ b/src/abis_nm.c @@ -365,7 +365,7 @@ static const struct tlv_definition nm_att_tlvdef = { int abis_nm_tlv_parse(struct tlv_parsed *tp, const u_int8_t *buf, int len) { - return tlv_parse(tp, &nm_att_tlvdef, buf, len); + return tlv_parse(tp, &nm_att_tlvdef, buf, len, 0, 0); } static int is_in_arr(enum abis_nm_msgtype mt, const enum abis_nm_msgtype *arr, int size) diff --git a/src/abis_rsl.c b/src/abis_rsl.c index e7f953e0c..753a66609 100644 --- a/src/abis_rsl.c +++ b/src/abis_rsl.c @@ -112,7 +112,8 @@ static const struct tlv_definition rsl_att_tlvdef = { [0xfc] = { TLV_TYPE_TV }, }, }; -#define rsl_tlv_parse(dec, buf, len) tlv_parse(dec, &rsl_att_tlvdef, buf, len) +#define rsl_tlv_parse(dec, buf, len) \ + tlv_parse(dec, &rsl_att_tlvdef, buf, len, 0, 0) static u_int8_t mdisc_by_msgtype(u_int8_t msg_type) { diff --git a/src/gsm_04_08.c b/src/gsm_04_08.c index ad7bba687..849ef4888 100644 --- a/src/gsm_04_08.c +++ b/src/gsm_04_08.c @@ -1119,7 +1119,7 @@ static int gsm48_cc_rx_setup(struct msgb *msg) call->local_lchan = msg->lchan; call->transaction_id = gh->proto_discr & 0xf0; - tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len); + tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, 0, 0); if (!TLVP_PRESENT(&tp, GSM48_IE_CALLED_BCD)) goto err; diff --git a/src/tlv_parser.c b/src/tlv_parser.c index fe6d28926..e835f951f 100644 --- a/src/tlv_parser.c +++ b/src/tlv_parser.c @@ -13,16 +13,47 @@ int tlv_dump(struct tlv_parsed *dec) return 0; } +/* 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 + * buf_len: input: the length of the input data buffer + * lv_tag: input: an initial LV tag at the start of the buffer + * lv_tag2: input: a second initial LV tag following lv_tag + */ int tlv_parse(struct tlv_parsed *dec, const struct tlv_definition *def, - const u_int8_t *buf, int buf_len) + 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; + const u_int8_t *pos = buf; int num_parsed = 0; memset(dec, 0, sizeof(*dec)); - for (pos = buf; pos < buf+buf_len; pos += len) { + if (lv_tag) { + if (pos > buf + buf_len) + return -1; + dec->lv[lv_tag].val = pos+1; + dec->lv[lv_tag].len = *pos; + len = dec->lv[lv_tag].len + 1; + if (pos + len > buf + buf_len) + return -2; + num_parsed++; + pos += len; + } + if (lv_tag2) { + if (pos > buf + buf_len) + return -1; + dec->lv[lv_tag2].val = pos+1; + dec->lv[lv_tag2].len = *pos; + len = dec->lv[lv_tag2].len + 1; + if (pos + len > buf + buf_len) + return -2; + num_parsed++; + pos += len; + } + + for (; pos < buf+buf_len; pos += len) { tag = *pos; /* FIXME: use tables for knwon IEI */ switch (def->def[tag].type) { |