blob: 0ac90929c14304dbba0aebf86ce07f4189c6422b [file] [log] [blame]
Harald Welteec8b4502010-02-20 20:34:29 +01001#include <stdio.h>
2#include <stdint.h>
Pablo Neira Ayuso83419342011-03-22 16:36:13 +01003#include <osmocom/core/utils.h>
4#include <osmocom/gsm/tlv.h>
Harald Welteec8b4502010-02-20 20:34:29 +01005
Harald Welte57c7d372011-08-17 17:50:55 +02006/*! \addtogroup tlv
7 * @{
8 */
9/*! \file tlv.c */
10
Harald Welteec8b4502010-02-20 20:34:29 +010011struct tlv_definition tvlv_att_def;
12
Harald Welte57c7d372011-08-17 17:50:55 +020013/*! \brief Dump pasred TLV structure to stdout */
Harald Welteec8b4502010-02-20 20:34:29 +010014int tlv_dump(struct tlv_parsed *dec)
15{
16 int i;
17
18 for (i = 0; i <= 0xff; i++) {
19 if (!dec->lv[i].val)
20 continue;
21 printf("T=%02x L=%d\n", i, dec->lv[i].len);
22 }
23 return 0;
24}
25
Harald Welte57c7d372011-08-17 17:50:55 +020026/*! \brief Parse a single TLV encoded IE
27 * \param[out] o_tag the tag of the IE that was found
28 * \param[out] o_len length of the IE that was found
29 * \param[out] o_val pointer to the data of the IE that was found
30 * \param[in] def structure defining the valid TLV tags / configurations
31 * \param[in] buf the input data buffer to be parsed
32 * \param[in] buf_len length of the input data buffer
33 * \returns number of bytes consumed by the TLV entry / IE parsed
Harald Welteec8b4502010-02-20 20:34:29 +010034 */
35int tlv_parse_one(uint8_t *o_tag, uint16_t *o_len, const uint8_t **o_val,
36 const struct tlv_definition *def,
37 const uint8_t *buf, int buf_len)
38{
39 uint8_t tag;
40 int len;
41
42 tag = *buf;
43 *o_tag = tag;
44
Andreas Eversbergcd2a74b2010-07-12 08:55:14 +020045 /* single octet TV IE */
46 if (def->def[tag & 0xf0].type == TLV_TYPE_SINGLE_TV) {
47 *o_tag = tag & 0xf0;
48 *o_val = buf;
49 *o_len = 1;
50 return 1;
51 }
52
Harald Welteec8b4502010-02-20 20:34:29 +010053 /* FIXME: use tables for knwon IEI */
54 switch (def->def[tag].type) {
55 case TLV_TYPE_T:
56 /* GSM TS 04.07 11.2.4: Type 1 TV or Type 2 T */
57 *o_val = buf;
58 *o_len = 0;
59 len = 1;
60 break;
61 case TLV_TYPE_TV:
62 *o_val = buf+1;
63 *o_len = 1;
64 len = 2;
65 break;
66 case TLV_TYPE_FIXED:
67 *o_val = buf+1;
68 *o_len = def->def[tag].fixed_len;
69 len = def->def[tag].fixed_len + 1;
70 break;
71 case TLV_TYPE_TLV:
72 /* GSM TS 04.07 11.2.4: Type 4 TLV */
73 if (buf + 1 > buf + buf_len)
74 return -1;
75 *o_val = buf+2;
76 *o_len = *(buf+1);
77 len = *o_len + 2;
78 if (len > buf_len)
79 return -2;
80 break;
81 case TLV_TYPE_TvLV:
82 if (*(buf+1) & 0x80) {
83 /* like TLV, but without highest bit of len */
84 if (buf + 1 > buf + buf_len)
85 return -1;
86 *o_val = buf+2;
87 *o_len = *(buf+1) & 0x7f;
88 len = *o_len + 2;
89 if (len > buf_len)
90 return -2;
91 break;
92 }
93 /* like TL16V, fallthrough */
94 case TLV_TYPE_TL16V:
95 if (2 > buf_len)
96 return -1;
97 *o_val = buf+3;
98 *o_len = *(buf+1) << 8 | *(buf+2);
99 len = *o_len + 3;
100 if (len > buf_len)
101 return -2;
102 break;
103 default:
104 return -3;
105 }
106
107 return len;
108}
109
Harald Welte57c7d372011-08-17 17:50:55 +0200110/*! \brief Parse an entire buffer of TLV encoded Information Eleemnts
111 * \param[out] dec caller-allocated pointer to \ref tlv_parsed
112 * \param[in] def structure defining the valid TLV tags / configurations
113 * \param[in] buf the input data buffer to be parsed
114 * \param[in] buf_len length of the input data buffer
115 * \param[in] lv_tag an initial LV tag at the start of the buffer
116 * \param[in] lv_tag2 a second initial LV tag following the \a lv_tag
117 * \returns number of bytes consumed by the TLV entry / IE parsed
Harald Welteec8b4502010-02-20 20:34:29 +0100118 */
119int tlv_parse(struct tlv_parsed *dec, const struct tlv_definition *def,
120 const uint8_t *buf, int buf_len, uint8_t lv_tag,
121 uint8_t lv_tag2)
122{
123 int ofs = 0, num_parsed = 0;
124 uint16_t len;
125
126 memset(dec, 0, sizeof(*dec));
127
128 if (lv_tag) {
129 if (ofs > buf_len)
130 return -1;
131 dec->lv[lv_tag].val = &buf[ofs+1];
132 dec->lv[lv_tag].len = buf[ofs];
133 len = dec->lv[lv_tag].len + 1;
134 if (ofs + len > buf_len)
135 return -2;
136 num_parsed++;
137 ofs += len;
138 }
139 if (lv_tag2) {
140 if (ofs > buf_len)
141 return -1;
142 dec->lv[lv_tag2].val = &buf[ofs+1];
143 dec->lv[lv_tag2].len = buf[ofs];
144 len = dec->lv[lv_tag2].len + 1;
145 if (ofs + len > buf_len)
146 return -2;
147 num_parsed++;
148 ofs += len;
149 }
150
151 while (ofs < buf_len) {
152 int rv;
153 uint8_t tag;
154 const uint8_t *val;
155
156 rv = tlv_parse_one(&tag, &len, &val, def,
157 &buf[ofs], buf_len-ofs);
158 if (rv < 0)
159 return rv;
160 dec->lv[tag].val = val;
161 dec->lv[tag].len = len;
162 ofs += rv;
163 num_parsed++;
164 }
165 //tlv_dump(dec);
166 return num_parsed;
167}
168
Harald Welte57c7d372011-08-17 17:50:55 +0200169/*! \brief take a master (src) tlvdev and fill up all empty slots in 'dst' */
Harald Welteec8b4502010-02-20 20:34:29 +0100170void tlv_def_patch(struct tlv_definition *dst, const struct tlv_definition *src)
171{
172 int i;
173
174 for (i = 0; i < ARRAY_SIZE(dst->def); i++) {
175 if (src->def[i].type == TLV_TYPE_NONE)
176 continue;
177 if (dst->def[i].type == TLV_TYPE_NONE)
178 dst->def[i] = src->def[i];
179 }
180}
181
182static __attribute__((constructor)) void on_dso_load_tlv(void)
183{
184 int i;
185 for (i = 0; i < ARRAY_SIZE(tvlv_att_def.def); i++)
186 tvlv_att_def.def[i].type = TLV_TYPE_TvLV;
187}
Harald Welte57c7d372011-08-17 17:50:55 +0200188
Sylvain Munautdca7d2c2012-04-18 21:53:23 +0200189/*! @} */