blob: 8cb2139dd3f888d03a7b8f5a13609807d824e2db [file] [log] [blame]
Harald Welteec8b4502010-02-20 20:34:29 +01001#include <stdio.h>
2#include <stdint.h>
Pablo Neira Ayuso83419342011-03-22 16:36:13 +01003#include <osmocom/core/utils.h>
4#include <osmocom/gsm/tlv.h>
Harald Welteec8b4502010-02-20 20:34:29 +01005
Harald Welte57c7d372011-08-17 17:50:55 +02006/*! \addtogroup tlv
7 * @{
8 */
Katerina Barone-Adesic28c6a02013-02-15 13:27:59 +01009/*! \file tlv_parser.c */
Harald Welte57c7d372011-08-17 17:50:55 +020010
Harald Welteec8b4502010-02-20 20:34:29 +010011struct tlv_definition tvlv_att_def;
Harald Welte2fe68472012-07-14 01:50:33 +020012struct tlv_definition vtvlv_gan_att_def;
Harald Welteec8b4502010-02-20 20:34:29 +010013
Harald Welte57c7d372011-08-17 17:50:55 +020014/*! \brief Dump pasred TLV structure to stdout */
Harald Welteec8b4502010-02-20 20:34:29 +010015int tlv_dump(struct tlv_parsed *dec)
16{
17 int i;
18
19 for (i = 0; i <= 0xff; i++) {
20 if (!dec->lv[i].val)
21 continue;
22 printf("T=%02x L=%d\n", i, dec->lv[i].len);
23 }
24 return 0;
25}
26
Harald Welte57c7d372011-08-17 17:50:55 +020027/*! \brief Parse a single TLV encoded IE
28 * \param[out] o_tag the tag of the IE that was found
29 * \param[out] o_len length of the IE that was found
30 * \param[out] o_val pointer to the data of the IE that was found
31 * \param[in] def structure defining the valid TLV tags / configurations
32 * \param[in] buf the input data buffer to be parsed
33 * \param[in] buf_len length of the input data buffer
34 * \returns number of bytes consumed by the TLV entry / IE parsed
Harald Welteec8b4502010-02-20 20:34:29 +010035 */
36int tlv_parse_one(uint8_t *o_tag, uint16_t *o_len, const uint8_t **o_val,
37 const struct tlv_definition *def,
38 const uint8_t *buf, int buf_len)
39{
40 uint8_t tag;
41 int len;
42
43 tag = *buf;
44 *o_tag = tag;
45
Andreas Eversbergcd2a74b2010-07-12 08:55:14 +020046 /* single octet TV IE */
47 if (def->def[tag & 0xf0].type == TLV_TYPE_SINGLE_TV) {
48 *o_tag = tag & 0xf0;
49 *o_val = buf;
50 *o_len = 1;
51 return 1;
52 }
53
Harald Welteec8b4502010-02-20 20:34:29 +010054 /* FIXME: use tables for knwon IEI */
55 switch (def->def[tag].type) {
56 case TLV_TYPE_T:
57 /* GSM TS 04.07 11.2.4: Type 1 TV or Type 2 T */
58 *o_val = buf;
59 *o_len = 0;
60 len = 1;
61 break;
62 case TLV_TYPE_TV:
63 *o_val = buf+1;
64 *o_len = 1;
65 len = 2;
66 break;
67 case TLV_TYPE_FIXED:
68 *o_val = buf+1;
69 *o_len = def->def[tag].fixed_len;
70 len = def->def[tag].fixed_len + 1;
71 break;
72 case TLV_TYPE_TLV:
Harald Welte2fe68472012-07-14 01:50:33 +020073tlv: /* GSM TS 04.07 11.2.4: Type 4 TLV */
Harald Welteec8b4502010-02-20 20:34:29 +010074 if (buf + 1 > buf + buf_len)
75 return -1;
76 *o_val = buf+2;
77 *o_len = *(buf+1);
78 len = *o_len + 2;
79 if (len > buf_len)
80 return -2;
81 break;
Harald Welte2fe68472012-07-14 01:50:33 +020082 case TLV_TYPE_vTvLV_GAN: /* 44.318 / 11.1.4 */
83 /* FIXME: variable-length TAG! */
84 if (*(buf+1) & 0x80) {
85 /* like TL16Vbut without highest bit of len */
86 if (2 > buf_len)
87 return -1;
88 *o_val = buf+3;
89 *o_len = (*(buf+1) & 0x7F) << 8 | *(buf+2);
90 len = *o_len + 3;
91 if (len > buf_len)
92 return -2;
93 } else {
94 /* like TLV */
95 goto tlv;
96 }
97 break;
Harald Welteec8b4502010-02-20 20:34:29 +010098 case TLV_TYPE_TvLV:
99 if (*(buf+1) & 0x80) {
100 /* like TLV, but without highest bit of len */
101 if (buf + 1 > buf + buf_len)
102 return -1;
103 *o_val = buf+2;
104 *o_len = *(buf+1) & 0x7f;
105 len = *o_len + 2;
106 if (len > buf_len)
107 return -2;
108 break;
109 }
110 /* like TL16V, fallthrough */
111 case TLV_TYPE_TL16V:
112 if (2 > buf_len)
113 return -1;
114 *o_val = buf+3;
115 *o_len = *(buf+1) << 8 | *(buf+2);
116 len = *o_len + 3;
117 if (len > buf_len)
118 return -2;
119 break;
120 default:
121 return -3;
122 }
123
124 return len;
125}
126
Harald Welte57c7d372011-08-17 17:50:55 +0200127/*! \brief Parse an entire buffer of TLV encoded Information Eleemnts
128 * \param[out] dec caller-allocated pointer to \ref tlv_parsed
129 * \param[in] def structure defining the valid TLV tags / configurations
130 * \param[in] buf the input data buffer to be parsed
131 * \param[in] buf_len length of the input data buffer
132 * \param[in] lv_tag an initial LV tag at the start of the buffer
133 * \param[in] lv_tag2 a second initial LV tag following the \a lv_tag
134 * \returns number of bytes consumed by the TLV entry / IE parsed
Harald Welteec8b4502010-02-20 20:34:29 +0100135 */
136int tlv_parse(struct tlv_parsed *dec, const struct tlv_definition *def,
137 const uint8_t *buf, int buf_len, uint8_t lv_tag,
138 uint8_t lv_tag2)
139{
140 int ofs = 0, num_parsed = 0;
141 uint16_t len;
142
143 memset(dec, 0, sizeof(*dec));
144
145 if (lv_tag) {
146 if (ofs > buf_len)
147 return -1;
148 dec->lv[lv_tag].val = &buf[ofs+1];
149 dec->lv[lv_tag].len = buf[ofs];
150 len = dec->lv[lv_tag].len + 1;
151 if (ofs + len > buf_len)
152 return -2;
153 num_parsed++;
154 ofs += len;
155 }
156 if (lv_tag2) {
157 if (ofs > buf_len)
158 return -1;
159 dec->lv[lv_tag2].val = &buf[ofs+1];
160 dec->lv[lv_tag2].len = buf[ofs];
161 len = dec->lv[lv_tag2].len + 1;
162 if (ofs + len > buf_len)
163 return -2;
164 num_parsed++;
165 ofs += len;
166 }
167
168 while (ofs < buf_len) {
169 int rv;
170 uint8_t tag;
171 const uint8_t *val;
172
173 rv = tlv_parse_one(&tag, &len, &val, def,
174 &buf[ofs], buf_len-ofs);
175 if (rv < 0)
176 return rv;
177 dec->lv[tag].val = val;
178 dec->lv[tag].len = len;
179 ofs += rv;
180 num_parsed++;
181 }
182 //tlv_dump(dec);
183 return num_parsed;
184}
185
Harald Welte57c7d372011-08-17 17:50:55 +0200186/*! \brief take a master (src) tlvdev and fill up all empty slots in 'dst' */
Harald Welteec8b4502010-02-20 20:34:29 +0100187void tlv_def_patch(struct tlv_definition *dst, const struct tlv_definition *src)
188{
189 int i;
190
191 for (i = 0; i < ARRAY_SIZE(dst->def); i++) {
192 if (src->def[i].type == TLV_TYPE_NONE)
193 continue;
194 if (dst->def[i].type == TLV_TYPE_NONE)
195 dst->def[i] = src->def[i];
196 }
197}
198
199static __attribute__((constructor)) void on_dso_load_tlv(void)
200{
201 int i;
202 for (i = 0; i < ARRAY_SIZE(tvlv_att_def.def); i++)
203 tvlv_att_def.def[i].type = TLV_TYPE_TvLV;
Harald Welte2fe68472012-07-14 01:50:33 +0200204
205 for (i = 0; i < ARRAY_SIZE(vtvlv_gan_att_def.def); i++)
206 vtvlv_gan_att_def.def[i].type = TLV_TYPE_vTvLV_GAN;
Harald Welteec8b4502010-02-20 20:34:29 +0100207}
Harald Welte57c7d372011-08-17 17:50:55 +0200208
Sylvain Munautdca7d2c2012-04-18 21:53:23 +0200209/*! @} */