blob: 4cc43f67751a35a361c394c4aced900554476a73 [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 * @{
28 */
Katerina Barone-Adesic28c6a02013-02-15 13:27:59 +010029/*! \file tlv_parser.c */
Harald Welte57c7d372011-08-17 17:50:55 +020030
Harald Welteec8b4502010-02-20 20:34:29 +010031struct tlv_definition tvlv_att_def;
Harald Welte2fe68472012-07-14 01:50:33 +020032struct tlv_definition vtvlv_gan_att_def;
Harald Welteec8b4502010-02-20 20:34:29 +010033
Harald Welte57c7d372011-08-17 17:50:55 +020034/*! \brief Dump pasred TLV structure to stdout */
Harald Welteec8b4502010-02-20 20:34:29 +010035int tlv_dump(struct tlv_parsed *dec)
36{
37 int i;
38
39 for (i = 0; i <= 0xff; i++) {
40 if (!dec->lv[i].val)
41 continue;
42 printf("T=%02x L=%d\n", i, dec->lv[i].len);
43 }
44 return 0;
45}
46
Maxdbd3a922017-01-02 14:10:30 +010047/*! \brief Copy \ref tlv_parsed using given talloc context
48 * \param[in] tp_orig Parsed TLV structure
49 * \param[in] ctx Talloc context for allocations
50 * \returns NULL on errors, \ref tlv_parsed pointer otherwise
51 */
52struct tlv_parsed *osmo_tlvp_copy(const struct tlv_parsed *tp_orig, void *ctx)
53{
54 struct tlv_parsed *tp_out;
55 size_t i, len;
56
57 tp_out = talloc_zero(ctx, struct tlv_parsed);
58 if (!tp_out)
59 return NULL;
60
61 /* if the original is NULL, return empty tlvp */
62 if (!tp_orig)
63 return tp_out;
64
65 for (i = 0; i < ARRAY_SIZE(tp_orig->lv); i++) {
66 len = tp_orig->lv[i].len;
67 tp_out->lv[i].len = len;
68 if (len && tp_out->lv[i].val) {
69 tp_out->lv[i].val = talloc_zero_size(tp_out, len);
70 if (!tp_out->lv[i].val) {
71 talloc_free(tp_out);
72 return NULL;
73 }
74 memcpy((uint8_t *)tp_out->lv[i].val, tp_orig->lv[i].val,
75 len);
76 }
77 }
78
79 return tp_out;
80}
81
82/*! \brief Merge all \ref tlv_parsed attributes of 'src' into 'dst'
83 * \param[in] dst Parsed TLV structure to merge into
84 * \param[in] src Parsed TLV structure to merge from
85 * \returns 0 on success, negative on error
86 */
87int osmo_tlvp_merge(struct tlv_parsed *dst, const struct tlv_parsed *src)
88{
89 size_t i, len;
90 for (i = 0; i < ARRAY_SIZE(dst->lv); i++) {
91 len = src->lv[i].len;
92 if (len == 0 || src->lv[i].val == NULL)
93 continue;
94 if (dst->lv[i].val) {
95 talloc_free((uint8_t *) dst->lv[i].val);
96 dst->lv[i].len = 0;
97 }
98 dst->lv[i].val = talloc_zero_size(dst, len);
99 if (!dst->lv[i].val)
100 return -ENOMEM;
101 memcpy((uint8_t *) dst->lv[i].val, src->lv[i].val, len);
102 }
103 return 0;
104}
105
Harald Welte57c7d372011-08-17 17:50:55 +0200106/*! \brief Parse a single TLV encoded IE
107 * \param[out] o_tag the tag of the IE that was found
108 * \param[out] o_len length of the IE that was found
109 * \param[out] o_val pointer to the data of the IE that was found
110 * \param[in] def structure defining the valid TLV tags / configurations
111 * \param[in] buf the input data buffer to be parsed
112 * \param[in] buf_len length of the input data buffer
113 * \returns number of bytes consumed by the TLV entry / IE parsed
Harald Welteec8b4502010-02-20 20:34:29 +0100114 */
115int tlv_parse_one(uint8_t *o_tag, uint16_t *o_len, const uint8_t **o_val,
116 const struct tlv_definition *def,
117 const uint8_t *buf, int buf_len)
118{
119 uint8_t tag;
120 int len;
121
122 tag = *buf;
123 *o_tag = tag;
124
Andreas Eversbergcd2a74b2010-07-12 08:55:14 +0200125 /* single octet TV IE */
126 if (def->def[tag & 0xf0].type == TLV_TYPE_SINGLE_TV) {
127 *o_tag = tag & 0xf0;
128 *o_val = buf;
129 *o_len = 1;
130 return 1;
131 }
132
Neels Hofmeyr667e83d2015-11-02 20:18:11 +0100133 /* FIXME: use tables for known IEI */
Harald Welteec8b4502010-02-20 20:34:29 +0100134 switch (def->def[tag].type) {
135 case TLV_TYPE_T:
136 /* GSM TS 04.07 11.2.4: Type 1 TV or Type 2 T */
137 *o_val = buf;
138 *o_len = 0;
139 len = 1;
140 break;
141 case TLV_TYPE_TV:
142 *o_val = buf+1;
143 *o_len = 1;
144 len = 2;
145 break;
146 case TLV_TYPE_FIXED:
147 *o_val = buf+1;
148 *o_len = def->def[tag].fixed_len;
149 len = def->def[tag].fixed_len + 1;
150 break;
151 case TLV_TYPE_TLV:
Harald Welte2fe68472012-07-14 01:50:33 +0200152tlv: /* GSM TS 04.07 11.2.4: Type 4 TLV */
Harald Welteec8b4502010-02-20 20:34:29 +0100153 if (buf + 1 > buf + buf_len)
154 return -1;
155 *o_val = buf+2;
156 *o_len = *(buf+1);
157 len = *o_len + 2;
158 if (len > buf_len)
159 return -2;
160 break;
Harald Welte2fe68472012-07-14 01:50:33 +0200161 case TLV_TYPE_vTvLV_GAN: /* 44.318 / 11.1.4 */
162 /* FIXME: variable-length TAG! */
163 if (*(buf+1) & 0x80) {
164 /* like TL16Vbut without highest bit of len */
165 if (2 > buf_len)
166 return -1;
167 *o_val = buf+3;
168 *o_len = (*(buf+1) & 0x7F) << 8 | *(buf+2);
169 len = *o_len + 3;
170 if (len > buf_len)
171 return -2;
172 } else {
173 /* like TLV */
174 goto tlv;
175 }
176 break;
Harald Welteec8b4502010-02-20 20:34:29 +0100177 case TLV_TYPE_TvLV:
178 if (*(buf+1) & 0x80) {
179 /* like TLV, but without highest bit of len */
180 if (buf + 1 > buf + buf_len)
181 return -1;
182 *o_val = buf+2;
183 *o_len = *(buf+1) & 0x7f;
184 len = *o_len + 2;
185 if (len > buf_len)
186 return -2;
187 break;
188 }
189 /* like TL16V, fallthrough */
190 case TLV_TYPE_TL16V:
191 if (2 > buf_len)
192 return -1;
193 *o_val = buf+3;
194 *o_len = *(buf+1) << 8 | *(buf+2);
195 len = *o_len + 3;
196 if (len > buf_len)
197 return -2;
198 break;
199 default:
200 return -3;
201 }
202
203 return len;
204}
205
Neels Hofmeyr667e83d2015-11-02 20:18:11 +0100206/*! \brief Parse an entire buffer of TLV encoded Information Elements
Harald Welte57c7d372011-08-17 17:50:55 +0200207 * \param[out] dec caller-allocated pointer to \ref tlv_parsed
208 * \param[in] def structure defining the valid TLV tags / configurations
209 * \param[in] buf the input data buffer to be parsed
210 * \param[in] buf_len length of the input data buffer
211 * \param[in] lv_tag an initial LV tag at the start of the buffer
212 * \param[in] lv_tag2 a second initial LV tag following the \a lv_tag
213 * \returns number of bytes consumed by the TLV entry / IE parsed
Harald Welteec8b4502010-02-20 20:34:29 +0100214 */
215int tlv_parse(struct tlv_parsed *dec, const struct tlv_definition *def,
216 const uint8_t *buf, int buf_len, uint8_t lv_tag,
217 uint8_t lv_tag2)
218{
219 int ofs = 0, num_parsed = 0;
220 uint16_t len;
221
222 memset(dec, 0, sizeof(*dec));
223
224 if (lv_tag) {
225 if (ofs > buf_len)
226 return -1;
227 dec->lv[lv_tag].val = &buf[ofs+1];
228 dec->lv[lv_tag].len = buf[ofs];
229 len = dec->lv[lv_tag].len + 1;
230 if (ofs + len > buf_len)
231 return -2;
232 num_parsed++;
233 ofs += len;
234 }
235 if (lv_tag2) {
236 if (ofs > buf_len)
237 return -1;
238 dec->lv[lv_tag2].val = &buf[ofs+1];
239 dec->lv[lv_tag2].len = buf[ofs];
240 len = dec->lv[lv_tag2].len + 1;
241 if (ofs + len > buf_len)
242 return -2;
243 num_parsed++;
244 ofs += len;
245 }
246
247 while (ofs < buf_len) {
248 int rv;
249 uint8_t tag;
250 const uint8_t *val;
251
252 rv = tlv_parse_one(&tag, &len, &val, def,
253 &buf[ofs], buf_len-ofs);
254 if (rv < 0)
255 return rv;
256 dec->lv[tag].val = val;
257 dec->lv[tag].len = len;
258 ofs += rv;
259 num_parsed++;
260 }
261 //tlv_dump(dec);
262 return num_parsed;
263}
264
Harald Welte57c7d372011-08-17 17:50:55 +0200265/*! \brief take a master (src) tlvdev and fill up all empty slots in 'dst' */
Harald Welteec8b4502010-02-20 20:34:29 +0100266void tlv_def_patch(struct tlv_definition *dst, const struct tlv_definition *src)
267{
268 int i;
269
270 for (i = 0; i < ARRAY_SIZE(dst->def); i++) {
271 if (src->def[i].type == TLV_TYPE_NONE)
272 continue;
273 if (dst->def[i].type == TLV_TYPE_NONE)
274 dst->def[i] = src->def[i];
275 }
276}
277
278static __attribute__((constructor)) void on_dso_load_tlv(void)
279{
280 int i;
281 for (i = 0; i < ARRAY_SIZE(tvlv_att_def.def); i++)
282 tvlv_att_def.def[i].type = TLV_TYPE_TvLV;
Harald Welte2fe68472012-07-14 01:50:33 +0200283
284 for (i = 0; i < ARRAY_SIZE(vtvlv_gan_att_def.def); i++)
285 vtvlv_gan_att_def.def[i].type = TLV_TYPE_vTvLV_GAN;
Harald Welteec8b4502010-02-20 20:34:29 +0100286}
Harald Welte57c7d372011-08-17 17:50:55 +0200287
Harald Weltefbd02fa2016-04-25 15:19:35 +0200288/*! Advance the data pointer, subtract length and assign value pointer
289 * \param data pointer to the pointer to data
290 * \param data_len pointer to size_t containing \arg data length
291 * \param[in] len the length that we expect the fixed IE to hav
292 * \param[out] value pointer to pointer of value part of IE
293 * \returns length of IE value; negative in case of error
294 */
295int osmo_shift_v_fixed(uint8_t **data, size_t *data_len,
296 size_t len, uint8_t **value)
297{
298 if (len > *data_len)
299 goto fail;
300
301 if (value)
302 *value = *data;
303
304 *data += len;
305 *data_len -= len;
306
307 return len;
308
309fail:
310 *data += *data_len;
311 *data_len = 0;
312 return -1;
313}
314
315/*! Match tag, check length and assign value pointer
316 * \param data pointer to the pointer to data
317 * \param data_len pointer to size_t containing \arg data length
318 * \param[in] tag the tag (IEI) that we expect at \arg data
319 * \param[in] len the length that we expect the fixed IE to have
320 * \param[out] value pointer to pointer of value part of IE
321 * \returns length of IE value; negative in case of error
322 */
323int osmo_match_shift_tv_fixed(uint8_t **data, size_t *data_len,
324 uint8_t tag, size_t len,
325 uint8_t **value)
326{
327 size_t ie_len;
328
329 if (*data_len == 0)
330 goto fail;
331
332 if ((*data)[0] != tag)
333 return 0;
334
335 if (len > *data_len - 1)
336 goto fail;
337
338 if (value)
339 *value = *data + 1;
340
341 ie_len = len + 1;
342 *data += ie_len;
343 *data_len -= ie_len;
344
345 return ie_len;
346
347fail:
348 *data += *data_len;
349 *data_len = 0;
350 return -1;
351}
352
353/*! Verify TLV header and advance data / subtract length
354 * \param data pointer to the pointer to data
355 * \param data_len pointer to size_t containing \arg data length
356 * \param[in] expected_tag the tag (IEI) that we expect at \arg data
357 * \param[out] value pointer to pointer of value part of IE
358 * \param[out] value_len pointer to length of \arg value
359 * \returns length of IE value; negative in case of error
360 */
361int osmo_match_shift_tlv(uint8_t **data, size_t *data_len,
362 uint8_t expected_tag, uint8_t **value,
363 size_t *value_len)
364{
365 int rc;
366 uint8_t tag;
367 uint8_t *old_data = *data;
368 size_t old_data_len = *data_len;
369
370 rc = osmo_shift_tlv(data, data_len, &tag, value, value_len);
371
372 if (rc > 0 && tag != expected_tag) {
373 *data = old_data;
374 *data_len = old_data_len;
375 return 0;
376 }
377
378 return rc;
379}
380
381/*! Extract TLV and advance data pointer + subtract length
382 * \param data pointer to the pointer to data
383 * \param data_len pointer to size_t containing \arg data lengt
384 * \param[out] tag extract the tag (IEI) at start of \arg data
385 * \param[out] value extracted pointer to value part of TLV
386 * \param[out] value_len extracted length of \arg value
387 * \returns number of bytes subtracted
388 */
389int osmo_shift_tlv(uint8_t **data, size_t *data_len,
390 uint8_t *tag, uint8_t **value, size_t *value_len)
391{
392 size_t len;
393 size_t ie_len;
394
395 if (*data_len < 2)
396 goto fail;
397
398 len = (*data)[1];
399 if (len > *data_len - 2)
400 goto fail;
401
402 if (tag)
403 *tag = (*data)[0];
404 if (value)
405 *value = *data + 2;
406 if (value_len)
407 *value_len = len;
408
409 ie_len = len + 2;
410
411 *data += ie_len;
412 *data_len -= ie_len;
413
414 return ie_len;
415
416fail:
417 *data += *data_len;
418 *data_len = 0;
419 return -1;
420}
421
422/*! Extract LV and advance data pointer + subtract length
423 * \param data pointer to the pointer to data
424 * \param data_len pointer to size_t containing \arg data lengt
425 * \param[out] value extracted pointer to value part of TLV
426 * \param[out] value_len extracted length of \arg value
427 * \returns number of bytes subtracted
428 */
429int osmo_shift_lv(uint8_t **data, size_t *data_len,
430 uint8_t **value, size_t *value_len)
431{
432 size_t len;
433 size_t ie_len;
434
435 if (*data_len < 1)
436 goto fail;
437
438 len = (*data)[0];
439 if (len > *data_len - 1)
440 goto fail;
441
442 if (value)
443 *value = *data + 1;
444 if (value_len)
445 *value_len = len;
446
447 ie_len = len + 1;
448 *data += ie_len;
449 *data_len -= ie_len;
450
451 return ie_len;
452
453fail:
454 *data += *data_len;
455 *data_len = 0;
456 return -1;
457}
458
Sylvain Munautdca7d2c2012-04-18 21:53:23 +0200459/*! @} */