/src/wireshark/epan/dissectors/packet-ccsds-sp.c
Line | Count | Source |
1 | | /* packet-ccsds-sp.c |
2 | | * Routines for CCSDS Space Packet dissection |
3 | | * Copyright 2000, Scott Hovis scott.hovis@ums.msfc.nasa.gov |
4 | | * Enhanced 2008, Matt Dunkle Matthew.L.Dunkle@nasa.gov |
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 | | #include "config.h" |
14 | | |
15 | | #include <epan/packet.h> |
16 | | #include <epan/expert.h> |
17 | | #include <epan/prefs.h> |
18 | | #include <epan/to_str.h> |
19 | | |
20 | | void proto_register_ccsds(void); |
21 | | void proto_reg_handoff_ccsds(void); |
22 | | |
23 | | /* |
24 | | * See |
25 | | * |
26 | | * https://ccsds.org/Pubs/133x0b1s.pdf Space Packet Protocol section 4.1 -- CCSDS 133.0-B-1 replaces CCSDS 701.0-B-2 |
27 | | * https://ccsds.org/Pubs/133x0b2e2.pdf Space Packet Protocol section 4.1 -- CCSDS 133.0-B-2, replaces CCSDS 133.0-B-1 |
28 | | * http://everyspec.com/NASA/NASA-JSC/NASA-SSP-PUBS/download.php?spec=SSP_52050E.003096.pdf section 3.1.3 |
29 | | * |
30 | | * for some information. |
31 | | * |
32 | | * See |
33 | | * |
34 | | * https://arc.aiaa.org/doi/pdf/10.2514/6.2002-T5-11 |
35 | | * |
36 | | * for a discussion of a restructuring of the CCSDS protocol definitions |
37 | | * that combined: |
38 | | * |
39 | | * * the Source Packet from CCSDS 102.0 - https://ccsds.org/Pubs/102x0b5s.pdf; |
40 | | * * the CCSDS Packet from CCSDS 701.0 - https://ccsds.org/Pubs/701x0b3s.pdf; |
41 | | * * the TC Packet from CCSDS 203.0 - https://ccsds.org/Pubs/203x0b2s.pdf; |
42 | | * |
43 | | * all of which had the same format, into the Space Packet. |
44 | | */ |
45 | | |
46 | | |
47 | | /* Initialize the protocol and registered fields */ |
48 | | static int proto_ccsds; |
49 | | |
50 | | /* primary header */ |
51 | | static int hf_ccsds_header_flags; |
52 | | static int hf_ccsds_apid; |
53 | | static int hf_ccsds_version; |
54 | | static int hf_ccsds_secheader; |
55 | | static int hf_ccsds_type; |
56 | | static int hf_ccsds_seqnum; |
57 | | static int hf_ccsds_seqflag; |
58 | | static int hf_ccsds_length; |
59 | | |
60 | | /* common secondary header */ |
61 | | static int hf_ccsds_coarse_time; |
62 | | static int hf_ccsds_fine_time; |
63 | | static int hf_ccsds_timeid; |
64 | | static int hf_ccsds_checkword_flag; |
65 | | |
66 | | /* payload specific secondary header */ |
67 | | static int hf_ccsds_zoe; |
68 | | static int hf_ccsds_packet_type_unused; |
69 | | static int hf_ccsds_vid; |
70 | | static int hf_ccsds_dcc; |
71 | | |
72 | | /* core specific secondary header */ |
73 | | /* static int hf_ccsds_spare1; */ |
74 | | static int hf_ccsds_packet_type; |
75 | | /* static int hf_ccsds_spare2; */ |
76 | | static int hf_ccsds_element_id; |
77 | | static int hf_ccsds_cmd_data_packet; |
78 | | static int hf_ccsds_format_version_id; |
79 | | static int hf_ccsds_extended_format_id; |
80 | | /* static int hf_ccsds_spare3; */ |
81 | | static int hf_ccsds_frame_id; |
82 | | static int hf_ccsds_embedded_time; |
83 | | static int hf_ccsds_user_data; |
84 | | |
85 | | /* Checkword (checksum) */ |
86 | | static int hf_ccsds_checkword; |
87 | | static int hf_ccsds_checkword_good; |
88 | | static int hf_ccsds_checkword_bad; |
89 | | |
90 | | /* Initialize the subtree pointers */ |
91 | | static int ett_ccsds_primary_header_flags; |
92 | | static int ett_ccsds; |
93 | | static int ett_ccsds_primary_header; |
94 | | static int ett_ccsds_secondary_header; |
95 | | static int ett_ccsds_checkword; |
96 | | |
97 | | static expert_field ei_ccsds_length_error; |
98 | | static expert_field ei_ccsds_checkword; |
99 | | |
100 | | static dissector_handle_t ccsds_handle; |
101 | | /* Dissector table */ |
102 | | static dissector_table_t ccsds_dissector_table; |
103 | | |
104 | | static const enum_val_t dissect_checkword[] = { |
105 | | { "hdr", "Use header flag", 2 }, |
106 | | { "no", "Override header flag to be false", 0 }, |
107 | | { "yes", "Override header flag to be true", 1 }, |
108 | | { NULL, NULL, 0 } |
109 | | }; |
110 | | |
111 | | /* Global preferences */ |
112 | | /* As defined above, default is to use header flag */ |
113 | | static int global_dissect_checkword = 2; |
114 | | |
115 | | /* |
116 | | * Bits in the first 16-bit header word |
117 | | */ |
118 | 16 | #define HDR_VERSION 0xe000 |
119 | 16 | #define HDR_TYPE 0x1000 |
120 | 16 | #define HDR_SECHDR 0x0800 |
121 | 16 | #define HDR_APID 0x07ff |
122 | | |
123 | | /* some basic sizing parameters */ |
124 | | enum |
125 | | { |
126 | | CCSDS_PRIMARY_HEADER_LENGTH = 6, |
127 | | CCSDS_SECONDARY_HEADER_LENGTH = 10 |
128 | | }; |
129 | | |
130 | | /* leap year macro */ |
131 | | #ifndef Leap |
132 | 0 | # define Leap(yr) ( ( 0 == (yr)%4 && 0 != (yr)%100 ) || ( 0 == (yr)%400 ) ) |
133 | | #endif |
134 | | |
135 | | |
136 | | static const value_string ccsds_primary_header_sequence_flags[] = { |
137 | | { 0, "Continuation segment" }, |
138 | | { 1, "First segment" }, |
139 | | { 2, "Last segment" }, |
140 | | { 3, "Unsegmented data" }, |
141 | | { 0, NULL } |
142 | | }; |
143 | | |
144 | | static const value_string ccsds_secondary_header_type[] = { |
145 | | { 0, "Core" }, |
146 | | { 1, "Payload" }, |
147 | | { 0, NULL } |
148 | | }; |
149 | | |
150 | | static const value_string ccsds_secondary_header_packet_type[] = { |
151 | | { 0, "UNDEFINED" }, |
152 | | { 1, "Data Dump" }, |
153 | | { 2, "UNDEFINED" }, |
154 | | { 3, "UNDEFINED" }, |
155 | | { 4, "TLM/Status" }, |
156 | | { 5, "UNDEFINED" }, |
157 | | { 6, "Payload Private/Science" }, |
158 | | { 7, "Ancillary Data" }, |
159 | | { 8, "Essential Cmd" }, |
160 | | { 9, "System Cmd" }, |
161 | | { 10, "Payload Cmd" }, |
162 | | { 11, "Data Load/File Transfer" }, |
163 | | { 12, "UNDEFINED" }, |
164 | | { 13, "UNDEFINED" }, |
165 | | { 14, "UNDEFINED" }, |
166 | | { 15, "UNDEFINED" }, |
167 | | { 0, NULL } |
168 | | }; |
169 | | |
170 | | static const value_string ccsds_secondary_header_element_id[] = { |
171 | | { 0, "NASA (Ground Test Only)" }, |
172 | | { 1, "NASA" }, |
173 | | { 2, "ESA/APM" }, |
174 | | { 3, "NASDA" }, |
175 | | { 4, "RSA" }, |
176 | | { 5, "CSA" }, |
177 | | { 6, "ESA/ATV" }, |
178 | | { 7, "ASI" }, |
179 | | { 8, "ESA/ERA" }, |
180 | | { 9, "Reserved" }, |
181 | | { 10, "RSA SPP" }, |
182 | | { 11, "NASDA HTV" }, |
183 | | { 12, "Reserved" }, |
184 | | { 13, "Reserved" }, |
185 | | { 14, "Reserved" }, |
186 | | { 15, "Reserved" }, |
187 | | { 0, NULL } |
188 | | }; |
189 | | |
190 | | static const value_string ccsds_secondary_header_cmd_data_packet[] = { |
191 | | { 0, "Command Packet" }, |
192 | | { 1, "Data Packet" }, |
193 | | { 0, NULL } |
194 | | }; |
195 | | |
196 | | static const value_string ccsds_secondary_header_format_id[] = { |
197 | | { 0, "Reserved" }, |
198 | | { 1, "Essential Telemetry" }, |
199 | | { 2, "Housekeeping Tlm - 1" }, |
200 | | { 3, "Housekeeping Tlm - 2" }, |
201 | | { 4, "PCS DDT" }, |
202 | | { 5, "CCS S-Band Command Response" }, |
203 | | { 6, "Contingency Telemetry via the SMCC" }, |
204 | | { 7, "Normal Data Dump" }, |
205 | | { 8, "Extended Data Dump" }, |
206 | | { 9, "Reserved" }, |
207 | | { 10, "Reserved" }, |
208 | | { 11, "Broadcast Ancillary Data" }, |
209 | | { 12, "Reserved" }, |
210 | | { 13, "NCS to OIU Telemetry and ECOMM Telemetry" }, |
211 | | { 14, "CCS to OIU Telemetry - Direct" }, |
212 | | { 15, "Reserved" }, |
213 | | { 16, "Normal File Dump" }, |
214 | | { 17, "Extended File Dump" }, |
215 | | { 18, "NCS to FGB Telemetry" }, |
216 | | { 19, "Reserved" }, |
217 | | { 20, "ZOE Normal Dump (S-Band)" }, |
218 | | { 21, "ZOE Extended Dump (S-Band)" }, |
219 | | { 22, "EMU S-Band TLM Packet" }, |
220 | | { 23, "Reserved" }, |
221 | | { 24, "Reserved" }, |
222 | | { 25, "Reserved" }, |
223 | | { 26, "CCS to OIU Telemetry via UHF" }, |
224 | | { 27, "OSTP Telemetry (After Flight 1E, CCS R5)" }, |
225 | | { 28, "Reserved" }, |
226 | | { 29, "Reserved" }, |
227 | | { 30, "Reserved" }, |
228 | | { 31, "Reserved" }, |
229 | | { 32, "Reserved" }, |
230 | | { 33, "Reserved" }, |
231 | | { 34, "Reserved" }, |
232 | | { 35, "Reserved" }, |
233 | | { 36, "Reserved" }, |
234 | | { 37, "Reserved" }, |
235 | | { 38, "Reserved" }, |
236 | | { 39, "Reserved" }, |
237 | | { 40, "Reserved" }, |
238 | | { 41, "Reserved" }, |
239 | | { 42, "Reserved" }, |
240 | | { 43, "Reserved" }, |
241 | | { 44, "Reserved" }, |
242 | | { 45, "Reserved" }, |
243 | | { 46, "Reserved" }, |
244 | | { 47, "Reserved" }, |
245 | | { 48, "Reserved" }, |
246 | | { 49, "Reserved" }, |
247 | | { 50, "Reserved" }, |
248 | | { 51, "Reserved" }, |
249 | | { 52, "Reserved" }, |
250 | | { 53, "Reserved" }, |
251 | | { 54, "Reserved" }, |
252 | | { 55, "Reserved" }, |
253 | | { 56, "Reserved" }, |
254 | | { 57, "Reserved" }, |
255 | | { 58, "Reserved" }, |
256 | | { 59, "Reserved" }, |
257 | | { 60, "Reserved" }, |
258 | | { 61, "Reserved" }, |
259 | | { 62, "Reserved" }, |
260 | | { 63, "Reserved" }, |
261 | | { 0, NULL } |
262 | | }; |
263 | | static value_string_ext ccsds_secondary_header_format_id_ext = VALUE_STRING_EXT_INIT(ccsds_secondary_header_format_id); |
264 | | |
265 | | |
266 | | |
267 | | /* convert CCSDS embedded time to a human readable string - NOT THREAD SAFE */ |
268 | | static const char* embedded_time_to_string ( wmem_allocator_t *pool, int coarse_time, int fine_time ) |
269 | 0 | { |
270 | 0 | static int utcdiff = 0; |
271 | 0 | nstime_t t; |
272 | 0 | int yr; |
273 | 0 | int fraction; |
274 | 0 | int multiplier = 1000; |
275 | | |
276 | | /* compute the static constant difference in seconds |
277 | | * between midnight 5-6 January 1980 (GPS time) and |
278 | | * seconds since 1/1/1970 (UTC time) just this once |
279 | | */ |
280 | 0 | if ( 0 == utcdiff ) |
281 | 0 | { |
282 | 0 | for ( yr = 1970; yr < 1980; ++yr ) |
283 | 0 | { |
284 | 0 | utcdiff += ( Leap(yr) ? 366 : 365 ) * 24 * 60 * 60; |
285 | 0 | } |
286 | |
|
287 | 0 | utcdiff += 5 * 24 * 60 * 60; /* five days of January 1980 */ |
288 | 0 | } |
289 | |
|
290 | 0 | t.secs = coarse_time + utcdiff; |
291 | 0 | fraction = ( multiplier * ( (int)fine_time & 0xff ) ) / 256; |
292 | 0 | t.nsecs = fraction*1000000; /* msecs to nsecs */ |
293 | |
|
294 | 0 | return abs_time_to_str(pool, &t, ABSOLUTE_TIME_DOY_UTC, true); |
295 | 0 | } |
296 | | |
297 | | |
298 | | /* |
299 | | * Dissect CCSDS Space Packet. |
300 | | */ |
301 | | static int |
302 | | dissect_ccsds(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_) |
303 | 0 | { |
304 | 0 | int offset = 0; |
305 | 0 | proto_item *ccsds_packet; |
306 | 0 | proto_tree *ccsds_tree; |
307 | 0 | proto_item *primary_header; |
308 | 0 | proto_tree *primary_header_tree; |
309 | 0 | uint16_t first_word; |
310 | 0 | uint32_t coarse_time; |
311 | 0 | uint8_t fine_time; |
312 | 0 | proto_item *secondary_header; |
313 | 0 | proto_tree *secondary_header_tree; |
314 | 0 | const char *time_string; |
315 | 0 | int ccsds_length; |
316 | 0 | int length = 0; |
317 | 0 | int reported_length; |
318 | 0 | uint8_t checkword_flag = 0; |
319 | 0 | int counter = 0; |
320 | 0 | proto_item *item, *checkword_item = NULL; |
321 | 0 | proto_tree *checkword_tree; |
322 | 0 | uint16_t checkword_field = 0; |
323 | 0 | uint16_t checkword_sum = 0; |
324 | 0 | tvbuff_t *next_tvb; |
325 | 0 | static int * const header_flags[] = { |
326 | 0 | &hf_ccsds_version, |
327 | 0 | &hf_ccsds_type, |
328 | 0 | &hf_ccsds_secheader, |
329 | 0 | &hf_ccsds_apid, |
330 | 0 | NULL |
331 | 0 | }; |
332 | |
|
333 | 0 | col_set_str(pinfo->cinfo, COL_PROTOCOL, "CCSDS SP"); |
334 | 0 | col_set_str(pinfo->cinfo, COL_INFO, "CCSDS Space Packet"); |
335 | |
|
336 | 0 | first_word = tvb_get_ntohs(tvb, 0); |
337 | 0 | col_add_fstr(pinfo->cinfo, COL_INFO, "APID %4d (0x%03X)", first_word&HDR_APID, first_word&HDR_APID); |
338 | |
|
339 | 0 | reported_length = tvb_reported_length_remaining(tvb, 0); |
340 | 0 | ccsds_length = tvb_get_ntohs(tvb, 4) + CCSDS_PRIMARY_HEADER_LENGTH + 1; |
341 | | |
342 | | |
343 | | /* Min length is size of headers, whereas max length is reported length. |
344 | | * If the length field in the CCSDS header is outside of these bounds, |
345 | | * use the value it violates. Otherwise, use the length field value. |
346 | | */ |
347 | 0 | if (ccsds_length > reported_length) |
348 | 0 | length = reported_length; |
349 | 0 | else if (ccsds_length < CCSDS_PRIMARY_HEADER_LENGTH + CCSDS_SECONDARY_HEADER_LENGTH) |
350 | 0 | length = CCSDS_PRIMARY_HEADER_LENGTH + CCSDS_SECONDARY_HEADER_LENGTH; |
351 | 0 | else |
352 | 0 | length = ccsds_length; |
353 | |
|
354 | 0 | ccsds_packet = proto_tree_add_item(tree, proto_ccsds, tvb, 0, length, ENC_NA); |
355 | 0 | ccsds_tree = proto_item_add_subtree(ccsds_packet, ett_ccsds); |
356 | | |
357 | | /* build the primary header tree */ |
358 | 0 | primary_header_tree = proto_tree_add_subtree(ccsds_tree, tvb, offset, CCSDS_PRIMARY_HEADER_LENGTH, |
359 | 0 | ett_ccsds_primary_header, &primary_header, "Primary Header"); |
360 | |
|
361 | 0 | proto_tree_add_bitmask(primary_header_tree, tvb, offset, hf_ccsds_header_flags, |
362 | 0 | ett_ccsds_primary_header_flags, header_flags, ENC_BIG_ENDIAN); |
363 | 0 | offset += 2; |
364 | |
|
365 | 0 | proto_tree_add_item(primary_header_tree, hf_ccsds_seqflag, tvb, offset, 2, ENC_BIG_ENDIAN); |
366 | 0 | proto_tree_add_item(primary_header_tree, hf_ccsds_seqnum, tvb, offset, 2, ENC_BIG_ENDIAN); |
367 | 0 | offset += 2; |
368 | |
|
369 | 0 | item = proto_tree_add_item(primary_header_tree, hf_ccsds_length, tvb, offset, 2, ENC_BIG_ENDIAN); |
370 | |
|
371 | 0 | if (ccsds_length > reported_length) { |
372 | 0 | expert_add_info(pinfo, item, &ei_ccsds_length_error); |
373 | 0 | } |
374 | |
|
375 | 0 | offset += 2; |
376 | 0 | proto_item_set_end(primary_header, tvb, offset); |
377 | | |
378 | | /* |
379 | | * Build the secondary header tree. |
380 | | * |
381 | | * 133.0 says this consists of: |
382 | | * |
383 | | * a time code field that "shall consist of one of the CCSDS |
384 | | * segmented binary or unsegmented binary time codes specified |
385 | | * in reference [3].", reference [3] being "Time Code Formats |
386 | | * . Issue 4. Recommendation for Space Data System Standards (Blue |
387 | | * Book), CCSDS 301.0-B-4. Washington, D.C.: CCSDS, November 2010"; |
388 | | * |
389 | | * followed by an ancillary data field not specified by 133.0. |
390 | | * |
391 | | * 102.0 says this consists of: |
392 | | * |
393 | | * "a PACKET SECONDARY HEADER DATA FIELD; |
394 | | * or a PACKET SECONDARY HEADER TIME CODE FIELD; |
395 | | * or a PACKET SECONDARY HEADER TIME CODE FIELD followed by a |
396 | | * PACKET SECONDARY HEADER DATA FIELD." |
397 | | * |
398 | | * where "a PACKET SECONDARY HEADER DATA FIELD" isn't defined by 102.0 |
399 | | * and "a PACKET SECONDARY HEADER TIME CODE FIELD" "shall consist of |
400 | | * one of the CCSDS segmented binary or unsegmented binary time codes |
401 | | * specified in Reference [3]", reference [3] being "Time Code Formats |
402 | | * Recommendation for Space Data Systems Standards, CCSDS |
403 | | * 301.0-B-2. Blue Book. Issue 2. Washington, D.C.: CCSDS, April 1990. |
404 | | * |
405 | | * 701.0 says this is "specified by the source end user" and gives, |
406 | | * in Annex A, a secondary header for time stamping, which "shall |
407 | | * consist of: |
408 | | * |
409 | | * a Time Code Field only; or |
410 | | * an Ancillary Data Field only; or |
411 | | * a Time Code Field followed by an Ancillary Data Field" |
412 | | * |
413 | | * wbere "it is preferred that the Packet Secondary Header Time Code Field |
414 | | * should contain one of the CCSDS segmented binary or unsegmented binary |
415 | | * time codes specified in Reference [20]", where reference [20] is |
416 | | * "'Time Code Formats', Recommendation for Space Data Systems Standards, |
417 | | * CCSDS 301.0-B-2, Blue Book, Issue 2 (Washington, D.C.: CCSDS, April |
418 | | * 1990 or later issue)." |
419 | | * |
420 | | * 203.0 says: |
421 | | * |
422 | | * "The purpose of the Secondary Header is to provide a means for |
423 | | * encoding, within a TC Packet, any ancillary data (time, sending |
424 | | * address, data field format, etc.) which may be necessary for the |
425 | | * interpretation of the application data contained within the packet. |
426 | | * |
427 | | * If a Secondary Header is present, this shall be indicated by |
428 | | * setting the Secondary Header Flag to value "1". At present, the |
429 | | * CCSDS has not developed recommendations for the internal format |
430 | | * of the Secondary Header: therefore, local conventions may be used as |
431 | | * required. However, in order to permit future standardization of |
432 | | * this data structure the most-significant bit (Bit 0) of the |
433 | | * leading octet within the Secondary Header shall be reserved and |
434 | | * assigned as follows whenever the Secondary Header Flag is set to |
435 | | * value "1": |
436 | | * |
437 | | * Bit 0 = "0" : non-CCSDS-defined Secondary Header follows |
438 | | * Bit 0 = "1" : CCSDS-defined Secondary Header follows" |
439 | | * |
440 | | */ |
441 | 0 | if ( first_word & HDR_SECHDR ) |
442 | 0 | { |
443 | 0 | secondary_header_tree = proto_tree_add_subtree(ccsds_tree, tvb, offset, CCSDS_SECONDARY_HEADER_LENGTH, |
444 | 0 | ett_ccsds_secondary_header, &secondary_header, "CCSDS Space Packet Secondary Header"); |
445 | | |
446 | | /* command secondary header flags */ |
447 | 0 | proto_tree_add_item_ret_uint(secondary_header_tree, hf_ccsds_coarse_time, tvb, offset, 4, ENC_BIG_ENDIAN, &coarse_time); |
448 | 0 | offset += 4; |
449 | |
|
450 | 0 | proto_tree_add_item_ret_uint8(secondary_header_tree, hf_ccsds_fine_time, tvb, offset, 1, ENC_BIG_ENDIAN, &fine_time); |
451 | 0 | ++offset; |
452 | |
|
453 | 0 | time_string = embedded_time_to_string ( pinfo->pool, coarse_time, fine_time ); |
454 | 0 | proto_tree_add_string(secondary_header_tree, hf_ccsds_embedded_time, tvb, offset-5, 5, time_string); |
455 | |
|
456 | 0 | proto_tree_add_item(secondary_header_tree, hf_ccsds_timeid, tvb, offset, 1, ENC_BIG_ENDIAN); |
457 | 0 | checkword_item = proto_tree_add_item(secondary_header_tree, hf_ccsds_checkword_flag, tvb, offset, 1, ENC_BIG_ENDIAN); |
458 | | |
459 | | /* Global Preference: how to handle checkword flag */ |
460 | 0 | switch (global_dissect_checkword) { |
461 | 0 | case 0: |
462 | | /* force checkword presence flag to be false */ |
463 | 0 | checkword_flag = 0; |
464 | 0 | break; |
465 | 0 | case 1: |
466 | | /* force checkword presence flag to be true */ |
467 | 0 | checkword_flag = 1; |
468 | 0 | break; |
469 | 0 | default: |
470 | | /* use value of checkword presence flag from header */ |
471 | 0 | checkword_flag = (tvb_get_uint8(tvb, offset)&0x20) >> 5; |
472 | 0 | break; |
473 | 0 | } |
474 | | |
475 | | /* payload specific secondary header flags */ |
476 | 0 | if ( first_word & HDR_TYPE ) |
477 | 0 | { |
478 | 0 | proto_tree_add_item(secondary_header_tree, hf_ccsds_zoe, tvb, offset, 1, ENC_BIG_ENDIAN); |
479 | 0 | proto_tree_add_item(secondary_header_tree, hf_ccsds_packet_type_unused, tvb, offset, 1, ENC_BIG_ENDIAN); |
480 | 0 | ++offset; |
481 | |
|
482 | 0 | proto_tree_add_item(secondary_header_tree, hf_ccsds_vid, tvb, offset, 2, ENC_BIG_ENDIAN); |
483 | 0 | offset += 2; |
484 | |
|
485 | 0 | proto_tree_add_item(secondary_header_tree, hf_ccsds_dcc, tvb, offset, 2, ENC_BIG_ENDIAN); |
486 | 0 | offset += 2; |
487 | 0 | } |
488 | | |
489 | | /* core specific secondary header flags */ |
490 | 0 | else |
491 | 0 | { |
492 | | /* proto_tree_add_item(secondary_header_tree, hf_ccsds_spare1, tvb, offset, 1, ENC_BIG_ENDIAN); */ |
493 | 0 | proto_tree_add_item(secondary_header_tree, hf_ccsds_packet_type, tvb, offset, 1, ENC_BIG_ENDIAN); |
494 | 0 | ++offset; |
495 | | |
496 | | /* proto_tree_add_item(secondary_header_tree, hf_ccsds_spare2, tvb, offset, 2, ENC_BIG_ENDIAN); */ |
497 | 0 | proto_tree_add_item(secondary_header_tree, hf_ccsds_element_id, tvb, offset, 2, ENC_BIG_ENDIAN); |
498 | 0 | proto_tree_add_item(secondary_header_tree, hf_ccsds_cmd_data_packet, tvb, offset, 2, ENC_BIG_ENDIAN); |
499 | 0 | proto_tree_add_item(secondary_header_tree, hf_ccsds_format_version_id, tvb, offset, 2, ENC_BIG_ENDIAN); |
500 | 0 | proto_tree_add_item(secondary_header_tree, hf_ccsds_extended_format_id, tvb, offset, 2, ENC_BIG_ENDIAN); |
501 | 0 | offset += 2; |
502 | | |
503 | | /* proto_tree_add_item(secondary_header_tree, hf_ccsds_spare3, tvb, offset, 1, ENC_BIG_ENDIAN); */ |
504 | 0 | ++offset; |
505 | |
|
506 | 0 | proto_tree_add_item(secondary_header_tree, hf_ccsds_frame_id, tvb, offset, 1, ENC_BIG_ENDIAN); |
507 | 0 | ++offset; |
508 | 0 | } |
509 | | |
510 | | /* finish the secondary header */ |
511 | 0 | proto_item_set_end(secondary_header, tvb, offset); |
512 | 0 | } |
513 | | |
514 | | /* If there wasn't a full packet, then don't allow a tree item for checkword. */ |
515 | 0 | if (reported_length < ccsds_length || ccsds_length < CCSDS_PRIMARY_HEADER_LENGTH + CCSDS_SECONDARY_HEADER_LENGTH) { |
516 | | /* Label payload 'User Data' */ |
517 | 0 | if (length > offset) |
518 | 0 | proto_tree_add_item(ccsds_tree, hf_ccsds_user_data, tvb, offset, length-offset, ENC_NA); |
519 | 0 | offset += length-offset; |
520 | 0 | if (checkword_flag == 1) |
521 | 0 | expert_add_info(pinfo, checkword_item, &ei_ccsds_checkword); |
522 | 0 | } |
523 | | /* Handle checkword according to preference setting. */ |
524 | 0 | else { |
525 | 0 | next_tvb = tvb_new_subset_remaining(tvb, offset); |
526 | | /* Look for a subdissector for the payload */ |
527 | 0 | if (!dissector_try_uint(ccsds_dissector_table, first_word&HDR_APID, next_tvb, pinfo, tree)) { |
528 | | /* If no subdissector is found, label the payload as 'User Data' */ |
529 | 0 | proto_tree_add_item(ccsds_tree, hf_ccsds_user_data, tvb, offset, length-offset-2*checkword_flag, ENC_NA); |
530 | 0 | } |
531 | 0 | offset += length-offset-2*checkword_flag; |
532 | | |
533 | | /* If checkword is present, calculate packet checksum (16-bit running sum) for comparison */ |
534 | 0 | if (checkword_flag == 1) { |
535 | | /* don't count the checkword as part of the checksum */ |
536 | 0 | while (counter < ccsds_length-2) { |
537 | 0 | checkword_sum += tvb_get_ntohs(tvb, counter); |
538 | 0 | counter += 2; |
539 | 0 | } |
540 | 0 | checkword_field = tvb_get_ntohs(tvb, offset); |
541 | | |
542 | | /* Report checkword status */ |
543 | 0 | if (checkword_sum == checkword_field) { |
544 | 0 | item = proto_tree_add_uint_format_value(ccsds_tree, hf_ccsds_checkword, tvb, offset, 2, checkword_field, |
545 | 0 | "0x%04x [correct]", checkword_field); |
546 | 0 | checkword_tree = proto_item_add_subtree(item, ett_ccsds_checkword); |
547 | 0 | item = proto_tree_add_boolean(checkword_tree, hf_ccsds_checkword_good, tvb, offset, 2, true); |
548 | 0 | proto_item_set_generated(item); |
549 | 0 | item = proto_tree_add_boolean(checkword_tree, hf_ccsds_checkword_bad, tvb, offset, 2, false); |
550 | 0 | proto_item_set_generated(item); |
551 | 0 | } else { |
552 | 0 | item = proto_tree_add_uint_format_value(ccsds_tree, hf_ccsds_checkword, tvb, offset, 2, checkword_field, |
553 | 0 | "0x%04x [incorrect, should be 0x%04x]", checkword_field, checkword_sum); |
554 | 0 | checkword_tree = proto_item_add_subtree(item, ett_ccsds_checkword); |
555 | 0 | item = proto_tree_add_boolean(checkword_tree, hf_ccsds_checkword_good, tvb, offset, 2, false); |
556 | 0 | proto_item_set_generated(item); |
557 | 0 | item = proto_tree_add_boolean(checkword_tree, hf_ccsds_checkword_bad, tvb, offset, 2, true); |
558 | 0 | proto_item_set_generated(item); |
559 | 0 | } |
560 | 0 | offset += 2; |
561 | 0 | } |
562 | 0 | } |
563 | | |
564 | | /* Give the data dissector any bytes past the packet length */ |
565 | 0 | call_data_dissector(tvb_new_subset_remaining(tvb, offset), pinfo, tree); |
566 | 0 | return tvb_captured_length(tvb); |
567 | 0 | } |
568 | | |
569 | | |
570 | | void |
571 | | proto_register_ccsds(void) |
572 | 16 | { |
573 | 16 | static hf_register_info hf[] = { |
574 | | |
575 | | /* primary header flags */ |
576 | 16 | { &hf_ccsds_header_flags, |
577 | 16 | { "Header Flags", "ccsds.header_flags", |
578 | 16 | FT_UINT16, BASE_HEX, NULL, 0x0, |
579 | 16 | NULL, HFILL } |
580 | 16 | }, |
581 | 16 | { &hf_ccsds_version, |
582 | 16 | { "Version", "ccsds.version", |
583 | 16 | FT_UINT16, BASE_DEC, NULL, HDR_VERSION, |
584 | 16 | NULL, HFILL } |
585 | 16 | }, |
586 | 16 | { &hf_ccsds_type, |
587 | 16 | { "Type", "ccsds.type", |
588 | 16 | FT_UINT16, BASE_DEC, VALS(ccsds_secondary_header_type), HDR_TYPE, |
589 | 16 | NULL, HFILL } |
590 | 16 | }, |
591 | 16 | { &hf_ccsds_secheader, |
592 | 16 | { "Secondary Header", "ccsds.secheader", |
593 | 16 | FT_BOOLEAN, 16, NULL, HDR_SECHDR, |
594 | 16 | "Secondary Header Present", HFILL } |
595 | 16 | }, |
596 | 16 | { &hf_ccsds_apid, |
597 | 16 | { "APID", "ccsds.apid", |
598 | 16 | FT_UINT16, BASE_DEC, NULL, HDR_APID, |
599 | 16 | NULL, HFILL } |
600 | 16 | }, |
601 | 16 | { &hf_ccsds_seqflag, |
602 | 16 | { "Sequence Flags", "ccsds.seqflag", |
603 | 16 | FT_UINT16, BASE_DEC, VALS(ccsds_primary_header_sequence_flags), 0xc000, |
604 | 16 | NULL, HFILL } |
605 | 16 | }, |
606 | 16 | { &hf_ccsds_seqnum, |
607 | 16 | { "Sequence Number", "ccsds.seqnum", |
608 | 16 | FT_UINT16, BASE_DEC, NULL, 0x3fff, |
609 | 16 | NULL, HFILL } |
610 | 16 | }, |
611 | 16 | { &hf_ccsds_length, |
612 | 16 | { "Packet Length", "ccsds.length", |
613 | 16 | FT_UINT16, BASE_DEC, NULL, 0x0, |
614 | 16 | NULL, HFILL } |
615 | 16 | }, |
616 | | |
617 | | |
618 | | /* common secondary header flags */ |
619 | 16 | { &hf_ccsds_coarse_time, |
620 | 16 | { "Coarse Time", "ccsds.coarse_time", |
621 | 16 | FT_UINT32, BASE_DEC, NULL, 0x0, |
622 | 16 | NULL, HFILL } |
623 | 16 | }, |
624 | 16 | { &hf_ccsds_fine_time, |
625 | 16 | { "Fine Time", "ccsds.fine_time", |
626 | 16 | FT_UINT8, BASE_DEC, NULL, 0x0, |
627 | 16 | NULL, HFILL } |
628 | 16 | }, |
629 | 16 | { &hf_ccsds_timeid, |
630 | 16 | { "Time Identifier", "ccsds.timeid", |
631 | 16 | FT_UINT8, BASE_DEC, NULL, 0xc0, |
632 | 16 | NULL, HFILL } |
633 | 16 | }, |
634 | 16 | { &hf_ccsds_checkword_flag, |
635 | 16 | { "Checkword Indicator", "ccsds.checkword_flag", |
636 | 16 | FT_BOOLEAN, 8, NULL, 0x20, |
637 | 16 | "Checkword present", HFILL } |
638 | 16 | }, |
639 | | |
640 | | |
641 | | /* payload specific secondary header flags */ |
642 | 16 | { &hf_ccsds_zoe, |
643 | 16 | { "ZOE TLM", "ccsds.zoe", |
644 | 16 | FT_UINT8, BASE_DEC, NULL, 0x10, |
645 | 16 | "Contains S-band ZOE Packets", HFILL } |
646 | 16 | }, |
647 | 16 | { &hf_ccsds_packet_type_unused, |
648 | 16 | { "Packet Type (unused for Ku-band)", "ccsds.packet_type", |
649 | 16 | FT_UINT8, BASE_DEC, NULL, 0x0f, |
650 | 16 | NULL, HFILL } |
651 | 16 | }, |
652 | 16 | { &hf_ccsds_vid, |
653 | 16 | { "Version Identifier", "ccsds.vid", |
654 | 16 | FT_UINT16, BASE_DEC, NULL, 0x0, |
655 | 16 | NULL, HFILL } |
656 | 16 | }, |
657 | 16 | { &hf_ccsds_dcc, |
658 | 16 | { "Data Cycle Counter", "ccsds.dcc", |
659 | 16 | FT_UINT16, BASE_DEC, NULL, 0x0, |
660 | 16 | NULL, HFILL } |
661 | 16 | }, |
662 | | |
663 | | |
664 | | /* core specific secondary header flags */ |
665 | | #if 0 |
666 | | { &hf_ccsds_spare1, |
667 | | { "Spare Bit 1", "ccsds.spare1", |
668 | | FT_UINT8, BASE_DEC, NULL, 0x10, |
669 | | "unused spare bit 1", HFILL } |
670 | | }, |
671 | | #endif |
672 | 16 | { &hf_ccsds_packet_type, |
673 | 16 | { "Packet Type", "ccsds.packet_type", |
674 | 16 | FT_UINT8, BASE_DEC, VALS(ccsds_secondary_header_packet_type), 0x0f, |
675 | 16 | NULL, HFILL } |
676 | 16 | }, |
677 | | #if 0 |
678 | | { &hf_ccsds_spare2, |
679 | | { "Spare Bit 2", "ccsds.spare2", |
680 | | FT_UINT16, BASE_DEC, NULL, 0x8000, |
681 | | NULL, HFILL } |
682 | | }, |
683 | | #endif |
684 | 16 | { &hf_ccsds_element_id, |
685 | 16 | { "Element ID", "ccsds.element_id", |
686 | 16 | FT_UINT16, BASE_DEC, VALS(ccsds_secondary_header_element_id), 0x7800, |
687 | 16 | NULL, HFILL } |
688 | 16 | }, |
689 | 16 | { &hf_ccsds_cmd_data_packet, |
690 | 16 | { "Cmd/Data Packet Indicator", "ccsds.cmd_data_packet", |
691 | 16 | FT_UINT16, BASE_DEC, VALS(ccsds_secondary_header_cmd_data_packet), 0x0400, |
692 | 16 | NULL, HFILL } |
693 | 16 | }, |
694 | 16 | { &hf_ccsds_format_version_id, |
695 | 16 | { "Format Version ID", "ccsds.format_version_id", |
696 | 16 | FT_UINT16, BASE_DEC, NULL, 0x03c0, |
697 | 16 | NULL, HFILL } |
698 | 16 | }, |
699 | 16 | { &hf_ccsds_extended_format_id, |
700 | 16 | { "Extended Format ID", "ccsds.extended_format_id", |
701 | 16 | FT_UINT16, BASE_DEC | BASE_EXT_STRING, &ccsds_secondary_header_format_id_ext, 0x003f, |
702 | 16 | NULL, HFILL } |
703 | 16 | }, |
704 | | #if 0 |
705 | | { &hf_ccsds_spare3, |
706 | | { "Spare Bits 3", "ccsds.spare3", |
707 | | FT_UINT8, BASE_DEC, NULL, 0xff, |
708 | | NULL, HFILL } |
709 | | }, |
710 | | #endif |
711 | 16 | { &hf_ccsds_frame_id, |
712 | 16 | { "Frame ID", "ccsds.frame_id", |
713 | 16 | FT_UINT8, BASE_DEC, NULL, 0x0, |
714 | 16 | NULL, HFILL } |
715 | 16 | }, |
716 | 16 | { &hf_ccsds_embedded_time, |
717 | 16 | { "Embedded Time", "ccsds.embedded_time", |
718 | 16 | FT_STRING, BASE_NONE, NULL, 0x0, |
719 | 16 | NULL, HFILL } |
720 | 16 | }, |
721 | 16 | { &hf_ccsds_user_data, |
722 | 16 | { "User Data", "ccsds.user_data", |
723 | 16 | FT_BYTES, BASE_NONE, NULL, 0x0, |
724 | 16 | NULL, HFILL } |
725 | 16 | }, |
726 | 16 | { &hf_ccsds_checkword, |
727 | 16 | { "Checkword", "ccsds.checkword", |
728 | 16 | FT_UINT16, BASE_HEX, NULL, 0x0, |
729 | 16 | "Checkword: 16-bit running sum of all bytes excluding the checkword", HFILL } |
730 | 16 | }, |
731 | 16 | { &hf_ccsds_checkword_good, |
732 | 16 | { "Good", "ccsds.checkword_good", |
733 | 16 | FT_BOOLEAN, BASE_NONE, NULL, 0x0, |
734 | 16 | "True: checkword matches packet content; False: doesn't match content", HFILL } |
735 | 16 | }, |
736 | 16 | { &hf_ccsds_checkword_bad, |
737 | 16 | { "Bad", "ccsds.checkword_bad", |
738 | 16 | FT_BOOLEAN, BASE_NONE, NULL, 0x0, |
739 | 16 | "True: checkword doesn't match packet content; False: matches content", HFILL } |
740 | 16 | } |
741 | 16 | }; |
742 | | |
743 | | /* Setup protocol subtree array */ |
744 | 16 | static int *ett[] = { |
745 | 16 | &ett_ccsds_primary_header_flags, |
746 | 16 | &ett_ccsds, |
747 | 16 | &ett_ccsds_primary_header, |
748 | 16 | &ett_ccsds_secondary_header, |
749 | 16 | &ett_ccsds_checkword |
750 | 16 | }; |
751 | | |
752 | 16 | static ei_register_info ei[] = { |
753 | 16 | { &ei_ccsds_length_error, { "ccsds.length.error", PI_MALFORMED, PI_ERROR, "Length field value is greater than the packet seen on the wire", EXPFILL }}, |
754 | 16 | { &ei_ccsds_checkword, { "ccsds.no_checkword", PI_PROTOCOL, PI_WARN, "Packet does not contain a Checkword", EXPFILL }}, |
755 | 16 | }; |
756 | | |
757 | | /* Define the preferences module */ |
758 | 16 | module_t *ccsds_module; |
759 | 16 | expert_module_t* expert_ccsds; |
760 | | |
761 | | /* Register the protocol name and description */ |
762 | 16 | proto_ccsds = proto_register_protocol("CCSDS Space Packet", "CCSDS SP", "ccsds"); |
763 | | |
764 | | /* Required function calls to register the header fields and subtrees used */ |
765 | 16 | proto_register_field_array(proto_ccsds, hf, array_length(hf)); |
766 | 16 | proto_register_subtree_array(ett, array_length(ett)); |
767 | 16 | expert_ccsds = expert_register_protocol(proto_ccsds); |
768 | 16 | expert_register_field_array(expert_ccsds, ei, array_length(ei)); |
769 | | |
770 | 16 | ccsds_handle = register_dissector("ccsds", dissect_ccsds, proto_ccsds); |
771 | | |
772 | | /* Register preferences module */ |
773 | 16 | ccsds_module = prefs_register_protocol(proto_ccsds, NULL); |
774 | | |
775 | 16 | prefs_register_enum_preference(ccsds_module, "global_pref_checkword", |
776 | 16 | "How to handle the CCSDS Checkword", |
777 | 16 | "Specify how the dissector should handle the Ccheckword", |
778 | 16 | &global_dissect_checkword, dissect_checkword, false); |
779 | | |
780 | | /* Dissector table for sub-dissectors */ |
781 | 16 | ccsds_dissector_table = register_dissector_table("ccsds.apid", "CCSDS SP apid", proto_ccsds, FT_UINT16, BASE_DEC); |
782 | 16 | } |
783 | | |
784 | | |
785 | | void |
786 | | proto_reg_handoff_ccsds(void) |
787 | 16 | { |
788 | 16 | dissector_add_for_decode_as_with_preference( "udp.port", ccsds_handle); |
789 | 16 | } |
790 | | |
791 | | /* |
792 | | * Editor modelines - https://www.wireshark.org/tools/modelines.html |
793 | | * |
794 | | * Local variables: |
795 | | * c-basic-offset: 4 |
796 | | * tab-width: 8 |
797 | | * indent-tabs-mode: nil |
798 | | * End: |
799 | | * |
800 | | * vi: set shiftwidth=4 tabstop=8 expandtab: |
801 | | * :indentSize=4:tabSize=8:noTabs=true: |
802 | | */ |