blob: d9072c9dd7f3ce9da987c1db3c306afc2668f25e [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>
Maxdbd3a922017-01-02 14:10:30 +010022#include <errno.h>
Pablo Neira Ayuso83419342011-03-22 16:36:13 +010023#include <osmocom/core/utils.h>
24#include <osmocom/gsm/tlv.h>
Harald Welteec8b4502010-02-20 20:34:29 +010025
Harald Welte57c7d372011-08-17 17:50:55 +020026/*! \addtogroup tlv
27 * @{
Neels Hofmeyr87e45502017-06-20 00:17:59 +020028 * Osmocom TLV Parser
Harald Welte96e2a002017-06-12 21:44:18 +020029 *
30 * The Osmocom TLV parser is intended to operate as a low-level C
31 * implementation without dynamic memory allocations. Basically, it
32 * iterates over the IE (Information Elements) of the message and fills
33 * an array of pointers, indexed by the IEI (IE Identifier). The
34 * parser output is thus an array of pointers to the start of the
35 * respective IE inside the message.
36 *
37 * The TLV parser is configured by a TLV parser definition, which
38 * determines which if the IEIs for a given protocol are of which
39 * particular type. Types are e.g. TV (Tag + single byte value), Tag +
40 * fixed-length value, TLV with 8bit length, TLV with 16bit length, TLV
41 * with variable-length length field, etc.
Harald Welte57c7d372011-08-17 17:50:55 +020042 */
Harald Welte96e2a002017-06-12 21:44:18 +020043
Katerina Barone-Adesic28c6a02013-02-15 13:27:59 +010044/*! \file tlv_parser.c */
Harald Welte57c7d372011-08-17 17:50:55 +020045
Harald Welteec8b4502010-02-20 20:34:29 +010046struct tlv_definition tvlv_att_def;
Harald Welte2fe68472012-07-14 01:50:33 +020047struct tlv_definition vtvlv_gan_att_def;
Harald Welteec8b4502010-02-20 20:34:29 +010048
Neels Hofmeyr87e45502017-06-20 00:17:59 +020049/*! Dump pasred TLV structure to stdout */
Harald Welteec8b4502010-02-20 20:34:29 +010050int tlv_dump(struct tlv_parsed *dec)
51{
52 int i;
53
54 for (i = 0; i <= 0xff; i++) {
55 if (!dec->lv[i].val)
56 continue;
57 printf("T=%02x L=%d\n", i, dec->lv[i].len);
58 }
59 return 0;
60}
61
Neels Hofmeyr87e45502017-06-20 00:17:59 +020062/*! Copy \ref tlv_parsed using given talloc context
Maxdbd3a922017-01-02 14:10:30 +010063 * \param[in] tp_orig Parsed TLV structure
64 * \param[in] ctx Talloc context for allocations
65 * \returns NULL on errors, \ref tlv_parsed pointer otherwise
66 */
67struct tlv_parsed *osmo_tlvp_copy(const struct tlv_parsed *tp_orig, void *ctx)
68{
69 struct tlv_parsed *tp_out;
70 size_t i, len;
71
72 tp_out = talloc_zero(ctx, struct tlv_parsed);
73 if (!tp_out)
74 return NULL;
75
76 /* if the original is NULL, return empty tlvp */
77 if (!tp_orig)
78 return tp_out;
79
80 for (i = 0; i < ARRAY_SIZE(tp_orig->lv); i++) {
81 len = tp_orig->lv[i].len;
82 tp_out->lv[i].len = len;
83 if (len && tp_out->lv[i].val) {
84 tp_out->lv[i].val = talloc_zero_size(tp_out, len);
85 if (!tp_out->lv[i].val) {
86 talloc_free(tp_out);
87 return NULL;
88 }
89 memcpy((uint8_t *)tp_out->lv[i].val, tp_orig->lv[i].val,
90 len);
91 }
92 }
93
94 return tp_out;
95}
96
Neels Hofmeyr87e45502017-06-20 00:17:59 +020097/*! Merge all \ref tlv_parsed attributes of 'src' into 'dst'
Maxdbd3a922017-01-02 14:10:30 +010098 * \param[in] dst Parsed TLV structure to merge into
99 * \param[in] src Parsed TLV structure to merge from
100 * \returns 0 on success, negative on error
101 */
102int osmo_tlvp_merge(struct tlv_parsed *dst, const struct tlv_parsed *src)
103{
104 size_t i, len;
105 for (i = 0; i < ARRAY_SIZE(dst->lv); i++) {
106 len = src->lv[i].len;
107 if (len == 0 || src->lv[i].val == NULL)
108 continue;
109 if (dst->lv[i].val) {
110 talloc_free((uint8_t *) dst->lv[i].val);
111 dst->lv[i].len = 0;
112 }
113 dst->lv[i].val = talloc_zero_size(dst, len);
114 if (!dst->lv[i].val)
115 return -ENOMEM;
116 memcpy((uint8_t *) dst->lv[i].val, src->lv[i].val, len);
117 }
118 return 0;
119}
120
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200121/*! Parse a single TLV encoded IE
Harald Welte57c7d372011-08-17 17:50:55 +0200122 * \param[out] o_tag the tag of the IE that was found
123 * \param[out] o_len length of the IE that was found
124 * \param[out] o_val pointer to the data of the IE that was found
125 * \param[in] def structure defining the valid TLV tags / configurations
126 * \param[in] buf the input data buffer to be parsed
127 * \param[in] buf_len length of the input data buffer
128 * \returns number of bytes consumed by the TLV entry / IE parsed
Harald Welteec8b4502010-02-20 20:34:29 +0100129 */
130int tlv_parse_one(uint8_t *o_tag, uint16_t *o_len, const uint8_t **o_val,
131 const struct tlv_definition *def,
132 const uint8_t *buf, int buf_len)
133{
134 uint8_t tag;
135 int len;
136
137 tag = *buf;
138 *o_tag = tag;
139
Andreas Eversbergcd2a74b2010-07-12 08:55:14 +0200140 /* single octet TV IE */
141 if (def->def[tag & 0xf0].type == TLV_TYPE_SINGLE_TV) {
142 *o_tag = tag & 0xf0;
143 *o_val = buf;
144 *o_len = 1;
145 return 1;
146 }
147
Neels Hofmeyr667e83d2015-11-02 20:18:11 +0100148 /* FIXME: use tables for known IEI */
Harald Welteec8b4502010-02-20 20:34:29 +0100149 switch (def->def[tag].type) {
150 case TLV_TYPE_T:
151 /* GSM TS 04.07 11.2.4: Type 1 TV or Type 2 T */
152 *o_val = buf;
153 *o_len = 0;
154 len = 1;
155 break;
156 case TLV_TYPE_TV:
157 *o_val = buf+1;
158 *o_len = 1;
159 len = 2;
160 break;
161 case TLV_TYPE_FIXED:
162 *o_val = buf+1;
163 *o_len = def->def[tag].fixed_len;
164 len = def->def[tag].fixed_len + 1;
165 break;
166 case TLV_TYPE_TLV:
Harald Welte2fe68472012-07-14 01:50:33 +0200167tlv: /* GSM TS 04.07 11.2.4: Type 4 TLV */
Harald Welteec8b4502010-02-20 20:34:29 +0100168 if (buf + 1 > buf + buf_len)
169 return -1;
170 *o_val = buf+2;
171 *o_len = *(buf+1);
172 len = *o_len + 2;
173 if (len > buf_len)
174 return -2;
175 break;
Harald Welte2fe68472012-07-14 01:50:33 +0200176 case TLV_TYPE_vTvLV_GAN: /* 44.318 / 11.1.4 */
177 /* FIXME: variable-length TAG! */
178 if (*(buf+1) & 0x80) {
179 /* like TL16Vbut without highest bit of len */
180 if (2 > buf_len)
181 return -1;
182 *o_val = buf+3;
183 *o_len = (*(buf+1) & 0x7F) << 8 | *(buf+2);
184 len = *o_len + 3;
185 if (len > buf_len)
186 return -2;
187 } else {
188 /* like TLV */
189 goto tlv;
190 }
191 break;
Harald Welteec8b4502010-02-20 20:34:29 +0100192 case TLV_TYPE_TvLV:
193 if (*(buf+1) & 0x80) {
194 /* like TLV, but without highest bit of len */
195 if (buf + 1 > buf + buf_len)
196 return -1;
197 *o_val = buf+2;
198 *o_len = *(buf+1) & 0x7f;
199 len = *o_len + 2;
200 if (len > buf_len)
201 return -2;
202 break;
203 }
204 /* like TL16V, fallthrough */
205 case TLV_TYPE_TL16V:
206 if (2 > buf_len)
207 return -1;
208 *o_val = buf+3;
209 *o_len = *(buf+1) << 8 | *(buf+2);
210 len = *o_len + 3;
211 if (len > buf_len)
212 return -2;
213 break;
214 default:
215 return -3;
216 }
217
218 return len;
219}
220
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200221/*! Parse an entire buffer of TLV encoded Information Elements
Harald Welte57c7d372011-08-17 17:50:55 +0200222 * \param[out] dec caller-allocated pointer to \ref tlv_parsed
223 * \param[in] def structure defining the valid TLV tags / configurations
224 * \param[in] buf the input data buffer to be parsed
225 * \param[in] buf_len length of the input data buffer
226 * \param[in] lv_tag an initial LV tag at the start of the buffer
227 * \param[in] lv_tag2 a second initial LV tag following the \a lv_tag
228 * \returns number of bytes consumed by the TLV entry / IE parsed
Harald Welteec8b4502010-02-20 20:34:29 +0100229 */
230int tlv_parse(struct tlv_parsed *dec, const struct tlv_definition *def,
231 const uint8_t *buf, int buf_len, uint8_t lv_tag,
232 uint8_t lv_tag2)
233{
234 int ofs = 0, num_parsed = 0;
235 uint16_t len;
236
237 memset(dec, 0, sizeof(*dec));
238
239 if (lv_tag) {
240 if (ofs > buf_len)
241 return -1;
242 dec->lv[lv_tag].val = &buf[ofs+1];
243 dec->lv[lv_tag].len = buf[ofs];
244 len = dec->lv[lv_tag].len + 1;
245 if (ofs + len > buf_len)
246 return -2;
247 num_parsed++;
248 ofs += len;
249 }
250 if (lv_tag2) {
251 if (ofs > buf_len)
252 return -1;
253 dec->lv[lv_tag2].val = &buf[ofs+1];
254 dec->lv[lv_tag2].len = buf[ofs];
255 len = dec->lv[lv_tag2].len + 1;
256 if (ofs + len > buf_len)
257 return -2;
258 num_parsed++;
259 ofs += len;
260 }
261
262 while (ofs < buf_len) {
263 int rv;
264 uint8_t tag;
265 const uint8_t *val;
266
267 rv = tlv_parse_one(&tag, &len, &val, def,
268 &buf[ofs], buf_len-ofs);
269 if (rv < 0)
270 return rv;
271 dec->lv[tag].val = val;
272 dec->lv[tag].len = len;
273 ofs += rv;
274 num_parsed++;
275 }
276 //tlv_dump(dec);
277 return num_parsed;
278}
279
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200280/*! take a master (src) tlvdev and fill up all empty slots in 'dst'
Harald Welte96e2a002017-06-12 21:44:18 +0200281 * \param dst TLV parser definition that is to be patched
282 * \param[in] src TLV parser definition whose content is patched into \a dst */
Harald Welteec8b4502010-02-20 20:34:29 +0100283void tlv_def_patch(struct tlv_definition *dst, const struct tlv_definition *src)
284{
285 int i;
286
287 for (i = 0; i < ARRAY_SIZE(dst->def); i++) {
288 if (src->def[i].type == TLV_TYPE_NONE)
289 continue;
290 if (dst->def[i].type == TLV_TYPE_NONE)
291 dst->def[i] = src->def[i];
292 }
293}
294
295static __attribute__((constructor)) void on_dso_load_tlv(void)
296{
297 int i;
298 for (i = 0; i < ARRAY_SIZE(tvlv_att_def.def); i++)
299 tvlv_att_def.def[i].type = TLV_TYPE_TvLV;
Harald Welte2fe68472012-07-14 01:50:33 +0200300
301 for (i = 0; i < ARRAY_SIZE(vtvlv_gan_att_def.def); i++)
302 vtvlv_gan_att_def.def[i].type = TLV_TYPE_vTvLV_GAN;
Harald Welteec8b4502010-02-20 20:34:29 +0100303}
Harald Welte57c7d372011-08-17 17:50:55 +0200304
Harald Weltefbd02fa2016-04-25 15:19:35 +0200305/*! Advance the data pointer, subtract length and assign value pointer
306 * \param data pointer to the pointer to data
307 * \param data_len pointer to size_t containing \arg data length
308 * \param[in] len the length that we expect the fixed IE to hav
309 * \param[out] value pointer to pointer of value part of IE
310 * \returns length of IE value; negative in case of error
311 */
312int osmo_shift_v_fixed(uint8_t **data, size_t *data_len,
313 size_t len, uint8_t **value)
314{
315 if (len > *data_len)
316 goto fail;
317
318 if (value)
319 *value = *data;
320
321 *data += len;
322 *data_len -= len;
323
324 return len;
325
326fail:
327 *data += *data_len;
328 *data_len = 0;
329 return -1;
330}
331
332/*! Match tag, check length and assign value pointer
333 * \param data pointer to the pointer to data
334 * \param data_len pointer to size_t containing \arg data length
335 * \param[in] tag the tag (IEI) that we expect at \arg data
336 * \param[in] len the length that we expect the fixed IE to have
337 * \param[out] value pointer to pointer of value part of IE
338 * \returns length of IE value; negative in case of error
339 */
340int osmo_match_shift_tv_fixed(uint8_t **data, size_t *data_len,
341 uint8_t tag, size_t len,
342 uint8_t **value)
343{
344 size_t ie_len;
345
346 if (*data_len == 0)
347 goto fail;
348
349 if ((*data)[0] != tag)
350 return 0;
351
352 if (len > *data_len - 1)
353 goto fail;
354
355 if (value)
356 *value = *data + 1;
357
358 ie_len = len + 1;
359 *data += ie_len;
360 *data_len -= ie_len;
361
362 return ie_len;
363
364fail:
365 *data += *data_len;
366 *data_len = 0;
367 return -1;
368}
369
370/*! Verify TLV header and advance data / subtract length
371 * \param data pointer to the pointer to data
372 * \param data_len pointer to size_t containing \arg data length
373 * \param[in] expected_tag the tag (IEI) that we expect at \arg data
374 * \param[out] value pointer to pointer of value part of IE
375 * \param[out] value_len pointer to length of \arg value
376 * \returns length of IE value; negative in case of error
377 */
378int osmo_match_shift_tlv(uint8_t **data, size_t *data_len,
379 uint8_t expected_tag, uint8_t **value,
380 size_t *value_len)
381{
382 int rc;
383 uint8_t tag;
384 uint8_t *old_data = *data;
385 size_t old_data_len = *data_len;
386
387 rc = osmo_shift_tlv(data, data_len, &tag, value, value_len);
388
389 if (rc > 0 && tag != expected_tag) {
390 *data = old_data;
391 *data_len = old_data_len;
392 return 0;
393 }
394
395 return rc;
396}
397
398/*! Extract TLV and advance data pointer + subtract length
399 * \param data pointer to the pointer to data
400 * \param data_len pointer to size_t containing \arg data lengt
401 * \param[out] tag extract the tag (IEI) at start of \arg data
402 * \param[out] value extracted pointer to value part of TLV
403 * \param[out] value_len extracted length of \arg value
404 * \returns number of bytes subtracted
405 */
406int osmo_shift_tlv(uint8_t **data, size_t *data_len,
407 uint8_t *tag, uint8_t **value, size_t *value_len)
408{
409 size_t len;
410 size_t ie_len;
411
412 if (*data_len < 2)
413 goto fail;
414
415 len = (*data)[1];
416 if (len > *data_len - 2)
417 goto fail;
418
419 if (tag)
420 *tag = (*data)[0];
421 if (value)
422 *value = *data + 2;
423 if (value_len)
424 *value_len = len;
425
426 ie_len = len + 2;
427
428 *data += ie_len;
429 *data_len -= ie_len;
430
431 return ie_len;
432
433fail:
434 *data += *data_len;
435 *data_len = 0;
436 return -1;
437}
438
439/*! Extract LV and advance data pointer + subtract length
440 * \param data pointer to the pointer to data
441 * \param data_len pointer to size_t containing \arg data lengt
442 * \param[out] value extracted pointer to value part of TLV
443 * \param[out] value_len extracted length of \arg value
444 * \returns number of bytes subtracted
445 */
446int osmo_shift_lv(uint8_t **data, size_t *data_len,
447 uint8_t **value, size_t *value_len)
448{
449 size_t len;
450 size_t ie_len;
451
452 if (*data_len < 1)
453 goto fail;
454
455 len = (*data)[0];
456 if (len > *data_len - 1)
457 goto fail;
458
459 if (value)
460 *value = *data + 1;
461 if (value_len)
462 *value_len = len;
463
464 ie_len = len + 1;
465 *data += ie_len;
466 *data_len -= ie_len;
467
468 return ie_len;
469
470fail:
471 *data += *data_len;
472 *data_len = 0;
473 return -1;
474}
475
Sylvain Munautdca7d2c2012-04-18 21:53:23 +0200476/*! @} */