Coverage Report

Created: 2026-11-12 07:01

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
 */