blob: fd0045f978ca1cd1b03eb62e326b76e32a80da51 [file] [log] [blame]
Harald Welte59b04682009-06-10 05:40:52 +08001#include <stdio.h>
2#include <openbsc/tlv.h>
Harald Welte724ddb82009-10-24 10:04:02 +02003#include <openbsc/gsm_data.h>
4
5struct tlv_definition tvlv_att_def;
Harald Welte59b04682009-06-10 05:40:52 +08006
7int 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 Munaut1ebac142009-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 Welte59b04682009-06-10 05:40:52 +080095/* 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 */
102int tlv_parse(struct tlv_parsed *dec, const struct tlv_definition *def,
103 const u_int8_t *buf, int buf_len, u_int8_t lv_tag,
104 u_int8_t lv_tag2)
105{
Sylvain Munaut1ebac142009-10-26 20:19:59 +0100106 int ofs = 0, num_parsed = 0;
107 u_int16_t len;
Harald Welte59b04682009-06-10 05:40:52 +0800108
109 memset(dec, 0, sizeof(*dec));
110
111 if (lv_tag) {
Sylvain Munaut1ebac142009-10-26 20:19:59 +0100112 if (ofs > buf_len)
Harald Welte59b04682009-06-10 05:40:52 +0800113 return -1;
Sylvain Munaut1ebac142009-10-26 20:19:59 +0100114 dec->lv[lv_tag].val = &buf[ofs+1];
115 dec->lv[lv_tag].len = buf[ofs];
Harald Welte59b04682009-06-10 05:40:52 +0800116 len = dec->lv[lv_tag].len + 1;
Sylvain Munaut1ebac142009-10-26 20:19:59 +0100117 if (ofs + len > buf_len)
Harald Welte59b04682009-06-10 05:40:52 +0800118 return -2;
119 num_parsed++;
Sylvain Munaut1ebac142009-10-26 20:19:59 +0100120 ofs += len;
Harald Welte59b04682009-06-10 05:40:52 +0800121 }
122 if (lv_tag2) {
Sylvain Munaut1ebac142009-10-26 20:19:59 +0100123 if (ofs > buf_len)
Harald Welte59b04682009-06-10 05:40:52 +0800124 return -1;
Sylvain Munaut1ebac142009-10-26 20:19:59 +0100125 dec->lv[lv_tag2].val = &buf[ofs+1];
126 dec->lv[lv_tag2].len = buf[ofs];
Harald Welte59b04682009-06-10 05:40:52 +0800127 len = dec->lv[lv_tag2].len + 1;
Sylvain Munaut1ebac142009-10-26 20:19:59 +0100128 if (ofs + len > buf_len)
Harald Welte59b04682009-06-10 05:40:52 +0800129 return -2;
130 num_parsed++;
Sylvain Munaut1ebac142009-10-26 20:19:59 +0100131 ofs += len;
Harald Welte59b04682009-06-10 05:40:52 +0800132 }
133
Sylvain Munaut1ebac142009-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 Welte59b04682009-06-10 05:40:52 +0800147 }
148 //tlv_dump(dec);
149 return num_parsed;
150}
151
Harald Welte724ddb82009-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}