blob: 5eee53a67b212747fdc11ca8dc231eb259f3ab1d [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>
Pablo Neira Ayuso83419342011-03-22 16:36:13 +010022#include <osmocom/core/utils.h>
23#include <osmocom/gsm/tlv.h>
Harald Welteec8b4502010-02-20 20:34:29 +010024
Harald Welte57c7d372011-08-17 17:50:55 +020025/*! \addtogroup tlv
26 * @{
27 */
Katerina Barone-Adesic28c6a02013-02-15 13:27:59 +010028/*! \file tlv_parser.c */
Harald Welte57c7d372011-08-17 17:50:55 +020029
Harald Welteec8b4502010-02-20 20:34:29 +010030struct tlv_definition tvlv_att_def;
Harald Welte2fe68472012-07-14 01:50:33 +020031struct tlv_definition vtvlv_gan_att_def;
Harald Welteec8b4502010-02-20 20:34:29 +010032
Harald Welte57c7d372011-08-17 17:50:55 +020033/*! \brief Dump pasred TLV structure to stdout */
Harald Welteec8b4502010-02-20 20:34:29 +010034int tlv_dump(struct tlv_parsed *dec)
35{
36 int i;
37
38 for (i = 0; i <= 0xff; i++) {
39 if (!dec->lv[i].val)
40 continue;
41 printf("T=%02x L=%d\n", i, dec->lv[i].len);
42 }
43 return 0;
44}
45
Harald Welte57c7d372011-08-17 17:50:55 +020046/*! \brief Parse a single TLV encoded IE
47 * \param[out] o_tag the tag of the IE that was found
48 * \param[out] o_len length of the IE that was found
49 * \param[out] o_val pointer to the data of the IE that was found
50 * \param[in] def structure defining the valid TLV tags / configurations
51 * \param[in] buf the input data buffer to be parsed
52 * \param[in] buf_len length of the input data buffer
53 * \returns number of bytes consumed by the TLV entry / IE parsed
Harald Welteec8b4502010-02-20 20:34:29 +010054 */
55int tlv_parse_one(uint8_t *o_tag, uint16_t *o_len, const uint8_t **o_val,
56 const struct tlv_definition *def,
57 const uint8_t *buf, int buf_len)
58{
59 uint8_t tag;
60 int len;
61
62 tag = *buf;
63 *o_tag = tag;
64
Andreas Eversbergcd2a74b2010-07-12 08:55:14 +020065 /* single octet TV IE */
66 if (def->def[tag & 0xf0].type == TLV_TYPE_SINGLE_TV) {
67 *o_tag = tag & 0xf0;
68 *o_val = buf;
69 *o_len = 1;
70 return 1;
71 }
72
Harald Welteec8b4502010-02-20 20:34:29 +010073 /* FIXME: use tables for knwon IEI */
74 switch (def->def[tag].type) {
75 case TLV_TYPE_T:
76 /* GSM TS 04.07 11.2.4: Type 1 TV or Type 2 T */
77 *o_val = buf;
78 *o_len = 0;
79 len = 1;
80 break;
81 case TLV_TYPE_TV:
82 *o_val = buf+1;
83 *o_len = 1;
84 len = 2;
85 break;
86 case TLV_TYPE_FIXED:
87 *o_val = buf+1;
88 *o_len = def->def[tag].fixed_len;
89 len = def->def[tag].fixed_len + 1;
90 break;
91 case TLV_TYPE_TLV:
Harald Welte2fe68472012-07-14 01:50:33 +020092tlv: /* GSM TS 04.07 11.2.4: Type 4 TLV */
Harald Welteec8b4502010-02-20 20:34:29 +010093 if (buf + 1 > buf + buf_len)
94 return -1;
95 *o_val = buf+2;
96 *o_len = *(buf+1);
97 len = *o_len + 2;
98 if (len > buf_len)
99 return -2;
100 break;
Harald Welte2fe68472012-07-14 01:50:33 +0200101 case TLV_TYPE_vTvLV_GAN: /* 44.318 / 11.1.4 */
102 /* FIXME: variable-length TAG! */
103 if (*(buf+1) & 0x80) {
104 /* like TL16Vbut without highest bit of len */
105 if (2 > buf_len)
106 return -1;
107 *o_val = buf+3;
108 *o_len = (*(buf+1) & 0x7F) << 8 | *(buf+2);
109 len = *o_len + 3;
110 if (len > buf_len)
111 return -2;
112 } else {
113 /* like TLV */
114 goto tlv;
115 }
116 break;
Harald Welteec8b4502010-02-20 20:34:29 +0100117 case TLV_TYPE_TvLV:
118 if (*(buf+1) & 0x80) {
119 /* like TLV, but without highest bit of len */
120 if (buf + 1 > buf + buf_len)
121 return -1;
122 *o_val = buf+2;
123 *o_len = *(buf+1) & 0x7f;
124 len = *o_len + 2;
125 if (len > buf_len)
126 return -2;
127 break;
128 }
129 /* like TL16V, fallthrough */
130 case TLV_TYPE_TL16V:
131 if (2 > buf_len)
132 return -1;
133 *o_val = buf+3;
134 *o_len = *(buf+1) << 8 | *(buf+2);
135 len = *o_len + 3;
136 if (len > buf_len)
137 return -2;
138 break;
139 default:
140 return -3;
141 }
142
143 return len;
144}
145
Harald Welte57c7d372011-08-17 17:50:55 +0200146/*! \brief Parse an entire buffer of TLV encoded Information Eleemnts
147 * \param[out] dec caller-allocated pointer to \ref tlv_parsed
148 * \param[in] def structure defining the valid TLV tags / configurations
149 * \param[in] buf the input data buffer to be parsed
150 * \param[in] buf_len length of the input data buffer
151 * \param[in] lv_tag an initial LV tag at the start of the buffer
152 * \param[in] lv_tag2 a second initial LV tag following the \a lv_tag
153 * \returns number of bytes consumed by the TLV entry / IE parsed
Harald Welteec8b4502010-02-20 20:34:29 +0100154 */
155int tlv_parse(struct tlv_parsed *dec, const struct tlv_definition *def,
156 const uint8_t *buf, int buf_len, uint8_t lv_tag,
157 uint8_t lv_tag2)
158{
159 int ofs = 0, num_parsed = 0;
160 uint16_t len;
161
162 memset(dec, 0, sizeof(*dec));
163
164 if (lv_tag) {
165 if (ofs > buf_len)
166 return -1;
167 dec->lv[lv_tag].val = &buf[ofs+1];
168 dec->lv[lv_tag].len = buf[ofs];
169 len = dec->lv[lv_tag].len + 1;
170 if (ofs + len > buf_len)
171 return -2;
172 num_parsed++;
173 ofs += len;
174 }
175 if (lv_tag2) {
176 if (ofs > buf_len)
177 return -1;
178 dec->lv[lv_tag2].val = &buf[ofs+1];
179 dec->lv[lv_tag2].len = buf[ofs];
180 len = dec->lv[lv_tag2].len + 1;
181 if (ofs + len > buf_len)
182 return -2;
183 num_parsed++;
184 ofs += len;
185 }
186
187 while (ofs < buf_len) {
188 int rv;
189 uint8_t tag;
190 const uint8_t *val;
191
192 rv = tlv_parse_one(&tag, &len, &val, def,
193 &buf[ofs], buf_len-ofs);
194 if (rv < 0)
195 return rv;
196 dec->lv[tag].val = val;
197 dec->lv[tag].len = len;
198 ofs += rv;
199 num_parsed++;
200 }
201 //tlv_dump(dec);
202 return num_parsed;
203}
204
Harald Welte57c7d372011-08-17 17:50:55 +0200205/*! \brief take a master (src) tlvdev and fill up all empty slots in 'dst' */
Harald Welteec8b4502010-02-20 20:34:29 +0100206void tlv_def_patch(struct tlv_definition *dst, const struct tlv_definition *src)
207{
208 int i;
209
210 for (i = 0; i < ARRAY_SIZE(dst->def); i++) {
211 if (src->def[i].type == TLV_TYPE_NONE)
212 continue;
213 if (dst->def[i].type == TLV_TYPE_NONE)
214 dst->def[i] = src->def[i];
215 }
216}
217
218static __attribute__((constructor)) void on_dso_load_tlv(void)
219{
220 int i;
221 for (i = 0; i < ARRAY_SIZE(tvlv_att_def.def); i++)
222 tvlv_att_def.def[i].type = TLV_TYPE_TvLV;
Harald Welte2fe68472012-07-14 01:50:33 +0200223
224 for (i = 0; i < ARRAY_SIZE(vtvlv_gan_att_def.def); i++)
225 vtvlv_gan_att_def.def[i].type = TLV_TYPE_vTvLV_GAN;
Harald Welteec8b4502010-02-20 20:34:29 +0100226}
Harald Welte57c7d372011-08-17 17:50:55 +0200227
Sylvain Munautdca7d2c2012-04-18 21:53:23 +0200228/*! @} */