blob: 6e089f7fd3aa43e09ccc3a48deb9ebd3c7f398f3 [file] [log] [blame]
Harald Weltee08da972017-11-13 01:00:26 +09001/* (C) 2008-2017 by Harald Welte <laforge@gnumonks.org>
2 * (C) 2016-2017 by sysmocom - s.f.m.c. GmbH
Harald Welte468b6432014-09-11 13:05:51 +08003 *
4 * All Rights Reserved
5 *
Harald Weltee08da972017-11-13 01:00:26 +09006 * SPDX-License-Identifier: GPL-2.0+
7 *
Harald Welte468b6432014-09-11 13:05:51 +08008 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 *
21 */
22
Harald Welteec8b4502010-02-20 20:34:29 +010023#include <stdio.h>
24#include <stdint.h>
Maxdbd3a922017-01-02 14:10:30 +010025#include <errno.h>
Pablo Neira Ayuso83419342011-03-22 16:36:13 +010026#include <osmocom/core/utils.h>
27#include <osmocom/gsm/tlv.h>
Harald Welteec8b4502010-02-20 20:34:29 +010028
Harald Welte57c7d372011-08-17 17:50:55 +020029/*! \addtogroup tlv
30 * @{
Neels Hofmeyr87e45502017-06-20 00:17:59 +020031 * Osmocom TLV Parser
Harald Welte96e2a002017-06-12 21:44:18 +020032 *
33 * The Osmocom TLV parser is intended to operate as a low-level C
34 * implementation without dynamic memory allocations. Basically, it
35 * iterates over the IE (Information Elements) of the message and fills
36 * an array of pointers, indexed by the IEI (IE Identifier). The
37 * parser output is thus an array of pointers to the start of the
38 * respective IE inside the message.
39 *
40 * The TLV parser is configured by a TLV parser definition, which
41 * determines which if the IEIs for a given protocol are of which
42 * particular type. Types are e.g. TV (Tag + single byte value), Tag +
43 * fixed-length value, TLV with 8bit length, TLV with 16bit length, TLV
44 * with variable-length length field, etc.
Neels Hofmeyr17518fe2017-06-20 04:35:06 +020045 *
46 * \file tlv_parser.c */
Harald Welte57c7d372011-08-17 17:50:55 +020047
Harald Welteec8b4502010-02-20 20:34:29 +010048struct tlv_definition tvlv_att_def;
Harald Welte2fe68472012-07-14 01:50:33 +020049struct tlv_definition vtvlv_gan_att_def;
Harald Welteec8b4502010-02-20 20:34:29 +010050
Stefan Sperlingc9bebbd2018-03-16 15:59:01 +010051/*! Dump parsed TLV structure to stdout */
Harald Welteec8b4502010-02-20 20:34:29 +010052int tlv_dump(struct tlv_parsed *dec)
53{
54 int i;
55
56 for (i = 0; i <= 0xff; i++) {
57 if (!dec->lv[i].val)
58 continue;
59 printf("T=%02x L=%d\n", i, dec->lv[i].len);
60 }
61 return 0;
62}
63
Neels Hofmeyr87e45502017-06-20 00:17:59 +020064/*! Copy \ref tlv_parsed using given talloc context
Maxdbd3a922017-01-02 14:10:30 +010065 * \param[in] tp_orig Parsed TLV structure
66 * \param[in] ctx Talloc context for allocations
67 * \returns NULL on errors, \ref tlv_parsed pointer otherwise
68 */
69struct tlv_parsed *osmo_tlvp_copy(const struct tlv_parsed *tp_orig, void *ctx)
70{
71 struct tlv_parsed *tp_out;
72 size_t i, len;
73
74 tp_out = talloc_zero(ctx, struct tlv_parsed);
75 if (!tp_out)
76 return NULL;
77
78 /* if the original is NULL, return empty tlvp */
79 if (!tp_orig)
80 return tp_out;
81
82 for (i = 0; i < ARRAY_SIZE(tp_orig->lv); i++) {
83 len = tp_orig->lv[i].len;
84 tp_out->lv[i].len = len;
85 if (len && tp_out->lv[i].val) {
86 tp_out->lv[i].val = talloc_zero_size(tp_out, len);
87 if (!tp_out->lv[i].val) {
88 talloc_free(tp_out);
89 return NULL;
90 }
91 memcpy((uint8_t *)tp_out->lv[i].val, tp_orig->lv[i].val,
92 len);
93 }
94 }
95
96 return tp_out;
97}
98
Neels Hofmeyr87e45502017-06-20 00:17:59 +020099/*! Merge all \ref tlv_parsed attributes of 'src' into 'dst'
Maxdbd3a922017-01-02 14:10:30 +0100100 * \param[in] dst Parsed TLV structure to merge into
101 * \param[in] src Parsed TLV structure to merge from
102 * \returns 0 on success, negative on error
103 */
104int osmo_tlvp_merge(struct tlv_parsed *dst, const struct tlv_parsed *src)
105{
106 size_t i, len;
107 for (i = 0; i < ARRAY_SIZE(dst->lv); i++) {
108 len = src->lv[i].len;
109 if (len == 0 || src->lv[i].val == NULL)
110 continue;
111 if (dst->lv[i].val) {
112 talloc_free((uint8_t *) dst->lv[i].val);
113 dst->lv[i].len = 0;
114 }
115 dst->lv[i].val = talloc_zero_size(dst, len);
116 if (!dst->lv[i].val)
117 return -ENOMEM;
118 memcpy((uint8_t *) dst->lv[i].val, src->lv[i].val, len);
119 }
120 return 0;
121}
122
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200123/*! Parse a single TLV encoded IE
Harald Welte57c7d372011-08-17 17:50:55 +0200124 * \param[out] o_tag the tag of the IE that was found
125 * \param[out] o_len length of the IE that was found
126 * \param[out] o_val pointer to the data of the IE that was found
127 * \param[in] def structure defining the valid TLV tags / configurations
128 * \param[in] buf the input data buffer to be parsed
129 * \param[in] buf_len length of the input data buffer
Stefan Sperling1e50e2a2018-01-08 19:20:02 +0100130 * \returns number of bytes consumed by the TLV entry / IE parsed; negative in case of error
Harald Welteec8b4502010-02-20 20:34:29 +0100131 */
132int tlv_parse_one(uint8_t *o_tag, uint16_t *o_len, const uint8_t **o_val,
133 const struct tlv_definition *def,
134 const uint8_t *buf, int buf_len)
135{
136 uint8_t tag;
137 int len;
138
139 tag = *buf;
140 *o_tag = tag;
141
Andreas Eversbergcd2a74b2010-07-12 08:55:14 +0200142 /* single octet TV IE */
143 if (def->def[tag & 0xf0].type == TLV_TYPE_SINGLE_TV) {
144 *o_tag = tag & 0xf0;
145 *o_val = buf;
146 *o_len = 1;
147 return 1;
148 }
149
Neels Hofmeyr667e83d2015-11-02 20:18:11 +0100150 /* FIXME: use tables for known IEI */
Harald Welteec8b4502010-02-20 20:34:29 +0100151 switch (def->def[tag].type) {
152 case TLV_TYPE_T:
153 /* GSM TS 04.07 11.2.4: Type 1 TV or Type 2 T */
154 *o_val = buf;
155 *o_len = 0;
156 len = 1;
157 break;
158 case TLV_TYPE_TV:
159 *o_val = buf+1;
160 *o_len = 1;
161 len = 2;
162 break;
163 case TLV_TYPE_FIXED:
164 *o_val = buf+1;
165 *o_len = def->def[tag].fixed_len;
166 len = def->def[tag].fixed_len + 1;
167 break;
168 case TLV_TYPE_TLV:
Harald Welte2fe68472012-07-14 01:50:33 +0200169tlv: /* GSM TS 04.07 11.2.4: Type 4 TLV */
Harald Welteec8b4502010-02-20 20:34:29 +0100170 if (buf + 1 > buf + buf_len)
171 return -1;
172 *o_val = buf+2;
173 *o_len = *(buf+1);
174 len = *o_len + 2;
175 if (len > buf_len)
176 return -2;
177 break;
Harald Welte2fe68472012-07-14 01:50:33 +0200178 case TLV_TYPE_vTvLV_GAN: /* 44.318 / 11.1.4 */
179 /* FIXME: variable-length TAG! */
180 if (*(buf+1) & 0x80) {
181 /* like TL16Vbut without highest bit of len */
182 if (2 > buf_len)
183 return -1;
184 *o_val = buf+3;
185 *o_len = (*(buf+1) & 0x7F) << 8 | *(buf+2);
186 len = *o_len + 3;
187 if (len > buf_len)
188 return -2;
189 } else {
190 /* like TLV */
191 goto tlv;
192 }
193 break;
Harald Welteec8b4502010-02-20 20:34:29 +0100194 case TLV_TYPE_TvLV:
195 if (*(buf+1) & 0x80) {
196 /* like TLV, but without highest bit of len */
197 if (buf + 1 > buf + buf_len)
198 return -1;
199 *o_val = buf+2;
200 *o_len = *(buf+1) & 0x7f;
201 len = *o_len + 2;
202 if (len > buf_len)
203 return -2;
204 break;
205 }
206 /* like TL16V, fallthrough */
207 case TLV_TYPE_TL16V:
208 if (2 > buf_len)
209 return -1;
210 *o_val = buf+3;
211 *o_len = *(buf+1) << 8 | *(buf+2);
212 len = *o_len + 3;
213 if (len > buf_len)
214 return -2;
215 break;
216 default:
217 return -3;
218 }
219
220 return len;
221}
222
Neels Hofmeyra78b22b2018-04-13 03:36:49 +0200223/*! Parse an entire buffer of TLV encoded Information Elements.
224 * In case of multiple occurences of an IE, keep only the first occurence.
225 * Most GSM related protocols clearly indicate that in case of duplicate
226 * IEs, only the first occurrence shall be used, while any further occurrences
227 * shall be ignored. See e.g. 3GPP TS 24.008 Section 8.6.3.
228 * For multiple occurences, use tlv_parse2().
Harald Welte57c7d372011-08-17 17:50:55 +0200229 * \param[out] dec caller-allocated pointer to \ref tlv_parsed
230 * \param[in] def structure defining the valid TLV tags / configurations
231 * \param[in] buf the input data buffer to be parsed
232 * \param[in] buf_len length of the input data buffer
233 * \param[in] lv_tag an initial LV tag at the start of the buffer
234 * \param[in] lv_tag2 a second initial LV tag following the \a lv_tag
Stefan Sperlingc9bebbd2018-03-16 15:59:01 +0100235 * \returns number of TLV entries parsed; negative in case of error
Harald Welteec8b4502010-02-20 20:34:29 +0100236 */
237int tlv_parse(struct tlv_parsed *dec, const struct tlv_definition *def,
238 const uint8_t *buf, int buf_len, uint8_t lv_tag,
239 uint8_t lv_tag2)
240{
Neels Hofmeyra78b22b2018-04-13 03:36:49 +0200241 return tlv_parse2(dec, 1, def, buf, buf_len, lv_tag, lv_tag2);
242}
243
244/*! Like tlv_parse(), but capable of decoding multiple occurences of the same IE.
245 * Parse an entire buffer of TLV encoded Information Elements.
246 * To decode multiple occurences of IEs, provide in dec an _array_ of tlv_parsed, and
247 * pass the size of that array in dec_multiples. The first occurence of each IE
248 * is stored in dec[0], the second in dec[1] and so forth. If there are more
249 * occurences than the array length given in dec_multiples, the remaining
250 * occurences are dropped.
251 * \param[out] dec caller-allocated pointer to \ref tlv_parsed
252 * \param[in] dec_multiples length of the tlv_parsed[] in \a dec.
253 * \param[in] def structure defining the valid TLV tags / configurations
254 * \param[in] buf the input data buffer to be parsed
255 * \param[in] buf_len length of the input data buffer
256 * \param[in] lv_tag an initial LV tag at the start of the buffer
257 * \param[in] lv_tag2 a second initial LV tag following the \a lv_tag
258 * \returns number of TLV entries parsed; negative in case of error
259 */
260int tlv_parse2(struct tlv_parsed *dec, int dec_multiples,
261 const struct tlv_definition *def, const uint8_t *buf, int buf_len,
262 uint8_t lv_tag, uint8_t lv_tag2)
263{
Harald Welteec8b4502010-02-20 20:34:29 +0100264 int ofs = 0, num_parsed = 0;
265 uint16_t len;
Neels Hofmeyra78b22b2018-04-13 03:36:49 +0200266 int dec_i;
Harald Welteec8b4502010-02-20 20:34:29 +0100267
Neels Hofmeyra78b22b2018-04-13 03:36:49 +0200268 for (dec_i = 0; dec_i < dec_multiples; dec_i++)
269 memset(&dec[dec_i], 0, sizeof(*dec));
Harald Welteec8b4502010-02-20 20:34:29 +0100270
271 if (lv_tag) {
Neels Hofmeyra78b22b2018-04-13 03:36:49 +0200272 const uint8_t *val;
273 uint16_t parsed_len;
Harald Welteec8b4502010-02-20 20:34:29 +0100274 if (ofs > buf_len)
275 return -1;
Neels Hofmeyra78b22b2018-04-13 03:36:49 +0200276 val = &buf[ofs+1];
277 len = buf[ofs];
278 parsed_len = len + 1;
279 if (ofs + parsed_len > buf_len)
Harald Welteec8b4502010-02-20 20:34:29 +0100280 return -2;
281 num_parsed++;
Neels Hofmeyra78b22b2018-04-13 03:36:49 +0200282 ofs += parsed_len;
283 /* store the resulting val and len */
284 for (dec_i = 0; dec_i < dec_multiples; dec_i++) {
285 if (dec[dec_i].lv[lv_tag].val != NULL)
286 continue;
287 dec->lv[lv_tag].val = val;
288 dec->lv[lv_tag].len = len;
289 break;
290 }
Harald Welteec8b4502010-02-20 20:34:29 +0100291 }
292 if (lv_tag2) {
Neels Hofmeyra78b22b2018-04-13 03:36:49 +0200293 const uint8_t *val;
294 uint16_t parsed_len;
Harald Welteec8b4502010-02-20 20:34:29 +0100295 if (ofs > buf_len)
296 return -1;
Neels Hofmeyra78b22b2018-04-13 03:36:49 +0200297 val = &buf[ofs+1];
298 len = buf[ofs];
299 parsed_len = len + 1;
300 if (ofs + parsed_len > buf_len)
Harald Welteec8b4502010-02-20 20:34:29 +0100301 return -2;
302 num_parsed++;
Neels Hofmeyra78b22b2018-04-13 03:36:49 +0200303 ofs += parsed_len;
304 /* store the resulting val and len */
305 for (dec_i = 0; dec_i < dec_multiples; dec_i++) {
306 if (dec[dec_i].lv[lv_tag2].val != NULL)
307 continue;
308 dec->lv[lv_tag2].val = val;
309 dec->lv[lv_tag2].len = len;
310 break;
311 }
Harald Welteec8b4502010-02-20 20:34:29 +0100312 }
313
314 while (ofs < buf_len) {
315 int rv;
316 uint8_t tag;
317 const uint8_t *val;
318
319 rv = tlv_parse_one(&tag, &len, &val, def,
320 &buf[ofs], buf_len-ofs);
321 if (rv < 0)
322 return rv;
Neels Hofmeyra78b22b2018-04-13 03:36:49 +0200323 for (dec_i = 0; dec_i < dec_multiples; dec_i++) {
324 if (dec[dec_i].lv[tag].val != NULL)
325 continue;
326 dec[dec_i].lv[tag].val = val;
327 dec[dec_i].lv[tag].len = len;
328 break;
Harald Weltebf383a12018-02-02 12:11:14 +0100329 }
Harald Welteec8b4502010-02-20 20:34:29 +0100330 ofs += rv;
331 num_parsed++;
332 }
333 //tlv_dump(dec);
334 return num_parsed;
335}
336
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200337/*! take a master (src) tlvdev and fill up all empty slots in 'dst'
Harald Welte96e2a002017-06-12 21:44:18 +0200338 * \param dst TLV parser definition that is to be patched
339 * \param[in] src TLV parser definition whose content is patched into \a dst */
Harald Welteec8b4502010-02-20 20:34:29 +0100340void tlv_def_patch(struct tlv_definition *dst, const struct tlv_definition *src)
341{
342 int i;
343
344 for (i = 0; i < ARRAY_SIZE(dst->def); i++) {
345 if (src->def[i].type == TLV_TYPE_NONE)
346 continue;
347 if (dst->def[i].type == TLV_TYPE_NONE)
348 dst->def[i] = src->def[i];
349 }
350}
351
352static __attribute__((constructor)) void on_dso_load_tlv(void)
353{
354 int i;
355 for (i = 0; i < ARRAY_SIZE(tvlv_att_def.def); i++)
356 tvlv_att_def.def[i].type = TLV_TYPE_TvLV;
Harald Welte2fe68472012-07-14 01:50:33 +0200357
358 for (i = 0; i < ARRAY_SIZE(vtvlv_gan_att_def.def); i++)
359 vtvlv_gan_att_def.def[i].type = TLV_TYPE_vTvLV_GAN;
Harald Welteec8b4502010-02-20 20:34:29 +0100360}
Harald Welte57c7d372011-08-17 17:50:55 +0200361
Harald Weltefbd02fa2016-04-25 15:19:35 +0200362/*! Advance the data pointer, subtract length and assign value pointer
363 * \param data pointer to the pointer to data
364 * \param data_len pointer to size_t containing \arg data length
365 * \param[in] len the length that we expect the fixed IE to hav
366 * \param[out] value pointer to pointer of value part of IE
367 * \returns length of IE value; negative in case of error
368 */
369int osmo_shift_v_fixed(uint8_t **data, size_t *data_len,
370 size_t len, uint8_t **value)
371{
372 if (len > *data_len)
373 goto fail;
374
375 if (value)
376 *value = *data;
377
378 *data += len;
379 *data_len -= len;
380
381 return len;
382
383fail:
384 *data += *data_len;
385 *data_len = 0;
386 return -1;
387}
388
389/*! Match tag, check length and assign value pointer
390 * \param data pointer to the pointer to data
391 * \param data_len pointer to size_t containing \arg data length
392 * \param[in] tag the tag (IEI) that we expect at \arg data
393 * \param[in] len the length that we expect the fixed IE to have
394 * \param[out] value pointer to pointer of value part of IE
395 * \returns length of IE value; negative in case of error
396 */
397int osmo_match_shift_tv_fixed(uint8_t **data, size_t *data_len,
398 uint8_t tag, size_t len,
399 uint8_t **value)
400{
401 size_t ie_len;
402
403 if (*data_len == 0)
404 goto fail;
405
406 if ((*data)[0] != tag)
407 return 0;
408
409 if (len > *data_len - 1)
410 goto fail;
411
412 if (value)
413 *value = *data + 1;
414
415 ie_len = len + 1;
416 *data += ie_len;
417 *data_len -= ie_len;
418
419 return ie_len;
420
421fail:
422 *data += *data_len;
423 *data_len = 0;
424 return -1;
425}
426
427/*! Verify TLV header and advance data / subtract length
428 * \param data pointer to the pointer to data
429 * \param data_len pointer to size_t containing \arg data length
430 * \param[in] expected_tag the tag (IEI) that we expect at \arg data
431 * \param[out] value pointer to pointer of value part of IE
432 * \param[out] value_len pointer to length of \arg value
433 * \returns length of IE value; negative in case of error
434 */
435int osmo_match_shift_tlv(uint8_t **data, size_t *data_len,
436 uint8_t expected_tag, uint8_t **value,
437 size_t *value_len)
438{
439 int rc;
440 uint8_t tag;
441 uint8_t *old_data = *data;
442 size_t old_data_len = *data_len;
443
444 rc = osmo_shift_tlv(data, data_len, &tag, value, value_len);
445
446 if (rc > 0 && tag != expected_tag) {
447 *data = old_data;
448 *data_len = old_data_len;
449 return 0;
450 }
451
452 return rc;
453}
454
455/*! Extract TLV and advance data pointer + subtract length
456 * \param data pointer to the pointer to data
457 * \param data_len pointer to size_t containing \arg data lengt
458 * \param[out] tag extract the tag (IEI) at start of \arg data
459 * \param[out] value extracted pointer to value part of TLV
460 * \param[out] value_len extracted length of \arg value
461 * \returns number of bytes subtracted
462 */
463int osmo_shift_tlv(uint8_t **data, size_t *data_len,
464 uint8_t *tag, uint8_t **value, size_t *value_len)
465{
466 size_t len;
467 size_t ie_len;
468
469 if (*data_len < 2)
470 goto fail;
471
472 len = (*data)[1];
473 if (len > *data_len - 2)
474 goto fail;
475
476 if (tag)
477 *tag = (*data)[0];
478 if (value)
479 *value = *data + 2;
480 if (value_len)
481 *value_len = len;
482
483 ie_len = len + 2;
484
485 *data += ie_len;
486 *data_len -= ie_len;
487
488 return ie_len;
489
490fail:
491 *data += *data_len;
492 *data_len = 0;
493 return -1;
494}
495
496/*! Extract LV and advance data pointer + subtract length
497 * \param data pointer to the pointer to data
498 * \param data_len pointer to size_t containing \arg data lengt
499 * \param[out] value extracted pointer to value part of TLV
500 * \param[out] value_len extracted length of \arg value
501 * \returns number of bytes subtracted
502 */
503int osmo_shift_lv(uint8_t **data, size_t *data_len,
504 uint8_t **value, size_t *value_len)
505{
506 size_t len;
507 size_t ie_len;
508
509 if (*data_len < 1)
510 goto fail;
511
512 len = (*data)[0];
513 if (len > *data_len - 1)
514 goto fail;
515
516 if (value)
517 *value = *data + 1;
518 if (value_len)
519 *value_len = len;
520
521 ie_len = len + 1;
522 *data += ie_len;
523 *data_len -= ie_len;
524
525 return ie_len;
526
527fail:
528 *data += *data_len;
529 *data_len = 0;
530 return -1;
531}
532
Sylvain Munautdca7d2c2012-04-18 21:53:23 +0200533/*! @} */