blob: fd0045f978ca1cd1b03eb62e326b76e32a80da51 [file] [log] [blame]
Harald Weltee0590df2009-02-15 03:34:15 +00001#include <stdio.h>
Harald Welte91b5b0d2009-02-06 12:51:39 +00002#include <openbsc/tlv.h>
Harald Welte73310c32009-10-24 10:04:02 +02003#include <openbsc/gsm_data.h>
4
5struct tlv_definition tvlv_att_def;
Harald Welte91b5b0d2009-02-06 12:51:39 +00006
Harald Weltee0590df2009-02-15 03:34:15 +00007int tlv_dump(struct tlv_parsed *dec)
8{
9 int i;
10
11 for (i = 0; i <= 0xff; i++) {
12 if (!dec->lv[i].val)
13 continue;
14 printf("T=%02x L=%d\n", i, dec->lv[i].len);
15 }
16 return 0;
17}
18
Sylvain Munauteb429b72009-10-26 20:19:59 +010019/* o_tag: output: tag found
20 * o_len: output: length of the data
21 * o_val: output: pointer to the data
22 * def: input: a structure defining the valid TLV tags / configurations
23 * buf: input: the input data buffer to be parsed
24 * buf_len: input: the length of the input data buffer
25 *
26 * Also, returns the number of bytes consumed by the TLV entry
27 */
28int tlv_parse_one(u_int8_t *o_tag, u_int16_t *o_len, const u_int8_t **o_val,
29 const struct tlv_definition *def,
30 const u_int8_t *buf, int buf_len)
31{
32 u_int8_t tag;
33 int len;
34
35 tag = *buf;
36 *o_tag = tag;
37
38 /* FIXME: use tables for knwon IEI */
39 switch (def->def[tag].type) {
40 case TLV_TYPE_T:
41 /* GSM TS 04.07 11.2.4: Type 1 TV or Type 2 T */
42 *o_val = buf;
43 *o_len = 0;
44 len = 1;
45 break;
46 case TLV_TYPE_TV:
47 *o_val = buf+1;
48 *o_len = 1;
49 len = 2;
50 break;
51 case TLV_TYPE_FIXED:
52 *o_val = buf+1;
53 *o_len = def->def[tag].fixed_len;
54 len = def->def[tag].fixed_len + 1;
55 break;
56 case TLV_TYPE_TLV:
57 /* GSM TS 04.07 11.2.4: Type 4 TLV */
58 if (buf + 1 > buf + buf_len)
59 return -1;
60 *o_val = buf+2;
61 *o_len = *(buf+1);
62 len = *o_len + 2;
63 if (len > buf_len)
64 return -2;
65 break;
66 case TLV_TYPE_TvLV:
67 if (*(buf+1) & 0x80) {
68 /* like TLV, but without highest bit of len */
69 if (buf + 1 > buf + buf_len)
70 return -1;
71 *o_val = buf+2;
72 *o_len = *(buf+1) & 0x7f;
73 len = *o_len + 2;
74 if (len > buf_len)
75 return -2;
76 break;
77 }
78 /* like TL16V, fallthrough */
79 case TLV_TYPE_TL16V:
80 if (2 > buf_len)
81 return -1;
82 *o_val = buf+3;
83 *o_len = *(buf+1) << 8 | *(buf+2);
84 len = *o_len + 3;
85 if (len > buf_len)
86 return -2;
87 break;
88 default:
89 return -3;
90 }
91
92 return len;
93}
94
Harald Weltea4d49e92009-05-23 06:39:58 +000095/* dec: output: a caller-allocated pointer to a struct tlv_parsed,
96 * def: input: a structure defining the valid TLV tags / configurations
97 * buf: input: the input data buffer to be parsed
98 * buf_len: input: the length of the input data buffer
99 * lv_tag: input: an initial LV tag at the start of the buffer
100 * lv_tag2: input: a second initial LV tag following lv_tag
101 */
Harald Welte7bc4cbc2009-02-21 12:59:22 +0000102int tlv_parse(struct tlv_parsed *dec, const struct tlv_definition *def,
Harald Weltea4d49e92009-05-23 06:39:58 +0000103 const u_int8_t *buf, int buf_len, u_int8_t lv_tag,
104 u_int8_t lv_tag2)
Harald Welte91b5b0d2009-02-06 12:51:39 +0000105{
Sylvain Munauteb429b72009-10-26 20:19:59 +0100106 int ofs = 0, num_parsed = 0;
107 u_int16_t len;
Harald Welte91b5b0d2009-02-06 12:51:39 +0000108
Harald Welte2fa79342009-02-14 19:07:10 +0000109 memset(dec, 0, sizeof(*dec));
Harald Welte91b5b0d2009-02-06 12:51:39 +0000110
Harald Weltea4d49e92009-05-23 06:39:58 +0000111 if (lv_tag) {
Sylvain Munauteb429b72009-10-26 20:19:59 +0100112 if (ofs > buf_len)
Harald Weltea4d49e92009-05-23 06:39:58 +0000113 return -1;
Sylvain Munauteb429b72009-10-26 20:19:59 +0100114 dec->lv[lv_tag].val = &buf[ofs+1];
115 dec->lv[lv_tag].len = buf[ofs];
Harald Weltea4d49e92009-05-23 06:39:58 +0000116 len = dec->lv[lv_tag].len + 1;
Sylvain Munauteb429b72009-10-26 20:19:59 +0100117 if (ofs + len > buf_len)
Harald Weltea4d49e92009-05-23 06:39:58 +0000118 return -2;
119 num_parsed++;
Sylvain Munauteb429b72009-10-26 20:19:59 +0100120 ofs += len;
Harald Weltea4d49e92009-05-23 06:39:58 +0000121 }
122 if (lv_tag2) {
Sylvain Munauteb429b72009-10-26 20:19:59 +0100123 if (ofs > buf_len)
Harald Weltea4d49e92009-05-23 06:39:58 +0000124 return -1;
Sylvain Munauteb429b72009-10-26 20:19:59 +0100125 dec->lv[lv_tag2].val = &buf[ofs+1];
126 dec->lv[lv_tag2].len = buf[ofs];
Harald Weltea4d49e92009-05-23 06:39:58 +0000127 len = dec->lv[lv_tag2].len + 1;
Sylvain Munauteb429b72009-10-26 20:19:59 +0100128 if (ofs + len > buf_len)
Harald Weltea4d49e92009-05-23 06:39:58 +0000129 return -2;
130 num_parsed++;
Sylvain Munauteb429b72009-10-26 20:19:59 +0100131 ofs += len;
Harald Weltea4d49e92009-05-23 06:39:58 +0000132 }
133
Sylvain Munauteb429b72009-10-26 20:19:59 +0100134 while (ofs < buf_len) {
135 int rv;
136 u_int8_t tag;
137 const u_int8_t *val;
138
139 rv = tlv_parse_one(&tag, &len, &val, def,
140 &buf[ofs], buf_len-ofs);
141 if (rv < 0)
142 return rv;
143 dec->lv[tag].val = val;
144 dec->lv[tag].len = len;
145 ofs += rv;
146 num_parsed++;
Harald Welte91b5b0d2009-02-06 12:51:39 +0000147 }
Harald Weltee0590df2009-02-15 03:34:15 +0000148 //tlv_dump(dec);
Harald Welte2fa79342009-02-14 19:07:10 +0000149 return num_parsed;
Harald Welte91b5b0d2009-02-06 12:51:39 +0000150}
Harald Weltee0590df2009-02-15 03:34:15 +0000151
Harald Welte73310c32009-10-24 10:04:02 +0200152static __attribute__((constructor)) void on_dso_load_tlv(void)
153{
154 int i;
155 for (i = 0; i < ARRAY_SIZE(tvlv_att_def.def); i++)
156 tvlv_att_def.def[i].type = TLV_TYPE_TvLV;
157}