blob: 8dd460db5061ea3e103143f6b581e6501f63babe [file] [log] [blame]
Harald Welte95109922020-12-04 13:55:38 +01001/* (C) 2008-2020 by Harald Welte <laforge@gnumonks.org>
Harald Weltee08da972017-11-13 01:00:26 +09002 * (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>
Harald Welte95109922020-12-04 13:55:38 +010027#include <osmocom/core/logging.h>
Pablo Neira Ayuso83419342011-03-22 16:36:13 +010028#include <osmocom/gsm/tlv.h>
Harald Welteec8b4502010-02-20 20:34:29 +010029
Harald Welte57c7d372011-08-17 17:50:55 +020030/*! \addtogroup tlv
31 * @{
Neels Hofmeyr87e45502017-06-20 00:17:59 +020032 * Osmocom TLV Parser
Harald Welte96e2a002017-06-12 21:44:18 +020033 *
34 * The Osmocom TLV parser is intended to operate as a low-level C
35 * implementation without dynamic memory allocations. Basically, it
36 * iterates over the IE (Information Elements) of the message and fills
37 * an array of pointers, indexed by the IEI (IE Identifier). The
38 * parser output is thus an array of pointers to the start of the
39 * respective IE inside the message.
40 *
41 * The TLV parser is configured by a TLV parser definition, which
42 * determines which if the IEIs for a given protocol are of which
43 * particular type. Types are e.g. TV (Tag + single byte value), Tag +
44 * fixed-length value, TLV with 8bit length, TLV with 16bit length, TLV
45 * with variable-length length field, etc.
Neels Hofmeyr17518fe2017-06-20 04:35:06 +020046 *
47 * \file tlv_parser.c */
Harald Welte57c7d372011-08-17 17:50:55 +020048
Harald Welteec8b4502010-02-20 20:34:29 +010049struct tlv_definition tvlv_att_def;
Harald Welte2fe68472012-07-14 01:50:33 +020050struct tlv_definition vtvlv_gan_att_def;
Harald Welteec8b4502010-02-20 20:34:29 +010051
Stefan Sperlingc9bebbd2018-03-16 15:59:01 +010052/*! Dump parsed TLV structure to stdout */
Harald Welteec8b4502010-02-20 20:34:29 +010053int tlv_dump(struct tlv_parsed *dec)
54{
55 int i;
56
57 for (i = 0; i <= 0xff; i++) {
58 if (!dec->lv[i].val)
59 continue;
60 printf("T=%02x L=%d\n", i, dec->lv[i].len);
61 }
62 return 0;
63}
64
Neels Hofmeyr87e45502017-06-20 00:17:59 +020065/*! Copy \ref tlv_parsed using given talloc context
Maxdbd3a922017-01-02 14:10:30 +010066 * \param[in] tp_orig Parsed TLV structure
67 * \param[in] ctx Talloc context for allocations
68 * \returns NULL on errors, \ref tlv_parsed pointer otherwise
69 */
70struct tlv_parsed *osmo_tlvp_copy(const struct tlv_parsed *tp_orig, void *ctx)
71{
72 struct tlv_parsed *tp_out;
73 size_t i, len;
74
75 tp_out = talloc_zero(ctx, struct tlv_parsed);
76 if (!tp_out)
77 return NULL;
78
79 /* if the original is NULL, return empty tlvp */
80 if (!tp_orig)
81 return tp_out;
82
83 for (i = 0; i < ARRAY_SIZE(tp_orig->lv); i++) {
84 len = tp_orig->lv[i].len;
85 tp_out->lv[i].len = len;
86 if (len && tp_out->lv[i].val) {
87 tp_out->lv[i].val = talloc_zero_size(tp_out, len);
88 if (!tp_out->lv[i].val) {
89 talloc_free(tp_out);
90 return NULL;
91 }
92 memcpy((uint8_t *)tp_out->lv[i].val, tp_orig->lv[i].val,
93 len);
94 }
95 }
96
97 return tp_out;
98}
99
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200100/*! Merge all \ref tlv_parsed attributes of 'src' into 'dst'
Maxdbd3a922017-01-02 14:10:30 +0100101 * \param[in] dst Parsed TLV structure to merge into
102 * \param[in] src Parsed TLV structure to merge from
103 * \returns 0 on success, negative on error
104 */
105int osmo_tlvp_merge(struct tlv_parsed *dst, const struct tlv_parsed *src)
106{
107 size_t i, len;
108 for (i = 0; i < ARRAY_SIZE(dst->lv); i++) {
109 len = src->lv[i].len;
110 if (len == 0 || src->lv[i].val == NULL)
111 continue;
112 if (dst->lv[i].val) {
113 talloc_free((uint8_t *) dst->lv[i].val);
114 dst->lv[i].len = 0;
115 }
116 dst->lv[i].val = talloc_zero_size(dst, len);
117 if (!dst->lv[i].val)
118 return -ENOMEM;
119 memcpy((uint8_t *) dst->lv[i].val, src->lv[i].val, len);
120 }
121 return 0;
122}
123
Harald Welte8006f532019-02-13 22:23:13 +0100124
125/*! Encode a single TLV into given message buffer
126 * \param[inout] msg Caller-allocated message buffer with sufficient tailroom
127 * \param[in] type TLV type/format to use during encode
128 * \param[in] tag Tag of TLV to be encoded
Vadim Yanitskiy64277a02023-02-28 03:30:27 +0700129 * \param[in] len Length of TLV to be encoded
Harald Welte8006f532019-02-13 22:23:13 +0100130 * \param[in] val Value part of TLV to be encoded
131 * \returns 0 on success; negative in case of error */
132int tlv_encode_one(struct msgb *msg, enum tlv_type type, uint8_t tag,
133 unsigned int len, const uint8_t *val)
134{
135 switch (type) {
136 case TLV_TYPE_NONE:
137 break;
138 case TLV_TYPE_FIXED:
139 msgb_tv_fixed_put(msg, tag, len, val);
140 break;
141 case TLV_TYPE_T:
142 msgb_v_put(msg, tag);
143 break;
144 case TLV_TYPE_TV:
145 msgb_tv_put(msg, tag, val[0]);
146 break;
147 case TLV_TYPE_TLV:
148 msgb_tlv_put(msg, tag, len, val);
149 break;
150 case TLV_TYPE_TL16V:
151 msgb_tl16v_put(msg, tag, len, val);
152 break;
153 case TLV_TYPE_TvLV:
154 msgb_tvlv_put(msg, tag, len, val);
155 break;
156 case TLV_TYPE_SINGLE_TV:
157 msgb_v_put(msg, (tag << 4) | (val[0] & 0xf));
158 break;
159 case TLV_TYPE_vTvLV_GAN:
160 msgb_vtvlv_gan_put(msg, tag, len, val);
161 break;
162 default:
163 return -EINVAL;
164 }
165 return 0;
166}
167
168/*! Encode a set of decoded TLVs according to a given definition into a message buffer
169 * \param[inout] msg Caller-allocated message buffer with sufficient tailroom
170 * \param[in] def structure defining the valid TLV tags / configurations
171 * \param[in] tp decoded values to be encoded
172 * \returns number of bytes consumed in msg; negative in case of error */
173int tlv_encode(struct msgb *msg, const struct tlv_definition *def, const struct tlv_parsed *tp)
174{
175 unsigned int tailroom_before = msgb_tailroom(msg);
176 unsigned int i;
177 int rc;
178
179 for (i = 0; i < ARRAY_SIZE(tp->lv); i++) {
180 /* skip entries in the array that aren't used/filled */
181 if (!TLVP_PRESENT(tp, i))
182 continue;
183
184 rc = tlv_encode_one(msg, def->def[i].type, i, TLVP_LEN(tp, i), TLVP_VAL(tp, i));
185 if (rc < 0)
186 return rc;
187 }
188
189 return tailroom_before - msgb_tailroom(msg);
190}
191
192/*! Encode a set of decoded TLVs according to a given definition and IE order into a message buffer
193 * \param[inout] msg Caller-allocated message buffer with sufficient tailroom
194 * \param[in] def structure defining the valid TLV tags / configurations
195 * \param[in] tp decoded values to be encoded
196 * \param[in] tag_order array of tags determining the IE encoding order
197 * \param[in] tag_order_len length of tag_order
198 * \returns number of bytes consumed in msg; negative in case of error */
199int tlv_encode_ordered(struct msgb *msg, const struct tlv_definition *def, const struct tlv_parsed *tp,
200 const uint8_t *tag_order, unsigned int tag_order_len)
201{
202
203 unsigned int tailroom_before = msgb_tailroom(msg);
204 unsigned int i;
205 int rc;
206
207 for (i = 0; i < tag_order_len; i++) {
208 uint8_t tag = tag_order[i];
209
210 /* skip entries in the array that aren't used/filled */
211 if (!TLVP_PRESENT(tp, tag))
212 continue;
213
214 rc = tlv_encode_one(msg, def->def[tag].type, tag, TLVP_LEN(tp, tag), TLVP_VAL(tp, tag));
215 if (rc < 0)
216 return rc;
217 }
218 return tailroom_before - msgb_tailroom(msg);
219}
220
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200221/*! Parse a single TLV encoded IE
Harald Welte57c7d372011-08-17 17:50:55 +0200222 * \param[out] o_tag the tag of the IE that was found
223 * \param[out] o_len length of the IE that was found
224 * \param[out] o_val pointer to the data of the IE that was found
225 * \param[in] def structure defining the valid TLV tags / configurations
226 * \param[in] buf the input data buffer to be parsed
227 * \param[in] buf_len length of the input data buffer
Pau Espin Pedrolbeb7c172023-04-28 17:33:36 +0200228 * \returns number of bytes consumed by the TLV entry / IE parsed; negative in case of error.
229 *
230 * In IEs of type TLV_TYPE_SINGLE_TV, the data pointer \ref o_val will point to the
231 * byte shared by both the Tag and te Value, hence the tag is to be trimmed
232 * by the caller.
Harald Welteec8b4502010-02-20 20:34:29 +0100233 */
234int tlv_parse_one(uint8_t *o_tag, uint16_t *o_len, const uint8_t **o_val,
235 const struct tlv_definition *def,
236 const uint8_t *buf, int buf_len)
237{
238 uint8_t tag;
Harald Welteefdd6412021-01-12 18:07:18 +0100239 int len; /* number of bytes consumed by TLV entry */
240
241 if (buf_len < 1)
242 return OSMO_TLVP_ERR_OFS_BEYOND_BUFFER;
Harald Welteec8b4502010-02-20 20:34:29 +0100243
244 tag = *buf;
245 *o_tag = tag;
246
Andreas Eversbergcd2a74b2010-07-12 08:55:14 +0200247 /* single octet TV IE */
Pau Espin Pedrolbeb7c172023-04-28 17:33:36 +0200248 if (def->def[tag >> 4].type == TLV_TYPE_SINGLE_TV) {
249 *o_tag = tag >> 4;
250 *o_val = buf;
251 *o_len = 1;
252 return 1;
253 } else if ((tag > 0x0f) && (def->def[tag & 0xf0].type == TLV_TYPE_SINGLE_TV)) {
Pau Espin Pedrol559a6ee2023-03-22 13:17:09 +0100254 /* backward compat for old IEs with half-octet tag defined as 0xN0: */
Andreas Eversbergcd2a74b2010-07-12 08:55:14 +0200255 *o_tag = tag & 0xf0;
256 *o_val = buf;
257 *o_len = 1;
258 return 1;
259 }
260
Neels Hofmeyr667e83d2015-11-02 20:18:11 +0100261 /* FIXME: use tables for known IEI */
Harald Welteec8b4502010-02-20 20:34:29 +0100262 switch (def->def[tag].type) {
263 case TLV_TYPE_T:
264 /* GSM TS 04.07 11.2.4: Type 1 TV or Type 2 T */
265 *o_val = buf;
266 *o_len = 0;
267 len = 1;
268 break;
269 case TLV_TYPE_TV:
270 *o_val = buf+1;
271 *o_len = 1;
272 len = 2;
273 break;
274 case TLV_TYPE_FIXED:
275 *o_val = buf+1;
276 *o_len = def->def[tag].fixed_len;
277 len = def->def[tag].fixed_len + 1;
278 break;
279 case TLV_TYPE_TLV:
Harald Welte2fe68472012-07-14 01:50:33 +0200280tlv: /* GSM TS 04.07 11.2.4: Type 4 TLV */
Harald Welteefdd6412021-01-12 18:07:18 +0100281 if (buf_len < 2)
Harald Welte30a92942020-12-04 14:09:22 +0100282 return OSMO_TLVP_ERR_OFS_BEYOND_BUFFER;
Harald Welteec8b4502010-02-20 20:34:29 +0100283 *o_val = buf+2;
284 *o_len = *(buf+1);
285 len = *o_len + 2;
Harald Welteec8b4502010-02-20 20:34:29 +0100286 break;
Harald Welte2fe68472012-07-14 01:50:33 +0200287 case TLV_TYPE_vTvLV_GAN: /* 44.318 / 11.1.4 */
288 /* FIXME: variable-length TAG! */
Harald Welteefdd6412021-01-12 18:07:18 +0100289 if (buf_len < 2)
290 return OSMO_TLVP_ERR_OFS_BEYOND_BUFFER;
Harald Welte2fe68472012-07-14 01:50:33 +0200291 if (*(buf+1) & 0x80) {
Harald Welteefdd6412021-01-12 18:07:18 +0100292 if (buf_len < 3)
Harald Welte30a92942020-12-04 14:09:22 +0100293 return OSMO_TLVP_ERR_OFS_BEYOND_BUFFER;
Harald Welteefdd6412021-01-12 18:07:18 +0100294 /* like TL16Vbut without highest bit of len */
Harald Welte2fe68472012-07-14 01:50:33 +0200295 *o_val = buf+3;
296 *o_len = (*(buf+1) & 0x7F) << 8 | *(buf+2);
297 len = *o_len + 3;
Harald Welte2fe68472012-07-14 01:50:33 +0200298 } else {
299 /* like TLV */
300 goto tlv;
301 }
302 break;
Harald Welteec8b4502010-02-20 20:34:29 +0100303 case TLV_TYPE_TvLV:
Harald Welteefdd6412021-01-12 18:07:18 +0100304 if (buf_len < 2)
305 return OSMO_TLVP_ERR_OFS_BEYOND_BUFFER;
Harald Welteec8b4502010-02-20 20:34:29 +0100306 if (*(buf+1) & 0x80) {
307 /* like TLV, but without highest bit of len */
Harald Welteec8b4502010-02-20 20:34:29 +0100308 *o_val = buf+2;
309 *o_len = *(buf+1) & 0x7f;
310 len = *o_len + 2;
Harald Welteec8b4502010-02-20 20:34:29 +0100311 break;
312 }
313 /* like TL16V, fallthrough */
314 case TLV_TYPE_TL16V:
Harald Welteefdd6412021-01-12 18:07:18 +0100315 if (buf_len < 3)
Harald Welte30a92942020-12-04 14:09:22 +0100316 return OSMO_TLVP_ERR_OFS_BEYOND_BUFFER;
Harald Welteec8b4502010-02-20 20:34:29 +0100317 *o_val = buf+3;
318 *o_len = *(buf+1) << 8 | *(buf+2);
319 len = *o_len + 3;
Harald Welteec8b4502010-02-20 20:34:29 +0100320 break;
321 default:
Harald Welte30a92942020-12-04 14:09:22 +0100322 return OSMO_TLVP_ERR_UNKNOWN_TLV_TYPE;
Harald Welteec8b4502010-02-20 20:34:29 +0100323 }
324
Harald Welteefdd6412021-01-12 18:07:18 +0100325 if (buf_len < len) {
326 *o_val = NULL;
327 return OSMO_TLVP_ERR_OFS_LEN_BEYOND_BUFFER;
328 }
Harald Welteec8b4502010-02-20 20:34:29 +0100329 return len;
330}
331
Neels Hofmeyra78b22b2018-04-13 03:36:49 +0200332/*! Parse an entire buffer of TLV encoded Information Elements.
333 * In case of multiple occurences of an IE, keep only the first occurence.
334 * Most GSM related protocols clearly indicate that in case of duplicate
335 * IEs, only the first occurrence shall be used, while any further occurrences
336 * shall be ignored. See e.g. 3GPP TS 24.008 Section 8.6.3.
337 * For multiple occurences, use tlv_parse2().
Harald Welte57c7d372011-08-17 17:50:55 +0200338 * \param[out] dec caller-allocated pointer to \ref tlv_parsed
339 * \param[in] def structure defining the valid TLV tags / configurations
340 * \param[in] buf the input data buffer to be parsed
341 * \param[in] buf_len length of the input data buffer
342 * \param[in] lv_tag an initial LV tag at the start of the buffer
343 * \param[in] lv_tag2 a second initial LV tag following the \a lv_tag
Stefan Sperlingc9bebbd2018-03-16 15:59:01 +0100344 * \returns number of TLV entries parsed; negative in case of error
Harald Welteec8b4502010-02-20 20:34:29 +0100345 */
346int tlv_parse(struct tlv_parsed *dec, const struct tlv_definition *def,
347 const uint8_t *buf, int buf_len, uint8_t lv_tag,
348 uint8_t lv_tag2)
349{
Neels Hofmeyra78b22b2018-04-13 03:36:49 +0200350 return tlv_parse2(dec, 1, def, buf, buf_len, lv_tag, lv_tag2);
351}
352
353/*! Like tlv_parse(), but capable of decoding multiple occurences of the same IE.
354 * Parse an entire buffer of TLV encoded Information Elements.
355 * To decode multiple occurences of IEs, provide in dec an _array_ of tlv_parsed, and
356 * pass the size of that array in dec_multiples. The first occurence of each IE
357 * is stored in dec[0], the second in dec[1] and so forth. If there are more
358 * occurences than the array length given in dec_multiples, the remaining
359 * occurences are dropped.
360 * \param[out] dec caller-allocated pointer to \ref tlv_parsed
361 * \param[in] dec_multiples length of the tlv_parsed[] in \a dec.
362 * \param[in] def structure defining the valid TLV tags / configurations
363 * \param[in] buf the input data buffer to be parsed
364 * \param[in] buf_len length of the input data buffer
365 * \param[in] lv_tag an initial LV tag at the start of the buffer
366 * \param[in] lv_tag2 a second initial LV tag following the \a lv_tag
367 * \returns number of TLV entries parsed; negative in case of error
368 */
369int tlv_parse2(struct tlv_parsed *dec, int dec_multiples,
370 const struct tlv_definition *def, const uint8_t *buf, int buf_len,
371 uint8_t lv_tag, uint8_t lv_tag2)
372{
Harald Welteec8b4502010-02-20 20:34:29 +0100373 int ofs = 0, num_parsed = 0;
374 uint16_t len;
Neels Hofmeyra78b22b2018-04-13 03:36:49 +0200375 int dec_i;
Harald Welteec8b4502010-02-20 20:34:29 +0100376
Neels Hofmeyra78b22b2018-04-13 03:36:49 +0200377 for (dec_i = 0; dec_i < dec_multiples; dec_i++)
378 memset(&dec[dec_i], 0, sizeof(*dec));
Harald Welteec8b4502010-02-20 20:34:29 +0100379
380 if (lv_tag) {
Neels Hofmeyra78b22b2018-04-13 03:36:49 +0200381 const uint8_t *val;
382 uint16_t parsed_len;
Harald Welteec8b4502010-02-20 20:34:29 +0100383 if (ofs > buf_len)
Harald Welte30a92942020-12-04 14:09:22 +0100384 return OSMO_TLVP_ERR_OFS_BEYOND_BUFFER;
Neels Hofmeyra78b22b2018-04-13 03:36:49 +0200385 val = &buf[ofs+1];
386 len = buf[ofs];
387 parsed_len = len + 1;
388 if (ofs + parsed_len > buf_len)
Harald Welte30a92942020-12-04 14:09:22 +0100389 return OSMO_TLVP_ERR_OFS_LEN_BEYOND_BUFFER;
Harald Welteec8b4502010-02-20 20:34:29 +0100390 num_parsed++;
Neels Hofmeyra78b22b2018-04-13 03:36:49 +0200391 ofs += parsed_len;
392 /* store the resulting val and len */
393 for (dec_i = 0; dec_i < dec_multiples; dec_i++) {
394 if (dec[dec_i].lv[lv_tag].val != NULL)
395 continue;
396 dec->lv[lv_tag].val = val;
397 dec->lv[lv_tag].len = len;
398 break;
399 }
Harald Welteec8b4502010-02-20 20:34:29 +0100400 }
401 if (lv_tag2) {
Neels Hofmeyra78b22b2018-04-13 03:36:49 +0200402 const uint8_t *val;
403 uint16_t parsed_len;
Harald Welteec8b4502010-02-20 20:34:29 +0100404 if (ofs > buf_len)
Harald Welte30a92942020-12-04 14:09:22 +0100405 return OSMO_TLVP_ERR_OFS_BEYOND_BUFFER;
Neels Hofmeyra78b22b2018-04-13 03:36:49 +0200406 val = &buf[ofs+1];
407 len = buf[ofs];
408 parsed_len = len + 1;
409 if (ofs + parsed_len > buf_len)
Harald Welte30a92942020-12-04 14:09:22 +0100410 return OSMO_TLVP_ERR_OFS_LEN_BEYOND_BUFFER;
Harald Welteec8b4502010-02-20 20:34:29 +0100411 num_parsed++;
Neels Hofmeyra78b22b2018-04-13 03:36:49 +0200412 ofs += parsed_len;
413 /* store the resulting val and len */
414 for (dec_i = 0; dec_i < dec_multiples; dec_i++) {
415 if (dec[dec_i].lv[lv_tag2].val != NULL)
416 continue;
417 dec->lv[lv_tag2].val = val;
418 dec->lv[lv_tag2].len = len;
419 break;
420 }
Harald Welteec8b4502010-02-20 20:34:29 +0100421 }
422
423 while (ofs < buf_len) {
424 int rv;
425 uint8_t tag;
426 const uint8_t *val;
427
428 rv = tlv_parse_one(&tag, &len, &val, def,
429 &buf[ofs], buf_len-ofs);
430 if (rv < 0)
431 return rv;
Neels Hofmeyra78b22b2018-04-13 03:36:49 +0200432 for (dec_i = 0; dec_i < dec_multiples; dec_i++) {
433 if (dec[dec_i].lv[tag].val != NULL)
434 continue;
435 dec[dec_i].lv[tag].val = val;
436 dec[dec_i].lv[tag].len = len;
437 break;
Harald Weltebf383a12018-02-02 12:11:14 +0100438 }
Harald Welteec8b4502010-02-20 20:34:29 +0100439 ofs += rv;
440 num_parsed++;
441 }
442 //tlv_dump(dec);
443 return num_parsed;
444}
445
Pau Espin Pedrold1105292021-04-14 17:21:02 +0200446/*! take a master (src) tlv_definition and fill up all empty slots in 'dst'
Harald Welte96e2a002017-06-12 21:44:18 +0200447 * \param dst TLV parser definition that is to be patched
448 * \param[in] src TLV parser definition whose content is patched into \a dst */
Harald Welteec8b4502010-02-20 20:34:29 +0100449void tlv_def_patch(struct tlv_definition *dst, const struct tlv_definition *src)
450{
451 int i;
452
453 for (i = 0; i < ARRAY_SIZE(dst->def); i++) {
454 if (src->def[i].type == TLV_TYPE_NONE)
455 continue;
456 if (dst->def[i].type == TLV_TYPE_NONE)
457 dst->def[i] = src->def[i];
458 }
459}
460
461static __attribute__((constructor)) void on_dso_load_tlv(void)
462{
463 int i;
464 for (i = 0; i < ARRAY_SIZE(tvlv_att_def.def); i++)
465 tvlv_att_def.def[i].type = TLV_TYPE_TvLV;
Harald Welte2fe68472012-07-14 01:50:33 +0200466
467 for (i = 0; i < ARRAY_SIZE(vtvlv_gan_att_def.def); i++)
468 vtvlv_gan_att_def.def[i].type = TLV_TYPE_vTvLV_GAN;
Harald Welteec8b4502010-02-20 20:34:29 +0100469}
Harald Welte57c7d372011-08-17 17:50:55 +0200470
Harald Weltefbd02fa2016-04-25 15:19:35 +0200471/*! Advance the data pointer, subtract length and assign value pointer
472 * \param data pointer to the pointer to data
473 * \param data_len pointer to size_t containing \arg data length
474 * \param[in] len the length that we expect the fixed IE to hav
475 * \param[out] value pointer to pointer of value part of IE
476 * \returns length of IE value; negative in case of error
477 */
478int osmo_shift_v_fixed(uint8_t **data, size_t *data_len,
479 size_t len, uint8_t **value)
480{
481 if (len > *data_len)
482 goto fail;
483
484 if (value)
485 *value = *data;
486
487 *data += len;
488 *data_len -= len;
489
490 return len;
491
492fail:
493 *data += *data_len;
494 *data_len = 0;
495 return -1;
496}
497
498/*! Match tag, check length and assign value pointer
499 * \param data pointer to the pointer to data
500 * \param data_len pointer to size_t containing \arg data length
501 * \param[in] tag the tag (IEI) that we expect at \arg data
502 * \param[in] len the length that we expect the fixed IE to have
503 * \param[out] value pointer to pointer of value part of IE
504 * \returns length of IE value; negative in case of error
505 */
506int osmo_match_shift_tv_fixed(uint8_t **data, size_t *data_len,
507 uint8_t tag, size_t len,
508 uint8_t **value)
509{
510 size_t ie_len;
511
512 if (*data_len == 0)
513 goto fail;
514
515 if ((*data)[0] != tag)
516 return 0;
517
518 if (len > *data_len - 1)
519 goto fail;
520
521 if (value)
522 *value = *data + 1;
523
524 ie_len = len + 1;
525 *data += ie_len;
526 *data_len -= ie_len;
527
528 return ie_len;
529
530fail:
531 *data += *data_len;
532 *data_len = 0;
533 return -1;
534}
535
536/*! Verify TLV header and advance data / subtract length
537 * \param data pointer to the pointer to data
538 * \param data_len pointer to size_t containing \arg data length
539 * \param[in] expected_tag the tag (IEI) that we expect at \arg data
540 * \param[out] value pointer to pointer of value part of IE
541 * \param[out] value_len pointer to length of \arg value
542 * \returns length of IE value; negative in case of error
543 */
544int osmo_match_shift_tlv(uint8_t **data, size_t *data_len,
545 uint8_t expected_tag, uint8_t **value,
546 size_t *value_len)
547{
548 int rc;
549 uint8_t tag;
550 uint8_t *old_data = *data;
551 size_t old_data_len = *data_len;
552
553 rc = osmo_shift_tlv(data, data_len, &tag, value, value_len);
554
555 if (rc > 0 && tag != expected_tag) {
556 *data = old_data;
557 *data_len = old_data_len;
558 return 0;
559 }
560
561 return rc;
562}
563
564/*! Extract TLV and advance data pointer + subtract length
565 * \param data pointer to the pointer to data
566 * \param data_len pointer to size_t containing \arg data lengt
567 * \param[out] tag extract the tag (IEI) at start of \arg data
568 * \param[out] value extracted pointer to value part of TLV
569 * \param[out] value_len extracted length of \arg value
570 * \returns number of bytes subtracted
571 */
572int osmo_shift_tlv(uint8_t **data, size_t *data_len,
573 uint8_t *tag, uint8_t **value, size_t *value_len)
574{
575 size_t len;
576 size_t ie_len;
577
578 if (*data_len < 2)
579 goto fail;
580
581 len = (*data)[1];
582 if (len > *data_len - 2)
583 goto fail;
584
585 if (tag)
586 *tag = (*data)[0];
587 if (value)
588 *value = *data + 2;
589 if (value_len)
590 *value_len = len;
591
592 ie_len = len + 2;
593
594 *data += ie_len;
595 *data_len -= ie_len;
596
597 return ie_len;
598
599fail:
600 *data += *data_len;
601 *data_len = 0;
602 return -1;
603}
604
605/*! Extract LV and advance data pointer + subtract length
606 * \param data pointer to the pointer to data
607 * \param data_len pointer to size_t containing \arg data lengt
608 * \param[out] value extracted pointer to value part of TLV
609 * \param[out] value_len extracted length of \arg value
610 * \returns number of bytes subtracted
611 */
612int osmo_shift_lv(uint8_t **data, size_t *data_len,
613 uint8_t **value, size_t *value_len)
614{
615 size_t len;
616 size_t ie_len;
617
618 if (*data_len < 1)
619 goto fail;
620
621 len = (*data)[0];
622 if (len > *data_len - 1)
623 goto fail;
624
625 if (value)
626 *value = *data + 1;
627 if (value_len)
628 *value_len = len;
629
630 ie_len = len + 1;
631 *data += ie_len;
632 *data_len -= ie_len;
633
634 return ie_len;
635
636fail:
637 *data += *data_len;
638 *data_len = 0;
639 return -1;
640}
641
Harald Welte95109922020-12-04 13:55:38 +0100642static __thread char ienamebuf[32];
643static __thread char msgnamebuf[32];
644
645/*! get the message name for given msg_type in protocol pdef */
646const char *osmo_tlv_prot_msg_name(const struct osmo_tlv_prot_def *pdef, uint8_t msg_type)
647{
648 if (pdef->msg_def[msg_type].name) {
649 return pdef->msg_def[msg_type].name;
650 } else if (pdef->msgt_names) {
651 return get_value_string(pdef->msgt_names, msg_type);
652 } else {
653 snprintf(msgnamebuf, sizeof(msgnamebuf), "Unknown msg_type 0x%02x", msg_type);
654 return msgnamebuf;
655 }
656}
657
658/*! get the IE name for given IEI in protocol pdef */
659const char *osmo_tlv_prot_ie_name(const struct osmo_tlv_prot_def *pdef, uint8_t iei)
660{
661 if (pdef->ie_def[iei].name) {
662 return pdef->ie_def[iei].name;
663 } else {
664 snprintf(ienamebuf, sizeof(ienamebuf), "Unknown IEI 0x%02x", iei);
665 return ienamebuf;
666 }
667}
668
669/*! Validate an already TLV-decoded message against the protocol definition.
670 * \param[in] pdef protocol definition of given protocol
671 * \param[in] msg_type message type of the parsed message
672 * \param[in] tp TLV parser result
673 * \param[in] log_subsys logging sub-system for log messages
674 * \param[in] log_pfx prefix for log messages
675 * \returns 0 in case of success; negative osmo_tlv_parser_error in case of error
676 */
677int osmo_tlv_prot_validate_tp(const struct osmo_tlv_prot_def *pdef, uint8_t msg_type,
678 const struct tlv_parsed *tp, int log_subsys, const char *log_pfx)
679{
680 const struct osmo_tlv_prot_msg_def *msg_def= &pdef->msg_def[msg_type];
Harald Welte30a92942020-12-04 14:09:22 +0100681 unsigned int err = 0;
Harald Welte95109922020-12-04 13:55:38 +0100682 unsigned int i;
683
684 if (msg_def->mand_ies) {
685 for (i = 0; i < msg_def->mand_count; i++) {
686 uint8_t iei = msg_def->mand_ies[i];
687 if (!TLVP_PRESENT(tp, iei)) {
688 LOGP(log_subsys, LOGL_ERROR, "%s %s %s: Missing Mandatory IE: %s\n",
689 log_pfx, pdef->name, osmo_tlv_prot_msg_name(pdef, msg_type),
690 osmo_tlv_prot_ie_name(pdef, iei));
Harald Welte30a92942020-12-04 14:09:22 +0100691 if (!err)
692 err = OSMO_TLVP_ERR_MAND_IE_MISSING;
Harald Welte95109922020-12-04 13:55:38 +0100693 }
694 }
695 }
696
697 for (i = 0; i < ARRAY_SIZE(tp->lv); i++) {
698 uint16_t min_len;
699
700 if (!TLVP_PRESENT(tp, i))
701 continue;
702
703 min_len = pdef->ie_def[i].min_len;
704 if (TLVP_LEN(tp, i) < min_len) {
705 LOGP(log_subsys, LOGL_ERROR, "%s %s %s: Short IE %s: %u < %u\n", log_pfx,
706 pdef->name, osmo_tlv_prot_msg_name(pdef, msg_type),
707 osmo_tlv_prot_ie_name(pdef, i), TLVP_LEN(tp, i), min_len);
Harald Welte30a92942020-12-04 14:09:22 +0100708 if (!err)
709 err = OSMO_TLVP_ERR_IE_TOO_SHORT;
Harald Welte95109922020-12-04 13:55:38 +0100710 }
711 }
712
Harald Welte30a92942020-12-04 14:09:22 +0100713 return err;
Harald Welte95109922020-12-04 13:55:38 +0100714}
715
716/*! Parse + Validate a TLV-encoded message against the protocol definition.
717 * \param[in] pdef protocol definition of given protocol
718 * \param[out] dec caller-allocated pointer to \ref tlv_parsed
719 * \param[in] dec_multiples length of the tlv_parsed[] in \a dec.
720 * \param[in] msg_type message type of the parsed message
721 * \param[in] buf the input data buffer to be parsed
722 * \param[in] buf_len length of the input data buffer
723 * \param[in] lv_tag an initial LV tag at the start of the buffer
724 * \param[in] lv_tag2 a second initial LV tag following the \a lv_tag
725 * \param[in] log_subsys logging sub-system for log messages
726 * \param[in] log_pfx prefix for log messages
727 * \returns 0 in case of success; negative osmo_tlv_parser_error in case of error
728 */
729int osmo_tlv_prot_parse(const struct osmo_tlv_prot_def *pdef,
730 struct tlv_parsed *dec, unsigned int dec_multiples, uint8_t msg_type,
731 const uint8_t *buf, unsigned int buf_len, uint8_t lv_tag, uint8_t lv_tag2,
732 int log_subsys, const char *log_pfx)
733{
734 int rc;
735
736 rc = tlv_parse2(dec, dec_multiples, pdef->tlv_def, buf, buf_len, lv_tag, lv_tag2);
737 if (rc < 0) {
738 LOGP(log_subsys, LOGL_ERROR, "%s %s %s: TLV parser error %d\n", log_pfx,
739 pdef->name, osmo_tlv_prot_msg_name(pdef, msg_type), rc);
740 return rc;
741 }
742
743 return osmo_tlv_prot_validate_tp(pdef, msg_type, dec, log_subsys, log_pfx);
744}
745
Sylvain Munautdca7d2c2012-04-18 21:53:23 +0200746/*! @} */