/src/open5gs/lib/core/ogs-tlv.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com> |
3 | | * Copyright (C) 2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de> |
4 | | * |
5 | | * This file is part of Open5GS. |
6 | | * |
7 | | * This program is free software: you can redistribute it and/or modify |
8 | | * it under the terms of the GNU Affero General Public License as published by |
9 | | * the Free Software Foundation, either version 3 of the License, or |
10 | | * (at your option) any later version. |
11 | | * |
12 | | * This program is distributed in the hope that it will be useful, |
13 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 | | * GNU General Public License for more details. |
16 | | * |
17 | | * You should have received a copy of the GNU General Public License |
18 | | * along with this program. If not, see <https://www.gnu.org/licenses/>. |
19 | | */ |
20 | | |
21 | | #include "ogs-core.h" |
22 | | |
23 | | #undef OGS_LOG_DOMAIN |
24 | 486 | #define OGS_LOG_DOMAIN __ogs_tlv_domain |
25 | | |
26 | | static OGS_POOL(pool, ogs_tlv_t); |
27 | | |
28 | | /* ogs_tlv_t common functions */ |
29 | | ogs_tlv_t *ogs_tlv_get(void) |
30 | 7.92k | { |
31 | 7.92k | ogs_tlv_t *tlv = NULL; |
32 | | |
33 | | /* get tlv node from node pool */ |
34 | 7.92k | ogs_pool_alloc(&pool, &tlv); |
35 | | |
36 | | /* check for error */ |
37 | 7.92k | ogs_assert(tlv); |
38 | | |
39 | | /* intialize tlv node */ |
40 | 7.92k | memset(tlv, 0, sizeof(ogs_tlv_t)); |
41 | 7.92k | return tlv; |
42 | 7.92k | } |
43 | | |
44 | | void ogs_tlv_free(ogs_tlv_t *tlv) |
45 | 7.92k | { |
46 | | /* free tlv node to the node pool */ |
47 | 7.92k | ogs_pool_free(&pool, tlv); |
48 | 7.92k | } |
49 | | |
50 | | void ogs_tlv_init(void) |
51 | 2 | { |
52 | 2 | ogs_pool_init(&pool, ogs_core()->tlv.pool); |
53 | 2 | } |
54 | | |
55 | | void ogs_tlv_final(void) |
56 | 0 | { |
57 | 0 | ogs_pool_final(&pool); |
58 | 0 | } |
59 | | |
60 | | uint32_t ogs_tlv_pool_avail(void) |
61 | 0 | { |
62 | 0 | return ogs_pool_avail(&pool); |
63 | 0 | } |
64 | | |
65 | | void ogs_tlv_free_all(ogs_tlv_t *root) |
66 | 818 | { |
67 | | /* free all tlv node to the node pool */ |
68 | 818 | ogs_tlv_t *iter = root; |
69 | 818 | ogs_tlv_t *next = NULL; |
70 | 8.74k | while (iter) { |
71 | 7.92k | if(iter->embedded != NULL) { |
72 | 335 | ogs_tlv_free_all(iter->embedded); |
73 | 335 | } |
74 | 7.92k | next = iter->next; |
75 | 7.92k | ogs_tlv_free(iter); |
76 | 7.92k | iter = next; |
77 | 7.92k | } |
78 | 818 | } |
79 | | |
80 | | uint8_t ogs_tlv_value_8(ogs_tlv_t *tlv) |
81 | 0 | { |
82 | 0 | return (*((uint8_t*)(tlv->value))); |
83 | 0 | } |
84 | | |
85 | | uint16_t ogs_tlv_value_16(ogs_tlv_t *tlv) |
86 | 0 | { |
87 | 0 | uint16_t u_16; |
88 | 0 | uint8_t *v = tlv->value; |
89 | |
|
90 | 0 | u_16 = ((v[0] << 8) & 0xff00) | |
91 | 0 | ((v[1] ) & 0x00ff); |
92 | |
|
93 | 0 | return u_16; |
94 | 0 | } |
95 | | |
96 | | uint32_t ogs_tlv_value_32(ogs_tlv_t *tlv) |
97 | 0 | { |
98 | 0 | uint32_t u_32; |
99 | 0 | uint8_t *v = tlv->value; |
100 | |
|
101 | 0 | u_32 = ((v[0] << 24) & 0xff000000) | |
102 | 0 | ((v[1] << 16) & 0x00ff0000) | |
103 | 0 | ((v[2] << 8) & 0x0000ff00) | |
104 | 0 | ((v[3] ) & 0x000000ff); |
105 | |
|
106 | 0 | return u_32; |
107 | 0 | } |
108 | | |
109 | | uint32_t ogs_tlv_calc_length(ogs_tlv_t *tlv) |
110 | 0 | { |
111 | 0 | ogs_tlv_t *iter = tlv; |
112 | 0 | uint32_t length = 0; |
113 | |
|
114 | 0 | while(iter) { |
115 | | /* this is length for type field */ |
116 | 0 | switch(iter->mode) { |
117 | 0 | case OGS_TLV_MODE_T1_L1: |
118 | 0 | length += 2; |
119 | 0 | break; |
120 | 0 | case OGS_TLV_MODE_T1_L2: |
121 | 0 | length += 3; |
122 | 0 | break; |
123 | 0 | case OGS_TLV_MODE_T1_L2_I1: |
124 | 0 | case OGS_TLV_MODE_T2_L2: |
125 | 0 | length += 4; |
126 | 0 | break; |
127 | 0 | case OGS_TLV_MODE_T1: |
128 | 0 | length += 1; |
129 | 0 | break; |
130 | 0 | default: |
131 | 0 | ogs_assert_if_reached(); |
132 | 0 | break; |
133 | 0 | } |
134 | | |
135 | | /* this is length for type field */ |
136 | 0 | if(iter->embedded != NULL) { |
137 | 0 | iter->length = ogs_tlv_calc_length(iter->embedded); |
138 | 0 | } |
139 | | |
140 | | /* this is length for value field */ |
141 | 0 | length += iter->length; |
142 | |
|
143 | 0 | iter = iter->next; |
144 | 0 | } |
145 | 0 | return length; |
146 | 0 | } |
147 | | |
148 | | uint32_t ogs_tlv_calc_count(ogs_tlv_t *tlv) |
149 | 0 | { |
150 | 0 | ogs_tlv_t *iter = tlv; |
151 | 0 | uint32_t count = 0; |
152 | |
|
153 | 0 | while(iter) { |
154 | 0 | if(iter->embedded != NULL) { |
155 | 0 | count += ogs_tlv_calc_count(iter->embedded); |
156 | 0 | } else { |
157 | 0 | count++; |
158 | 0 | } |
159 | 0 | iter = iter->next; |
160 | 0 | } |
161 | 0 | return count; |
162 | 0 | } |
163 | | |
164 | | static uint8_t *tlv_put_type(uint32_t type, uint8_t *pos, uint8_t mode) |
165 | 0 | { |
166 | 0 | switch(mode) { |
167 | 0 | case OGS_TLV_MODE_T1_L1: |
168 | 0 | case OGS_TLV_MODE_T1_L2: |
169 | 0 | case OGS_TLV_MODE_T1_L2_I1: |
170 | 0 | case OGS_TLV_MODE_T1: |
171 | 0 | *(pos++) = type & 0xFF; |
172 | 0 | break; |
173 | 0 | case OGS_TLV_MODE_T2_L2: |
174 | 0 | *(pos++) = (type >> 8) & 0xFF; |
175 | 0 | *(pos++) = type & 0xFF; |
176 | 0 | break; |
177 | 0 | default: |
178 | 0 | ogs_assert_if_reached(); |
179 | 0 | break; |
180 | 0 | } |
181 | 0 | return pos; |
182 | 0 | } |
183 | | |
184 | | static uint8_t *tlv_put_length(uint32_t length, uint8_t *pos, uint8_t mode) |
185 | 0 | { |
186 | 0 | switch(mode) { |
187 | 0 | case OGS_TLV_MODE_T1_L1: |
188 | 0 | *(pos++) = length & 0xFF; |
189 | 0 | break; |
190 | 0 | case OGS_TLV_MODE_T1_L2: |
191 | 0 | case OGS_TLV_MODE_T1_L2_I1: |
192 | 0 | case OGS_TLV_MODE_T2_L2: |
193 | 0 | *(pos++) = (length >> 8) & 0xFF; |
194 | 0 | *(pos++) = length & 0xFF; |
195 | 0 | break; |
196 | 0 | case OGS_TLV_MODE_T1: |
197 | 0 | break; |
198 | 0 | default: |
199 | 0 | ogs_assert_if_reached(); |
200 | 0 | break; |
201 | 0 | } |
202 | | |
203 | 0 | return pos; |
204 | 0 | } |
205 | | |
206 | | static uint8_t *tlv_put_instance(uint8_t instance, uint8_t *pos, uint8_t mode) |
207 | 0 | { |
208 | 0 | switch(mode) { |
209 | 0 | case OGS_TLV_MODE_T1_L2_I1: |
210 | 0 | *(pos++) = instance & 0xFF; |
211 | 0 | break; |
212 | 0 | default: |
213 | 0 | break; |
214 | 0 | } |
215 | | |
216 | 0 | return pos; |
217 | 0 | } |
218 | | |
219 | | uint8_t *tlv_get_element(ogs_tlv_t *tlv, uint8_t *blk, uint8_t mode) |
220 | 7.92k | { |
221 | 7.92k | uint8_t *pos = blk; |
222 | | |
223 | 7.92k | tlv->mode = mode; |
224 | | |
225 | 7.92k | switch(mode) { |
226 | 0 | case OGS_TLV_MODE_T1_L1: |
227 | 0 | tlv->type = *(pos++); |
228 | 0 | tlv->length = *(pos++); |
229 | 0 | break; |
230 | 0 | case OGS_TLV_MODE_T1_L2: |
231 | 0 | tlv->type = *(pos++); |
232 | 0 | tlv->length = *(pos++) << 8; |
233 | 0 | tlv->length += *(pos++); |
234 | 0 | break; |
235 | 7.92k | case OGS_TLV_MODE_T1_L2_I1: |
236 | 7.92k | tlv->type = *(pos++); |
237 | 7.92k | tlv->length = *(pos++) << 8; |
238 | 7.92k | tlv->length += *(pos++); |
239 | 7.92k | tlv->instance = *(pos++) & 0b00001111; |
240 | 7.92k | break; |
241 | 0 | case OGS_TLV_MODE_T2_L2: |
242 | 0 | tlv->type = *(pos++) << 8; |
243 | 0 | tlv->type += *(pos++); |
244 | 0 | tlv->length = *(pos++) << 8; |
245 | 0 | tlv->length += *(pos++); |
246 | 0 | break; |
247 | 0 | case OGS_TLV_MODE_T1: |
248 | 0 | tlv->type = *(pos++); |
249 | 0 | tlv->length = 0; |
250 | 0 | break; |
251 | 0 | default: |
252 | 0 | ogs_assert_if_reached(); |
253 | 0 | break; |
254 | 7.92k | } |
255 | | |
256 | 7.92k | tlv->value = pos; |
257 | | |
258 | 7.92k | return (pos + ogs_tlv_length(tlv)); |
259 | 7.92k | } |
260 | | |
261 | | uint8_t *tlv_get_element_fixed(ogs_tlv_t *tlv, uint8_t *blk, uint8_t mode, uint32_t fixed_length) |
262 | 0 | { |
263 | 0 | uint8_t *pos = blk; |
264 | |
|
265 | 0 | switch(mode) { |
266 | 0 | case OGS_TLV_MODE_T1: |
267 | 0 | tlv->type = *(pos++); |
268 | 0 | tlv->length = fixed_length; |
269 | 0 | break; |
270 | 0 | default: |
271 | 0 | ogs_assert_if_reached(); |
272 | 0 | break; |
273 | 0 | } |
274 | | |
275 | 0 | tlv->value = pos; |
276 | |
|
277 | 0 | return (pos + ogs_tlv_length(tlv)); |
278 | 0 | } |
279 | | |
280 | | static void tlv_alloc_buff_to_tlv( |
281 | | ogs_tlv_t *head, uint8_t *buff, uint32_t buff_len) |
282 | 0 | { |
283 | 0 | head->buff_allocated = true; |
284 | 0 | head->buff_len = buff_len; |
285 | 0 | head->buff_ptr = buff; |
286 | 0 | head->buff = buff; |
287 | 0 | } |
288 | | |
289 | | ogs_tlv_t *ogs_tlv_find_root(ogs_tlv_t *tlv) |
290 | 0 | { |
291 | 0 | ogs_tlv_t *head = tlv->head; |
292 | 0 | ogs_tlv_t *parent; |
293 | |
|
294 | 0 | parent = head->parent; |
295 | 0 | while(parent) { |
296 | 0 | head = parent->head; |
297 | 0 | parent = head->parent; |
298 | 0 | } |
299 | |
|
300 | 0 | return head; |
301 | 0 | } |
302 | | |
303 | | ogs_tlv_t *ogs_tlv_add(ogs_tlv_t *head, uint8_t mode, |
304 | | uint32_t type, uint32_t length, uint8_t instance, void *value) |
305 | 0 | { |
306 | 0 | ogs_tlv_t *curr = head; |
307 | 0 | ogs_tlv_t *new = NULL; |
308 | |
|
309 | 0 | new = ogs_tlv_get(); |
310 | 0 | ogs_assert(new); |
311 | 0 | if(length != 0) |
312 | 0 | ogs_assert(value); |
313 | | |
314 | 0 | new->mode = mode; |
315 | 0 | new->type = type; |
316 | 0 | new->length = length; |
317 | 0 | new->instance = instance; |
318 | 0 | new->value = value; |
319 | |
|
320 | 0 | if (head != NULL && head->buff_allocated == true) { |
321 | 0 | ogs_assert((head->buff_ptr - head->buff + length) < head->buff_len); |
322 | | |
323 | 0 | memcpy(head->buff_ptr, value, length); |
324 | 0 | new->value = head->buff_ptr; |
325 | 0 | head->buff_ptr += length; |
326 | 0 | } |
327 | | |
328 | 0 | if(curr == NULL) { |
329 | 0 | new->head = new; |
330 | 0 | new->tail = new; |
331 | 0 | } else { |
332 | 0 | head = head->head; /* in case head is not head */ |
333 | 0 | new->head = head; |
334 | 0 | head->tail->next = new; |
335 | 0 | head->tail = new; |
336 | 0 | } |
337 | 0 | return new; |
338 | 0 | } |
339 | | |
340 | | ogs_tlv_t *ogs_tlv_copy(void *buff, uint32_t buff_len, uint8_t mode, |
341 | | uint32_t type, uint32_t length, uint8_t instance, void *value) |
342 | 0 | { |
343 | 0 | ogs_tlv_t *new = NULL; |
344 | |
|
345 | 0 | new = ogs_tlv_get(); |
346 | 0 | ogs_assert(new); |
347 | | |
348 | 0 | new->mode = mode; |
349 | 0 | new->type = type; |
350 | 0 | new->length = length; |
351 | 0 | new->instance = instance; |
352 | 0 | new->value = value; |
353 | 0 | new->head = new->tail = new; |
354 | |
|
355 | 0 | tlv_alloc_buff_to_tlv(new, buff, buff_len); |
356 | |
|
357 | 0 | memcpy(new->buff_ptr, value, length); |
358 | 0 | new->value = new->buff_ptr; |
359 | 0 | new->buff_ptr += length; |
360 | |
|
361 | 0 | return new; |
362 | 0 | } |
363 | | |
364 | | ogs_tlv_t *ogs_tlv_embed(ogs_tlv_t *parent, uint8_t mode, |
365 | | uint32_t type, uint32_t length, uint8_t instance, void *value) |
366 | 0 | { |
367 | 0 | ogs_tlv_t *new = NULL, *root = NULL; |
368 | |
|
369 | 0 | ogs_assert(parent); |
370 | | |
371 | 0 | new = ogs_tlv_get(); |
372 | 0 | ogs_assert(new); |
373 | | |
374 | 0 | new->mode = mode; |
375 | 0 | new->type = type; |
376 | 0 | new->length = length; |
377 | 0 | new->instance = instance; |
378 | 0 | new->value = value; |
379 | |
|
380 | 0 | root = ogs_tlv_find_root(parent); |
381 | |
|
382 | 0 | if(root->buff_allocated == true) { |
383 | 0 | ogs_assert((root->buff_ptr - root->buff + length) < root->buff_len); |
384 | | |
385 | 0 | memcpy(root->buff_ptr, value, length); |
386 | 0 | new->value = root->buff_ptr; |
387 | 0 | root->buff_ptr += length; |
388 | 0 | } |
389 | | |
390 | 0 | if(parent->embedded == NULL) { |
391 | 0 | parent->embedded = new->head = new->tail = new; |
392 | 0 | new->parent = parent; |
393 | 0 | } else { |
394 | 0 | new->head = parent->embedded; |
395 | 0 | parent->embedded->tail->next = new; |
396 | 0 | parent->embedded->tail = new; |
397 | 0 | } |
398 | |
|
399 | 0 | return new; |
400 | 0 | } |
401 | | |
402 | | uint32_t ogs_tlv_render(ogs_tlv_t *root, void *data, uint32_t length) |
403 | 0 | { |
404 | 0 | ogs_tlv_t *curr = root; |
405 | 0 | uint8_t *pos = data; |
406 | 0 | uint8_t *blk = data; |
407 | 0 | uint32_t embedded_len = 0; |
408 | |
|
409 | 0 | while(curr) { |
410 | 0 | pos = tlv_put_type(curr->type, pos, curr->mode); |
411 | |
|
412 | 0 | if(curr->embedded == NULL) { |
413 | 0 | pos = tlv_put_length(curr->length, pos, curr->mode); |
414 | 0 | pos = tlv_put_instance(curr->instance, pos, curr->mode); |
415 | |
|
416 | 0 | if ((pos - blk) + ogs_tlv_length(curr) > length) |
417 | 0 | ogs_assert_if_reached(); |
418 | | |
419 | 0 | memcpy((char*)pos, (char*)curr->value, curr->length); |
420 | 0 | pos += curr->length; |
421 | 0 | } else { |
422 | 0 | embedded_len = ogs_tlv_calc_length(curr->embedded); |
423 | 0 | pos = tlv_put_length(embedded_len, pos, curr->mode); |
424 | 0 | pos = tlv_put_instance(curr->instance, pos, curr->mode); |
425 | 0 | ogs_tlv_render(curr->embedded, |
426 | 0 | pos, length - (uint32_t)(pos-blk)); |
427 | 0 | pos += embedded_len; |
428 | 0 | } |
429 | 0 | curr = curr->next; |
430 | 0 | } |
431 | | |
432 | 0 | return (pos - blk); |
433 | 0 | } |
434 | | |
435 | | /* ogs_tlv_t parsing functions */ |
436 | | ogs_tlv_t *ogs_tlv_parse_block(uint32_t length, void *data, uint8_t mode) |
437 | 818 | { |
438 | 818 | uint8_t *pos = data; |
439 | 818 | uint8_t *blk = data; |
440 | | |
441 | 818 | ogs_tlv_t *root = NULL; |
442 | 818 | ogs_tlv_t *prev = NULL; |
443 | 818 | ogs_tlv_t *curr = NULL; |
444 | | |
445 | 818 | root = curr = ogs_tlv_get(); |
446 | | |
447 | 818 | ogs_assert(curr); |
448 | | |
449 | 818 | pos = tlv_get_element(curr, pos, mode); |
450 | | |
451 | 818 | ogs_assert(pos); |
452 | | |
453 | 7.92k | while(pos - blk < length) { |
454 | 7.10k | prev = curr; |
455 | | |
456 | 7.10k | curr = ogs_tlv_get(); |
457 | 7.10k | ogs_assert(curr); |
458 | 7.10k | prev->next = curr; |
459 | | |
460 | 7.10k | pos = tlv_get_element(curr, pos, mode); |
461 | 7.10k | ogs_assert(pos); |
462 | 7.10k | } |
463 | | |
464 | 818 | if (length != (pos - blk)) { |
465 | 162 | ogs_error("ogs_tlv_parse_block() failed[LEN:%d,MODE:%d]", length, mode); |
466 | 162 | ogs_error("POS[%p] BLK[%p] POS-BLK[%d]", pos, blk, (int)(pos - blk)); |
467 | 162 | ogs_log_hexdump(OGS_LOG_FATAL, data, length); |
468 | | |
469 | 162 | ogs_tlv_free_all(root); |
470 | 162 | return NULL; |
471 | 162 | } |
472 | | |
473 | 656 | return root; |
474 | 818 | } |
475 | | |
476 | | ogs_tlv_t *ogs_tlv_parse_embedded_block(ogs_tlv_t *tlv, uint8_t mode) |
477 | 369 | { |
478 | 369 | tlv->embedded = ogs_tlv_parse_block(tlv->length, tlv->value, mode); |
479 | | |
480 | 369 | return tlv->embedded; |
481 | 369 | } |
482 | | |
483 | | /* tlv operation-related function */ |
484 | | ogs_tlv_t *ogs_tlv_find(ogs_tlv_t *root, uint32_t type) |
485 | 0 | { |
486 | 0 | ogs_tlv_t *iter = root, *embed = NULL; |
487 | 0 | while(iter) { |
488 | 0 | if(iter->type == type) { |
489 | 0 | return iter; |
490 | 0 | } |
491 | | |
492 | 0 | if(iter->embedded != NULL) { |
493 | 0 | embed = ogs_tlv_find(iter->embedded, type); |
494 | 0 | if(embed != NULL) { |
495 | 0 | return embed; |
496 | 0 | } |
497 | 0 | } |
498 | 0 | iter = iter->next; |
499 | 0 | } |
500 | | |
501 | | /* tlv for the designated type doesn't exist */ |
502 | 0 | return NULL; |
503 | 0 | } |