/src/wireshark/epan/dissectors/packet-asterix.c
Line | Count | Source |
1 | | /* packet-asterix.c |
2 | | * Routines for ASTERIX decoding |
3 | | * |
4 | | * By BoĊĦtjan Polanc <bostjan.polanc@gmail.com> |
5 | | * |
6 | | * Wireshark - Network traffic analyzer |
7 | | * By Gerald Combs <gerald@wireshark.org> |
8 | | * Copyright 1998 Gerald Combs |
9 | | * |
10 | | * SPDX-License-Identifier: GPL-2.0-or-later |
11 | | */ |
12 | | |
13 | | /* |
14 | | * ASTERIX (All-purpose structured EUROCONTROL surveillances |
15 | | * information exchange) is a protocol/data format related to air traffic control. |
16 | | * |
17 | | * Specifications can be downloaded from: |
18 | | * - https://www.eurocontrol.int/asterix (original specifications - in PDF) |
19 | | * - https://zoranbosnjak.github.io/asterix-specs/ (structured version) |
20 | | */ |
21 | | |
22 | | #include <config.h> |
23 | | |
24 | | #include "exceptions.h" |
25 | | #include "packet-asterix.h" |
26 | | #include "packet-asterix-generated.h" |
27 | | #include "packet-tcp.h" |
28 | | |
29 | 15.4k | #define HEADER_LENGTH 3 |
30 | 30 | #define ASTERIX_PORT 8600 |
31 | 9.96k | #define MAX_INTERPRETATIONS 100 |
32 | | #define MAX_INTERPRETATION_DEPTH 30 |
33 | 23.7M | #define MAX_FSPEC_BIT_LENGTH 1024 |
34 | 676k | #define OCTAL_BIT_LENGTH 3 |
35 | 327k | #define ICAO_BIT_LENGTH 6 |
36 | | |
37 | | static int proto_asterix; |
38 | | |
39 | | static char fspec_bit_string[MAX_FSPEC_BIT_LENGTH]; |
40 | | |
41 | | static dissector_handle_t asterix_handle; |
42 | | static dissector_handle_t asterix_tcp_handle; |
43 | | |
44 | | static int ett_asterix; |
45 | | static int ett_asterix_record; |
46 | | static int ett_asterix_possible_interpretation; |
47 | | static int ett_asterix_possible_interpretations; |
48 | | static int ett_asterix_spare_error; |
49 | | |
50 | | /* With invalid data (e.g. fuzz tests), to great of interpretation depth can cause "freezes", |
51 | | where search for interpretations can last a long time. By default depth is set to 15, |
52 | | which should never be a problem for random data. Users can select a higher depth. |
53 | | */ |
54 | | static int selected_interpretations_depth = depth_15; |
55 | | |
56 | | static unsigned int solution_count; |
57 | | static int solutions[MAX_INTERPRETATIONS][MAX_INTERPRETATION_DEPTH + 1]; |
58 | | |
59 | | static unsigned asterix_get_unsigned_value(tvbuff_t *tvb, unsigned offset, unsigned bytes) |
60 | 3.23M | { |
61 | 3.23M | switch (bytes) |
62 | 3.23M | { |
63 | 1.90M | case 1: |
64 | 1.90M | return tvb_get_uint8(tvb, offset); |
65 | 982k | case 2: |
66 | 982k | return tvb_get_uint16(tvb, offset, ENC_BIG_ENDIAN); |
67 | 267k | case 3: |
68 | 267k | return tvb_get_uint24(tvb, offset, ENC_BIG_ENDIAN); |
69 | 81.1k | case 4: |
70 | 81.1k | return tvb_get_uint32(tvb, offset, ENC_BIG_ENDIAN); |
71 | 0 | default: |
72 | 0 | return -1; |
73 | 3.23M | } |
74 | 3.23M | } |
75 | | |
76 | | static int asterix_get_signed_value(tvbuff_t *tvb, unsigned offset, unsigned bytes) |
77 | 46.7k | { |
78 | 46.7k | switch (bytes) |
79 | 46.7k | { |
80 | 33.1k | case 1: |
81 | 33.1k | return tvb_get_int8(tvb, offset); |
82 | 13.6k | case 2: |
83 | 13.6k | return tvb_get_int16(tvb, offset, ENC_BIG_ENDIAN); |
84 | 0 | case 3: |
85 | 0 | return tvb_get_int24(tvb, offset, ENC_BIG_ENDIAN); |
86 | 0 | case 4: |
87 | 0 | return tvb_get_int32(tvb, offset, ENC_BIG_ENDIAN); |
88 | 0 | default: |
89 | 0 | return -1; |
90 | 46.7k | } |
91 | 46.7k | } |
92 | | |
93 | | // get signed integer from specified number of bits |
94 | | static int get_signed_int(unsigned value, unsigned bits) |
95 | 308k | { |
96 | 308k | int ret = 0; |
97 | 308k | int sign = 1; |
98 | 308k | if ((value >> (bits - 1) & 1)) |
99 | 101k | { |
100 | 101k | sign = -1; |
101 | 101k | value = ~value; |
102 | 101k | value++; |
103 | 101k | } |
104 | | |
105 | 308k | unsigned int mask = 1; |
106 | 5.28M | for (unsigned int i = 0; i < (bits - 1); i++) |
107 | 4.97M | { |
108 | 4.97M | if ((value >> i) & 1) |
109 | 1.67M | { |
110 | 1.67M | ret |= mask; |
111 | 1.67M | } |
112 | 4.97M | mask *= 2; |
113 | 4.97M | } |
114 | 308k | return ret * sign; |
115 | 308k | } |
116 | | |
117 | | // test extended FX bit |
118 | | static bool asterix_extended_end (tvbuff_t *tvb, unsigned offset) |
119 | 399k | { |
120 | 399k | uint8_t val = tvb_get_uint8(tvb, offset); |
121 | 399k | if ((val & 0x01) == 0) |
122 | 177k | { |
123 | 177k | return true; |
124 | 177k | } |
125 | 221k | else |
126 | 221k | { |
127 | 221k | return false; |
128 | 221k | } |
129 | 399k | } |
130 | | |
131 | | // test FSPEC bit |
132 | | static bool asterix_field_exists (tvbuff_t *tvb, unsigned offset, unsigned bit_index) |
133 | 20.5M | { |
134 | 20.5M | unsigned int byte_index = bit_index / 8; |
135 | 20.5M | uint8_t value = tvb_get_uint8 (tvb, offset + byte_index); |
136 | 20.5M | bit_index = bit_index % 8; |
137 | 20.5M | return 0x80 == ( (value << bit_index) & 0x80); |
138 | 20.5M | } |
139 | | |
140 | | // prints null terminated octal string |
141 | | static void print_octal_string (tvbuff_t *tvb, unsigned offset, unsigned bit_offset, unsigned bit_size, unsigned byte_size, proto_tree *tree, int expand_var) |
142 | 67.7k | { |
143 | 67.7k | if ((bit_size % OCTAL_BIT_LENGTH) != 0) |
144 | 0 | { |
145 | 0 | return; |
146 | 0 | } |
147 | 67.7k | unsigned count = bit_size / OCTAL_BIT_LENGTH; |
148 | 67.7k | char buff[1024]; |
149 | 67.7k | if (count > (sizeof(buff) - 1)) |
150 | 0 | { |
151 | 0 | return; |
152 | 0 | } |
153 | 338k | for (unsigned i = 0; i < count; i++) |
154 | 270k | { |
155 | 270k | uint8_t value = tvb_get_bits8(tvb, offset * 8 + bit_offset, OCTAL_BIT_LENGTH); |
156 | 270k | bit_offset += OCTAL_BIT_LENGTH; |
157 | | |
158 | 270k | buff[i] = value + 0x30; |
159 | 270k | } |
160 | 67.7k | buff[count] = 0; |
161 | | |
162 | 67.7k | proto_tree_add_string(tree, expand_var, tvb, offset, byte_size, buff); |
163 | 67.7k | } |
164 | | |
165 | | static char decode_icao_char(uint8_t x) |
166 | 137k | { |
167 | 137k | if (x >= 0x01 && x <= 0x1A) |
168 | 46.5k | { |
169 | 46.5k | return 'A' + (x - 1); |
170 | 46.5k | } |
171 | 91.2k | else if (x == 0x20) |
172 | 3.59k | { |
173 | 3.59k | return ' '; |
174 | 3.59k | } |
175 | 87.6k | else if (x >= 0x30 && x <= 0x39) |
176 | 14.3k | { |
177 | 14.3k | return '0' + (x - 0x30); |
178 | 14.3k | } |
179 | 73.2k | return '?'; |
180 | 137k | } |
181 | | |
182 | | // prints null terminated ICAO string |
183 | | static void print_icao_string (tvbuff_t *tvb, unsigned offset, unsigned bit_offset, unsigned bit_size, unsigned byte_size, proto_tree *tree, int expand_var) |
184 | 21.2k | { |
185 | 21.2k | if ((bit_size % ICAO_BIT_LENGTH) != 0) |
186 | 0 | { |
187 | 0 | return; |
188 | 0 | } |
189 | 21.2k | unsigned count = bit_size / ICAO_BIT_LENGTH; |
190 | 21.2k | char buff[1024]; |
191 | 21.2k | if (count > (sizeof(buff) - 1)) |
192 | 0 | { |
193 | 0 | return; |
194 | 0 | } |
195 | 163k | for (unsigned i = 0; i < count; i++) |
196 | 142k | { |
197 | 142k | uint8_t value = tvb_get_bits8(tvb, offset * 8 + bit_offset, ICAO_BIT_LENGTH); |
198 | 142k | bit_offset += ICAO_BIT_LENGTH; |
199 | | |
200 | 142k | buff[i] = decode_icao_char(value); |
201 | 142k | } |
202 | 21.2k | buff[count] = 0; |
203 | | |
204 | 21.2k | proto_tree_add_string(tree, expand_var, tvb, offset, byte_size, buff); |
205 | 21.2k | } |
206 | | |
207 | | static void check_spare_bits (tvbuff_t *tvb, unsigned bit_offset, unsigned bit_size, proto_item *item) |
208 | 334k | { |
209 | 334k | if (bit_size > (64 - bit_offset)) |
210 | 1.33k | { |
211 | 1.33k | return; |
212 | 1.33k | } |
213 | 332k | uint64_t bits = tvb_get_bits64(tvb, bit_offset, bit_size, ENC_BIG_ENDIAN); |
214 | 332k | if (bits != 0) |
215 | 176k | { |
216 | 176k | expert_add_info(NULL, item, &hf_asterix_spare_error); |
217 | 176k | } |
218 | 332k | } |
219 | | |
220 | | static unsigned asterix_fspec_len (tvbuff_t *tvb, unsigned offset) |
221 | 965k | { |
222 | 965k | unsigned int i; |
223 | 965k | unsigned int max_length = tvb_reported_length (tvb) - offset; |
224 | 1.49M | for (i = 0; (tvb_get_uint8 (tvb, offset + i) & 1) && i < max_length; i++); |
225 | 965k | return i + 1; |
226 | 965k | } |
227 | | |
228 | | static bool asterix_fspec_check (unsigned fspec_len, unsigned list_length, proto_item *ti) |
229 | 81.1k | { |
230 | 81.1k | unsigned fspec_expected_len = list_length / 7; |
231 | 81.1k | if ((list_length % 7) != 0) { |
232 | 37.2k | fspec_expected_len++; |
233 | 37.2k | } |
234 | 81.1k | if (fspec_len > fspec_expected_len) { |
235 | 19.5k | expert_add_info(NULL, ti, &hf_asterix_fspec_error); |
236 | 19.5k | return false; |
237 | 19.5k | } |
238 | 61.6k | return true; |
239 | 81.1k | } |
240 | | |
241 | | static unsigned asterix_dissect_fspec (tvbuff_t *tvb, unsigned offset, proto_tree *tree) |
242 | 883k | { |
243 | 883k | unsigned fspec_len = asterix_fspec_len (tvb, offset); |
244 | | |
245 | 883k | if (fspec_len > 4) { |
246 | 20.1k | proto_tree_add_item (tree, hf_asterix_fspec, tvb, offset, fspec_len, ENC_NA); |
247 | 863k | } else { |
248 | 863k | unsigned value = asterix_get_unsigned_value(tvb, offset, fspec_len); |
249 | 863k | unsigned fspec_bit_length = fspec_len * 9 + 1; |
250 | 863k | memset(fspec_bit_string, 0, fspec_bit_length); |
251 | 863k | unsigned str_index = 0; |
252 | 10.1M | for (unsigned int i = 0; i < fspec_len * 8; i++) { |
253 | 9.33M | fspec_bit_string[str_index] = (value & (1U << ((fspec_len * 8 - 1) - i))) ? '1' : '0'; |
254 | 9.33M | if (i > 0 && ((i + 1) % 8) == 0) { |
255 | 1.16M | str_index++; |
256 | 1.16M | fspec_bit_string[str_index] = ' '; |
257 | 1.16M | } |
258 | 9.33M | str_index++; |
259 | 9.33M | } |
260 | 863k | proto_tree_add_string_format_value (tree, hf_asterix_fspec_bitstring, tvb, offset, fspec_len, NULL, "%s", fspec_bit_string); |
261 | 863k | } |
262 | 883k | return fspec_len; |
263 | 883k | } |
264 | | |
265 | | static unsigned asterix_parse_re_field (tvbuff_t *tvb, unsigned offset, proto_tree *tree, unsigned fspec_len, unsigned cat) |
266 | 531 | { |
267 | 531 | int start_offset = offset; |
268 | 531 | offset+=fspec_len; |
269 | | |
270 | 531 | unsigned int ed = -1; |
271 | 16.4k | for (size_t i = 0; i < array_length(asterix_properties); i++) |
272 | 15.9k | { |
273 | 15.9k | const dialog_cat_struct *prop_cat = &asterix_properties[i]; |
274 | 15.9k | if (prop_cat->cat == cat && !prop_cat->cat_basic) |
275 | 531 | { |
276 | 531 | ed = *prop_cat->edition_default_value; |
277 | 531 | } |
278 | 15.9k | } |
279 | | |
280 | 531 | table_params table_p = {0}; |
281 | 531 | get_expansion_table(cat, ed, &table_p); |
282 | | |
283 | 4.48k | for (unsigned i = 0; i < table_p.table_size; i++) { |
284 | 3.95k | if (asterix_field_exists(tvb, start_offset, i)) { |
285 | 1.98k | const int *expand = table_p.table_pointer_expand[i]; |
286 | 1.98k | int expand_value = -1; |
287 | 1.98k | if (expand != NULL) |
288 | 1.98k | { |
289 | 1.98k | expand_value = *expand; |
290 | 1.98k | } |
291 | 1.98k | offset += table_p.table_pointer[i](tvb, offset, tree, expand_value); |
292 | 1.98k | } |
293 | 3.95k | } |
294 | 531 | return offset - start_offset; |
295 | 531 | } |
296 | | |
297 | | static bool check_fspec_validity (tvbuff_t *tvb, unsigned offset, table_params *table) |
298 | 967k | { |
299 | 967k | unsigned i = 0; |
300 | 967k | unsigned fs_index = 0; |
301 | 11.8M | while (fs_index < MAX_FSPEC_BIT_LENGTH && i < table->table_size) { |
302 | 11.7M | if (((fs_index + 1) % 8) == 0) { |
303 | 1.43M | if (!asterix_field_exists(tvb, offset, fs_index)) { |
304 | | // FSPEC end, all fields may not have been present, but that's ok |
305 | 772k | return true; |
306 | 772k | } |
307 | 662k | fs_index++; |
308 | 662k | } |
309 | 11.0M | if (asterix_field_exists(tvb, offset, fs_index)) { |
310 | 3.62M | if (table->table_pointer[i] == NULL) { |
311 | | // bit should not be set, FSPEC invalid |
312 | 119k | return false; |
313 | 119k | } |
314 | 3.62M | } |
315 | 10.8M | i++; |
316 | 10.8M | fs_index++; |
317 | 10.8M | } |
318 | 75.2k | if (fs_index >= MAX_FSPEC_BIT_LENGTH) { |
319 | 0 | return false; |
320 | 0 | } |
321 | 75.2k | unsigned fs_end_index = ((fs_index / 8) + 1) * 8 - 1; |
322 | 75.2k | if (asterix_field_exists(tvb, offset, fs_end_index)) { |
323 | | // FSPEC should not go beyond current byte |
324 | 31.7k | return false; |
325 | 31.7k | } |
326 | 43.4k | return true; |
327 | 75.2k | } |
328 | | |
329 | | static int probe_possible_record (tvbuff_t *tvb, unsigned offset, unsigned int cat, unsigned int ed, unsigned int record) |
330 | 690k | { |
331 | | // use NULL pointer for proto_tree pointer, so that wireshark ignores all tree add calls |
332 | 690k | proto_tree *asterix_packet_tree = NULL; |
333 | | |
334 | 690k | int start_offset = offset; |
335 | | |
336 | 690k | table_params table_p; |
337 | 690k | get_category_uap_table(cat, ed, record, &table_p); |
338 | | |
339 | 690k | if (table_p.table_pointer == NULL) { |
340 | | //unknown category, abort |
341 | 0 | return -1; |
342 | 0 | } |
343 | 690k | if (!check_fspec_validity (tvb, start_offset, &table_p)) { |
344 | 150k | return -1; |
345 | 150k | } |
346 | | |
347 | 539k | offset += asterix_dissect_fspec (tvb, offset, asterix_packet_tree); |
348 | 539k | unsigned i = 0; |
349 | 539k | unsigned fs_index = 0; |
350 | 4.95M | while (i < table_p.table_size) { |
351 | 4.79M | if (((fs_index + 1) % 8) == 0) { |
352 | 527k | if (!asterix_field_exists(tvb, start_offset, fs_index)) { |
353 | 346k | break; |
354 | 346k | } |
355 | 180k | fs_index++; |
356 | 180k | } |
357 | 4.45M | if (asterix_field_exists(tvb, start_offset, fs_index)) { |
358 | 1.21M | const int *expand = table_p.table_pointer_expand[i]; |
359 | 1.21M | int expand_value = -1; |
360 | 1.21M | if (expand != NULL) |
361 | 1.21M | { |
362 | 1.21M | expand_value = *expand; |
363 | 1.21M | } |
364 | 1.21M | int fun_len = table_p.table_pointer[i](tvb, offset, asterix_packet_tree, expand_value); |
365 | 1.21M | if (fun_len == -1) { |
366 | 42.3k | return -1; |
367 | 42.3k | } |
368 | 1.17M | offset += fun_len; |
369 | 1.17M | } |
370 | 4.41M | i++; |
371 | 4.41M | fs_index++; |
372 | 4.41M | } |
373 | 497k | return offset; |
374 | 539k | } |
375 | | |
376 | | /* possible return values: |
377 | | 0 success |
378 | | -1 to many interpretations |
379 | | -2 recursive depth limit breached |
380 | | */ |
381 | | // NOLINTNEXTLINE(misc-no-recursion) |
382 | | static int probe_possible_records (tvbuff_t *tvb, packet_info *pinfo, int offset, int datablock_end, unsigned int cat, unsigned int ed, uap_table_indexes *indexes, unsigned int *stack, unsigned int depth) |
383 | 346k | { |
384 | 1.03M | for (volatile unsigned int i = indexes->start_index; i <= indexes->end_index; i++) |
385 | 690k | { |
386 | 690k | volatile int new_offset = (int)offset; |
387 | 690k | stack[depth] = i; |
388 | 690k | TRY |
389 | 690k | { |
390 | 690k | new_offset = probe_possible_record (tvb, new_offset, cat, ed, i); |
391 | 690k | } |
392 | 690k | CATCH_NONFATAL_ERRORS |
393 | 141k | { |
394 | 141k | new_offset = -1; |
395 | 141k | } |
396 | 690k | ENDTRY; |
397 | 690k | if (new_offset != -1) { |
398 | 355k | if (new_offset == datablock_end) |
399 | 9.96k | { |
400 | 109k | for (unsigned int j = 0; j <= depth; j++) |
401 | 99.1k | { |
402 | 99.1k | solutions[solution_count][j] = stack[j]; |
403 | 99.1k | } |
404 | 9.96k | solution_count++; |
405 | 9.96k | if (solution_count >= MAX_INTERPRETATIONS) |
406 | 62 | { |
407 | 62 | return -1; |
408 | 62 | } |
409 | 9.96k | } |
410 | 345k | else if (new_offset < datablock_end) |
411 | 345k | { |
412 | 345k | if ((depth + 1) >= (unsigned)selected_interpretations_depth) |
413 | 75 | { |
414 | 75 | return -2; |
415 | 75 | } |
416 | 345k | int result = probe_possible_records (tvb, pinfo, new_offset, datablock_end, cat, ed, indexes, stack, depth + 1); |
417 | 345k | if (result != 0) |
418 | 1.68k | { |
419 | 1.68k | return result; |
420 | 1.68k | } |
421 | 345k | } |
422 | 355k | } |
423 | 690k | } |
424 | 344k | return 0; |
425 | 346k | } |
426 | | |
427 | | /* possible return values: |
428 | | -1 for error |
429 | | new offset value, larger by at least one byte (fspec) |
430 | | */ |
431 | | static int dissect_asterix_record (tvbuff_t *tvb, packet_info *pinfo, unsigned offset, unsigned datablock_end, proto_tree *tree, unsigned int cat, unsigned int ed, unsigned int uap) |
432 | 277k | { |
433 | 277k | proto_item *ti; |
434 | 277k | proto_tree *asterix_packet_tree; |
435 | | |
436 | 277k | ti = proto_tree_add_item (tree, hf_asterix_record, tvb, offset, 0, ENC_NA); |
437 | 277k | asterix_packet_tree = proto_item_add_subtree (ti, ett_asterix); |
438 | | |
439 | 277k | table_params table_p; |
440 | 277k | get_category_uap_table(cat, ed, uap, &table_p); |
441 | | |
442 | 277k | if (table_p.table_pointer == NULL || table_p.table_pointer_expand == NULL) { |
443 | | // skip unknown category |
444 | 358 | expert_add_info_format(pinfo, ti, &ei_asterix_overflow, "Unknown category"); |
445 | 358 | return -1; |
446 | 358 | } |
447 | 277k | if (!check_fspec_validity (tvb, offset, &table_p)) { |
448 | | // something wrong with FSPEC field |
449 | 892 | expert_add_info_format(pinfo, ti, &ei_asterix_overflow, "FSPEC field invalid"); |
450 | 892 | return -1; |
451 | 892 | } |
452 | 276k | int start_offset = offset; |
453 | | |
454 | 276k | offset += asterix_dissect_fspec (tvb, offset, asterix_packet_tree); |
455 | 276k | unsigned i = 0; |
456 | 276k | unsigned fs_index = 0; |
457 | 2.66M | while (i < table_p.table_size) { |
458 | 2.65M | if (((fs_index + 1) % 8) == 0 && fs_index > 0) { |
459 | 332k | if (!asterix_field_exists(tvb, start_offset, fs_index)) { |
460 | 263k | break; |
461 | 263k | } |
462 | 68.1k | fs_index++; |
463 | 68.1k | } |
464 | 2.39M | if (asterix_field_exists(tvb, start_offset, fs_index)) { |
465 | 457k | const int *expand = table_p.table_pointer_expand[i]; |
466 | 457k | int expand_value = -1; |
467 | 457k | if (expand != NULL) |
468 | 457k | { |
469 | 457k | expand_value = *expand; |
470 | 457k | } |
471 | 457k | if (table_p.table_pointer[i] != NULL) { |
472 | 457k | int fun_len = table_p.table_pointer[i](tvb, offset, asterix_packet_tree, expand_value); |
473 | 457k | if (fun_len == -1) { |
474 | 1.04k | return -1; |
475 | 1.04k | } |
476 | 456k | offset += fun_len; |
477 | 456k | } else { |
478 | 0 | return -1; |
479 | 0 | } |
480 | 457k | } |
481 | 2.39M | i++; |
482 | 2.39M | fs_index++; |
483 | 2.39M | } |
484 | 275k | unsigned int item_length = offset - start_offset; |
485 | 275k | proto_item_set_len(ti, item_length); |
486 | 275k | proto_item_append_text(ti, ", length %u", item_length); |
487 | 275k | proto_item_append_text(ti, ", %s", table_p.uap_name); |
488 | | |
489 | 275k | if (offset > datablock_end) { |
490 | | //record outside datablock |
491 | 23 | expert_add_info_format(pinfo, ti, &ei_asterix_overflow, "Record out of bounds"); |
492 | 23 | } |
493 | 275k | return offset; |
494 | 276k | } |
495 | | |
496 | | static void dissect_asterix_records (tvbuff_t *tvb, packet_info *pinfo, int offset, unsigned datablock_length, proto_tree *tree, unsigned int cat) |
497 | 7.73k | { |
498 | 7.73k | int datablock_end = offset + datablock_length; |
499 | | |
500 | | // get edition from settings |
501 | 7.73k | unsigned int ed = -1; |
502 | 106k | for (size_t i = 0; i < array_length(asterix_properties); i++) |
503 | 105k | { |
504 | 105k | const dialog_cat_struct *prop_cat = &asterix_properties[i]; |
505 | 105k | if (prop_cat->cat == cat && prop_cat->cat_basic) |
506 | 7.37k | { |
507 | 7.37k | ed = *prop_cat->edition_default_value; |
508 | 7.37k | break; |
509 | 7.37k | } |
510 | 105k | } |
511 | | // get uap selection |
512 | 7.73k | int uap = -1; |
513 | 38.6k | for (size_t i = 0; i < array_length(interpretation_properties); i++) |
514 | 30.9k | { |
515 | 30.9k | if (interpretation_properties[i].cat == cat && interpretation_properties[i].ed == ed) |
516 | 1.07k | { |
517 | 1.07k | uap = *interpretation_properties[i].int_default_value; |
518 | 1.07k | } |
519 | 30.9k | } |
520 | | |
521 | 7.73k | memset(solutions, -1, sizeof(solutions)); |
522 | 7.73k | solution_count = 0; |
523 | 7.73k | unsigned int stack[MAX_INTERPRETATION_DEPTH]; |
524 | | |
525 | 7.73k | uap_table_indexes indexes; |
526 | 7.73k | get_uap_tables(cat, ed, &indexes); |
527 | | |
528 | 7.73k | if (uap >= 0) |
529 | 0 | { |
530 | | // specific uap selected in settings, disable probing |
531 | 0 | indexes.start_index = uap; |
532 | 0 | indexes.end_index = uap; |
533 | 0 | } |
534 | | |
535 | | // if category unknown both start_index and end_index are 0 |
536 | 7.73k | if ((indexes.end_index - indexes.start_index) > 0) |
537 | 1.07k | { |
538 | 1.07k | int result = probe_possible_records (tvb, pinfo, offset, datablock_end, cat, ed, &indexes, stack, 0); |
539 | | |
540 | 1.07k | unsigned int backup_offset = offset; |
541 | | |
542 | 1.07k | proto_item *possibilities_ti = proto_tree_add_item (tree, hf_asterix_possible_interpretations, tvb, offset, 0, ENC_NA); |
543 | 1.07k | proto_tree *possibilities_tree = proto_item_add_subtree (possibilities_ti, ett_asterix_possible_interpretations); |
544 | 1.07k | proto_item_append_text (possibilities_tree, " %u", solution_count); |
545 | | |
546 | 1.07k | if (result < 0) { |
547 | 137 | if (result == -1) { |
548 | 62 | expert_add_info_format(pinfo, possibilities_ti, &ei_asterix_overflow, "Interpretations number of solutions exceeded"); |
549 | 75 | } else { |
550 | 75 | expert_add_info_format(pinfo, possibilities_ti, &ei_asterix_overflow, "Interpretations depth exceeded"); |
551 | 75 | } |
552 | 137 | } |
553 | | |
554 | 1.07k | if (solution_count == 0) { |
555 | 656 | expert_add_info_format(pinfo, possibilities_ti, &ei_asterix_overflow, "No possible solution found"); |
556 | 656 | } else { |
557 | 10.3k | for (unsigned int i = 0; i < solution_count; i++) { |
558 | 9.96k | offset = backup_offset; |
559 | 9.96k | proto_item *possible_ti = proto_tree_add_item (possibilities_tree, hf_asterix_possible_interpretation, tvb, offset, 0, ENC_NA); |
560 | 9.96k | proto_item_append_text (possible_ti, " (%u/%u)", i + 1, solution_count); |
561 | 9.96k | proto_tree *possible_tree = proto_item_add_subtree (possible_ti, ett_asterix_possible_interpretation); |
562 | | |
563 | 109k | for (unsigned int j = 0; solutions[i][j] != -1; j++) { |
564 | 99.1k | int new_offset = dissect_asterix_record (tvb, pinfo, offset, datablock_end, possible_tree, cat, ed, solutions[i][j]); |
565 | | // there should not be any error, as this solution already tried, but check anyway |
566 | 99.1k | if (new_offset <= offset) { |
567 | 0 | return; |
568 | 0 | } |
569 | 99.1k | offset = new_offset; |
570 | 99.1k | } |
571 | 9.96k | } |
572 | 423 | } |
573 | 1.07k | } |
574 | 6.65k | else { |
575 | 182k | while (offset < datablock_end) { |
576 | 178k | int new_offset = dissect_asterix_record (tvb, pinfo, offset, datablock_end, tree, cat, ed, indexes.start_index); |
577 | 178k | if (new_offset <= offset) { |
578 | 2.29k | return; |
579 | 2.29k | } |
580 | 176k | offset = new_offset; |
581 | 176k | } |
582 | 6.65k | } |
583 | 7.73k | } |
584 | | |
585 | | static bool check_datagram_datablocks (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) |
586 | 7.77k | { |
587 | 7.77k | int i = 0; |
588 | 7.77k | int n = tvb_reported_length (tvb); |
589 | | |
590 | 15.5k | while (i < n) { |
591 | 7.78k | int remaining = n - i; |
592 | 7.78k | proto_item *item; |
593 | | |
594 | 7.78k | if (remaining < 4) { |
595 | 42 | item = proto_tree_add_item (tree, hf_asterix_datablock, tvb, i, remaining, ENC_NA); |
596 | 42 | expert_add_info_format(pinfo, item, &ei_asterix_overflow, "Data length less than 4B"); |
597 | 42 | return false; |
598 | 42 | } |
599 | | |
600 | 7.74k | uint16_t len = tvb_get_uint16 (tvb, i+1, ENC_BIG_ENDIAN); |
601 | 7.74k | if (len < 4) { |
602 | 1 | item = proto_tree_add_item (tree, hf_asterix_datablock, tvb, i, len, ENC_NA); |
603 | 1 | expert_add_info_format(pinfo, item, &ei_asterix_overflow, "Datablock length less than 4B"); |
604 | 1 | return false; |
605 | 1 | } |
606 | | |
607 | 7.74k | if (remaining < len) { |
608 | 3 | item = proto_tree_add_item (tree, hf_asterix_datablock, tvb, i, len, ENC_NA); |
609 | 3 | expert_add_info_format(pinfo, item, &ei_asterix_overflow, "Not enough data for datablock"); |
610 | 3 | return false; |
611 | 3 | } |
612 | | // move to next datablock |
613 | 7.74k | i += len; |
614 | 7.74k | } |
615 | 7.73k | if (i == n) { |
616 | 7.73k | return true; |
617 | 7.73k | } |
618 | 0 | else { |
619 | 0 | proto_item *item = proto_tree_add_item (tree, hf_asterix_datablock, tvb, i, n, ENC_NA); |
620 | 0 | expert_add_info_format(pinfo, item, &ei_asterix_overflow, "Datablocks datagram misalignment"); |
621 | 0 | return false; |
622 | 0 | } |
623 | 7.73k | } |
624 | | |
625 | | static void dissect_asterix_data_blocks (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) |
626 | 7.77k | { |
627 | 7.77k | int i = 0; |
628 | 7.77k | int n = tvb_reported_length (tvb); |
629 | | |
630 | | // Datablock parsing is strict. This means that all datablocks must be aligned with UDP datagram data. If not, no datablock is parsed. |
631 | | // In future versions strictness level may be added as a configuration option. |
632 | 7.77k | if (check_datagram_datablocks (tvb, pinfo, tree)) |
633 | 7.73k | { |
634 | 15.4k | while (i < n) { |
635 | 7.73k | uint8_t cat = tvb_get_uint8 (tvb, i); |
636 | 7.73k | uint16_t len = tvb_get_uint16 (tvb, i+1, ENC_BIG_ENDIAN); |
637 | | |
638 | 7.73k | proto_item *datablock = proto_tree_add_item (tree, hf_asterix_datablock, tvb, i, len, ENC_NA); |
639 | 7.73k | proto_tree *datablock_tree = proto_item_add_subtree (datablock, ett_asterix_record); |
640 | | |
641 | 7.73k | proto_item_append_text (datablock_tree, ", Category %03d", cat); |
642 | 7.73k | proto_tree_add_item (datablock_tree, hf_asterix_category, tvb, i, 1, ENC_BIG_ENDIAN); |
643 | 7.73k | proto_tree_add_item (datablock_tree, hf_asterix_length, tvb, i + 1, 2, ENC_BIG_ENDIAN); |
644 | | |
645 | 7.73k | dissect_asterix_records (tvb, pinfo, i + HEADER_LENGTH, len - HEADER_LENGTH, datablock_tree, cat); |
646 | | |
647 | | // move to next datablock |
648 | 7.73k | i += len; |
649 | 7.73k | } |
650 | 7.73k | } |
651 | 7.77k | } |
652 | | |
653 | | static int dissect_asterix (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_) |
654 | 7.77k | { |
655 | 7.77k | col_set_str (pinfo->cinfo, COL_PROTOCOL, "ASTERIX"); |
656 | 7.77k | col_clear (pinfo->cinfo, COL_INFO); |
657 | | |
658 | 7.77k | if (hf_asterix_category <= 0) { |
659 | 2 | proto_registrar_get_byname("asterix.category"); |
660 | 2 | } |
661 | | |
662 | 7.77k | if (tree) { /* we are being asked for details */ |
663 | 7.77k | dissect_asterix_data_blocks (tvb, pinfo, tree); |
664 | 7.77k | } |
665 | | |
666 | 7.77k | return tvb_captured_length(tvb); |
667 | 7.77k | } |
668 | | |
669 | | static unsigned get_asterix_pdu_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset, void *data _U_) |
670 | 6.27k | { |
671 | 6.27k | uint16_t plen; |
672 | 6.27k | plen = tvb_get_uint16 (tvb, offset + 1, ENC_BIG_ENDIAN); |
673 | 6.27k | return plen; |
674 | 6.27k | } |
675 | | |
676 | | static int dissect_asterix_tcp (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) |
677 | 5.64k | { |
678 | | /* We do delayed field registration if needed in dissect_asterix. */ |
679 | 5.64k | tcp_dissect_pdus(tvb, pinfo, tree, true, 3, get_asterix_pdu_len, dissect_asterix, data); |
680 | 5.64k | return tvb_reported_length (tvb); |
681 | 5.64k | } |
682 | | |
683 | | static void register_asterix_fields(const char* unused _U_) |
684 | 2 | { |
685 | 2 | proto_register_field_array (proto_asterix, hf, array_length (hf)); |
686 | | |
687 | 2 | static int *ett[] = { |
688 | 2 | &ett_asterix, |
689 | 2 | &ett_asterix_record, |
690 | 2 | &ett_asterix_subtree, |
691 | 2 | &ett_asterix_possible_interpretation, |
692 | 2 | &ett_asterix_possible_interpretations, |
693 | 2 | &ett_asterix_spare_error |
694 | 2 | }; |
695 | | |
696 | 2 | proto_register_subtree_array (ett, array_length (ett)); |
697 | 2 | } |
698 | | |
699 | | void proto_register_asterix (void) |
700 | 15 | { |
701 | 15 | module_t* asterix_module; |
702 | 15 | expert_module_t* expert_asterix; |
703 | | |
704 | 15 | static ei_register_info ei[] = { |
705 | 15 | { &ei_asterix_overflow, { "asterix.overflow", PI_PROTOCOL, PI_ERROR, "Asterix overflow", EXPFILL }}, |
706 | 15 | { &hf_asterix_spare_error, { "asterix.spare_error", PI_PROTOCOL, PI_WARN, "Spare bit error", EXPFILL }}, |
707 | 15 | { &hf_asterix_fx_error, { "asterix.fx_error", PI_PROTOCOL, PI_ERROR, "FX end bit error", EXPFILL }}, |
708 | 15 | { &hf_asterix_fspec_error, { "asterix.fspec_error", PI_PROTOCOL, PI_ERROR, "FSPEC error", EXPFILL }} |
709 | 15 | }; |
710 | | |
711 | 15 | proto_asterix = proto_register_protocol ("ASTERIX packet", "ASTERIX", "asterix"); |
712 | | |
713 | | /* Delay registration of ASTERIX fields */ |
714 | 15 | proto_register_prefix("asterix", register_asterix_fields); |
715 | | |
716 | 15 | asterix_module = prefs_register_protocol(proto_asterix, NULL); |
717 | 15 | asterix_handle = register_dissector ("asterix", dissect_asterix, proto_asterix); |
718 | 15 | asterix_tcp_handle = register_dissector ("asterix-tcp", dissect_asterix_tcp, proto_asterix); |
719 | | |
720 | 15 | expert_asterix = expert_register_protocol(proto_asterix); |
721 | 15 | expert_register_field_array(expert_asterix, ei, array_length(ei)); |
722 | | |
723 | 465 | for (size_t i = 0; i < array_length(asterix_properties); i++) |
724 | 450 | { |
725 | 450 | const dialog_cat_struct *cat = &asterix_properties[i]; |
726 | 450 | prefs_register_enum_preference(asterix_module, cat->edition_name, cat->edition_desc, NULL, cat->edition_default_value, cat->edition_enums, FALSE); |
727 | | |
728 | 2.25k | for (size_t j = 0; j < array_length(interpretation_properties); j++) |
729 | 1.80k | { |
730 | 1.80k | if (interpretation_properties[j].cat == cat->cat) |
731 | 60 | { |
732 | 60 | const dialog_int_struct *int_prop = &interpretation_properties[j]; |
733 | 60 | prefs_register_enum_preference(asterix_module, int_prop->edition_name, int_prop->edition_desc, NULL, int_prop->int_default_value, int_prop->int_enums, FALSE); |
734 | 60 | } |
735 | 1.80k | } |
736 | 450 | } |
737 | | |
738 | 15 | prefs_register_enum_preference(asterix_module, "interpretations_depth", "Interpretations depth", |
739 | 15 | "Interpretations depth for categories with multiple possible UAPs", |
740 | 15 | &selected_interpretations_depth, interpretations_level_enum_vals, false); |
741 | 15 | } |
742 | | |
743 | | void proto_reg_handoff_asterix (void) |
744 | 15 | { |
745 | 15 | dissector_add_uint_with_preference("udp.port", ASTERIX_PORT, asterix_handle); |
746 | 15 | dissector_add_uint_with_preference("tcp.port", ASTERIX_PORT, asterix_tcp_handle); |
747 | 15 | } |