blob: e84edd978fe3153e029175e9ba413626a6eb0d48 [file] [log] [blame]
Harald Welte468b6432014-09-11 13:05:51 +08001/* (C) 2008-2010 by Harald Welte <laforge@gnumonks.org>
2 *
3 * All Rights Reserved
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 *
18 */
19
Harald Welteec8b4502010-02-20 20:34:29 +010020#include <stdio.h>
21#include <stdint.h>
Pablo Neira Ayuso83419342011-03-22 16:36:13 +010022#include <osmocom/core/utils.h>
23#include <osmocom/gsm/tlv.h>
Harald Welteec8b4502010-02-20 20:34:29 +010024
Harald Welte57c7d372011-08-17 17:50:55 +020025/*! \addtogroup tlv
26 * @{
27 */
Katerina Barone-Adesic28c6a02013-02-15 13:27:59 +010028/*! \file tlv_parser.c */
Harald Welte57c7d372011-08-17 17:50:55 +020029
Harald Welteec8b4502010-02-20 20:34:29 +010030struct tlv_definition tvlv_att_def;
Harald Welte2fe68472012-07-14 01:50:33 +020031struct tlv_definition vtvlv_gan_att_def;
Harald Welteec8b4502010-02-20 20:34:29 +010032
Harald Welte57c7d372011-08-17 17:50:55 +020033/*! \brief Dump pasred TLV structure to stdout */
Harald Welteec8b4502010-02-20 20:34:29 +010034int tlv_dump(struct tlv_parsed *dec)
35{
36 int i;
37
38 for (i = 0; i <= 0xff; i++) {
39 if (!dec->lv[i].val)
40 continue;
41 printf("T=%02x L=%d\n", i, dec->lv[i].len);
42 }
43 return 0;
44}
45
Harald Welte57c7d372011-08-17 17:50:55 +020046/*! \brief Parse a single TLV encoded IE
47 * \param[out] o_tag the tag of the IE that was found
48 * \param[out] o_len length of the IE that was found
49 * \param[out] o_val pointer to the data of the IE that was found
50 * \param[in] def structure defining the valid TLV tags / configurations
51 * \param[in] buf the input data buffer to be parsed
52 * \param[in] buf_len length of the input data buffer
53 * \returns number of bytes consumed by the TLV entry / IE parsed
Harald Welteec8b4502010-02-20 20:34:29 +010054 */
55int tlv_parse_one(uint8_t *o_tag, uint16_t *o_len, const uint8_t **o_val,
56 const struct tlv_definition *def,
57 const uint8_t *buf, int buf_len)
58{
59 uint8_t tag;
60 int len;
61
62 tag = *buf;
63 *o_tag = tag;
64
Andreas Eversbergcd2a74b2010-07-12 08:55:14 +020065 /* single octet TV IE */
66 if (def->def[tag & 0xf0].type == TLV_TYPE_SINGLE_TV) {
67 *o_tag = tag & 0xf0;
68 *o_val = buf;
69 *o_len = 1;
70 return 1;
71 }
72
Neels Hofmeyr667e83d2015-11-02 20:18:11 +010073 /* FIXME: use tables for known IEI */
Harald Welteec8b4502010-02-20 20:34:29 +010074 switch (def->def[tag].type) {
75 case TLV_TYPE_T:
76 /* GSM TS 04.07 11.2.4: Type 1 TV or Type 2 T */
77 *o_val = buf;
78 *o_len = 0;
79 len = 1;
80 break;
81 case TLV_TYPE_TV:
82 *o_val = buf+1;
83 *o_len = 1;
84 len = 2;
85 break;
86 case TLV_TYPE_FIXED:
87 *o_val = buf+1;
88 *o_len = def->def[tag].fixed_len;
89 len = def->def[tag].fixed_len + 1;
90 break;
91 case TLV_TYPE_TLV:
Harald Welte2fe68472012-07-14 01:50:33 +020092tlv: /* GSM TS 04.07 11.2.4: Type 4 TLV */
Harald Welteec8b4502010-02-20 20:34:29 +010093 if (buf + 1 > buf + buf_len)
94 return -1;
95 *o_val = buf+2;
96 *o_len = *(buf+1);
97 len = *o_len + 2;
98 if (len > buf_len)
99 return -2;
100 break;
Harald Welte2fe68472012-07-14 01:50:33 +0200101 case TLV_TYPE_vTvLV_GAN: /* 44.318 / 11.1.4 */
102 /* FIXME: variable-length TAG! */
103 if (*(buf+1) & 0x80) {
104 /* like TL16Vbut without highest bit of len */
105 if (2 > buf_len)
106 return -1;
107 *o_val = buf+3;
108 *o_len = (*(buf+1) & 0x7F) << 8 | *(buf+2);
109 len = *o_len + 3;
110 if (len > buf_len)
111 return -2;
112 } else {
113 /* like TLV */
114 goto tlv;
115 }
116 break;
Harald Welteec8b4502010-02-20 20:34:29 +0100117 case TLV_TYPE_TvLV:
118 if (*(buf+1) & 0x80) {
119 /* like TLV, but without highest bit of len */
120 if (buf + 1 > buf + buf_len)
121 return -1;
122 *o_val = buf+2;
123 *o_len = *(buf+1) & 0x7f;
124 len = *o_len + 2;
125 if (len > buf_len)
126 return -2;
127 break;
128 }
129 /* like TL16V, fallthrough */
130 case TLV_TYPE_TL16V:
131 if (2 > buf_len)
132 return -1;
133 *o_val = buf+3;
134 *o_len = *(buf+1) << 8 | *(buf+2);
135 len = *o_len + 3;
136 if (len > buf_len)
137 return -2;
138 break;
139 default:
140 return -3;
141 }
142
143 return len;
144}
145
Neels Hofmeyr667e83d2015-11-02 20:18:11 +0100146/*! \brief Parse an entire buffer of TLV encoded Information Elements
Harald Welte57c7d372011-08-17 17:50:55 +0200147 * \param[out] dec caller-allocated pointer to \ref tlv_parsed
148 * \param[in] def structure defining the valid TLV tags / configurations
149 * \param[in] buf the input data buffer to be parsed
150 * \param[in] buf_len length of the input data buffer
151 * \param[in] lv_tag an initial LV tag at the start of the buffer
152 * \param[in] lv_tag2 a second initial LV tag following the \a lv_tag
153 * \returns number of bytes consumed by the TLV entry / IE parsed
Harald Welteec8b4502010-02-20 20:34:29 +0100154 */
155int tlv_parse(struct tlv_parsed *dec, const struct tlv_definition *def,
156 const uint8_t *buf, int buf_len, uint8_t lv_tag,
157 uint8_t lv_tag2)
158{
159 int ofs = 0, num_parsed = 0;
160 uint16_t len;
161
162 memset(dec, 0, sizeof(*dec));
163
164 if (lv_tag) {
165 if (ofs > buf_len)
166 return -1;
167 dec->lv[lv_tag].val = &buf[ofs+1];
168 dec->lv[lv_tag].len = buf[ofs];
169 len = dec->lv[lv_tag].len + 1;
170 if (ofs + len > buf_len)
171 return -2;
172 num_parsed++;
173 ofs += len;
174 }
175 if (lv_tag2) {
176 if (ofs > buf_len)
177 return -1;
178 dec->lv[lv_tag2].val = &buf[ofs+1];
179 dec->lv[lv_tag2].len = buf[ofs];
180 len = dec->lv[lv_tag2].len + 1;
181 if (ofs + len > buf_len)
182 return -2;
183 num_parsed++;
184 ofs += len;
185 }
186
187 while (ofs < buf_len) {
188 int rv;
189 uint8_t tag;
190 const uint8_t *val;
191
192 rv = tlv_parse_one(&tag, &len, &val, def,
193 &buf[ofs], buf_len-ofs);
194 if (rv < 0)
195 return rv;
196 dec->lv[tag].val = val;
197 dec->lv[tag].len = len;
198 ofs += rv;
199 num_parsed++;
200 }
201 //tlv_dump(dec);
202 return num_parsed;
203}
204
Harald Welte57c7d372011-08-17 17:50:55 +0200205/*! \brief take a master (src) tlvdev and fill up all empty slots in 'dst' */
Harald Welteec8b4502010-02-20 20:34:29 +0100206void tlv_def_patch(struct tlv_definition *dst, const struct tlv_definition *src)
207{
208 int i;
209
210 for (i = 0; i < ARRAY_SIZE(dst->def); i++) {
211 if (src->def[i].type == TLV_TYPE_NONE)
212 continue;
213 if (dst->def[i].type == TLV_TYPE_NONE)
214 dst->def[i] = src->def[i];
215 }
216}
217
218static __attribute__((constructor)) void on_dso_load_tlv(void)
219{
220 int i;
221 for (i = 0; i < ARRAY_SIZE(tvlv_att_def.def); i++)
222 tvlv_att_def.def[i].type = TLV_TYPE_TvLV;
Harald Welte2fe68472012-07-14 01:50:33 +0200223
224 for (i = 0; i < ARRAY_SIZE(vtvlv_gan_att_def.def); i++)
225 vtvlv_gan_att_def.def[i].type = TLV_TYPE_vTvLV_GAN;
Harald Welteec8b4502010-02-20 20:34:29 +0100226}
Harald Welte57c7d372011-08-17 17:50:55 +0200227
Harald Weltefbd02fa2016-04-25 15:19:35 +0200228/*! Advance the data pointer, subtract length and assign value pointer
229 * \param data pointer to the pointer to data
230 * \param data_len pointer to size_t containing \arg data length
231 * \param[in] len the length that we expect the fixed IE to hav
232 * \param[out] value pointer to pointer of value part of IE
233 * \returns length of IE value; negative in case of error
234 */
235int osmo_shift_v_fixed(uint8_t **data, size_t *data_len,
236 size_t len, uint8_t **value)
237{
238 if (len > *data_len)
239 goto fail;
240
241 if (value)
242 *value = *data;
243
244 *data += len;
245 *data_len -= len;
246
247 return len;
248
249fail:
250 *data += *data_len;
251 *data_len = 0;
252 return -1;
253}
254
255/*! Match tag, check length and assign value pointer
256 * \param data pointer to the pointer to data
257 * \param data_len pointer to size_t containing \arg data length
258 * \param[in] tag the tag (IEI) that we expect at \arg data
259 * \param[in] len the length that we expect the fixed IE to have
260 * \param[out] value pointer to pointer of value part of IE
261 * \returns length of IE value; negative in case of error
262 */
263int osmo_match_shift_tv_fixed(uint8_t **data, size_t *data_len,
264 uint8_t tag, size_t len,
265 uint8_t **value)
266{
267 size_t ie_len;
268
269 if (*data_len == 0)
270 goto fail;
271
272 if ((*data)[0] != tag)
273 return 0;
274
275 if (len > *data_len - 1)
276 goto fail;
277
278 if (value)
279 *value = *data + 1;
280
281 ie_len = len + 1;
282 *data += ie_len;
283 *data_len -= ie_len;
284
285 return ie_len;
286
287fail:
288 *data += *data_len;
289 *data_len = 0;
290 return -1;
291}
292
293/*! Verify TLV header and advance data / subtract length
294 * \param data pointer to the pointer to data
295 * \param data_len pointer to size_t containing \arg data length
296 * \param[in] expected_tag the tag (IEI) that we expect at \arg data
297 * \param[out] value pointer to pointer of value part of IE
298 * \param[out] value_len pointer to length of \arg value
299 * \returns length of IE value; negative in case of error
300 */
301int osmo_match_shift_tlv(uint8_t **data, size_t *data_len,
302 uint8_t expected_tag, uint8_t **value,
303 size_t *value_len)
304{
305 int rc;
306 uint8_t tag;
307 uint8_t *old_data = *data;
308 size_t old_data_len = *data_len;
309
310 rc = osmo_shift_tlv(data, data_len, &tag, value, value_len);
311
312 if (rc > 0 && tag != expected_tag) {
313 *data = old_data;
314 *data_len = old_data_len;
315 return 0;
316 }
317
318 return rc;
319}
320
321/*! Extract TLV and advance data pointer + subtract length
322 * \param data pointer to the pointer to data
323 * \param data_len pointer to size_t containing \arg data lengt
324 * \param[out] tag extract the tag (IEI) at start of \arg data
325 * \param[out] value extracted pointer to value part of TLV
326 * \param[out] value_len extracted length of \arg value
327 * \returns number of bytes subtracted
328 */
329int osmo_shift_tlv(uint8_t **data, size_t *data_len,
330 uint8_t *tag, uint8_t **value, size_t *value_len)
331{
332 size_t len;
333 size_t ie_len;
334
335 if (*data_len < 2)
336 goto fail;
337
338 len = (*data)[1];
339 if (len > *data_len - 2)
340 goto fail;
341
342 if (tag)
343 *tag = (*data)[0];
344 if (value)
345 *value = *data + 2;
346 if (value_len)
347 *value_len = len;
348
349 ie_len = len + 2;
350
351 *data += ie_len;
352 *data_len -= ie_len;
353
354 return ie_len;
355
356fail:
357 *data += *data_len;
358 *data_len = 0;
359 return -1;
360}
361
362/*! Extract LV and advance data pointer + subtract length
363 * \param data pointer to the pointer to data
364 * \param data_len pointer to size_t containing \arg data lengt
365 * \param[out] value extracted pointer to value part of TLV
366 * \param[out] value_len extracted length of \arg value
367 * \returns number of bytes subtracted
368 */
369int osmo_shift_lv(uint8_t **data, size_t *data_len,
370 uint8_t **value, size_t *value_len)
371{
372 size_t len;
373 size_t ie_len;
374
375 if (*data_len < 1)
376 goto fail;
377
378 len = (*data)[0];
379 if (len > *data_len - 1)
380 goto fail;
381
382 if (value)
383 *value = *data + 1;
384 if (value_len)
385 *value_len = len;
386
387 ie_len = len + 1;
388 *data += ie_len;
389 *data_len -= ie_len;
390
391 return ie_len;
392
393fail:
394 *data += *data_len;
395 *data_len = 0;
396 return -1;
397}
398
Sylvain Munautdca7d2c2012-04-18 21:53:23 +0200399/*! @} */