blob: 159b42bdd937edc1c5d3790a2071f207085beb36 [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
Harald Welte8006f532019-02-13 22:23:13 +0100123
124/*! Encode a single TLV into given message buffer
125 * \param[inout] msg Caller-allocated message buffer with sufficient tailroom
126 * \param[in] type TLV type/format to use during encode
127 * \param[in] tag Tag of TLV to be encoded
128 * \parma[in] len Length of TLV to be encoded
129 * \param[in] val Value part of TLV to be encoded
130 * \returns 0 on success; negative in case of error */
131int tlv_encode_one(struct msgb *msg, enum tlv_type type, uint8_t tag,
132 unsigned int len, const uint8_t *val)
133{
134 switch (type) {
135 case TLV_TYPE_NONE:
136 break;
137 case TLV_TYPE_FIXED:
138 msgb_tv_fixed_put(msg, tag, len, val);
139 break;
140 case TLV_TYPE_T:
141 msgb_v_put(msg, tag);
142 break;
143 case TLV_TYPE_TV:
144 msgb_tv_put(msg, tag, val[0]);
145 break;
146 case TLV_TYPE_TLV:
147 msgb_tlv_put(msg, tag, len, val);
148 break;
149 case TLV_TYPE_TL16V:
150 msgb_tl16v_put(msg, tag, len, val);
151 break;
152 case TLV_TYPE_TvLV:
153 msgb_tvlv_put(msg, tag, len, val);
154 break;
155 case TLV_TYPE_SINGLE_TV:
156 msgb_v_put(msg, (tag << 4) | (val[0] & 0xf));
157 break;
158 case TLV_TYPE_vTvLV_GAN:
159 msgb_vtvlv_gan_put(msg, tag, len, val);
160 break;
161 default:
162 return -EINVAL;
163 }
164 return 0;
165}
166
167/*! Encode a set of decoded TLVs according to a given definition into a message buffer
168 * \param[inout] msg Caller-allocated message buffer with sufficient tailroom
169 * \param[in] def structure defining the valid TLV tags / configurations
170 * \param[in] tp decoded values to be encoded
171 * \returns number of bytes consumed in msg; negative in case of error */
172int tlv_encode(struct msgb *msg, const struct tlv_definition *def, const struct tlv_parsed *tp)
173{
174 unsigned int tailroom_before = msgb_tailroom(msg);
175 unsigned int i;
176 int rc;
177
178 for (i = 0; i < ARRAY_SIZE(tp->lv); i++) {
179 /* skip entries in the array that aren't used/filled */
180 if (!TLVP_PRESENT(tp, i))
181 continue;
182
183 rc = tlv_encode_one(msg, def->def[i].type, i, TLVP_LEN(tp, i), TLVP_VAL(tp, i));
184 if (rc < 0)
185 return rc;
186 }
187
188 return tailroom_before - msgb_tailroom(msg);
189}
190
191/*! Encode a set of decoded TLVs according to a given definition and IE order into a message buffer
192 * \param[inout] msg Caller-allocated message buffer with sufficient tailroom
193 * \param[in] def structure defining the valid TLV tags / configurations
194 * \param[in] tp decoded values to be encoded
195 * \param[in] tag_order array of tags determining the IE encoding order
196 * \param[in] tag_order_len length of tag_order
197 * \returns number of bytes consumed in msg; negative in case of error */
198int tlv_encode_ordered(struct msgb *msg, const struct tlv_definition *def, const struct tlv_parsed *tp,
199 const uint8_t *tag_order, unsigned int tag_order_len)
200{
201
202 unsigned int tailroom_before = msgb_tailroom(msg);
203 unsigned int i;
204 int rc;
205
206 for (i = 0; i < tag_order_len; i++) {
207 uint8_t tag = tag_order[i];
208
209 /* skip entries in the array that aren't used/filled */
210 if (!TLVP_PRESENT(tp, tag))
211 continue;
212
213 rc = tlv_encode_one(msg, def->def[tag].type, tag, TLVP_LEN(tp, tag), TLVP_VAL(tp, tag));
214 if (rc < 0)
215 return rc;
216 }
217 return tailroom_before - msgb_tailroom(msg);
218}
219
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200220/*! Parse a single TLV encoded IE
Harald Welte57c7d372011-08-17 17:50:55 +0200221 * \param[out] o_tag the tag of the IE that was found
222 * \param[out] o_len length of the IE that was found
223 * \param[out] o_val pointer to the data of the IE that was found
224 * \param[in] def structure defining the valid TLV tags / configurations
225 * \param[in] buf the input data buffer to be parsed
226 * \param[in] buf_len length of the input data buffer
Stefan Sperling1e50e2a2018-01-08 19:20:02 +0100227 * \returns number of bytes consumed by the TLV entry / IE parsed; negative in case of error
Harald Welteec8b4502010-02-20 20:34:29 +0100228 */
229int tlv_parse_one(uint8_t *o_tag, uint16_t *o_len, const uint8_t **o_val,
230 const struct tlv_definition *def,
231 const uint8_t *buf, int buf_len)
232{
233 uint8_t tag;
234 int len;
235
236 tag = *buf;
237 *o_tag = tag;
238
Andreas Eversbergcd2a74b2010-07-12 08:55:14 +0200239 /* single octet TV IE */
240 if (def->def[tag & 0xf0].type == TLV_TYPE_SINGLE_TV) {
241 *o_tag = tag & 0xf0;
242 *o_val = buf;
243 *o_len = 1;
244 return 1;
245 }
246
Neels Hofmeyr667e83d2015-11-02 20:18:11 +0100247 /* FIXME: use tables for known IEI */
Harald Welteec8b4502010-02-20 20:34:29 +0100248 switch (def->def[tag].type) {
249 case TLV_TYPE_T:
250 /* GSM TS 04.07 11.2.4: Type 1 TV or Type 2 T */
251 *o_val = buf;
252 *o_len = 0;
253 len = 1;
254 break;
255 case TLV_TYPE_TV:
256 *o_val = buf+1;
257 *o_len = 1;
258 len = 2;
259 break;
260 case TLV_TYPE_FIXED:
261 *o_val = buf+1;
262 *o_len = def->def[tag].fixed_len;
263 len = def->def[tag].fixed_len + 1;
264 break;
265 case TLV_TYPE_TLV:
Harald Welte2fe68472012-07-14 01:50:33 +0200266tlv: /* GSM TS 04.07 11.2.4: Type 4 TLV */
Harald Welteec8b4502010-02-20 20:34:29 +0100267 if (buf + 1 > buf + buf_len)
268 return -1;
269 *o_val = buf+2;
270 *o_len = *(buf+1);
271 len = *o_len + 2;
272 if (len > buf_len)
273 return -2;
274 break;
Harald Welte2fe68472012-07-14 01:50:33 +0200275 case TLV_TYPE_vTvLV_GAN: /* 44.318 / 11.1.4 */
276 /* FIXME: variable-length TAG! */
277 if (*(buf+1) & 0x80) {
278 /* like TL16Vbut without highest bit of len */
279 if (2 > buf_len)
280 return -1;
281 *o_val = buf+3;
282 *o_len = (*(buf+1) & 0x7F) << 8 | *(buf+2);
283 len = *o_len + 3;
284 if (len > buf_len)
285 return -2;
286 } else {
287 /* like TLV */
288 goto tlv;
289 }
290 break;
Harald Welteec8b4502010-02-20 20:34:29 +0100291 case TLV_TYPE_TvLV:
292 if (*(buf+1) & 0x80) {
293 /* like TLV, but without highest bit of len */
294 if (buf + 1 > buf + buf_len)
295 return -1;
296 *o_val = buf+2;
297 *o_len = *(buf+1) & 0x7f;
298 len = *o_len + 2;
299 if (len > buf_len)
300 return -2;
301 break;
302 }
303 /* like TL16V, fallthrough */
304 case TLV_TYPE_TL16V:
305 if (2 > buf_len)
306 return -1;
307 *o_val = buf+3;
308 *o_len = *(buf+1) << 8 | *(buf+2);
309 len = *o_len + 3;
310 if (len > buf_len)
311 return -2;
312 break;
313 default:
314 return -3;
315 }
316
317 return len;
318}
319
Neels Hofmeyra78b22b2018-04-13 03:36:49 +0200320/*! Parse an entire buffer of TLV encoded Information Elements.
321 * In case of multiple occurences of an IE, keep only the first occurence.
322 * Most GSM related protocols clearly indicate that in case of duplicate
323 * IEs, only the first occurrence shall be used, while any further occurrences
324 * shall be ignored. See e.g. 3GPP TS 24.008 Section 8.6.3.
325 * For multiple occurences, use tlv_parse2().
Harald Welte57c7d372011-08-17 17:50:55 +0200326 * \param[out] dec caller-allocated pointer to \ref tlv_parsed
327 * \param[in] def structure defining the valid TLV tags / configurations
328 * \param[in] buf the input data buffer to be parsed
329 * \param[in] buf_len length of the input data buffer
330 * \param[in] lv_tag an initial LV tag at the start of the buffer
331 * \param[in] lv_tag2 a second initial LV tag following the \a lv_tag
Stefan Sperlingc9bebbd2018-03-16 15:59:01 +0100332 * \returns number of TLV entries parsed; negative in case of error
Harald Welteec8b4502010-02-20 20:34:29 +0100333 */
334int tlv_parse(struct tlv_parsed *dec, const struct tlv_definition *def,
335 const uint8_t *buf, int buf_len, uint8_t lv_tag,
336 uint8_t lv_tag2)
337{
Neels Hofmeyra78b22b2018-04-13 03:36:49 +0200338 return tlv_parse2(dec, 1, def, buf, buf_len, lv_tag, lv_tag2);
339}
340
341/*! Like tlv_parse(), but capable of decoding multiple occurences of the same IE.
342 * Parse an entire buffer of TLV encoded Information Elements.
343 * To decode multiple occurences of IEs, provide in dec an _array_ of tlv_parsed, and
344 * pass the size of that array in dec_multiples. The first occurence of each IE
345 * is stored in dec[0], the second in dec[1] and so forth. If there are more
346 * occurences than the array length given in dec_multiples, the remaining
347 * occurences are dropped.
348 * \param[out] dec caller-allocated pointer to \ref tlv_parsed
349 * \param[in] dec_multiples length of the tlv_parsed[] in \a dec.
350 * \param[in] def structure defining the valid TLV tags / configurations
351 * \param[in] buf the input data buffer to be parsed
352 * \param[in] buf_len length of the input data buffer
353 * \param[in] lv_tag an initial LV tag at the start of the buffer
354 * \param[in] lv_tag2 a second initial LV tag following the \a lv_tag
355 * \returns number of TLV entries parsed; negative in case of error
356 */
357int tlv_parse2(struct tlv_parsed *dec, int dec_multiples,
358 const struct tlv_definition *def, const uint8_t *buf, int buf_len,
359 uint8_t lv_tag, uint8_t lv_tag2)
360{
Harald Welteec8b4502010-02-20 20:34:29 +0100361 int ofs = 0, num_parsed = 0;
362 uint16_t len;
Neels Hofmeyra78b22b2018-04-13 03:36:49 +0200363 int dec_i;
Harald Welteec8b4502010-02-20 20:34:29 +0100364
Neels Hofmeyra78b22b2018-04-13 03:36:49 +0200365 for (dec_i = 0; dec_i < dec_multiples; dec_i++)
366 memset(&dec[dec_i], 0, sizeof(*dec));
Harald Welteec8b4502010-02-20 20:34:29 +0100367
368 if (lv_tag) {
Neels Hofmeyra78b22b2018-04-13 03:36:49 +0200369 const uint8_t *val;
370 uint16_t parsed_len;
Harald Welteec8b4502010-02-20 20:34:29 +0100371 if (ofs > buf_len)
372 return -1;
Neels Hofmeyra78b22b2018-04-13 03:36:49 +0200373 val = &buf[ofs+1];
374 len = buf[ofs];
375 parsed_len = len + 1;
376 if (ofs + parsed_len > buf_len)
Harald Welteec8b4502010-02-20 20:34:29 +0100377 return -2;
378 num_parsed++;
Neels Hofmeyra78b22b2018-04-13 03:36:49 +0200379 ofs += parsed_len;
380 /* store the resulting val and len */
381 for (dec_i = 0; dec_i < dec_multiples; dec_i++) {
382 if (dec[dec_i].lv[lv_tag].val != NULL)
383 continue;
384 dec->lv[lv_tag].val = val;
385 dec->lv[lv_tag].len = len;
386 break;
387 }
Harald Welteec8b4502010-02-20 20:34:29 +0100388 }
389 if (lv_tag2) {
Neels Hofmeyra78b22b2018-04-13 03:36:49 +0200390 const uint8_t *val;
391 uint16_t parsed_len;
Harald Welteec8b4502010-02-20 20:34:29 +0100392 if (ofs > buf_len)
393 return -1;
Neels Hofmeyra78b22b2018-04-13 03:36:49 +0200394 val = &buf[ofs+1];
395 len = buf[ofs];
396 parsed_len = len + 1;
397 if (ofs + parsed_len > buf_len)
Harald Welteec8b4502010-02-20 20:34:29 +0100398 return -2;
399 num_parsed++;
Neels Hofmeyra78b22b2018-04-13 03:36:49 +0200400 ofs += parsed_len;
401 /* store the resulting val and len */
402 for (dec_i = 0; dec_i < dec_multiples; dec_i++) {
403 if (dec[dec_i].lv[lv_tag2].val != NULL)
404 continue;
405 dec->lv[lv_tag2].val = val;
406 dec->lv[lv_tag2].len = len;
407 break;
408 }
Harald Welteec8b4502010-02-20 20:34:29 +0100409 }
410
411 while (ofs < buf_len) {
412 int rv;
413 uint8_t tag;
414 const uint8_t *val;
415
416 rv = tlv_parse_one(&tag, &len, &val, def,
417 &buf[ofs], buf_len-ofs);
418 if (rv < 0)
419 return rv;
Neels Hofmeyra78b22b2018-04-13 03:36:49 +0200420 for (dec_i = 0; dec_i < dec_multiples; dec_i++) {
421 if (dec[dec_i].lv[tag].val != NULL)
422 continue;
423 dec[dec_i].lv[tag].val = val;
424 dec[dec_i].lv[tag].len = len;
425 break;
Harald Weltebf383a12018-02-02 12:11:14 +0100426 }
Harald Welteec8b4502010-02-20 20:34:29 +0100427 ofs += rv;
428 num_parsed++;
429 }
430 //tlv_dump(dec);
431 return num_parsed;
432}
433
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200434/*! take a master (src) tlvdev and fill up all empty slots in 'dst'
Harald Welte96e2a002017-06-12 21:44:18 +0200435 * \param dst TLV parser definition that is to be patched
436 * \param[in] src TLV parser definition whose content is patched into \a dst */
Harald Welteec8b4502010-02-20 20:34:29 +0100437void tlv_def_patch(struct tlv_definition *dst, const struct tlv_definition *src)
438{
439 int i;
440
441 for (i = 0; i < ARRAY_SIZE(dst->def); i++) {
442 if (src->def[i].type == TLV_TYPE_NONE)
443 continue;
444 if (dst->def[i].type == TLV_TYPE_NONE)
445 dst->def[i] = src->def[i];
446 }
447}
448
449static __attribute__((constructor)) void on_dso_load_tlv(void)
450{
451 int i;
452 for (i = 0; i < ARRAY_SIZE(tvlv_att_def.def); i++)
453 tvlv_att_def.def[i].type = TLV_TYPE_TvLV;
Harald Welte2fe68472012-07-14 01:50:33 +0200454
455 for (i = 0; i < ARRAY_SIZE(vtvlv_gan_att_def.def); i++)
456 vtvlv_gan_att_def.def[i].type = TLV_TYPE_vTvLV_GAN;
Harald Welteec8b4502010-02-20 20:34:29 +0100457}
Harald Welte57c7d372011-08-17 17:50:55 +0200458
Harald Weltefbd02fa2016-04-25 15:19:35 +0200459/*! Advance the data pointer, subtract length and assign value pointer
460 * \param data pointer to the pointer to data
461 * \param data_len pointer to size_t containing \arg data length
462 * \param[in] len the length that we expect the fixed IE to hav
463 * \param[out] value pointer to pointer of value part of IE
464 * \returns length of IE value; negative in case of error
465 */
466int osmo_shift_v_fixed(uint8_t **data, size_t *data_len,
467 size_t len, uint8_t **value)
468{
469 if (len > *data_len)
470 goto fail;
471
472 if (value)
473 *value = *data;
474
475 *data += len;
476 *data_len -= len;
477
478 return len;
479
480fail:
481 *data += *data_len;
482 *data_len = 0;
483 return -1;
484}
485
486/*! Match tag, check length and assign value pointer
487 * \param data pointer to the pointer to data
488 * \param data_len pointer to size_t containing \arg data length
489 * \param[in] tag the tag (IEI) that we expect at \arg data
490 * \param[in] len the length that we expect the fixed IE to have
491 * \param[out] value pointer to pointer of value part of IE
492 * \returns length of IE value; negative in case of error
493 */
494int osmo_match_shift_tv_fixed(uint8_t **data, size_t *data_len,
495 uint8_t tag, size_t len,
496 uint8_t **value)
497{
498 size_t ie_len;
499
500 if (*data_len == 0)
501 goto fail;
502
503 if ((*data)[0] != tag)
504 return 0;
505
506 if (len > *data_len - 1)
507 goto fail;
508
509 if (value)
510 *value = *data + 1;
511
512 ie_len = len + 1;
513 *data += ie_len;
514 *data_len -= ie_len;
515
516 return ie_len;
517
518fail:
519 *data += *data_len;
520 *data_len = 0;
521 return -1;
522}
523
524/*! Verify TLV header and advance data / subtract length
525 * \param data pointer to the pointer to data
526 * \param data_len pointer to size_t containing \arg data length
527 * \param[in] expected_tag the tag (IEI) that we expect at \arg data
528 * \param[out] value pointer to pointer of value part of IE
529 * \param[out] value_len pointer to length of \arg value
530 * \returns length of IE value; negative in case of error
531 */
532int osmo_match_shift_tlv(uint8_t **data, size_t *data_len,
533 uint8_t expected_tag, uint8_t **value,
534 size_t *value_len)
535{
536 int rc;
537 uint8_t tag;
538 uint8_t *old_data = *data;
539 size_t old_data_len = *data_len;
540
541 rc = osmo_shift_tlv(data, data_len, &tag, value, value_len);
542
543 if (rc > 0 && tag != expected_tag) {
544 *data = old_data;
545 *data_len = old_data_len;
546 return 0;
547 }
548
549 return rc;
550}
551
552/*! Extract TLV and advance data pointer + subtract length
553 * \param data pointer to the pointer to data
554 * \param data_len pointer to size_t containing \arg data lengt
555 * \param[out] tag extract the tag (IEI) at start of \arg data
556 * \param[out] value extracted pointer to value part of TLV
557 * \param[out] value_len extracted length of \arg value
558 * \returns number of bytes subtracted
559 */
560int osmo_shift_tlv(uint8_t **data, size_t *data_len,
561 uint8_t *tag, uint8_t **value, size_t *value_len)
562{
563 size_t len;
564 size_t ie_len;
565
566 if (*data_len < 2)
567 goto fail;
568
569 len = (*data)[1];
570 if (len > *data_len - 2)
571 goto fail;
572
573 if (tag)
574 *tag = (*data)[0];
575 if (value)
576 *value = *data + 2;
577 if (value_len)
578 *value_len = len;
579
580 ie_len = len + 2;
581
582 *data += ie_len;
583 *data_len -= ie_len;
584
585 return ie_len;
586
587fail:
588 *data += *data_len;
589 *data_len = 0;
590 return -1;
591}
592
593/*! Extract LV and advance data pointer + subtract length
594 * \param data pointer to the pointer to data
595 * \param data_len pointer to size_t containing \arg data lengt
596 * \param[out] value extracted pointer to value part of TLV
597 * \param[out] value_len extracted length of \arg value
598 * \returns number of bytes subtracted
599 */
600int osmo_shift_lv(uint8_t **data, size_t *data_len,
601 uint8_t **value, size_t *value_len)
602{
603 size_t len;
604 size_t ie_len;
605
606 if (*data_len < 1)
607 goto fail;
608
609 len = (*data)[0];
610 if (len > *data_len - 1)
611 goto fail;
612
613 if (value)
614 *value = *data + 1;
615 if (value_len)
616 *value_len = len;
617
618 ie_len = len + 1;
619 *data += ie_len;
620 *data_len -= ie_len;
621
622 return ie_len;
623
624fail:
625 *data += *data_len;
626 *data_len = 0;
627 return -1;
628}
629
Sylvain Munautdca7d2c2012-04-18 21:53:23 +0200630/*! @} */