aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBrian Sipos <brian.sipos@gmail.com>2021-10-11 01:03:50 -0400
committerWireshark GitLab Utility <gerald+gitlab-utility@wireshark.org>2021-10-17 14:09:07 +0000
commitc36ce0b01b25eac9a5ecf377f48f2b33f0f49df0 (patch)
tree8ae3a0dc9b987df95f26769945d1ac21063e93e0
parent0e667117b43fda4aadc78bfd1402fe9516cf8d2a (diff)
TCPCLv4: Update TCPCL dissector to include version 4 from dtn-wireshark
Some enhancements and visual fixes to version 3 dissector are also included.
-rw-r--r--debian/libwireshark0.symbols1
-rw-r--r--docbook/release-notes.adoc1
-rw-r--r--epan/dissectors/CMakeLists.txt4
-rw-r--r--epan/dissectors/packet-tcpcl.c2154
-rw-r--r--epan/dissectors/packet-tcpcl.h242
-rw-r--r--epan/dissectors/packet-tcpclv3.c775
-rw-r--r--epan/dissectors/packet-tcpclv3.h101
-rw-r--r--test/captures/dtn_tcpclv3_bpv6_transfer.pcapngbin0 -> 4768 bytes
-rw-r--r--test/captures/dtn_tcpclv4_bpv7_transfer.pcapngbin0 -> 3960 bytes
-rw-r--r--test/suite_dissection.py19
10 files changed, 2419 insertions, 878 deletions
diff --git a/debian/libwireshark0.symbols b/debian/libwireshark0.symbols
index 16c0cb1ba9..7470df9f3e 100644
--- a/debian/libwireshark0.symbols
+++ b/debian/libwireshark0.symbols
@@ -1617,6 +1617,7 @@ libwireshark.so.0 libwireshark0 #MINVER#
tap_register_plugin@Base 2.5.0
tcp_dissect_pdus@Base 1.9.1
tcp_port_to_display@Base 1.99.2
+ tcpcl_dissect_ctx_get@Base 3.7.0
tfs_accept_reject@Base 1.9.1
tfs_accepted_not_accepted@Base 1.9.1
tfs_ack_nack@Base 1.9.1
diff --git a/docbook/release-notes.adoc b/docbook/release-notes.adoc
index c4b84ad415..7253ac654c 100644
--- a/docbook/release-notes.adoc
+++ b/docbook/release-notes.adoc
@@ -130,6 +130,7 @@ Bluetooth Link Manager Protocol (BT LMP)
Bundle Protocol version 7 (BPv7)
Bundle Protocol version 7 Security (BPSec)
CBOR Object Signing and Encryption (COSE)
+DTN TCP Convergence Layer version 4 (TCPCLv4)
E2 Application Protocol (E2AP)
Event Tracing for Windows (ETW)
High-Performance Connectivity Tracer (HiPerConTracer)
diff --git a/epan/dissectors/CMakeLists.txt b/epan/dissectors/CMakeLists.txt
index 0374df2315..0c1e4e1738 100644
--- a/epan/dissectors/CMakeLists.txt
+++ b/epan/dissectors/CMakeLists.txt
@@ -584,7 +584,7 @@ set(DISSECTOR_PUBLIC_HEADERS
packet-tacacs.h
packet-tcap.h
packet-tcp.h
- packet-tcpclv3.h
+ packet-tcpcl.h
packet-tetra.h
packet-thrift.h
packet-tls-utils.h
@@ -1838,7 +1838,7 @@ set(DISSECTOR_SRC
${CMAKE_CURRENT_SOURCE_DIR}/packet-tapa.c
${CMAKE_CURRENT_SOURCE_DIR}/packet-tcg-cp-oids.c
${CMAKE_CURRENT_SOURCE_DIR}/packet-tcp.c
- ${CMAKE_CURRENT_SOURCE_DIR}/packet-tcpclv3.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/packet-tcpcl.c
${CMAKE_CURRENT_SOURCE_DIR}/packet-tcpros.c
${CMAKE_CURRENT_SOURCE_DIR}/packet-tdmoe.c
${CMAKE_CURRENT_SOURCE_DIR}/packet-tdmop.c
diff --git a/epan/dissectors/packet-tcpcl.c b/epan/dissectors/packet-tcpcl.c
new file mode 100644
index 0000000000..d2565e1736
--- /dev/null
+++ b/epan/dissectors/packet-tcpcl.c
@@ -0,0 +1,2154 @@
+/* packet-tcpcl.c
+ * References:
+ * RFC 7242: https://tools.ietf.org/html/rfc7242
+ * TCPCLv4: https://www.ietf.org/archive/id/draft-ietf-dtn-tcpclv4-28.html
+ *
+ * TCPCLv4 portions copyright 2019-2021, Brian Sipos <brian.sipos@gmail.com>
+ * Copyright 2006-2007 The MITRE Corporation.
+ * All Rights Reserved.
+ * Approved for Public Release; Distribution Unlimited.
+ * Tracking Number 07-0090.
+ *
+ * The US Government will not be charged any license fee and/or royalties
+ * related to this software. Neither name of The MITRE Corporation; nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * Specification reference:
+ * RFC 5050
+ * https://tools.ietf.org/html/rfc5050
+ */
+
+/*
+ * Modifications were made to this file under designation MFS-33289-1 and
+ * are Copyright 2015 United States Government as represented by NASA
+ * Marshall Space Flight Center. All Rights Reserved.
+ *
+ * Released under the GNU GPL with NASA legal approval granted 2016-06-10.
+ *
+ * The subject software is provided "AS IS" WITHOUT ANY WARRANTY of any kind,
+ * either expressed, implied or statutory and this agreement does not,
+ * in any manner, constitute an endorsement by government agency of any
+ * results, designs or products resulting from use of the subject software.
+ * See the Agreement for the specific language governing permissions and
+ * limitations.
+ */
+
+#include "config.h"
+
+#include <inttypes.h>
+#include <stdio.h>
+#include <epan/packet.h>
+#include <epan/reassemble.h>
+#include <epan/expert.h>
+#include <epan/tvbuff-int.h>
+#include <epan/dissectors/packet-tls-utils.h>
+#include <epan/dissectors/packet-tcp.h>
+#include <epan/dissectors/packet-ber.h>
+#include <epan/dissectors/packet-bpv6.h>
+#include "packet-tcpcl.h"
+
+
+static const char magic[] = {'d', 't', 'n', '!'};
+
+static int proto_tcpcl = -1;
+/// Protocol column name
+static const char *const proto_name_tcpcl = "TCPCL";
+
+static gboolean tcpcl_desegment_transfer = TRUE;
+static gboolean tcpcl_analyze_sequence = TRUE;
+static gboolean tcpcl_decode_bundle = TRUE;
+
+/* For Reassembling TCP Convergence Layer segments */
+static reassembly_table xfer_reassembly_table;
+
+/// Dissector handles
+static dissector_handle_t tcpcl_handle = NULL;
+static dissector_handle_t tls_handle = NULL;
+static dissector_handle_t bundle_handle = NULL;
+
+/// Extension sub-dissectors
+static dissector_table_t sess_ext_dissectors = NULL;
+static dissector_table_t xfer_ext_dissectors = NULL;
+
+static const value_string v3_message_type_vals[] = {
+ {((TCPCLV3_DATA_SEGMENT>>4) & 0x0F), "DATA_SEGMENT"},
+ {((TCPCLV3_ACK_SEGMENT>>4) & 0x0F), "ACK_SEGMENT"},
+ {((TCPCLV3_REFUSE_BUNDLE>>4) & 0x0F), "REFUSE_BUNDLE"},
+ {((TCPCLV3_KEEP_ALIVE>>4) & 0x0F), "KEEPALIVE"},
+ {((TCPCLV3_SHUTDOWN>>4) & 0x0F), "SHUTDOWN"},
+ {((TCPCLV3_LENGTH>>4) & 0x0F), "LENGTH"},
+ {0, NULL}
+};
+
+/* Refuse-Bundle Reason-Code Flags as per RFC-7242: Section-5.4 */
+static const value_string v3_refuse_reason_code[] = {
+ {TCPCLV3_REFUSE_REASON_UNKNOWN, "Reason for refusal is unknown"},
+ {TCPCLV3_REFUSE_REASON_RX_COMPLETE, "Complete Bundle Received"},
+ {TCPCLV3_REFUSE_REASON_RX_EXHAUSTED, "Receiver's resources exhausted"},
+ {TCPCLV3_REFUSE_REASON_RX_RETRANSMIT, "Receiver expects re-transmission of bundle"},
+ {0, NULL}
+};
+
+static const value_string v4_message_type_vals[]={
+ {TCPCLV4_MSGTYPE_SESS_INIT, "SESS_INIT"},
+ {TCPCLV4_MSGTYPE_SESS_TERM, "SESS_TERM"},
+ {TCPCLV4_MSGTYPE_MSG_REJECT, "MSG_REJECT"},
+ {TCPCLV4_MSGTYPE_KEEPALIVE, "KEEPALIVE"},
+ {TCPCLV4_MSGTYPE_XFER_SEGMENT, "XFER_SEGMENT"},
+ {TCPCLV4_MSGTYPE_XFER_ACK, "XFER_ACK"},
+ {TCPCLV4_MSGTYPE_XFER_REFUSE, "XFER_REFUSE"},
+ {0, NULL},
+};
+
+static const value_string v4_sess_term_reason_vals[]={
+ {0x00, "Unknown"},
+ {0x01, "Idle timeout"},
+ {0x02, "Version mismatch"},
+ {0x03, "Busy"},
+ {0x04, "Contact Failure"},
+ {0x05, "Resource Exhaustion"},
+ {0, NULL},
+};
+
+static const value_string v4_xfer_refuse_reason_vals[]={
+ {0x00, "Unknown"},
+ {0x01, "Completed"},
+ {0x02, "No Resources"},
+ {0x03, "Retransmit"},
+ {0x04, "Not Acceptable"},
+ {0x05, "Extension Failure"},
+ {0, NULL},
+};
+
+static const value_string v4_msg_reject_reason_vals[]={
+ {0x00, "reserved"},
+ {0x01, "Message Type Unknown"},
+ {0x02, "Message Unsupported"},
+ {0x03, "Message Unexpected"},
+ {0, NULL},
+};
+
+static int hf_chdr_tree = -1;
+static int hf_chdr_magic = -1;
+static int hf_chdr_version = -1;
+static int hf_chdr_related = -1;
+
+/* TCP Convergence Header Variables */
+static int hf_tcpclv3_mhdr = -1;
+static int hf_tcpclv3_pkt_type = -1;
+
+/* Refuse-Bundle reason code */
+static int hf_tcpclv3_refuse_reason_code = -1;
+
+static int hf_tcpclv3_chdr_flags = -1;
+static int hf_tcpclv3_chdr_keep_alive = -1;
+static int hf_tcpclv3_chdr_flags_ack_req = -1;
+static int hf_tcpclv3_chdr_flags_frag_enable = -1;
+static int hf_tcpclv3_chdr_flags_nak = -1;
+static int hf_tcpclv3_chdr_local_eid_length = -1;
+static int hf_tcpclv3_chdr_local_eid = -1;
+
+/* TCP Convergence Data Header Variables */
+static int hf_tcpclv3_data_procflags = -1;
+static int hf_tcpclv3_data_procflags_start = -1;
+static int hf_tcpclv3_data_procflags_end = -1;
+static int hf_tcpclv3_xfer_id = -1;
+static int hf_tcpclv3_data_segment_length = -1;
+static int hf_tcpclv3_data_segment_data = -1;
+
+/* TCP Convergence Ack Variables */
+static int hf_tcpclv3_ack_length = -1;
+
+/* TCP Convergence Shutdown Header Variables */
+static int hf_tcpclv3_shutdown_flags = -1;
+static int hf_tcpclv3_shutdown_flags_reason = -1;
+static int hf_tcpclv3_shutdown_flags_delay = -1;
+static int hf_tcpclv3_shutdown_reason = -1;
+static int hf_tcpclv3_shutdown_delay = -1;
+
+static int hf_tcpclv4_chdr_flags = -1;
+static int hf_tcpclv4_chdr_flags_cantls = -1;
+static int hf_tcpclv4_negotiate_use_tls = -1;
+
+static int hf_tcpclv4_mhdr_tree = -1;
+static int hf_tcpclv4_mhdr_type = -1;
+static int hf_tcpclv4_sess_init_keepalive = -1;
+static int hf_tcpclv4_sess_init_seg_mru = -1;
+static int hf_tcpclv4_sess_init_xfer_mru = -1;
+static int hf_tcpclv4_sess_init_nodeid_len = -1;
+static int hf_tcpclv4_sess_init_nodeid_data = -1;
+static int hf_tcpclv4_sess_init_extlist_len = -1;
+static int hf_tcpclv4_sess_init_related = -1;
+static int hf_tcpclv4_negotiate_keepalive = -1;
+
+static int hf_tcpclv4_sess_term_flags = -1;
+static int hf_tcpclv4_sess_term_flags_reply = -1;
+static int hf_tcpclv4_sess_term_reason = -1;
+static int hf_tcpclv4_sess_term_related = -1;
+
+static int hf_tcpclv4_sessext_tree = -1;
+static int hf_tcpclv4_sessext_flags = -1;
+static int hf_tcpclv4_sessext_flags_crit = -1;
+static int hf_tcpclv4_sessext_type = -1;
+static int hf_tcpclv4_sessext_len = -1;
+static int hf_tcpclv4_sessext_data = -1;
+
+static int hf_tcpclv4_xferext_tree = -1;
+static int hf_tcpclv4_xferext_flags = -1;
+static int hf_tcpclv4_xferext_flags_crit = -1;
+static int hf_tcpclv4_xferext_type = -1;
+static int hf_tcpclv4_xferext_len = -1;
+static int hf_tcpclv4_xferext_data = -1;
+
+static int hf_tcpclv4_xfer_flags = -1;
+static int hf_tcpclv4_xfer_flags_start = -1;
+static int hf_tcpclv4_xfer_flags_end = -1;
+static int hf_tcpclv4_xfer_id = -1;
+static int hf_tcpclv4_xfer_total_len = -1;
+static int hf_tcpclv4_xfer_segment_extlist_len = -1;
+static int hf_tcpclv4_xfer_segment_data_len = -1;
+static int hf_tcpclv4_xfer_segment_data = -1;
+static int hf_tcpclv4_xfer_segment_seen_len = -1;
+static int hf_tcpclv4_xfer_segment_related_start = -1;
+static int hf_tcpclv4_xfer_segment_time_start = -1;
+static int hf_tcpclv4_xfer_segment_related_ack = -1;
+static int hf_tcpclv4_xfer_segment_time_diff = -1;
+static int hf_tcpclv4_xfer_ack_ack_len = -1;
+static int hf_tcpclv4_xfer_ack_related_start = -1;
+static int hf_tcpclv4_xfer_ack_time_start = -1;
+static int hf_tcpclv4_xfer_ack_related_seg = -1;
+static int hf_tcpclv4_xfer_ack_time_diff = -1;
+static int hf_tcpclv4_xfer_refuse_reason = -1;
+static int hf_tcpclv4_xfer_refuse_related_seg = -1;
+static int hf_tcpclv4_msg_reject_reason = -1;
+static int hf_tcpclv4_msg_reject_head = -1;
+
+static int hf_tcpclv4_xferext_transferlen_total_len = -1;
+
+static int hf_othername_bundleeid = -1;
+
+/*TCP Convergence Layer Reassembly boilerplate*/
+static int hf_xfer_fragments = -1;
+static int hf_xfer_fragment = -1;
+static int hf_xfer_fragment_overlap = -1;
+static int hf_xfer_fragment_overlap_conflicts = -1;
+static int hf_xfer_fragment_multiple_tails = -1;
+static int hf_xfer_fragment_too_long_fragment = -1;
+static int hf_xfer_fragment_error = -1;
+static int hf_xfer_fragment_count = -1;
+static int hf_xfer_reassembled_in = -1;
+static int hf_xfer_reassembled_length = -1;
+static int hf_xfer_reassembled_data = -1;
+
+static hf_register_info hf_tcpcl[] = {
+ {&hf_chdr_tree, {"Contact Header", "tcpcl.contact_hdr", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}},
+ {&hf_chdr_magic, {"Protocol Magic", "tcpcl.contact_hdr.magic", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL}},
+ {&hf_chdr_version, {"Version", "tcpcl.contact_hdr.version", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL}},
+ {&hf_chdr_related, {"Related Header", "tcpcl.contact_hdr.related", FT_FRAMENUM, BASE_NONE, NULL, 0x0, NULL, HFILL}},
+
+ {&hf_tcpclv3_mhdr,
+ {"TCPCLv3 Header", "tcpcl.mhdr",
+ FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}
+ },
+ {&hf_tcpclv3_pkt_type,
+ {"Message Type", "tcpcl.pkt_type",
+ FT_UINT8, BASE_DEC, VALS(v3_message_type_vals), 0xF0, NULL, HFILL}
+ },
+ {&hf_tcpclv3_refuse_reason_code,
+ {"Reason-Code", "tcpcl.refuse.reason_code",
+ FT_UINT8, BASE_DEC, VALS(v3_refuse_reason_code), 0x0F, NULL, HFILL}
+ },
+ {&hf_tcpclv3_data_procflags,
+ {"Data Flags", "tcpcl.data.proc.flag",
+ FT_UINT8, BASE_HEX, NULL, TCPCLV3_DATA_FLAGS, NULL, HFILL}
+ },
+ {&hf_tcpclv3_data_procflags_start,
+ {"Segment contains start of bundle", "tcpcl.data.proc.start",
+ FT_BOOLEAN, 8, TFS(&tfs_set_notset), TCPCLV3_DATA_START_FLAG, NULL, HFILL}
+ },
+ {&hf_tcpclv3_data_procflags_end,
+ {"Segment contains end of Bundle", "tcpcl.data.proc.end",
+ FT_BOOLEAN, 8, TFS(&tfs_set_notset), TCPCLV3_DATA_END_FLAG, NULL, HFILL}
+ },
+ {&hf_tcpclv3_xfer_id, {"Implied Transfer ID", "tcpcl.xfer_id", FT_UINT64, BASE_HEX, NULL, 0x0, NULL, HFILL}},
+ {&hf_tcpclv3_data_segment_length,
+ {"Segment Length", "tcpcl.data.length",
+ FT_INT32, BASE_DEC, NULL, 0x0, NULL, HFILL}
+ },
+ {&hf_tcpclv3_data_segment_data,
+ {"Segment Data", "tcpcl.data",
+ FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL}
+ },
+ {&hf_tcpclv3_shutdown_flags,
+ {"TCP Convergence Shutdown Flags", "tcpcl.shutdown.flags",
+ FT_UINT8, BASE_HEX, NULL, TCPCLV3_SHUTDOWN_FLAGS, NULL, HFILL}
+ },
+ {&hf_tcpclv3_shutdown_flags_reason,
+ {"Shutdown includes Reason Code", "tcpcl.shutdown.reason.flag",
+ FT_BOOLEAN, 8, TFS(&tfs_set_notset), TCPCLV3_SHUTDOWN_REASON, NULL, HFILL}
+ },
+ {&hf_tcpclv3_shutdown_flags_delay,
+ {"Shutdown includes Reconnection Delay", "tcpcl.shutdown.delay.flag",
+ FT_BOOLEAN, 8, TFS(&tfs_set_notset), TCPCLV3_SHUTDOWN_DELAY, NULL, HFILL}
+ },
+ {&hf_tcpclv3_shutdown_reason,
+ {"Shutdown Reason Code", "tcpcl.shutdown.reason",
+ FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL}
+ },
+ {&hf_tcpclv3_shutdown_delay,
+ {"Shutdown Reconnection Delay", "tcpcl.shutdown.delay",
+ FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL}
+ },
+ {&hf_tcpclv3_ack_length,
+ {"Ack Length", "tcpcl.ack.length",
+ FT_INT32, BASE_DEC, NULL, 0x0, NULL, HFILL}
+ },
+ {&hf_tcpclv3_chdr_flags,
+ {"Flags", "tcpcl.contact_hdr.flags",
+ FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL}
+ },
+ {&hf_tcpclv3_chdr_flags_ack_req,
+ {"Bundle Acks Requested", "tcpcl.contact_hdr.flags.ackreq",
+ FT_BOOLEAN, 8, TFS(&tfs_set_notset), TCPCLV3_BUNDLE_ACK_FLAG, NULL, HFILL}
+ },
+ {&hf_tcpclv3_chdr_flags_frag_enable,
+ {"Reactive Fragmentation Enabled", "tcpcl.contact_hdr.flags.fragen",
+ FT_BOOLEAN, 8, TFS(&tfs_set_notset), TCPCLV3_REACTIVE_FRAG_FLAG, NULL, HFILL}
+ },
+ {&hf_tcpclv3_chdr_flags_nak,
+ {"Support Negative Acknowledgements", "tcpcl.contact_hdr.flags.nak",
+ FT_BOOLEAN, 8, TFS(&tfs_set_notset), TCPCLV3_CONNECTOR_RCVR_FLAG, NULL, HFILL}
+ },
+ {&hf_tcpclv3_chdr_keep_alive,
+ {"Keep Alive", "tcpcl.contact_hdr.keep_alive",
+ FT_UINT16, BASE_DEC|BASE_UNIT_STRING, &units_seconds, 0x0, NULL, HFILL}
+ },
+ {&hf_tcpclv3_chdr_local_eid,
+ {"Local EID", "tcpcl.contact_hdr.local_eid",
+ FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL}
+ },
+ {&hf_tcpclv3_chdr_local_eid_length,
+ {"Local EID Length", "tcpcl.contact_hdr.local_eid_length",
+ FT_INT32, BASE_DEC, NULL, 0x0, NULL, HFILL}
+ },
+
+ {&hf_tcpclv4_chdr_flags, {"Contact Flags", "tcpcl.v4.chdr.flags", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL}},
+ {&hf_tcpclv4_chdr_flags_cantls, {"CAN_TLS", "tcpcl.v4.chdr.flags.can_tls", FT_BOOLEAN, 8, TFS(&tfs_set_notset), TCPCLV4_CONTACT_FLAG_CANTLS, NULL, HFILL}},
+ // Contact negotiation results
+ {&hf_tcpclv4_negotiate_use_tls, {"Negotiated Use TLS", "tcpcl.v4.negotiated.use_tls", FT_BOOLEAN, BASE_NONE, NULL, 0x0, NULL, HFILL}},
+
+ {&hf_tcpclv4_mhdr_tree, {"TCPCLv4 Message", "tcpcl.v4.mhdr", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}},
+ {&hf_tcpclv4_mhdr_type, {"Message Type", "tcpcl.v4.mhdr.type", FT_UINT8, BASE_HEX, VALS(v4_message_type_vals), 0x0, NULL, HFILL}},
+
+ // Session extension fields
+ {&hf_tcpclv4_sessext_tree, {"Session Extension Item", "tcpcl.v4.sessext", FT_PROTOCOL, BASE_NONE, NULL, 0x0, NULL, HFILL}},
+ {&hf_tcpclv4_sessext_flags, {"Item Flags", "tcpcl.v4.sessext.flags", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL}},
+ {&hf_tcpclv4_sessext_flags_crit, {"CRITICAL", "tcpcl.v4.sessext.flags.critical", FT_BOOLEAN, 8, TFS(&tfs_set_notset), TCPCLV4_EXTENSION_FLAG_CRITICAL, NULL, HFILL}},
+ {&hf_tcpclv4_sessext_type, {"Item Type", "tcpcl.v4.sessext.type", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL}},
+ {&hf_tcpclv4_sessext_len, {"Item Length", "tcpcl.v4.sessext.len", FT_UINT32, BASE_DEC|BASE_UNIT_STRING, &units_octet_octets, 0x0, NULL, HFILL}},
+ {&hf_tcpclv4_sessext_data, {"Type-Specific Data", "tcpcl.v4.sessext.data", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}},
+
+ // Transfer extension fields
+ {&hf_tcpclv4_xferext_tree, {"Transfer Extension Item", "tcpcl.v4.xferext", FT_PROTOCOL, BASE_NONE, NULL, 0x0, NULL, HFILL}},
+ {&hf_tcpclv4_xferext_flags, {"Item Flags", "tcpcl.v4.xferext.flags", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL}},
+ {&hf_tcpclv4_xferext_flags_crit, {"CRITICAL", "tcpcl.v4.xferext.flags.critical", FT_BOOLEAN, 8, TFS(&tfs_set_notset), TCPCLV4_EXTENSION_FLAG_CRITICAL, NULL, HFILL}},
+ {&hf_tcpclv4_xferext_type, {"Item Type", "tcpcl.v4.xferext.type", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL}},
+ {&hf_tcpclv4_xferext_len, {"Item Length", "tcpcl.v4.xferext.len", FT_UINT32, BASE_DEC|BASE_UNIT_STRING, &units_octet_octets, 0x0, NULL, HFILL}},
+ {&hf_tcpclv4_xferext_data, {"Type-Specific Data", "tcpcl.v4.xferext.data", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}},
+
+ // SESS_INIT fields
+ {&hf_tcpclv4_sess_init_keepalive, {"Keepalive Interval", "tcpcl.v4.sess_init.keepalive", FT_UINT16, BASE_DEC|BASE_UNIT_STRING, &units_seconds, 0x0, NULL, HFILL}},
+ {&hf_tcpclv4_sess_init_seg_mru, {"Segment MRU", "tcpcl.v4.sess_init.seg_mru", FT_UINT64, BASE_DEC|BASE_UNIT_STRING, &units_octet_octets, 0x0, NULL, HFILL}},
+ {&hf_tcpclv4_sess_init_xfer_mru, {"Transfer MRU", "tcpcl.v4.sess_init.xfer_mru", FT_UINT64, BASE_DEC|BASE_UNIT_STRING, &units_octet_octets, 0x0, NULL, HFILL}},
+ {&hf_tcpclv4_sess_init_nodeid_len, {"Node ID Length", "tcpcl.v4.sess_init.nodeid_len", FT_UINT16, BASE_DEC|BASE_UNIT_STRING, &units_octet_octets, 0x0, NULL, HFILL}},
+ {&hf_tcpclv4_sess_init_nodeid_data, {"Node ID Data (UTF8)", "tcpcl.v4.sess_init.nodeid_data", FT_STRING, STR_UNICODE, NULL, 0x0, NULL, HFILL}},
+ {&hf_tcpclv4_sess_init_extlist_len, {"Extension Items Length", "tcpcl.v4.sess_init.extlist_len", FT_UINT32, BASE_DEC|BASE_UNIT_STRING, &units_octet_octets, 0x0, NULL, HFILL}},
+ {&hf_tcpclv4_sess_init_related, {"Related SESS_INIT", "tcpcl.v4.sess_init.related", FT_FRAMENUM, BASE_NONE, NULL, 0x0, NULL, HFILL}},
+ // Session negotiation results
+ {&hf_tcpclv4_negotiate_keepalive, {"Negotiated Keepalive Interval", "tcpcl.v4.negotiated.keepalive", FT_UINT16, BASE_DEC|BASE_UNIT_STRING, &units_seconds, 0x0, NULL, HFILL}},
+ // SESS_TERM fields
+ {&hf_tcpclv4_sess_term_flags, {"Flags", "tcpcl.v4.sess_term.flags", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL}},
+ {&hf_tcpclv4_sess_term_flags_reply, {"REPLY", "tcpcl.v4.sess_term.flags.reply", FT_BOOLEAN, 8, TFS(&tfs_set_notset), TCPCLV4_SESS_TERM_FLAG_REPLY, NULL, HFILL}},
+ {&hf_tcpclv4_sess_term_reason, {"Reason", "tcpcl.v4.ses_term.reason", FT_UINT8, BASE_DEC, VALS(v4_sess_term_reason_vals), 0x0, NULL, HFILL}},
+ {&hf_tcpclv4_sess_term_related, {"Related SESS_TERM", "tcpcl.v4.ses_term.related", FT_FRAMENUM, BASE_NONE, NULL, 0x0, NULL, HFILL}},
+
+ // Common transfer fields
+ {&hf_tcpclv4_xfer_flags, {"Transfer Flags", "tcpcl.v4.xfer_flags", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL}},
+ {&hf_tcpclv4_xfer_flags_start, {"START", "tcpcl.v4.xfer_flags.start", FT_BOOLEAN, 8, TFS(&tfs_set_notset), TCPCLV4_TRANSFER_FLAG_START, NULL, HFILL}},
+ {&hf_tcpclv4_xfer_flags_end, {"END", "tcpcl.v4.xfer_flags.end", FT_BOOLEAN, 8, TFS(&tfs_set_notset), TCPCLV4_TRANSFER_FLAG_END, NULL, HFILL}},
+ {&hf_tcpclv4_xfer_id, {"Transfer ID", "tcpcl.v4.xfer_id", FT_UINT64, BASE_HEX, NULL, 0x0, NULL, HFILL}},
+ {&hf_tcpclv4_xfer_total_len, {"Expected Total Length", "tcpcl.v4.xfer.total_len", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL}},
+ // XFER_SEGMENT fields
+ {&hf_tcpclv4_xfer_segment_extlist_len, {"Extension Items Length", "tcpcl.v4.xfer_segment.extlist_len", FT_UINT32, BASE_DEC|BASE_UNIT_STRING, &units_octet_octets, 0x0, NULL, HFILL}},
+ {&hf_tcpclv4_xfer_segment_data_len, {"Segment Length", "tcpcl.v4.xfer_segment.data_len", FT_UINT64, BASE_DEC|BASE_UNIT_STRING, &units_octet_octets, 0x0, NULL, HFILL}},
+ {&hf_tcpclv4_xfer_segment_data, {"Segment Data", "tcpcl.v4.xfer_segment.data", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL}},
+ {&hf_tcpclv4_xfer_segment_seen_len, {"Seen Length", "tcpcl.v4.xfer_segment.seen_len", FT_UINT64, BASE_DEC|BASE_UNIT_STRING, &units_octet_octets, 0x0, NULL, HFILL}},
+ {&hf_tcpclv4_xfer_segment_related_start, {"Related XFER_SEGMENT start", "tcpcl.v4.xfer_segment.related_start", FT_FRAMENUM, BASE_NONE, NULL, 0x0, NULL, HFILL}},
+ {&hf_tcpclv4_xfer_segment_time_start, {"Time since transfer Start", "tcpcl.v4.xfer_segment.time_since_start", FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0, NULL, HFILL}},
+ {&hf_tcpclv4_xfer_segment_related_ack, {"Related XFER_ACK", "tcpcl.v4.xfer_segment.related_ack", FT_FRAMENUM, BASE_NONE, NULL, 0x0, NULL, HFILL}},
+ {&hf_tcpclv4_xfer_segment_time_diff, {"Acknowledgment Time", "tcpcl.v4.xfer_segment.time_diff", FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0, NULL, HFILL}},
+ // XFER_ACK fields
+ {&hf_tcpclv4_xfer_ack_ack_len, {"Acknowledged Length", "tcpcl.v4.xfer_ack.ack_len", FT_UINT64, BASE_DEC|BASE_UNIT_STRING, &units_octet_octets, 0x0, NULL, HFILL}},
+ {&hf_tcpclv4_xfer_ack_related_start, {"Related XFER_SEGMENT start", "tcpcl.v4.xfer_ack.related_start", FT_FRAMENUM, BASE_NONE, NULL, 0x0, NULL, HFILL}},
+ {&hf_tcpclv4_xfer_ack_time_start, {"Time since transfer Start", "tcpcl.v4.xfer_ack.time_since_start", FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0, NULL, HFILL}},
+ {&hf_tcpclv4_xfer_ack_related_seg, {"Related XFER_SEGMENT", "tcpcl.v4.xfer_ack.related_seg", FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_ACK), 0x0, NULL, HFILL}},
+ {&hf_tcpclv4_xfer_ack_time_diff, {"Acknowledgment Time", "tcpcl.v4.xfer_ack.time_diff", FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0, NULL, HFILL}},
+ // XFER_REFUSE fields
+ {&hf_tcpclv4_xfer_refuse_reason, {"Reason", "tcpcl.v4.xfer_refuse.reason", FT_UINT8, BASE_DEC, VALS(v4_xfer_refuse_reason_vals), 0x0, NULL, HFILL}},
+ {&hf_tcpclv4_xfer_refuse_related_seg, {"Related XFER_SEGMENT", "tcpcl.v4.xfer_refuse.related_seg", FT_FRAMENUM, BASE_NONE, VALS(v4_xfer_refuse_reason_vals), 0x0, NULL, HFILL}},
+ // MSG_REJECT fields
+ {&hf_tcpclv4_msg_reject_reason, {"Reason", "tcpcl.v4.msg_reject.reason", FT_UINT8, BASE_DEC, VALS(v4_msg_reject_reason_vals), 0x0, NULL, HFILL}},
+ {&hf_tcpclv4_msg_reject_head, {"Rejected Type", "tcpcl.v4.msg_reject.head", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL}},
+
+ // Specific extensions
+ {&hf_tcpclv4_xferext_transferlen_total_len, {"Total Length", "tcpcl.v4.xferext.transfer_length.total_len", FT_UINT64, BASE_DEC|BASE_UNIT_STRING, &units_octet_octets, 0x0, NULL, HFILL}},
+ // PKIX other name form
+ {&hf_othername_bundleeid, {"BundleEID", "tcpcl.v4.BundleEID", FT_STRING, STR_ASCII, NULL, 0x0, NULL, HFILL}},
+
+ {&hf_xfer_fragments,
+ {"Transfer fragments", "tcpcl.xfer.fragments",
+ FT_NONE, BASE_NONE, NULL, 0x00, NULL, HFILL } },
+ {&hf_xfer_fragment,
+ {"Transfer fragment", "tcpcl.xfer.fragment",
+ FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL } },
+ {&hf_xfer_fragment_overlap,
+ {"Transfer fragment overlap", "tcpcl.xfer.fragment.overlap",
+ FT_BOOLEAN, 0, NULL, 0x00, NULL, HFILL } },
+ {&hf_xfer_fragment_overlap_conflicts,
+ {"Transfer fragment overlapping with conflicting data",
+ "tcpcl.xfer.fragment.overlap.conflicts",
+ FT_BOOLEAN, 0, NULL, 0x00, NULL, HFILL } },
+ {&hf_xfer_fragment_multiple_tails,
+ {"Message has multiple tail fragments",
+ "tcpcl.xfer.fragment.multiple_tails",
+ FT_BOOLEAN, 0, NULL, 0x00, NULL, HFILL } },
+ {&hf_xfer_fragment_too_long_fragment,
+ {"Transfer fragment too long", "tcpcl.xfer.fragment.too_long_fragment",
+ FT_BOOLEAN, 0, NULL, 0x00, NULL, HFILL } },
+ {&hf_xfer_fragment_error,
+ {"Transfer defragmentation error", "tcpcl.xfer.fragment.error",
+ FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL } },
+ {&hf_xfer_fragment_count,
+ {"Transfer fragment count", "tcpcl.xfer.fragment.count",
+ FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL } },
+ {&hf_xfer_reassembled_in,
+ {"Reassembled in", "tcpcl.xfer.reassembled.in",
+ FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL } },
+ {&hf_xfer_reassembled_length,
+ {"Reassembled length", "tcpcl.xfer.reassembled.length",
+ FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL } },
+ {&hf_xfer_reassembled_data,
+ {"Reassembled data", "tcpcl.xfer.reassembled.data",
+ FT_BYTES, BASE_NONE, NULL, 0x00, NULL, HFILL } },
+
+};
+
+static int *const v3_chdr_flags[] = {
+ &hf_tcpclv3_chdr_flags_ack_req,
+ &hf_tcpclv3_chdr_flags_frag_enable,
+ &hf_tcpclv3_chdr_flags_nak,
+ NULL
+};
+
+static int *const v3_data_procflags[] = {
+ &hf_tcpclv3_data_procflags_start,
+ &hf_tcpclv3_data_procflags_end,
+ NULL
+};
+static int *const v4_chdr_flags[] = {
+ &hf_tcpclv4_chdr_flags_cantls,
+ NULL
+};
+static int *const v4_sess_term_flags[] = {
+ &hf_tcpclv4_sess_term_flags_reply,
+ NULL
+};
+static int *const v4_xfer_flags[] = {
+ &hf_tcpclv4_xfer_flags_start,
+ &hf_tcpclv4_xfer_flags_end,
+ NULL
+};
+static int *const v4_sessext_flags[] = {
+ &hf_tcpclv4_sessext_flags_crit,
+ NULL
+};
+static int *const v4_xferext_flags[] = {
+ &hf_tcpclv4_xferext_flags_crit,
+ NULL
+};
+
+/* Tree Node Variables */
+static gint ett_proto_tcpcl = -1;
+static gint ett_chdr = -1;
+static gint ett_tcpclv3_chdr_flags = -1;
+static gint ett_tcpclv3_mhdr = -1;
+static gint ett_tcpclv3_data_procflags = -1;
+static gint ett_tcpclv3_shutdown_flags = -1;
+static gint ett_xfer_fragment = -1;
+static gint ett_xfer_fragments = -1;
+static gint ett_tcpclv4_chdr_flags = -1;
+static gint ett_tcpclv4_mhdr = -1;
+static gint ett_tcpclv4_sess_term_flags = -1;
+static gint ett_tcpclv4_xfer_flags = -1;
+static gint ett_tcpclv4_sessext = -1;
+static gint ett_tcpclv4_sessext_flags = -1;
+static gint ett_tcpclv4_sessext_data = -1;
+static gint ett_tcpclv4_xferext = -1;
+static gint ett_tcpclv4_xferext_flags = -1;
+static gint ett_tcpclv4_xferext_data = -1;
+
+static gint *ett[] = {
+ &ett_proto_tcpcl,
+ &ett_chdr,
+ &ett_tcpclv3_chdr_flags,
+ &ett_tcpclv3_mhdr,
+ &ett_tcpclv3_data_procflags,
+ &ett_tcpclv3_shutdown_flags,
+ &ett_tcpclv4_chdr_flags,
+ &ett_tcpclv4_mhdr,
+ &ett_tcpclv4_sess_term_flags,
+ &ett_tcpclv4_xfer_flags,
+ &ett_tcpclv4_sessext,
+ &ett_tcpclv4_sessext_flags,
+ &ett_tcpclv4_sessext_data,
+ &ett_tcpclv4_xferext,
+ &ett_tcpclv4_xferext_flags,
+ &ett_tcpclv4_xferext_data,
+ &ett_xfer_fragment,
+ &ett_xfer_fragments,
+};
+
+static expert_field ei_invalid_magic = EI_INIT;
+static expert_field ei_invalid_version = EI_INIT;
+static expert_field ei_mismatch_version = EI_INIT;
+static expert_field ei_chdr_duplicate = EI_INIT;
+
+static expert_field ei_tcpclv3_invalid_msg_type = EI_INIT;
+static expert_field ei_tcpclv3_data_flags = EI_INIT;
+static expert_field ei_tcpclv3_segment_length = EI_INIT;
+static expert_field ei_tcpclv3_ack_length = EI_INIT;
+
+static expert_field ei_tcpclv4_invalid_msg_type = EI_INIT;
+static expert_field ei_tcpclv4_invalid_sessext_type = EI_INIT;
+static expert_field ei_tcpclv4_invalid_xferext_type = EI_INIT;
+static expert_field ei_tcpclv4_extitem_critical = EI_INIT;
+static expert_field ei_tcpclv4_sess_init_missing = EI_INIT;
+static expert_field ei_tcpclv4_sess_init_duplicate = EI_INIT;
+static expert_field ei_tcpclv4_sess_term_duplicate = EI_INIT;
+static expert_field ei_tcpclv4_sess_term_reply_flag = EI_INIT;
+static expert_field ei_tcpclv4_xfer_seg_over_seg_mru = EI_INIT;
+static expert_field ei_tcpclv4_xfer_seg_missing_start = EI_INIT;
+static expert_field ei_tcpclv4_xfer_seg_duplicate_start = EI_INIT;
+static expert_field ei_tcpclv4_xfer_seg_missing_end = EI_INIT;
+static expert_field ei_tcpclv4_xfer_seg_duplicate_end = EI_INIT;
+static expert_field ei_tcpclv4_xfer_seg_no_relation = EI_INIT;
+static expert_field ei_xfer_seg_large_xferid = EI_INIT;
+static expert_field ei_xfer_seg_over_total_len = EI_INIT;
+static expert_field ei_xfer_mismatch_total_len = EI_INIT;
+static expert_field ei_xfer_ack_mismatch_flags = EI_INIT;
+static expert_field ei_xfer_ack_no_relation = EI_INIT;
+static expert_field ei_tcpclv4_xfer_refuse_no_transfer = EI_INIT;
+static expert_field ei_tcpclv4_xferload_over_xfer_mru = EI_INIT;
+
+static ei_register_info ei_tcpcl[] = {
+ {&ei_invalid_magic, { "tcpcl.invalid_contact_magic", PI_PROTOCOL, PI_ERROR, "Magic string is invalid", EXPFILL}},
+ {&ei_invalid_version, { "tcpcl.invalid_contact_version", PI_PROTOCOL, PI_ERROR, "Protocol version not handled", EXPFILL}},
+ {&ei_mismatch_version, { "tcpcl.mismatch_contact_version", PI_PROTOCOL, PI_ERROR, "Protocol version mismatch", EXPFILL}},
+ {&ei_chdr_duplicate, { "tcpcl.contact_duplicate", PI_SEQUENCE, PI_ERROR, "Duplicate Contact Header", EXPFILL}},
+
+ {&ei_tcpclv3_invalid_msg_type, { "tcpcl.unknown_message_type", PI_UNDECODED, PI_ERROR, "Message type is unknown", EXPFILL}},
+ {&ei_tcpclv3_data_flags, { "tcpcl.data.flags.invalid", PI_PROTOCOL, PI_WARN, "Invalid TCP CL Data Segment Flags", EXPFILL }},
+ {&ei_tcpclv3_segment_length, { "tcpcl.data.length.invalid", PI_PROTOCOL, PI_ERROR, "Invalid Data Length", EXPFILL }},
+ {&ei_tcpclv3_ack_length, { "tcpcl.ack.length.error", PI_PROTOCOL, PI_WARN, "Ack Length: Error", EXPFILL }},
+
+ {&ei_tcpclv4_invalid_msg_type, { "tcpcl.v4.unknown_message_type", PI_UNDECODED, PI_ERROR, "Message type is unknown", EXPFILL}},
+ {&ei_tcpclv4_invalid_sessext_type, { "tcpcl.v4.unknown_sessext_type", PI_UNDECODED, PI_WARN, "Session Extension type is unknown", EXPFILL}},
+ {&ei_tcpclv4_invalid_xferext_type, { "tcpcl.v4.unknown_xferext_type", PI_UNDECODED, PI_WARN, "Transfer Extension type is unknown", EXPFILL}},
+ {&ei_tcpclv4_extitem_critical, { "tcpcl.v4.extitem_critical", PI_REQUEST_CODE, PI_CHAT, "Extension Item is critical", EXPFILL}},
+ {&ei_tcpclv4_sess_init_missing, { "tcpcl.v4.sess_init_missing", PI_SEQUENCE, PI_ERROR, "Expected SESS_INIT message first", EXPFILL}},
+ {&ei_tcpclv4_sess_init_duplicate, { "tcpcl.v4.sess_init_duplicate", PI_SEQUENCE, PI_ERROR, "Duplicate SESS_INIT message", EXPFILL}},
+ {&ei_tcpclv4_sess_term_duplicate, { "tcpcl.v4.sess_term_duplicate", PI_SEQUENCE, PI_ERROR, "Duplicate SESS_TERM message", EXPFILL}},
+ {&ei_tcpclv4_sess_term_reply_flag, { "tcpcl.v4.sess_term_reply_flag", PI_SEQUENCE, PI_ERROR, "Reply SESS_TERM missing flag", EXPFILL}},
+ {&ei_tcpclv4_xfer_seg_over_seg_mru, { "tcpcl.v4.xfer_seg_over_seg_mru", PI_PROTOCOL, PI_WARN, "Segment data size larger than peer MRU", EXPFILL}},
+ {&ei_tcpclv4_xfer_seg_missing_start, { "tcpcl.v4.xfer_seg_missing_start", PI_SEQUENCE, PI_ERROR, "First XFER_SEGMENT is missing START flag", EXPFILL}},
+ {&ei_tcpclv4_xfer_seg_duplicate_start, { "tcpcl.v4.xfer_seg_duplicate_start", PI_SEQUENCE, PI_ERROR, "Non-first XFER_SEGMENT has START flag", EXPFILL}},
+ {&ei_tcpclv4_xfer_seg_missing_end, { "tcpcl.v4.xfer_seg_missing_end", PI_SEQUENCE, PI_ERROR, "Last XFER_SEGMENT is missing END flag", EXPFILL}},
+ {&ei_tcpclv4_xfer_seg_duplicate_end, { "tcpcl.v4.xfer_seg_duplicate_end", PI_SEQUENCE, PI_ERROR, "Non-last XFER_SEGMENT has END flag", EXPFILL}},
+ {&ei_tcpclv4_xfer_seg_no_relation, { "tcpcl.v4.xfer_seg_no_relation", PI_SEQUENCE, PI_NOTE, "XFER_SEGMENT has no related XFER_ACK", EXPFILL}},
+ {&ei_tcpclv4_xfer_refuse_no_transfer, { "tcpcl.v4.xfer_refuse_no_transfer", PI_SEQUENCE, PI_NOTE, "XFER_REFUSE has no related XFER_SEGMENT(s)", EXPFILL}},
+ {&ei_tcpclv4_xferload_over_xfer_mru, { "tcpcl.v4.xferload_over_xfer_mru", PI_SEQUENCE, PI_NOTE, "Transfer larger than peer MRU", EXPFILL}},
+ {&ei_xfer_seg_large_xferid, { "tcpcl.xfer_seg_large_xferid", PI_REASSEMBLE, PI_WARN, "XFER_SEGMENT has a transfer ID larger than Wireshark can handle", EXPFILL}},
+ {&ei_xfer_seg_over_total_len, { "tcpcl.xfer_seg_over_total_len", PI_SEQUENCE, PI_ERROR, "XFER_SEGMENT has accumulated length beyond the Transfer Length extension", EXPFILL}},
+ {&ei_xfer_mismatch_total_len, { "tcpcl.xfer_mismatch_total_len", PI_SEQUENCE, PI_ERROR, "Transfer has total length different than the Transfer Length extension", EXPFILL}},
+ {&ei_xfer_ack_mismatch_flags, { "tcpcl.xfer_ack_mismatch_flags", PI_SEQUENCE, PI_ERROR, "XFER_ACK does not have flags matching XFER_SEGMENT", EXPFILL}},
+ {&ei_xfer_ack_no_relation, { "tcpcl.xfer_ack_no_relation", PI_SEQUENCE, PI_NOTE, "XFER_ACK has no related XFER_SEGMENT", EXPFILL}},
+};
+
+static const fragment_items xfer_frag_items = {
+ /*Fragment subtrees*/
+ &ett_xfer_fragment,
+ &ett_xfer_fragments,
+ /*Fragment Fields*/
+ &hf_xfer_fragments,
+ &hf_xfer_fragment,
+ &hf_xfer_fragment_overlap,
+ &hf_xfer_fragment_overlap_conflicts,
+ &hf_xfer_fragment_multiple_tails,
+ &hf_xfer_fragment_too_long_fragment,
+ &hf_xfer_fragment_error,
+ &hf_xfer_fragment_count,
+ /*Reassembled in field*/
+ &hf_xfer_reassembled_in,
+ /*Reassembled length field*/
+ &hf_xfer_reassembled_length,
+ /* Reassembled data field */
+ &hf_xfer_reassembled_data,
+ /*Tag*/
+ "Transfer fragments"
+};
+
+static void tcpcl_frame_loc_init(tcpcl_frame_loc_t *loc, const packet_info *pinfo, tvbuff_t *tvb, const gint offset) {
+ loc->frame_num = pinfo->num;
+ // This is a messy way to determine the index,
+ // but no other public functions allow determining how two TVB are related
+ loc->src_ix = -1;
+ for(GSList *srcit = pinfo->data_src; srcit != NULL; srcit = g_slist_next(srcit)) {
+ ++(loc->src_ix);
+ struct data_source *src = srcit->data;
+ if (get_data_source_tvb(src)->real_data == tvb->real_data) {
+ break;
+ }
+ }
+ loc->raw_offset = tvb_raw_offset(tvb) + offset;
+}
+
+/** Construct a new object on the file allocator.
+ */
+static tcpcl_frame_loc_t * tcpcl_frame_loc_new(wmem_allocator_t *alloc, const packet_info *pinfo, tvbuff_t *tvb, const gint offset) {
+ tcpcl_frame_loc_t *obj = wmem_new(alloc, tcpcl_frame_loc_t);
+ tcpcl_frame_loc_init(obj, pinfo, tvb, offset);
+ return obj;
+}
+
+/** Construct a new object on the file allocator.
+ */
+static tcpcl_frame_loc_t * tcpcl_frame_loc_clone(wmem_allocator_t *alloc, const tcpcl_frame_loc_t *loc) {
+ tcpcl_frame_loc_t *obj = wmem_new(alloc, tcpcl_frame_loc_t);
+ *obj = *loc;
+ return obj;
+}
+
+#define tcpcl_frame_loc_free wmem_free
+
+/** Function to match the GCompareDataFunc signature.
+ */
+static gint tcpcl_frame_loc_compare(gconstpointer a, gconstpointer b, gpointer user_data _U_) {
+ const tcpcl_frame_loc_t *aloc = a;
+ const tcpcl_frame_loc_t *bloc = b;
+
+ if (aloc->frame_num < bloc->frame_num) {
+ return -1;
+ }
+ else if (aloc->frame_num > bloc->frame_num) {
+ return 1;
+ }
+
+ if (aloc->raw_offset < bloc->raw_offset) {
+ return -1;
+ }
+ else if (aloc->raw_offset > bloc->raw_offset) {
+ return 1;
+ }
+ return 0;
+}
+
+/** Function to match the GCompareFunc signature.
+ */
+static gboolean tcpcl_frame_loc_equal(gconstpointer a, gconstpointer b) {
+ const tcpcl_frame_loc_t *aobj = a;
+ const tcpcl_frame_loc_t *bobj = b;
+ return (
+ (aobj->frame_num == bobj->frame_num)
+ && (aobj->raw_offset == bobj->raw_offset)
+ );
+}
+
+/** Function to match the GHashFunc signature.
+ */
+static guint tcpcl_frame_loc_hash(gconstpointer key) {
+ const tcpcl_frame_loc_t *obj = key;
+ return (
+ g_int_hash(&(obj->frame_num))
+ ^ g_int64_hash(&(obj->raw_offset))
+ );
+}
+
+struct tcpcl_ack_meta;
+typedef struct tcpcl_ack_meta tcpcl_ack_meta_t;
+struct tcpcl_seg_meta;
+typedef struct tcpcl_seg_meta tcpcl_seg_meta_t;
+
+struct tcpcl_seg_meta {
+ /// Location associated with this metadata
+ tcpcl_frame_loc_t frame_loc;
+ /// Timestamp on the frame (end time if reassembled)
+ nstime_t frame_time;
+ /// Copy of message flags
+ guint8 flags;
+ /// Total transfer length including this segment
+ guint64 seen_len;
+
+ /// Potential related start segment
+ tcpcl_seg_meta_t *related_start;
+ /// Potential related XFER_ACK
+ tcpcl_ack_meta_t *related_ack;
+};
+
+static tcpcl_seg_meta_t * tcpcl_seg_meta_new(const packet_info *pinfo, const tcpcl_frame_loc_t *loc) {
+ tcpcl_seg_meta_t *obj = wmem_new(wmem_file_scope(), tcpcl_seg_meta_t);
+ obj->frame_loc = *loc;
+ obj->frame_time = pinfo->abs_ts;
+ obj->flags = 0;
+ obj->seen_len = 0;
+ obj->related_start = NULL;
+ obj->related_ack = NULL;
+ return obj;
+}
+
+static void tcpcl_seg_meta_free(tcpcl_seg_meta_t *obj) {
+ wmem_free(wmem_file_scope(), obj);
+}
+
+/** Function to match the GCompareFunc signature.
+ */
+static gint tcpcl_seg_meta_compare_loc(gconstpointer a, gconstpointer b) {
+ return tcpcl_frame_loc_compare(
+ &(((tcpcl_seg_meta_t *)a)->frame_loc),
+ &(((tcpcl_seg_meta_t *)b)->frame_loc),
+ NULL
+ );
+}
+
+struct tcpcl_ack_meta {
+ /// Location associated with this metadata
+ tcpcl_frame_loc_t frame_loc;
+ /// Timestamp on the frame (end time if reassembled)
+ nstime_t frame_time;
+ /// Copy of message flags
+ guint8 flags;
+ /// Total acknowledged length including this ack
+ guint64 seen_len;
+
+ /// Potential related start segment
+ tcpcl_seg_meta_t *related_start;
+ /// Potential related XFER_SEGMENT
+ tcpcl_seg_meta_t *related_seg;
+};
+
+static tcpcl_ack_meta_t * tcpcl_ack_meta_new(const packet_info *pinfo, const tcpcl_frame_loc_t *loc) {
+ tcpcl_ack_meta_t *obj = wmem_new(wmem_file_scope(), tcpcl_ack_meta_t);
+ obj->frame_loc = *loc;
+ obj->frame_time = pinfo->abs_ts;
+ obj->flags = 0;
+ obj->seen_len = 0;
+ obj->related_start = NULL;
+ obj->related_seg = NULL;
+ return obj;
+}
+
+static void tcpcl_ack_meta_free(tcpcl_ack_meta_t *obj) {
+ wmem_free(wmem_file_scope(), obj);
+}
+
+/** Function to match the GCompareFunc signature.
+ */
+static gint tcpcl_ack_meta_compare_loc(gconstpointer a, gconstpointer b) {
+ return tcpcl_frame_loc_compare(
+ &(((tcpcl_seg_meta_t *)a)->frame_loc),
+ &(((tcpcl_seg_meta_t *)b)->frame_loc),
+ NULL
+ );
+}
+
+static tcpcl_transfer_t * tcpcl_transfer_new(void) {
+ tcpcl_transfer_t *obj = wmem_new(wmem_file_scope(), tcpcl_transfer_t);
+ obj->seg_list = wmem_list_new(wmem_file_scope());
+ obj->ack_list = wmem_list_new(wmem_file_scope());
+ obj->total_length = NULL;
+ return obj;
+}
+
+static tcpcl_transfer_t * get_or_create_transfer_t(wmem_map_t *table, const guint64 xfer_id) {
+ tcpcl_transfer_t *xfer = wmem_map_lookup(table, &xfer_id);
+ if (!xfer) {
+ guint64 *key = wmem_new(wmem_file_scope(), guint64);
+ *key = xfer_id;
+ xfer = tcpcl_transfer_new();
+ wmem_map_insert(table, key, xfer);
+ }
+ return xfer;
+}
+
+static tcpcl_peer_t * tcpcl_peer_new(void) {
+ tcpcl_peer_t *obj = wmem_new0(wmem_file_scope(), tcpcl_peer_t);
+ clear_address(&(obj->addr));
+ obj->frame_loc_to_transfer = wmem_map_new(wmem_file_scope(), tcpcl_frame_loc_hash, tcpcl_frame_loc_equal);
+ obj->transfers = wmem_map_new(wmem_file_scope(), g_int64_hash, g_int64_equal);
+ return obj;
+}
+
+static void tcpcl_peer_associate_transfer(tcpcl_peer_t *peer, const tcpcl_frame_loc_t *loc, const guint64 xfer_id) {
+ gpointer *xfer = wmem_map_lookup(peer->frame_loc_to_transfer, loc);
+ if (!xfer) {
+ tcpcl_frame_loc_t *key = tcpcl_frame_loc_clone(wmem_file_scope(), loc);
+ guint64 *val = wmem_new(wmem_file_scope(), guint64);
+ *val = xfer_id;
+ wmem_map_insert(peer->frame_loc_to_transfer, key, val);
+ }
+}
+
+static tcpcl_conversation_t * tcpcl_conversation_new(void) {
+ tcpcl_conversation_t *obj = wmem_new0(wmem_file_scope(), tcpcl_conversation_t);
+ obj->active = tcpcl_peer_new();
+ obj->passive = tcpcl_peer_new();
+ return obj;
+}
+
+tcpcl_dissect_ctx_t * tcpcl_dissect_ctx_get(tvbuff_t *tvb, packet_info *pinfo, const gint offset) {
+ conversation_t *convo = find_or_create_conversation(pinfo);
+ tcpcl_conversation_t *tcpcl_convo = (tcpcl_conversation_t *)conversation_get_proto_data(convo, proto_tcpcl);
+ if (!tcpcl_convo) {
+ return NULL;
+ }
+ tcpcl_dissect_ctx_t *ctx = wmem_new0(wmem_packet_scope(), tcpcl_dissect_ctx_t);
+ ctx->convo = tcpcl_convo;
+ ctx->cur_loc = tcpcl_frame_loc_new(wmem_packet_scope(), pinfo, tvb, offset);
+
+ const gboolean src_is_active = (
+ addresses_equal(&(ctx->convo->active->addr), &(pinfo->src))
+ && (ctx->convo->active->port == pinfo->srcport)
+ );
+ if (src_is_active) {
+ ctx->tx_peer = ctx->convo->active;
+ ctx->rx_peer = ctx->convo->passive;
+ }
+ else {
+ ctx->tx_peer = ctx->convo->passive;
+ ctx->rx_peer = ctx->convo->active;
+ }
+
+ ctx->is_contact = (
+ !(ctx->tx_peer->chdr_seen)
+ || tcpcl_frame_loc_equal(ctx->tx_peer->chdr_seen, ctx->cur_loc)
+ );
+
+ return ctx;
+}
+
+static void try_negotiate(tcpcl_dissect_ctx_t *ctx, packet_info *pinfo) {
+ if (!(ctx->convo->contact_negotiated)
+ && (ctx->convo->active->chdr_seen)
+ && (ctx->convo->passive->chdr_seen)) {
+ ctx->convo->session_use_tls = (
+ ctx->convo->active->can_tls & ctx->convo->passive->can_tls
+ );
+ ctx->convo->contact_negotiated = TRUE;
+
+ if (ctx->convo->session_use_tls
+ && (!(ctx->convo->session_tls_start))) {
+ col_append_str(pinfo->cinfo, COL_INFO, " [STARTTLS]");
+ ctx->convo->session_tls_start = tcpcl_frame_loc_clone(wmem_file_scope(), ctx->cur_loc);
+ ssl_starttls_ack(tls_handle, pinfo, tcpcl_handle);
+ }
+ }
+
+ if (!(ctx->convo->sess_negotiated)
+ && (ctx->convo->active->sess_init_seen)
+ && (ctx->convo->passive->sess_init_seen)) {
+ ctx->convo->sess_keepalive = MIN(
+ ctx->convo->active->keepalive,
+ ctx->convo->passive->keepalive
+ );
+ ctx->convo->sess_negotiated = TRUE;
+
+ }
+}
+
+/** Record metadata about one segment in a transfer.
+ */
+static void transfer_add_segment(tcpcl_dissect_ctx_t *ctx, guint64 xfer_id, guint8 flags,
+ guint64 data_len,
+ packet_info *pinfo, tvbuff_t *tvb, proto_tree *tree_msg,
+ proto_item *item_msg, proto_item *item_flags) {
+ tcpcl_transfer_t *xfer = get_or_create_transfer_t(ctx->tx_peer->transfers, xfer_id);
+
+ guint8 flag_start, flag_end;
+ if (ctx->tx_peer->version == 3) {
+ flag_start = TCPCLV3_DATA_START_FLAG;
+ flag_end = TCPCLV3_DATA_END_FLAG;
+ }
+ else {
+ flag_start = TCPCLV4_TRANSFER_FLAG_START;
+ flag_end = TCPCLV4_TRANSFER_FLAG_END;
+ }
+
+ // Add or get the segment metadata
+ tcpcl_seg_meta_t *seg_meta = tcpcl_seg_meta_new(pinfo, ctx->cur_loc);
+ wmem_list_frame_t *frm = wmem_list_find_custom(xfer->seg_list, seg_meta, tcpcl_seg_meta_compare_loc);
+ if (frm) {
+ tcpcl_seg_meta_free(seg_meta);
+ seg_meta = wmem_list_frame_data(frm);
+ }
+ else {
+ wmem_list_insert_sorted(xfer->seg_list, seg_meta, tcpcl_seg_meta_compare_loc);
+ frm = wmem_list_find_custom(xfer->seg_list, seg_meta, tcpcl_seg_meta_compare_loc);
+ // Set for new item
+ seg_meta->flags = flags;
+ }
+
+ // mark start-of-transfer
+ if (!(seg_meta->related_start)) {
+ wmem_list_frame_t *frm_front = wmem_list_head(xfer->seg_list);
+ tcpcl_seg_meta_t *seg_front = frm_front ? wmem_list_frame_data(frm_front) : NULL;
+ if (seg_front && (seg_front->flags & flag_start)) {
+ seg_meta->related_start = seg_front;
+ }
+ }
+
+ // accumulate segment sizes
+ guint64 prev_seen_len;
+ wmem_list_frame_t *frm_prev = wmem_list_frame_prev(frm);
+ if (!frm_prev) {
+ if (!(flags & flag_start)) {
+ expert_add_info(pinfo, item_flags, &ei_tcpclv4_xfer_seg_missing_start);
+ }
+ prev_seen_len = 0;
+ }
+ else {
+ const tcpcl_seg_meta_t *seg_prev = wmem_list_frame_data(frm_prev);
+ if (flags & flag_start) {
+ expert_add_info(pinfo, item_flags, &ei_tcpclv4_xfer_seg_duplicate_start);
+ }
+ prev_seen_len = seg_prev->seen_len;
+ }
+ wmem_list_frame_t *frm_next = wmem_list_frame_next(frm);
+ if (!frm_next) {
+ if (!(flags & flag_end)) {
+ expert_add_info(pinfo, item_flags, &ei_tcpclv4_xfer_seg_missing_end);
+ }
+ }
+ else {
+ if (flags & flag_end) {
+ expert_add_info(pinfo, item_flags, &ei_tcpclv4_xfer_seg_duplicate_end);
+ }
+ }
+ seg_meta->seen_len = prev_seen_len + data_len;
+
+ proto_item *item_seen = proto_tree_add_uint64(tree_msg, hf_tcpclv4_xfer_segment_seen_len, tvb, 0, 0, seg_meta->seen_len);
+ PROTO_ITEM_SET_GENERATED(item_seen);
+ if (seg_meta->seen_len > ctx->rx_peer->transfer_mru) {
+ expert_add_info(pinfo, item_seen, &ei_tcpclv4_xferload_over_xfer_mru);
+ }
+ if (xfer->total_length) {
+ if (seg_meta->seen_len > *(xfer->total_length)) {
+ expert_add_info(pinfo, item_seen, &ei_xfer_seg_over_total_len);
+ }
+ else if ((flags & flag_end)
+ && (seg_meta->seen_len != *(xfer->total_length))) {
+ expert_add_info(pinfo, item_seen, &ei_xfer_mismatch_total_len);
+ }
+ proto_item *item_total = proto_tree_add_uint64(tree_msg, hf_tcpclv4_xfer_total_len, tvb, 0, 0, *(xfer->total_length));
+ PROTO_ITEM_SET_GENERATED(item_total);
+ }
+
+ if (seg_meta->related_ack) {
+ proto_item *item_rel = proto_tree_add_uint(tree_msg, hf_tcpclv4_xfer_segment_related_ack, tvb, 0, 0, seg_meta->related_ack->frame_loc.frame_num);
+ PROTO_ITEM_SET_GENERATED(item_rel);
+
+ nstime_t td;
+ nstime_delta(&td, &(seg_meta->related_ack->frame_time), &(seg_meta->frame_time));
+ proto_item *item_td = proto_tree_add_time(tree_msg, hf_tcpclv4_xfer_segment_time_diff, tvb, 0, 0, &td);
+ PROTO_ITEM_SET_GENERATED(item_td);
+
+ }
+ else {
+ expert_add_info(pinfo, item_msg, &ei_tcpclv4_xfer_seg_no_relation);
+ }
+ if (seg_meta->related_start && (seg_meta->related_start != seg_meta)) {
+ proto_item *item_rel = proto_tree_add_uint(tree_msg, hf_tcpclv4_xfer_segment_related_start, tvb, 0, 0, seg_meta->related_start->frame_loc.frame_num);
+ PROTO_ITEM_SET_GENERATED(item_rel);
+
+ nstime_t td;
+ nstime_delta(&td, &(seg_meta->frame_time), &(seg_meta->related_start->frame_time));
+ proto_item *item_td = proto_tree_add_time(tree_msg, hf_tcpclv4_xfer_segment_time_start, tvb, 0, 0, &td);
+ PROTO_ITEM_SET_GENERATED(item_td);
+ }
+}
+
+static void transfer_add_ack(tcpcl_dissect_ctx_t *ctx, guint64 xfer_id, guint8 flags,
+ guint64 ack_len,
+ packet_info *pinfo, tvbuff_t *tvb, proto_tree *tree_msg,
+ proto_item *item_msg, proto_item *item_flags) {
+ tcpcl_transfer_t *xfer = get_or_create_transfer_t(ctx->rx_peer->transfers, xfer_id);
+
+ // Add or get the ack metadata
+ tcpcl_ack_meta_t *ack_meta = tcpcl_ack_meta_new(pinfo, ctx->cur_loc);
+ wmem_list_frame_t *frm = wmem_list_find_custom(xfer->ack_list, ack_meta, tcpcl_ack_meta_compare_loc);
+ if (frm) {
+ tcpcl_ack_meta_free(ack_meta);
+ ack_meta = wmem_list_frame_data(frm);
+ }
+ else {
+ wmem_list_insert_sorted(xfer->ack_list, ack_meta, tcpcl_ack_meta_compare_loc);
+ frm = wmem_list_find_custom(xfer->ack_list, ack_meta, tcpcl_ack_meta_compare_loc);
+ // Set for new item
+ ack_meta->flags = flags;
+ ack_meta->seen_len = ack_len;
+ }
+
+ // mark start-of-transfer
+ if (!(ack_meta->related_start)) {
+ wmem_list_frame_t *frm_front = wmem_list_head(xfer->seg_list);
+ tcpcl_seg_meta_t *seg_front = frm_front ? wmem_list_frame_data(frm_front) : NULL;
+ if (seg_front && (seg_front->flags & TCPCLV4_TRANSFER_FLAG_START)) {
+ ack_meta->related_start = seg_front;
+ }
+ }
+
+ // Assemble both of the links here, as ACK will always follow segment
+ if (!(ack_meta->related_seg)) {
+ wmem_list_frame_t *seg_iter = wmem_list_head(xfer->seg_list);
+ for (; seg_iter; seg_iter = wmem_list_frame_next(seg_iter)) {
+ tcpcl_seg_meta_t *seg_meta = wmem_list_frame_data(seg_iter);
+ if (seg_meta->seen_len == ack_meta->seen_len) {
+ seg_meta->related_ack = ack_meta;
+ ack_meta->related_seg = seg_meta;
+ }
+ }
+ }
+
+ if (xfer->total_length) {
+ proto_item *item_total = proto_tree_add_uint64(tree_msg, hf_tcpclv4_xfer_total_len, tvb, 0, 0, *(xfer->total_length));
+ PROTO_ITEM_SET_GENERATED(item_total);
+ }
+ if (ack_meta->related_seg) {
+ proto_item *item_rel = proto_tree_add_uint(tree_msg, hf_tcpclv4_xfer_ack_related_seg, tvb, 0, 0, ack_meta->related_seg->frame_loc.frame_num);
+ PROTO_ITEM_SET_GENERATED(item_rel);
+
+ nstime_t td;
+ nstime_delta(&td, &(ack_meta->frame_time), &(ack_meta->related_seg->frame_time));
+ proto_item *item_td = proto_tree_add_time(tree_msg, hf_tcpclv4_xfer_ack_time_diff, tvb, 0, 0, &td);
+ PROTO_ITEM_SET_GENERATED(item_td);
+
+ if (item_flags && (ack_meta->flags != ack_meta->related_seg->flags)) {
+ expert_add_info(pinfo, item_flags, &ei_xfer_ack_mismatch_flags);
+ }
+ }
+ else {
+ expert_add_info(pinfo, item_msg, &ei_xfer_ack_no_relation);
+ }
+ if (ack_meta->related_start) {
+ proto_item *item_rel = proto_tree_add_uint(tree_msg, hf_tcpclv4_xfer_ack_related_start, tvb, 0, 0, ack_meta->related_start->frame_loc.frame_num);
+ PROTO_ITEM_SET_GENERATED(item_rel);
+
+ nstime_t td;
+ nstime_delta(&td, &(ack_meta->frame_time), &(ack_meta->related_start->frame_time));
+ proto_item *item_td = proto_tree_add_time(tree_msg, hf_tcpclv4_xfer_ack_time_start, tvb, 0, 0, &td);
+ PROTO_ITEM_SET_GENERATED(item_td);
+ }
+}
+
+static void transfer_add_refuse(tcpcl_dissect_ctx_t *ctx, guint64 xfer_id,
+ packet_info *pinfo, tvbuff_t *tvb, proto_tree *tree_msg,
+ proto_item *item_msg) {
+ const tcpcl_transfer_t *xfer = wmem_map_lookup(ctx->rx_peer->transfers, &xfer_id);
+ const tcpcl_seg_meta_t *seg_last = NULL;
+ if (xfer) {
+ wmem_list_frame_t *seg_iter = wmem_list_tail(xfer->seg_list);
+ seg_iter = seg_iter ? wmem_list_frame_prev(seg_iter) : NULL;
+ seg_last = seg_iter ? wmem_list_frame_data(seg_iter) : NULL;
+ }
+
+ if (seg_last) {
+ proto_item *item_rel = proto_tree_add_uint(tree_msg, hf_tcpclv4_xfer_refuse_related_seg, tvb, 0, 0, seg_last->frame_loc.frame_num);
+ PROTO_ITEM_SET_GENERATED(item_rel);
+ }
+ else {
+ expert_add_info(pinfo, item_msg, &ei_tcpclv4_xfer_refuse_no_transfer);
+ }
+}
+
+static gint get_clamped_length(guint64 orig) {
+ gint clamped;
+ if (orig > G_MAXINT) {
+ clamped = G_MAXINT;
+ }
+ else {
+ clamped = (gint) orig;
+ }
+ return clamped;
+}
+
+static guint
+get_v3_msg_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset,
+ tcpcl_dissect_ctx_t *ctx _U_)
+{
+ const int orig_offset = offset;
+ int len, bytecount;
+ guint8 conv_hdr = tvb_get_guint8(tvb, offset);
+ offset += 1;
+
+ switch (conv_hdr & TCPCLV3_TYPE_MASK)
+ {
+ case TCPCLV3_DATA_SEGMENT:
+ /* get length from sdnv */
+ len = evaluate_sdnv(tvb, offset, &bytecount);
+ if (len < 0)
+ return 0;
+
+ offset += bytecount+len;
+ break;
+
+ case TCPCLV3_ACK_SEGMENT:
+ /* get length from sdnv */
+ len = evaluate_sdnv(tvb, offset, &bytecount);
+ if (len < 0)
+ return 0;
+
+ offset += bytecount;
+ break;
+
+ case TCPCLV3_KEEP_ALIVE:
+ case TCPCLV3_REFUSE_BUNDLE:
+ /* always 1 byte */
+ break;
+ case TCPCLV3_SHUTDOWN:
+ if (conv_hdr & TCPCLV3_SHUTDOWN_REASON) {
+ offset += 1;
+ }
+ if (conv_hdr & TCPCLV3_SHUTDOWN_DELAY) {
+ offset += 2;
+ }
+ break;
+
+ case TCPCLV3_LENGTH:
+ /* get length from sdnv */
+ len = evaluate_sdnv(tvb, offset, &bytecount);
+ if (len < 0)
+ return 0;
+ offset += bytecount;
+ break;
+ }
+
+ /* This probably isn't a TCPCL/Bundle packet, so just stop dissection */
+ return offset - orig_offset;
+}
+
+static int
+dissect_v3_msg(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
+ tcpcl_dissect_ctx_t *ctx)
+{
+ guint8 conv_hdr;
+ const char *msgtype_name;
+ guint8 refuse_bundle_hdr;
+ int offset = 0;
+ int sdnv_length, segment_length;
+ proto_item *conv_item, *sub_item;
+ proto_tree *conv_tree, *sub_tree;
+ guint64 *xfer_id = NULL;
+ proto_item *item_xfer_id = NULL;
+
+ conv_item = proto_tree_add_item(tree, hf_tcpclv3_mhdr, tvb, 0, -1, ENC_NA);
+ conv_tree = proto_item_add_subtree(conv_item, ett_tcpclv3_mhdr);
+
+ conv_hdr = tvb_get_guint8(tvb, offset);
+ proto_tree_add_item(conv_tree, hf_tcpclv3_pkt_type, tvb, offset, 1, ENC_BIG_ENDIAN);
+
+ msgtype_name = val_to_str_const((conv_hdr>>4)&0xF, v3_message_type_vals, "Unknown");
+ col_append_sep_str(pinfo->cinfo, COL_INFO, NULL, msgtype_name);
+ proto_item_append_text(proto_tree_get_parent(conv_tree), ": %s", msgtype_name);
+
+ switch (conv_hdr & TCPCLV3_TYPE_MASK) {
+ case TCPCLV3_DATA_SEGMENT: {
+ proto_item *item_flags;
+
+ item_flags = proto_tree_add_bitmask(
+ conv_tree, tvb,
+ offset, hf_tcpclv3_data_procflags,
+ ett_tcpclv3_data_procflags, v3_data_procflags,
+ ENC_BIG_ENDIAN
+ );
+
+ /* Only Start and End flags (bits 0 & 1) are valid in Data Segment */
+ if ((conv_hdr & ~(TCPCLV3_TYPE_MASK | TCPCLV3_DATA_FLAGS)) != 0) {
+ expert_add_info(pinfo, item_flags, &ei_tcpclv3_data_flags);
+ }
+
+ segment_length = evaluate_sdnv(tvb, 1, &sdnv_length);
+ sub_item = proto_tree_add_int(conv_tree, hf_tcpclv3_data_segment_length, tvb, 1, sdnv_length, segment_length);
+ if (segment_length < 0) {
+ expert_add_info(pinfo, sub_item, &ei_tcpclv3_segment_length);
+ return 1;
+ }
+ offset += 1 + sdnv_length;
+
+ // implied transfer ID
+ xfer_id = wmem_map_lookup(ctx->tx_peer->frame_loc_to_transfer, ctx->cur_loc);
+ if (!xfer_id) {
+ xfer_id = wmem_new(wmem_packet_scope(), guint64);
+ *xfer_id = wmem_map_size(ctx->tx_peer->transfers);
+
+ if (conv_hdr & TCPCLV3_DATA_START_FLAG) {
+ *xfer_id += 1;
+ get_or_create_transfer_t(ctx->tx_peer->transfers, *xfer_id);
+ }
+ tcpcl_peer_associate_transfer(ctx->tx_peer, ctx->cur_loc, *xfer_id);
+ }
+ item_xfer_id = proto_tree_add_uint64(conv_tree, hf_tcpclv3_xfer_id, tvb, 0, 0, *xfer_id);
+ PROTO_ITEM_SET_GENERATED(item_xfer_id);
+
+ proto_tree_add_item(conv_tree, hf_tcpclv3_data_segment_data, tvb, offset, segment_length, ENC_NA);
+
+ if (tcpcl_analyze_sequence) {
+ transfer_add_segment(ctx, *xfer_id, (conv_hdr & TCPCLV3_DATA_FLAGS), segment_length, pinfo, tvb, conv_tree, conv_item, item_flags);
+ }
+
+ if (tcpcl_desegment_transfer) {
+ // wireshark fragment set IDs are 32-bits only
+ const guint32 corr_id = *xfer_id & 0xFFFFFFFF;
+ // Reassemble the segments
+ fragment_head *frag_msg;
+ frag_msg = fragment_add_seq_next(
+ &xfer_reassembly_table,
+ tvb, offset,
+ pinfo, corr_id, NULL,
+ segment_length,
+ !(conv_hdr & TCPCLV3_DATA_END_FLAG)
+ );
+ ctx->xferload = process_reassembled_data(
+ tvb, offset, pinfo,
+ "Reassembled Transfer",
+ frag_msg,
+ &xfer_frag_items,
+ NULL,
+ proto_tree_get_parent_tree(tree)
+ );
+ }
+ offset += segment_length;
+
+ break;
+ }
+ case TCPCLV3_ACK_SEGMENT: {
+ /*No valid flags*/
+ offset += 1;
+
+ segment_length = evaluate_sdnv(tvb, offset, &sdnv_length);
+ sub_item = proto_tree_add_int(conv_tree, hf_tcpclv3_ack_length, tvb, offset, sdnv_length, segment_length);
+ if (segment_length < 0) {
+ expert_add_info(pinfo, sub_item, &ei_tcpclv3_ack_length);
+ } else {
+ offset += sdnv_length;
+ }
+
+ // implied transfer ID
+ xfer_id = wmem_map_lookup(ctx->rx_peer->frame_loc_to_transfer, ctx->cur_loc);
+ if (!xfer_id) {
+ xfer_id = wmem_new(wmem_packet_scope(), guint64);
+ *xfer_id = wmem_map_size(ctx->rx_peer->transfers);
+
+ tcpcl_peer_associate_transfer(ctx->rx_peer, ctx->cur_loc, *xfer_id);
+ }
+ item_xfer_id = proto_tree_add_uint64(conv_tree, hf_tcpclv3_xfer_id, tvb, 0, 0, *xfer_id);
+ PROTO_ITEM_SET_GENERATED(item_xfer_id);
+
+ if (tcpcl_analyze_sequence) {
+ transfer_add_ack(ctx, *xfer_id, 0, segment_length, pinfo, tvb, conv_tree, conv_item, NULL);
+ }
+
+ break;
+ }
+ case TCPCLV3_KEEP_ALIVE:
+ /*No valid flags in Keep Alive*/
+ offset += 1;
+ break;
+
+ case TCPCLV3_SHUTDOWN:
+ /* Add tree for Shutdown Flags */
+ sub_item = proto_tree_add_item(conv_tree, hf_tcpclv3_shutdown_flags, tvb,
+ offset, 1, ENC_BIG_ENDIAN);
+ sub_tree = proto_item_add_subtree(sub_item, ett_tcpclv3_shutdown_flags);
+
+ proto_tree_add_item(sub_tree, hf_tcpclv3_shutdown_flags_reason,
+ tvb, offset, 1, ENC_BIG_ENDIAN);
+ proto_tree_add_item(sub_tree, hf_tcpclv3_shutdown_flags_delay,
+ tvb, offset, 1, ENC_BIG_ENDIAN);
+
+ offset += 1;
+ if (conv_hdr & TCPCLV3_SHUTDOWN_REASON) {
+ proto_tree_add_item(conv_tree,
+ hf_tcpclv3_shutdown_reason, tvb,
+ offset, 1, ENC_BIG_ENDIAN);
+ offset += 1;
+ }
+ if (conv_hdr & TCPCLV3_SHUTDOWN_DELAY) {
+ proto_tree_add_item(conv_tree,
+ hf_tcpclv3_shutdown_delay, tvb,
+ offset, 2, ENC_BIG_ENDIAN);
+ offset += 1;
+ }
+ break;
+ case TCPCLV3_REFUSE_BUNDLE:
+ /*No valid flags*/
+ offset += 1;
+
+ refuse_bundle_hdr = tvb_get_guint8(tvb, offset);
+ proto_tree_add_item(conv_tree, hf_tcpclv3_refuse_reason_code, tvb, offset, 1, ENC_BIG_ENDIAN);
+ offset += 1;
+ col_add_str(pinfo->cinfo, COL_INFO, val_to_str_const((refuse_bundle_hdr>>4)&0xF, v3_refuse_reason_code, "Unknown"));
+
+ // implied transfer ID
+ xfer_id = wmem_map_lookup(ctx->rx_peer->frame_loc_to_transfer, ctx->cur_loc);
+ if (!xfer_id) {
+ xfer_id = wmem_new(wmem_packet_scope(), guint64);
+ *xfer_id = wmem_map_size(ctx->rx_peer->transfers);
+
+ tcpcl_peer_associate_transfer(ctx->rx_peer, ctx->cur_loc, *xfer_id);
+ }
+ item_xfer_id = proto_tree_add_uint64(conv_tree, hf_tcpclv3_xfer_id, tvb, 0, 0, *xfer_id);
+ PROTO_ITEM_SET_GENERATED(item_xfer_id);
+
+ if (tcpcl_analyze_sequence) {
+ transfer_add_refuse(ctx, *xfer_id, pinfo, tvb, conv_tree, conv_item);
+ }
+
+ break;
+
+ default:
+ expert_add_info(pinfo, proto_tree_get_parent(conv_tree), &ei_tcpclv3_invalid_msg_type);
+ break;
+ }
+
+ return offset;
+}
+
+static guint get_v4_msg_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset,
+ tcpcl_dissect_ctx_t *ctx _U_) {
+ const int init_offset = offset;
+ guint8 msgtype = tvb_get_guint8(tvb, offset);
+ offset += 1;
+ switch(msgtype) {
+ case TCPCLV4_MSGTYPE_SESS_INIT: {
+ const gint buflen = tvb_reported_length(tvb);
+ offset += 2 + 8 + 8;
+ if (buflen < offset + 2) {
+ return 0;
+ }
+ guint16 nodeid_len = tvb_get_guint16(tvb, offset, ENC_BIG_ENDIAN);
+ offset += 2;
+ offset += nodeid_len;
+ if (buflen < offset + 4) {
+ return 0;
+ }
+ guint32 extlist_len = tvb_get_guint32(tvb, offset, ENC_BIG_ENDIAN);
+ offset += 4;
+ offset += extlist_len;
+ break;
+ }
+ case TCPCLV4_MSGTYPE_SESS_TERM: {
+ offset += 1 + 1;
+ break;
+ }
+ case TCPCLV4_MSGTYPE_XFER_SEGMENT: {
+ const gint buflen = tvb_reported_length(tvb);
+ if (buflen < offset + 1) {
+ return 0;
+ }
+ guint8 flags = tvb_get_guint8(tvb, offset);
+ offset += 1;
+ offset += 8;
+ if (flags & TCPCLV4_TRANSFER_FLAG_START) {
+ if (buflen < offset + 4) {
+ return 0;
+ }
+ guint32 extlist_len = tvb_get_guint32(tvb, offset, ENC_BIG_ENDIAN);
+ offset += 4;
+ offset += extlist_len;
+ }
+ if (buflen < offset + 8) {
+ return 0;
+ }
+ guint64 data_len = tvb_get_guint64(tvb, offset, ENC_BIG_ENDIAN);
+ offset += 8;
+ const gint data_len_clamp = get_clamped_length(data_len);
+ offset += data_len_clamp;
+ break;
+ }
+ case TCPCLV4_MSGTYPE_XFER_ACK: {
+ offset += 1 + 8 + 8;
+ break;
+ }
+ case TCPCLV4_MSGTYPE_XFER_REFUSE: {
+ offset += 1 + 8;
+ break;
+ }
+ case TCPCLV4_MSGTYPE_KEEPALIVE: {
+ break;
+ }
+ case TCPCLV4_MSGTYPE_MSG_REJECT: {
+ offset += 1 + 1;
+ break;
+ }
+ default:
+ break;
+ }
+ return offset - init_offset;
+}
+
+static int
+dissect_v4_msg(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
+ tcpcl_dissect_ctx_t *ctx _U_) {
+ int offset = 0;
+ // Length of non-protocol 'payload' data in this message
+ gint payload_len = 0;
+
+ guint8 msgtype = 0;
+ const char *msgtype_name = NULL;
+
+ proto_item *item_msg = proto_tree_add_item(tree, hf_tcpclv4_mhdr_tree, tvb, offset, 0, ENC_NA);
+ proto_tree *tree_msg = proto_item_add_subtree(item_msg, ett_tcpclv4_mhdr);
+
+ msgtype = tvb_get_guint8(tvb, offset);
+ proto_tree_add_uint(tree_msg, hf_tcpclv4_mhdr_type, tvb, offset, 1, msgtype);
+ offset += 1;
+ msgtype_name = val_to_str(msgtype, v4_message_type_vals, "type 0x%" PRIx32);
+ wmem_strbuf_t *suffix_text = wmem_strbuf_new(wmem_packet_scope(), NULL);
+
+ switch(msgtype) {
+ case TCPCLV4_MSGTYPE_SESS_INIT: {
+ guint16 keepalive = tvb_get_guint16(tvb, offset, ENC_BIG_ENDIAN);
+ proto_tree_add_uint(tree_msg, hf_tcpclv4_sess_init_keepalive, tvb, offset, 2, keepalive);
+ offset += 2;
+
+ guint64 seg_mru = tvb_get_guint64(tvb, offset, ENC_BIG_ENDIAN);
+ proto_tree_add_uint64(tree_msg, hf_tcpclv4_sess_init_seg_mru, tvb, offset, 8, seg_mru);
+ offset += 8;
+
+ guint64 xfer_mru = tvb_get_guint64(tvb, offset, ENC_BIG_ENDIAN);
+ proto_tree_add_uint64(tree_msg, hf_tcpclv4_sess_init_xfer_mru, tvb, offset, 8, xfer_mru);
+ offset += 8;
+
+ guint16 nodeid_len = tvb_get_guint16(tvb, offset, ENC_BIG_ENDIAN);
+ proto_tree_add_uint(tree_msg, hf_tcpclv4_sess_init_nodeid_len, tvb, offset, 2, nodeid_len);
+ offset += 2;
+
+ {
+ guint8 *nodeid_data = tvb_get_string_enc(wmem_packet_scope(), tvb, offset, nodeid_len, ENC_UTF_8);
+ proto_tree_add_string(tree_msg, hf_tcpclv4_sess_init_nodeid_data, tvb, offset, nodeid_len, (const char *)nodeid_data);
+ wmem_free(wmem_packet_scope(), nodeid_data);
+ }
+ offset += nodeid_len;
+
+ guint32 extlist_len = tvb_get_guint32(tvb, offset, ENC_BIG_ENDIAN);
+ proto_tree_add_uint(tree_msg, hf_tcpclv4_sess_init_extlist_len, tvb, offset, 4, extlist_len);
+ offset += 4;
+
+ gint extlist_offset = 0;
+ while (extlist_offset < (int)extlist_len) {
+ gint extitem_offset = 0;
+ proto_item *item_ext = proto_tree_add_item(tree_msg, hf_tcpclv4_sessext_tree, tvb, offset + extlist_offset, 0, ENC_NA);
+ proto_tree *tree_ext = proto_item_add_subtree(item_ext, ett_tcpclv4_sessext);
+
+ guint8 extitem_flags = tvb_get_guint8(tvb, offset + extlist_offset + extitem_offset);
+ proto_tree_add_bitmask(tree_ext, tvb, offset + extlist_offset + extitem_offset, hf_tcpclv4_sessext_flags, ett_tcpclv4_sessext_flags, v4_sessext_flags, ENC_BIG_ENDIAN);
+ extitem_offset += 1;
+ const gboolean is_critical = (extitem_flags & TCPCLV4_EXTENSION_FLAG_CRITICAL);
+ if (is_critical) {
+ expert_add_info(pinfo, item_ext, &ei_tcpclv4_extitem_critical);
+ }
+
+ guint32 extitem_type = tvb_get_guint16(tvb, offset + extlist_offset + extitem_offset, ENC_BIG_ENDIAN);
+ proto_item *item_type = proto_tree_add_uint(tree_ext, hf_tcpclv4_sessext_type, tvb, offset + extlist_offset + extitem_offset, 2, extitem_type);
+ extitem_offset += 2;
+
+ guint32 extitem_len = tvb_get_guint16(tvb, offset + extlist_offset + extitem_offset, ENC_BIG_ENDIAN);
+ proto_tree_add_uint(tree_ext, hf_tcpclv4_sessext_len, tvb, offset + extlist_offset + extitem_offset, 2, extitem_len);
+ extitem_offset += 2;
+
+ tvbuff_t *extitem_tvb = tvb_new_subset_length(tvb, offset + extlist_offset + extitem_offset, extitem_len);
+ proto_item *item_extdata = proto_tree_add_item(tree_ext, hf_tcpclv4_sessext_data, extitem_tvb, 0, tvb_captured_length(extitem_tvb), ENC_NA);
+ proto_tree *tree_extdata = proto_item_add_subtree(item_extdata, ett_tcpclv4_sessext_data);
+
+ int sublen = dissector_try_uint(sess_ext_dissectors, extitem_type, extitem_tvb, pinfo, tree_extdata);
+ if (sublen == 0) {
+ expert_add_info(pinfo, item_type, &ei_tcpclv4_invalid_sessext_type);
+ }
+ extitem_offset += extitem_len;
+
+ proto_item_set_len(item_ext, extitem_offset);
+ extlist_offset += extitem_offset;
+
+ proto_item_append_text(item_ext, ": Type 0x%" PRIx32, extitem_type);
+ if (is_critical) {
+ proto_item_append_text(item_ext, ", CRITICAL");
+ }
+ }
+ // advance regardless of any internal offset processing
+ offset += extlist_len;
+
+ if (ctx->tx_peer->sess_init_seen) {
+ if (tcpcl_analyze_sequence) {
+ if (!tcpcl_frame_loc_equal(ctx->tx_peer->sess_init_seen, ctx->cur_loc)) {
+ expert_add_info(pinfo, item_msg, &ei_tcpclv4_sess_init_duplicate);
+ }
+ }
+ }
+ else {
+ ctx->tx_peer->sess_init_seen = tcpcl_frame_loc_clone(wmem_file_scope(), ctx->cur_loc);
+ ctx->tx_peer->keepalive = keepalive;
+ ctx->tx_peer->segment_mru = seg_mru;
+ ctx->tx_peer->transfer_mru = xfer_mru;
+ }
+
+ break;
+ }
+ case TCPCLV4_MSGTYPE_SESS_TERM: {
+ guint8 flags = tvb_get_guint8(tvb, offset);
+ proto_tree_add_bitmask(tree_msg, tvb, offset, hf_tcpclv4_sess_term_flags, ett_tcpclv4_sess_term_flags, v4_sess_term_flags, ENC_BIG_ENDIAN);
+ offset += 1;
+
+ guint8 reason = tvb_get_guint8(tvb, offset);
+ proto_tree_add_uint(tree_msg, hf_tcpclv4_sess_term_reason, tvb, offset, 1, reason);
+ offset += 1;
+
+ if (ctx->tx_peer->sess_term_seen) {
+ if (tcpcl_analyze_sequence) {
+ if (!tcpcl_frame_loc_equal(ctx->tx_peer->sess_term_seen, ctx->cur_loc)) {
+ expert_add_info(pinfo, item_msg, &ei_tcpclv4_sess_term_duplicate);
+ }
+ }
+ }
+ else {
+ ctx->tx_peer->sess_term_seen = tcpcl_frame_loc_clone(wmem_file_scope(), ctx->cur_loc);
+ ctx->tx_peer->sess_term_reason = reason;
+ }
+
+ if (tcpcl_analyze_sequence) {
+ if (ctx->rx_peer->sess_term_seen) {
+ proto_item *item_rel = proto_tree_add_uint(tree_msg, hf_tcpclv4_sess_term_related, tvb, 0, 0, ctx->rx_peer->sess_term_seen->frame_num);
+ PROTO_ITEM_SET_GENERATED(item_rel);
+
+ // Is this message after the other SESS_TERM?
+ if (tcpcl_frame_loc_compare(ctx->tx_peer->sess_term_seen, ctx->rx_peer->sess_term_seen, NULL) > 0) {
+ if (!(flags & TCPCLV4_SESS_TERM_FLAG_REPLY)) {
+ expert_add_info(pinfo, item_msg, &ei_tcpclv4_sess_term_reply_flag);
+ }
+ }
+ }
+ }
+
+ break;
+ }
+ case TCPCLV4_MSGTYPE_XFER_SEGMENT:{
+ guint8 flags = tvb_get_guint8(tvb, offset);
+ proto_item *item_flags = proto_tree_add_bitmask(tree_msg, tvb, offset, hf_tcpclv4_xfer_flags, ett_tcpclv4_xfer_flags, v4_xfer_flags, ENC_BIG_ENDIAN);
+ offset += 1;
+
+ guint64 xfer_id = tvb_get_guint64(tvb, offset, ENC_BIG_ENDIAN);
+ proto_item *item_xfer_id = proto_tree_add_uint64(tree_msg, hf_tcpclv4_xfer_id, tvb, offset, 8, xfer_id);
+ offset += 8;
+
+ if (flags & TCPCLV4_TRANSFER_FLAG_START) {
+ guint32 extlist_len = tvb_get_guint32(tvb, offset, ENC_BIG_ENDIAN);
+ proto_tree_add_uint(tree_msg, hf_tcpclv4_xfer_segment_extlist_len, tvb, offset, 4, extlist_len);
+ offset += 4;
+
+ gint extlist_offset = 0;
+ while (extlist_offset < (int)extlist_len) {
+ gint extitem_offset = 0;
+ proto_item *item_ext = proto_tree_add_item(tree_msg, hf_tcpclv4_xferext_tree, tvb, offset + extlist_offset, 0, ENC_NA);
+ proto_tree *tree_ext = proto_item_add_subtree(item_ext, ett_tcpclv4_xferext);
+
+ guint8 extitem_flags = tvb_get_guint8(tvb, offset + extlist_offset + extitem_offset);
+ proto_tree_add_bitmask(tree_ext, tvb, offset + extlist_offset + extitem_offset, hf_tcpclv4_xferext_flags, ett_tcpclv4_xferext_flags, v4_xferext_flags, ENC_BIG_ENDIAN);
+ extitem_offset += 1;
+ const gboolean is_critical = (extitem_flags & TCPCLV4_EXTENSION_FLAG_CRITICAL);
+ if (is_critical) {
+ expert_add_info(pinfo, item_ext, &ei_tcpclv4_extitem_critical);
+ }
+
+ guint32 extitem_type = tvb_get_guint16(tvb, offset + extlist_offset + extitem_offset, ENC_BIG_ENDIAN);
+ proto_item *item_type = proto_tree_add_uint(tree_ext, hf_tcpclv4_xferext_type, tvb, offset + extlist_offset + extitem_offset, 2, extitem_type);
+ extitem_offset += 2;
+
+ guint32 extitem_len = tvb_get_guint16(tvb, offset + extlist_offset + extitem_offset, ENC_BIG_ENDIAN);
+ proto_tree_add_uint(tree_ext, hf_tcpclv4_xferext_len, tvb, offset + extlist_offset + extitem_offset, 2, extitem_len);
+ extitem_offset += 2;
+
+ tvbuff_t *extitem_tvb = tvb_new_subset_length(tvb, offset + extlist_offset + extitem_offset, extitem_len);
+ proto_item *item_extdata = proto_tree_add_item(tree_ext, hf_tcpclv4_xferext_data, extitem_tvb, 0, tvb_captured_length(extitem_tvb), ENC_NA);
+ proto_tree *tree_extdata = proto_item_add_subtree(item_extdata, ett_tcpclv4_xferext_data);
+
+ tcpcl_frame_loc_t *extitem_loc = tcpcl_frame_loc_new(wmem_packet_scope(), pinfo, extitem_tvb, 0);
+ tcpcl_peer_associate_transfer(ctx->tx_peer, extitem_loc, xfer_id);
+ int sublen = dissector_try_uint(xfer_ext_dissectors, extitem_type, extitem_tvb, pinfo, tree_extdata);
+ if (sublen == 0) {
+ expert_add_info(pinfo, item_type, &ei_tcpclv4_invalid_xferext_type);
+ }
+ extitem_offset += extitem_len;
+
+ proto_item_append_text(item_ext, ": Type 0x%" PRIx32, extitem_type);
+ if (is_critical) {
+ proto_item_append_text(item_ext, ", CRITICAL");
+ }
+
+ proto_item_set_len(item_ext, extitem_offset);
+ extlist_offset += extitem_offset;
+ }
+ // advance regardless of any internal offset processing
+ offset += extlist_len;
+ }
+
+ guint64 data_len = tvb_get_guint64(tvb, offset, ENC_BIG_ENDIAN);
+ proto_item *item_len = proto_tree_add_uint64(tree_msg, hf_tcpclv4_xfer_segment_data_len, tvb, offset, 8, data_len);
+ offset += 8;
+
+ if (data_len > ctx->rx_peer->segment_mru) {
+ expert_add_info(pinfo, item_len, &ei_tcpclv4_xfer_seg_over_seg_mru);
+ }
+ const gint data_len_clamp = get_clamped_length(data_len);
+
+ // Treat data as payload layer
+ const gint data_offset = offset;
+ proto_tree_add_item(tree_msg, hf_tcpclv4_xfer_segment_data, tvb, offset, data_len_clamp, ENC_NA);
+ offset += data_len_clamp;
+ payload_len = data_len_clamp;
+
+ wmem_strbuf_append_printf(suffix_text, ", Xfer ID: %" PRIu64, xfer_id);
+
+ if (flags) {
+ wmem_strbuf_append(suffix_text, ", Flags: ");
+ gboolean sep = FALSE;
+ if (flags & TCPCLV4_TRANSFER_FLAG_START) {
+ wmem_strbuf_append(suffix_text, "START");
+ sep = TRUE;
+ }
+ if (flags & TCPCLV4_TRANSFER_FLAG_END) {
+ if (sep) {
+ wmem_strbuf_append(suffix_text, "|");
+ }
+ wmem_strbuf_append(suffix_text, "END");
+ sep = TRUE;
+ }
+ }
+
+ if (tcpcl_analyze_sequence) {
+ transfer_add_segment(ctx, xfer_id, flags, data_len, pinfo, tvb, tree_msg, item_msg, item_flags);
+ }
+
+ if (tcpcl_desegment_transfer) {
+ // wireshark fragment set IDs are 32-bits only
+ const guint32 corr_id = xfer_id & 0xFFFFFFFF;
+ if (corr_id != xfer_id) {
+ expert_add_info(pinfo, item_xfer_id, &ei_xfer_seg_large_xferid);
+ }
+ else {
+ // Reassemble the segments
+ fragment_head *xferload_frag_msg = fragment_add_seq_next(
+ &xfer_reassembly_table,
+ tvb, data_offset,
+ pinfo, corr_id, NULL,
+ data_len_clamp,
+ !(flags & TCPCLV4_TRANSFER_FLAG_END)
+ );
+ ctx->xferload = process_reassembled_data(
+ tvb, data_offset, pinfo,
+ "Reassembled Transfer",
+ xferload_frag_msg,
+ &xfer_frag_items,
+ NULL,
+ proto_tree_get_parent_tree(tree)
+ );
+ }
+ }
+
+ break;
+ }
+ case TCPCLV4_MSGTYPE_XFER_ACK:{
+ guint8 flags = tvb_get_guint8(tvb, offset);
+ proto_item *item_flags = proto_tree_add_bitmask(tree_msg, tvb, offset, hf_tcpclv4_xfer_flags, ett_tcpclv4_xfer_flags, v4_xfer_flags, ENC_BIG_ENDIAN);
+ offset += 1;
+
+ guint64 xfer_id = tvb_get_guint64(tvb, offset, ENC_BIG_ENDIAN);
+ proto_tree_add_uint64(tree_msg, hf_tcpclv4_xfer_id, tvb, offset, 8, xfer_id);
+ offset += 8;
+
+ guint64 ack_len = tvb_get_guint64(tvb, offset, ENC_BIG_ENDIAN);
+ proto_tree_add_uint64(tree_msg, hf_tcpclv4_xfer_ack_ack_len, tvb, offset, 8, ack_len);
+ offset += 8;
+
+ wmem_strbuf_append_printf(suffix_text, ", Xfer ID: %" PRIu64, xfer_id);
+
+ if (flags) {
+ wmem_strbuf_append(suffix_text, ", Flags: ");
+ gboolean sep = FALSE;
+ if (flags & TCPCLV4_TRANSFER_FLAG_START) {
+ wmem_strbuf_append(suffix_text, "START");
+ sep = TRUE;
+ }
+ if (flags & TCPCLV4_TRANSFER_FLAG_END) {
+ if (sep) {
+ wmem_strbuf_append(suffix_text, "|");
+ }
+ wmem_strbuf_append(suffix_text, "END");
+ sep = TRUE;
+ }
+ }
+
+ if (tcpcl_analyze_sequence) {
+ transfer_add_ack(ctx, xfer_id, flags, ack_len, pinfo, tvb, tree_msg, item_msg, item_flags);
+ }
+
+ break;
+ }
+ case TCPCLV4_MSGTYPE_XFER_REFUSE: {
+ guint8 reason = tvb_get_guint8(tvb, offset);
+ proto_tree_add_uint(tree_msg, hf_tcpclv4_xfer_refuse_reason, tvb, offset, 1, reason);
+ offset += 1;
+
+ guint64 xfer_id = tvb_get_guint64(tvb, offset, ENC_BIG_ENDIAN);
+ proto_tree_add_uint64(tree_msg, hf_tcpclv4_xfer_id, tvb, offset, 8, xfer_id);
+ offset += 8;
+
+ wmem_strbuf_append_printf(suffix_text, ", Xfer ID: %" PRIu64, xfer_id);
+
+ if (tcpcl_analyze_sequence) {
+ transfer_add_refuse(ctx, xfer_id, pinfo, tvb, tree_msg, item_msg);
+ }
+
+ break;
+ }
+ case TCPCLV4_MSGTYPE_KEEPALIVE: {
+ break;
+ }
+ case TCPCLV4_MSGTYPE_MSG_REJECT: {
+ guint8 reason = tvb_get_guint8(tvb, offset);
+ proto_tree_add_uint(tree_msg, hf_tcpclv4_msg_reject_reason, tvb, offset, 1, reason);
+ offset += 1;
+
+ guint8 rej_head = tvb_get_guint8(tvb, offset);
+ proto_tree_add_uint(tree_msg, hf_tcpclv4_msg_reject_head, tvb, offset, 1, rej_head);
+ offset += 1;
+
+ break;
+ }
+ default:
+ expert_add_info(pinfo, item_msg, &ei_tcpclv4_invalid_msg_type);
+ break;
+ }
+
+ proto_item_set_len(item_msg, offset - payload_len);
+ proto_item_append_text(item_msg, ": %s%s", msgtype_name, wmem_strbuf_get_str(suffix_text));
+ wmem_strbuf_finalize(suffix_text);
+
+ if (tcpcl_analyze_sequence) {
+ if (!(ctx->tx_peer->sess_init_seen)) {
+ expert_add_info(pinfo, item_msg, &ei_tcpclv4_sess_init_missing);
+ }
+ else {
+ // This message is before SESS_INIT (but is not the SESS_INIT)
+ const gint cmp_sess_init = tcpcl_frame_loc_compare(ctx->cur_loc, ctx->tx_peer->sess_init_seen, NULL);
+ if (((msgtype == TCPCLV4_MSGTYPE_SESS_INIT) && (cmp_sess_init < 0))
+ || ((msgtype != TCPCLV4_MSGTYPE_SESS_INIT) && (cmp_sess_init <= 0))) {
+ expert_add_info(pinfo, item_msg, &ei_tcpclv4_sess_init_missing);
+ }
+ }
+ }
+
+ if (msgtype_name) {
+ col_append_sep_str(pinfo->cinfo, COL_INFO, NULL, msgtype_name);
+ }
+
+ try_negotiate(ctx, pinfo);
+ // Show negotiation results
+ if (msgtype == TCPCLV4_MSGTYPE_SESS_INIT) {
+ if (ctx->convo->sess_negotiated) {
+ if (ctx->rx_peer->sess_init_seen){
+ proto_item *item_nego = proto_tree_add_uint(tree_msg, hf_tcpclv4_sess_init_related, tvb, 0, 0, ctx->rx_peer->sess_init_seen->frame_num);
+ PROTO_ITEM_SET_GENERATED(item_nego);
+ }
+ {
+ proto_item *item_nego = proto_tree_add_uint(tree_msg, hf_tcpclv4_negotiate_keepalive, tvb, 0, 0, ctx->convo->sess_keepalive);
+ PROTO_ITEM_SET_GENERATED(item_nego);
+ }
+ }
+ }
+
+ return offset;
+}
+
+static guint get_message_len(packet_info *pinfo, tvbuff_t *tvb, int ext_offset, void *data _U_) {
+ tcpcl_dissect_ctx_t *ctx = tcpcl_dissect_ctx_get(tvb, pinfo, ext_offset);
+ if (!ctx) {
+ return 0;
+ }
+ const guint init_offset = ext_offset;
+ guint offset = ext_offset;
+
+ if (ctx->is_contact) {
+ offset += 4;
+ guint8 version = tvb_get_guint8(tvb, offset);
+ offset += 1;
+ if (version == 3) {
+ offset += 3;
+ int bytecount;
+ int len = evaluate_sdnv(tvb, offset, &bytecount);
+ offset += len + 1;
+ }
+ else if (version == 4) {
+ offset += 1;
+ }
+ else {
+ return 0;
+ }
+ }
+ else {
+ if (ctx->tx_peer->version == 3) {
+ guint sublen = get_v3_msg_len(pinfo, tvb, offset, ctx);
+ if (sublen == 0) {
+ return 0;
+ }
+ offset += sublen;
+ }
+ else if (ctx->tx_peer->version == 4) {
+ guint sublen = get_v4_msg_len(pinfo, tvb, offset, ctx);
+ if (sublen == 0) {
+ return 0;
+ }
+ offset += sublen;
+ }
+ else {
+ return 0;
+ }
+ }
+ const int needlen = offset - init_offset;
+ return needlen;
+}
+
+static gint dissect_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) {
+ gint offset = 0;
+ tcpcl_dissect_ctx_t *ctx = tcpcl_dissect_ctx_get(tvb, pinfo, offset);
+ if (!ctx) {
+ return 0;
+ }
+
+ {
+ const gchar *proto_name = col_get_text(pinfo->cinfo, COL_PROTOCOL);
+ if (g_strcmp0(proto_name, proto_name_tcpcl) != 0) {
+ col_set_str(pinfo->cinfo, COL_PROTOCOL, proto_name_tcpcl);
+ col_clear(pinfo->cinfo, COL_INFO);
+ }
+ }
+
+ // Don't add more than one TCPCL tree item
+ proto_item *item_tcpcl;
+ proto_tree *tree_tcpcl;
+ if (tree && (tree->last_child)
+ && (tree->last_child->finfo->hfinfo->id == proto_tcpcl)) {
+ item_tcpcl = tree->last_child;
+ tree_tcpcl = proto_item_get_subtree(item_tcpcl);
+ }
+ else {
+ item_tcpcl = proto_tree_add_item(tree, proto_tcpcl, tvb, 0, 0, ENC_NA);
+ tree_tcpcl = proto_item_add_subtree(item_tcpcl, ett_proto_tcpcl);
+ }
+
+ if (ctx->is_contact) {
+ col_append_sep_str(pinfo->cinfo, COL_INFO, NULL, "Contact Header");
+
+ proto_item *item_chdr = proto_tree_add_item(tree_tcpcl, hf_chdr_tree, tvb, offset, -1, ENC_NA);
+ proto_tree *tree_chdr = proto_item_add_subtree(item_chdr, ett_chdr);
+
+ const void *magic_data = tvb_memdup(wmem_packet_scope(), tvb, offset, sizeof(magic));
+ proto_item *item_magic = proto_tree_add_bytes(tree_chdr, hf_chdr_magic, tvb, offset, 4, magic_data);
+ offset += sizeof(magic);
+ if (memcmp(magic_data, magic, sizeof(magic)) != 0) {
+ expert_add_info(pinfo, item_magic, &ei_invalid_magic);
+ return offset;
+ }
+
+ ctx->tx_peer->version = tvb_get_guint8(tvb, offset);
+ proto_item *item_version = proto_tree_add_uint(tree_chdr, hf_chdr_version, tvb, offset, 1, ctx->tx_peer->version);
+ offset += 1;
+
+ // Mark or check version match
+ if (!ctx->convo->version) {
+ ctx->convo->version = wmem_new(wmem_file_scope(), guint8);
+ *(ctx->convo->version) = ctx->tx_peer->version;
+ }
+ else if (*(ctx->convo->version) != ctx->tx_peer->version) {
+ expert_add_info(pinfo, item_version, &ei_mismatch_version);
+ }
+
+ if ((ctx->tx_peer->version < 3) || (ctx->tx_peer->version > 4)) {
+ expert_add_info(pinfo, item_version, &ei_invalid_version);
+ return offset;
+ }
+
+ if (ctx->tx_peer->version == 3) {
+ /* Subtree to expand the bits in the Contact Header Flags */
+ proto_tree_add_bitmask(tree_chdr, tvb, offset, hf_tcpclv3_chdr_flags, ett_tcpclv3_chdr_flags, v3_chdr_flags, ENC_BIG_ENDIAN);
+ offset++;
+
+ proto_tree_add_item(tree_chdr, hf_tcpclv3_chdr_keep_alive, tvb, offset, 2, ENC_BIG_ENDIAN);
+ offset += 2;
+
+ /*
+ * New format Contact header has length field followed by Bundle Header.
+ */
+ int sdnv_length;
+ expert_field *ei_bundle_sdnv_length;
+ int eid_length = evaluate_sdnv_ei(tvb, offset, &sdnv_length, &ei_bundle_sdnv_length);
+ proto_item *item_len = proto_tree_add_int(tree_chdr, hf_tcpclv3_chdr_local_eid_length, tvb, offset, sdnv_length, eid_length);
+ offset += sdnv_length;
+ if (ei_bundle_sdnv_length) {
+ expert_add_info(pinfo, item_len, ei_bundle_sdnv_length);
+ return offset;
+ }
+
+ proto_tree_add_item(tree_chdr, hf_tcpclv3_chdr_local_eid, tvb, offset, eid_length, ENC_NA|ENC_ASCII);
+ offset += eid_length;
+
+ // assumed parameters
+ ctx->tx_peer->segment_mru = G_MAXUINT64;
+ ctx->tx_peer->transfer_mru = G_MAXUINT64;
+ }
+ else if (ctx->tx_peer->version == 4) {
+ guint8 flags = tvb_get_guint8(tvb, offset);
+ proto_tree_add_bitmask(tree_chdr, tvb, offset, hf_tcpclv4_chdr_flags, ett_tcpclv4_chdr_flags, v4_chdr_flags, ENC_BIG_ENDIAN);
+ offset += 1;
+
+ ctx->tx_peer->can_tls = (flags & TCPCLV4_CONTACT_FLAG_CANTLS);
+ }
+
+ proto_item_set_len(item_chdr, offset);
+
+ if (ctx->tx_peer->chdr_seen) {
+ if (tcpcl_analyze_sequence) {
+ if (!tcpcl_frame_loc_equal(ctx->tx_peer->chdr_seen, ctx->cur_loc)) {
+ expert_add_info(pinfo, item_chdr, &ei_chdr_duplicate);
+ }
+ }
+ }
+ else {
+ ctx->tx_peer->chdr_seen = tcpcl_frame_loc_clone(wmem_file_scope(), ctx->cur_loc);
+ }
+
+ try_negotiate(ctx, pinfo);
+ // Show negotiation results
+ if (ctx->convo->contact_negotiated) {
+ if (ctx->rx_peer->chdr_seen) {
+ proto_item *item_nego = proto_tree_add_uint(tree_chdr, hf_chdr_related, tvb, 0, 0, ctx->rx_peer->chdr_seen->frame_num);
+ PROTO_ITEM_SET_GENERATED(item_nego);
+ }
+ if (ctx->tx_peer->version == 4) {
+ proto_item *item_nego = proto_tree_add_boolean(tree_chdr, hf_tcpclv4_negotiate_use_tls, tvb, 0, 0, ctx->convo->session_use_tls);
+ PROTO_ITEM_SET_GENERATED(item_nego);
+ }
+ }
+ }
+ else {
+ if (ctx->tx_peer->version == 3) {
+ offset += dissect_v3_msg(tvb, pinfo, tree_tcpcl, ctx);
+ }
+ else if (ctx->tx_peer->version == 4) {
+ offset += dissect_v4_msg(tvb, pinfo, tree_tcpcl, ctx);
+ }
+ }
+
+ const int item_len = proto_item_get_len(item_tcpcl);
+ gboolean is_new_item_tcpcl = (item_len <= 0);
+ if (is_new_item_tcpcl) {
+ proto_item_set_len(item_tcpcl, offset);
+ proto_item_append_text(item_tcpcl, " Version %d", ctx->tx_peer->version);
+ }
+ else {
+ proto_item_set_len(item_tcpcl, item_len + offset);
+ }
+
+ if (ctx->xferload) {
+ col_append_str(pinfo->cinfo, COL_INFO, " [Bundle]");
+
+ if (tcpcl_decode_bundle) {
+ if (bundle_handle) {
+ call_dissector(
+ bundle_handle,
+ ctx->xferload,
+ pinfo,
+ tree
+ );
+ }
+ }
+ }
+
+ return offset;
+}
+
+static int
+dissect_tcpcl(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
+{
+ /* Retrieve information from conversation, or add it if it isn't
+ * there yet */
+ conversation_t *convo = find_or_create_conversation(pinfo);
+ tcpcl_conversation_t *tcpcl_convo = (tcpcl_conversation_t *)conversation_get_proto_data(convo, proto_tcpcl);
+ if (!tcpcl_convo) {
+ tcpcl_convo = tcpcl_conversation_new();
+ conversation_add_proto_data(convo, proto_tcpcl, tcpcl_convo);
+ // Assume the first source (i.e. TCP initiator) is the active node
+ copy_address_wmem(wmem_file_scope(), &(tcpcl_convo->active->addr), &(pinfo->src));
+ tcpcl_convo->active->port = pinfo->srcport;
+ copy_address_wmem(wmem_file_scope(), &(tcpcl_convo->passive->addr), &(pinfo->dst));
+ tcpcl_convo->passive->port = pinfo->destport;
+ }
+
+ tcp_dissect_pdus(tvb, pinfo, tree, TRUE, 1, get_message_len, dissect_message, NULL);
+
+ const guint buflen = tvb_captured_length(tvb);
+ return buflen;
+}
+
+static int dissect_xferext_transferlen(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, void *data _U_) {
+ int offset = 0;
+ tcpcl_dissect_ctx_t *ctx = tcpcl_dissect_ctx_get(tvb, pinfo, offset);
+ if (!ctx) {
+ return 0;
+ }
+
+ guint64 total_len = tvb_get_guint64(tvb, offset, ENC_BIG_ENDIAN);
+ proto_item *item_len = proto_tree_add_uint64(tree, hf_tcpclv4_xferext_transferlen_total_len, tvb, offset, 8, total_len);
+ offset += 8;
+ if (total_len > ctx->rx_peer->transfer_mru) {
+ expert_add_info(pinfo, item_len, &ei_tcpclv4_xferload_over_xfer_mru);
+ }
+
+ if (tcpcl_analyze_sequence) {
+ guint64 *xfer_id = wmem_map_lookup(ctx->tx_peer->frame_loc_to_transfer, ctx->cur_loc);
+ if (xfer_id) {
+ tcpcl_transfer_t *xfer = get_or_create_transfer_t(ctx->tx_peer->transfers, *xfer_id);
+ xfer->total_length = wmem_new(wmem_file_scope(), guint64);
+ *(xfer->total_length) = total_len;
+ }
+ }
+
+ return offset;
+}
+
+static int dissect_othername_bundleeid(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) {
+ int offset = 0;
+ asn1_ctx_t actx;
+ asn1_ctx_init(&actx, ASN1_ENC_BER, TRUE, pinfo);
+ offset += dissect_ber_restricted_string(
+ FALSE, BER_UNI_TAG_IA5String,
+ &actx, tree, tvb, offset, hf_othername_bundleeid, NULL
+ );
+ return offset;
+}
+
+/// Re-initialize after a configuration change
+static void reinit_tcpcl(void) {
+}
+
+void
+proto_register_tcpclv3(void)
+{
+ expert_module_t *expert_tcpcl;
+
+ proto_tcpcl = proto_register_protocol(
+ "DTN TCP Convergence Layer Protocol",
+ "TCPCL",
+ "tcpcl"
+ );
+
+ proto_register_field_array(proto_tcpcl, hf_tcpcl, array_length(hf_tcpcl));
+ proto_register_subtree_array(ett, array_length(ett));
+ expert_tcpcl = expert_register_protocol(proto_tcpcl);
+ expert_register_field_array(expert_tcpcl, ei_tcpcl, array_length(ei_tcpcl));
+
+ tcpcl_handle = create_dissector_handle(dissect_tcpcl, proto_tcpcl);
+ sess_ext_dissectors = register_dissector_table("tcpcl.v4.sess_ext", "TCPCLv4 Session Extension", proto_tcpcl, FT_UINT16, BASE_DEC);
+ xfer_ext_dissectors = register_dissector_table("tcpcl.v4.xfer_ext", "TCPCLv4 Transfer Extension", proto_tcpcl, FT_UINT16, BASE_DEC);
+
+ module_t *module_tcpcl = prefs_register_protocol(proto_tcpcl, reinit_tcpcl);
+ prefs_register_bool_preference(
+ module_tcpcl,
+ "analyze_sequence",
+ "Analyze message sequences",
+ "Whether the TCPCLv4 dissector should analyze the sequencing of "
+ "the messages within each session.",
+ &tcpcl_analyze_sequence
+ );
+ prefs_register_bool_preference(
+ module_tcpcl,
+ "desegment_transfer",
+ "Reassemble the segments of each transfer",
+ "Whether the TCPCLv4 dissector should combine the sequential segments "
+ "of a transfer into the full bundle being transfered."
+ "To use this option, you must also enable "
+ "\"Allow subdissectors to reassemble TCP streams\" "
+ "in the TCP protocol settings.",
+ &tcpcl_desegment_transfer
+ );
+ prefs_register_bool_preference(
+ module_tcpcl,
+ "decode_bundle",
+ "Decode bundle data",
+ "If enabled, the bundle will be decoded as BPv7 content. "
+ "Otherwise, it is assumed to be plain CBOR.",
+ &tcpcl_decode_bundle
+ );
+
+ reassembly_table_register(
+ &xfer_reassembly_table,
+ &addresses_reassembly_table_functions
+ );
+
+}
+
+void
+proto_reg_handoff_tcpclv3(void)
+{
+ tls_handle = find_dissector_add_dependency("tls", proto_tcpcl);
+ bundle_handle = find_dissector("bundle");
+
+ dissector_add_uint_with_preference("tcp.port", BUNDLE_PORT, tcpcl_handle);
+
+ /* Packaged extensions */
+ {
+ dissector_handle_t dis_h = create_dissector_handle(dissect_xferext_transferlen, proto_tcpcl);
+ dissector_add_uint("tcpcl.v4.xfer_ext", TCPCLV4_XFEREXT_TRANSFER_LEN, dis_h);
+ }
+
+ register_ber_oid_dissector("1.3.6.1.5.5.7.3.35", NULL, proto_tcpcl, "id-kp-bundleSecurity");
+ register_ber_oid_dissector("1.3.6.1.5.5.7.8.11", dissect_othername_bundleeid, proto_tcpcl, "id-on-bundleEID");
+
+ reinit_tcpcl();
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/epan/dissectors/packet-tcpcl.h b/epan/dissectors/packet-tcpcl.h
new file mode 100644
index 0000000000..07dcd1de8c
--- /dev/null
+++ b/epan/dissectors/packet-tcpcl.h
@@ -0,0 +1,242 @@
+/* packet-tcpcl.h
+ * References:
+ * RFC 7242: https://tools.ietf.org/html/rfc7242
+ * TCPCLv4: https://www.ietf.org/archive/id/draft-ietf-dtn-tcpclv4-28.html
+ *
+ * TCPCLv4 portions copyright 2019-2021, Brian Sipos <brian.sipos@gmail.com>
+ * Copyright 2006-2007 The MITRE Corporation.
+ * All Rights Reserved.
+ * Approved for Public Release; Distribution Unlimited.
+ * Tracking Number 07-0090.
+ *
+ * The US Government will not be charged any license fee and/or royalties
+ * related to this software. Neither name of The MITRE Corporation; nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#ifndef PACKET_TCPCL_H
+#define PACKET_TCPCL_H
+
+#include <ws_symbol_export.h>
+#include <epan/tvbuff.h>
+#include <epan/proto.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Extension points for TCPCLv4 are available as:
+ * For session extension item dissectors, the dissector table
+ * "tcpcl.v4.sess_ext" has a FT_UINT16 key for registering.
+ * For transfer extension item dissectors, the dissector table
+ * "tcpcl.v4.xfer_ext" has a FT_UINT16 key for registering.
+ * Both have user data dissection context which is obtained with the
+ * tcpcl_dissect_ctx_get() function.
+ */
+
+/* TCP Convergence Layer v3 - Message Types */
+typedef enum {
+ TCPCLV3_TYPE_MASK = 0xf0,
+ TCPCLV3_DATA_SEGMENT = 0x10,
+ TCPCLV3_ACK_SEGMENT = 0x20,
+ TCPCLV3_REFUSE_BUNDLE = 0x30,
+ TCPCLV3_KEEP_ALIVE = 0x40,
+ TCPCLV3_SHUTDOWN = 0x50,
+ TCPCLV3_LENGTH = 0x60,
+} Tcpclv3MessageType;
+
+/* TCP Convergence Layer - Contact Header Flags */
+typedef enum {
+ TCPCLV3_BUNDLE_ACK_FLAG = 0x01,
+ TCPCLV3_REACTIVE_FRAG_FLAG = 0x02,
+ TCPCLV3_CONNECTOR_RCVR_FLAG = 0x04,
+} Tcpclv3ContactFlag;
+
+/* TCP Convergence Layer - Data Segment Flags */
+typedef enum {
+ TCPCLV3_DATA_FLAGS = 0x03,
+ TCPCLV3_DATA_END_FLAG = 0x01,
+ TCPCLV3_DATA_START_FLAG = 0x02,
+} Tcpclv3DataSegmentFlag;
+
+/* TCP Convergence Layer - Shutdown Segment Flags */
+typedef enum {
+ TCPCLV3_SHUTDOWN_FLAGS = 0x03,
+ TCPCLV3_SHUTDOWN_REASON = 0x02,
+ TCPCLV3_SHUTDOWN_DELAY = 0x01,
+} Tcpclv3ShutdownFlag;
+
+/* REFUSE-BUNDLE Reason-Codes */
+typedef enum {
+ TCPCLV3_REFUSE_REASON_UNKNOWN = 0x00,
+ TCPCLV3_REFUSE_REASON_RX_COMPLETE = 0x01,
+ TCPCLV3_REFUSE_REASON_RX_EXHAUSTED = 0x02,
+ TCPCLV3_REFUSE_REASON_RX_RETRANSMIT = 0x03,
+ /* 0x4-0x7 - Unassigned
+ * 0x8-0xf - Reserved for future Use */
+} Tcpclv3RefuseType;
+
+typedef enum {
+ TCPCLV4_MSGTYPE_INVALID = 0x00,
+ TCPCLV4_MSGTYPE_XFER_SEGMENT = 0x01,
+ TCPCLV4_MSGTYPE_XFER_ACK = 0x02,
+ TCPCLV4_MSGTYPE_XFER_REFUSE = 0x03,
+ TCPCLV4_MSGTYPE_KEEPALIVE = 0x04,
+ TCPCLV4_MSGTYPE_SESS_TERM = 0x05,
+ TCPCLV4_MSGTYPE_MSG_REJECT = 0x06,
+ TCPCLV4_MSGTYPE_SESS_INIT = 0x07,
+} Tcpclv4MessageType;
+
+typedef enum {
+ TCPCLV4_SESSEXT_INVALID = 0x00,
+} Tcpclv4SessExtenionType;
+
+typedef enum {
+ TCPCLV4_XFEREXT_INVALID = 0x00,
+ TCPCLV4_XFEREXT_TRANSFER_LEN = 0x01,
+} Tcpclv4XferExtenionType;
+
+typedef enum {
+ TCPCLV4_CONTACT_FLAG_CANTLS = 0x01,
+} Tcpclv4ContactFlag;
+
+typedef enum {
+ TCPCLV4_SESS_TERM_FLAG_REPLY = 0x01,
+} Tcpclv4SessTermFlag;
+
+typedef enum {
+ TCPCLV4_TRANSFER_FLAG_START = 0x02,
+ TCPCLV4_TRANSFER_FLAG_END = 0x01,
+} Tcpclv4TransferFlag;
+
+typedef enum {
+ TCPCLV4_EXTENSION_FLAG_CRITICAL = 0x01,
+} Tcpclv4ExtensionFlag;
+
+/// Finer grained locating than just the frame number
+typedef struct {
+ /// Index of the frame
+ guint32 frame_num;
+ /// Source index within the frame
+ gint src_ix;
+ /// Offset within the source TVB
+ gint raw_offset;
+} tcpcl_frame_loc_t;
+
+typedef struct {
+ /// Ordered list of seg_meta_t* for XFER_SEGMENT as seen in the first scan.
+ wmem_list_t *seg_list;
+
+ /// Ordered list of ack_meta_t* for XFER_ACK as seen in the first scan.
+ wmem_list_t *ack_list;
+
+ /// Optional Transfer Length extension
+ guint64 *total_length;
+} tcpcl_transfer_t;
+
+typedef struct {
+ /// Address for this peer
+ address addr;
+ /// Port for the this peer
+ guint32 port;
+
+ /// Frame number in which the contact header starts
+ tcpcl_frame_loc_t *chdr_seen;
+ /// TCPCL version seen from this peer
+ guint8 version;
+ /// CAN_TLS flag from the contact header
+ gboolean can_tls;
+
+ /// Frame number in which the v4 SESS_INIT message starts
+ tcpcl_frame_loc_t *sess_init_seen;
+ /// Keepalive duration (s) from v4 SESS_INIT
+ guint16 keepalive;
+ /// Segment MRU
+ guint64 segment_mru;
+ /// Transfer MRU
+ guint64 transfer_mru;
+
+ /// Frame number in which the SESS_TERM message starts
+ tcpcl_frame_loc_t *sess_term_seen;
+ /// SESS_TERM reason
+ guint8 sess_term_reason;
+
+ /// Map from tcpcl_frame_loc_t* to possible associated transfer ID guint64*
+ wmem_map_t *frame_loc_to_transfer;
+
+ /// Map from transfer ID guint64* to tcpcl_transfer_t* sent from this peer
+ wmem_map_t *transfers;
+} tcpcl_peer_t;
+
+/// Persistent state associated with a TCP conversation
+typedef struct {
+ /// Information for the active side of the session
+ tcpcl_peer_t *active;
+ /// Information for the passive side of the session
+ tcpcl_peer_t *passive;
+
+ /// Set to the first TCPCL version seen.
+ /// Used later for validity check.
+ guint8 *version;
+ /// True when contact negotiation is finished
+ gboolean contact_negotiated;
+ /// Negotiated use of TLS from @c can_tls of the peers
+ gboolean session_use_tls;
+ /// The last frame before TLS handshake
+ tcpcl_frame_loc_t *session_tls_start;
+
+ /// True when session negotiation is finished
+ gboolean sess_negotiated;
+ /// Negotiated session keepalive
+ guint16 sess_keepalive;
+} tcpcl_conversation_t;
+
+/// Context for a single packet dissection
+typedef struct {
+ tcpcl_conversation_t *convo;
+ /// Dissection cursor
+ tcpcl_frame_loc_t *cur_loc;
+ /// True if the dissection is on a contact header
+ gboolean is_contact;
+ /// The sending peer
+ tcpcl_peer_t *tx_peer;
+ /// The receiving peer
+ tcpcl_peer_t *rx_peer;
+ /// Possible transfer payload
+ tvbuff_t *xferload;
+} tcpcl_dissect_ctx_t;
+
+/** Initialize members of the dissection context.
+ *
+ * @param pinfo Packet info for the frame.
+ * @param tvb The buffer dissected.
+ * @param offset The start offset.
+ * @return ctx The new packet context.
+ */
+WS_DLL_PUBLIC
+tcpcl_dissect_ctx_t * tcpcl_dissect_ctx_get(tvbuff_t *tvb, packet_info *pinfo, const gint offset);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* PACKET_TCPCL_H */
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/epan/dissectors/packet-tcpclv3.c b/epan/dissectors/packet-tcpclv3.c
deleted file mode 100644
index 2f7734be00..0000000000
--- a/epan/dissectors/packet-tcpclv3.c
+++ /dev/null
@@ -1,775 +0,0 @@
-/* packet-tcpclv3.c
- * References:
- * RFC 7242: https://tools.ietf.org/html/rfc7242
- *
- * Copyright 2006-2007 The MITRE Corporation.
- * All Rights Reserved.
- * Approved for Public Release; Distribution Unlimited.
- * Tracking Number 07-0090.
- *
- * The US Government will not be charged any license fee and/or royalties
- * related to this software. Neither name of The MITRE Corporation; nor the
- * names of its contributors may be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * Wireshark - Network traffic analyzer
- * By Gerald Combs <gerald@wireshark.org>
- * Copyright 1998 Gerald Combs
- *
- * SPDX-License-Identifier: GPL-2.0-or-later
- *
- * Specification reference:
- * RFC 5050
- * https://tools.ietf.org/html/rfc5050
- */
-
-/*
- * Modifications were made to this file under designation MFS-33289-1 and
- * are Copyright 2015 United States Government as represented by NASA
- * Marshall Space Flight Center. All Rights Reserved.
- *
- * Released under the GNU GPL with NASA legal approval granted 2016-06-10.
- *
- * The subject software is provided "AS IS" WITHOUT ANY WARRANTY of any kind,
- * either expressed, implied or statutory and this agreement does not,
- * in any manner, constitute an endorsement by government agency of any
- * results, designs or products resulting from use of the subject software.
- * See the Agreement for the specific language governing permissions and
- * limitations.
- */
-
-#include "config.h"
-
-#include <stdio.h>
-#include <epan/packet.h>
-#include <epan/reassemble.h>
-#include <epan/expert.h>
-#include "packet-tcpclv3.h"
-#include "packet-bpv6.h"
-#include "packet-tcp.h"
-
-/* For Reassembling TCP Convergence Layer segments */
-static reassembly_table msg_reassembly_table;
-
-static const char magic[] = {'d', 't', 'n', '!'};
-
-static int proto_tcp_conv = -1;
-
-/* TCP Convergence Header Variables */
-static int hf_tcp_convergence_pkt_type = -1;
-
-/* Refuse-Bundle reason code */
-static int hf_dtn_refuse_bundle_reason_code = -1;
-
-static int hf_contact_hdr_version = -1;
-static int hf_contact_hdr_flags = -1;
-static int hf_contact_hdr_keep_alive = -1;
-static int hf_contact_hdr_flags_ack_req = -1;
-static int hf_contact_hdr_flags_frag_enable = -1;
-static int hf_contact_hdr_flags_nak = -1;
-static int hf_contact_hdr_magic = -1;
-static int hf_contact_hdr_local_eid_length = -1;
-static int hf_contact_hdr_local_eid = -1;
-
-/* TCP Convergence Data Header Variables */
-static int hf_tcp_convergence_data_procflags = -1;
-static int hf_tcp_convergence_data_procflags_start = -1;
-static int hf_tcp_convergence_data_procflags_end = -1;
-static int hf_tcp_convergence_data_segment_length = -1;
-
-/* TCP Convergence Ack Variables */
-static int hf_tcp_convergence_ack_length = -1;
-
-/* TCP Convergence Shutdown Header Variables */
-static int hf_tcp_convergence_shutdown_flags = -1;
-static int hf_tcp_convergence_shutdown_flags_reason = -1;
-static int hf_tcp_convergence_shutdown_flags_delay = -1;
-static int hf_tcp_convergence_shutdown_reason = -1;
-static int hf_tcp_convergence_shutdown_delay = -1;
-
-/*TCP Convergence Layer Reassembly boilerplate*/
-static int hf_msg_fragments = -1;
-static int hf_msg_fragment = -1;
-static int hf_msg_fragment_overlap = -1;
-static int hf_msg_fragment_overlap_conflicts = -1;
-static int hf_msg_fragment_multiple_tails = -1;
-static int hf_msg_fragment_too_long_fragment = -1;
-static int hf_msg_fragment_error = -1;
-static int hf_msg_fragment_count = -1;
-static int hf_msg_reassembled_in = -1;
-static int hf_msg_reassembled_length = -1;
-
-/* Tree Node Variables */
-static gint ett_conv_flags = -1;
-static gint ett_shutdown_flags = -1;
-static gint ett_contact_hdr_flags = -1;
-static gint ett_tcp_conv = -1;
-static gint ett_tcp_conv_hdr = -1;
-static gint ett_msg_fragment = -1;
-static gint ett_msg_fragments = -1;
-
-static expert_field ei_tcp_convergence_data_flags = EI_INIT;
-static expert_field ei_tcp_convergence_segment_length = EI_INIT;
-static expert_field ei_tcp_convergence_ack_length = EI_INIT;
-
-
-static dissector_handle_t bundle_handle;
-
-typedef struct dictionary_data {
- int bundle_header_dict_length;
-
- int dest_scheme_offset;
- int dst_scheme_pos;
- int dst_scheme_len;
- int source_scheme_offset;
- int src_scheme_pos;
- int src_scheme_len;
- int report_scheme_offset;
- int rpt_scheme_pos;
- int rpt_scheme_len;
- int cust_scheme_offset;
- int cust_scheme_pos;
- int cust_scheme_len;
- int dest_ssp_offset;
- int dst_ssp_len;
- int source_ssp_offset;
- int src_ssp_len;
- int report_ssp_offset;
- int rpt_ssp_len;
- int cust_ssp_offset;
- int cust_ssp_len;
-
-} dictionary_data_t;
-
-
-static const value_string packet_type_vals[] = {
- {((TCP_CONVERGENCE_DATA_SEGMENT>>4) & 0x0F), "Data"},
- {((TCP_CONVERGENCE_ACK_SEGMENT>>4) & 0x0F), "Ack"},
- {((TCP_CONVERGENCE_REFUSE_BUNDLE>>4) & 0x0F), "Refuse Bundle"},
- {((TCP_CONVERGENCE_KEEP_ALIVE>>4) & 0x0F), "Keep Alive"},
- {((TCP_CONVERGENCE_SHUTDOWN>>4) & 0x0F), "Shutdown"},
- {((TCP_CONVERGENCE_LENGTH>>4) & 0x0F), "Length"},
- {0, NULL}
-};
-
-/* Refuse-Bundle Reason-Code Flags as per RFC-7242: Section-5.4 */
-static const value_string refuse_bundle_reason_code[] = {
- {TCP_REFUSE_BUNDLE_REASON_UNKNOWN, "Reason for refusal is unknown"},
- {TCP_REFUSE_BUNDLE_REASON_RX_COMPLETE, "Complete Bundle Received"},
- {TCP_REFUSE_BUNDLE_REASON_RX_EXHAUSTED, "Receiver's resources exhausted"},
- {TCP_REFUSE_BUNDLE_REASON_RX_RETRANSMIT, "Receiver expects re-transmission of bundle"},
- {0, NULL}
-};
-
-static const fragment_items msg_frag_items = {
- /*Fragment subtrees*/
- &ett_msg_fragment,
- &ett_msg_fragments,
- /*Fragment Fields*/
- &hf_msg_fragments,
- &hf_msg_fragment,
- &hf_msg_fragment_overlap,
- &hf_msg_fragment_overlap_conflicts,
- &hf_msg_fragment_multiple_tails,
- &hf_msg_fragment_too_long_fragment,
- &hf_msg_fragment_error,
- &hf_msg_fragment_count,
- /*Reassembled in field*/
- &hf_msg_reassembled_in,
- /*Reassembled length field*/
- &hf_msg_reassembled_length,
- /* Reassembled data field */
- NULL,
- /*Tag*/
- "Message fragments"
-};
-
-static guint
-get_dtn_contact_header_len(packet_info *pinfo _U_, tvbuff_t *tvb,
- int offset, void *data _U_)
-{
- int len, bytecount;
-
- /* get length from sdnv */
- len = evaluate_sdnv(tvb, offset+8, &bytecount);
- if (len < 0)
- return 0;
-
- return len+bytecount+8;
-}
-
-static int
-dissect_dtn_contact_header(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
-{
- proto_item *ti;
- proto_tree *conv_proto_tree, *conv_tree, *conv_flag_tree;
- int eid_length, sdnv_length;
- int offset = 0;
-
- col_set_str(pinfo->cinfo, COL_PROTOCOL, "TCPCLv3");
- col_clear(pinfo->cinfo,COL_INFO); /* Clear out stuff in the info column */
- col_add_str(pinfo->cinfo, COL_INFO, "Contact Header");
-
- ti = proto_tree_add_item(tree, proto_tcp_conv, tvb, offset, -1, ENC_NA);
- conv_proto_tree = proto_item_add_subtree(ti, ett_tcp_conv);
-
- conv_tree = proto_tree_add_subtree(conv_proto_tree, tvb, offset, -1, ett_tcp_conv, NULL, "Contact Header");
-
- proto_tree_add_item(conv_tree, hf_contact_hdr_magic, tvb, offset, 4, ENC_NA|ENC_ASCII);
- offset += 4;
- proto_tree_add_item(conv_tree, hf_contact_hdr_version, tvb, offset, 1, ENC_BIG_ENDIAN);
- offset++;
-
- /* Subtree to expand the bits in the Contact Header Flags */
- ti = proto_tree_add_item(conv_tree, hf_contact_hdr_flags, tvb, offset, 1, ENC_BIG_ENDIAN);
- conv_flag_tree = proto_item_add_subtree(ti, ett_contact_hdr_flags);
- proto_tree_add_item(conv_flag_tree, hf_contact_hdr_flags_ack_req, tvb, offset, 1, ENC_BIG_ENDIAN);
- proto_tree_add_item(conv_flag_tree, hf_contact_hdr_flags_frag_enable, tvb, offset, 1, ENC_BIG_ENDIAN);
- proto_tree_add_item(conv_flag_tree, hf_contact_hdr_flags_nak, tvb, offset, 1, ENC_BIG_ENDIAN);
- offset++;
-
- proto_tree_add_item(conv_tree, hf_contact_hdr_keep_alive, tvb, offset, 2, ENC_BIG_ENDIAN);
- offset += 2;
-
- /*
- * New format Contact header has length field followed by Bundle Header.
- */
- expert_field *ei_bundle_sdnv_length;
- eid_length = evaluate_sdnv_ei(tvb, offset, &sdnv_length, &ei_bundle_sdnv_length);
- ti = proto_tree_add_int(tree, hf_contact_hdr_local_eid_length, tvb, offset, sdnv_length, eid_length);
- if (ei_bundle_sdnv_length) {
- expert_add_info(pinfo, ti, ei_bundle_sdnv_length);
- return offset;
- }
-
- proto_tree_add_item(conv_tree, hf_contact_hdr_local_eid, tvb, sdnv_length + offset, eid_length, ENC_NA|ENC_ASCII);
- return tvb_captured_length(tvb);
-}
-
-static guint
-get_tcpcl_pdu_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset, void *data _U_)
-{
- int len, bytecount;
- guint8 conv_hdr = tvb_get_guint8(tvb, offset);
-
- switch (conv_hdr & TCP_CONVERGENCE_TYPE_MASK)
- {
- case TCP_CONVERGENCE_DATA_SEGMENT:
- /* get length from sdnv */
- len = evaluate_sdnv(tvb, offset+1, &bytecount);
- if (len < 0)
- return 0;
-
- return len+bytecount+1;
-
- case TCP_CONVERGENCE_ACK_SEGMENT:
- /* get length from sdnv */
- len = evaluate_sdnv(tvb, offset+1, &bytecount);
- if (len < 0)
- return 0;
-
- return bytecount+1;
-
- case TCP_CONVERGENCE_KEEP_ALIVE:
- case TCP_CONVERGENCE_REFUSE_BUNDLE:
- /* always 1 byte */
- return 1;
- case TCP_CONVERGENCE_SHUTDOWN:
- len = 1;
-
- if (conv_hdr & TCP_CONVERGENCE_SHUTDOWN_REASON) {
- len += 1;
- }
- if (conv_hdr & TCP_CONVERGENCE_SHUTDOWN_DELAY) {
- len += 2;
- }
-
- return len;
-
- case TCP_CONVERGENCE_LENGTH:
- /* get length from sdnv */
- len = evaluate_sdnv(tvb, offset+1, &bytecount);
- if (len < 0)
- return 0;
- return bytecount+1;
-
- }
-
- /* This probably isn't a TCPCL/Bundle packet, so just stop dissection */
- return -1;
-}
-
-static int
-dissect_tcpcl_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data)
-{
- guint8 conv_hdr;
- guint8 refuse_bundle_hdr;
- int offset = 0;
- int sdnv_length, segment_length, convergence_hdr_size;
- proto_item *ci, *sub_item;
- proto_tree *conv_proto_tree, *conv_tree, *sub_tree;
- fragment_head *frag_msg;
- tvbuff_t *new_tvb;
- gboolean more_frags;
- int processed_length = 0;
- const gchar* col_text;
- gboolean bundle_in_col_info;
-
- static guint32 frag_id = 0;
- static guint32 last_frame = 0;
- static int last_raw_offset = 0;
-
- if (last_frame != pinfo->fd->num || tvb_raw_offset(tvb) < last_raw_offset)
- frag_id = 0;
- last_frame = pinfo->fd->num;
- last_raw_offset = tvb_raw_offset(tvb);
-
- col_set_str(pinfo->cinfo, COL_PROTOCOL, "TCPCL");
- col_clear(pinfo->cinfo,COL_INFO); /* Clear out stuff in the info column */
-
- col_text = col_get_text(pinfo->cinfo, COL_INFO);
- bundle_in_col_info = (col_text && strstr(col_text, " > "));
-
- ci = proto_tree_add_item(tree, proto_tcp_conv, tvb, offset, -1, ENC_NA);
- conv_proto_tree = proto_item_add_subtree(ci, ett_tcp_conv);
-
- conv_tree = proto_tree_add_subtree(conv_proto_tree, tvb, 0, -1, ett_tcp_conv_hdr, NULL, "TCP Convergence Header");
-
- conv_hdr = tvb_get_guint8(tvb, offset);
- proto_tree_add_item(conv_tree, hf_tcp_convergence_pkt_type, tvb, offset, 1, ENC_BIG_ENDIAN);
- col_add_str(pinfo->cinfo, COL_INFO, val_to_str_const((conv_hdr>>4)&0xF, packet_type_vals, "Unknown"));
-
- switch (conv_hdr & TCP_CONVERGENCE_TYPE_MASK)
- {
- case TCP_CONVERGENCE_DATA_SEGMENT:
- sub_item = proto_tree_add_item(conv_tree, hf_tcp_convergence_data_procflags, tvb,
- offset, 1, ENC_BIG_ENDIAN);
- sub_tree = proto_item_add_subtree(sub_item, ett_conv_flags);
- proto_tree_add_item(sub_tree, hf_tcp_convergence_data_procflags_start,
- tvb, offset, 1, ENC_BIG_ENDIAN);
- proto_tree_add_item(sub_tree, hf_tcp_convergence_data_procflags_end,
- tvb, offset, 1, ENC_BIG_ENDIAN);
-
- /* Only Start and End flags (bits 0 & 1) are valid in Data Segment */
- if ((conv_hdr & ~(TCP_CONVERGENCE_TYPE_MASK | TCP_CONVERGENCE_DATA_FLAGS)) != 0) {
- expert_add_info(pinfo, sub_item, &ei_tcp_convergence_data_flags);
- }
-
- segment_length = evaluate_sdnv(tvb, 1, &sdnv_length);
- sub_item = proto_tree_add_int(conv_tree, hf_tcp_convergence_data_segment_length, tvb, 1, sdnv_length, segment_length);
- if (segment_length < 0) {
- expert_add_info(pinfo, sub_item, &ei_tcp_convergence_segment_length);
- return 1;
- }
-
- convergence_hdr_size = sdnv_length + 1;
-
- /*
- * 1/11/2006 - If I got here, I should have a complete convergence layer
- * "segment" beginning at frame_offset. However that might not be a
- * complete bundle. Or there might be a complete bundle plus one or more
- * additional convergence layer headers.
- */
-
- new_tvb = NULL;
- sub_tree = NULL;
- if ((conv_hdr & TCP_CONVERGENCE_DATA_END_FLAG) == TCP_CONVERGENCE_DATA_END_FLAG) {
- more_frags = FALSE;
- }
- else {
- more_frags = TRUE;
- }
-
- frag_msg = fragment_add_seq_next(&msg_reassembly_table,
- tvb, offset + convergence_hdr_size,
- pinfo, frag_id, data,
- segment_length, more_frags);
-
- if (!more_frags) ++frag_id;
-
- processed_length = convergence_hdr_size + segment_length;
-
- if (frag_msg && !more_frags) {
-
- int save_fd_head_layer = frag_msg->reas_in_layer_num;
- frag_msg->reas_in_layer_num = pinfo->curr_layer_num;
-
- new_tvb = process_reassembled_data(tvb, offset + convergence_hdr_size,
- pinfo, "Reassembled DTN", frag_msg,
- &msg_frag_items, NULL,
- proto_tree_get_parent_tree(tree)
- );
-
- frag_msg->reas_in_layer_num = save_fd_head_layer;
- }
-
- if (new_tvb) {
- if (0 == call_dissector_with_data(bundle_handle, new_tvb, pinfo, sub_tree, data)) {
- /*Couldn't parse bundle, treat as raw data */
- call_data_dissector(new_tvb, pinfo, sub_tree);
- return tvb_captured_length(tvb);
- }
- }
- else {
-
- /*
- * If there are 2 segments, the second of which is very short, this
- * gets displayed instead of the usual Source EID/Destination EID in
- * the Bundle Dissection frame. If these statements are left out entirely,
- * nothing is displayed, i.e., there seems to be no way to get the
- * Source/Destination in the 2-segment case. I'll leave it in because I
- * think it is informative in the multi-segment case although confusing in the
- * 2-segment case.
- */
- col_add_str(pinfo->cinfo, COL_INFO, "[Bundle TCPCL Segment]");
- }
- break;
- case TCP_CONVERGENCE_ACK_SEGMENT:
- if (bundle_in_col_info) {
- if (!strstr(col_text, ", TCPL ACK")) {
- col_add_str(pinfo->cinfo, COL_INFO, ", TCPL ACK Segment(s)");
- }
- } else {
- col_set_str(pinfo->cinfo, COL_INFO, "TCPL ACK Segment(s)");
- }
- segment_length = evaluate_sdnv(tvb, offset+1, &sdnv_length);
- sub_item = proto_tree_add_int(conv_tree, hf_tcp_convergence_ack_length, tvb, offset+1, sdnv_length, segment_length);
- if (segment_length < 0) {
- expert_add_info(pinfo, sub_item, &ei_tcp_convergence_ack_length);
- processed_length = tvb_captured_length(tvb);
- } else {
- processed_length = sdnv_length + 1;
- }
- break;
- case TCP_CONVERGENCE_KEEP_ALIVE:
- if (bundle_in_col_info) {
- if (!strstr(col_text, ", TCPL KEEPALIVE")) {
- col_add_str(pinfo->cinfo, COL_INFO, ", TCPL KEEPALIVE Segment");
- }
- } else {
- col_set_str(pinfo->cinfo, COL_INFO, "TCPL KEEPALIVE Segment");
- }
- /*No valid flags in Keep Alive*/
- processed_length = 1;
- break;
-
- case TCP_CONVERGENCE_SHUTDOWN:
- if (bundle_in_col_info) {
- if (!strstr(col_text, ", TCPL SHUTDOWN")) {
- col_add_str(pinfo->cinfo, COL_INFO, ", TCPL SHUTDOWN Segment");
- }
- } else {
- col_set_str(pinfo->cinfo, COL_INFO, "TCPL SHUTDOWN Segment");
- }
- /* Add tree for Shutdown Flags */
- sub_item = proto_tree_add_item(conv_tree, hf_tcp_convergence_shutdown_flags, tvb,
- offset, 1, ENC_BIG_ENDIAN);
- sub_tree = proto_item_add_subtree(sub_item, ett_shutdown_flags);
-
- proto_tree_add_item(sub_tree, hf_tcp_convergence_shutdown_flags_reason,
- tvb, offset, 1, ENC_BIG_ENDIAN);
- proto_tree_add_item(sub_tree, hf_tcp_convergence_shutdown_flags_delay,
- tvb, offset, 1, ENC_BIG_ENDIAN);
-
- offset += 1;
- if (conv_hdr & TCP_CONVERGENCE_SHUTDOWN_REASON) {
- proto_tree_add_item(conv_tree,
- hf_tcp_convergence_shutdown_reason, tvb,
- offset, 1, ENC_BIG_ENDIAN);
- offset += 1;
- }
- if (conv_hdr & TCP_CONVERGENCE_SHUTDOWN_DELAY) {
- proto_tree_add_item(conv_tree,
- hf_tcp_convergence_shutdown_delay, tvb,
- offset, 2, ENC_BIG_ENDIAN);
- }
- break;
- case TCP_CONVERGENCE_REFUSE_BUNDLE:
- if (bundle_in_col_info) {
- if (!strstr(col_text, ", TCPL REFUSE")) {
- col_add_str(pinfo->cinfo, COL_INFO, ", TCPL REFUSE_BUNDLE Segment");
- }
- } else {
- col_set_str(pinfo->cinfo, COL_INFO, "TCPL REFUSE_BUNDLE Segment");
- }
-
- refuse_bundle_hdr = tvb_get_guint8(tvb, offset);
- proto_tree_add_item(conv_tree, hf_dtn_refuse_bundle_reason_code, tvb, offset, 1, ENC_BIG_ENDIAN);
- col_add_str(pinfo->cinfo, COL_INFO, val_to_str_const((refuse_bundle_hdr>>4)&0xF, refuse_bundle_reason_code, "Unknown"));
-
- /*No valid flags*/
- processed_length = tvb_captured_length(tvb);
- break;
- }
-
- return processed_length;
-}
-
-static int
-dissect_tcpcl(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
-{
- guint8 conv_hdr;
- int offset, bytecount;
- int processed_length;
-
- /* Make sure we have a convergence header byte */
- if (!tvb_bytes_exist(tvb, 0, 1))
- return 0;
-
- conv_hdr = tvb_get_guint8(tvb, 0);
- switch (conv_hdr & TCP_CONVERGENCE_TYPE_MASK)
- {
- case TCP_CONVERGENCE_DATA_SEGMENT:
- case TCP_CONVERGENCE_ACK_SEGMENT:
- /* ensure sdnv */
- offset = 1;
- bytecount = 1;
-
- if (!tvb_bytes_exist(tvb, offset, 1)) {
- pinfo->desegment_offset = 0;
- pinfo->desegment_len = DESEGMENT_ONE_MORE_SEGMENT;
- return 0;
- }
-
- while (tvb_get_guint8(tvb, offset) & ~SDNV_MASK) {
- if (bytecount > (int)sizeof(int)) {
- /* invalid length field */
- return 0;
- }
-
- bytecount++;
- offset++;
-
- if (!tvb_bytes_exist(tvb, offset, 1)) {
- pinfo->desegment_offset = 0;
- pinfo->desegment_len = DESEGMENT_ONE_MORE_SEGMENT;
- return 0;
- }
- }
- break;
- case TCP_CONVERGENCE_KEEP_ALIVE:
- case TCP_CONVERGENCE_REFUSE_BUNDLE:
- /* always 1 byte */
- break;
- case TCP_CONVERGENCE_SHUTDOWN:
- if ((conv_hdr &
- ~(TCP_CONVERGENCE_TYPE_MASK | TCP_CONVERGENCE_SHUTDOWN_FLAGS)) != 0) {
- /* Not for us */
- return 0;
- }
- break;
- default:
- if (conv_hdr == (guint8)magic[0]) {
- if (!tvb_bytes_exist(tvb, 0, 4) || tvb_memeql(tvb, 0, magic, 4)) {
- /* Not for us */
- return 0;
- }
-
- tcp_dissect_pdus(tvb, pinfo, tree, TRUE, 8, get_dtn_contact_header_len, dissect_dtn_contact_header, data);
- return tvb_captured_length(tvb);
- }
-
- /* Not for us */
- return 0;
- };
-
- processed_length = get_tcpcl_pdu_len(pinfo, tvb, 0, data);
-
- tcp_dissect_pdus(tvb, pinfo, tree, TRUE, 1, get_tcpcl_pdu_len, dissect_tcpcl_pdu, data);
-
- return processed_length;
-}
-
-
-void
-proto_register_tcpclv3(void)
-{
-
- static hf_register_info hf_tcpcl[] = {
- {&hf_tcp_convergence_pkt_type,
- {"Pkt Type", "tcpcl.pkt_type",
- FT_UINT8, BASE_DEC, VALS(packet_type_vals), 0xF0, NULL, HFILL}
- },
- {&hf_dtn_refuse_bundle_reason_code,
- {"Reason-Code", "tcpcl.refuse.reason_code",
- FT_UINT8, BASE_DEC, VALS(refuse_bundle_reason_code), 0x0F, NULL, HFILL}
- },
- {&hf_tcp_convergence_data_procflags,
- {"TCP Convergence Data Flags", "tcpcl.data.proc.flag",
- FT_UINT8, BASE_HEX, NULL, TCP_CONVERGENCE_DATA_FLAGS, NULL, HFILL}
- },
- {&hf_tcp_convergence_data_procflags_start,
- {"Segment contains start of bundle", "tcpcl.data.proc.start",
- FT_BOOLEAN, 8, NULL, TCP_CONVERGENCE_DATA_START_FLAG, NULL, HFILL}
- },
- {&hf_tcp_convergence_data_procflags_end,
- {"Segment contains end of Bundle", "tcpcl.data.proc.end",
- FT_BOOLEAN, 8, NULL, TCP_CONVERGENCE_DATA_END_FLAG, NULL, HFILL}
- },
- {&hf_tcp_convergence_data_segment_length,
- {"Segment Length", "tcpcl.data.length",
- FT_INT32, BASE_DEC, NULL, 0x0, NULL, HFILL}
- },
- {&hf_tcp_convergence_shutdown_flags,
- {"TCP Convergence Shutdown Flags", "tcpcl.shutdown.flags",
- FT_UINT8, BASE_HEX, NULL, TCP_CONVERGENCE_SHUTDOWN_FLAGS, NULL, HFILL}
- },
- {&hf_tcp_convergence_shutdown_flags_reason,
- {"Shutdown includes Reason Code", "tcpcl.shutdown.reason.flag",
- FT_BOOLEAN, 8, NULL, TCP_CONVERGENCE_SHUTDOWN_REASON, NULL, HFILL}
- },
- {&hf_tcp_convergence_shutdown_flags_delay,
- {"Shutdown includes Reconnection Delay", "tcpcl.shutdown.delay.flag",
- FT_BOOLEAN, 8, NULL, TCP_CONVERGENCE_SHUTDOWN_DELAY, NULL, HFILL}
- },
- {&hf_tcp_convergence_shutdown_reason,
- {"Shutdown Reason Code", "tcpcl.shutdown.reason",
- FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL}
- },
- {&hf_tcp_convergence_shutdown_delay,
- {"Shutdown Reconnection Delay", "tcpcl.shutdown.delay",
- FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL}
- },
- {&hf_tcp_convergence_ack_length,
- {"Ack Length", "tcpcl.ack.length",
- FT_INT32, BASE_DEC, NULL, 0x0, NULL, HFILL}
- },
- {&hf_contact_hdr_version,
- {"Version", "tcpcl.contact_hdr.version",
- FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL}
- },
- {&hf_contact_hdr_flags,
- {"Flags", "tcpcl.contact_hdr.flags",
- FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL}
- },
- {&hf_contact_hdr_flags_ack_req,
- {"Bundle Acks Requested", "tcpcl.contact_hdr.flags.ackreq",
- FT_BOOLEAN, 8, NULL, TCP_CONV_BUNDLE_ACK_FLAG, NULL, HFILL}
- },
- {&hf_contact_hdr_flags_frag_enable,
- {"Reactive Fragmentation Enabled", "tcpcl.contact_hdr.flags.fragen",
- FT_BOOLEAN, 8, NULL, TCP_CONV_REACTIVE_FRAG_FLAG, NULL, HFILL}
- },
- {&hf_contact_hdr_flags_nak,
- {"Support Negative Acknowledgements", "tcpcl.contact_hdr.flags.nak",
- FT_BOOLEAN, 8, NULL, TCP_CONV_CONNECTOR_RCVR_FLAG, NULL, HFILL}
- },
- {&hf_contact_hdr_keep_alive,
- {"Keep Alive", "tcpcl.contact_hdr.keep_alive",
- FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL}
- },
- {&hf_contact_hdr_magic,
- {"Magic", "tcpcl.contact_hdr.magic",
- FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL}
- },
- {&hf_contact_hdr_local_eid,
- {"Local EID", "tcpcl.contact_hdr.local_eid",
- FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL}
- },
- {&hf_contact_hdr_local_eid_length,
- {"Local EID Length", "tcpcl.contact_hdr.local_eid_length",
- FT_INT32, BASE_DEC, NULL, 0x0, NULL, HFILL}
- },
-
- {&hf_msg_fragments,
- {"Message Fragments", "tcpcl.msg.fragments",
- FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}
- },
- {&hf_msg_fragment,
- {"Message Fragment", "tcpcl.msg.fragment",
- FT_FRAMENUM, BASE_NONE, NULL, 0x0, NULL, HFILL}
- },
- {&hf_msg_fragment_overlap,
- {"Message fragment overlap", "tcpcl.msg.fragment.overlap",
- FT_BOOLEAN, BASE_NONE, NULL, 0x0, NULL, HFILL}
- },
- {&hf_msg_fragment_overlap_conflicts,
- {"Message fragment overlapping with conflicting data",
- "tcpcl.msg.fragment.overlap.conflicts",
- FT_BOOLEAN, BASE_NONE, NULL, 0x0, NULL, HFILL}
- },
- {&hf_msg_fragment_multiple_tails,
- {"Message has multiple tails", "tcpcl.msg.fragment.multiple_tails",
- FT_BOOLEAN, BASE_NONE, NULL, 0x0, NULL, HFILL}
- },
- {&hf_msg_fragment_too_long_fragment,
- {"Message fragment too long", "tcpcl.msg.fragment.too_long_fragment",
- FT_BOOLEAN, BASE_NONE, NULL, 0x0, NULL, HFILL}
- },
- {&hf_msg_fragment_error,
- {"Message defragmentation error", "tcpcl.msg.fragment.error",
- FT_FRAMENUM, BASE_NONE, NULL, 0x0, NULL, HFILL}
- },
- {&hf_msg_fragment_count,
- {"Message fragment count", "tcpcl.msg.fragment.count",
- FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL}
- },
- {&hf_msg_reassembled_in,
- {"Reassembled in", "tcpcl.msg.reassembled.in",
- FT_FRAMENUM, BASE_NONE, NULL, 0x0, NULL, HFILL}
- },
- {&hf_msg_reassembled_length,
- {"Reassembled DTN length", "tcpcl.msg.reassembled.length",
- FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL}
- },
- };
-
- static gint *ett_tcpcl[] = {
- &ett_tcp_conv,
- &ett_tcp_conv_hdr,
- &ett_conv_flags,
- &ett_contact_hdr_flags,
- &ett_shutdown_flags,
- &ett_msg_fragment,
- &ett_msg_fragments,
- };
-
- static ei_register_info ei_tcpcl[] = {
- { &ei_tcp_convergence_data_flags,
- { "tcpcl.data.flags.invalid", PI_PROTOCOL, PI_WARN, "Invalid TCP CL Data Segment Flags", EXPFILL }
- },
- { &ei_tcp_convergence_segment_length,
- { "tcpcl.data.length.invalid", PI_PROTOCOL, PI_ERROR, "Invalid Data Length", EXPFILL }
- },
- { &ei_tcp_convergence_ack_length,
- { "tcpcl.ack.length.error", PI_PROTOCOL, PI_WARN, "Ack Length: Error", EXPFILL }
- },
- };
-
- expert_module_t *expert_tcpcl;
-
- proto_tcp_conv = proto_register_protocol ("DTN TCP Convergence Layer Protocol", "TCPCL", "tcpcl");
-
- proto_register_field_array(proto_tcp_conv, hf_tcpcl, array_length(hf_tcpcl));
- proto_register_subtree_array(ett_tcpcl, array_length(ett_tcpcl));
- expert_tcpcl = expert_register_protocol(proto_tcp_conv);
- expert_register_field_array(expert_tcpcl, ei_tcpcl, array_length(ei_tcpcl));
-
- reassembly_table_register(&msg_reassembly_table,
- &addresses_reassembly_table_functions);
-
-}
-
-void
-proto_reg_handoff_tcpclv3(void)
-{
- dissector_handle_t tcpcl_handle;
-
- bundle_handle = find_dissector("bundle");
-
- tcpcl_handle = create_dissector_handle(dissect_tcpcl, proto_tcp_conv);
- dissector_add_uint_with_preference("tcp.port", BUNDLE_PORT, tcpcl_handle);
-}
-
-/*
- * Editor modelines - https://www.wireshark.org/tools/modelines.html
- *
- * Local variables:
- * c-basic-offset: 4
- * tab-width: 8
- * indent-tabs-mode: nil
- * End:
- *
- * vi: set shiftwidth=4 tabstop=8 expandtab:
- * :indentSize=4:tabSize=8:noTabs=true:
- */
diff --git a/epan/dissectors/packet-tcpclv3.h b/epan/dissectors/packet-tcpclv3.h
deleted file mode 100644
index 17db040a2a..0000000000
--- a/epan/dissectors/packet-tcpclv3.h
+++ /dev/null
@@ -1,101 +0,0 @@
-/* packet-tcpclv3.h
- * References:
- * RFC 7242: https://tools.ietf.org/html/rfc7242
- *
- * Copyright 2006-2007 The MITRE Corporation.
- * All Rights Reserved.
- * Approved for Public Release; Distribution Unlimited.
- * Tracking Number 07-0090.
- *
- * The US Government will not be charged any license fee and/or royalties
- * related to this software. Neither name of The MITRE Corporation; nor the
- * names of its contributors may be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * Wireshark - Network traffic analyzer
- * By Gerald Combs <gerald@wireshark.org>
- * Copyright 1998 Gerald Combs
- *
- * SPDX-License-Identifier: GPL-2.0-or-later
- */
-#ifndef PACKET_TCPCLV3_H
-#define PACKET_TCPCLV3_H
-
-#include <ws_symbol_export.h>
-#include <epan/tvbuff.h>
-#include <epan/proto.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* TCP Convergence Layer - Message Types */
-#define TCP_CONV_MSG_TYPE_DATA 0x01
-#define TCP_CONV_MSG_TYPE_ACK 0x02
-#define TCP_CONV_MSG_TYPE_KEEP_ALIVE 0x03
-#define TCP_CONV_MSG_TYPE_SHUTDOWN 0x04
-
-/* TCP Convergence Layer (3) - Message Types */
-#define TCP_CONVERGENCE_TYPE_MASK 0xf0
-#define TCP_CONVERGENCE_DATA_SEGMENT 0x10
-#define TCP_CONVERGENCE_ACK_SEGMENT 0x20
-#define TCP_CONVERGENCE_REFUSE_BUNDLE 0x30
-#define TCP_CONVERGENCE_KEEP_ALIVE 0x40
-#define TCP_CONVERGENCE_SHUTDOWN 0x50
-#define TCP_CONVERGENCE_LENGTH 0x60
-
-/* TCP Convergence Layer - Contact Header Flags */
-#define TCP_CONV_BUNDLE_ACK_FLAG 0x01
-#define TCP_CONV_REACTIVE_FRAG_FLAG 0x02
-#define TCP_CONV_CONNECTOR_RCVR_FLAG 0x04
-
-/* TCP Convergence Layer - Data Segment Flags */
-#define TCP_CONVERGENCE_DATA_FLAGS 0x03
-#define TCP_CONVERGENCE_DATA_END_FLAG 0x01
-#define TCP_CONVERGENCE_DATA_START_FLAG 0x02
-
-/* TCP Convergence Layer - Shutdown Segment Flags */
-#define TCP_CONVERGENCE_SHUTDOWN_FLAGS 0x03
-#define TCP_CONVERGENCE_SHUTDOWN_REASON 0x02
-#define TCP_CONVERGENCE_SHUTDOWN_DELAY 0x01
-
-/* REFUSE-BUNDLE Reason-Codes */
-#define TCP_REFUSE_BUNDLE_REASON_UNKNOWN 0x00
-#define TCP_REFUSE_BUNDLE_REASON_RX_COMPLETE 0x01
-#define TCP_REFUSE_BUNDLE_REASON_RX_EXHAUSTED 0x02
-#define TCP_REFUSE_BUNDLE_REASON_RX_RETRANSMIT 0x03
-/* 0x4-0x7 - Unassigned
- * 0x8-0xf - Reserved for future Use */
-
-/*
- * TCP Convergence Layer - Minimum buffer sizes
- * For Data Packet require 5 bytes fixed plus
- * up to 4 additional for length SDV
- */
-
-#define TCP_CONV_MIN_DATA_BUFFER 9
-
-/* Header Fixed Sizes */
-#define TCP_CONV_HDR_DATA_FIXED_LENGTH 5
-#define TCP_CONV_HDR_ACK_LENGTH 9
-#define TCP_CONV_HDR_KEEP_ALIVE_LENGTH 1
-#define TCP_CONV_HDR_SHUTDOWN_LENGTH 1
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* PACKET_TCPCLV3_H */
-
-/*
- * Editor modelines - https://www.wireshark.org/tools/modelines.html
- *
- * Local variables:
- * c-basic-offset: 4
- * tab-width: 8
- * indent-tabs-mode: nil
- * End:
- *
- * vi: set shiftwidth=4 tabstop=8 expandtab:
- * :indentSize=4:tabSize=8:noTabs=true:
- */
diff --git a/test/captures/dtn_tcpclv3_bpv6_transfer.pcapng b/test/captures/dtn_tcpclv3_bpv6_transfer.pcapng
new file mode 100644
index 0000000000..d8e0764618
--- /dev/null
+++ b/test/captures/dtn_tcpclv3_bpv6_transfer.pcapng
Binary files differ
diff --git a/test/captures/dtn_tcpclv4_bpv7_transfer.pcapng b/test/captures/dtn_tcpclv4_bpv7_transfer.pcapng
new file mode 100644
index 0000000000..4bdf152fdd
--- /dev/null
+++ b/test/captures/dtn_tcpclv4_bpv7_transfer.pcapng
Binary files differ
diff --git a/test/suite_dissection.py b/test/suite_dissection.py
index 40fbe53f00..45616d6727 100644
--- a/test/suite_dissection.py
+++ b/test/suite_dissection.py
@@ -17,6 +17,25 @@ import sys
@fixtures.mark_usefixtures('test_env')
@fixtures.uses_fixtures
+class case_dissect_dtn_tcpcl(subprocesstest.SubprocessTestCase):
+
+ def test_tcpclv3_xfer(self, cmd_tshark, features, dirs, capture_file):
+ self.assertRun((cmd_tshark,
+ '-r', capture_file('dtn_tcpclv3_bpv6_transfer.pcapng'),
+ '-Tfields', '-etcpcl.ack.length',
+ ))
+ self.assertEqual(self.countOutput(r'1064'), 2)
+
+ def test_tcpclv4_xfer(self, cmd_tshark, features, dirs, capture_file):
+ self.assertRun((cmd_tshark,
+ '-r', capture_file('dtn_tcpclv4_bpv7_transfer.pcapng'),
+ '-Tfields', '-etcpcl.v4.xfer_ack.ack_len',
+ ))
+ self.assertEqual(self.countOutput(r'199'), 2)
+
+
+@fixtures.mark_usefixtures('test_env')
+@fixtures.uses_fixtures
class case_dissect_bpv7(subprocesstest.SubprocessTestCase):
def test_bpv7_admin_status(self, cmd_tshark, features, dirs, capture_file):