From 2fe684755a3c8181a4dde0f5dcabac9c0c5a809c Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Sat, 14 Jul 2012 01:50:33 +0200 Subject: TLV: add new "vTvLV" type for GAN This new TLV type is specific to TS 44.318. Contrary to the TvLV type of TS 08.16/08.18, it has an inverted meaning of the extension (0x80) bit: * if the extension bit is not set, 1-byte length * if the extension bit is set, 2-byte length Futhermore, it has support for variable-length tags, where the tag part can be optionally two bytes in length. As this esoteric option hasn't been seen in the wild yet, we only add encoding support but skip decoding for now. --- include/osmocom/gsm/tlv.h | 98 ++++++++++++++++++++++++++++++++++++++++++++++- src/gsm/libosmogsm.map | 1 + src/gsm/tlv_parser.c | 22 ++++++++++- 3 files changed, 119 insertions(+), 2 deletions(-) diff --git a/include/osmocom/gsm/tlv.h b/include/osmocom/gsm/tlv.h index d1efc553..9c0319d9 100644 --- a/include/osmocom/gsm/tlv.h +++ b/include/osmocom/gsm/tlv.h @@ -20,6 +20,7 @@ TL16V 8 16 N * 8 TLV16 8 8 N * 16 TvLV 8 8/16 N * 8 + vTvLV 8/16 8/16 N * 8 */ @@ -46,6 +47,36 @@ static inline uint16_t TVLV_GROSS_LEN(uint16_t len) return TL16V_GROSS_LEN(len); } +/*! \brief gross length of vTvL header (tag+len) */ +static inline uint16_t VTVL_GAN_GROSS_LEN(uint16_t tag, uint16_t len) +{ + uint16_t ret = 2; + + if (tag > TVLV_MAX_ONEBYTE) + ret++; + + if (len > TVLV_MAX_ONEBYTE) + ret++; + + return ret; +} + +/*! \brief gross length of vTvLV (tag+len+val) */ +static inline uint16_t VTVLV_GAN_GROSS_LEN(uint16_t tag, uint16_t len) +{ + uint16_t ret; + + if (len <= TVLV_MAX_ONEBYTE) + return TLV_GROSS_LEN(len); + else + return TL16V_GROSS_LEN(len); + + if (tag > TVLV_MAX_ONEBYTE) + ret += 1; + + return ret; +} + /* TLV generation */ /*! \brief put (append) a LV field */ @@ -103,6 +134,42 @@ static inline uint8_t *tvlv_put(uint8_t *buf, uint8_t tag, uint16_t len, return ret; } +/*! \brief put (append) a variable-length tag or variable-length length * */ +static inline uint8_t *vt_gan_put(uint8_t *buf, uint16_t tag) +{ + if (tag > TVLV_MAX_ONEBYTE) { + /* two-byte TAG */ + *buf++ = 0x80 | (tag >> 8); + *buf++ = (tag & 0xff); + } else + *buf++ = tag; + + return buf; +} + +/* \brief put (append) vTvL (GAN) field (tag + length)*/ +static inline uint8_t *vtvl_gan_put(uint8_t *buf, uint16_t tag, uint16_t len) +{ + uint8_t *ret; + + ret = vt_gan_put(buf, tag); + return vt_gan_put(ret, len); +} + +/* \brief put (append) vTvLV (GAN) field (tag + length + val) */ +static inline uint8_t *vtvlv_gan_put(uint8_t *buf, uint16_t tag, uint16_t len, + const uint8_t *val) +{ + uint8_t *ret; + + ret = vtvl_gan_put(buf, tag, len ); + + memcpy(ret, val, len); + ret = buf + len; + + return ret; +} + /*! \brief put (append) a TLV16 field to \ref msgb */ static inline uint8_t *msgb_tlv16_put(struct msgb *msg, uint8_t tag, uint8_t len, const uint16_t *val) { @@ -126,6 +193,14 @@ static inline uint8_t *msgb_tvlv_put(struct msgb *msg, uint8_t tag, uint16_t len return tvlv_put(buf, tag, len, val); } +/*! \brief put (append) a vTvLV field to \ref msgb */ +static inline uint8_t *msgb_vtvlv_gan_put(struct msgb *msg, uint16_t tag, + uint16_t len, const uint8_t *val) +{ + uint8_t *buf = msgb_put(msg, VTVLV_GAN_GROSS_LEN(tag, len)); + return vtvlv_gan_put(buf, tag, len, val); +} + /*! \brief put (append) a L16TV field to \ref msgb */ static inline uint8_t *msgb_l16tv_put(struct msgb *msg, uint16_t len, uint8_t tag, const uint8_t *val) @@ -264,6 +339,25 @@ static inline uint8_t *msgb_tvlv_push(struct msgb *msg, uint8_t tag, uint16_t le return buf; } +/* \brief push (prepend) a vTvL header to a \ref msgb + */ +static inline uint8_t *msgb_vtvl_gan_push(struct msgb *msg, uint16_t tag, + uint16_t len) +{ + uint8_t *buf = msgb_push(msg, VTVL_GAN_GROSS_LEN(tag, len)); + vtvl_gan_put(buf, tag, len); + return buf; +} + + +static inline uint8_t *msgb_vtvlv_gan_push(struct msgb *msg, uint16_t tag, + uint16_t len, const uint8_t *val) +{ + uint8_t *buf = msgb_push(msg, VTVLV_GAN_GROSS_LEN(tag, len)); + vtvlv_gan_put(buf, tag, len, val); + return buf; +} + /* TLV parsing */ /*! \brief Entry in a TLV parser array */ @@ -281,7 +375,8 @@ enum tlv_type { TLV_TYPE_TLV, /*!< \brief tag-length-value */ TLV_TYPE_TL16V, /*!< \brief tag, 16 bit length, value */ TLV_TYPE_TvLV, /*!< \brief tag, variable length, value */ - TLV_TYPE_SINGLE_TV /*!< \brief tag and value (both 4 bit) in 1 byte */ + TLV_TYPE_SINGLE_TV, /*!< \brief tag and value (both 4 bit) in 1 byte */ + TLV_TYPE_vTvLV_GAN, /*!< \brief variable-length tag, variable-length length */ }; /*! \brief Definition of a single IE (Information Element) */ @@ -301,6 +396,7 @@ struct tlv_parsed { }; extern struct tlv_definition tvlv_att_def; +extern struct tlv_definition vtvlv_gan_att_def; int tlv_parse_one(uint8_t *o_tag, uint16_t *o_len, const uint8_t **o_val, const struct tlv_definition *def, diff --git a/src/gsm/libosmogsm.map b/src/gsm/libosmogsm.map index 9ef5ccd4..33738881 100644 --- a/src/gsm/libosmogsm.map +++ b/src/gsm/libosmogsm.map @@ -227,6 +227,7 @@ tlv_dump; tlv_parse; tlv_parse_one; tvlv_att_def; +vtvlv_gan_att_def; gan_msgt_vals; gan_pdisc_vals; diff --git a/src/gsm/tlv_parser.c b/src/gsm/tlv_parser.c index 0ac90929..d18a6bfd 100644 --- a/src/gsm/tlv_parser.c +++ b/src/gsm/tlv_parser.c @@ -9,6 +9,7 @@ /*! \file tlv.c */ struct tlv_definition tvlv_att_def; +struct tlv_definition vtvlv_gan_att_def; /*! \brief Dump pasred TLV structure to stdout */ int tlv_dump(struct tlv_parsed *dec) @@ -69,7 +70,7 @@ int tlv_parse_one(uint8_t *o_tag, uint16_t *o_len, const uint8_t **o_val, len = def->def[tag].fixed_len + 1; break; case TLV_TYPE_TLV: - /* GSM TS 04.07 11.2.4: Type 4 TLV */ +tlv: /* GSM TS 04.07 11.2.4: Type 4 TLV */ if (buf + 1 > buf + buf_len) return -1; *o_val = buf+2; @@ -78,6 +79,22 @@ int tlv_parse_one(uint8_t *o_tag, uint16_t *o_len, const uint8_t **o_val, if (len > buf_len) return -2; break; + case TLV_TYPE_vTvLV_GAN: /* 44.318 / 11.1.4 */ + /* FIXME: variable-length TAG! */ + if (*(buf+1) & 0x80) { + /* like TL16Vbut without highest bit of len */ + if (2 > buf_len) + return -1; + *o_val = buf+3; + *o_len = (*(buf+1) & 0x7F) << 8 | *(buf+2); + len = *o_len + 3; + if (len > buf_len) + return -2; + } else { + /* like TLV */ + goto tlv; + } + break; case TLV_TYPE_TvLV: if (*(buf+1) & 0x80) { /* like TLV, but without highest bit of len */ @@ -184,6 +201,9 @@ static __attribute__((constructor)) void on_dso_load_tlv(void) int i; for (i = 0; i < ARRAY_SIZE(tvlv_att_def.def); i++) tvlv_att_def.def[i].type = TLV_TYPE_TvLV; + + for (i = 0; i < ARRAY_SIZE(vtvlv_gan_att_def.def); i++) + vtvlv_gan_att_def.def[i].type = TLV_TYPE_vTvLV_GAN; } /*! @} */ -- cgit v1.2.3