/* packet-tcpcl.c * References: * RFC 7242: https://tools.ietf.org/html/rfc7242 * RFC 9174: https://www.rfc-editor.org/rfc/rfc9174.html * * TCPCLv4 portions copyright 2019-2021, Brian Sipos * 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 * 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 #include #include #include #include #include "packet-tls-utils.h" #include "packet-tcp.h" #include "packet-ber.h" #include "packet-bpv6.h" #include "packet-tcpcl.h" void proto_register_tcpclv3(void); void proto_reg_handoff_tcpclv3(void); static const char magic[] = {'d', 't', 'n', '!'}; static int proto_tcpcl = -1; static int proto_tcpcl_exts = -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_UINT64, 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_UINT64, 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_UINT64, 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_UINT16, 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_UINT16, 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, BASE_NONE, 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, BASE_NONE, 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_length_clamped = EI_INIT; static expert_field ei_tcpclv3_eid_length = 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_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_length_clamped, { "tcpcl.length_clamped", PI_UNDECODED, PI_ERROR, "Length too large for Wireshark to handle", EXPFILL}}, {&ei_tcpclv3_eid_length, { "tcpcl.eid_length_invalid", PI_PROTOCOL, PI_ERROR, "Invalid EID Length", 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_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 guint tvb_get_sdnv(tvbuff_t *tvb, guint offset, guint64 *value) { return tvb_get_varint(tvb, offset, FT_VARINT_MAX_LEN, value, ENC_VARINT_SDNV); } 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_int_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; } } typedef struct { // key type for addresses_ports_reassembly_table_functions void *addr_port; // TCPCL ID guint64 xfer_id; } tcpcl_fragment_key_t; static guint fragment_key_hash(gconstpointer ptr) { const tcpcl_fragment_key_t *obj = (const tcpcl_fragment_key_t *)ptr; return ( addresses_ports_reassembly_table_functions.hash_func(obj->addr_port) ^ g_int64_hash(&(obj->xfer_id)) ); } static gboolean fragment_key_equal(gconstpointer ptrA, gconstpointer ptrB) { const tcpcl_fragment_key_t *objA = (const tcpcl_fragment_key_t *)ptrA; const tcpcl_fragment_key_t *objB = (const tcpcl_fragment_key_t *)ptrB; return ( addresses_ports_reassembly_table_functions.equal_func(objA->addr_port, objB->addr_port) && (objA->xfer_id == objB->xfer_id) ); } static gpointer fragment_key_temporary(const packet_info *pinfo, const guint32 id, const void *data) { tcpcl_fragment_key_t *obj = g_slice_new(tcpcl_fragment_key_t); obj->addr_port = addresses_ports_reassembly_table_functions.temporary_key_func(pinfo, id, NULL); obj->xfer_id = *((const guint64 *)data); return (gpointer)obj; } static gpointer fragment_key_persistent(const packet_info *pinfo, const guint32 id, const void *data) { tcpcl_fragment_key_t *obj = g_slice_new(tcpcl_fragment_key_t); obj->addr_port = addresses_ports_reassembly_table_functions.persistent_key_func(pinfo, id, NULL); obj->xfer_id = *((const guint64 *)data); return (gpointer)obj; } static void fragment_key_free_temporary(gpointer ptr) { tcpcl_fragment_key_t *obj = (tcpcl_fragment_key_t *)ptr; if (obj) { addresses_ports_reassembly_table_functions.free_temporary_key_func(obj->addr_port); g_slice_free(tcpcl_fragment_key_t, obj); } } static void fragment_key_free_persistent(gpointer ptr) { tcpcl_fragment_key_t *obj = (tcpcl_fragment_key_t *)ptr; if (obj) { addresses_ports_reassembly_table_functions.free_persistent_key_func(obj->addr_port); g_slice_free(tcpcl_fragment_key_t, obj); } } static reassembly_table_functions xfer_reassembly_table_functions = { fragment_key_hash, fragment_key_equal, fragment_key_temporary, fragment_key_persistent, fragment_key_free_temporary, fragment_key_free_persistent }; /** 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); 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, packet_info *pinfo, proto_item *item) { gint clamped; if (orig > G_MAXINT) { clamped = G_MAXINT; if (pinfo && item) { expert_add_info(pinfo, item, &ei_length_clamped); } } 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; guint64 len; guint 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 */ bytecount = tvb_get_sdnv(tvb, offset, &len); if (bytecount == 0) { return 0; } const gint len_clamp = get_clamped_length(len, NULL, NULL); offset += bytecount + len_clamp; break; } case TCPCLV3_ACK_SEGMENT: /* get length from sdnv */ bytecount = tvb_get_sdnv(tvb, offset, &len); if (bytecount == 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 */ bytecount = tvb_get_sdnv(tvb, offset, &len); if (bytecount == 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; gint sdnv_length; guint64 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 ); offset += 1; /* 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); } sub_item = proto_tree_add_item_ret_varint(conv_tree, hf_tcpclv3_data_segment_length, tvb, offset, -1, ENC_VARINT_SDNV, &segment_length, &sdnv_length); if (sdnv_length == 0) { expert_add_info(pinfo, sub_item, &ei_tcpclv3_segment_length); return 0; } offset += sdnv_length; const gint data_len_clamp = get_clamped_length(segment_length, pinfo, sub_item); // 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, data_len_clamp, 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) { // Reassemble the segments fragment_head *frag_msg; frag_msg = fragment_add_seq_next( &xfer_reassembly_table, tvb, offset, pinfo, 0, xfer_id, data_len_clamp, !(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 += data_len_clamp; break; } case TCPCLV3_ACK_SEGMENT: { /*No valid flags*/ offset += 1; sub_item = proto_tree_add_item_ret_varint(conv_tree, hf_tcpclv3_ack_length, tvb, offset, -1, ENC_VARINT_SDNV, &segment_length, &sdnv_length); if (sdnv_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, NULL, NULL); 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%02" 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); } guint16 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; dissector_handle_t subdis = dissector_get_uint_handle(xfer_ext_dissectors, extitem_type); /* XXX - dissector name, or protocol name? */ const char *subname = dissector_handle_get_dissector_name(subdis); if (subdis) { proto_item_set_text(item_type, "Item Type: %s (0x%04" PRIx16 ")", subname, extitem_type); } guint16 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 = 0; if (subdis) { sublen = call_dissector_only(subdis, extitem_tvb, pinfo, tree_extdata, NULL); } 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; if (subname) { proto_item_append_text(item_ext, ": %s", subname); } else { proto_item_append_text(item_ext, ": Type 0x%04" PRIx16, 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_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); } guint16 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; dissector_handle_t subdis = dissector_get_uint_handle(xfer_ext_dissectors, extitem_type); /* XXX - dissector name, or protocol name? */ const char *subname = dissector_handle_get_dissector_name(subdis); if (subdis) { proto_item_set_text(item_type, "Item Type: %s (0x%04" PRIx16 ")", subname, extitem_type); } guint16 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 = 0; if (subdis) { sublen = call_dissector_only(subdis, extitem_tvb, pinfo, tree_extdata, NULL); } if (sublen == 0) { expert_add_info(pinfo, item_type, &ei_tcpclv4_invalid_xferext_type); } extitem_offset += extitem_len; proto_item_set_len(item_ext, extitem_offset); extlist_offset += extitem_offset; if (subname) { proto_item_append_text(item_ext, ": %s", subname); } else { proto_item_append_text(item_ext, ": Type 0x%04" PRIx16, extitem_type); } if (is_critical) { proto_item_append_text(item_ext, ", CRITICAL"); } } // 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, pinfo, item_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: %" PRIi64, 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"); } } 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) { // Reassemble the segments fragment_head *xferload_frag_msg = fragment_add_seq_next( &xfer_reassembly_table, tvb, data_offset, pinfo, 0, &xfer_id, 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: %" PRIi64, 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"); } } 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: %" PRIi64, 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 += sizeof(magic); guint8 version = tvb_get_guint8(tvb, offset); offset += 1; if (version == 3) { offset += 3; // flags + keepalive guint64 eid_len; const guint bytecount = tvb_get_sdnv(tvb, offset, &eid_len); const gint len_clamp = get_clamped_length(eid_len, NULL, NULL); offset += bytecount + len_clamp; } else if (version == 4) { offset += 1; // flags } 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 EID. */ guint64 eid_length; int sdnv_length; proto_item *sub_item = proto_tree_add_item_ret_varint(tree_chdr, hf_tcpclv3_chdr_local_eid_length, tvb, offset, -1, ENC_VARINT_SDNV, &eid_length, &sdnv_length); if (sdnv_length == 0) { expert_add_info(pinfo, sub_item, &ei_tcpclv3_eid_length); return 0; } offset += sdnv_length; const gint eid_len_clamp = get_clamped_length(eid_length, pinfo, sub_item); proto_tree_add_item(tree_chdr, hf_tcpclv3_chdr_local_eid, tvb, offset, eid_len_clamp, ENC_NA|ENC_ASCII); offset += eid_len_clamp; // 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_tcpcl_exts = proto_register_protocol_in_name_only( "TCPCL Extension Subdissectors", "TCPCL Extension Subdissectors", "tcpcl_exts", proto_tcpcl, FT_PROTOCOL ); 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 = register_dissector("tcpcl", dissect_tcpcl, proto_tcpcl); sess_ext_dissectors = register_dissector_table("tcpcl.v4.sess_ext", "TCPCLv4 Session Extension", proto_tcpcl, FT_UINT16, BASE_HEX); xfer_ext_dissectors = register_dissector_table("tcpcl.v4.xfer_ext", "TCPCLv4 Transfer Extension", proto_tcpcl, FT_UINT16, BASE_HEX); 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, &xfer_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_with_name(dissect_xferext_transferlen, proto_tcpcl_exts, "Transfer Length"); 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_exts, "id-kp-bundleSecurity"); register_ber_oid_dissector("1.3.6.1.5.5.7.8.11", dissect_othername_bundleeid, proto_tcpcl_exts, "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: */