/src/wireshark/epan/dissectors/packet-amp.c
Line | Count | Source |
1 | | /* packet-amp.c |
2 | | * Routines for Asynchronous management Protocol dissection |
3 | | * Copyright 2018, Krishnamurthy Mayya (krishnamurthymayya@gmail.com) |
4 | | * Updated to CBOR encoding: Keith Scott, 2019 (kscott@mitre.org) |
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 | | #include "config.h" |
13 | | |
14 | | #include <epan/exceptions.h> |
15 | | #include <epan/expert.h> |
16 | | #include <epan/packet.h> |
17 | | #include <epan/tfs.h> |
18 | | #include "packet-amp.h" |
19 | | |
20 | | /* The AMP standard can be found here: |
21 | | * https://tools.ietf.org/html/draft-birrane-dtn-amp-04 |
22 | | * https://tools.ietf.org/html/draft-birrane-dtn-amp-03 |
23 | | */ |
24 | | |
25 | 16 | #define AMP_APID 0x000 /* TODO - To be decided. Currently, the function 'dissect_amp_as_subtree' is |
26 | | being called from dtn.c file when required to decode the bundle protocol's |
27 | | data-payload as AMP. Later in the future, when a dedicated field is given to |
28 | | this, this should be filled. */ |
29 | | |
30 | | /* |
31 | | */ |
32 | | static void |
33 | | add_value_time_to_tree(uint64_t value, int len, proto_tree *tree, tvbuff_t *tvb, unsigned offset, int hf_time_format) |
34 | 1 | { |
35 | 1 | nstime_t dtn_time; |
36 | | |
37 | | // If it's a relative time, just make it zero. |
38 | 1 | if ( value < 558230400 ) { |
39 | 0 | value = 0; |
40 | 0 | } |
41 | 1 | dtn_time.secs = (time_t)(value); |
42 | 1 | dtn_time.nsecs = 0; |
43 | 1 | proto_tree_add_time(tree, hf_time_format, tvb, offset, len, &dtn_time); |
44 | | |
45 | 1 | return; |
46 | 1 | } |
47 | | |
48 | 16 | #define AMP_HDR_RESERVED 0xC0 |
49 | 16 | #define AMP_HDR_ACL 0x20 |
50 | 16 | #define AMP_HDR_NACK 0x10 |
51 | 16 | #define AMP_HDR_ACK 0x08 |
52 | 16 | #define AMP_HDR_OPCODE 0x07 |
53 | | |
54 | 16 | #define AMP_ARI_NICKNAME 0x80 |
55 | 16 | #define AMP_ARI_PARAMETERS 0x40 |
56 | 16 | #define AMP_ARI_ISSUER 0x20 |
57 | 16 | #define AMP_ARI_TAG 0x10 |
58 | 16 | #define AMP_ARI_STRUCT 0x0F |
59 | 16 | #define AMP_ARI_VALUE 0xF0 |
60 | | |
61 | 16 | #define AMP_CBOR_UINT_SMALL 0x1F |
62 | | |
63 | 16 | #define AMP_TNVC_RESERVED 0xF0 |
64 | 16 | #define AMP_TNVC_MIXED 0x08 |
65 | 16 | #define AMP_TNVC_TYPE 0x04 |
66 | 16 | #define AMP_TNVC_NAME 0x02 |
67 | 16 | #define AMP_TNVC_VALUE 0x01 |
68 | | |
69 | | #define AMP_MSG_REGISTER_AGENT 0x01 |
70 | | #define AMP_MSG_DATA_REPORT 0x00 |
71 | | #define AMP_MSG_PERFORM_CONTROL 0x02 |
72 | | |
73 | | static dissector_handle_t amp_handle; |
74 | | |
75 | | void proto_register_amp(void); |
76 | | void proto_reg_handoff_amp(void); |
77 | | |
78 | | static int hf_amp_message_header; |
79 | | static int hf_amp_report_bytestring; |
80 | | static int hf_amp_report_data; |
81 | | static int hf_amp_report_integer8_small; |
82 | | static int hf_amp_report_integer; |
83 | | static int hf_amp_cbor_header; |
84 | | static int hf_amp_primary_timestamp; |
85 | | static int hf_amp_agent_name; |
86 | | static int hf_amp_text_string; |
87 | | static int hf_amp_ari_flags; |
88 | | static int hf_ari_value; |
89 | | static int hf_ari_struct; |
90 | | static int hf_ari_nickname; |
91 | | static int hf_ari_parameters; |
92 | | static int hf_ari_issuer; |
93 | | static int hf_ari_tag; |
94 | | static int hf_amp_tnvc_flags; |
95 | | static int hf_amp_tnvc_reserved; |
96 | | static int hf_amp_tnvc_mixed; |
97 | | static int hf_amp_tnvc_typed; |
98 | | static int hf_amp_tnvc_name; |
99 | | static int hf_amp_tnvc_values; |
100 | | |
101 | | /* Initialize the protocol and registered fields */ |
102 | | static int proto_amp; |
103 | | |
104 | | static int ett_amp_message_header; |
105 | | static int ett_amp_proto; |
106 | | static int ett_amp; |
107 | | static int ett_amp_cbor_header; |
108 | | static int ett_amp_message; |
109 | | static int ett_amp_register; |
110 | | static int ett_amp_report_set; |
111 | | static int ett_amp_report; |
112 | | static int ett_amp_tnvc_flags; |
113 | | static int ett_amp_ari_flags; |
114 | | |
115 | | static int hf_amp_reserved; |
116 | | static int hf_amp_acl; |
117 | | static int hf_amp_nack; |
118 | | static int hf_amp_ack; |
119 | | static int hf_amp_opcode; |
120 | | static int hf_amp_rx_name; |
121 | | |
122 | | static expert_field ei_amp_cbor_malformed; |
123 | | |
124 | | static const value_string opcode[] = { |
125 | | { 0, "Register Agent" }, |
126 | | { 1, "Report Set" }, |
127 | | { 2, "Perform Control" }, |
128 | | { 3, "Table Set" }, |
129 | | { 0, NULL } |
130 | | }; |
131 | | |
132 | | static const value_string amp_ari_struct_type[] = { |
133 | | { 0, "Const" }, |
134 | | { 1, "Control" }, |
135 | | { 2, "Externally Defined Data" }, |
136 | | { 3, "Macro" }, |
137 | | { 4, "Operation" }, |
138 | | { 5, "Report Template" }, |
139 | | { 6, "State-Based Rule" }, |
140 | | { 7, "Table Templates" }, |
141 | | { 8, "Time-Based Rule" }, |
142 | | { 9, "Variables" }, |
143 | | { 10, "Metadata" }, |
144 | | { 11, "Reserved" }, |
145 | | { 12, "Reserved" }, |
146 | | { 13, "Reserved" }, |
147 | | { 14, "Reserved" }, |
148 | | { 15, "Reserved" }, |
149 | | { 0, NULL } |
150 | | }; |
151 | | |
152 | | |
153 | | /* AMP Message Header */ |
154 | | static int * const amp_message_header[] = { |
155 | | &hf_amp_reserved, |
156 | | &hf_amp_acl, |
157 | | &hf_amp_nack, |
158 | | &hf_amp_ack, |
159 | | &hf_amp_opcode, |
160 | | 0 |
161 | | }; |
162 | | |
163 | | /* TNVC Flags */ |
164 | | static int * const amp_tnvc_flags[] = { |
165 | | &hf_amp_tnvc_reserved, |
166 | | &hf_amp_tnvc_mixed, |
167 | | &hf_amp_tnvc_typed, |
168 | | &hf_amp_tnvc_name, |
169 | | &hf_amp_tnvc_values, |
170 | | 0 |
171 | | }; |
172 | | |
173 | | /* ARI Flags */ |
174 | | static int * const amp_ari_flags[] = { |
175 | | &hf_ari_nickname, |
176 | | &hf_ari_parameters, |
177 | | &hf_ari_issuer, |
178 | | &hf_ari_tag, |
179 | | &hf_ari_struct, |
180 | | 0 |
181 | | }; |
182 | | |
183 | | /* CBOR Types */ |
184 | | typedef enum { |
185 | | CBOR_UNKNOWN = -1, |
186 | | CBOR_UINT = 0, // Positive, unsigned integer |
187 | | CBOR_INT = 1, // Negative integer |
188 | | CBOR_BYTESTRING = 2, |
189 | | CBOR_TEXTSTRING = 3, |
190 | | CBOR_ARRAY = 4, |
191 | | CBOR_MAP = 5, |
192 | | CBOR_SEMANTIC_TAG = 6, |
193 | | CBOR_PRIMITIVE = 7 |
194 | | } CBOR_TYPE; |
195 | | |
196 | | typedef struct { |
197 | | int type; |
198 | | int size; // for integers, the value |
199 | | // for bytestrings and textstrings, the size of the string |
200 | | // for arrays, the number of elements in the array |
201 | | uint64_t totalSize; // total size (including size above and any bytestring |
202 | | // size). |
203 | | uint64_t uint; |
204 | | } cborObj; |
205 | | |
206 | | |
207 | | // Decode the CBOR object at the given offset in the tvb. |
208 | | // The returned cborObj contains the object (with type) and the size |
209 | | // (including the CBOR identifier). |
210 | | static cborObj cbor_info(tvbuff_t *tvb, unsigned offset) |
211 | 41 | { |
212 | 41 | int tmp = 0; |
213 | 41 | cborObj ret; |
214 | 41 | ret.size = 0; |
215 | 41 | ret.totalSize = 0; |
216 | 41 | ret.uint = -1; |
217 | 41 | int theSize; |
218 | | |
219 | 41 | tmp = tvb_get_uint8(tvb, offset); |
220 | | |
221 | 41 | offset += 1; |
222 | 41 | ret.size += 1; |
223 | | |
224 | 41 | ret.type = (tmp & 0xE0)>>5; // Top 3 bits |
225 | 41 | theSize = (tmp & 0x1F); |
226 | | |
227 | 41 | switch ( ret.type ) |
228 | 41 | { |
229 | 9 | case 0x00: // Positive / Unsigned integer |
230 | 9 | if ( theSize<24 ) { |
231 | 9 | ret.uint = (tmp & 0x1F); // May be actual size or indication of follow-on size |
232 | 9 | } else if (theSize==24) { // next byte is uint8_t data |
233 | 0 | ret.uint = tvb_get_uint8(tvb, offset); |
234 | 0 | ret.size += 1; |
235 | 0 | } else if (theSize==25) { // next 2 bytes are uint16_t data |
236 | 0 | ret.uint = tvb_get_uint16(tvb, offset, ENC_BIG_ENDIAN); |
237 | 0 | ret.size += 2; |
238 | 0 | } else if (theSize==26) { // next 4 bytes are uint32_t data |
239 | 0 | ret.uint = tvb_get_uint32(tvb, offset, ENC_BIG_ENDIAN); |
240 | 0 | ret.size += 4; |
241 | 0 | } else if (theSize==27) { // next 8 bytes are uint64_t data |
242 | 0 | ret.uint = tvb_get_uint64(tvb, offset, ENC_BIG_ENDIAN); |
243 | 0 | ret.size += 8; |
244 | 0 | } |
245 | 9 | ret.totalSize = ret.size; |
246 | 9 | break; |
247 | | |
248 | 21 | case 0x02: // Byte string |
249 | 21 | if ( theSize<24 ) { // Array size is contained in the identifier byte |
250 | 0 | ret.uint = (tmp & 0x1F); |
251 | 21 | } else if (theSize==24) { // next byte is uint8_t data (length) |
252 | 21 | ret.uint = tvb_get_uint8(tvb, offset); |
253 | 21 | ret.size += 1; |
254 | 21 | } else if (theSize==25) { // next 2bytes are uint16_t data (length) |
255 | 0 | ret.uint = tvb_get_uint16(tvb, offset, ENC_BIG_ENDIAN); |
256 | 0 | ret.size += 2; |
257 | 0 | } else if (theSize==26) { // next 4bytes are uint32_t data |
258 | 0 | ret.uint = tvb_get_uint32(tvb, offset, ENC_BIG_ENDIAN); |
259 | 0 | ret.size += 4; |
260 | 0 | } else if (theSize==27) { // next byte is uint64_t data |
261 | 0 | ret.uint = tvb_get_uint64(tvb, offset, ENC_BIG_ENDIAN); |
262 | 0 | ret.size += 8; |
263 | 0 | } |
264 | 21 | ret.totalSize = ret.size+ret.uint; |
265 | 21 | break; |
266 | | |
267 | 1 | case 0x03: // Text string |
268 | 1 | if ( theSize<24 ) // Array size is contained in the identifier byte |
269 | 1 | { |
270 | 1 | ret.uint = (tmp & 0x1F); |
271 | 1 | } else if (theSize==24) // next byte is uint8_t data |
272 | 0 | { |
273 | 0 | ret.uint = tvb_get_uint8(tvb, offset); |
274 | 0 | ret.size += 1; |
275 | 0 | } else if (theSize==25) // next 2bytes are uint16_t data |
276 | 0 | { |
277 | 0 | ret.uint = tvb_get_uint16(tvb, offset, ENC_BIG_ENDIAN); |
278 | 0 | ret.size += 2; |
279 | 0 | } else if (theSize==26) // next 4bytes are uint32_t data |
280 | 0 | { |
281 | 0 | ret.uint = tvb_get_uint32(tvb, offset, ENC_BIG_ENDIAN); |
282 | 0 | ret.size += 4; |
283 | 0 | } else if (theSize==27) // next byte is uint64_t data |
284 | 0 | { |
285 | 0 | ret.uint = tvb_get_uint64(tvb, offset, ENC_BIG_ENDIAN); |
286 | 0 | ret.size += 8; |
287 | 0 | } |
288 | 1 | ret.totalSize = ret.size+ret.uint; |
289 | 1 | break; |
290 | | |
291 | 5 | case 0x04: // Array |
292 | 5 | if ( theSize<24 ) // Array size is contained in the identifier byte |
293 | 0 | { |
294 | 0 | ret.uint = (tmp & 0x1F); |
295 | 5 | } else if (theSize==24) // next byte is uint8_t data |
296 | 0 | { |
297 | 0 | ret.uint = tvb_get_uint8(tvb, offset); |
298 | 0 | ret.size += 1; |
299 | 5 | } else if (theSize==25) // next 2bytes are uint16_t data |
300 | 0 | { |
301 | 0 | ret.uint = tvb_get_uint16(tvb, offset, ENC_BIG_ENDIAN); |
302 | 0 | ret.size += 2; |
303 | 5 | } else if (theSize==26) // next 4bytes are uint32_t data |
304 | 0 | { |
305 | 0 | ret.uint = tvb_get_uint32(tvb, offset, ENC_BIG_ENDIAN); |
306 | 0 | ret.size += 4; |
307 | 5 | } else if (theSize==27) // next byte is uint64_t data |
308 | 0 | { |
309 | 0 | ret.uint = tvb_get_uint64(tvb, offset, ENC_BIG_ENDIAN); |
310 | 0 | ret.size += 8; |
311 | 0 | } |
312 | | // I know how many elements are in the array, but NOT the total |
313 | | // size of the array data. |
314 | 5 | ret.totalSize = -1; |
315 | 5 | break; |
316 | | |
317 | 1 | case 0x06: // Semantic tag |
318 | 1 | if ( theSize<24 ) |
319 | 0 | { |
320 | 0 | ret.uint = (tmp & 0x1F); |
321 | 0 | } |
322 | 1 | ret.totalSize += ret.uint; |
323 | 1 | break; |
324 | | |
325 | 1 | case 0x01: // Negative integer |
326 | 4 | case 0x07: // Primitives e.g. break, float, simple values |
327 | 4 | default: |
328 | | // TODO -- not supported yet. |
329 | 4 | break; |
330 | 41 | } |
331 | 41 | return ret; |
332 | 41 | } |
333 | | |
334 | | void |
335 | | dissect_amp_as_subtree(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, unsigned offset) |
336 | 1 | { |
337 | 1 | uint64_t messages = 0; |
338 | 1 | unsigned int i=0; |
339 | 1 | unsigned int j=0; |
340 | 1 | unsigned int k=0; |
341 | 1 | unsigned int numTNVCEntries = 0; |
342 | | |
343 | 1 | proto_tree *amp_tree = NULL; |
344 | 1 | proto_tree *amp_register_tree = NULL; |
345 | 1 | proto_tree *amp_report_set_tree = NULL; |
346 | 1 | proto_tree *amp_report_tree = NULL; |
347 | 1 | proto_tree *amp_report_TNVC_tree = NULL; |
348 | 1 | proto_tree *amp_control_tree = NULL; |
349 | 1 | proto_tree *amp_table_tree = NULL; |
350 | 1 | proto_item *payload_item = NULL; |
351 | 1 | proto_tree *amp_message_tree = NULL; |
352 | 1 | proto_tree *amp_report_TNVC_sub_tree = NULL; |
353 | 1 | proto_item *amp_message = NULL; |
354 | 1 | proto_item *amp_register = NULL; |
355 | 1 | proto_item *amp_report_set = NULL; |
356 | 1 | proto_item *amp_report = NULL; |
357 | | |
358 | 1 | cborObj myObj; |
359 | 1 | cborObj tmpObj; |
360 | 1 | cborObj tmpObj2; |
361 | 1 | cborObj tmpObj3; |
362 | 1 | cborObj tmpObj4; |
363 | 1 | int reportHasTimestamp = 0; |
364 | 1 | unsigned report_types_offset = 0; |
365 | | |
366 | 1 | amp_tree = proto_tree_add_subtree(tree, tvb, offset, -1, ett_amp_proto, |
367 | 1 | &payload_item, "Payload Data: AMP Protocol"); |
368 | | |
369 | | // First byte is the main CBOR type (probably an array of things) |
370 | | // e.g. 0x82 (byte array of 2 things) |
371 | 1 | myObj = cbor_info(tvb, offset); |
372 | 1 | offset += myObj.size; |
373 | 1 | messages = myObj.uint; |
374 | | |
375 | | // Item 0 is the timestamp |
376 | 1 | myObj = cbor_info(tvb, offset); |
377 | 1 | add_value_time_to_tree((int) myObj.uint, MIN(myObj.size-1, 1), amp_tree, tvb, offset, hf_amp_primary_timestamp); |
378 | 1 | offset += myObj.size; |
379 | | |
380 | 27 | for ( i=1; i<messages; i++ ) { |
381 | | // Get the bytestring object that gives the total length of the AMP chunk |
382 | 27 | myObj = cbor_info(tvb, offset); |
383 | 27 | offset += myObj.size; // Note: myObj.uint is the length of the amp chunk; used later |
384 | | // to advance offset past this message. |
385 | | |
386 | | // The first byte of this byte string (the AMP message) is going to be the message header |
387 | | // This is just a byte, not a CBOR uint8 |
388 | 27 | int ampHeader; |
389 | 27 | ampHeader = tvb_get_uint8(tvb, offset); |
390 | 27 | amp_message_tree = proto_tree_add_subtree(amp_tree, tvb, offset, -1, |
391 | 27 | ett_amp_message, &_message, "AMP Message"); |
392 | | |
393 | 27 | proto_tree_add_bitmask(amp_message_tree, tvb, offset, hf_amp_message_header, ett_amp_message_header, |
394 | 27 | amp_message_header, ENC_BIG_ENDIAN); |
395 | 27 | offset += 1; |
396 | 27 | unsigned old_offset; |
397 | | |
398 | 27 | switch ( ampHeader & 0x07) |
399 | 27 | { |
400 | 12 | case 0x00: // Register agent |
401 | | //amp_register_sub_tree = proto_item_add_subtree(amp_message_tree, ett_amp); |
402 | 12 | amp_register_tree = proto_tree_add_subtree(amp_message_tree, tvb, offset-1, 1, |
403 | 12 | ett_amp_register, &_register, "Register-Agent"); |
404 | 12 | tmpObj = cbor_info(tvb, offset); // Should come back a CBOR text string if some length. |
405 | 12 | offset += tmpObj.size; |
406 | 12 | proto_tree_add_item(amp_register_tree, hf_amp_agent_name, tvb, offset, |
407 | 12 | (int) tmpObj.uint, ENC_ASCII|ENC_NA); |
408 | 12 | old_offset = offset; |
409 | 12 | offset += (int) tmpObj.uint; |
410 | 12 | if (offset < old_offset) { |
411 | 0 | proto_tree_add_expert_remaining(amp_tree, pinfo, &ei_amp_cbor_malformed, tvb, offset); |
412 | 0 | return; |
413 | 0 | } |
414 | 12 | break; |
415 | | |
416 | 12 | case 0x01: // Report set |
417 | 0 | amp_report_set_tree = proto_tree_add_subtree(amp_message_tree, tvb, offset-2, -1, |
418 | 0 | ett_amp_report_set, &_report_set, "Report-Set"); |
419 | | |
420 | | // Rx Names |
421 | 0 | tmpObj = cbor_info(tvb, offset); // Should come back a CBOR array of some size (Rx Names) |
422 | 0 | if ( tmpObj.type != 0x04 ) { |
423 | 0 | return; |
424 | 0 | } |
425 | 0 | offset += tmpObj.size; |
426 | | // read rx names |
427 | 0 | for ( j=0; j<tmpObj.uint; j++ ) { |
428 | 0 | tmpObj2 = cbor_info(tvb, offset); // Should be text string of some size |
429 | 0 | offset += tmpObj2.size; |
430 | 0 | proto_tree_add_item(amp_report_set_tree, hf_amp_rx_name, tvb, offset, |
431 | 0 | (int) tmpObj2.uint, ENC_ASCII|ENC_NA); |
432 | 0 | old_offset = offset; |
433 | 0 | offset += (unsigned)tmpObj2.uint; |
434 | 0 | if (offset < old_offset) { |
435 | 0 | proto_tree_add_expert_remaining(amp_tree, pinfo, &ei_amp_cbor_malformed, tvb, offset); |
436 | 0 | return; |
437 | 0 | } |
438 | 0 | } |
439 | | |
440 | | // How many reports? |
441 | 0 | tmpObj2 = cbor_info(tvb, offset); // Should come back a CBOR array of some size |
442 | 0 | offset += tmpObj2.size; |
443 | |
|
444 | 0 | for ( j=0; j<tmpObj2.uint; j++ ) { |
445 | | // Internals of each report per section 8.4.7 of https://tools.ietf.org/pdf/draft-birrane-dtn-amp-07.pdf |
446 | | // amp_report_sub_tree = proto_item_add_subtree(amp_report_set_tree, ett_amp); |
447 | 0 | amp_report_tree = proto_tree_add_subtree(amp_report_set_tree, tvb, offset, -1, |
448 | 0 | ett_amp_report, &_report, "Report"); |
449 | | |
450 | | // Each Report is a: |
451 | | // Template [ARI] |
452 | | // Timestamp [TS] (opt) |
453 | | // Entries [TNVC] (bytestring containing a TNVC) |
454 | 0 | tmpObj3 = cbor_info(tvb, offset); // Should come back a CBOR array of size 2 or 3 |
455 | 0 | offset += tmpObj3.size; |
456 | 0 | if ( tmpObj3.uint==3 ) |
457 | 0 | { |
458 | 0 | reportHasTimestamp = 1; |
459 | 0 | } else |
460 | 0 | { |
461 | 0 | reportHasTimestamp = 0; |
462 | 0 | } |
463 | | |
464 | | // Template (bytestring); starts with ARI |
465 | 0 | tmpObj3 = cbor_info(tvb, offset); |
466 | 0 | offset += tmpObj3.size; |
467 | 0 | uint8_t ariFlags; |
468 | 0 | ariFlags = tvb_get_uint8(tvb, offset); |
469 | |
|
470 | 0 | if ( (ariFlags&0x0F)==0x03 ) { |
471 | | // Literal |
472 | 0 | proto_tree_add_uint(amp_report_tree, hf_ari_value, tvb, offset, 1, ariFlags); |
473 | 0 | } else { |
474 | | // NOT literal |
475 | 0 | proto_tree_add_bitmask(amp_report_tree, tvb, offset, hf_amp_ari_flags, ett_amp_ari_flags, |
476 | 0 | amp_ari_flags, ENC_BIG_ENDIAN); |
477 | |
|
478 | 0 | } |
479 | | //proto_tree_add_uint(amp_ari_tree, hf_ari_struct, tvb, offset, 1, ariFlags); |
480 | |
|
481 | 0 | if ( (ariFlags & 0x0F) != 0x03 && tmpObj3.uint > 0 ) |
482 | 0 | { |
483 | | // ARI is NOT Literal |
484 | 0 | proto_tree_add_item(amp_report_tree, hf_amp_report_bytestring, tvb, offset+1, (int) tmpObj3.uint-1, ENC_NA); |
485 | 0 | } |
486 | 0 | old_offset = offset; |
487 | 0 | offset += (int) tmpObj3.uint; |
488 | 0 | if (offset < old_offset) { |
489 | 0 | proto_tree_add_expert_remaining(amp_tree, pinfo, &ei_amp_cbor_malformed, tvb, offset); |
490 | 0 | return; |
491 | 0 | } |
492 | | |
493 | 0 | if ( reportHasTimestamp ) |
494 | 0 | { |
495 | 0 | tmpObj3 = cbor_info(tvb, offset); |
496 | 0 | offset += 1; |
497 | 0 | add_value_time_to_tree((int) tmpObj3.uint, 4, amp_report_tree, tvb, offset, hf_amp_primary_timestamp); |
498 | 0 | offset += (tmpObj3.size-1); |
499 | 0 | } |
500 | | |
501 | | // Entries [TNVC] -- This is th collection of data values that comprise the report |
502 | | // contents in accordance with the associated Report Template. |
503 | | // Contained in a CBOR bytestring |
504 | 0 | tmpObj3 = cbor_info(tvb, offset); |
505 | 0 | offset += tmpObj3.size; |
506 | | |
507 | | // Now read array length for TNVC |
508 | 0 | tmpObj3 = cbor_info(tvb, offset); |
509 | 0 | offset += tmpObj3.size; |
510 | 0 | numTNVCEntries = (int) tmpObj3.uint; |
511 | | |
512 | | // TNVC Flags |
513 | 0 | proto_tree_add_bitmask(amp_report_tree, tvb, offset, hf_amp_tnvc_flags, ett_amp_tnvc_flags, |
514 | 0 | amp_tnvc_flags, ENC_BIG_ENDIAN); |
515 | 0 | offset += 1; |
516 | | |
517 | | // TNVC entries |
518 | 0 | amp_report_TNVC_sub_tree = proto_item_add_subtree(amp_report_tree, ett_amp); |
519 | 0 | amp_report_TNVC_tree = proto_tree_add_subtree(amp_report_TNVC_sub_tree, tvb, offset, -1, |
520 | 0 | ett_amp_message, &_message, "TNVC Entries"); |
521 | | |
522 | | // Byte string containing data types |
523 | 0 | tmpObj3 = cbor_info(tvb, offset); |
524 | 0 | offset += tmpObj3.size; |
525 | 0 | report_types_offset = offset; |
526 | 0 | offset += (int) tmpObj3.uint; |
527 | 0 | if (offset < report_types_offset) { |
528 | 0 | proto_tree_add_expert_remaining(amp_tree, pinfo, &ei_amp_cbor_malformed, tvb, offset); |
529 | 0 | return; |
530 | 0 | } |
531 | | |
532 | | // TNVC data items |
533 | 0 | for ( k=0; k+2<numTNVCEntries; k++ ) { |
534 | 0 | tmpObj3 = cbor_info(tvb, offset); |
535 | 0 | switch ( tmpObj3.type ) { |
536 | 0 | case 0x02: // bytestring |
537 | | // Get the type from the type dictionary |
538 | 0 | switch ( tvb_get_uint8(tvb, report_types_offset+k) ) { |
539 | 0 | case 0x12: // string |
540 | | // It's a text string of some size INSIDE a byte string |
541 | 0 | tmpObj4 = cbor_info(tvb, offset+tmpObj3.size); |
542 | | // printf("tmpObj4.type of (%02x) is (%d)\n", tvb_get_uint8(tvb, offset+tmpObj3.size), tmpObj4.type); |
543 | 0 | proto_tree_add_item(amp_report_TNVC_tree, hf_amp_text_string, tvb, |
544 | 0 | offset+tmpObj3.size+tmpObj4.size, |
545 | 0 | (int) tmpObj3.uint-tmpObj4.size, 0x00); |
546 | 0 | break; |
547 | 0 | case 0x16: // uvast |
548 | 0 | tmpObj4 = cbor_info(tvb, offset+tmpObj3.size); |
549 | 0 | if ( tmpObj4.type != CBOR_UINT ) { |
550 | 0 | break; |
551 | 0 | } |
552 | 0 | switch ( tmpObj4.size ) { |
553 | 0 | case 1: |
554 | 0 | proto_tree_add_item(amp_report_TNVC_tree, hf_amp_report_integer8_small, |
555 | 0 | tvb, offset+tmpObj3.size, 1, 0x00); |
556 | 0 | break; |
557 | 0 | default: // Note: CBOR will only let these sizes be 1, 2, 4, or 8 bytes. |
558 | 0 | proto_tree_add_item(amp_report_TNVC_tree, hf_amp_report_integer, |
559 | 0 | tvb, offset+tmpObj3.size+1, tmpObj4.size-1, 0x00); |
560 | 0 | break; |
561 | 0 | } |
562 | 0 | break; |
563 | 0 | default: |
564 | 0 | break; |
565 | 0 | } |
566 | 0 | break; |
567 | 0 | default: |
568 | 0 | proto_tree_add_item(amp_report_TNVC_tree, hf_amp_report_data, tvb, offset+tmpObj3.size, (int) tmpObj3.uint, 0x00); |
569 | 0 | break; |
570 | 0 | } |
571 | 0 | if ( tmpObj3.totalSize == 0 ) { |
572 | 0 | break; |
573 | 0 | } |
574 | | /* Arrays have no known size, and a string length can exceed the packet */ |
575 | 0 | if ( tmpObj3.totalSize > tvb_reported_length_remaining(tvb, offset) ) { |
576 | 0 | proto_tree_add_expert_remaining(amp_tree, pinfo, &ei_amp_cbor_malformed, tvb, offset); |
577 | 0 | return; |
578 | 0 | } |
579 | 0 | offset += (unsigned)tmpObj3.totalSize; |
580 | 0 | } |
581 | |
|
582 | 0 | } |
583 | 0 | break; |
584 | | |
585 | 0 | case 0x02: // Perform Control |
586 | | // TODO |
587 | | //amp_control_sub_tree = proto_item_add_subtree(amp_message_tree, ett_amp); |
588 | 0 | amp_control_tree = proto_tree_add_subtree(amp_message_tree, tvb, offset-1, -1, |
589 | 0 | ett_amp_message, &_message, "Perform-Control"); |
590 | 0 | proto_tree_add_item(amp_control_tree, hf_amp_cbor_header, tvb, offset, 1, ENC_BIG_ENDIAN); |
591 | 0 | break; |
592 | | |
593 | 0 | case 0x03: // Table Set |
594 | | // TODO |
595 | | //amp_table_sub_tree = proto_item_add_subtree(amp_message_tree, ett_amp); |
596 | 0 | amp_table_tree = proto_tree_add_subtree(amp_message_tree, tvb, offset-1, -1, |
597 | 0 | ett_amp_message, &_message, "AMP Message: Table-Set"); |
598 | 0 | proto_tree_add_item(amp_table_tree, hf_amp_cbor_header, tvb, offset, 1, ENC_BIG_ENDIAN); |
599 | 0 | break; |
600 | 15 | default: |
601 | 15 | break; |
602 | 27 | } |
603 | 27 | } |
604 | | |
605 | 0 | return; |
606 | 1 | } |
607 | | |
608 | | /* Code to actually dissect the packets */ |
609 | | static int |
610 | | dissect_amp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_) |
611 | 0 | { |
612 | 0 | int offset = 0; |
613 | 0 | proto_item *amp_packet; |
614 | 0 | proto_item *amp_tree; |
615 | 0 | int amp_packet_reported_length; |
616 | |
|
617 | 0 | col_set_str(pinfo->cinfo, COL_PROTOCOL, "AMP"); |
618 | 0 | col_clear(pinfo->cinfo, COL_INFO); |
619 | |
|
620 | 0 | amp_packet_reported_length = tvb_reported_length_remaining(tvb, 0); |
621 | |
|
622 | 0 | amp_packet = proto_tree_add_item(tree, proto_amp, tvb, 0, amp_packet_reported_length, ENC_NA); |
623 | 0 | amp_tree = proto_item_add_subtree(amp_packet, ett_amp); |
624 | |
|
625 | 0 | dissect_amp_as_subtree (tvb, pinfo, amp_tree, offset); |
626 | |
|
627 | 0 | return tvb_captured_length(tvb); |
628 | 0 | } |
629 | | |
630 | | void |
631 | | proto_register_amp(void) |
632 | 16 | { |
633 | 16 | static hf_register_info hf[] = { |
634 | | |
635 | 16 | { &hf_amp_message_header, |
636 | 16 | { "AMP Message Header", "amp.message.header", |
637 | 16 | FT_UINT8, BASE_DEC, NULL, 0x0, |
638 | 16 | NULL, HFILL } |
639 | 16 | }, |
640 | 16 | { &hf_amp_report_data, |
641 | 16 | { "Report-Data", "amp.report.data", |
642 | 16 | FT_BYTES, BASE_NONE, NULL, 0x0, |
643 | 16 | NULL, HFILL } |
644 | 16 | }, |
645 | 16 | { &hf_amp_report_bytestring, |
646 | 16 | { "Report-Bytestring", "amp.report.bytestring", |
647 | 16 | FT_BYTES, BASE_NONE, NULL, 0x0, |
648 | 16 | NULL, HFILL } |
649 | 16 | }, |
650 | 16 | { &hf_amp_report_integer8_small, |
651 | 16 | { "Report-Integer8_small", "amp.report.integer8_small", |
652 | 16 | FT_UINT8, BASE_DEC, NULL, AMP_CBOR_UINT_SMALL, |
653 | 16 | NULL, HFILL } |
654 | 16 | }, |
655 | 16 | { &hf_amp_report_integer, |
656 | 16 | { "Report-Integer", "amp.report.integer", |
657 | 16 | FT_UINT64, BASE_DEC, NULL, 0x0, |
658 | 16 | NULL, HFILL } |
659 | 16 | }, |
660 | 16 | { &hf_amp_cbor_header, |
661 | 16 | { "CBOR-Header", "amp.cbor_header", |
662 | 16 | FT_UINT8, BASE_HEX, NULL, 0x0, |
663 | 16 | NULL, HFILL } |
664 | 16 | }, |
665 | 16 | { &hf_amp_primary_timestamp, |
666 | 16 | { "Timestamp", "amp.primary_timestamp", |
667 | 16 | FT_ABSOLUTE_TIME, ABSOLUTE_TIME_LOCAL, NULL, 0x0, NULL, HFILL} |
668 | 16 | }, |
669 | 16 | { &hf_amp_tnvc_flags, |
670 | 16 | { "TNVC Flags", "amp.tnvc.flags", |
671 | 16 | FT_UINT8, BASE_DEC, NULL, 0x0, |
672 | 16 | NULL, HFILL } |
673 | 16 | }, |
674 | 16 | { &hf_amp_tnvc_reserved, |
675 | 16 | { "Reserved", "amp.tnvc.reserved", |
676 | 16 | FT_UINT8, BASE_DEC, NULL, AMP_TNVC_RESERVED, |
677 | 16 | NULL, HFILL } |
678 | 16 | }, |
679 | 16 | { &hf_amp_tnvc_mixed, |
680 | 16 | { "Mixed", "amp.tnvc.mixed", |
681 | 16 | FT_BOOLEAN, 8, TFS(&tfs_yes_no), AMP_TNVC_MIXED, |
682 | 16 | NULL, HFILL } |
683 | 16 | }, |
684 | 16 | { &hf_amp_tnvc_typed, |
685 | 16 | { "TNVC Values are Typed", "amp.tnvc.typed", |
686 | 16 | FT_BOOLEAN, 8, TFS(&tfs_yes_no), AMP_TNVC_TYPE, |
687 | 16 | NULL, HFILL } |
688 | 16 | }, |
689 | 16 | { &hf_amp_tnvc_name, |
690 | 16 | { "Name", "amp.tnvc.name", |
691 | 16 | FT_BOOLEAN, 8, TFS(&tfs_yes_no), AMP_TNVC_NAME, |
692 | 16 | NULL, HFILL } |
693 | 16 | }, |
694 | 16 | { &hf_amp_tnvc_values, |
695 | 16 | { "Values", "amp.tnvc.value", |
696 | 16 | FT_BOOLEAN, 8, TFS(&tfs_yes_no), AMP_TNVC_VALUE, |
697 | 16 | NULL, HFILL } |
698 | 16 | }, |
699 | 16 | { &hf_ari_nickname, |
700 | 16 | { "Nickname", "amp.nickname", |
701 | 16 | FT_BOOLEAN, 8, TFS(&tfs_present_not_present), AMP_ARI_NICKNAME, |
702 | 16 | NULL, HFILL } |
703 | 16 | }, |
704 | 16 | { &hf_amp_ari_flags, |
705 | 16 | { "ARI Flags", "amp.ari.flags", |
706 | 16 | FT_UINT8, BASE_DEC, NULL, 0x0, |
707 | 16 | NULL, HFILL } |
708 | 16 | }, |
709 | 16 | { &hf_ari_parameters, |
710 | 16 | { "Parameters", "amp.parameters", |
711 | 16 | FT_BOOLEAN, 8, TFS(&tfs_present_not_present), AMP_ARI_PARAMETERS, |
712 | 16 | NULL, HFILL } |
713 | 16 | }, |
714 | 16 | { &hf_ari_issuer, |
715 | 16 | { "Issuer", "amp.issuer", |
716 | 16 | FT_BOOLEAN, 8, TFS(&tfs_present_not_present), AMP_ARI_ISSUER, |
717 | 16 | NULL, HFILL } |
718 | 16 | }, |
719 | 16 | { &hf_ari_tag, |
720 | 16 | { "Tag", "amp.tag", |
721 | 16 | FT_BOOLEAN, 8, TFS(&tfs_present_not_present), AMP_ARI_TAG, |
722 | 16 | NULL, HFILL } |
723 | 16 | }, |
724 | 16 | { &hf_ari_value, |
725 | 16 | { "Value", "amp.value", |
726 | 16 | FT_UINT8, BASE_DEC, NULL, AMP_ARI_VALUE, |
727 | 16 | NULL, HFILL } |
728 | 16 | }, |
729 | 16 | { &hf_ari_struct, |
730 | 16 | { "Struct Type", "amp.struct", |
731 | 16 | FT_UINT8, BASE_DEC, VALS(amp_ari_struct_type), AMP_ARI_STRUCT, |
732 | 16 | NULL, HFILL } |
733 | 16 | }, |
734 | 16 | { &hf_amp_reserved, |
735 | 16 | { "Reserved", "amp.reserved", |
736 | 16 | FT_UINT8, BASE_DEC, NULL, AMP_HDR_RESERVED, |
737 | 16 | NULL, HFILL } |
738 | 16 | }, |
739 | 16 | { &hf_amp_acl, |
740 | 16 | { "ACL", "amp.acl", |
741 | 16 | FT_BOOLEAN, 8, TFS(&tfs_present_not_present), AMP_HDR_ACL, |
742 | 16 | NULL, HFILL } |
743 | 16 | }, |
744 | 16 | { &hf_amp_nack, |
745 | 16 | { "NACK", "amp.nack", |
746 | 16 | FT_BOOLEAN, 8, TFS(&tfs_present_not_present), AMP_HDR_NACK, |
747 | 16 | NULL, HFILL } |
748 | 16 | }, |
749 | 16 | { &hf_amp_ack, |
750 | 16 | { "ACK", "amp.ack", |
751 | 16 | FT_BOOLEAN, 8, TFS(&tfs_present_not_present), AMP_HDR_ACK, |
752 | 16 | NULL, HFILL } |
753 | 16 | }, |
754 | 16 | { &hf_amp_opcode, |
755 | 16 | { "Opcode", "amp.opcode", |
756 | 16 | FT_UINT8, BASE_DEC, VALS(opcode), AMP_HDR_OPCODE, |
757 | 16 | NULL, HFILL } |
758 | 16 | }, |
759 | 16 | {&hf_amp_agent_name, |
760 | 16 | {"Agent-Name", "amp.agent_name", |
761 | 16 | FT_STRINGZPAD, BASE_NONE, NULL, 0x0, NULL, HFILL} |
762 | 16 | }, |
763 | 16 | {&hf_amp_text_string, |
764 | 16 | {"String", "amp.string", |
765 | 16 | FT_STRINGZPAD, BASE_NONE, NULL, 0x0, NULL, HFILL} |
766 | 16 | }, |
767 | 16 | {&hf_amp_rx_name, |
768 | 16 | {"Rx-Name", "amp.rx_name", |
769 | 16 | FT_STRINGZPAD, BASE_NONE, NULL, 0x0, NULL, HFILL} |
770 | 16 | }, |
771 | | |
772 | 16 | }; |
773 | | |
774 | | /* Setup protocol subtree array */ |
775 | 16 | static int *ett[] = { |
776 | 16 | &ett_amp, |
777 | 16 | &ett_amp_message_header, |
778 | 16 | &ett_amp_cbor_header, |
779 | 16 | &ett_amp_message, |
780 | 16 | &ett_amp_register, |
781 | 16 | &ett_amp_report_set, |
782 | 16 | &ett_amp_report, |
783 | 16 | &ett_amp_tnvc_flags, |
784 | 16 | &ett_amp_ari_flags, |
785 | 16 | &ett_amp_proto |
786 | 16 | }; |
787 | | |
788 | 16 | static ei_register_info ei[] = { |
789 | 16 | { &ei_amp_cbor_malformed, { "amp.cbor.malformed", PI_MALFORMED, PI_ERROR, "Malformed CBOR object", EXPFILL }}, |
790 | 16 | }; |
791 | | |
792 | | /* Register the protocol name and description */ |
793 | 16 | proto_amp = proto_register_protocol("AMP", "AMP", "amp"); |
794 | | |
795 | | /* Required function calls to register the header fields and subtrees used */ |
796 | 16 | proto_register_field_array(proto_amp, hf, array_length(hf)); |
797 | 16 | proto_register_subtree_array(ett, array_length(ett)); |
798 | 16 | expert_module_t* expert_amp = expert_register_protocol(proto_amp); |
799 | 16 | expert_register_field_array(expert_amp, ei, array_length(ei)); |
800 | | |
801 | 16 | amp_handle = register_dissector("amp", dissect_amp, proto_amp); |
802 | 16 | } |
803 | | |
804 | | void |
805 | | proto_reg_handoff_amp(void) |
806 | 16 | { |
807 | 16 | dissector_add_uint("ccsds.apid", AMP_APID, amp_handle); |
808 | 16 | dissector_add_for_decode_as_with_preference("udp.port", amp_handle); |
809 | 16 | } |
810 | | |
811 | | /* |
812 | | * Editor modelines - http://www.wireshark.org/tools/modelines.html |
813 | | * |
814 | | * Local variables: |
815 | | * c-basic-offset: 4 |
816 | | * tab-width: 8 |
817 | | * indent-tabs-mode: nil |
818 | | * End: |
819 | | * |
820 | | * vi: set shiftwidth=4 tabstop=8 expandtab: |
821 | | * :indentSize=4:tabSize=8:noTabs=true: |
822 | | */ |