2 * Routines for VCDU dissection
3 * Copyright 2000, Scott Hovis scott.hovis@ums.msfc.nasa.gov
4 * Enhanced 2008, Matt Dunkle Matthew.L.Dunkle@nasa.gov
8 * Wireshark - Network traffic analyzer
9 * By Gerald Combs <gerald@wireshark.com>
10 * Copyright 1998 Gerald Combs
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
30 #include <epan/packet.h>
31 #include <epan/prefs.h>
32 #include <epan/wmem/wmem.h>
35 /* Initialize the protocol and registered fields */
36 static int proto_vcdu
= -1;
38 static int hf_smex_gsc
= -1;
39 /* static int hf_smex_unused = -1; */
40 static int hf_smex_version
= -1;
41 static int hf_smex_framelen
= -1;
42 static int hf_smex_rs_error
= -1;
43 static int hf_smex_rs_enable
= -1;
44 static int hf_smex_crc_enable
= -1;
45 static int hf_smex_crc_error
= -1;
46 static int hf_smex_mcs_enable
= -1;
47 static int hf_smex_mcs_num_error
= -1;
48 static int hf_smex_data_inv
= -1;
49 static int hf_smex_frame_sync
= -1;
50 static int hf_smex_data_dir
= -1;
51 static int hf_smex_data_class
= -1;
52 static int hf_smex_pb5
= -1;
53 static int hf_smex_jday
= -1;
54 static int hf_smex_seconds
= -1;
55 static int hf_smex_msec
= -1;
56 /* static int hf_smex_spare = -1; */
58 static int hf_vcdu_version
= -1;
59 static int hf_vcdu_sp_id
= -1;
60 static int hf_vcdu_vc_id
= -1;
61 static int hf_vcdu_seq
= -1;
62 static int hf_vcdu_replay
= -1;
64 /* although technically not part of the vcdu header, the
65 * first header pointer (for ccsds), and the last bit
66 * pointer (for bitstream), are more easily processed by
67 * simply adding them to the tail end of the vcdu header
68 * branch rather than creating a distinct branch for them
70 static int hf_vcdu_fhp
= -1;
71 static int hf_vcdu_lbp
= -1;
73 static dissector_handle_t vcdu_handle
;
75 static dissector_handle_t ccsds_handle
;
77 /* Initialize the subtree pointers */
78 static gint ett_vcdu
= -1;
79 static gint ett_smex
= -1;
80 static gint ett_vcduh
= -1;
83 * Bits in the first 16-bit header word
85 #define SMEX_VERSION 0xc000
86 #define SMEX_FRAMELEN 0x3fff
88 /* some basic sizing parameters */
89 #define IP_HEADER_LENGTH 48
90 #define SMEX_HEADER_LENGTH 20
91 #define VCDU_HEADER_LENGTH 6
92 #define CCSDS_PRIMARY_HEADER_LENGTH 6
93 #define CCSDS_SECONDARY_HEADER_LENGTH 10
95 #define PB5_JULIAN_DAY_MASK 0x7ffe
96 #define PB5_SECONDS_MASK 0x01ffff
97 #define PB5_MILLISECONDS_MASK 0xffc0
99 #define LBP_ALL_DATA 0x3fff
100 #define LBP_ALL_DATA_ANOMALY 0x7ff
101 #define LBP_ALL_FILL 0x3ffe
103 #define FHP_ALL_FILL 0x7fe
104 #define FHP_CONTINUATION 0x7ff
106 #define LBP_MASK 0x3fff
107 #define FHP_MASK 0x7ff
109 /* leap year macro */
111 # define Leap(yr) ( ( 0 == (yr)%4 && 0 != (yr)%100 ) || ( 0 == (yr)%400 ) )
115 static const value_string smex_data_inversion_type
[] = {
116 { 0, "Data True (not inverted)" },
117 { 1, "Data Inverted (not corrected)" },
118 { 2, "Data Inversion State UNDEFINED" },
119 { 3, "Data Inverted (and corrected)" },
123 static const value_string smex_frame_sync_mode
[] = {
131 static const value_string smex_data_direction
[] = {
137 static const value_string smex_data_class
[] = {
138 { 0, "Data Class UNDEFINED" },
139 { 1, "CCSDS Frame" },
140 { 2, "CCSDS Packet" },
142 { 4, "Stopped TDM Frame" },
146 /* default bitstream channel assignments:
147 * the audio channels 4-6 are designated as bitstream channels
148 * the standard bitstream channels are 12 through 19
149 * the video channels 28-30 are designated as bitstream channels
150 * the fill channel 63 is designated as bitstream
152 static int bitstream_channels
[] =
154 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* channels 0-9 */
155 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, /* channels 10-19 */
156 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* channels 20-29 */
157 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* channels 30-39 */
158 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* channels 40-49 */
159 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* channels 50-59 */
160 0, 0, 0, 1 /* channels 60-63 */
167 static uat_channel_t
*uat_bitchannels
= NULL
;
168 static uat_t
*vcdu_uat
= NULL
;
169 static guint num_channels_uat
= 0;
171 UAT_DEC_CB_DEF(uat_bitchannels
, channel
, uat_channel_t
)
174 vcdu_uat_data_update_cb(void *p
, const char **err
) {
175 uat_channel_t
*ud
= (uat_channel_t
*)p
;
177 if (ud
->channel
>= 64) {
178 *err
= g_strdup("Channel must be between 0-63.");
184 vcdu_prefs_apply_cb(void)
188 if (num_channels_uat
> 0)
190 memset(bitstream_channels
, 0, sizeof(bitstream_channels
));
192 for (i
= 0; i
< num_channels_uat
; i
++)
194 bitstream_channels
[uat_bitchannels
[i
].channel
] = 1;
199 /* convert smex PB5 header time to a human readable string - NOT THREAD SAFE
201 * note: this is not true PB5 time either, but a tsi specific version, although it is similar
204 smex_time_to_string (int pb5_days_since_midnight_9_10_oct_1995
, int pb5_seconds
, int pb5_milliseconds
)
206 static int utcdiff
= 0;
209 static int Days
[2][13] =
211 { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
212 { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
218 /* compute the static constant difference in seconds
219 * between midnight 9-10 October 1995 (PB5 time) and
220 * seconds since 1/1/1970 (UTC time) just this once
224 for (yr
=1970; yr
< 1995; ++yr
)
226 utcdiff
+= (Leap(yr
) ? 366 : 365) * 24 * 60 * 60;
230 ix
= (Leap(1995) ? 1 : 0);
232 for (month
=1; month
< 10; ++month
)
234 days
+= Days
[ix
][month
];
237 days
+= 9; /* this gets us up to midnight october 9-10 */
239 utcdiff
+= days
* 24 * 60 * 60; /* add days in 1995 prior to October 10 */
242 t
.secs
= (pb5_days_since_midnight_9_10_oct_1995
* 86400) + pb5_seconds
+ utcdiff
;
243 t
.nsecs
= pb5_milliseconds
*1000000; /* msecs to nsecs */
245 return abs_time_to_str(&t
, ABSOLUTE_TIME_DOY_UTC
, TRUE
);
250 dissect_vcdu(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
)
252 int packet_boundary
= 0;
255 int ccsds_tree_added
= 0;
258 proto_item
*smex_header
= NULL
;
259 proto_tree
*smex_tree
= NULL
;
261 proto_item
*vcdu_header
= NULL
;
262 proto_tree
*vcdu_tree
= NULL
;
264 guint16 first_word
= 0;
265 guint32 long_word
= 0;
268 tvbuff_t
*new_tvb
= NULL
;
270 int vcid
= 0, pb5_days
= 0, pb5_seconds
= 0, pb5_milliseconds
= 0;
271 const char *time_string
= NULL
;
273 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "VCDU");
274 col_set_str(pinfo
->cinfo
, COL_INFO
, "Virtual Channel Data Unit");
277 /* build the smex header tree */
278 smex_header
= proto_tree_add_text(tree
, tvb
, offset
, SMEX_HEADER_LENGTH
, "SMEX Header");
279 smex_tree
= proto_item_add_subtree(smex_header
, ett_smex
);
281 proto_tree_add_item(smex_tree
, hf_smex_gsc
, tvb
, offset
, 8, ENC_BIG_ENDIAN
);
283 /* proto_tree_add_item(smex_tree, hf_smex_unused, tvb, offset, 2, ENC_BIG_ENDIAN); */
286 first_word
= tvb_get_ntohs(tvb
, offset
);
287 proto_tree_add_uint(smex_tree
, hf_smex_version
, tvb
, offset
, 2, first_word
);
288 proto_tree_add_uint(smex_tree
, hf_smex_framelen
, tvb
, offset
, 2, first_word
);
291 proto_tree_add_item(smex_tree
, hf_smex_rs_enable
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
292 proto_tree_add_item(smex_tree
, hf_smex_rs_error
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
293 proto_tree_add_item(smex_tree
, hf_smex_crc_enable
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
294 proto_tree_add_item(smex_tree
, hf_smex_crc_error
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
295 proto_tree_add_item(smex_tree
, hf_smex_mcs_enable
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
296 proto_tree_add_item(smex_tree
, hf_smex_mcs_num_error
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
297 proto_tree_add_item(smex_tree
, hf_smex_data_inv
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
300 proto_tree_add_item(smex_tree
, hf_smex_frame_sync
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
301 proto_tree_add_item(smex_tree
, hf_smex_data_dir
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
302 proto_tree_add_item(smex_tree
, hf_smex_data_class
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
305 /* extract smex ground receipt time tag */
306 long_word
= tvb_get_ntohl(tvb
, offset
);
307 pb5_days
= (long_word
>> 17) & PB5_JULIAN_DAY_MASK
;
308 pb5_seconds
= (long_word
& PB5_SECONDS_MASK
);
310 first_word
= tvb_get_ntohs(tvb
, offset
+4);
311 pb5_milliseconds
= (first_word
& PB5_MILLISECONDS_MASK
) >> 6;
313 proto_tree_add_item(smex_tree
, hf_smex_pb5
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
314 proto_tree_add_item(smex_tree
, hf_smex_jday
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
316 proto_tree_add_item(smex_tree
, hf_smex_seconds
, tvb
, offset
, 3, ENC_BIG_ENDIAN
);
319 proto_tree_add_item(smex_tree
, hf_smex_msec
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
320 /* proto_tree_add_item(smex_tree, hf_smex_spare, tvb, offset, 2, ENC_BIG_ENDIAN); */
323 /* format ground receipt time into human readable time format for display */
324 time_string
= smex_time_to_string(pb5_days
, pb5_seconds
, pb5_milliseconds
);
325 proto_tree_add_text(smex_tree
, tvb
, offset
-6, 6, "%s = Ground Receipt Time", time_string
);
327 proto_item_set_end(smex_header
, tvb
, offset
);
330 /* build the vcdu header tree */
331 vcdu_header
= proto_tree_add_text(tree
, tvb
, offset
, VCDU_HEADER_LENGTH
, "VCDU Header");
332 vcdu_tree
= proto_item_add_subtree(vcdu_header
, ett_vcdu
);
334 /* extract the virtual channel for use later on */
335 first_word
= tvb_get_ntohs(tvb
, offset
);
336 vcid
= first_word
& 0x3f;
338 proto_tree_add_item(vcdu_tree
, hf_vcdu_version
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
339 proto_tree_add_item(vcdu_tree
, hf_vcdu_sp_id
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
340 proto_tree_add_item(vcdu_tree
, hf_vcdu_vc_id
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
342 proto_tree_add_item(vcdu_tree
, hf_vcdu_seq
, tvb
, offset
, 3, ENC_BIG_ENDIAN
);
344 proto_tree_add_item(vcdu_tree
, hf_vcdu_replay
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
347 /* extract mpdu/bpdu header word */
348 first_word
= tvb_get_ntohs(tvb
, offset
);
350 /* do bitstream channel processing */
351 if (bitstream_channels
[vcid
])
353 /* extract last bit pointer for bitstream channels */
354 new_ptr
= first_word
& LBP_MASK
;
356 /* add last bit pointer to display tree */
357 proto_tree_add_item(vcdu_tree
, hf_vcdu_lbp
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
362 proto_tree_add_text(vcdu_tree
, tvb
, 0, -1, "Bitream ALL Data");
365 case LBP_ALL_DATA_ANOMALY
:
366 proto_tree_add_text(vcdu_tree
, tvb
, 0, -1, "Bitream ALL Data (Anomaly)");
370 proto_tree_add_text(vcdu_tree
, tvb
, 0, -1, "Bitream ALL Fill");
376 } /* end of bitstream channel processing */
378 /* do ccsds channel processing */
381 /* extract first header pointer for ccsds channels */
382 new_ptr
= first_word
& FHP_MASK
;
384 /* add first header pointer to display tree */
385 proto_tree_add_item(vcdu_tree
, hf_vcdu_fhp
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
387 /* process special cases of first header pointer */
388 if (FHP_ALL_FILL
== new_ptr
)
390 proto_tree_add_text(vcdu_tree
, tvb
, 0, -1, "Ccsds ALL Fill");
393 else if (FHP_CONTINUATION
== new_ptr
)
395 proto_tree_add_text(vcdu_tree
, tvb
, 0, -1, "Ccsds Continuation Packet");
398 /* process as many ccsds packet headers as we can using the ccsds packet dissector */
401 /* compute offset and packet boundary lengths for ccsds dissector loop */
402 new_offset
= offset
+ 2 + new_ptr
;
405 tvb_reported_length(tvb
) - VCDU_HEADER_LENGTH
406 - CCSDS_PRIMARY_HEADER_LENGTH
- CCSDS_SECONDARY_HEADER_LENGTH
;
408 while ( ((new_offset
-offset
+2) < packet_boundary
) && ((new_offset
-offset
+2) >= 4) )
410 ccsds_tree_added
= 1;
411 ccsds_len
= tvb_get_ntohs(tvb
, new_offset
+4);
413 new_tvb
= tvb_new_subset_remaining(tvb
, new_offset
);
414 call_dissector(ccsds_handle
, new_tvb
, pinfo
, vcdu_tree
);
416 new_offset
= new_offset
+ ccsds_len
+ 7;
419 if (! ccsds_tree_added
)
421 proto_tree_add_text(vcdu_tree
, tvb
, 0, -1,
422 "FHP too close to end of VCDU. Incomplete Hdr Info Available"
423 " - Unable to format CCSDS Hdr(s)." );
427 } /* end of ccsds channel processing */
429 /* don't include the mpdu/bpdu header in the vcdu header highlighting.
430 * by skipping the offset bump the vcdu header highlighting will show
431 * just 6 bytes as it really should, and the fhp/lbp will be included
432 * in the data zone, which is technically more correct.
435 proto_item_set_end(vcdu_tree
, tvb
, offset
);
437 if (! ccsds_tree_added
)
439 /* add "Data" section if ccsds parsing did not do so already */
440 proto_tree_add_text(vcdu_tree
, tvb
, offset
, -1, "Data");
447 proto_register_vcdu(void)
449 module_t
*vcdu_module
;
451 /* Setup list of header fields See Section 1.6.1 for details*/
452 static hf_register_info hf
[] = {
454 { "Ground Sequence Counter", "vcdu.smex.gsc",
455 FT_UINT64
, BASE_DEC
, NULL
, 0x0,
456 "SMEX Ground Sequence Counter", HFILL
}
460 { "Unused", "vcdu.smex.unused",
461 FT_UINT16
, BASE_DEC
, NULL
, 0x0,
462 "SMEX Unused", HFILL
}
466 { "Version", "vcdu.smex.version",
467 FT_UINT16
, BASE_DEC
, NULL
, SMEX_VERSION
,
468 "SMEX Version", HFILL
}
471 { "Frame Length", "vcdu.smex.frame_len",
472 FT_UINT16
, BASE_DEC
, NULL
, SMEX_FRAMELEN
,
473 "SMEX Frame Length", HFILL
}
475 { &hf_smex_rs_enable
,
476 { "RS Enable", "vcdu.smex.rs_enable",
477 FT_BOOLEAN
, 8, NULL
, 0x80,
478 "SMEX RS Enable", HFILL
}
481 { "RS Error", "vcdu.smex.rs_error",
482 FT_BOOLEAN
, 8, NULL
, 0x40,
483 "SMEX RS Error", HFILL
}
485 { &hf_smex_crc_enable
,
486 { "CRC Enable", "vcdu.smex.crc_enable",
487 FT_BOOLEAN
, 8, NULL
, 0x20,
488 "SMEX CRC Enable", HFILL
}
490 { &hf_smex_crc_error
,
491 { "CRC Error", "vcdu.smex.crc_error",
492 FT_BOOLEAN
, 8, NULL
, 0x10,
493 "SMEX CRC Error", HFILL
}
495 { &hf_smex_mcs_enable
,
496 { "MCS Enable", "vcdu.smex.mcs_enable",
497 FT_BOOLEAN
, 8, NULL
, 0x08,
498 "SMEX MCS Enable", HFILL
}
500 { &hf_smex_mcs_num_error
,
501 { "MCS Number Error", "vcdu.smex.mcs_numerr",
502 FT_BOOLEAN
, 8, NULL
, 0x04,
503 "SMEX MCS Number Error", HFILL
}
506 { "Data Inversion", "vcdu.smex.data_inv",
507 FT_UINT16
, BASE_DEC
, VALS(smex_data_inversion_type
), 0x03,
508 "SMEX Data Inversion", HFILL
}
510 { &hf_smex_frame_sync
,
511 { "Frame Sync", "vcdu.smex.frame_sync",
512 FT_UINT16
, BASE_DEC
, VALS(smex_frame_sync_mode
), 0xc0,
513 "SMEX Frame Sync Flag", HFILL
}
516 { "Data Direction", "vcdu.smex.data_dir",
517 FT_UINT16
, BASE_DEC
, VALS(smex_data_direction
), 0x20,
518 "SMEX Data Direction flag", HFILL
}
520 { &hf_smex_data_class
,
521 { "Data Class", "vcdu.smex.data_class",
522 FT_UINT16
, BASE_DEC
, VALS(smex_data_class
), 0x1f,
523 "SMEX Data Class", HFILL
}
526 { "PB5 Flag", "vcdu.smex.pb5",
527 FT_UINT16
, BASE_DEC
, NULL
, 0x8000,
528 "SMEX PB5 Flag", HFILL
}
531 { "Julian Day", "vcdu.smex.jday",
532 FT_UINT16
, BASE_DEC
, NULL
, PB5_JULIAN_DAY_MASK
,
533 "SMEX Julian Day", HFILL
}
536 { "Seconds", "vcdu.smex.seconds",
537 FT_UINT24
, BASE_DEC
, NULL
, PB5_SECONDS_MASK
,
538 "SMEX Seconds", HFILL
}
541 { "Milliseconds", "vcdu.smex.msec",
542 FT_UINT16
, BASE_DEC
, NULL
, PB5_MILLISECONDS_MASK
,
543 "SMEX Milliseconds", HFILL
}
547 { "Spare", "vcdu.smex.spare",
548 FT_UINT16
, BASE_DEC
, NULL
, 0x03f,
549 "SMEX Spare", HFILL
}
554 { "Version", "vcdu.version",
555 FT_UINT16
, BASE_DEC
, NULL
, 0xc0,
556 "VCDU Version", HFILL
}
559 { "Space Craft ID", "vcdu.spid",
560 FT_UINT16
, BASE_DEC
, NULL
, 0x3fc0,
561 "VCDU Space Craft ID", HFILL
}
564 { "Virtual Channel ID", "vcdu.vcid",
565 FT_UINT16
, BASE_DEC
, NULL
, 0x3f,
566 "VCDU Virtual Channel ID", HFILL
}
569 { "Sequence Count", "vcdu.seq",
570 FT_UINT16
, BASE_DEC
, NULL
, 0xffffff,
571 "VCDU Sequence Count", HFILL
}
574 { "Replay Flag", "vcdu.replay",
575 FT_BOOLEAN
, 8, NULL
, 0x80,
576 "VCDU Replay Flag", HFILL
}
579 /* not really part of the vcdu header, but it's easier this way */
581 { "First Header Pointer", "vcdu.fhp",
582 FT_UINT16
, BASE_DEC
, NULL
, FHP_MASK
,
583 "VCDU/MPDU First Header Pointer", HFILL
}
586 { "Last Bit Pointer", "vcdu.lbp",
587 FT_UINT16
, BASE_DEC
, NULL
, LBP_MASK
,
588 "VCDU/BPDU Last Bit Pointer", HFILL
}
592 static uat_field_t vcdu_uat_flds
[] = {
593 UAT_FLD_DEC(uat_bitchannels
, channel
, "Bitstream Channel", "Bitstream Channel"),
597 /* Setup protocol subtree array */
598 static gint
*ett
[] = {
604 /* Register the protocol name and description */
605 proto_vcdu
= proto_register_protocol("VCDU", "VCDU", "vcdu");
607 /* Required function calls to register the header fields and subtrees used */
608 proto_register_field_array(proto_vcdu
, hf
, array_length(hf
));
609 proto_register_subtree_array(ett
, array_length(ett
));
611 /* XX: Does this dissector need to be publicly registered ?? */
612 vcdu_handle
= register_dissector("vcdu", dissect_vcdu
, proto_vcdu
);
614 vcdu_module
= prefs_register_protocol(proto_vcdu
, vcdu_prefs_apply_cb
);
616 vcdu_uat
= uat_new("Bitstream Channel Table",
617 sizeof(uat_channel_t
),
618 "vcdu_bitstream_channels",
620 (void**)&uat_bitchannels
,
622 UAT_AFFECTS_DISSECTION
, /* affects dissection of packets, but not set of named fields */
625 vcdu_uat_data_update_cb
,
630 prefs_register_uat_preference(vcdu_module
,
631 "bitstream_channels",
632 "Bitstream Channel Table",
633 "Bitstream Channel Table",
640 proto_reg_handoff_vcdu(void)
642 dissector_add_handle("udp.port", vcdu_handle
); /* for 'decode as' */
643 ccsds_handle
= find_dissector("ccsds");