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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
36 #include <epan/packet.h>
37 #include <epan/filesystem.h>
38 #include <wsutil/file_util.h>
41 /* Initialize the protocol and registered fields */
42 static int proto_vcdu = -1;
44 static int hf_smex_gsc = -1;
45 static int hf_smex_unused = -1;
46 static int hf_smex_version = -1;
47 static int hf_smex_framelen = -1;
48 static int hf_smex_rs_error = -1;
49 static int hf_smex_rs_enable = -1;
50 static int hf_smex_crc_enable = -1;
51 static int hf_smex_crc_error = -1;
52 static int hf_smex_mcs_enable = -1;
53 static int hf_smex_mcs_num_error = -1;
54 static int hf_smex_data_inv = -1;
55 static int hf_smex_frame_sync = -1;
56 static int hf_smex_data_dir = -1;
57 static int hf_smex_data_class = -1;
58 static int hf_smex_pb5 = -1;
59 static int hf_smex_jday = -1;
60 static int hf_smex_seconds = -1;
61 static int hf_smex_msec = -1;
62 static int hf_smex_spare = -1;
64 static int hf_vcdu_version = -1;
65 static int hf_vcdu_sp_id = -1;
66 static int hf_vcdu_vc_id = -1;
67 static int hf_vcdu_seq = -1;
68 static int hf_vcdu_replay = -1;
70 /* although technically not part of the vcdu header, the
71 * first header pointer (for ccsds), and the last bit
72 * pointer (for bitstream), are more easily processed by
73 * simply adding them to the tail end of the vcdu header
74 * branch rather than creating a distinct branch for them
76 static int hf_vcdu_fhp = -1;
77 static int hf_vcdu_lbp = -1;
79 static dissector_handle_t ccsds_handle = (dissector_handle_t)-1;
81 /* Initialize the subtree pointers */
82 static gint ett_vcdu = -1;
83 static gint ett_smex = -1;
84 static gint ett_vcduh = -1;
87 * Bits in the first 16-bit header word
89 #define SMEX_VERSION 0xc000
90 #define SMEX_FRAMELEN 0x3fff
92 /* some basic sizing parameters */
93 #define IP_HEADER_LENGTH 48
94 #define SMEX_HEADER_LENGTH 20
95 #define VCDU_HEADER_LENGTH 6
96 #define CCSDS_PRIMARY_HEADER_LENGTH 6
97 #define CCSDS_SECONDARY_HEADER_LENGTH 10
99 #define PB5_JULIAN_DAY_MASK 0x7ffe
100 #define PB5_SECONDS_MASK 0x01ffff
101 #define PB5_MILLISECONDS_MASK 0xffc0
103 #define LBP_ALL_DATA 0x3fff
104 #define LBP_ALL_DATA_ANOMALY 0x7ff
105 #define LBP_ALL_FILL 0x3ffe
107 #define FHP_ALL_FILL 0x7fe
108 #define FHP_CONTINUATION 0x7ff
110 #define LBP_MASK 0x3fff
111 #define FHP_MASK 0x7ff
113 /* leap year macro */
115 # define Leap(yr) ( ( 0 == (yr)%4 && 0 != (yr)%100 ) || ( 0 == (yr)%400 ) )
119 static const value_string smex_data_inversion_type[] = {
120 { 0, "Data True (not inverted)" },
121 { 1, "Data Inverted (not corrected)" },
122 { 2, "Data Inversion State UNDEFINED" },
123 { 3, "Data Inverted (and corrected)" },
127 static const value_string smex_frame_sync_mode[] = {
135 static const value_string smex_data_direction[] = {
141 static const value_string smex_data_class[] = {
142 { 0, "Data Class UNDEFINED" },
143 { 1, "CCSDS Frame" },
144 { 2, "CCSDS Packet" },
146 { 4, "Stopped TDM Frame" },
153 /* prototype of utc to julian time conversion function - see packet-ccsds.c for the full source code */
154 extern void utc_to_julian ( int utc, int* year, int* julianday, int* hour, int* minute, int* second );
157 /* convert smex PB5 header time to a human readable string - NOT THREAD SAFE
159 * note: this is not true PB5 time either, but a tsi specific version, although it is similar
161 static const char* smex_time_to_string ( int pb5_days_since_midnight_9_10_oct_1995, int pb5_seconds, int pb5_milliseconds )
163 static const char* fmt = "%04d/%03d:%02d:%02d:%02d.%03d";
164 static char juliantime[40];
165 static int utcdiff = 0;
167 static int Days[2][13] =
169 { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
170 { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
173 int utc, yr, year, julianday, hour, minute, second;
176 /* compute the static constant difference in seconds
177 * between midnight 9-10 October 1995 (PB5 time) and
178 * seconds since 1/1/1970 (UTC time) just this once
182 for ( yr=1970; yr < 1995; ++yr )
184 utcdiff += ( Leap(yr) ? 366 : 365 ) * 24 * 60 * 60;
188 ix = ( Leap(1995) ? 1 : 0 );
190 for ( month=1; month < 10; ++month )
192 days += Days[ix][month];
195 days += 9; /* this gets us up to midnight october 9-10 */
197 utcdiff += days * 24 * 60 * 60; /* add days in 1995 prior to October 10 */
200 utc = ( pb5_days_since_midnight_9_10_oct_1995 * 86400 ) + pb5_seconds + utcdiff;
201 utc_to_julian ( utc, &year, &julianday, &hour, &minute, &second );
203 g_snprintf ( juliantime, sizeof(juliantime), fmt, year, julianday, hour, minute, second, pb5_milliseconds );
210 /* Code to actually dissect the packets */
212 dissect_vcdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
214 static int bitstream_channels_file_read = 0;
216 /* default bitstream channel assignments:
217 * the audio channels 4-6 are designated as bitstream channels
218 * the standard bitstream channels are 12 through 19
219 * the video channels 28-30 are designated as bitstream channels
220 * the fill channel 63 is designated as bitstream
222 static int bitstream_channels[] =
224 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* channels 0-9 */
225 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, /* channels 10-19 */
226 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* channels 20-29 */
227 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* channels 30-39 */
228 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* channels 40-49 */
229 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* channels 50-59 */
230 0, 0, 0, 1 /* channels 60-63 */
233 int packet_boundary = 0;
236 int ccsds_tree_added = 0;
241 proto_item *smex_header = NULL;
242 proto_tree *smex_tree = NULL;
244 proto_item *vcdu_header = NULL;
245 proto_tree *vcdu_tree = NULL;
247 guint16 first_word = 0;
248 guint32 long_word = 0;
251 tvbuff_t *new_tvb = NULL;
253 int channel = 0, vcid = 0;
254 char *filename = NULL;
255 char *endptr = NULL, *cptr = NULL;
259 int pb5_days = 0, pb5_seconds = 0, pb5_milliseconds = 0;
260 const char* time_string = NULL;
263 /* if the bitstream channels file has not been read attempt to do so now.
264 * this file potentially contains a modified list of the channels that
265 * should be processed as bitstream instead of ccsds.
267 if ( ! bitstream_channels_file_read )
269 bitstream_channels_file_read = 1;
270 filename = get_persconffile_path ( ".bitstream_channels", FALSE, FALSE );
271 fp = ws_fopen ( filename, "r" );
275 if ( fgets ( readbuf, sizeof(readbuf), fp ) == readbuf )
277 memset ( bitstream_channels, 0, sizeof(bitstream_channels) );
282 channel = strtoul ( cptr, &endptr, 0 );
283 if ( cptr == endptr ) break;
285 if ( channel >= 0 && channel < 64 )
287 bitstream_channels[channel] = 1;
299 if (check_col(pinfo->cinfo, COL_PROTOCOL))
300 col_set_str(pinfo->cinfo, COL_PROTOCOL, "VCDU");
301 if (check_col(pinfo->cinfo, COL_INFO))
302 col_set_str(pinfo->cinfo, COL_INFO, "Virtual Channel Data Unit");
305 /* build the smex header tree */
306 smex_header=proto_tree_add_text(tree, tvb, offset, SMEX_HEADER_LENGTH, "SMEX Header");
307 smex_tree=proto_item_add_subtree(smex_header, ett_smex);
309 proto_tree_add_item(smex_tree, hf_smex_gsc, tvb, offset, 8, FALSE);
311 /* proto_tree_add_uint(smex_tree, hf_smex_unused, tvb, offset, 2, FALSE); */
314 first_word=tvb_get_ntohs(tvb, offset);
315 proto_tree_add_uint(smex_tree, hf_smex_version, tvb, offset, 2, first_word);
316 proto_tree_add_uint(smex_tree, hf_smex_framelen, tvb, offset, 2, first_word);
319 proto_tree_add_item(smex_tree, hf_smex_rs_enable, tvb, offset, 1, FALSE);
320 proto_tree_add_item(smex_tree, hf_smex_rs_error, tvb, offset, 1, FALSE);
321 proto_tree_add_item(smex_tree, hf_smex_crc_enable, tvb, offset, 1, FALSE);
322 proto_tree_add_item(smex_tree, hf_smex_crc_error, tvb, offset, 1, FALSE);
323 proto_tree_add_item(smex_tree, hf_smex_mcs_enable, tvb, offset, 1, FALSE);
324 proto_tree_add_item(smex_tree, hf_smex_mcs_num_error, tvb, offset, 1, FALSE);
325 proto_tree_add_item(smex_tree, hf_smex_data_inv, tvb, offset, 1, FALSE);
328 proto_tree_add_item(smex_tree, hf_smex_frame_sync, tvb, offset, 1, FALSE);
329 proto_tree_add_item(smex_tree, hf_smex_data_dir, tvb, offset, 1, FALSE);
330 proto_tree_add_item(smex_tree, hf_smex_data_class, tvb, offset, 1, FALSE);
333 /* extract smex ground receipt time tag */
334 long_word = tvb_get_ntohl ( tvb, offset );
335 pb5_days = ( long_word >> 17 ) & PB5_JULIAN_DAY_MASK;
336 pb5_seconds = ( long_word & PB5_SECONDS_MASK );
338 first_word = tvb_get_ntohs ( tvb, offset+4 );
339 pb5_milliseconds = ( first_word & PB5_MILLISECONDS_MASK ) >> 6;
341 proto_tree_add_item(smex_tree, hf_smex_pb5, tvb, offset, 2, FALSE);
342 proto_tree_add_item(smex_tree, hf_smex_jday, tvb, offset, 2, FALSE);
344 proto_tree_add_item(smex_tree, hf_smex_seconds, tvb, offset, 3, FALSE);
347 proto_tree_add_item(smex_tree, hf_smex_msec, tvb, offset, 2, FALSE);
348 /* proto_tree_add_item(smex_tree, hf_smex_spare, tvb, offset, 2, FALSE); */
351 /* format ground receipt time into human readable time format for display */
352 time_string = smex_time_to_string ( pb5_days, pb5_seconds, pb5_milliseconds );
353 proto_tree_add_text (smex_tree, tvb, offset-6, 6, "%s = Ground Receipt Time", time_string );
355 proto_item_set_end(smex_header, tvb, offset);
358 /* build the vcdu header tree */
359 vcdu_header=proto_tree_add_text(tree, tvb, offset, VCDU_HEADER_LENGTH, "VCDU Header");
360 vcdu_tree = proto_item_add_subtree(vcdu_header, ett_vcdu);
362 /* extract the virtual channel for use later on */
363 first_word=tvb_get_ntohs(tvb, offset);
364 vcid = first_word & 0x3f;
366 proto_tree_add_item(vcdu_tree, hf_vcdu_version, tvb, offset, 2, FALSE);
367 proto_tree_add_item(vcdu_tree, hf_vcdu_sp_id, tvb, offset, 2, FALSE);
368 proto_tree_add_item(vcdu_tree, hf_vcdu_vc_id, tvb, offset, 2, FALSE);
370 proto_tree_add_item(vcdu_tree, hf_vcdu_seq, tvb, offset, 3, FALSE);
372 proto_tree_add_item(vcdu_tree, hf_vcdu_replay, tvb, offset, 1, FALSE);
375 /* extract mpdu/bpdu header word */
376 first_word=tvb_get_ntohs(tvb, offset);
378 /* do bitstream channel processing */
379 if ( bitstream_channels[vcid] )
381 /* extract last bit pointer for bitstream channels */
382 new_ptr=first_word & LBP_MASK;
384 /* add last bit pointer to display tree */
385 proto_tree_add_item(vcdu_tree, hf_vcdu_lbp, tvb, offset, 2, FALSE);
390 proto_tree_add_text(vcdu_tree, tvb, 0, -1, "Bitream ALL Data");
393 case LBP_ALL_DATA_ANOMALY:
394 proto_tree_add_text(vcdu_tree, tvb, 0, -1, "Bitream ALL Data (Anomaly)");
398 proto_tree_add_text(vcdu_tree, tvb, 0, -1, "Bitream ALL Fill");
404 } /* end of bitstream channel processing */
406 /* do ccsds channel processing */
409 /* extract first header pointer for ccsds channels */
410 new_ptr=first_word & FHP_MASK;
412 /* add first header pointer to display tree */
413 proto_tree_add_item(vcdu_tree, hf_vcdu_fhp, tvb, offset, 2, FALSE);
415 /* process special cases of first header pointer */
416 if ( FHP_ALL_FILL == new_ptr )
418 proto_tree_add_text(vcdu_tree, tvb, 0, -1, "Ccsds ALL Fill");
421 else if ( FHP_CONTINUATION == new_ptr )
423 proto_tree_add_text(vcdu_tree, tvb, 0, -1, "Ccsds Continuation Packet");
426 /* process as many ccsds packet headers as we can using the ccsds packet dissector */
429 /* compute offset and packet boundary lengths for ccsds dissector loop */
430 new_offset=offset+2+new_ptr;
432 packet_boundary = pinfo->iplen - IP_HEADER_LENGTH - VCDU_HEADER_LENGTH - CCSDS_PRIMARY_HEADER_LENGTH - CCSDS_SECONDARY_HEADER_LENGTH;
434 while ( (new_offset-offset+2) < packet_boundary && (new_offset-offset+2) >= 4 )
436 ccsds_tree_added = 1;
437 ccsds_len=tvb_get_ntohs(tvb, new_offset+4);
439 apid=tvb_get_ntohs(tvb, new_offset);
441 /* printf ( "new_ptr=%d new_offset=%d apid=%d ccsds_len=%d\n", new_ptr, new_offset, apid, ccsds_len ); fflush(stdout); */
443 new_tvb = tvb_new_subset(tvb, new_offset, -1, -1);
444 call_dissector(ccsds_handle, new_tvb, pinfo, vcdu_tree);
446 new_offset=new_offset+ccsds_len+7;
449 if ( ! ccsds_tree_added )
451 proto_tree_add_text(vcdu_tree, tvb, 0, -1, "FHP too close to end of VCDU. Incomplete Hdr Info Available - Unable to format CCSDS Hdr(s)." );
455 } /* end of ccsds channel processing */
457 /* don't include the mpdu/bpdu header in the vcdu header highlighting.
458 * by skipping the offset bump the vcdu header highlighting will show
459 * just 6 bytes as it really should, and the fhp/lbp will be included
460 * in the data zone, which is technically more correct.
463 proto_item_set_end(vcdu_tree, tvb, offset);
465 if ( ! ccsds_tree_added )
467 /* add "Data" section if ccsds parsing did not do so already */
468 proto_tree_add_text(vcdu_tree, tvb, offset, -1, "Data");
474 /* Register the protocol with Wireshark
475 * this format is require because a script is used to build the C function
476 * that calls all the protocol registration.
479 proto_register_vcdu(void)
481 /* Setup list of header fields See Section 1.6.1 for details*/
482 static hf_register_info hf[] = {
484 { "Ground Sequence Counter", "smex.gsc",
485 FT_UINT64, BASE_DEC, NULL, 0x0,
486 "SMEX Ground Sequence Counter", HFILL }
489 { "Unused", "smex.unused",
490 FT_UINT16, BASE_DEC, NULL, 0x0,
491 "SMEX Unused", HFILL }
494 { "Version", "smex.version",
495 FT_UINT16, BASE_DEC, NULL, SMEX_VERSION,
496 "SMEX Version", HFILL }
499 { "Frame Length", "smex.frame_len",
500 FT_UINT16, BASE_DEC, NULL, SMEX_FRAMELEN,
501 "SMEX Frame Length", HFILL }
503 { &hf_smex_rs_enable,
504 { "RS Enable", "smex.rs_enable",
505 FT_BOOLEAN, 8, NULL, 0x80,
506 "SMEX RS Enable", HFILL }
509 { "RS Error", "smex.rs_error",
510 FT_BOOLEAN, 8, NULL, 0x40,
511 "SMEX RS Error", HFILL }
513 { &hf_smex_crc_enable,
514 { "CRC Enable", "smex.crc_enable",
515 FT_BOOLEAN, 8, NULL, 0x20,
516 "SMEX CRC Enable", HFILL }
518 { &hf_smex_crc_error,
519 { "CRC Error", "smex.crc_error",
520 FT_BOOLEAN, 8, NULL, 0x10,
521 "SMEX CRC Error", HFILL }
523 { &hf_smex_mcs_enable,
524 { "MCS Enable", "smex.mcs_enable",
525 FT_BOOLEAN, 8, NULL, 0x08,
526 "SMEX MCS Enable", HFILL }
528 { &hf_smex_mcs_num_error,
529 { "MCS Number Error", "smex.mcs_numerr",
530 FT_BOOLEAN, 8, NULL, 0x04,
531 "SMEX MCS Number Error", HFILL }
534 { "Data Inversion", "smex.data_inv",
535 FT_UINT16, BASE_DEC, VALS(smex_data_inversion_type), 0x03,
536 "SMEX Data Inversion", HFILL }
538 { &hf_smex_frame_sync,
539 { "Frame Sync", "smex.frame_sync",
540 FT_UINT16, BASE_DEC, VALS(smex_frame_sync_mode), 0xc0,
541 "SMEX Frame Sync Flag", HFILL }
544 { "Data Direction", "smex.data_dir",
545 FT_UINT16, BASE_DEC, VALS(smex_data_direction), 0x20,
546 "SMEX Data Direction flag", HFILL }
548 { &hf_smex_data_class,
549 { "Data Class", "smex.data_class",
550 FT_UINT16, BASE_DEC, VALS(smex_data_class), 0x1f,
551 "SMEX Data Class", HFILL }
554 { "PB5 Flag", "smex.pb5",
555 FT_UINT16, BASE_DEC, NULL, 0x8000,
556 "SMEX PB5 Flag", HFILL }
559 { "Julian Day", "smex.jday",
560 FT_UINT16, BASE_DEC, NULL, PB5_JULIAN_DAY_MASK,
561 "SMEX Julian Day", HFILL }
564 { "Seconds", "smex.seconds",
565 FT_UINT24, BASE_DEC, NULL, PB5_SECONDS_MASK,
566 "SMEX Seconds", HFILL }
569 { "Milliseconds", "smex.msec",
570 FT_UINT16, BASE_DEC, NULL, PB5_MILLISECONDS_MASK,
571 "SMEX Milliseconds", HFILL }
574 { "Spare", "smex.spare",
575 FT_UINT16, BASE_DEC, NULL, 0x03f,
576 "SMEX Spare", HFILL }
582 { "Version", "vcdu.version",
583 FT_UINT16, BASE_DEC, NULL, 0xc0,
584 "VCDU Version", HFILL }
587 { "Space Craft ID", "vcdu.spid",
588 FT_UINT16, BASE_DEC, NULL, 0x3fc0,
589 "VCDU Space Craft ID", HFILL }
592 { "Virtual Channel ID", "vcdu.vcid",
593 FT_UINT16, BASE_DEC, NULL, 0x3f,
594 "VCDU Virtual Channel ID", HFILL }
597 { "Sequence Count", "vcdu.seq",
598 FT_UINT16, BASE_DEC, NULL, 0xffffff,
599 "VCDU Sequence Count", HFILL }
602 { "Replay Flag", "vcdu.replay",
603 FT_BOOLEAN, 8, NULL, 0x80,
604 "VCDU Replay Flag", HFILL }
607 /* not really part of the vcdu header, but it's easier this way */
609 { "First Header Pointer", "vcdu.fhp",
610 FT_UINT16, BASE_DEC, NULL, FHP_MASK,
611 "VCDU/MPDU First Header Pointer", HFILL }
614 { "Last Bit Pointer", "vcdu.lbp",
615 FT_UINT16, BASE_DEC, NULL, LBP_MASK,
616 "VCDU/BPDU Last Bit Pointer", HFILL }
620 /* Setup protocol subtree array */
621 static gint *ett[] = {
627 /* Register the protocol name and description */
628 proto_vcdu = proto_register_protocol("VCDU", "VCDU", "vcdu");
630 /* Required function calls to register the header fields and subtrees used */
631 proto_register_field_array(proto_vcdu, hf, array_length(hf));
632 proto_register_subtree_array(ett, array_length(ett));
637 /* If this dissector uses sub-dissector registration add a registration routine.
638 * This format is required because a script is used to find these routines and
639 * create the code that calls these routines.
642 proto_reg_handoff_vcdu(void)
644 register_dissector ( "vcdu", dissect_vcdu, proto_vcdu );
645 dissector_add ( "udp.port", 0, find_dissector("vcdu") );
646 ccsds_handle = find_dissector ( "ccsds" );