aboutsummaryrefslogtreecommitdiffstats
path: root/epan/dissectors/packet-tcp.c
diff options
context:
space:
mode:
Diffstat (limited to 'epan/dissectors/packet-tcp.c')
-rw-r--r--epan/dissectors/packet-tcp.c3495
1 files changed, 2516 insertions, 979 deletions
diff --git a/epan/dissectors/packet-tcp.c b/epan/dissectors/packet-tcp.c
index 246f487fd9..65edc16bbb 100644
--- a/epan/dissectors/packet-tcp.c
+++ b/epan/dissectors/packet-tcp.c
@@ -10,7 +10,6 @@
#include "config.h"
-#include <stdio.h>
#include <epan/packet.h>
#include <epan/capture_dissectors.h>
#include <epan/exceptions.h>
@@ -44,10 +43,10 @@ void proto_register_tcp(void);
void proto_reg_handoff_tcp(void);
static void conversation_completeness_fill(gchar*, guint32);
-static int tcp_tap = -1;
-static int tcp_follow_tap = -1;
-static int mptcp_tap = -1;
-static int exported_pdu_tap = -1;
+static int tcp_tap;
+static int tcp_follow_tap;
+static int mptcp_tap;
+static int exported_pdu_tap;
/* Place TCP summary in proto tree */
static gboolean tcp_summary_in_tree = TRUE;
@@ -97,6 +96,18 @@ static gboolean tcp_check_checksum = FALSE;
};
/*
+ * Analysis overriding values to be used when not satisfied by the automatic
+ * result. (Accessed through preferences but not stored as a preference)
+ */
+ enum override_analysis_value {
+ OverrideAnalysis_0=0,
+ OverrideAnalysis_1,
+ OverrideAnalysis_2,
+ OverrideAnalysis_3,
+ OverrideAnalysis_4
+};
+
+/*
* Using enum instead of boolean make API easier
*/
enum mptcp_dsn_conversion {
@@ -123,329 +134,360 @@ static const value_string mp_tcprst_reasons[] = {
static gint tcp_default_window_scaling = (gint)WindowScaling_NotKnown;
-static int proto_tcp = -1;
-static int proto_ip = -1;
-static int proto_icmp = -1;
-
-static int proto_tcp_option_nop = -1;
-static int proto_tcp_option_eol = -1;
-static int proto_tcp_option_timestamp = -1;
-static int proto_tcp_option_mss = -1;
-static int proto_tcp_option_wscale = -1;
-static int proto_tcp_option_sack_perm = -1;
-static int proto_tcp_option_sack = -1;
-static int proto_tcp_option_echo = -1;
-static int proto_tcp_option_echoreply = -1;
-static int proto_tcp_option_cc = -1;
-static int proto_tcp_option_cc_new = -1;
-static int proto_tcp_option_cc_echo = -1;
-static int proto_tcp_option_md5 = -1;
-static int proto_tcp_option_ao = -1;
-static int proto_tcp_option_scps = -1;
-static int proto_tcp_option_snack = -1;
-static int proto_tcp_option_scpsrec = -1;
-static int proto_tcp_option_scpscor = -1;
-static int proto_tcp_option_qs = -1;
-static int proto_tcp_option_user_to = -1;
-static int proto_tcp_option_tfo = -1;
-static int proto_tcp_option_rvbd_probe = -1;
-static int proto_tcp_option_rvbd_trpy = -1;
-static int proto_tcp_option_exp = -1;
-static int proto_tcp_option_unknown = -1;
-static int proto_mptcp = -1;
-
-static int hf_tcp_srcport = -1;
-static int hf_tcp_dstport = -1;
-static int hf_tcp_port = -1;
-static int hf_tcp_stream = -1;
-static int hf_tcp_completeness = -1;
-static int hf_tcp_seq = -1;
-static int hf_tcp_seq_abs = -1;
-static int hf_tcp_nxtseq = -1;
-static int hf_tcp_ack = -1;
-static int hf_tcp_ack_abs = -1;
-static int hf_tcp_hdr_len = -1;
-static int hf_tcp_flags = -1;
-static int hf_tcp_flags_res = -1;
-static int hf_tcp_flags_ns = -1;
-static int hf_tcp_flags_cwr = -1;
-static int hf_tcp_flags_ecn = -1;
-static int hf_tcp_flags_urg = -1;
-static int hf_tcp_flags_ack = -1;
-static int hf_tcp_flags_push = -1;
-static int hf_tcp_flags_reset = -1;
-static int hf_tcp_flags_syn = -1;
-static int hf_tcp_flags_fin = -1;
-static int hf_tcp_flags_str = -1;
-static int hf_tcp_window_size_value = -1;
-static int hf_tcp_window_size = -1;
-static int hf_tcp_window_size_scalefactor = -1;
-static int hf_tcp_checksum = -1;
-static int hf_tcp_checksum_status = -1;
-static int hf_tcp_checksum_calculated = -1;
-static int hf_tcp_len = -1;
-static int hf_tcp_urgent_pointer = -1;
-static int hf_tcp_analysis = -1;
-static int hf_tcp_analysis_flags = -1;
-static int hf_tcp_analysis_bytes_in_flight = -1;
-static int hf_tcp_analysis_push_bytes_sent = -1;
-static int hf_tcp_analysis_acks_frame = -1;
-static int hf_tcp_analysis_ack_rtt = -1;
-static int hf_tcp_analysis_first_rtt = -1;
-static int hf_tcp_analysis_rto = -1;
-static int hf_tcp_analysis_rto_frame = -1;
-static int hf_tcp_analysis_duplicate_ack = -1;
-static int hf_tcp_analysis_duplicate_ack_num = -1;
-static int hf_tcp_analysis_duplicate_ack_frame = -1;
-static int hf_tcp_continuation_to = -1;
-static int hf_tcp_pdu_time = -1;
-static int hf_tcp_pdu_size = -1;
-static int hf_tcp_pdu_last_frame = -1;
-static int hf_tcp_reassembled_in = -1;
-static int hf_tcp_reassembled_length = -1;
-static int hf_tcp_reassembled_data = -1;
-static int hf_tcp_segments = -1;
-static int hf_tcp_segment = -1;
-static int hf_tcp_segment_overlap = -1;
-static int hf_tcp_segment_overlap_conflict = -1;
-static int hf_tcp_segment_multiple_tails = -1;
-static int hf_tcp_segment_too_long_fragment = -1;
-static int hf_tcp_segment_error = -1;
-static int hf_tcp_segment_count = -1;
-static int hf_tcp_options = -1;
-static int hf_tcp_option_kind = -1;
-static int hf_tcp_option_len = -1;
-static int hf_tcp_option_mss_val = -1;
-static int hf_tcp_option_wscale_shift = -1;
-static int hf_tcp_option_wscale_multiplier = -1;
-static int hf_tcp_option_sack_sle = -1;
-static int hf_tcp_option_sack_sre = -1;
-static int hf_tcp_option_sack_range_count = -1;
-static int hf_tcp_option_sack_dsack_le = -1;
-static int hf_tcp_option_sack_dsack_re = -1;
-static int hf_tcp_option_echo = -1;
-static int hf_tcp_option_timestamp_tsval = -1;
-static int hf_tcp_option_timestamp_tsecr = -1;
-static int hf_tcp_option_cc = -1;
-static int hf_tcp_option_md5_digest = -1;
-static int hf_tcp_option_ao_keyid = -1;
-static int hf_tcp_option_ao_rnextkeyid = -1;
-static int hf_tcp_option_ao_mac = -1;
-static int hf_tcp_option_qs_rate = -1;
-static int hf_tcp_option_qs_ttl_diff = -1;
-static int hf_tcp_option_exp_data = -1;
-static int hf_tcp_option_exp_magic_number = -1;
-static int hf_tcp_option_unknown_payload = -1;
-
-static int hf_tcp_option_rvbd_probe_version1 = -1;
-static int hf_tcp_option_rvbd_probe_version2 = -1;
-static int hf_tcp_option_rvbd_probe_type1 = -1;
-static int hf_tcp_option_rvbd_probe_type2 = -1;
-static int hf_tcp_option_rvbd_probe_prober = -1;
-static int hf_tcp_option_rvbd_probe_proxy = -1;
-static int hf_tcp_option_rvbd_probe_client = -1;
-static int hf_tcp_option_rvbd_probe_proxy_port = -1;
-static int hf_tcp_option_rvbd_probe_appli_ver = -1;
-static int hf_tcp_option_rvbd_probe_storeid = -1;
-static int hf_tcp_option_rvbd_probe_flags = -1;
-static int hf_tcp_option_rvbd_probe_flag_last_notify = -1;
-static int hf_tcp_option_rvbd_probe_flag_server_connected = -1;
-static int hf_tcp_option_rvbd_probe_flag_not_cfe = -1;
-static int hf_tcp_option_rvbd_probe_flag_sslcert = -1;
-static int hf_tcp_option_rvbd_probe_flag_probe_cache = -1;
-
-static int hf_tcp_option_rvbd_trpy_flags = -1;
-static int hf_tcp_option_rvbd_trpy_flag_mode = -1;
-static int hf_tcp_option_rvbd_trpy_flag_oob = -1;
-static int hf_tcp_option_rvbd_trpy_flag_chksum = -1;
-static int hf_tcp_option_rvbd_trpy_flag_fw_rst = -1;
-static int hf_tcp_option_rvbd_trpy_flag_fw_rst_inner = -1;
-static int hf_tcp_option_rvbd_trpy_flag_fw_rst_probe = -1;
-static int hf_tcp_option_rvbd_trpy_src = -1;
-static int hf_tcp_option_rvbd_trpy_dst = -1;
-static int hf_tcp_option_rvbd_trpy_src_port = -1;
-static int hf_tcp_option_rvbd_trpy_dst_port = -1;
-static int hf_tcp_option_rvbd_trpy_client_port = -1;
-
-static int hf_tcp_option_mptcp_flags = -1;
-static int hf_tcp_option_mptcp_backup_flag = -1;
-static int hf_tcp_option_mptcp_checksum_flag = -1;
-static int hf_tcp_option_mptcp_B_flag = -1;
-static int hf_tcp_option_mptcp_C_flag = -1;
-static int hf_tcp_option_mptcp_H_v0_flag = -1;
-static int hf_tcp_option_mptcp_H_v1_flag = -1;
-static int hf_tcp_option_mptcp_F_flag = -1;
-static int hf_tcp_option_mptcp_m_flag = -1;
-static int hf_tcp_option_mptcp_M_flag = -1;
-static int hf_tcp_option_mptcp_a_flag = -1;
-static int hf_tcp_option_mptcp_A_flag = -1;
-static int hf_tcp_option_mptcp_U_flag = -1;
-static int hf_tcp_option_mptcp_V_flag = -1;
-static int hf_tcp_option_mptcp_W_flag = -1;
-static int hf_tcp_option_mptcp_T_flag = -1;
-static int hf_tcp_option_mptcp_tcprst_reason = -1;
-static int hf_tcp_option_mptcp_reserved_v0_flag = -1;
-static int hf_tcp_option_mptcp_reserved_v1_flag = -1;
-static int hf_tcp_option_mptcp_subtype = -1;
-static int hf_tcp_option_mptcp_version = -1;
-static int hf_tcp_option_mptcp_reserved = -1;
-static int hf_tcp_option_mptcp_address_id = -1;
-static int hf_tcp_option_mptcp_recv_token = -1;
-static int hf_tcp_option_mptcp_sender_key = -1;
-static int hf_tcp_option_mptcp_recv_key = -1;
-static int hf_tcp_option_mptcp_sender_rand = -1;
-static int hf_tcp_option_mptcp_sender_trunc_hmac = -1;
-static int hf_tcp_option_mptcp_sender_hmac = -1;
-static int hf_tcp_option_mptcp_addaddr_trunc_hmac = -1;
-static int hf_tcp_option_mptcp_data_ack_raw = -1;
-static int hf_tcp_option_mptcp_data_seq_no_raw = -1;
-static int hf_tcp_option_mptcp_subflow_seq_no = -1;
-static int hf_tcp_option_mptcp_data_lvl_len = -1;
-static int hf_tcp_option_mptcp_checksum = -1;
-static int hf_tcp_option_mptcp_ipver = -1;
-static int hf_tcp_option_mptcp_echo = -1;
-static int hf_tcp_option_mptcp_ipv4 = -1;
-static int hf_tcp_option_mptcp_ipv6 = -1;
-static int hf_tcp_option_mptcp_port = -1;
-static int hf_mptcp_expected_idsn = -1;
-
-static int hf_mptcp_dsn = -1;
-static int hf_mptcp_rawdsn64 = -1;
-static int hf_mptcp_dss_dsn = -1;
-static int hf_mptcp_ack = -1;
-static int hf_mptcp_stream = -1;
-static int hf_mptcp_expected_token = -1;
-static int hf_mptcp_analysis = -1;
-static int hf_mptcp_analysis_master = -1;
-static int hf_mptcp_analysis_subflows = -1;
-static int hf_mptcp_number_of_removed_addresses = -1;
-static int hf_mptcp_related_mapping = -1;
-static int hf_mptcp_reinjection_of = -1;
-static int hf_mptcp_reinjected_in = -1;
-
-
-static int hf_tcp_option_fast_open_cookie_request = -1;
-static int hf_tcp_option_fast_open_cookie = -1;
-
-static int hf_tcp_ts_relative = -1;
-static int hf_tcp_ts_delta = -1;
-static int hf_tcp_option_scps_vector = -1;
-static int hf_tcp_option_scps_binding = -1;
-static int hf_tcp_option_scps_binding_len = -1;
-static int hf_tcp_scpsoption_flags_bets = -1;
-static int hf_tcp_scpsoption_flags_snack1 = -1;
-static int hf_tcp_scpsoption_flags_snack2 = -1;
-static int hf_tcp_scpsoption_flags_compress = -1;
-static int hf_tcp_scpsoption_flags_nlts = -1;
-static int hf_tcp_scpsoption_flags_reserved = -1;
-static int hf_tcp_scpsoption_connection_id = -1;
-static int hf_tcp_option_snack_offset = -1;
-static int hf_tcp_option_snack_size = -1;
-static int hf_tcp_option_snack_le = -1;
-static int hf_tcp_option_snack_re = -1;
-static int hf_tcp_option_user_to_granularity = -1;
-static int hf_tcp_option_user_to_val = -1;
-static int hf_tcp_proc_src_uid = -1;
-static int hf_tcp_proc_src_pid = -1;
-static int hf_tcp_proc_src_uname = -1;
-static int hf_tcp_proc_src_cmd = -1;
-static int hf_tcp_proc_dst_uid = -1;
-static int hf_tcp_proc_dst_pid = -1;
-static int hf_tcp_proc_dst_uname = -1;
-static int hf_tcp_proc_dst_cmd = -1;
-static int hf_tcp_segment_data = -1;
-static int hf_tcp_payload = -1;
-static int hf_tcp_reset_cause = -1;
-static int hf_tcp_fin_retransmission = -1;
-static int hf_tcp_option_rvbd_probe_reserved = -1;
-static int hf_tcp_option_scps_binding_data = -1;
-
-static gint ett_tcp = -1;
-static gint ett_tcp_flags = -1;
-static gint ett_tcp_options = -1;
-static gint ett_tcp_option_timestamp = -1;
-static gint ett_tcp_option_mss = -1;
-static gint ett_tcp_option_wscale = -1;
-static gint ett_tcp_option_sack = -1;
-static gint ett_tcp_option_snack = -1;
-static gint ett_tcp_option_scps = -1;
-static gint ett_tcp_scpsoption_flags = -1;
-static gint ett_tcp_option_scps_extended = -1;
-static gint ett_tcp_option_user_to = -1;
-static gint ett_tcp_option_exp = -1;
-static gint ett_tcp_option_sack_perm = -1;
-static gint ett_tcp_analysis = -1;
-static gint ett_tcp_analysis_faults = -1;
-static gint ett_tcp_timestamps = -1;
-static gint ett_tcp_segments = -1;
-static gint ett_tcp_segment = -1;
-static gint ett_tcp_checksum = -1;
-static gint ett_tcp_process_info = -1;
-static gint ett_tcp_option_mptcp = -1;
-static gint ett_tcp_opt_rvbd_probe = -1;
-static gint ett_tcp_opt_rvbd_probe_flags = -1;
-static gint ett_tcp_opt_rvbd_trpy = -1;
-static gint ett_tcp_opt_rvbd_trpy_flags = -1;
-static gint ett_tcp_opt_echo = -1;
-static gint ett_tcp_opt_cc = -1;
-static gint ett_tcp_opt_md5 = -1;
-static gint ett_tcp_opt_ao = -1;
-static gint ett_tcp_opt_qs = -1;
-static gint ett_tcp_opt_recbound = -1;
-static gint ett_tcp_opt_scpscor = -1;
-static gint ett_tcp_unknown_opt = -1;
-static gint ett_tcp_option_other = -1;
-static gint ett_mptcp_analysis = -1;
-static gint ett_mptcp_analysis_subflows = -1;
-
-static expert_field ei_tcp_opt_len_invalid = EI_INIT;
-static expert_field ei_tcp_analysis_retransmission = EI_INIT;
-static expert_field ei_tcp_analysis_fast_retransmission = EI_INIT;
-static expert_field ei_tcp_analysis_spurious_retransmission = EI_INIT;
-static expert_field ei_tcp_analysis_out_of_order = EI_INIT;
-static expert_field ei_tcp_analysis_reused_ports = EI_INIT;
-static expert_field ei_tcp_analysis_lost_packet = EI_INIT;
-static expert_field ei_tcp_analysis_ack_lost_packet = EI_INIT;
-static expert_field ei_tcp_analysis_window_update = EI_INIT;
-static expert_field ei_tcp_analysis_window_full = EI_INIT;
-static expert_field ei_tcp_analysis_keep_alive = EI_INIT;
-static expert_field ei_tcp_analysis_keep_alive_ack = EI_INIT;
-static expert_field ei_tcp_analysis_duplicate_ack = EI_INIT;
-static expert_field ei_tcp_analysis_zero_window_probe = EI_INIT;
-static expert_field ei_tcp_analysis_zero_window = EI_INIT;
-static expert_field ei_tcp_analysis_zero_window_probe_ack = EI_INIT;
-static expert_field ei_tcp_analysis_tfo_syn = EI_INIT;
-static expert_field ei_tcp_analysis_tfo_ack = EI_INIT;
-static expert_field ei_tcp_analysis_tfo_ignored = EI_INIT;
-static expert_field ei_tcp_scps_capable = EI_INIT;
-static expert_field ei_tcp_option_sack_dsack = EI_INIT;
-static expert_field ei_tcp_option_snack_sequence = EI_INIT;
-static expert_field ei_tcp_option_wscale_shift_invalid = EI_INIT;
-static expert_field ei_tcp_option_mss_absent = EI_INIT;
-static expert_field ei_tcp_option_mss_present = EI_INIT;
-static expert_field ei_tcp_short_segment = EI_INIT;
-static expert_field ei_tcp_ack_nonzero = EI_INIT;
-static expert_field ei_tcp_connection_synack = EI_INIT;
-static expert_field ei_tcp_connection_syn = EI_INIT;
-static expert_field ei_tcp_connection_fin = EI_INIT;
-static expert_field ei_tcp_connection_rst = EI_INIT;
-static expert_field ei_tcp_connection_fin_active = EI_INIT;
-static expert_field ei_tcp_connection_fin_passive = EI_INIT;
-static expert_field ei_tcp_checksum_ffff = EI_INIT;
-static expert_field ei_tcp_checksum_bad = EI_INIT;
-static expert_field ei_tcp_urgent_pointer_non_zero = EI_INIT;
-static expert_field ei_tcp_suboption_malformed = EI_INIT;
-static expert_field ei_tcp_nop = EI_INIT;
-static expert_field ei_tcp_bogus_header_length = EI_INIT;
-
-/* static expert_field ei_mptcp_analysis_unexpected_idsn = EI_INIT; */
-static expert_field ei_mptcp_analysis_echoed_key_mismatch = EI_INIT;
-static expert_field ei_mptcp_analysis_missing_algorithm = EI_INIT;
-static expert_field ei_mptcp_analysis_unsupported_algorithm = EI_INIT;
-static expert_field ei_mptcp_infinite_mapping= EI_INIT;
-static expert_field ei_mptcp_mapping_missing = EI_INIT;
-/* static expert_field ei_mptcp_stream_incomplete = EI_INIT; */
-/* static expert_field ei_mptcp_analysis_dsn_out_of_order = EI_INIT; */
+static gint tcp_default_override_analysis = (gint)OverrideAnalysis_0;
+
+static int proto_tcp;
+static int proto_ip;
+static int proto_icmp;
+
+static int proto_tcp_option_nop;
+static int proto_tcp_option_eol;
+static int proto_tcp_option_timestamp;
+static int proto_tcp_option_mss;
+static int proto_tcp_option_wscale;
+static int proto_tcp_option_sack_perm;
+static int proto_tcp_option_sack;
+static int proto_tcp_option_echo;
+static int proto_tcp_option_echoreply;
+static int proto_tcp_option_cc;
+static int proto_tcp_option_cc_new;
+static int proto_tcp_option_cc_echo;
+static int proto_tcp_option_md5;
+static int proto_tcp_option_ao;
+static int proto_tcp_option_scps;
+static int proto_tcp_option_snack;
+static int proto_tcp_option_scpsrec;
+static int proto_tcp_option_scpscor;
+static int proto_tcp_option_qs;
+static int proto_tcp_option_user_to;
+static int proto_tcp_option_tfo;
+static int proto_tcp_option_acc_ecn;
+static int proto_tcp_option_rvbd_probe;
+static int proto_tcp_option_rvbd_trpy;
+static int proto_tcp_option_exp;
+static int proto_tcp_option_unknown;
+static int proto_mptcp;
+
+static int hf_tcp_srcport;
+static int hf_tcp_dstport;
+static int hf_tcp_port;
+static int hf_tcp_stream;
+static int hf_tcp_completeness;
+static int hf_tcp_completeness_syn;
+static int hf_tcp_completeness_syn_ack;
+static int hf_tcp_completeness_ack;
+static int hf_tcp_completeness_data;
+static int hf_tcp_completeness_fin;
+static int hf_tcp_completeness_rst;
+static int hf_tcp_completeness_str;
+static int hf_tcp_seq;
+static int hf_tcp_seq_abs;
+static int hf_tcp_nxtseq;
+static int hf_tcp_ack;
+static int hf_tcp_ack_abs;
+static int hf_tcp_hdr_len;
+static int hf_tcp_flags;
+static int hf_tcp_flags_res;
+static int hf_tcp_flags_ae;
+static int hf_tcp_flags_cwr;
+static int hf_tcp_flags_ece;
+static int hf_tcp_flags_ace;
+static int hf_tcp_flags_urg;
+static int hf_tcp_flags_ack;
+static int hf_tcp_flags_push;
+static int hf_tcp_flags_reset;
+static int hf_tcp_flags_syn;
+static int hf_tcp_flags_fin;
+static int hf_tcp_flags_str;
+static int hf_tcp_window_size_value;
+static int hf_tcp_window_size;
+static int hf_tcp_window_size_scalefactor;
+static int hf_tcp_checksum;
+static int hf_tcp_checksum_status;
+static int hf_tcp_checksum_calculated;
+static int hf_tcp_len;
+static int hf_tcp_urgent_pointer;
+static int hf_tcp_analysis;
+static int hf_tcp_analysis_flags;
+static int hf_tcp_analysis_bytes_in_flight;
+static int hf_tcp_analysis_push_bytes_sent;
+static int hf_tcp_analysis_acks_frame;
+static int hf_tcp_analysis_ack_rtt;
+static int hf_tcp_analysis_first_rtt;
+static int hf_tcp_analysis_rto;
+static int hf_tcp_analysis_rto_frame;
+static int hf_tcp_analysis_duplicate_ack;
+static int hf_tcp_analysis_duplicate_ack_num;
+static int hf_tcp_analysis_duplicate_ack_frame;
+static int hf_tcp_continuation_to;
+static int hf_tcp_pdu_time;
+static int hf_tcp_pdu_size;
+static int hf_tcp_pdu_last_frame;
+static int hf_tcp_reassembled_in;
+static int hf_tcp_reassembled_length;
+static int hf_tcp_reassembled_data;
+static int hf_tcp_segments;
+static int hf_tcp_segment;
+static int hf_tcp_segment_overlap;
+static int hf_tcp_segment_overlap_conflict;
+static int hf_tcp_segment_multiple_tails;
+static int hf_tcp_segment_too_long_fragment;
+static int hf_tcp_segment_error;
+static int hf_tcp_segment_count;
+static int hf_tcp_options;
+static int hf_tcp_option_kind;
+static int hf_tcp_option_len;
+static int hf_tcp_option_mss_val;
+static int hf_tcp_option_wscale_shift;
+static int hf_tcp_option_wscale_multiplier;
+static int hf_tcp_option_sack_sle;
+static int hf_tcp_option_sack_sre;
+static int hf_tcp_option_sack_range_count;
+static int hf_tcp_option_sack_dsack_le;
+static int hf_tcp_option_sack_dsack_re;
+static int hf_tcp_option_echo;
+static int hf_tcp_option_timestamp_tsval;
+static int hf_tcp_option_timestamp_tsecr;
+static int hf_tcp_option_cc;
+static int hf_tcp_option_md5_digest;
+static int hf_tcp_option_ao_keyid;
+static int hf_tcp_option_ao_rnextkeyid;
+static int hf_tcp_option_ao_mac;
+static int hf_tcp_option_qs_rate;
+static int hf_tcp_option_qs_ttl_diff;
+static int hf_tcp_option_tarr_rate;
+static int hf_tcp_option_tarr_reserved;
+static int hf_tcp_option_acc_ecn_ee0b;
+static int hf_tcp_option_acc_ecn_eceb;
+static int hf_tcp_option_acc_ecn_ee1b;
+static int hf_tcp_option_exp_data;
+static int hf_tcp_option_exp_exid;
+static int hf_tcp_option_unknown_payload;
+
+static int hf_tcp_option_rvbd_probe_version1;
+static int hf_tcp_option_rvbd_probe_version2;
+static int hf_tcp_option_rvbd_probe_type1;
+static int hf_tcp_option_rvbd_probe_type2;
+static int hf_tcp_option_rvbd_probe_prober;
+static int hf_tcp_option_rvbd_probe_proxy;
+static int hf_tcp_option_rvbd_probe_client;
+static int hf_tcp_option_rvbd_probe_proxy_port;
+static int hf_tcp_option_rvbd_probe_appli_ver;
+static int hf_tcp_option_rvbd_probe_storeid;
+static int hf_tcp_option_rvbd_probe_flags;
+static int hf_tcp_option_rvbd_probe_flag_last_notify;
+static int hf_tcp_option_rvbd_probe_flag_server_connected;
+static int hf_tcp_option_rvbd_probe_flag_not_cfe;
+static int hf_tcp_option_rvbd_probe_flag_sslcert;
+static int hf_tcp_option_rvbd_probe_flag_probe_cache;
+
+static int hf_tcp_option_rvbd_trpy_flags;
+static int hf_tcp_option_rvbd_trpy_flag_mode;
+static int hf_tcp_option_rvbd_trpy_flag_oob;
+static int hf_tcp_option_rvbd_trpy_flag_chksum;
+static int hf_tcp_option_rvbd_trpy_flag_fw_rst;
+static int hf_tcp_option_rvbd_trpy_flag_fw_rst_inner;
+static int hf_tcp_option_rvbd_trpy_flag_fw_rst_probe;
+static int hf_tcp_option_rvbd_trpy_src;
+static int hf_tcp_option_rvbd_trpy_dst;
+static int hf_tcp_option_rvbd_trpy_src_port;
+static int hf_tcp_option_rvbd_trpy_dst_port;
+static int hf_tcp_option_rvbd_trpy_client_port;
+
+static int hf_tcp_option_mptcp_flags;
+static int hf_tcp_option_mptcp_backup_flag;
+static int hf_tcp_option_mptcp_checksum_flag;
+static int hf_tcp_option_mptcp_B_flag;
+static int hf_tcp_option_mptcp_C_flag;
+static int hf_tcp_option_mptcp_H_v0_flag;
+static int hf_tcp_option_mptcp_H_v1_flag;
+static int hf_tcp_option_mptcp_F_flag;
+static int hf_tcp_option_mptcp_m_flag;
+static int hf_tcp_option_mptcp_M_flag;
+static int hf_tcp_option_mptcp_a_flag;
+static int hf_tcp_option_mptcp_A_flag;
+static int hf_tcp_option_mptcp_U_flag;
+static int hf_tcp_option_mptcp_V_flag;
+static int hf_tcp_option_mptcp_W_flag;
+static int hf_tcp_option_mptcp_T_flag;
+static int hf_tcp_option_mptcp_tcprst_reason;
+static int hf_tcp_option_mptcp_reserved_v0_flag;
+static int hf_tcp_option_mptcp_reserved_v1_flag;
+static int hf_tcp_option_mptcp_subtype;
+static int hf_tcp_option_mptcp_version;
+static int hf_tcp_option_mptcp_reserved;
+static int hf_tcp_option_mptcp_address_id;
+static int hf_tcp_option_mptcp_recv_token;
+static int hf_tcp_option_mptcp_sender_key;
+static int hf_tcp_option_mptcp_recv_key;
+static int hf_tcp_option_mptcp_sender_rand;
+static int hf_tcp_option_mptcp_sender_trunc_hmac;
+static int hf_tcp_option_mptcp_sender_hmac;
+static int hf_tcp_option_mptcp_addaddr_trunc_hmac;
+static int hf_tcp_option_mptcp_data_ack_raw;
+static int hf_tcp_option_mptcp_data_seq_no_raw;
+static int hf_tcp_option_mptcp_subflow_seq_no;
+static int hf_tcp_option_mptcp_data_lvl_len;
+static int hf_tcp_option_mptcp_checksum;
+static int hf_tcp_option_mptcp_ipver;
+static int hf_tcp_option_mptcp_echo;
+static int hf_tcp_option_mptcp_ipv4;
+static int hf_tcp_option_mptcp_ipv6;
+static int hf_tcp_option_mptcp_port;
+static int hf_mptcp_expected_idsn;
+
+static int hf_mptcp_dsn;
+static int hf_mptcp_rawdsn64;
+static int hf_mptcp_dss_dsn;
+static int hf_mptcp_ack;
+static int hf_mptcp_stream;
+static int hf_mptcp_expected_token;
+static int hf_mptcp_analysis;
+static int hf_mptcp_analysis_master;
+static int hf_mptcp_analysis_subflows;
+static int hf_mptcp_number_of_removed_addresses;
+static int hf_mptcp_related_mapping;
+static int hf_mptcp_reinjection_of;
+static int hf_mptcp_reinjected_in;
+
+
+static int hf_tcp_option_fast_open_cookie_request;
+static int hf_tcp_option_fast_open_cookie;
+
+static int hf_tcp_ts_relative;
+static int hf_tcp_ts_delta;
+static int hf_tcp_option_scps_vector;
+static int hf_tcp_option_scps_binding;
+static int hf_tcp_option_scps_binding_len;
+static int hf_tcp_scpsoption_flags_bets;
+static int hf_tcp_scpsoption_flags_snack1;
+static int hf_tcp_scpsoption_flags_snack2;
+static int hf_tcp_scpsoption_flags_compress;
+static int hf_tcp_scpsoption_flags_nlts;
+static int hf_tcp_scpsoption_flags_reserved;
+static int hf_tcp_scpsoption_connection_id;
+static int hf_tcp_option_snack_offset;
+static int hf_tcp_option_snack_size;
+static int hf_tcp_option_snack_le;
+static int hf_tcp_option_snack_re;
+static int hf_tcp_option_user_to_granularity;
+static int hf_tcp_option_user_to_val;
+static int hf_tcp_proc_src_uid;
+static int hf_tcp_proc_src_pid;
+static int hf_tcp_proc_src_uname;
+static int hf_tcp_proc_src_cmd;
+static int hf_tcp_proc_dst_uid;
+static int hf_tcp_proc_dst_pid;
+static int hf_tcp_proc_dst_uname;
+static int hf_tcp_proc_dst_cmd;
+static int hf_tcp_segment_data;
+static int hf_tcp_payload;
+static int hf_tcp_reset_cause;
+static int hf_tcp_fin_retransmission;
+static int hf_tcp_option_rvbd_probe_reserved;
+static int hf_tcp_option_scps_binding_data;
+static int hf_tcp_syncookie_time;
+static int hf_tcp_syncookie_mss;
+static int hf_tcp_syncookie_hash;
+static int hf_tcp_syncookie_option_timestamp;
+static int hf_tcp_syncookie_option_ecn;
+static int hf_tcp_syncookie_option_sack;
+static int hf_tcp_syncookie_option_wscale;
+
+static gint ett_tcp;
+static gint ett_tcp_completeness;
+static gint ett_tcp_flags;
+static gint ett_tcp_options;
+static gint ett_tcp_option_timestamp;
+static gint ett_tcp_option_mss;
+static gint ett_tcp_option_wscale;
+static gint ett_tcp_option_sack;
+static gint ett_tcp_option_snack;
+static gint ett_tcp_option_scps;
+static gint ett_tcp_scpsoption_flags;
+static gint ett_tcp_option_scps_extended;
+static gint ett_tcp_option_user_to;
+static gint ett_tcp_option_exp;
+static gint ett_tcp_option_acc_ecn;
+static gint ett_tcp_option_sack_perm;
+static gint ett_tcp_analysis;
+static gint ett_tcp_analysis_faults;
+static gint ett_tcp_timestamps;
+static gint ett_tcp_segments;
+static gint ett_tcp_segment;
+static gint ett_tcp_checksum;
+static gint ett_tcp_process_info;
+static gint ett_tcp_option_mptcp;
+static gint ett_tcp_opt_rvbd_probe;
+static gint ett_tcp_opt_rvbd_probe_flags;
+static gint ett_tcp_opt_rvbd_trpy;
+static gint ett_tcp_opt_rvbd_trpy_flags;
+static gint ett_tcp_opt_echo;
+static gint ett_tcp_opt_cc;
+static gint ett_tcp_opt_md5;
+static gint ett_tcp_opt_ao;
+static gint ett_tcp_opt_qs;
+static gint ett_tcp_opt_recbound;
+static gint ett_tcp_opt_scpscor;
+static gint ett_tcp_unknown_opt;
+static gint ett_tcp_option_other;
+static gint ett_tcp_syncookie;
+static gint ett_tcp_syncookie_option;
+static gint ett_mptcp_analysis;
+static gint ett_mptcp_analysis_subflows;
+
+static expert_field ei_tcp_opt_len_invalid;
+static expert_field ei_tcp_analysis_retransmission;
+static expert_field ei_tcp_analysis_fast_retransmission;
+static expert_field ei_tcp_analysis_spurious_retransmission;
+static expert_field ei_tcp_analysis_out_of_order;
+static expert_field ei_tcp_analysis_reused_ports;
+static expert_field ei_tcp_analysis_lost_packet;
+static expert_field ei_tcp_analysis_ack_lost_packet;
+static expert_field ei_tcp_analysis_window_update;
+static expert_field ei_tcp_analysis_window_full;
+static expert_field ei_tcp_analysis_keep_alive;
+static expert_field ei_tcp_analysis_keep_alive_ack;
+static expert_field ei_tcp_analysis_duplicate_ack;
+static expert_field ei_tcp_analysis_zero_window_probe;
+static expert_field ei_tcp_analysis_zero_window;
+static expert_field ei_tcp_analysis_zero_window_probe_ack;
+static expert_field ei_tcp_analysis_tfo_syn;
+static expert_field ei_tcp_analysis_tfo_ack;
+static expert_field ei_tcp_analysis_tfo_ignored;
+static expert_field ei_tcp_scps_capable;
+static expert_field ei_tcp_option_sack_dsack;
+static expert_field ei_tcp_option_snack_sequence;
+static expert_field ei_tcp_option_wscale_shift_invalid;
+static expert_field ei_tcp_option_mss_absent;
+static expert_field ei_tcp_option_mss_present;
+static expert_field ei_tcp_option_sack_perm_absent;
+static expert_field ei_tcp_option_sack_perm_present;
+static expert_field ei_tcp_short_segment;
+static expert_field ei_tcp_ack_nonzero;
+static expert_field ei_tcp_connection_synack;
+static expert_field ei_tcp_connection_syn;
+static expert_field ei_tcp_connection_fin;
+static expert_field ei_tcp_connection_rst;
+static expert_field ei_tcp_connection_fin_active;
+static expert_field ei_tcp_connection_fin_passive;
+static expert_field ei_tcp_checksum_ffff;
+static expert_field ei_tcp_checksum_partial;
+static expert_field ei_tcp_checksum_bad;
+static expert_field ei_tcp_urgent_pointer_non_zero;
+static expert_field ei_tcp_suboption_malformed;
+static expert_field ei_tcp_nop;
+static expert_field ei_tcp_non_zero_bytes_after_eol;
+static expert_field ei_tcp_bogus_header_length;
+
+/* static expert_field ei_mptcp_analysis_unexpected_idsn; */
+static expert_field ei_mptcp_analysis_echoed_key_mismatch;
+static expert_field ei_mptcp_analysis_missing_algorithm;
+static expert_field ei_mptcp_analysis_unsupported_algorithm;
+static expert_field ei_mptcp_infinite_mapping;
+static expert_field ei_mptcp_mapping_missing;
+/* static expert_field ei_mptcp_stream_incomplete; */
+/* static expert_field ei_mptcp_analysis_dsn_out_of_order; */
/* Some protocols such as encrypted DCE/RPCoverHTTP have dependencies
* from one PDU to the next PDU and require that they are called in sequence.
@@ -456,14 +498,19 @@ static expert_field ei_mptcp_mapping_missing = EI_INIT;
*/
static gboolean tcp_no_subdissector_on_error = TRUE;
+/* Enable buffering of out-of-order TCP segments before passing it to a
+ * subdissector (depends on "tcp_desegment"). */
+static gboolean tcp_reassemble_out_of_order = FALSE;
+
/*
- * FF: (draft-ietf-tcpm-experimental-options-03)
+ * FF: https://www.rfc-editor.org/rfc/rfc6994.html
* With this flag set we assume the option structure for experimental
- * codepoints (253, 254) has a magic number field (first field after the
- * Kind and Length). The magic number is used to differentiate different
- * experiments and thus will be used in data dissection.
+ * codepoints (253, 254) has an Experiment Identifier (ExID), which is
+ * the first 16-bit field after the Kind and Length.
+ * The ExID is used to differentiate different experiments and thus will
+ * be used in data dissection.
*/
-static gboolean tcp_exp_options_with_magic = TRUE;
+static gboolean tcp_exp_options_rfc6994 = TRUE;
/*
* This flag indicates which of Fast Retransmission or Out-of-Order
@@ -477,6 +524,9 @@ static gboolean tcp_fastrt_precedence = TRUE;
/* Process info, currently discovered via IPFIX */
static gboolean tcp_display_process_info = FALSE;
+/* Read the sequence number as syn cookie */
+static gboolean read_seq_as_syn_cookie = FALSE;
+
/*
* TCP option
*/
@@ -502,6 +552,8 @@ static gboolean tcp_display_process_info = FALSE;
#define TCPOPT_AO 29 /* RFC5925 The TCP Authentication Option */
#define TCPOPT_MPTCP 30 /* RFC6824 Multipath TCP */
#define TCPOPT_TFO 34 /* RFC7413 TCP Fast Open Cookie */
+#define TCPOPT_ACC_ECN_0 0xac /* draft-ietf-tcpm-accurate-ecn */
+#define TCPOPT_ACC_ECN_1 0xae /* draft-ietf-tcpm-accurate-ecn */
#define TCPOPT_EXP_FD 0xfd /* Experimental, reserved */
#define TCPOPT_EXP_FE 0xfe /* Experimental, reserved */
/* Non IANA registered option numbers */
@@ -532,7 +584,28 @@ static gboolean tcp_display_process_info = FALSE;
#define TCPOLEN_TFO_MIN 2
#define TCPOLEN_RVBD_PROBE_MIN 3
#define TCPOLEN_RVBD_TRPY_MIN 16
-#define TCPOLEN_EXP_MIN 2
+#define TCPOLEN_EXP_MIN 4
+
+/*
+ * TCP Experimental Option Experiment Identifiers (TCP ExIDs)
+ * See: https://www.iana.org/assignments/tcp-parameters/tcp-parameters.xhtml#tcp-exids
+ * Wireshark only supports 16-bit ExIDs
+ */
+
+#define TCPEXID_TARR 0x00ac
+#define TCPEXID_HOST_ID 0x0348
+#define TCPEXID_ASC 0x0a0d
+#define TCPEXID_CAPABILITY 0x0ca0
+#define TCPEXID_EDO 0x0ed0
+#define TCPEXID_ENO 0x454e
+#define TCPEXID_SNO 0x5323
+#define TCPEXID_TS_INTERVAL 0x75ec /* 32-bit ExID: 0x75ecffee */
+#define TCPEXID_ACC_ECN_0 0xacc0
+#define TCPEXID_ACC_ECN_1 0xacc1
+#define TCPEXID_ACC_ECN 0xacce
+#define TCPEXID_SMC_R 0xe2d4 /* 32-bit ExID: 0xe2d4c3d9 */
+#define TCPEXID_FO 0xf989
+#define TCPEXID_LOW_LATENCY 0xf990
/*
* Multipath TCP subtypes
@@ -596,12 +669,32 @@ static const value_string tcp_option_kind_vs[] = {
{ TCPOPT_TFO, "TCP Fast Open Cookie" },
{ TCPOPT_RVBD_PROBE, "Riverbed Probe" },
{ TCPOPT_RVBD_TRPY, "Riverbed Transparency" },
+ { TCPOPT_ACC_ECN_0, "Accurate ECN Order 0" },
+ { TCPOPT_ACC_ECN_1, "Accurate ECN Order 1" },
{ TCPOPT_EXP_FD, "RFC3692-style Experiment 1" },
{ TCPOPT_EXP_FE, "RFC3692-style Experiment 2" },
{ 0, NULL }
};
static value_string_ext tcp_option_kind_vs_ext = VALUE_STRING_EXT_INIT(tcp_option_kind_vs);
+static const value_string tcp_exid_vs[] = {
+ { TCPEXID_TARR, "TCP ACK Rate Request" },
+ { TCPEXID_HOST_ID, "Host ID" },
+ { TCPEXID_ASC, "Autonomous System Compensation" },
+ { TCPEXID_CAPABILITY, "Capability Option" },
+ { TCPEXID_EDO, "Extended Data Offset" },
+ { TCPEXID_ENO, "Encryption Negotiation" },
+ { TCPEXID_SNO, "Service Number" },
+ { TCPEXID_TS_INTERVAL, "Timestamp Interval" },
+ { TCPEXID_ACC_ECN_0, "Accurate ECN - Order 0" },
+ { TCPEXID_ACC_ECN_1, "Accurate ECN - Order 1" },
+ { TCPEXID_ACC_ECN, "Accurate ECN" },
+ { TCPEXID_SMC_R, "Shared Memory communications over RMDA protocol" },
+ { TCPEXID_FO, "Fast Open" },
+ { TCPEXID_LOW_LATENCY, "Low Latency" },
+ { 0, NULL }
+};
+
/* not all of the hf_fields below make sense for TCP but we have to provide
them anyways to comply with the API (which was aimed for IP fragment
reassembly) */
@@ -643,6 +736,8 @@ static dissector_handle_t data_handle;
static dissector_handle_t tcp_handle;
static dissector_handle_t sport_handle;
static dissector_handle_t tcp_opt_unknown_handle;
+static capture_dissector_handle_t tcp_cap_handle;
+
static guint32 tcp_stream_count;
static guint32 mptcp_stream_count;
@@ -695,28 +790,51 @@ static int * const tcp_option_mptcp_tcprst_flags[] = {
static const unit_name_string units_64bit_version = { " (64bits version)", NULL };
+static guint8
+tcp_get_ace(const struct tcpheader *tcph)
+{
+ guint8 ace;
+
+ ace = 0;
+ if (tcph->th_flags & TH_AE) {
+ ace += 4;
+ }
+ if (tcph->th_flags & TH_CWR) {
+ ace += 2;
+ }
+ if (tcph->th_flags & TH_ECE) {
+ ace += 1;
+ }
+ return ace;
+}
static char *
tcp_flags_to_str(wmem_allocator_t *scope, const struct tcpheader *tcph)
{
- static const char flags[][4] = { "FIN", "SYN", "RST", "PSH", "ACK", "URG", "ECN", "CWR", "NS" };
+ static const char flags[][4] = { "FIN", "SYN", "RST", "PSH", "ACK", "URG", "ECE", "CWR", "AE" };
+ static const char digit[][2] = { "0", "1", "2", "3", "4", "5", "6", "7" };
const int maxlength = 64; /* upper bounds, max 53B: 8 * 3 + 2 + strlen("Reserved") + 9 * 2 + 1 */
char *pbuf;
char *buf;
-
+ guint8 ace;
int i;
buf = pbuf = (char *) wmem_alloc(scope, maxlength);
*pbuf = '\0';
- for (i = 0; i < 9; i++) {
+ for (i = 0; i < (tcph->th_use_ace ? 6 : 9); i++) {
if (tcph->th_flags & (1 << i)) {
if (buf[0])
pbuf = g_stpcpy(pbuf, ", ");
pbuf = g_stpcpy(pbuf, flags[i]);
}
}
+ if (tcph->th_use_ace) {
+ ace = tcp_get_ace(tcph);
+ pbuf = g_stpcpy(pbuf, ", ACE=");
+ pbuf = g_stpcpy(pbuf, digit[ace]);
+ }
if (tcph->th_flags & TH_RES) {
if (buf[0])
@@ -725,36 +843,87 @@ tcp_flags_to_str(wmem_allocator_t *scope, const struct tcpheader *tcph)
}
if (buf[0] == '\0')
- buf = "<None>";
+ g_stpcpy(pbuf, "<None>");
return buf;
}
+
static char *
tcp_flags_to_str_first_letter(wmem_allocator_t *scope, const struct tcpheader *tcph)
{
wmem_strbuf_t *buf = wmem_strbuf_new(scope, "");
unsigned i;
const unsigned flags_count = 12;
- const char first_letters[] = "RRRNCEUAPRSF";
+ const char first_letters[] = "RRRACEUAPRSF";
+ const char digits[] = "01234567";
/* upper three bytes are marked as reserved ('R'). */
for (i = 0; i < flags_count; i++) {
- if (((tcph->th_flags >> (flags_count - 1 - i)) & 1)) {
- wmem_strbuf_append_c(buf, first_letters[i]);
+ if (tcph->th_use_ace && 3 <= i && i <= 5) {
+ if (i == 4) {
+ wmem_strbuf_append_c(buf, digits[tcp_get_ace(tcph)]);
+ } else {
+ wmem_strbuf_append_c(buf, '-');
+ }
} else {
- wmem_strbuf_append(buf, UTF8_MIDDLE_DOT);
+ if (((tcph->th_flags >> (flags_count - 1 - i)) & 1)) {
+ wmem_strbuf_append_c(buf, first_letters[i]);
+ } else {
+ wmem_strbuf_append(buf, UTF8_MIDDLE_DOT);
+ }
}
}
return wmem_strbuf_finalize(buf);
}
+/*
+ * Print the first letter of each flag set, or the dot character otherwise
+ */
+static char *
+completeness_flags_to_str_first_letter(wmem_allocator_t *scope, guint8 flags)
+{
+ wmem_strbuf_t *buf = wmem_strbuf_new(scope, "");
+
+ if( flags & TCP_COMPLETENESS_RST )
+ wmem_strbuf_append(buf, "R");
+ else
+ wmem_strbuf_append(buf, UTF8_MIDDLE_DOT);
+
+ if( flags & TCP_COMPLETENESS_FIN )
+ wmem_strbuf_append(buf, "F");
+ else
+ wmem_strbuf_append(buf, UTF8_MIDDLE_DOT);
+
+ if( flags & TCP_COMPLETENESS_DATA )
+ wmem_strbuf_append(buf, "D");
+ else
+ wmem_strbuf_append(buf, UTF8_MIDDLE_DOT);
+
+ if( flags & TCP_COMPLETENESS_ACK )
+ wmem_strbuf_append(buf, "A");
+ else
+ wmem_strbuf_append(buf, UTF8_MIDDLE_DOT);
+
+ if( flags & TCP_COMPLETENESS_SYNACK )
+ wmem_strbuf_append(buf, "S");
+ else
+ wmem_strbuf_append(buf, UTF8_MIDDLE_DOT);
+
+ if( flags & TCP_COMPLETENESS_SYNSENT )
+ wmem_strbuf_append(buf, "S");
+ else
+ wmem_strbuf_append(buf, UTF8_MIDDLE_DOT);
+
+ return wmem_strbuf_finalize(buf);
+}
+
static void
tcp_src_prompt(packet_info *pinfo, gchar *result)
{
guint32 port = GPOINTER_TO_UINT(p_get_proto_data(pinfo->pool, pinfo, hf_tcp_srcport, pinfo->curr_layer_num));
- g_snprintf(result, MAX_DECODE_AS_PROMPT_LEN, "source (%u%s)", port, UTF8_RIGHTWARDS_ARROW);
+ snprintf(result, MAX_DECODE_AS_PROMPT_LEN, "source (%u%s)", port, UTF8_RIGHTWARDS_ARROW);
}
static gpointer
@@ -768,7 +937,7 @@ tcp_dst_prompt(packet_info *pinfo, gchar *result)
{
guint32 port = GPOINTER_TO_UINT(p_get_proto_data(pinfo->pool, pinfo, hf_tcp_dstport, pinfo->curr_layer_num));
- g_snprintf(result, MAX_DECODE_AS_PROMPT_LEN, "destination (%s%u)", UTF8_RIGHTWARDS_ARROW, port);
+ snprintf(result, MAX_DECODE_AS_PROMPT_LEN, "destination (%s%u)", UTF8_RIGHTWARDS_ARROW, port);
}
static gpointer
@@ -782,7 +951,7 @@ tcp_both_prompt(packet_info *pinfo, gchar *result)
{
guint32 srcport = GPOINTER_TO_UINT(p_get_proto_data(pinfo->pool, pinfo, hf_tcp_srcport, pinfo->curr_layer_num)),
destport = GPOINTER_TO_UINT(p_get_proto_data(pinfo->pool, pinfo, hf_tcp_dstport, pinfo->curr_layer_num));
- g_snprintf(result, MAX_DECODE_AS_PROMPT_LEN, "both (%u%s%u)", srcport, UTF8_LEFT_RIGHT_ARROW, destport);
+ snprintf(result, MAX_DECODE_AS_PROMPT_LEN, "both (%u%s%u)", srcport, UTF8_LEFT_RIGHT_ARROW, destport);
}
static const char* tcp_conv_get_filter_type(conv_item_t* conv, conv_filter_type_e filter)
@@ -828,32 +997,36 @@ static const char* tcp_conv_get_filter_type(conv_item_t* conv, conv_filter_type_
static ct_dissector_info_t tcp_ct_dissector_info = {&tcp_conv_get_filter_type};
static tap_packet_status
-tcpip_conversation_packet(void *pct, packet_info *pinfo, epan_dissect_t *edt _U_, const void *vip)
+tcpip_conversation_packet(void *pct, packet_info *pinfo, epan_dissect_t *edt _U_, const void *vip, tap_flags_t flags)
{
conv_hash_t *hash = (conv_hash_t*) pct;
+ hash->flags = flags;
+
const struct tcpheader *tcphdr=(const struct tcpheader *)vip;
add_conversation_table_data_with_conv_id(hash, &tcphdr->ip_src, &tcphdr->ip_dst, tcphdr->th_sport, tcphdr->th_dport, (conv_id_t) tcphdr->th_stream, 1, pinfo->fd->pkt_len,
- &pinfo->rel_ts, &pinfo->abs_ts, &tcp_ct_dissector_info, ENDPOINT_TCP);
+ &pinfo->rel_ts, &pinfo->abs_ts, &tcp_ct_dissector_info, CONVERSATION_TCP);
return TAP_PACKET_REDRAW;
}
static tap_packet_status
-mptcpip_conversation_packet(void *pct, packet_info *pinfo, epan_dissect_t *edt _U_, const void *vip)
+mptcpip_conversation_packet(void *pct, packet_info *pinfo, epan_dissect_t *edt _U_, const void *vip, tap_flags_t flags)
{
conv_hash_t *hash = (conv_hash_t*) pct;
+ hash->flags = flags;
+
const struct tcp_analysis *tcpd=(const struct tcp_analysis *)vip;
const mptcp_meta_flow_t *meta=(const mptcp_meta_flow_t *)tcpd->fwd->mptcp_subflow->meta;
add_conversation_table_data_with_conv_id(hash, &meta->ip_src, &meta->ip_dst,
meta->sport, meta->dport, (conv_id_t) tcpd->mptcp_analysis->stream, 1, pinfo->fd->pkt_len,
- &pinfo->rel_ts, &pinfo->abs_ts, &tcp_ct_dissector_info, ENDPOINT_TCP);
+ &pinfo->rel_ts, &pinfo->abs_ts, &tcp_ct_dissector_info, CONVERSATION_TCP);
return TAP_PACKET_REDRAW;
}
-static const char* tcp_host_get_filter_type(hostlist_talker_t* host, conv_filter_type_e filter)
+static const char* tcp_endpoint_get_filter_type(endpoint_item_t* endpoint, conv_filter_type_e filter)
{
if (filter == CONV_FT_SRC_PORT)
return "tcp.srcport";
@@ -864,63 +1037,65 @@ static const char* tcp_host_get_filter_type(hostlist_talker_t* host, conv_filter
if (filter == CONV_FT_ANY_PORT)
return "tcp.port";
- if(!host) {
+ if(!endpoint) {
return CONV_FILTER_INVALID;
}
if (filter == CONV_FT_SRC_ADDRESS) {
- if (host->myaddress.type == AT_IPv4)
+ if (endpoint->myaddress.type == AT_IPv4)
return "ip.src";
- if (host->myaddress.type == AT_IPv6)
+ if (endpoint->myaddress.type == AT_IPv6)
return "ipv6.src";
}
if (filter == CONV_FT_DST_ADDRESS) {
- if (host->myaddress.type == AT_IPv4)
+ if (endpoint->myaddress.type == AT_IPv4)
return "ip.dst";
- if (host->myaddress.type == AT_IPv6)
+ if (endpoint->myaddress.type == AT_IPv6)
return "ipv6.dst";
}
if (filter == CONV_FT_ANY_ADDRESS) {
- if (host->myaddress.type == AT_IPv4)
+ if (endpoint->myaddress.type == AT_IPv4)
return "ip.addr";
- if (host->myaddress.type == AT_IPv6)
+ if (endpoint->myaddress.type == AT_IPv6)
return "ipv6.addr";
}
return CONV_FILTER_INVALID;
}
-static hostlist_dissector_info_t tcp_host_dissector_info = {&tcp_host_get_filter_type};
+static et_dissector_info_t tcp_endpoint_dissector_info = {&tcp_endpoint_get_filter_type};
static tap_packet_status
-tcpip_hostlist_packet(void *pit, packet_info *pinfo, epan_dissect_t *edt _U_, const void *vip)
+tcpip_endpoint_packet(void *pit, packet_info *pinfo, epan_dissect_t *edt _U_, const void *vip, tap_flags_t flags)
{
conv_hash_t *hash = (conv_hash_t*) pit;
+ hash->flags = flags;
+
const struct tcpheader *tcphdr=(const struct tcpheader *)vip;
/* Take two "add" passes per packet, adding for each direction, ensures that all
packets are counted properly (even if address is sending to itself)
- XXX - this could probably be done more efficiently inside hostlist_table */
- add_hostlist_table_data(hash, &tcphdr->ip_src, tcphdr->th_sport, TRUE, 1, pinfo->fd->pkt_len, &tcp_host_dissector_info, ENDPOINT_TCP);
- add_hostlist_table_data(hash, &tcphdr->ip_dst, tcphdr->th_dport, FALSE, 1, pinfo->fd->pkt_len, &tcp_host_dissector_info, ENDPOINT_TCP);
+ XXX - this could probably be done more efficiently inside endpoint_table */
+ add_endpoint_table_data(hash, &tcphdr->ip_src, tcphdr->th_sport, TRUE, 1, pinfo->fd->pkt_len, &tcp_endpoint_dissector_info, ENDPOINT_TCP);
+ add_endpoint_table_data(hash, &tcphdr->ip_dst, tcphdr->th_dport, FALSE, 1, pinfo->fd->pkt_len, &tcp_endpoint_dissector_info, ENDPOINT_TCP);
return TAP_PACKET_REDRAW;
}
static gboolean
-tcp_filter_valid(packet_info *pinfo)
+tcp_filter_valid(packet_info *pinfo, void *user_data _U_)
{
return proto_is_frame_protocol(pinfo->layers, "tcp");
}
static gchar*
-tcp_build_filter(packet_info *pinfo)
+tcp_build_filter(packet_info *pinfo, void *user_data _U_)
{
if( pinfo->net_src.type == AT_IPv4 && pinfo->net_dst.type == AT_IPv4 ) {
/* TCP over IPv4 */
- return g_strdup_printf("(ip.addr eq %s and ip.addr eq %s) and (tcp.port eq %d and tcp.port eq %d)",
+ return ws_strdup_printf("(ip.addr eq %s and ip.addr eq %s) and (tcp.port eq %d and tcp.port eq %d)",
address_to_str(pinfo->pool, &pinfo->net_src),
address_to_str(pinfo->pool, &pinfo->net_dst),
pinfo->srcport, pinfo->destport );
@@ -928,7 +1103,7 @@ tcp_build_filter(packet_info *pinfo)
if( pinfo->net_src.type == AT_IPv6 && pinfo->net_dst.type == AT_IPv6 ) {
/* TCP over IPv6 */
- return g_strdup_printf("(ipv6.addr eq %s and ipv6.addr eq %s) and (tcp.port eq %d and tcp.port eq %d)",
+ return ws_strdup_printf("(ipv6.addr eq %s and ipv6.addr eq %s) and (tcp.port eq %d and tcp.port eq %d)",
address_to_str(pinfo->pool, &pinfo->net_src),
address_to_str(pinfo->pool, &pinfo->net_dst),
pinfo->srcport, pinfo->destport );
@@ -941,7 +1116,7 @@ tcp_build_filter(packet_info *pinfo)
/* whenever a TCP packet is seen by the tap listener */
/* Add a new tcp frame into the graph */
static tap_packet_status
-tcp_seq_analysis_packet( void *ptr, packet_info *pinfo, epan_dissect_t *edt _U_, const void *tcp_info)
+tcp_seq_analysis_packet( void *ptr, packet_info *pinfo, epan_dissect_t *edt _U_, const void *tcp_info, tap_flags_t tapflags _U_)
{
seq_analysis_info_t *sainfo = (seq_analysis_info_t *) ptr;
const struct tcpheader *tcph = (const struct tcpheader *)tcp_info;
@@ -959,7 +1134,7 @@ tcp_seq_analysis_packet( void *ptr, packet_info *pinfo, epan_dissect_t *edt _U_,
flags = tcp_flags_to_str(NULL, tcph);
if ((tcph->th_have_seglen)&&(tcph->th_seglen!=0)){
- sai->frame_label = g_strdup_printf("%s - Len: %u",flags, tcph->th_seglen);
+ sai->frame_label = ws_strdup_printf("%s - Len: %u",flags, tcph->th_seglen);
}
else{
sai->frame_label = g_strdup(flags);
@@ -968,9 +1143,9 @@ tcp_seq_analysis_packet( void *ptr, packet_info *pinfo, epan_dissect_t *edt _U_,
wmem_free(NULL, flags);
if (tcph->th_flags & TH_ACK)
- sai->comment = g_strdup_printf("Seq = %u Ack = %u",tcph->th_seq, tcph->th_ack);
+ sai->comment = ws_strdup_printf("Seq = %u Ack = %u",tcph->th_seq, tcph->th_ack);
else
- sai->comment = g_strdup_printf("Seq = %u",tcph->th_seq);
+ sai->comment = ws_strdup_printf("Seq = %u",tcph->th_seq);
sai->line_style = 1;
sai->conv_num = (guint16) tcph->th_stream;
@@ -987,9 +1162,16 @@ gchar *tcp_follow_conv_filter(epan_dissect_t *edt _U_, packet_info *pinfo, guint
conversation_t *conv;
struct tcp_analysis *tcpd;
+ /* XXX: Since TCP doesn't use the endpoint API, we can only look
+ * up using the current pinfo addresses and ports. We don't want
+ * to create a new conversation or new TCP stream.
+ * Eventually the endpoint API should support storing multiple
+ * endpoints and TCP should be changed to use the endpoint API.
+ */
if (((pinfo->net_src.type == AT_IPv4 && pinfo->net_dst.type == AT_IPv4) ||
(pinfo->net_src.type == AT_IPv6 && pinfo->net_dst.type == AT_IPv6))
- && (conv=find_conversation_pinfo(pinfo, 0)) != NULL )
+ && (pinfo->ptype == PT_TCP) &&
+ (conv=find_conversation(pinfo->num, &pinfo->net_src, &pinfo->net_dst, CONVERSATION_TCP, pinfo->srcport, pinfo->destport, 0)) != NULL)
{
/* TCP over IPv4/6 */
tcpd=get_tcp_conversation_data(conv, pinfo);
@@ -997,7 +1179,7 @@ gchar *tcp_follow_conv_filter(epan_dissect_t *edt _U_, packet_info *pinfo, guint
return NULL;
*stream = tcpd->stream;
- return g_strdup_printf("tcp.stream eq %u", tcpd->stream);
+ return ws_strdup_printf("tcp.stream eq %u", tcpd->stream);
}
return NULL;
@@ -1005,7 +1187,7 @@ gchar *tcp_follow_conv_filter(epan_dissect_t *edt _U_, packet_info *pinfo, guint
gchar *tcp_follow_index_filter(guint stream, guint sub_stream _U_)
{
- return g_strdup_printf("tcp.stream eq %u", stream);
+ return ws_strdup_printf("tcp.stream eq %u", stream);
}
gchar *tcp_follow_address_filter(address *src_addr, address *dst_addr, int src_port, int dst_port)
@@ -1017,7 +1199,7 @@ gchar *tcp_follow_address_filter(address *src_addr, address *dst_addr, int src_p
address_to_str_buf(src_addr, src_addr_str, sizeof(src_addr_str));
address_to_str_buf(dst_addr, dst_addr_str, sizeof(dst_addr_str));
- return g_strdup_printf("((ip%s.src eq %s and tcp.srcport eq %d) and "
+ return ws_strdup_printf("((ip%s.src eq %s and tcp.srcport eq %d) and "
"(ip%s.dst eq %s and tcp.dstport eq %d))"
" or "
"((ip%s.src eq %s and tcp.srcport eq %d) and "
@@ -1045,7 +1227,7 @@ typedef struct tcp_follow_tap_data
* dummy data to mark packet loss if any).
*
* Returns TRUE if one fragment has been applied or FALSE if no more fragments
- * can be added the the payload (there might still be unacked fragments with
+ * can be added to the payload (there might still be unacked fragments with
* missing segments before them).
*/
static gboolean
@@ -1132,7 +1314,7 @@ check_follow_fragments(follow_info_t *follow_info, gboolean is_server, guint32 a
* by the receiving host. Add dummy stream chunk with the data
* "[xxx bytes missing in capture file]".
*/
- dummy_str = g_strdup_printf("[%d bytes missing in capture file]",
+ dummy_str = ws_strdup_printf("[%d bytes missing in capture file]",
(int)(lowest_seq - follow_info->seq[is_server]) );
// XXX the dummy replacement could be larger than the actual missing bytes.
@@ -1156,14 +1338,16 @@ check_follow_fragments(follow_info_t *follow_info, gboolean is_server, guint32 a
static tap_packet_status
follow_tcp_tap_listener(void *tapdata, packet_info *pinfo,
- epan_dissect_t *edt _U_, const void *data)
+ epan_dissect_t *edt _U_, const void *data, tap_flags_t flags _U_)
{
follow_record_t *follow_record;
follow_info_t *follow_info = (follow_info_t *)tapdata;
const tcp_follow_tap_data_t *follow_data = (const tcp_follow_tap_data_t *)data;
gboolean is_server;
guint32 sequence = follow_data->tcph->th_seq;
- guint32 length = follow_data->tcph->th_seglen;
+ guint32 length = follow_data->tcph->th_have_seglen
+ ? follow_data->tcph->th_seglen
+ : 0;
guint32 data_offset = 0;
guint32 data_length = tvb_captured_length(follow_data->tvb);
@@ -1273,10 +1457,34 @@ static int exp_pdu_tcp_dissector_data_populate_data(packet_info *pinfo _U_, void
return exp_pdu_tcp_dissector_data_size(pinfo, data);
}
+static tvbuff_t*
+handle_export_pdu_check_desegmentation(packet_info *pinfo, tvbuff_t *tvb)
+{
+ /* Check to see if the tvb we're planning on exporting PDUs from was
+ * dissected fully, or whether it requested further desegmentation.
+ * This should only matter on the first pass (so in one-pass tshark.)
+ */
+ if (pinfo->can_desegment > 0 && pinfo->desegment_len != 0) {
+ /* Desegmentation was requested. How much did we desegment here?
+ * The rest, presumably, will be handled in another frame.
+ */
+ if (pinfo->desegment_offset == 0) {
+ /* We couldn't, in fact, dissect any of it. */
+ return NULL;
+ }
+ tvb = tvb_new_subset_length(tvb, 0, pinfo->desegment_offset);
+ }
+ return tvb;
+}
+
static void
handle_export_pdu_dissection_table(packet_info *pinfo, tvbuff_t *tvb, guint32 port, struct tcpinfo *tcpinfo)
{
if (have_tap_listener(exported_pdu_tap)) {
+ tvb = handle_export_pdu_check_desegmentation(pinfo, tvb);
+ if (tvb == NULL) {
+ return;
+ }
exp_pdu_data_item_t exp_pdu_data_table_value = {exp_pdu_data_dissector_table_num_value_size, exp_pdu_data_dissector_table_num_value_populate_data, NULL};
exp_pdu_data_item_t exp_pdu_data_dissector_data = {exp_pdu_tcp_dissector_data_size, exp_pdu_tcp_dissector_data_populate_data, NULL};
const exp_pdu_data_item_t *tcp_exp_pdu_items[] = {
@@ -1311,9 +1519,13 @@ handle_export_pdu_heuristic(packet_info *pinfo, tvbuff_t *tvb, heur_dtbl_entry_t
exp_pdu_data_t *exp_pdu_data = NULL;
if (have_tap_listener(exported_pdu_tap)) {
+ tvb = handle_export_pdu_check_desegmentation(pinfo, tvb);
+ if (tvb == NULL) {
+ return;
+ }
if ((!hdtbl_entry->enabled) ||
(hdtbl_entry->protocol != NULL && !proto_is_protocol_enabled(hdtbl_entry->protocol))) {
- exp_pdu_data = export_pdu_create_common_tags(pinfo, "data", EXP_PDU_TAG_PROTO_NAME);
+ exp_pdu_data = export_pdu_create_common_tags(pinfo, "data", EXP_PDU_TAG_DISSECTOR_NAME);
} else if (hdtbl_entry->protocol != NULL) {
exp_pdu_data_item_t exp_pdu_data_dissector_data = {exp_pdu_tcp_dissector_data_size, exp_pdu_tcp_dissector_data_populate_data, NULL};
const exp_pdu_data_item_t *tcp_exp_pdu_items[] = {
@@ -1329,7 +1541,7 @@ handle_export_pdu_heuristic(packet_info *pinfo, tvbuff_t *tvb, heur_dtbl_entry_t
exp_pdu_data_dissector_data.data = tcpinfo;
- exp_pdu_data = export_pdu_create_tags(pinfo, hdtbl_entry->short_name, EXP_PDU_TAG_HEUR_PROTO_NAME, tcp_exp_pdu_items);
+ exp_pdu_data = export_pdu_create_tags(pinfo, hdtbl_entry->short_name, EXP_PDU_TAG_HEUR_DISSECTOR_NAME, tcp_exp_pdu_items);
}
if (exp_pdu_data != NULL) {
@@ -1346,7 +1558,11 @@ static void
handle_export_pdu_conversation(packet_info *pinfo, tvbuff_t *tvb, int src_port, int dst_port, struct tcpinfo *tcpinfo)
{
if (have_tap_listener(exported_pdu_tap)) {
- conversation_t *conversation = find_conversation(pinfo->num, &pinfo->src, &pinfo->dst, ENDPOINT_TCP, src_port, dst_port, 0);
+ tvb = handle_export_pdu_check_desegmentation(pinfo, tvb);
+ if (tvb == NULL) {
+ return;
+ }
+ conversation_t *conversation = find_conversation(pinfo->num, &pinfo->src, &pinfo->dst, CONVERSATION_TCP, src_port, dst_port, 0);
if (conversation != NULL)
{
dissector_handle_t handle = (dissector_handle_t)wmem_tree_lookup32_le(conversation->dissector_tree, pinfo->num);
@@ -1368,7 +1584,7 @@ handle_export_pdu_conversation(packet_info *pinfo, tvbuff_t *tvb, int src_port,
exp_pdu_data_dissector_data.data = tcpinfo;
- exp_pdu_data = export_pdu_create_tags(pinfo, dissector_handle_get_dissector_name(handle), EXP_PDU_TAG_PROTO_NAME, tcp_exp_pdu_items);
+ exp_pdu_data = export_pdu_create_tags(pinfo, dissector_handle_get_dissector_name(handle), EXP_PDU_TAG_DISSECTOR_NAME, tcp_exp_pdu_items);
exp_pdu_data->tvb_captured_length = tvb_captured_length(tvb);
exp_pdu_data->tvb_reported_length = tvb_reported_length(tvb);
exp_pdu_data->pdu_tvb = tvb;
@@ -1388,22 +1604,22 @@ static void conversation_completeness_fill(gchar *buf, guint32 value)
{
switch(value) {
case TCP_COMPLETENESS_SYNSENT:
- g_snprintf(buf, ITEM_LABEL_LENGTH, "Incomplete, SYN_SENT (%u)", value);
+ snprintf(buf, ITEM_LABEL_LENGTH, "Incomplete, SYN_SENT (%u)", value);
break;
case (TCP_COMPLETENESS_SYNSENT|
TCP_COMPLETENESS_SYNACK):
- g_snprintf(buf, ITEM_LABEL_LENGTH, "Incomplete, CLIENT_ESTABLISHED (%u)", value);
+ snprintf(buf, ITEM_LABEL_LENGTH, "Incomplete, CLIENT_ESTABLISHED (%u)", value);
break;
case (TCP_COMPLETENESS_SYNSENT|
TCP_COMPLETENESS_SYNACK|
TCP_COMPLETENESS_ACK):
- g_snprintf(buf, ITEM_LABEL_LENGTH, "Incomplete, ESTABLISHED (%u)", value);
+ snprintf(buf, ITEM_LABEL_LENGTH, "Incomplete, ESTABLISHED (%u)", value);
break;
case (TCP_COMPLETENESS_SYNSENT|
TCP_COMPLETENESS_SYNACK|
TCP_COMPLETENESS_ACK|
TCP_COMPLETENESS_DATA):
- g_snprintf(buf, ITEM_LABEL_LENGTH, "Incomplete, DATA (%u)", value);
+ snprintf(buf, ITEM_LABEL_LENGTH, "Incomplete, DATA (%u)", value);
break;
case (TCP_COMPLETENESS_SYNSENT|
TCP_COMPLETENESS_SYNACK|
@@ -1421,7 +1637,7 @@ static void conversation_completeness_fill(gchar *buf, guint32 value)
TCP_COMPLETENESS_DATA|
TCP_COMPLETENESS_FIN|
TCP_COMPLETENESS_RST):
- g_snprintf(buf, ITEM_LABEL_LENGTH, "Complete, WITH_DATA (%u)", value);
+ snprintf(buf, ITEM_LABEL_LENGTH, "Complete, WITH_DATA (%u)", value);
break;
case (TCP_COMPLETENESS_SYNSENT|
TCP_COMPLETENESS_SYNACK|
@@ -1436,10 +1652,10 @@ static void conversation_completeness_fill(gchar *buf, guint32 value)
TCP_COMPLETENESS_ACK|
TCP_COMPLETENESS_FIN|
TCP_COMPLETENESS_RST):
- g_snprintf(buf, ITEM_LABEL_LENGTH, "Complete, NO_DATA (%u)", value);
+ snprintf(buf, ITEM_LABEL_LENGTH, "Complete, NO_DATA (%u)", value);
break;
default:
- g_snprintf(buf, ITEM_LABEL_LENGTH, "Incomplete (%u)", value);
+ snprintf(buf, ITEM_LABEL_LENGTH, "Incomplete (%u)", value);
break;
}
}
@@ -1477,6 +1693,15 @@ static gboolean mptcp_intersubflows_retransmission = FALSE;
#define TCP_A_REUSED_PORTS 0x2000
#define TCP_A_SPURIOUS_RETRANSMISSION 0x4000
+/* This flag for desegment_tcp to exclude segments with previously
+ * seen sequence numbers.
+ * It is from the perspective of Wireshark's reassembler, whereas
+ * the other flags above are from the perspective of the sender.
+ * (E.g., TCP_A_RETRANSMISSION or TCP_A_SPURIOUS_RETRANSMISSION
+ * can be set even when first appearance in the capture file.)
+ */
+#define TCP_A_OLD_DATA 0x8000
+
/* Static TCP flags. Set in tcp_flow_t:static_flags */
#define TCP_S_BASE_SEQ_SET 0x01
#define TCP_S_SAW_SYN 0x03
@@ -1506,7 +1731,7 @@ mptcp_convert_dsn(guint64 dsn, mptcp_meta_flow_t *meta, enum mptcp_dsn_conversio
*result = dsn;
/* if relative is set then we need the 64 bits version anyway
- * we assume no wrapping was done on the 32 lsb so this may be wrong for elphant flows
+ * we assume no wrapping was done on the 32 lsb so this may be wrong for elephant flows
*/
if(conv == DSN_CONV_32_TO_64 || relative) {
@@ -1540,20 +1765,25 @@ process_tcp_payload(tvbuff_t *tvb, volatile int offset, packet_info *pinfo,
static struct tcp_analysis *
-init_tcp_conversation_data(packet_info *pinfo)
+init_tcp_conversation_data(packet_info *pinfo, int direction)
{
struct tcp_analysis *tcpd;
/* Initialize the tcp protocol data structure to add to the tcp conversation */
tcpd=wmem_new0(wmem_file_scope(), struct tcp_analysis);
- tcpd->flow1.win_scale=-1;
+ tcpd->flow1.win_scale = (direction >= 0) ? pinfo->src_win_scale : pinfo->dst_win_scale;
tcpd->flow1.window = G_MAXUINT32;
tcpd->flow1.multisegment_pdus=wmem_tree_new(wmem_file_scope());
tcpd->flow2.window = G_MAXUINT32;
- tcpd->flow2.win_scale=-1;
+ tcpd->flow2.win_scale = (direction >= 0) ? pinfo->dst_win_scale : pinfo->src_win_scale;
tcpd->flow2.multisegment_pdus=wmem_tree_new(wmem_file_scope());
+ if (tcp_reassemble_out_of_order) {
+ tcpd->flow1.ooo_segments=wmem_list_new(wmem_file_scope());
+ tcpd->flow2.ooo_segments=wmem_list_new(wmem_file_scope());
+ }
+
/* Only allocate the data if its actually going to be analyzed */
if (tcp_analyze_seq)
{
@@ -1632,13 +1862,18 @@ get_tcp_conversation_data(conversation_t *conv, packet_info *pinfo)
/* Get the data for this conversation */
tcpd=(struct tcp_analysis *)conversation_get_proto_data(conv, proto_tcp);
+ direction = cmp_address(&pinfo->src, &pinfo->dst);
+ /* if the addresses are equal, match the ports instead */
+ if (direction == 0) {
+ direction = (pinfo->srcport > pinfo->destport) ? 1 : -1;
+ }
/* If the conversation was just created or it matched a
* conversation with template options, tcpd will not
* have been initialized. So, initialize
* a new tcpd structure for the conversation.
*/
if (!tcpd) {
- tcpd = init_tcp_conversation_data(pinfo);
+ tcpd = init_tcp_conversation_data(pinfo, direction);
conversation_add_proto_data(conv, proto_tcp, tcpd);
}
@@ -1647,11 +1882,6 @@ get_tcp_conversation_data(conversation_t *conv, packet_info *pinfo)
}
/* check direction and get ua lists */
- direction=cmp_address(&pinfo->src, &pinfo->dst);
- /* if the addresses are equal, match the ports instead */
- if(direction==0) {
- direction= (pinfo->srcport > pinfo->destport) ? 1 : -1;
- }
if(direction>=0) {
tcpd->fwd=&(tcpd->flow1);
tcpd->rev=&(tcpd->flow2);
@@ -1677,7 +1907,7 @@ add_tcp_process_info(guint32 frame_num, address *local_addr, address *remote_add
if (!tcp_display_process_info)
return;
- conv = find_conversation(frame_num, local_addr, remote_addr, ENDPOINT_TCP, local_port, remote_port, 0);
+ conv = find_conversation(frame_num, local_addr, remote_addr, CONVERSATION_TCP, local_port, remote_port, 0);
if (!conv) {
return;
}
@@ -1968,6 +2198,7 @@ tcp_analyze_get_acked_struct(guint32 frame, guint32 seq, guint32 ack, gboolean c
}
+
/* fwd contains a list of all segments processed but not yet ACKed in the
* same direction as the current segment.
* rev contains a list of all segments received but not yet ACKed in the
@@ -1979,12 +2210,11 @@ tcp_analyze_get_acked_struct(guint32 frame, guint32 seq, guint32 ack, gboolean c
* Guide: docbook/wsug_src/WSUG_chapter_advanced.adoc
*/
static void
-tcp_analyze_sequence_number(packet_info *pinfo, guint32 seq, guint32 ack, guint32 seglen, guint16 flags, guint32 window, struct tcp_analysis *tcpd)
+tcp_analyze_sequence_number(packet_info *pinfo, guint32 seq, guint32 ack, guint32 seglen, guint16 flags, guint32 window, struct tcp_analysis *tcpd, struct tcp_per_packet_data_t *tcppd)
{
tcp_unacked_t *ual=NULL;
tcp_unacked_t *prevual=NULL;
guint32 nextseq;
- int ackcount;
#if 0
printf("\nanalyze_sequence numbers frame:%u\n",pinfo->num);
@@ -2000,43 +2230,6 @@ tcp_analyze_sequence_number(packet_info *pinfo, guint32 seq, guint32 ack, guint3
return;
}
- /* if this is the first segment for this list we need to store the
- * base_seq
- * We use TCP_S_SAW_SYN/SYNACK to distinguish between client and server
- *
- * Start relative seq and ack numbers at 1 if this
- * is not a SYN packet. This makes the relative
- * seq/ack numbers to be displayed correctly in the
- * event that the SYN or SYN/ACK packet is not seen
- * (this solves bug 1542)
- */
- if( !(tcpd->fwd->static_flags & TCP_S_BASE_SEQ_SET)) {
- if(flags & TH_SYN) {
- tcpd->fwd->base_seq = seq;
- tcpd->fwd->static_flags |= (flags & TH_ACK) ? TCP_S_SAW_SYNACK : TCP_S_SAW_SYN;
- }
- else {
- tcpd->fwd->base_seq = seq-1;
- }
- tcpd->fwd->static_flags |= TCP_S_BASE_SEQ_SET;
- }
-
- /* Only store reverse sequence if this isn't the SYN
- * There's no guarantee that the ACK field of a SYN
- * contains zeros; get the ISN from the first segment
- * with the ACK bit set instead (usually the SYN/ACK).
- *
- * If the SYN and SYN/ACK were received out-of-order,
- * the ISN is ack-1. If we missed the SYN/ACK, but got
- * the last ACK of the 3WHS, the ISN is ack-1. For all
- * other packets the ISN is unknown, so ack-1 is
- * as good a guess as ack.
- */
- if( !(tcpd->rev->static_flags & TCP_S_BASE_SEQ_SET) && (flags & TH_ACK) ) {
- tcpd->rev->base_seq = ack-1;
- tcpd->rev->static_flags |= TCP_S_BASE_SEQ_SET;
- }
-
if( flags & TH_ACK ) {
tcpd->rev->valid_bif = 1;
}
@@ -2168,13 +2361,23 @@ tcp_analyze_sequence_number(packet_info *pinfo, guint32 seq, guint32 ack, guint3
&& window==0
&& window==tcpd->fwd->window
&& seq==tcpd->fwd->tcp_analyze_seq_info->nextseq
- && ack==tcpd->fwd->tcp_analyze_seq_info->lastack
+ && (ack==tcpd->fwd->tcp_analyze_seq_info->lastack || EQ_SEQ(ack,tcpd->fwd->tcp_analyze_seq_info->lastack+1))
&& (tcpd->rev->lastsegmentflags&TCP_A_ZERO_WINDOW_PROBE)
&& (flags&(TH_SYN|TH_FIN|TH_RST))==0 ) {
if(!tcpd->ta) {
tcp_analyze_get_acked_struct(pinfo->num, seq, ack, TRUE, tcpd);
}
tcpd->ta->flags|=TCP_A_ZERO_WINDOW_PROBE_ACK;
+
+ /* Some receivers consume that extra byte brought in the PROBE,
+ * but it was too early to know that during the WINDOW PROBE analysis.
+ * Do it now by moving the rev nextseq & maxseqtobeacked.
+ * See issue 10745.
+ */
+ if(EQ_SEQ(ack,tcpd->fwd->tcp_analyze_seq_info->lastack+1)) {
+ tcpd->rev->tcp_analyze_seq_info->nextseq=ack;
+ tcpd->rev->tcp_analyze_seq_info->maxseqtobeacked=ack;
+ }
goto finished_fwd;
}
@@ -2195,6 +2398,7 @@ tcp_analyze_sequence_number(packet_info *pinfo, guint32 seq, guint32 ack, guint3
/* just ignore this DUPLICATE ACK */
} else {
tcpd->fwd->tcp_analyze_seq_info->dupacknum++;
+
if(!tcpd->ta) {
tcp_analyze_get_acked_struct(pinfo->num, seq, ack, TRUE, tcpd);
}
@@ -2217,7 +2421,10 @@ finished_fwd:
/* ACKED LOST PACKET
* If this segment acks beyond the 'max seq to be acked' in the other direction
* then that means we have missed packets going in the
- * other direction
+ * other direction.
+ * It might also indicate we are resuming from a Zero Window,
+ * where a Probe is just followed by an ACK opening again the window.
+ * See issue 8404.
*
* We only check this if we have actually seen some seq numbers
* in the other direction.
@@ -2228,11 +2435,48 @@ finished_fwd:
if(!tcpd->ta) {
tcp_analyze_get_acked_struct(pinfo->num, seq, ack, TRUE, tcpd);
}
- tcpd->ta->flags|=TCP_A_ACK_LOST_PACKET;
- /* update 'max seq to be acked' in the other direction so we don't get
- * this indication again.
+
+ /* resuming from a Zero Window Probe which re-opens the window,
+ * mark it as a Window Update
*/
- tcpd->rev->tcp_analyze_seq_info->maxseqtobeacked=tcpd->rev->tcp_analyze_seq_info->nextseq;
+ if(EQ_SEQ(ack,tcpd->fwd->tcp_analyze_seq_info->lastack+1)
+ && (seq==tcpd->fwd->tcp_analyze_seq_info->nextseq)
+ && (tcpd->rev->lastsegmentflags&TCP_A_ZERO_WINDOW_PROBE) ) {
+ tcpd->rev->tcp_analyze_seq_info->nextseq=ack;
+ tcpd->rev->tcp_analyze_seq_info->maxseqtobeacked=ack;
+ tcpd->ta->flags|=TCP_A_WINDOW_UPDATE;
+ }
+ /* real ACKED LOST PACKET */
+ else {
+ /* We ensure there is no matching packet waiting in the unacked list,
+ * and take this opportunity to push the tail further than this single packet
+ */
+ gboolean is_seq_in_unacked = FALSE;
+ guint32 maxseqtail = ack;
+ ual = tcpd->rev->tcp_analyze_seq_info->segments;
+ while(ual) {
+ /* prevent false positives */
+ if(GT_SEQ(ack,ual->seq) && LE_SEQ(ack,ual->nextseq)) {
+ is_seq_in_unacked = TRUE;
+ }
+ /* look for a possible tail pushing the maxseqtobeacked further */
+ if(maxseqtail==ual->seq) {
+ maxseqtail = ual->nextseq;
+ }
+ ual=ual->next;
+ }
+
+ /* update 'max seq to be acked' in the other direction so we don't get
+ * this indication again.
+ */
+ if(is_seq_in_unacked) {
+ tcpd->rev->tcp_analyze_seq_info->maxseqtobeacked=(GT_SEQ(maxseqtail, ack)) ? ack : maxseqtail;
+ }
+ else {
+ tcpd->rev->tcp_analyze_seq_info->maxseqtobeacked=maxseqtail;
+ tcpd->ta->flags|=TCP_A_ACK_LOST_PACKET;
+ }
+ }
}
@@ -2281,6 +2525,8 @@ finished_fwd:
goto finished_checking_retransmission_type;
}
+ nextseq = seq+seglen;
+
gboolean precedence_count = tcp_fastrt_precedence;
do {
switch(precedence_count) {
@@ -2304,6 +2550,31 @@ finished_fwd:
tcpd->ta->flags|=TCP_A_FAST_RETRANSMISSION;
goto finished_checking_retransmission_type;
}
+
+ /* Look for this segment in reported SACK ranges,
+ * if not present this might very well be a FAST Retrans,
+ * when the conditions above (timing, number of retrans) are still true */
+ if( seq_not_advanced
+ && t<20000000
+ && tcpd->rev->tcp_analyze_seq_info->dupacknum>=2
+ && tcpd->rev->tcp_analyze_seq_info->num_sack_ranges > 0) {
+
+ gboolean is_sacked = FALSE;
+ int i=0;
+ while( !is_sacked && i<tcpd->rev->tcp_analyze_seq_info->num_sack_ranges ) {
+ is_sacked = ((seq >= tcpd->rev->tcp_analyze_seq_info->sack_left_edge[i++])
+ && (nextseq <= tcpd->rev->tcp_analyze_seq_info->sack_right_edge[i]));
+ }
+
+ /* fine, it's probably a Fast Retrans triggered by the SACK sender algo */
+ if(!is_sacked) {
+ if(!tcpd->ta)
+ tcp_analyze_get_acked_struct(pinfo->num, seq, ack, TRUE, tcpd);
+ tcpd->ta->flags|=TCP_A_FAST_RETRANSMISSION;
+ goto finished_checking_retransmission_type;
+ }
+ }
+
precedence_count=!precedence_count;
break;
@@ -2320,14 +2591,45 @@ finished_fwd:
ooo_thres = tcpd->ts_first_rtt.nsecs + tcpd->ts_first_rtt.secs*1000000000;
}
- if( seq_not_advanced // XXX is this neccessary?
- && t < ooo_thres
- && tcpd->fwd->tcp_analyze_seq_info->nextseq != seq + seglen ) {
- if(!tcpd->ta) {
- tcp_analyze_get_acked_struct(pinfo->num, seq, ack, TRUE, tcpd);
+ /* If the segment is already seen and waiting to be acknowledged, ignore the
+ * Fast-Retrans/OOO debate and go ahead, as it only can be an ordinary Retrans.
+ * Fast-Retrans/Retrans are never ambiguous in the context of packets seen but
+ * this code could be moved above.
+ * See Issues 13284, 13843
+ * XXX: if compared packets have different sizes, it's not handled yet
+ */
+ gboolean pk_already_seen = FALSE;
+ ual = tcpd->fwd->tcp_analyze_seq_info->segments;
+ while(ual) {
+ if(GE_SEQ(seq,ual->seq) && LE_SEQ(seq+seglen,ual->nextseq)) {
+ pk_already_seen = TRUE;
+ break;
+ }
+ ual=ual->next;
+ }
+
+ if(seq_not_advanced && t < ooo_thres && !pk_already_seen) {
+ /* ordinary OOO with SEQ numbers and lengths clearly stating the situation */
+ if( tcpd->fwd->tcp_analyze_seq_info->nextseq != (seq + seglen + (flags&(TH_SYN|TH_FIN) ? 1 : 0))) {
+ if(!tcpd->ta) {
+ tcp_analyze_get_acked_struct(pinfo->num, seq, ack, TRUE, tcpd);
+ }
+
+ tcpd->ta->flags|=TCP_A_OUT_OF_ORDER;
+ goto finished_checking_retransmission_type;
+ }
+ else {
+ /* facing an OOO closing a series of disordered packets,
+ all preceded by a pure ACK. See issue 17214 */
+ if(tcpd->fwd->tcp_analyze_seq_info->lastacklen == 0) {
+ if(!tcpd->ta) {
+ tcp_analyze_get_acked_struct(pinfo->num, seq, ack, TRUE, tcpd);
+ }
+
+ tcpd->ta->flags|=TCP_A_OUT_OF_ORDER;
+ goto finished_checking_retransmission_type;
+ }
}
- tcpd->ta->flags|=TCP_A_OUT_OF_ORDER;
- goto finished_checking_retransmission_type;
}
precedence_count=!precedence_count;
break;
@@ -2352,11 +2654,17 @@ finished_fwd:
* better case scenario: if we have a list of the previous unacked packets,
* go back to the eldest one, which in theory is likely to be the one retransmitted here.
* It's not always the perfect match, particularly when original captured packet used LSO
+ * We may parse this list and try to find an obvious matching packet present in the
+ * capture. If such packet is actually missing, we'll reach the list first entry.
+ * See : issue #12259
+ * See : issue #17714
*/
ual = tcpd->fwd->tcp_analyze_seq_info->segments;
while(ual) {
- nstime_delta(&tcpd->ta->rto_ts, &pinfo->abs_ts, &ual->ts );
- tcpd->ta->rto_frame=ual->frame;
+ if(GE_SEQ(ual->seq, seq)) {
+ nstime_delta(&tcpd->ta->rto_ts, &pinfo->abs_ts, &ual->ts );
+ tcpd->ta->rto_frame=ual->frame;
+ }
ual=ual->next;
}
}
@@ -2364,6 +2672,53 @@ finished_fwd:
finished_checking_retransmission_type:
+ /* Override the TCP sequence analysis with the value given
+ * manually by the user. This only applies to flagged packets.
+ */
+ if(tcppd && tcpd->ta &&
+ (tcppd->tcp_snd_manual_analysis>0) &&
+ (tcpd->ta->flags & TCP_A_RETRANSMISSION ||
+ tcpd->ta->flags & TCP_A_OUT_OF_ORDER ||
+ tcpd->ta->flags & TCP_A_FAST_RETRANSMISSION ||
+ tcpd->ta->flags & TCP_A_SPURIOUS_RETRANSMISSION)) {
+
+ /* clean flags set during the automatic analysis */
+ tcpd->ta->flags &= ~(TCP_A_RETRANSMISSION|
+ TCP_A_OUT_OF_ORDER|
+ TCP_A_FAST_RETRANSMISSION|
+ TCP_A_SPURIOUS_RETRANSMISSION);
+
+ /* set the corresponding flag chosen by the user */
+ switch(tcppd->tcp_snd_manual_analysis) {
+ case 0:
+ /* the user asked for an empty overriding, which
+ * means removing any previous value, thus restoring
+ * the automatic analysis.
+ */
+ break;
+
+ case 1:
+ tcpd->ta->flags|=TCP_A_OUT_OF_ORDER;
+ break;
+
+ case 2:
+ tcpd->ta->flags|=TCP_A_RETRANSMISSION;
+ break;
+
+ case 3:
+ tcpd->ta->flags|=TCP_A_FAST_RETRANSMISSION;
+ break;
+
+ case 4:
+ tcpd->ta->flags|=TCP_A_SPURIOUS_RETRANSMISSION;
+ break;
+
+ default:
+ /* there is no expected default case */
+ break;
+ }
+ }
+
nextseq = seq+seglen;
if ((seglen || flags&(TH_SYN|TH_FIN)) && tcpd->fwd->tcp_analyze_seq_info->segment_count < TCP_MAX_UNACKED_SEGMENTS) {
/* Add this new sequence number to the fwd list. But only if there
@@ -2384,6 +2739,14 @@ finished_checking_retransmission_type:
ual->nextseq=nextseq;
}
+ /* Every time we are moving the highest number seen,
+ * we are also tracking the segment length then we will know for sure,
+ * later, if this was a pure ACK or an ordinary data packet. */
+ if(!tcpd->fwd->tcp_analyze_seq_info->nextseq
+ || GT_SEQ(nextseq, tcpd->fwd->tcp_analyze_seq_info->nextseq + (flags&(TH_SYN|TH_FIN) ? 1 : 0))) {
+ tcpd->fwd->tcp_analyze_seq_info->lastacklen=seglen;
+ }
+
/* Store the highest number seen so far for nextseq so we can detect
* when we receive segments that arrive with a "hole"
* If we don't have anything since before, just store what we got.
@@ -2399,7 +2762,13 @@ finished_checking_retransmission_type:
}
/* Store the highest continuous seq number seen so far for 'max seq to be acked',
- so we can detect TCP_A_ACK_LOST_PACKET condition
+ * so we can detect TCP_A_ACK_LOST_PACKET condition.
+ * If this ever happens, this boundary value can "jump" further in order to
+ * avoid duplicating multiple messages for the very same lost packet. See later
+ * how ACKED LOST PACKET are handled.
+ * Zero Window Probes are logically left out at this moment, but if their data
+ * really were to be ack'ed, then it will be done later when analyzing their
+ * Probe ACK (be it a real Probe ACK, or an ordinary ACK moving the RCV Window).
*/
if(EQ_SEQ(seq, tcpd->fwd->tcp_analyze_seq_info->maxseqtobeacked) || !tcpd->fwd->tcp_analyze_seq_info->maxseqtobeacked) {
if( !tcpd->ta || !(tcpd->ta->flags&TCP_A_ZERO_WINDOW_PROBE) ) {
@@ -2431,7 +2800,6 @@ finished_checking_retransmission_type:
/* remove all segments this ACKs and we don't need to keep around any more
*/
- ackcount=0;
prevual = NULL;
ual = tcpd->rev->tcp_analyze_seq_info->segments;
while(ual) {
@@ -2456,7 +2824,6 @@ finished_checking_retransmission_type:
}
/* This segment is old, or an exact match. Delete the segment from the list */
- ackcount++;
tmpual=ual->next;
if (tcpd->rev->scps_capable) {
@@ -3071,6 +3438,7 @@ mptcp_add_analysis_subtree(packet_info *pinfo, tvbuff_t *tvb, proto_tree *parent
proto_item_set_generated(item);
+#if 0 // nbOptionsChanged is currently unused.
/* store the TCP Options related to MPTCP then we will avoid false DUP ACKs later */
guint8 nbOptionsChanged = 0;
if((tcpd->mptcp_analysis->mp_operations&(0x01))!=tcph->th_mptcp->mh_mpc) {
@@ -3106,6 +3474,7 @@ mptcp_add_analysis_subtree(packet_info *pinfo, tvbuff_t *tvb, proto_tree *parent
nbOptionsChanged++;
}
/* we could track MPTCP option changes here, with nbOptionsChanged */
+#endif
item = proto_tree_add_uint(tree, hf_mptcp_stream, tvb, 0, 0, mptcpd->stream);
proto_item_set_generated(item);
@@ -3257,41 +3626,422 @@ print_tcp_fragment_tree(fragment_head *ipfd_head, proto_tree *tree, proto_tree *
#define TCPH_MIN_LEN 20
/* Desegmentation of TCP streams */
+
+/* The primary ID is the first frame of a multisegment PDU, which is
+ * most likely unique in the capture (unlike sequence numbers which
+ * can be re-used, especially when relative sequence numbers are enabled).
+ * However, frames can have multiple PDUs with certain encapsulations like
+ * GSE or MPE over DVB BaseBand Frames.
+ */
+
+typedef struct _tcp_endpoint {
+
+ address src_addr;
+ address dst_addr;
+ port_type ptype;
+ guint32 src_port;
+ guint32 dst_port;
+} tcp_endpoint_t;
+
+static void
+save_endpoint(packet_info *pinfo, tcp_endpoint_t *a)
+{
+ copy_address_shallow(&a->src_addr, &pinfo->src);
+ copy_address_shallow(&a->dst_addr, &pinfo->dst);
+ a->ptype = pinfo->ptype;
+ a->src_port = pinfo->srcport;
+ a->dst_port = pinfo->destport;
+}
+
+static void
+restore_endpoint(packet_info *pinfo, tcp_endpoint_t *a)
+{
+ copy_address_shallow(&pinfo->src, &a->src_addr);
+ copy_address_shallow(&pinfo->dst, &a->dst_addr);
+ pinfo->ptype = a->ptype;
+ pinfo->srcport = a->src_port;
+ pinfo->destport = a->dst_port;
+}
+
+typedef struct _tcp_segment_key {
+ address src_addr;
+ address dst_addr;
+ guint32 src_port;
+ guint32 dst_port;
+ guint32 id; /* msp->first_frame */
+ guint32 seq; /* msp->seq */
+} tcp_segment_key;
+
+static guint
+tcp_segment_hash(gconstpointer k)
+{
+ const tcp_segment_key* key = (const tcp_segment_key*) k;
+ guint hash_val;
+
+ hash_val = key->id;
+
+/* In most captures there is only one fragment per id / first_frame,
+ so we only use it in the hash as an optimization.
+
+ int i;
+ for (i = 0; i < key->src.len; i++)
+ hash_val += key->src_addr.data[i];
+ for (i = 0; i < key->dst.len; i++)
+ hash_val += key->dst_addr.data[i];
+ hash_val += key->src_port;
+ hash_val += key->dst_port;
+ hash_val += key->seq;
+*/
+
+ return hash_val;
+}
+
+static gint
+tcp_segment_equal(gconstpointer k1, gconstpointer k2)
+{
+ const tcp_segment_key* key1 = (const tcp_segment_key*) k1;
+ const tcp_segment_key* key2 = (const tcp_segment_key*) k2;
+
+ /*
+ * key.id is the first item to compare since it's the item most
+ * likely to differ between sessions, thus short-circuiting
+ * the comparison of addresses and ports.
+ */
+ return (key1->id == key2->id) &&
+ (addresses_equal(&key1->src_addr, &key2->src_addr)) &&
+ (addresses_equal(&key1->dst_addr, &key2->dst_addr)) &&
+ (key1->src_port == key2->src_port) &&
+ (key1->dst_port == key2->dst_port) &&
+ (key1->seq == key2->seq);
+}
+
+/*
+ * Create a fragment key for temporary use; it can point to non-
+ * persistent data, and so must only be used to look up and
+ * delete entries, not to add them.
+ */
+static gpointer
+tcp_segment_temporary_key(const packet_info *pinfo, const guint32 id,
+ const void *data)
+{
+ struct tcp_multisegment_pdu *msp = (struct tcp_multisegment_pdu*)data;
+ DISSECTOR_ASSERT(msp);
+ tcp_segment_key *key = g_slice_new(tcp_segment_key);
+
+ /*
+ * Do a shallow copy of the addresses.
+ */
+ copy_address_shallow(&key->src_addr, &pinfo->src);
+ copy_address_shallow(&key->dst_addr, &pinfo->dst);
+ key->src_port = pinfo->srcport;
+ key->dst_port = pinfo->destport;
+ key->id = id;
+ key->seq = msp->seq;
+
+ return (gpointer)key;
+}
+
+/*
+ * Create a fragment key for permanent use; it must point to persistent
+ * data, so that it can be used to add entries.
+ */
+static gpointer
+tcp_segment_persistent_key(const packet_info *pinfo,
+ const guint32 id, const void *data)
+{
+ struct tcp_multisegment_pdu *msp = (struct tcp_multisegment_pdu*)data;
+ DISSECTOR_ASSERT(msp);
+ tcp_segment_key *key = g_slice_new(tcp_segment_key);
+
+ /*
+ * Do a deep copy of the addresses.
+ */
+ copy_address(&key->src_addr, &pinfo->src);
+ copy_address(&key->dst_addr, &pinfo->dst);
+ key->src_port = pinfo->srcport;
+ key->dst_port = pinfo->destport;
+ key->id = id;
+ key->seq = msp->seq;
+
+ return (gpointer)key;
+}
+
+static void
+tcp_segment_free_temporary_key(gpointer ptr)
+{
+ tcp_segment_key *key = (tcp_segment_key *)ptr;
+ g_slice_free(tcp_segment_key, key);
+}
+
+static void
+tcp_segment_free_persistent_key(gpointer ptr)
+{
+ tcp_segment_key *key = (tcp_segment_key *)ptr;
+
+ if(key){
+ /*
+ * Free up the copies of the addresses from the old key.
+ */
+ free_address(&key->src_addr);
+ free_address(&key->dst_addr);
+
+ g_slice_free(tcp_segment_key, key);
+ }
+}
+
+const reassembly_table_functions
+tcp_reassembly_table_functions = {
+ tcp_segment_hash,
+ tcp_segment_equal,
+ tcp_segment_temporary_key,
+ tcp_segment_persistent_key,
+ tcp_segment_free_temporary_key,
+ tcp_segment_free_persistent_key
+};
+
static reassembly_table tcp_reassembly_table;
/* functions to trace tcp segments */
/* Enable desegmenting of TCP streams */
static gboolean tcp_desegment = TRUE;
-/* Enable buffering of out-of-order TCP segments before passing it to a
- * subdissector (depends on "tcp_desegment"). */
-static gboolean tcp_reassemble_out_of_order = FALSE;
-
-/* Returns true iff any gap exists in the segments associated with msp up to the
- * given sequence number (it ignores any gaps after the sequence number). */
-static gboolean
-missing_segments(packet_info *pinfo, struct tcp_multisegment_pdu *msp, guint32 seq)
+/* Returns the maximum contiguous sequence number of the reassembly associated
+ * with the msp *if* a new fragment were added ending in the given maxnextseq.
+ * The new fragment is from the current frame and may not have been added yet.
+ */
+static guint32
+find_maxnextseq(packet_info *pinfo, struct tcp_multisegment_pdu *msp, guint32 maxnextseq)
{
fragment_head *fd_head;
- guint32 frag_offset = seq - msp->seq;
- if ((gint32)frag_offset <= 0) {
- return FALSE;
- }
+ DISSECTOR_ASSERT(msp);
- fd_head = fragment_get(&tcp_reassembly_table, pinfo, msp->first_frame, NULL);
+ fd_head = fragment_get(&tcp_reassembly_table, pinfo, msp->first_frame, msp);
/* msp implies existence of fragments, this should never be NULL. */
DISSECTOR_ASSERT(fd_head);
- /* Find length of contiguous fragments. */
- guint32 max = 0;
- for (fragment_item *frag = fd_head; frag; frag = frag->next) {
- guint32 frag_end = frag->offset + frag->len;
- if (frag->offset <= max && max < frag_end) {
- max = frag_end;
+ /* Find length of contiguous fragments.
+ * Start with the first gap, but the new fragment is allowed to
+ * fill that gap. */
+ guint32 max_len = maxnextseq - msp->seq;
+ fragment_item* frag = (fd_head->first_gap) ? fd_head->first_gap : fd_head->next;
+ for (; frag && frag->offset <= max_len; frag = frag->next) {
+ max_len = MAX(max_len, frag->offset + frag->len);
+ }
+
+ return max_len + msp->seq;
+}
+
+static struct tcp_multisegment_pdu*
+split_msp(packet_info *pinfo, struct tcp_multisegment_pdu *msp, struct tcp_analysis *tcpd)
+{
+ fragment_head *fd_head;
+ guint32 first_frame = 0;
+ guint32 last_frame = 0;
+ const guint32 split_offset = pinfo->desegment_offset;
+
+ fd_head = fragment_get(&tcp_reassembly_table, pinfo, msp->first_frame, msp);
+ /* This is for splitting defragmented MSPs, so fd_head should exist
+ * and be defragmented. This also ensures that fd_i->tvb_data exists.
+ */
+ DISSECTOR_ASSERT(fd_head && fd_head->flags & FD_DEFRAGMENTED);
+
+ fragment_item *fd_i, *first_frag = NULL;
+
+ /* The fragment list is sorted in offset order, but not nec. frame order
+ * or end offset order due to out of order reassembly and possible overlap.
+ * fd_i->offset < split_offset - some bytes are before the split
+ * fd_i->offset + fd_i->len >= split_offset - some bytes are after split
+ * Look through all the fragments that have some data before the split point.
+ */
+ for (fd_i = fd_head->next; fd_i && (fd_i->offset < split_offset); fd_i = fd_i->next) {
+ if (last_frame < fd_i->frame) {
+ last_frame = fd_i->frame;
+ }
+ if (fd_i->offset + fd_i->len >= split_offset) {
+ if (first_frag == NULL) {
+ first_frag = fd_i;
+ first_frame = fd_i->frame;
+ } else if (fd_i->frame < first_frame) {
+ first_frame = fd_i->frame;
+ }
+ }
+ };
+
+ /* Now look through all the remaining fragments that only have bytes after
+ * the split.
+ */
+ for (; fd_i; fd_i = fd_i->next) {
+ guint32 frag_end = fd_i->offset + fd_i->len;
+ if (split_offset <= frag_end && fd_i->frame < first_frame) {
+ first_frame = fd_i->frame;
+ }
+ }
+
+ /* We only call this when the frame the fragments were reassembled in
+ * (which is the current frame) includes some data before the split
+ * point, so that it won't change and we can be consistent dissecting
+ * between passes. We also should have at least some data after the
+ * split point (because the subdissector claimed there was undissected
+ * data.)
+ */
+ DISSECTOR_ASSERT(fd_head->reassembled_in == last_frame);
+ DISSECTOR_ASSERT(first_frag != NULL);
+
+ guint32 new_seq = msp->seq + pinfo->desegment_offset;
+ struct tcp_multisegment_pdu *newmsp;
+ newmsp = pdu_store_sequencenumber_of_next_pdu(pinfo, new_seq,
+ new_seq+1, tcpd->fwd->multisegment_pdus);
+ newmsp->first_frame = first_frame;
+ newmsp->nxtpdu = msp->nxtpdu;
+
+ /* XXX: Could do the adding the new fragments in fragment_truncate */
+ for (fd_i = first_frag; fd_i; fd_i = fd_i->next) {
+ guint32 frag_offset = fd_i->offset;
+ guint32 frag_len = fd_i->len;
+ /* Check for some unusual out of order overlapping segment situations. */
+ if (split_offset < frag_offset + frag_len) {
+ if (fd_i->offset < split_offset) {
+ frag_offset = split_offset;
+ frag_len -= (split_offset - fd_i->offset);
+ }
+ fragment_add_out_of_order(&tcp_reassembly_table, fd_head->tvb_data,
+ frag_offset, pinfo, first_frame, newmsp,
+ frag_offset - split_offset, frag_len, TRUE, fd_i->frame);
}
}
- return max < frag_offset;
+
+ fragment_truncate(&tcp_reassembly_table, pinfo, msp->first_frame, msp, split_offset);
+ msp->nxtpdu = msp->seq + split_offset;
+
+ /* The newmsp nxtpdu will be adjusted after leaving this function. */
+ return newmsp;
+}
+
+typedef struct _ooo_segment_item {
+ guint32 frame;
+ guint32 seq;
+ guint32 len;
+ guint8 *data;
+} ooo_segment_item;
+
+static gint
+compare_ooo_segment_item(gconstpointer a, gconstpointer b)
+{
+ const ooo_segment_item *fd_a = a;
+ const ooo_segment_item *fd_b = b;
+
+ /* We only insert segments into this list that satisfy
+ * LT_SEQ(tcpd->fwd->maxnextseq, seq), for the current value
+ * of maxnextseq (removing segments when maxnextseq is advanced)
+ * so these rollover-aware comparisons are transitive over the
+ * domain (never greater than 2^31).
+ */
+ if (LT_SEQ(fd_a->seq, fd_b->seq))
+ return -1;
+
+ if (GT_SEQ(fd_a->seq, fd_b->seq))
+ return 1;
+
+ if (fd_a->frame < fd_b->frame)
+ return -1;
+
+ if (fd_a->frame > fd_b->frame)
+ return 1;
+
+ return 0;
+}
+
+/* Search through our list of out of order segments and add the ones that are
+ * now contiguous onto a MSP until we use them all or reach another gap.
+ *
+ * If the MSP parameter is a incomplete, returns it with any OOO segments added.
+ * If the MSP parameter is NULL or complete, returns a newly created MSP with
+ * OOO segments added, or NULL if there were no segments to add.
+ */
+static struct tcp_multisegment_pdu *
+msp_add_out_of_order(packet_info *pinfo, struct tcp_multisegment_pdu *msp, struct tcp_analysis *tcpd, guint32 seq)
+{
+
+ /* Whether a previous MSP exists with missing segments. */
+ gboolean has_unfinished_msp = msp && !(msp->flags & MSP_FLAGS_GOT_ALL_SEGMENTS);
+ bool updated_maxnextseq = FALSE;
+
+ if (msp) {
+ guint32 maxnextseq = find_maxnextseq(pinfo, msp, tcpd->fwd->maxnextseq);
+ if (LE_SEQ(tcpd->fwd->maxnextseq, maxnextseq)) {
+ tcpd->fwd->maxnextseq = maxnextseq;
+ }
+ updated_maxnextseq = TRUE;
+ }
+ wmem_list_frame_t *curr_entry;
+ curr_entry = wmem_list_head(tcpd->fwd->ooo_segments);
+ ooo_segment_item *fd;
+ tvbuff_t *tvb_data;
+ while (curr_entry) {
+ fd = (ooo_segment_item *)wmem_list_frame_data(curr_entry);
+ if (LT_SEQ(tcpd->fwd->maxnextseq, fd->seq)) {
+ /* There might be segments already added to the msp that now extend
+ * the maximum contiguous sequence number. Check for them. */
+ if (msp && !updated_maxnextseq) {
+ tcpd->fwd->maxnextseq = find_maxnextseq(pinfo, msp, tcpd->fwd->maxnextseq);
+ updated_maxnextseq = TRUE;
+ }
+ if (LT_SEQ(tcpd->fwd->maxnextseq, fd->seq)) {
+ break;
+ }
+ }
+ /* We have filled in the gap, so this out of order
+ * segment is now contiguous and can be processed along
+ * with the segment we just received.
+ */
+ tcpd->fwd->maxnextseq = fd->seq + fd->len;
+ tvb_data = tvb_new_real_data(fd->data, fd->len, fd->len);
+ if (has_unfinished_msp) {
+
+ /* Increase the expected MSP size if necessary. Yes, the
+ * subdissector may have told us that a PDU ended here, but we
+ * might have enough newly contiguous data to dissect another
+ * PDU past that, and we should send that to the subdissector
+ * too. */
+ if (LT_SEQ(msp->nxtpdu, fd->seq + fd->len)) {
+ msp->nxtpdu = fd->seq + fd->len;
+ }
+ /* Add this OOO segment to the unfinished MSP */
+ fragment_add_out_of_order(&tcp_reassembly_table,
+ tvb_data, 0,
+ pinfo, msp->first_frame, msp,
+ fd->seq - msp->seq, fd->len,
+ msp->nxtpdu, fd->frame);
+ } else {
+ /* No MSP in progress, so create one starting
+ * at the sequence number of segment received
+ * in this frame. Note that we will be adding
+ * the first segment below, and this is the frame
+ * of the first segment, so first_frame_with_seq
+ * is already correct (and unnecessary) and
+ * we don't need MSP_FLAGS_MISSING_FIRST_SEGMENT. */
+ msp = pdu_store_sequencenumber_of_next_pdu(pinfo,
+ seq, fd->seq + fd->len,
+ tcpd->fwd->multisegment_pdus);
+ fragment_add_out_of_order(&tcp_reassembly_table,
+ tvb_data, 0, pinfo, msp->first_frame,
+ msp, fd->seq - msp->seq, fd->len,
+ msp->nxtpdu, fd->frame);
+ has_unfinished_msp = TRUE;
+ }
+ updated_maxnextseq = FALSE;
+ tvb_free(tvb_data);
+ wmem_list_remove_frame(tcpd->fwd->ooo_segments, curr_entry);
+ curr_entry = wmem_list_head(tcpd->fwd->ooo_segments);
+
+ }
+ /* There might be segments already added to the msp that now extend
+ * the maximum contiguous sequence number. Check for them. */
+ if (msp && !updated_maxnextseq) {
+ tcpd->fwd->maxnextseq = find_maxnextseq(pinfo, msp, tcpd->fwd->maxnextseq);
+ }
+ return msp;
}
static void
@@ -3305,6 +4055,7 @@ desegment_tcp(tvbuff_t *tvb, packet_info *pinfo, int offset,
int last_fragment_len;
gboolean must_desegment;
gboolean called_dissector;
+ gboolean has_gap;
int another_pdu_follows;
int deseg_offset;
guint32 deseg_seq;
@@ -3312,13 +4063,20 @@ desegment_tcp(tvbuff_t *tvb, packet_info *pinfo, int offset,
proto_item *item;
struct tcp_multisegment_pdu *msp;
gboolean cleared_writable = col_get_writable(pinfo->cinfo, COL_PROTOCOL);
- const gboolean reassemble_ooo = tcp_analyze_seq && tcp_desegment && tcp_reassemble_out_of_order;
+ gboolean first_pdu = TRUE;
+ const gboolean reassemble_ooo = tcp_analyze_seq && tcp_desegment && tcp_reassemble_out_of_order && tcpd && tcpd->fwd->ooo_segments;
+
+ tcp_endpoint_t orig_endpoint, new_endpoint;
+
+ save_endpoint(pinfo, &orig_endpoint);
+ save_endpoint(pinfo, &new_endpoint);
again:
ipfd_head = NULL;
last_fragment_len = 0;
must_desegment = FALSE;
called_dissector = FALSE;
+ has_gap = FALSE;
another_pdu_follows = 0;
msp = NULL;
@@ -3338,178 +4096,320 @@ again:
*/
deseg_offset = offset;
+ /*
+ * TODO: Some notes on current limitations with TCP desegmentation:
+ *
+ * This function can be called with either relative or absolute sequence
+ * numbers; the ??_SEQ macros are called for comparisons to deal with
+ * with sequence number rollover. (With relative sequence numbers, if
+ * early TCP segments are received out of order before the SYN it can be
+ * possible for rollover to occur at the very beginning of a connection.)
+ *
+ * However, multi-segment PDU lookup does not work for MSPs that span
+ * TCP sequence number rollover, and desegmentation fails.
+ *
+ * When there is a single TCP connection that is longer than 4 GiB and
+ * thus sequence numbers are reused, multi-segment PDU lookup and
+ * retransmission identification does not work. (Bug 10503).
+ *
+ * Distinguishing between sequence number reuse on a very long connection
+ * and sequence number reuse due to retransmission is difficult. Right
+ * now very long connections are just not handled as the rarer case.
+ * Perhaps retransmission identification could be entirely left up to TCP
+ * analysis (if enabled, not done at all if disabled), instead of TCP
+ * analysis results only used to supplement work here?
+ *
+ * TCP sequence analysis can set TCP_A_RETRANSMISSION in cases where
+ * we still need to process the segment anyway because something other
+ * than the sequence number is different from the prior segment. That
+ * includes "retransmitted but with additional data" (Bug 13523) and
+ * "retransmitted due to bad checksum" (especially if checksum verification
+ * is enabled.)
+ *
+ * "Reassemble out-of-order segments" uses its own method of detecting
+ * retranmission, but uses more memory and CPU, and when used, a TCP stream
+ * that has missing segments that are never retransmitted stop processing
+ * after the missing segment.
+ *
+ * If multiple TCP/IP packets are encapsulated in the same frame (such
+ * as with GSE, which has very long Baseband Frames) this causes issues:
+ *
+ * If a subdissector reports that it can handle a payload, but needs
+ * more data (pinfo->desegment_len > 0) and did not actually dissect
+ * any of it (pinfo->desegment_offset == 0), on the first pass it
+ * still adds layers to the frame. On subsequent passes, the MSP created
+ * (or extended) in the first pass means that the subdissector won't be
+ * called at all. If there are other protocols contained in the frame
+ * that are dissected on the second pass they will have different
+ * layer numbers than in the first pass, which can disturb proto_data
+ * lookup, reassembly, etc. (Bug 16109 describes this for TLS.)
+ */
+
if (tcpd) {
- /* Have we seen this PDU before (and is it the start of a multi-
- * segment PDU)?
- *
- * If the sequence number was seen before, it is part of a
- * retransmission if the whole segment fits within the MSP.
- * (But if this is this frame was already visited and the first frame of
- * the MSP matches the current frame, then it is not a retransmission,
- * but the start of a new MSP.)
- *
- * If only part of the segment fits in the MSP, then either:
- * - The previous segment included with the MSP was a Zero Window Probe
- * with one byte of data and the subdissector just asked for one more
- * byte. Do not mark it as retransmission (Bug 15427).
- * - Data was actually being retransmitted, but with additional data
- * (Bug 13523). Do not mark it as retransmission to handle the extra
- * bytes. (NOTE Due to the TCP_A_RETRANSMISSION check below, such
- * extra data will still be ignored.)
- * - The MSP contains multiple segments, but the subdissector finished
- * reassembly using a subset of the final segment (thus "msp->nxtpdu"
- * is smaller than the nxtseq of the previous segment). If that final
- * segment was retransmitted, then "nxtseq > msp->nxtpdu".
- * Unfortunately that will *not* be marked as retransmission here.
- * The next TCP_A_RETRANSMISSION hopefully takes care of it though.
- *
- * Only shortcircuit here when the first segment of the MSP is known,
- * and when this this first segment is not one to complete the MSP.
- */
- if ((msp = (struct tcp_multisegment_pdu *)wmem_tree_lookup32(tcpd->fwd->multisegment_pdus, seq)) &&
- nxtseq <= msp->nxtpdu &&
- !(msp->flags & MSP_FLAGS_MISSING_FIRST_SEGMENT) && msp->last_frame != pinfo->num) {
- const char* str;
- gboolean is_retransmission = FALSE;
-
- /* Yes. This could be because we've dissected this frame before
- * or because this is a retransmission of a previously-seen
- * segment. Either way, we don't need to hand it off to the
- * subdissector and we certainly don't want to re-add it to the
- * multisegment_pdus list: if we did, subsequent lookups would
- * find this retransmission instead of the original transmission
- * (breaking desegmentation if we'd already linked other segments
- * to the original transmission's entry).
+
+ if (reassemble_ooo) {
+ /* If we are reassembling out of order, we can do this retransmission
+ * check. Anything before the latest consecutive sequence number we've
+ * already processed is a retransmission (from the perspective of has
+ * been passed to subdissectors; the judgment of TCP Sequence Analysis
+ * may be different, because it considers RTO and ACKs and so forth).
+ *
+ * XXX: If these segments are part of incomplete MSPs, we pass them
+ * to the reassembly code which tests for overlap conflicts.
+ * For those which are part of completed reassemblies or not part
+ * of MSPs, we just don't process them. The former would throw a
+ * ReassemblyError, which is likely acceptable in the case of
+ * retransmission of the same segment but not if retransmitted with
+ * additional data, where we'd need to catch the exception to
+ * process the extra data. For ones that were not added to MSPs at
+ * all, we can't do much. (Bug #13061)
*
- * Cases to handle here:
- * - In-order stream, pinfo->num matches begin of MSP.
- * - In-order stream, but pinfo->num does not match the begin of the
- * MSP. Must be a retransmission.
- * - OoO stream where this segment fills the gap in the begin of the
- * MSP. msp->first_frame is the start where the gap was detected
- * (and does NOT match pinfo->num).
+ * Retransmissions of out of order segments after our latest
+ * consecutive sequence number will all be stored and then eventually
+ * put on multisegment PDUs and go to the reassembler, which should
+ * be able to handle retransmission, as those are still incomplete.
*/
- if (msp->first_frame == pinfo->num || msp->first_frame_with_seq == pinfo->num) {
- str = "";
- col_append_sep_str(pinfo->cinfo, COL_INFO, " ", "[TCP segment of a reassembled PDU]");
- } else {
- str = "Retransmitted ";
- is_retransmission = TRUE;
- /* TCP analysis already flags this (in COL_INFO) as a retransmission--if it's enabled */
+ msp = (struct tcp_multisegment_pdu *)wmem_tree_lookup32_le(tcpd->fwd->multisegment_pdus, seq);
+
+ gboolean has_unfinished_msp = FALSE;
+ if (msp && LE_SEQ(msp->seq, seq) && GT_SEQ(msp->nxtpdu, seq) && !(msp->flags & MSP_FLAGS_GOT_ALL_SEGMENTS)) {
+ has_unfinished_msp = TRUE;
}
- /* Fix for bug 3264: look up ipfd for this (first) segment,
- so can add tcp.reassembled_in generated field on this code path. */
- if (!is_retransmission) {
- ipfd_head = fragment_get(&tcp_reassembly_table, pinfo, msp->first_frame, NULL);
- if (ipfd_head) {
- if (ipfd_head->reassembled_in != 0) {
- item = proto_tree_add_uint(tcp_tree, hf_tcp_reassembled_in, tvb, 0,
- 0, ipfd_head->reassembled_in);
- proto_item_set_generated(item);
+ if (!PINFO_FD_VISITED(pinfo) && first_pdu) {
+ if (tcpd->fwd->maxnextseq && LT_SEQ(seq, tcpd->fwd->maxnextseq) && !has_unfinished_msp) {
+ if(!tcpd->ta) {
+ tcp_analyze_get_acked_struct(pinfo->num, seq, tcpinfo->lastackseq, TRUE, tcpd);
+ }
+ tcpd->ta->flags |= TCP_A_OLD_DATA;
+ if (GT_SEQ(nxtseq, tcpd->fwd->maxnextseq)) {
+ tcpd->ta->new_data_seq = tcpd->fwd->maxnextseq;
+ } else {
+ tcpd->ta->new_data_seq = nxtseq;
}
}
}
- nbytes = tvb_reported_length_remaining(tvb, offset);
-
- proto_tree_add_bytes_format(tcp_tree, hf_tcp_segment_data, tvb, offset,
- nbytes, NULL, "%sTCP segment data (%u byte%s)", str, nbytes,
- plurality(nbytes, "", "s"));
- return;
- }
+ if(tcpd->ta && first_pdu) {
+ if((tcpd->ta->flags&TCP_A_OLD_DATA) == TCP_A_OLD_DATA) {
+ nbytes = tcpd->ta->new_data_seq - seq;
+
+ proto_tree_add_bytes_format(tcp_tree, hf_tcp_segment_data, tvb,
+ offset, nbytes, NULL,
+ "Retransmitted TCP segment data (%u byte%s)",
+ nbytes, plurality(nbytes, "", "s"));
+
+ offset += nbytes;
+ seq = tcpd->ta->new_data_seq;
+ first_pdu = FALSE;
+ if (tvb_captured_length_remaining(tvb, offset) > 0)
+ goto again;
+ goto clean_exit;
+ }
+ }
+ } else {
- /* The above code only finds retransmission if the PDU boundaries and the seq coincide I think
- * If we have sequence analysis active use the TCP_A_RETRANSMISSION flag.
- * XXXX Could the above code be improved?
- * XXX the following check works great for filtering duplicate
- * retransmissions, but could there be a case where it prevents
- * "tcp_reassemble_out_of_order" from functioning due to skipping
- * retransmission of a lost segment?
- * If the latter is enabled, it could use use "maxnextseq" for ignoring
- * retransmitted single-segment PDUs (that would require storing
- * per-packet state (tcp_per_packet_data_t) to make it work for two-pass
- * and random access dissection). Retransmitted segments that are part
- * of a MSP should already be passed only once to subdissectors due to
- * the "reassembled_in" check below.
- */
- if(tcpd->ta) {
- /* Spurious Retransmission is the most obvious case to handle, just ignore it.
- * See issue 10289
+ /* Have we seen this PDU before (and is it the start of a multi-
+ * segment PDU)?
+ *
+ * If the sequence number was seen before, it is part of a
+ * retransmission if the whole segment fits within the MSP.
+ * (But if this is this frame was already visited and the first frame of
+ * the MSP matches the current frame, then it is not a retransmission,
+ * but the start of a new MSP.)
+ *
+ * If only part of the segment fits in the MSP, then either:
+ * - The previous segment included with the MSP was a Zero Window Probe
+ * with one byte of data and the subdissector just asked for one more
+ * byte. Do not mark it as retransmission (Bug 15427).
+ * - Data was actually being retransmitted, but with additional data
+ * (Bug 13523). Do not mark it as retransmission to handle the extra
+ * bytes. (NOTE Due to the TCP_A_RETRANSMISSION check below, such
+ * extra data will still be ignored.)
+ * - The MSP contains multiple segments, but the subdissector finished
+ * reassembly using a subset of the final segment (thus "msp->nxtpdu"
+ * is smaller than the nxtseq of the previous segment). If that final
+ * segment was retransmitted, then "nxtseq > msp->nxtpdu".
+ * Unfortunately that will *not* be marked as retransmission here.
+ * The next TCP_A_RETRANSMISSION hopefully takes care of it though.
+ *
+ * Only shortcircuit here when the first segment of the MSP is known,
+ * and when this first segment is not one to complete the MSP.
*/
- if((tcpd->ta->flags&TCP_A_SPURIOUS_RETRANSMISSION) == TCP_A_SPURIOUS_RETRANSMISSION) {
- return;
- }
- if((tcpd->ta->flags&TCP_A_RETRANSMISSION) == TCP_A_RETRANSMISSION) {
- const char* str = "Retransmitted ";
+ if ((msp = (struct tcp_multisegment_pdu *)wmem_tree_lookup32(tcpd->fwd->multisegment_pdus, seq)) &&
+ nxtseq <= msp->nxtpdu &&
+ !(msp->flags & MSP_FLAGS_MISSING_FIRST_SEGMENT) && msp->last_frame != pinfo->num) {
+ const char* str;
+ gboolean is_retransmission = FALSE;
+
+ /* Yes. This could be because we've dissected this frame before
+ * or because this is a retransmission of a previously-seen
+ * segment. Either way, we don't need to hand it off to the
+ * subdissector and we certainly don't want to re-add it to the
+ * multisegment_pdus list: if we did, subsequent lookups would
+ * find this retransmission instead of the original transmission
+ * (breaking desegmentation if we'd already linked other segments
+ * to the original transmission's entry).
+ *
+ * Cases to handle here:
+ * - In-order stream, pinfo->num matches begin of MSP.
+ * - In-order stream, but pinfo->num does not match the begin of the
+ * MSP. Must be a retransmission.
+ * - OoO stream where this segment fills the gap in the begin of the
+ * MSP. msp->first_frame is the start where the gap was detected
+ * (and does NOT match pinfo->num).
+ */
+
+ if (msp->first_frame == pinfo->num || msp->first_frame_with_seq == pinfo->num) {
+ str = "";
+ } else {
+ str = "Retransmitted ";
+ is_retransmission = TRUE;
+ /* TCP analysis already flags this (in COL_INFO) as a retransmission--if it's enabled */
+ }
+
+ /* Fix for bug 3264: look up ipfd for this (first) segment,
+ so can add tcp.reassembled_in generated field on this code path. */
+ if (!is_retransmission) {
+ ipfd_head = fragment_get(&tcp_reassembly_table, pinfo, msp->first_frame, msp);
+ if (ipfd_head) {
+ if (ipfd_head->reassembled_in != 0) {
+ item = proto_tree_add_uint(tcp_tree, hf_tcp_reassembled_in, tvb, 0,
+ 0, ipfd_head->reassembled_in);
+ proto_item_set_generated(item);
+
+ if (first_pdu) {
+ col_append_sep_fstr(pinfo->cinfo, COL_INFO, " ", "[TCP PDU reassembled in %u]",
+ ipfd_head->reassembled_in);
+ }
+ }
+ }
+ }
+
nbytes = tvb_reported_length_remaining(tvb, offset);
+
proto_tree_add_bytes_format(tcp_tree, hf_tcp_segment_data, tvb, offset,
nbytes, NULL, "%sTCP segment data (%u byte%s)", str, nbytes,
plurality(nbytes, "", "s"));
- return;
+ goto clean_exit;
+ }
+
+ /* Else, find the most previous PDU starting before this sequence number */
+ if (!msp) {
+ msp = (struct tcp_multisegment_pdu *)wmem_tree_lookup32_le(tcpd->fwd->multisegment_pdus, seq-1);
+ }
+
+ gboolean has_unfinished_msp = FALSE;
+ if (msp && LE_SEQ(msp->seq, seq) && GT_SEQ(msp->nxtpdu, seq) && !(msp->flags & MSP_FLAGS_GOT_ALL_SEGMENTS)) {
+ has_unfinished_msp = TRUE;
+ }
+
+ /* The above code only finds retransmission if the PDU boundaries and the seq coincide
+ * If we have sequence analysis active use the TCP_A_RETRANSMISSION flag.
+ * XXXX Could the above code be improved?
+ */
+ if(tcpd->ta) {
+ /* If we have an unfinished MSP that this segment belongs to
+ * or if the sequence number is newer than anything we've seen,
+ * then this is Out of Order from the reassembly perspective
+ * and we want to process it anyway.
+ */
+ if (!PINFO_FD_VISITED(pinfo) && tcpd->fwd->maxnextseq && LE_SEQ(seq, tcpd->fwd->maxnextseq) && !has_unfinished_msp) {
+ /* Otherwise, if TCP Analysis calls the segment a
+ * Spurious Retransmission or Retransmission, ignore it
+ * here and on future passes.
+ * See issue 10289
+ * XXX: There are still some cases where TCP Analysis
+ * marks segments as Retransmissions when they are
+ * Out of Order from this perspective (#10725, #13843)
+ */
+ if((tcpd->ta->flags&TCP_A_SPURIOUS_RETRANSMISSION) == TCP_A_SPURIOUS_RETRANSMISSION ||
+ ((tcpd->ta->flags&TCP_A_RETRANSMISSION) == TCP_A_RETRANSMISSION)) {
+ tcpd->ta->flags |= TCP_A_OLD_DATA;
+ }
+ }
+ if((tcpd->ta->flags&TCP_A_OLD_DATA) == TCP_A_OLD_DATA) {
+ const char* str = "Retransmitted ";
+ nbytes = tvb_reported_length_remaining(tvb, offset);
+ proto_tree_add_bytes_format(tcp_tree, hf_tcp_segment_data, tvb, offset,
+ nbytes, NULL, "%sTCP segment data (%u byte%s)", str, nbytes,
+ plurality(nbytes, "", "s"));
+ goto clean_exit;
+ }
}
- }
- /* Else, find the most previous PDU starting before this sequence number */
- if (!msp) {
- msp = (struct tcp_multisegment_pdu *)wmem_tree_lookup32_le(tcpd->fwd->multisegment_pdus, seq-1);
}
}
- if (reassemble_ooo && tcpd && !(tcpd->fwd->flags & TCP_FLOW_REASSEMBLE_UNTIL_FIN) && !PINFO_FD_VISITED(pinfo)) {
- /* If there is a gap between this segment and any previous ones (that
- * is, seqno is larger than the maximum expected seqno), then it is
- * possibly an out-of-order segment. The very first segment is expected
- * to be in-order though (otherwise captures starting in midst of a
- * connection would never be reassembled).
- *
- * Do not bother checking for OoO segments for streams that are
- * reassembled at FIN, the order of segments before FIN does not matter
- * as reordering and reassembly occurs at FIN.
- */
- if (tcpd->fwd->maxnextseq) {
- /* Segments may be missing due to packet loss (assume later
- * retransmission) or out-of-order (assume it will appear later).
- *
- * Extend an unfinished MSP when (1) missing segments exist between
- * the start of the previous, (2) unfinished MSP and new segment.
+ if (reassemble_ooo && tcpd && !(tcpd->fwd->flags & TCP_FLOW_REASSEMBLE_UNTIL_FIN)) {
+ if (!PINFO_FD_VISITED(pinfo)) {
+ /* If there is a gap between this segment and any previous ones
+ * (that is, seqno is larger than the maximum expected seqno), then
+ * it is possibly an out-of-order segment. The very first segment
+ * is expected to be in-order though (otherwise captures starting
+ * in midst of a connection would never be reassembled).
+ * (maxnextseq is 0 if we have not seen a SYN packet, even with
+ * absolute sequence numbers.)
*
- * Create a new MSP when no (1) previous MSP exists and (2) a gap is
- * detected between the previous largest nxtseq and the new segment.
+ * Do not bother checking for OoO segments for streams that are
+ * reassembled at FIN, the order of segments before FIN does not
+ * matter as reordering and reassembly occurs at FIN.
*/
- /* Whether a previous MSP exists with missing segments. */
- gboolean has_unfinished_msp = msp && !(msp->flags & MSP_FLAGS_GOT_ALL_SEGMENTS);
- /* Whether the new segment creates a new gap. */
- gboolean has_gap = LT_SEQ(tcpd->fwd->maxnextseq, seq);
-
- if (has_unfinished_msp && missing_segments(pinfo, msp, seq)) {
- /* The last PDU is part of a MSP which still needed more data,
- * extend it (if necessary) to cover the entire new segment.
- */
- if (LT_SEQ(msp->nxtpdu, nxtseq)) {
- msp->nxtpdu = nxtseq;
- }
- } else if (!has_unfinished_msp && has_gap) {
- /* Either the previous segment was a single PDU that did not
- * belong to a MSP, or the previous MSP was completed and cannot
- * be extended.
- * Create a new one starting at the expected next position and
- * extend it to the end of the new segment.
+
+ if (tcpd->fwd->maxnextseq) {
+ /* Segments may be missing due to packet loss (assume later
+ * retransmission) or out-of-order (assume it appears later).
+ *
+ * XXX: It would be nice to handle captures that have both
+ * out-of-order packets and some lost packets that are
+ * never retransmitted. But using the reverse flow ACK
+ * (like follow_tcp_tap_listener) or using a known end of
+ * a MSP (that we haven't fully received yet) to process a
+ * segment that starts right afterwards would both break the
+ * promise of in-order delivery, if a missing packet did arrive
+ * later, which is a problem for any state-based dissector
+ * (including TLS.)
*/
- msp = pdu_store_sequencenumber_of_next_pdu(pinfo,
- tcpd->fwd->maxnextseq, nxtseq,
- tcpd->fwd->multisegment_pdus);
- msp->flags |= MSP_FLAGS_MISSING_FIRST_SEGMENT;
+ /* Whether the new segment has a gap from our latest contiguous
+ * sequence number. */
+ has_gap = LT_SEQ(tcpd->fwd->maxnextseq, seq);
+ }
+
+ if (!has_gap) {
+ /* Update the maximum expected seqno if no SYN packet was seen
+ * before, or if the new segment succeeds previous segments. */
+ tcpd->fwd->maxnextseq = nxtseq;
+
+ /* If there is no gap, look for any OOO packets that are now
+ * contiguous. */
+ msp = msp_add_out_of_order(pinfo, msp, tcpd, seq);
+ }
+ } else {
+ /* If we have visited this frame before, look for the frame in the
+ * list of unused out of order segments. Since we know the gap will
+ * never be filled, we could pass it to the subdissector, but
+ * we want to be consistent between passes.
+ */
+ ooo_segment_item *fd;
+ fd = wmem_new0(pinfo->pool, ooo_segment_item);
+ fd->frame = pinfo->num;
+ fd->seq = seq;
+ fd->len = nxtseq - seq;
+ if (wmem_list_find_custom(tcpd->fwd->ooo_segments, fd, compare_ooo_segment_item)) {
+ has_gap = TRUE;
}
- /* Now that the MSP is updated or created, continue adding the
- * segments to the MSP below. The subdissector will not be called as
- * the MSP is not complete yet. */
}
- if (tcpd->fwd->maxnextseq == 0 || LT_SEQ(tcpd->fwd->maxnextseq, nxtseq)) {
- /* Update the maximum expected seqno if no SYN packet was seen
- * before, or if the new segment succeeds previous segments. */
- tcpd->fwd->maxnextseq = nxtseq;
+ }
+
+ /* If we are not processing out of order, update the max nextseq value if
+ * is later than our current value (or our first value.)
+ */
+ if (!reassemble_ooo && tcpd && !(tcpd->fwd->flags & TCP_FLOW_REASSEMBLE_UNTIL_FIN)) {
+ if (!PINFO_FD_VISITED(pinfo)) {
+ if (LT_SEQ(tcpd->fwd->maxnextseq, nxtseq) || tcpd->fwd->maxnextseq == 0) {
+ tcpd->fwd->maxnextseq = nxtseq;
+ }
}
}
@@ -3552,16 +4452,16 @@ again:
* have to worry about increasing the fragment length here.
*/
fragment_reset_tot_len(&tcp_reassembly_table, pinfo,
- msp->first_frame, NULL,
+ msp->first_frame, msp,
MAX(seq + len, msp->nxtpdu) - msp->seq);
}
ipfd_head = fragment_add(&tcp_reassembly_table, tvb, offset,
- pinfo, msp->first_frame, NULL,
+ pinfo, msp->first_frame, msp,
seq - msp->seq, len,
(LT_SEQ (nxtseq,msp->nxtpdu)) );
- if (!PINFO_FD_VISITED(pinfo)
+ if (!PINFO_FD_VISITED(pinfo) && ipfd_head
&& msp->flags & MSP_FLAGS_REASSEMBLE_ENTIRE_SEGMENT) {
msp->flags &= (~MSP_FLAGS_REASSEMBLE_ENTIRE_SEGMENT);
@@ -3573,15 +4473,12 @@ again:
* will advance nxtpdu even further later down in
* the code.)
*/
- msp->nxtpdu = nxtseq;
+ if (LT_SEQ(msp->nxtpdu, nxtseq)) {
+ msp->nxtpdu = nxtseq;
+ }
}
if (reassemble_ooo && !PINFO_FD_VISITED(pinfo)) {
- /* If the first segment of the MSP was seen, remember it. */
- if (msp->seq == seq && msp->flags & MSP_FLAGS_MISSING_FIRST_SEGMENT) {
- msp->first_frame_with_seq = pinfo->num;
- msp->flags &= ~MSP_FLAGS_MISSING_FIRST_SEGMENT;
- }
/* Remember when all segments are ready to avoid subsequent
* out-of-order packets from extending this MSP. If a subsdissector
* needs more segments, the flag will be cleared below. */
@@ -3595,6 +4492,30 @@ again:
&& (len > 0)) {
another_pdu_follows=msp->nxtpdu - seq;
}
+ } else if (has_gap) {
+ /* This is an OOO segment with a gap and past the known end of
+ * the current MSP, if any. We don't know for certain which MSP
+ * it belongs to, and the reassembly functions don't let us remove
+ * fragment items added by mistake. Keep it around in a separate
+ * structure, and add it later.
+ *
+ * On the second and later passes, we know that this gap will
+ * never be filled in, so we could hand the segment to the
+ * subdissector anyway. However, we want dissection to be
+ * consistent between passes.
+ */
+ if (!PINFO_FD_VISITED(pinfo)) {
+ ooo_segment_item *fd;
+ fd = wmem_new0(wmem_file_scope(), ooo_segment_item);
+ fd->frame = pinfo->num;
+ fd->seq = seq;
+ fd->len = nxtseq - seq;
+ /* We only enter here if dissect_tcp set can_desegment,
+ * which means that these bytes exist. */
+ fd->data = tvb_memdup(wmem_file_scope(), tvb, offset, fd->len);
+ wmem_list_insert_sorted(tcpd->fwd->ooo_segments, fd, compare_ooo_segment_item);
+ }
+ ipfd_head = NULL;
} else {
/* This segment was not found in our table, so it doesn't
* contain a continuation of a higher-level PDU.
@@ -3610,17 +4531,28 @@ again:
process_tcp_payload(tvb, offset, pinfo, tree, tcp_tree,
sport, dport, 0, 0, FALSE, tcpd, tcpinfo);
+
+ /* Unless it failed to dissect any data at all, the subdissector
+ * might have changed the addresses and/or ports. Save them, and
+ * set them back to the original values temporarily so that the
+ * fragment functions work correctly (including in any later PDU.)
+ *
+ * (If we didn't dissect any data, the subdissector *shouldn't*
+ * have changed the addresses or ports, so don't save them, but
+ * restore them just in case.)
+ */
+ if (!(pinfo->desegment_len && pinfo->desegment_offset == 0)) {
+ save_endpoint(pinfo, &new_endpoint);
+ }
+ restore_endpoint(pinfo, &orig_endpoint);
called_dissector = TRUE;
/* Did the subdissector ask us to desegment some more data
* before it could handle the packet?
- * If so we have to create some structures in our table but
- * this is something we only do the first time we see this
- * packet.
+ * If so we'll have to handle that later.
*/
if(pinfo->desegment_len) {
- if (!PINFO_FD_VISITED(pinfo))
- must_desegment = TRUE;
+ must_desegment = TRUE;
/*
* Set "deseg_offset" to the offset in "tvb"
@@ -3647,14 +4579,13 @@ again:
* Note that the last segment may include more than what
* we needed.
*/
- if(ipfd_head->reassembled_in == pinfo->num) {
+ if (ipfd_head->reassembled_in == pinfo->num && ipfd_head->reas_in_layer_num == pinfo->curr_layer_num) {
/*
* OK, this is the last segment.
* Let's call the subdissector with the desegmented
* data.
*/
tvbuff_t *next_tvb;
- int old_len;
/* create a new TVB structure for desegmented data */
next_tvb = tvb_new_chain(tvb, ipfd_head->tvb_data);
@@ -3674,6 +4605,20 @@ again:
/* call subdissector */
process_tcp_payload(next_tvb, 0, pinfo, tree, tcp_tree, sport,
dport, 0, 0, FALSE, tcpd, tcpinfo);
+
+ /* Unless it failed to dissect any data at all, the subdissector
+ * might have changed the addresses and/or ports. Save them, and
+ * set them back to the original values temporarily so that the
+ * fragment functions work correctly (including in any later PDU.)
+ *
+ * (If we didn't dissect any data, the subdissector *shouldn't*
+ * have changed the addresses or ports, so don't save them, but
+ * restore them just in case.)
+ */
+ if (!(pinfo->desegment_len && pinfo->desegment_offset == 0)) {
+ save_endpoint(pinfo, &new_endpoint);
+ }
+ restore_endpoint(pinfo, &orig_endpoint);
called_dissector = TRUE;
/*
@@ -3681,71 +4626,126 @@ again:
* desegmented, or does it think we need even more
* data?
*/
- if (reassemble_ooo && !PINFO_FD_VISITED(pinfo) && pinfo->desegment_len) {
- /* "desegment_len" isn't 0, so it needs more data to extend the MSP. */
- msp->flags &= ~MSP_FLAGS_GOT_ALL_SEGMENTS;
- }
- old_len = (int)(tvb_reported_length(next_tvb) - last_fragment_len);
- if (pinfo->desegment_len &&
- pinfo->desegment_offset<=old_len) {
+ if (pinfo->desegment_len) {
/*
- * "desegment_len" isn't 0, so it needs more
- * data for something - and "desegment_offset"
- * is before "old_len", so it needs more data
- * to dissect the stuff we thought was
- * completely desegmented (as opposed to the
- * stuff at the beginning being completely
- * desegmented, but the stuff at the end
- * being a new higher-level PDU that also
- * needs desegmentation).
+ * "desegment_len" isn't 0, so it needs more data
+ * to fully dissect the current MSP. msp->nxtpdu was
+ * not accurate and needs to be updated.
+ *
+ * This can happen if a dissector asked for one
+ * more segment (but didn't know exactly how much data)
+ * or if segments were added out of order.
+ *
+ * This is opposed to the current MSP being completely
+ * desegmented, but the stuff at the end of the
+ * current frame past last_fragment_len starting a new
+ * higher-level PDU that may also need desegmentation.
+ * That case is handled on the next loop.
+ *
+ * We want to keep the same dissection and protocol layer
+ * numbers on subsequent passes.
*
* If "desegment_offset" is 0, then nothing in the reassembled
* TCP segments was dissected, so remove the data source.
*/
- if (pinfo->desegment_offset == 0)
+ if (pinfo->desegment_offset == 0) {
+ if (reassemble_ooo && !PINFO_FD_VISITED(pinfo)) {
+ msp->flags &= ~MSP_FLAGS_GOT_ALL_SEGMENTS;
+ }
remove_last_data_source(pinfo);
- fragment_set_partial_reassembly(&tcp_reassembly_table,
- pinfo, msp->first_frame, NULL);
-
- /* Update msp->nxtpdu to point to the new next
- * pdu boundary.
- */
- if (pinfo->desegment_len == DESEGMENT_ONE_MORE_SEGMENT) {
- /* We want reassembly of at least one
- * more segment so set the nxtpdu
- * boundary to one byte into the next
- * segment.
- * This means that the next segment
- * will complete reassembly even if it
- * is only one single byte in length.
- * If this is an OoO segment, then increment the MSP end.
- */
- msp->nxtpdu = MAX(seq + tvb_reported_length_remaining(tvb, offset), msp->nxtpdu) + 1;
- msp->flags |= MSP_FLAGS_REASSEMBLE_ENTIRE_SEGMENT;
- } else if (pinfo->desegment_len == DESEGMENT_UNTIL_FIN) {
- tcpd->fwd->flags |= TCP_FLOW_REASSEMBLE_UNTIL_FIN;
- /* This is not the first segment, and we thought the
- * reassembly would be done now, but now know we must
- * desgment until FIN. (E.g., HTTP Response with headers
- * split across segments, and no Content-Length or
- * Transfer-Encoding (RFC 7230, Section 3.3.3, case 7.)
- * For the same reasons as below when we encounter
- * DESEGMENT_UNTIL_FIN on the first segment, give
- * msp->nxtpdu a big (but not too big) offset so reassembly
- * will pick up the segments later.
- */
- msp->nxtpdu = msp->seq + 0x40000000;
+ fragment_set_partial_reassembly(&tcp_reassembly_table,
+ pinfo, msp->first_frame,
+ msp);
} else {
- if (seq + last_fragment_len >= msp->nxtpdu) {
- /* This is the segment (overlapping) the end of the MSP. */
- msp->nxtpdu = seq + last_fragment_len + pinfo->desegment_len;
+ /* If "desegment_offset" is not 0, then a PDU in the
+ * reassembled segments was dissected, but some stuff
+ * that was added previously is part of a later PDU.
+ */
+ if (LE_SEQ(msp->seq + pinfo->desegment_offset, seq)) {
+ /* If we don't use anything from the current frame's
+ * segment, then we can't split the msp. The frames of
+ * the earlier PDU weren't reassembled until now, so
+ * they need to point to a reassembled_in frame here
+ * or later.
+ *
+ * Since this segment is the first of newly contiguous
+ * segments, this means the subdissector is asking for
+ * fewer bytes than it did before.
+ * XXX: Report this as a dissector bug?
+ */
+ if (reassemble_ooo && !PINFO_FD_VISITED(pinfo)) {
+ msp->flags &= ~MSP_FLAGS_GOT_ALL_SEGMENTS;
+ }
+ fragment_set_partial_reassembly(&tcp_reassembly_table,
+ pinfo, msp->first_frame,
+ msp);
} else {
- /* This is a segment before the end of the MSP, so it
- * must be an out-of-order segmented that completed the
- * MSP. The requested additional data is relative to
- * that end.
+ /* If we did use bytes from the current segment, then
+ * we want to split the MSP; the earlier part is
+ * dissected in this frame on the first pass, so for
+ * consistency we want to do so on future passes, but
+ * the latter part we cannot dissect until later.
+ * We only need to do this on the first pass; split_msp
+ * truncates the msp so we don't get here a second
+ * time.
*/
- msp->nxtpdu += pinfo->desegment_len;
+ /* nxtpdu adjustment for the new msp is the same. */
+ if (!PINFO_FD_VISITED(pinfo)) {
+ /* We don't need to clear MSP_FLAGS_GOT_ALL_SEGMENTS
+ * since we are spliting the MSP.
+ */
+ msp = split_msp(pinfo, msp, tcpd);
+ }
+ print_tcp_fragment_tree(ipfd_head, tree, tcp_tree, pinfo, next_tvb);
+ }
+ }
+
+ if (!PINFO_FD_VISITED(pinfo)) {
+ /* Update msp->nxtpdu to point to the new next
+ * pdu boundary.
+ * We only do this on the first pass, though we shouldn't
+ * get here on a second pass (since we truncated the msp.)
+ */
+ if (pinfo->desegment_len == DESEGMENT_ONE_MORE_SEGMENT) {
+ /* We want reassembly of at least one
+ * more segment so set the nxtpdu
+ * boundary to one byte into the next
+ * segment.
+ * This means that the next segment
+ * will complete reassembly even if it
+ * is only one single byte in length.
+ * If this is an OoO segment, then increment
+ * the MSP end.
+ */
+ msp->nxtpdu = MAX(seq + tvb_reported_length_remaining(tvb, offset), msp->nxtpdu) + 1;
+ msp->flags |= MSP_FLAGS_REASSEMBLE_ENTIRE_SEGMENT;
+ } else if (pinfo->desegment_len == DESEGMENT_UNTIL_FIN) {
+ tcpd->fwd->flags |= TCP_FLOW_REASSEMBLE_UNTIL_FIN;
+ /* This is not the first segment, and we thought the
+ * reassembly would be done now, but now know we must
+ * desgment until FIN. (E.g., HTTP Response with headers
+ * split across segments, and no Content-Length or
+ * Transfer-Encoding (RFC 7230, Section 3.3.3, case 7.)
+ * For the same reasons as below when we encounter
+ * DESEGMENT_UNTIL_FIN on the first segment, give
+ * msp->nxtpdu a big (but not too big) offset so
+ * reassembly will pick up the segments later.
+ */
+ msp->nxtpdu = msp->seq + 0x40000000;
+ } else {
+ if (seq + last_fragment_len >= msp->nxtpdu) {
+ /* This is the segment (overlapping) the end of
+ * the MSP.
+ */
+ msp->nxtpdu = seq + last_fragment_len + pinfo->desegment_len;
+ } else {
+ /* This is a segment before the end of the MSP, so
+ * it must be an out-of-order segment that completed
+ * the MSP. The requested additional data is
+ * relative to that end.
+ */
+ msp->nxtpdu += pinfo->desegment_len;
+ }
}
}
@@ -3771,73 +4771,11 @@ again:
plurality(nbytes, "", "s"));
print_tcp_fragment_tree(ipfd_head, tree, tcp_tree, pinfo, next_tvb);
-
- /* Did the subdissector ask us to desegment
- * some more data? This means that the data
- * at the beginning of this segment completed
- * a higher-level PDU, but the data at the
- * end of this segment started a higher-level
- * PDU but didn't complete it.
- *
- * If so, we have to create some structures
- * in our table, but this is something we
- * only do the first time we see this packet.
- */
- if(pinfo->desegment_len) {
- if (!PINFO_FD_VISITED(pinfo))
- must_desegment = TRUE;
-
- /* The stuff we couldn't dissect
- * must have come from this segment,
- * so it's all in "tvb".
- *
- * "pinfo->desegment_offset" is
- * relative to the beginning of
- * "next_tvb"; we want an offset
- * relative to the beginning of "tvb".
- *
- * First, compute the offset relative
- * to the *end* of "next_tvb" - i.e.,
- * the number of bytes before the end
- * of "next_tvb" at which the
- * subdissector stopped. That's the
- * length of "next_tvb" minus the
- * offset, relative to the beginning
- * of "next_tvb, at which the
- * subdissector stopped.
- */
- deseg_offset = ipfd_head->datalen - pinfo->desegment_offset;
-
- /* "tvb" and "next_tvb" end at the
- * same byte of data, so the offset
- * relative to the end of "next_tvb"
- * of the byte at which we stopped
- * is also the offset relative to
- * the end of "tvb" of the byte at
- * which we stopped.
- *
- * Convert that back into an offset
- * relative to the beginning of
- * "tvb", by taking the length of
- * "tvb" and subtracting the offset
- * relative to the end.
- */
- deseg_offset = tvb_reported_length(tvb) - deseg_offset;
- }
}
}
}
if (must_desegment) {
- /* If the dissector requested "reassemble until FIN"
- * just set this flag for the flow and let reassembly
- * proceed at normal. We will check/pick up these
- * reassembled PDUs later down in dissect_tcp() when checking
- * for the FIN flag.
- */
- if (tcpd && pinfo->desegment_len == DESEGMENT_UNTIL_FIN) {
- tcpd->fwd->flags |= TCP_FLOW_REASSEMBLE_UNTIL_FIN;
- }
/*
* The sequence number at which the stuff to be desegmented
* starts is the sequence number of the byte at an offset
@@ -3850,51 +4788,73 @@ again:
*/
deseg_seq = seq + (deseg_offset - offset);
- if (tcpd && ((nxtseq - deseg_seq) <= 1024*1024)
- && (!PINFO_FD_VISITED(pinfo))) {
- if(pinfo->desegment_len == DESEGMENT_ONE_MORE_SEGMENT) {
- /* The subdissector asked to reassemble using the
- * entire next segment.
- * Just ask reassembly for one more byte
- * but set this msp flag so we can pick it up
- * above.
- */
- msp = pdu_store_sequencenumber_of_next_pdu(pinfo, deseg_seq,
- nxtseq+1, tcpd->fwd->multisegment_pdus);
- msp->flags |= MSP_FLAGS_REASSEMBLE_ENTIRE_SEGMENT;
- } else if (pinfo->desegment_len == DESEGMENT_UNTIL_FIN) {
- /*
- * The subdissector asked to reassemble at the end of the
- * connection. That will be done in dissect_tcp, but here we
- * have to ask reassembly to collect all future segments.
- * Note that TCP_FLOW_REASSEMBLE_UNTIL_FIN was set before, this
- * ensures that OoO detection is skipped.
- * The exact nxtpdu offset does not matter, but it should be
- * smaller than half of the maximum 32-bit unsigned integer to
- * allow detection of sequence number wraparound, and larger
- * than the largest possible stream size. Hopefully 1GiB
- * (0x40000000 bytes) should be enough.
- */
- msp = pdu_store_sequencenumber_of_next_pdu(pinfo, deseg_seq,
- nxtseq+0x40000000, tcpd->fwd->multisegment_pdus);
- } else {
- msp = pdu_store_sequencenumber_of_next_pdu(pinfo,
- deseg_seq, nxtseq+pinfo->desegment_len, tcpd->fwd->multisegment_pdus);
+ /* We have to create some structures in our table but
+ * this is something we only do the first time we see this
+ * packet. */
+ if (!PINFO_FD_VISITED(pinfo)) {
+ /* If the dissector requested "reassemble until FIN"
+ * just set this flag for the flow and let reassembly
+ * proceed at normal. We will check/pick up these
+ * reassembled PDUs later down in dissect_tcp() when checking
+ * for the FIN flag.
+ */
+ if (tcpd && pinfo->desegment_len == DESEGMENT_UNTIL_FIN) {
+ tcpd->fwd->flags |= TCP_FLOW_REASSEMBLE_UNTIL_FIN;
}
+ if (tcpd && ((nxtseq - deseg_seq) <= 1024*1024)) {
+ if(pinfo->desegment_len == DESEGMENT_ONE_MORE_SEGMENT) {
+ /* The subdissector asked to reassemble using the
+ * entire next segment.
+ * Just ask reassembly for one more byte
+ * but set this msp flag so we can pick it up
+ * above.
+ */
+ msp = pdu_store_sequencenumber_of_next_pdu(pinfo, deseg_seq,
+ nxtseq+1, tcpd->fwd->multisegment_pdus);
+ msp->flags |= MSP_FLAGS_REASSEMBLE_ENTIRE_SEGMENT;
+ } else if (pinfo->desegment_len == DESEGMENT_UNTIL_FIN) {
+ /*
+ * The subdissector asked to reassemble at the end of the
+ * connection. That will be done in dissect_tcp, but here we
+ * have to ask reassembly to collect all future segments.
+ * Note that TCP_FLOW_REASSEMBLE_UNTIL_FIN was set before,
+ * this ensures that OoO detection is skipped.
+ * The exact nxtpdu offset does not matter, but it should be
+ * smaller than half of the maximum 32-bit unsigned integer
+ * to allow detection of sequence number wraparound, and
+ * larger than the largest possible stream size. Hopefully
+ * 1GiB (0x40000000 bytes) should be enough.
+ */
+ msp = pdu_store_sequencenumber_of_next_pdu(pinfo, deseg_seq,
+ nxtseq+0x40000000, tcpd->fwd->multisegment_pdus);
+ } else {
+ msp = pdu_store_sequencenumber_of_next_pdu(pinfo,
+ deseg_seq, nxtseq+pinfo->desegment_len, tcpd->fwd->multisegment_pdus);
+ }
- /* add this segment as the first one for this new pdu */
- fragment_add(&tcp_reassembly_table, tvb, deseg_offset,
- pinfo, msp->first_frame, NULL,
- 0, nxtseq - deseg_seq,
- LT_SEQ(nxtseq, msp->nxtpdu));
+ /* add this segment as the first one for this new pdu */
+ fragment_add(&tcp_reassembly_table, tvb, deseg_offset,
+ pinfo, msp->first_frame, msp,
+ 0, nxtseq - deseg_seq,
+ LT_SEQ(nxtseq, msp->nxtpdu));
+ }
+ } else {
+ /* If this is not the first time we have seen the packet, then
+ * the MSP should already be created. Retrieve it to see if we
+ * know what later frame the PDU is reassembled in.
+ */
+ if (tcpd && (msp = (struct tcp_multisegment_pdu *)wmem_tree_lookup32(tcpd->fwd->multisegment_pdus, deseg_seq))) {
+ ipfd_head = fragment_get(&tcp_reassembly_table, pinfo, msp->first_frame, msp);
+ }
}
}
if (!called_dissector || pinfo->desegment_len != 0) {
if (ipfd_head != NULL && ipfd_head->reassembled_in != 0 &&
+ ipfd_head->reassembled_in != pinfo->num &&
!(ipfd_head->flags & FD_PARTIAL_REASSEMBLY)) {
/*
- * We know what frame this PDU is reassembled in;
+ * We know what other frame this PDU is reassembled in;
* let the user know.
*/
item = proto_tree_add_uint(tcp_tree, hf_tcp_reassembled_in, tvb, 0,
@@ -3919,20 +4879,23 @@ again:
* of the payload, and that's 0).
* Just mark this as TCP.
*/
- col_set_str(pinfo->cinfo, COL_PROTOCOL, "TCP");
- col_append_sep_str(pinfo->cinfo, COL_INFO, " ", "[TCP segment of a reassembled PDU]");
+ if (first_pdu && ipfd_head != NULL && ipfd_head->reassembled_in != 0) {
+ col_append_sep_fstr(pinfo->cinfo, COL_INFO, " ", "[TCP PDU reassembled in %u]",
+ ipfd_head->reassembled_in);
+ }
}
/*
* Show what's left in the packet as just raw TCP segment
- * data.
+ * data. (It's possible that another PDU follows in the case
+ * of an out of order frame that is part of two MSPs.)
* XXX - remember what protocol the last subdissector
* was, and report it as a continuation of that, instead?
*/
- nbytes = tvb_reported_length_remaining(tvb, deseg_offset);
+ nbytes = another_pdu_follows ? another_pdu_follows : tvb_reported_length_remaining(tvb, deseg_offset);
proto_tree_add_bytes_format(tcp_tree, hf_tcp_segment_data, tvb, deseg_offset,
- -1, NULL, "TCP segment data (%u byte%s)", nbytes,
+ nbytes, NULL, "TCP segment data (%u byte%s)", nbytes,
plurality(nbytes, "", "s"));
}
pinfo->can_desegment = 0;
@@ -3950,6 +4913,7 @@ again:
col_set_fence(pinfo->cinfo, COL_INFO);
cleared_writable |= col_get_writable(pinfo->cinfo, COL_PROTOCOL);
col_set_writable(pinfo->cinfo, COL_PROTOCOL, FALSE);
+ first_pdu = FALSE;
offset += another_pdu_follows;
seq += another_pdu_follows;
goto again;
@@ -3961,6 +4925,12 @@ again:
col_set_writable(pinfo->cinfo, COL_PROTOCOL, TRUE);
}
}
+
+clean_exit:
+ /* Restore the addresses and ports to whatever they were after
+ * the last segment that successfully dissected some data, if any.
+ */
+ restore_endpoint(pinfo, &new_endpoint);
}
void
@@ -3980,6 +4950,10 @@ tcp_dissect_pdus(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
guint8 curr_layer_num;
wmem_list_frame_t *frame;
+ tcp_endpoint_t orig_endpoint;
+
+ save_endpoint(pinfo, &orig_endpoint);
+
while (tvb_reported_length_remaining(tvb, offset) > 0) {
/*
* We use "tvb_ensure_captured_length_remaining()" to make
@@ -4025,7 +4999,16 @@ tcp_dissect_pdus(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
* Support protocols which have a variable length which cannot
* always be determined within the given fixed_len.
*/
- DISSECTOR_ASSERT(proto_desegment && pinfo->can_desegment);
+ /*
+ * If another segment was requested but we can't do reassembly,
+ * abort and warn about the unreassembled packet.
+ */
+ THROW_ON(!(proto_desegment && pinfo->can_desegment), FragmentBoundsError);
+ /*
+ * Tell the TCP dissector where the data for this message
+ * starts in the data it handed us, and that we need one
+ * more segment, and return.
+ */
pinfo->desegment_offset = offset;
pinfo->desegment_len = DESEGMENT_ONE_MORE_SEGMENT;
return;
@@ -4116,6 +5099,17 @@ tcp_dissect_pdus(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
if (length > plen)
length = plen;
next_tvb = tvb_new_subset_length_caplen(tvb, offset, length, plen);
+ if (!(proto_desegment && pinfo->can_desegment)) {
+ if (plen > length) {
+ /* If we can't do reassembly but the PDU is split across
+ * segment boundaries, mark the tvbuff as a fragment so
+ * we throw FragmentBoundsError instead of malformed
+ * errors.
+ */
+ tvb_set_fragment(next_tvb);
+ }
+ }
+
/*
* Dissect the PDU.
@@ -4130,6 +5124,7 @@ tcp_dissect_pdus(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
* data.
*/
saved_proto = pinfo->current_proto;
+ restore_endpoint(pinfo, &orig_endpoint);
TRY {
(*dissect_pdu)(next_tvb, pinfo, tree, dissector_data);
}
@@ -4163,6 +5158,12 @@ tcp_info_append_uint(packet_info *pinfo, const char *abbrev, guint32 val)
col_append_str_uint(pinfo->cinfo, COL_INFO, abbrev, val, " ");
}
+static void
+tcp_info_append_hex_uint(packet_info *pinfo, const char *abbrev, guint32 val)
+{
+ col_append_fstr(pinfo->cinfo, COL_INFO, " %s=%X", abbrev, val);
+}
+
static gboolean
tcp_option_len_check(proto_item* length_item, packet_info *pinfo, guint len, guint optlen)
{
@@ -4274,54 +5275,225 @@ dissect_tcpopt_tfo(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* da
return tvb_captured_length(tvb);
}
+/*
+ * TCP ACK Rate Request option is based on
+ * https://datatracker.ietf.org/doc/html/draft-gomez-tcpm-ack-rate-request-06
+ */
+
+#define TCPOPT_TARR_RATE_MASK 0xfe
+#define TCPOPT_TARR_RESERVED_MASK 0x01
+#define TCPOPT_TARR_RATE_SHIFT 1
+
+static void
+dissect_tcpopt_tarr_data(tvbuff_t *tvb, int data_offset, guint data_len,
+ packet_info *pinfo, proto_tree *tree, proto_item *item, void *data _U_)
+{
+ guint8 rate;
+
+ switch (data_len) {
+ case 0:
+ col_append_str(pinfo->cinfo, COL_INFO, " TARR");
+ break;
+ case 1:
+ rate = (tvb_get_guint8(tvb, data_offset) & TCPOPT_TARR_RATE_MASK) >> TCPOPT_TARR_RATE_SHIFT;
+ proto_tree_add_item(tree, hf_tcp_option_tarr_rate, tvb, data_offset, 1, ENC_BIG_ENDIAN);
+ proto_tree_add_item(tree, hf_tcp_option_tarr_reserved, tvb, data_offset, 1, ENC_BIG_ENDIAN);
+ tcp_info_append_uint(pinfo, "TARR", rate);
+ proto_item_append_text(item, " %u", rate);
+ break;
+ }
+}
+
+static void
+dissect_tcpopt_acc_ecn_data(tvbuff_t *tvb, int data_offset, guint data_len,
+ gboolean is_order_0, packet_info *pinfo, proto_tree *tree, proto_item *item, void *data _U_)
+{
+ struct tcp_analysis *tcpd;
+ guint32 ee0b, eceb, ee1b;
+
+ switch (data_len) {
+ case 0:
+ col_append_str(pinfo->cinfo, COL_INFO, " AccECN");
+ break;
+ case 3:
+ if (is_order_0) {
+ ee0b = tvb_get_guint24(tvb, data_offset, ENC_BIG_ENDIAN);
+ proto_tree_add_item(tree, hf_tcp_option_acc_ecn_ee0b, tvb, data_offset, 3, ENC_BIG_ENDIAN);
+ proto_item_append_text(item, " (Order 0): EE0B %u", ee0b);
+ tcp_info_append_uint(pinfo, "EE0B", ee0b);
+ } else {
+ ee1b = tvb_get_guint24(tvb, data_offset, ENC_BIG_ENDIAN);
+ proto_tree_add_item(tree, hf_tcp_option_acc_ecn_ee1b, tvb, data_offset, 3, ENC_BIG_ENDIAN);
+ proto_item_append_text(item, " (Order 1): EE1B %u", ee1b);
+ tcp_info_append_uint(pinfo, "EE1B", ee1b);
+ }
+ break;
+ case 6:
+ if (is_order_0) {
+ ee0b = tvb_get_guint24(tvb, data_offset, ENC_BIG_ENDIAN);
+ proto_tree_add_item(tree, hf_tcp_option_acc_ecn_ee0b, tvb, data_offset, 3, ENC_BIG_ENDIAN);
+ tcp_info_append_uint(pinfo, "EE0B", ee0b);
+ } else {
+ ee1b = tvb_get_guint24(tvb, data_offset, ENC_BIG_ENDIAN);
+ proto_tree_add_item(tree, hf_tcp_option_acc_ecn_ee1b, tvb, data_offset, 3, ENC_BIG_ENDIAN);
+ tcp_info_append_uint(pinfo, "EE1B", ee1b);
+ }
+ eceb = tvb_get_guint24(tvb, data_offset + 3, ENC_BIG_ENDIAN);
+ proto_tree_add_item(tree, hf_tcp_option_acc_ecn_eceb, tvb, data_offset + 3, 3, ENC_BIG_ENDIAN);
+ tcp_info_append_uint(pinfo, "ECEB", eceb);
+ if (is_order_0) {
+ proto_item_append_text(item, " (Order 0): EE0B %u, ECEB %u", ee0b, eceb);
+ } else {
+ proto_item_append_text(item, " (Order 1): EE1B %u, ECEB %u", ee1b, eceb);
+ }
+ break;
+ case 9:
+ if (is_order_0) {
+ ee0b = tvb_get_guint24(tvb, data_offset, ENC_BIG_ENDIAN);
+ proto_tree_add_item(tree, hf_tcp_option_acc_ecn_ee0b, tvb, data_offset, 3, ENC_BIG_ENDIAN);
+ tcp_info_append_uint(pinfo, "EE0B", ee0b);
+ } else {
+ ee1b = tvb_get_guint24(tvb, data_offset, ENC_BIG_ENDIAN);
+ proto_tree_add_item(tree, hf_tcp_option_acc_ecn_ee1b, tvb, data_offset, 3, ENC_BIG_ENDIAN);
+ tcp_info_append_uint(pinfo, "EE1B", ee1b);
+ }
+ eceb = tvb_get_guint24(tvb, data_offset + 3, ENC_BIG_ENDIAN);
+ proto_tree_add_item(tree, hf_tcp_option_acc_ecn_eceb, tvb, data_offset + 3, 3, ENC_BIG_ENDIAN);
+ tcp_info_append_uint(pinfo, "ECEB", eceb);
+ if (is_order_0) {
+ ee1b = tvb_get_guint24(tvb, data_offset + 6, ENC_BIG_ENDIAN);
+ proto_tree_add_item(tree, hf_tcp_option_acc_ecn_ee1b, tvb, data_offset + 6, 3, ENC_BIG_ENDIAN);
+ tcp_info_append_uint(pinfo, "EE1B", ee1b);
+ proto_item_append_text(item, " (Order 0): EE0B %u, ECEB %u, EE1B %u", ee0b, eceb, ee1b);
+ } else {
+ ee0b = tvb_get_guint24(tvb, data_offset + 6, ENC_BIG_ENDIAN);
+ proto_tree_add_item(tree, hf_tcp_option_acc_ecn_ee0b, tvb, data_offset + 6, 3, ENC_BIG_ENDIAN);
+ tcp_info_append_uint(pinfo, "EE0B", ee0b);
+ proto_item_append_text(item, " (Order 1): EE1B %u, ECEB %u, EE0B %u", ee1b, eceb, ee0b);
+ }
+ break;
+ }
+ tcpd = get_tcp_conversation_data(NULL, pinfo);
+ if (tcpd != NULL) {
+ tcpd->had_acc_ecn_option = TRUE;
+ }
+}
+
+static int
+dissect_tcpopt_acc_ecn(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
+{
+ proto_item *length_item, *item;
+ proto_tree *acc_ecn_tree;
+ int offset;
+ guint8 kind, length;
+
+ offset = 0;
+ item = proto_tree_add_item(tree, proto_tcp_option_acc_ecn, tvb, offset, -1, ENC_NA);
+ acc_ecn_tree = proto_item_add_subtree(item, ett_tcp_option_acc_ecn);
+ kind = tvb_get_guint8(tvb, offset);
+ proto_tree_add_item(acc_ecn_tree, hf_tcp_option_kind, tvb, offset, 1, ENC_BIG_ENDIAN);
+ offset += 1;
+ length = tvb_get_guint8(tvb, offset);
+ length_item = proto_tree_add_item(acc_ecn_tree, hf_tcp_option_len, tvb, offset, 1, ENC_BIG_ENDIAN);
+ offset += 1;
+ if (length != 2 && length != 5 && length != 8 && length != 11) {
+ expert_add_info_format(pinfo, length_item, &ei_tcp_opt_len_invalid,
+ "option length should be 2, 5, 8, or 11 instead of %u", length);
+ } else {
+ dissect_tcpopt_acc_ecn_data(tvb, offset, length - 2, kind == TCPOPT_ACC_ECN_0, pinfo, acc_ecn_tree, item, data);
+ }
+ return tvb_captured_length(tvb);
+}
+
static int
dissect_tcpopt_exp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
{
- proto_item *item;
+ proto_item *item, *length_item;
proto_tree *exp_tree;
- guint16 magic;
+ guint16 exid;
+ guint8 kind;
gint offset = 0, optlen = tvb_reported_length(tvb);
item = proto_tree_add_item(tree, proto_tcp_option_exp, tvb, offset, -1, ENC_NA);
exp_tree = proto_item_add_subtree(item, ett_tcp_option_exp);
-
proto_tree_add_item(exp_tree, hf_tcp_option_kind, tvb, offset, 1, ENC_BIG_ENDIAN);
- proto_tree_add_item(exp_tree, hf_tcp_option_len, tvb, offset + 1, 1, ENC_BIG_ENDIAN);
- if (tcp_exp_options_with_magic && ((optlen - 2) > 0)) {
- magic = tvb_get_ntohs(tvb, offset + 2);
- proto_tree_add_item(exp_tree, hf_tcp_option_exp_magic_number, tvb,
- offset + 2, 2, ENC_BIG_ENDIAN);
- switch (magic) {
- case 0xf989: /* RFC7413, TCP Fast Open */
- dissect_tcpopt_tfo_payload(tvb, offset+2, optlen-2, pinfo, exp_tree, data);
- break;
- default:
- /* Unknown magic number */
- break;
+ kind = tvb_get_guint8(tvb, offset);
+ length_item = proto_tree_add_item(exp_tree, hf_tcp_option_len, tvb, offset + 1, 1, ENC_BIG_ENDIAN);
+ if (tcp_exp_options_rfc6994) {
+ if (optlen >= TCPOLEN_EXP_MIN) {
+ exid = tvb_get_ntohs(tvb, offset + 2);
+ proto_tree_add_item(exp_tree, hf_tcp_option_exp_exid, tvb,
+ offset + 2, 2, ENC_BIG_ENDIAN);
+ proto_item_append_text(item, ": %s", val_to_str_const(exid, tcp_exid_vs, "Unknown"));
+ switch (exid) {
+ case TCPEXID_TARR:
+ if (optlen != 4 && optlen != 5) {
+ expert_add_info_format(pinfo, length_item, &ei_tcp_opt_len_invalid,
+ "option length should be 4 or 5 instead of %d",
+ optlen);
+ } else {
+ dissect_tcpopt_tarr_data(tvb, offset + 4, optlen - 4,
+ pinfo, exp_tree, item, data);
+ }
+ break;
+ case 0xACC0: /* draft-ietf-tcpm-accurate-ecn-20 */
+ case 0xACC1:
+ if (optlen != 4 && optlen != 7 && optlen != 10 && optlen != 13) {
+ expert_add_info_format(pinfo, length_item, &ei_tcp_opt_len_invalid,
+ "option length should be 4, 7, 10, or 13 instead of %d",
+ optlen);
+ } else {
+ proto_item_append_text(item, ": Accurate ECN");
+ dissect_tcpopt_acc_ecn_data(tvb, offset + 4, optlen - 4,
+ exid == 0xACC0, pinfo, exp_tree,
+ item, data);
+ }
+ break;
+ case TCPEXID_FO:
+ dissect_tcpopt_tfo_payload(tvb, offset + 2, optlen - 2, pinfo, exp_tree, data);
+ break;
+ default:
+ if (optlen > TCPOLEN_EXP_MIN) {
+ proto_tree_add_item(exp_tree, hf_tcp_option_exp_data, tvb,
+ offset + TCPOLEN_EXP_MIN,
+ optlen - TCPOLEN_EXP_MIN, ENC_NA);
+ }
+ tcp_info_append_hex_uint(pinfo, "ExID", exid);
+ break;
+ }
+ } else {
+ expert_add_info_format(pinfo, length_item, &ei_tcp_opt_len_invalid,
+ "option length %u smaller than 4", optlen);
}
} else {
proto_tree_add_item(exp_tree, hf_tcp_option_exp_data, tvb,
offset + 2, optlen - 2, ENC_NA);
- tcp_info_append_uint(pinfo, "Expxx", TRUE);
+ tcp_info_append_uint(pinfo, "Exp", (kind == TCPOPT_EXP_FD) ? 1 : 2);
}
return tvb_captured_length(tvb);
}
static int
-dissect_tcpopt_sack_perm(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
+dissect_tcpopt_sack_perm(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data)
{
proto_item *item;
proto_tree *exp_tree;
proto_item *length_item;
int offset = 0;
+ struct tcpheader *tcph = (struct tcpheader *)data;
item = proto_tree_add_item(tree, proto_tcp_option_sack_perm, tvb, offset, -1, ENC_NA);
exp_tree = proto_item_add_subtree(item, ett_tcp_option_sack_perm);
+ if (!(tcph->th_flags & TH_SYN))
+ {
+ expert_add_info(pinfo, item, &ei_tcp_option_sack_perm_present);
+ }
+
proto_tree_add_item(exp_tree, hf_tcp_option_kind, tvb, offset, 1, ENC_BIG_ENDIAN);
length_item = proto_tree_add_item(exp_tree, hf_tcp_option_len, tvb, offset + 1, 1, ENC_BIG_ENDIAN);
- tcp_info_append_uint(pinfo, "SACK_PERM", TRUE);
+ col_append_str(pinfo->cinfo, COL_INFO, " SACK_PERM");
if (!tcp_option_len_check(length_item, pinfo, tvb_reported_length(tvb), TCPOLEN_SACK_PERM))
return tvb_captured_length(tvb);
@@ -4448,6 +5620,31 @@ dissect_tcpopt_sack(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* d
}
}
+ /* Late discovery of a 'false' Window Update in presence of SACK option,
+ * which means we are dealing with a Dup ACK rather than a Window Update.
+ * Classify accordingly by removing the UPDATE and adding the DUP flags.
+ * Mostly a copy/paste from tcp_analyze_sequence_number(), ensure consistency
+ * whenever the latter changes.
+ * see Issue #14937
+ */
+ if( tcp_analyze_seq && tcpd && tcpd->ta && tcpd->ta->flags&TCP_A_WINDOW_UPDATE ) {
+
+ /* MPTCP tolerates duplicate acks in some circumstances, see RFC 8684 4. */
+ if(tcpd->mptcp_analysis && (tcpd->mptcp_analysis->mp_operations!=tcpd->fwd->mp_operations)) {
+ /* just ignore this DUPLICATE ACK */
+ } else {
+ tcpd->fwd->tcp_analyze_seq_info->dupacknum++;
+
+ /* no initialization required of the tcpd->ta as this code would
+ * be unreachable otherwise
+ */
+ tcpd->ta->flags &= ~TCP_A_WINDOW_UPDATE;
+ tcpd->ta->flags |= TCP_A_DUPLICATE_ACK;
+ tcpd->ta->dupack_num=tcpd->fwd->tcp_analyze_seq_info->dupacknum;
+ tcpd->ta->dupack_frame=tcpd->fwd->tcp_analyze_seq_info->lastnondupack;
+ }
+ }
+
ti = proto_tree_add_item(tree, proto_tcp_option_sack, tvb, offset, -1, ENC_NA);
field_tree = proto_item_add_subtree(ti, ett_tcp_option_sack);
@@ -4573,7 +5770,7 @@ static gboolean tcp_ignore_timestamps = FALSE;
static int
dissect_tcpopt_timestamp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
{
- proto_item *ti;
+ proto_item *ti, *tsval_ti;
proto_tree *ts_tree;
proto_item *length_item;
int offset = 0;
@@ -4592,13 +5789,11 @@ dissect_tcpopt_timestamp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, vo
if (!tcp_option_len_check(length_item, pinfo, len, TCPOLEN_TIMESTAMP))
return tvb_captured_length(tvb);
- proto_tree_add_item_ret_uint(ts_tree, hf_tcp_option_timestamp_tsval, tvb, offset,
+ tsval_ti = proto_tree_add_item_ret_uint(ts_tree, hf_tcp_option_timestamp_tsval, tvb, offset,
4, ENC_BIG_ENDIAN, &ts_val);
- offset += 4;
- proto_tree_add_item_ret_uint(ts_tree, hf_tcp_option_timestamp_tsecr, tvb, offset,
+ proto_tree_add_item_ret_uint(ts_tree, hf_tcp_option_timestamp_tsecr, tvb, offset + 4,
4, ENC_BIG_ENDIAN, &ts_ecr);
- /* offset += 4; */
proto_item_append_text(ti, ": TSval %u, TSecr %u", ts_val, ts_ecr);
if (tcp_ignore_timestamps == FALSE) {
@@ -4606,6 +5801,17 @@ dissect_tcpopt_timestamp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, vo
tcp_info_append_uint(pinfo, "TSecr", ts_ecr);
}
+ if (read_seq_as_syn_cookie) {
+ proto_item_append_text(ti, " (syn cookie)");
+ proto_item* syncookie_ti = proto_item_add_subtree(tsval_ti, ett_tcp_syncookie_option);
+ guint32 timestamp = tvb_get_bits32(tvb, offset * 8, 26, ENC_NA) << 6;
+ proto_tree_add_uint_bits_format_value(syncookie_ti, hf_tcp_syncookie_option_timestamp, tvb, offset * 8,
+ 26, timestamp, ENC_TIME_SECS, "%s", abs_time_secs_to_str(pinfo->pool, timestamp, ABSOLUTE_TIME_LOCAL, TRUE));
+ proto_tree_add_bits_item(syncookie_ti, hf_tcp_syncookie_option_ecn, tvb, offset * 8 + 26, 1, ENC_NA);
+ proto_tree_add_bits_item(syncookie_ti, hf_tcp_syncookie_option_sack, tvb, offset * 8 + 27, 1, ENC_NA);
+ proto_tree_add_bits_item(syncookie_ti, hf_tcp_syncookie_option_wscale, tvb, offset * 8 + 28, 4, ENC_NA);
+ }
+
return tvb_captured_length(tvb);
}
@@ -5007,7 +6213,7 @@ dissect_tcpopt_mptcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void*
if (mph->mh_dss_flags & MPTCP_DSS_FLAG_DATA_ACK_8BYTES) {
mph->mh_dss_rawack = tvb_get_ntoh64(tvb,offset);
- proto_tree_add_uint64_format_value(mptcp_tree, hf_tcp_option_mptcp_data_ack_raw, tvb, offset, 8, mph->mh_dss_rawack, "%" G_GINT64_MODIFIER "u (64bits)", mph->mh_dss_rawack);
+ proto_tree_add_uint64_format_value(mptcp_tree, hf_tcp_option_mptcp_data_ack_raw, tvb, offset, 8, mph->mh_dss_rawack, "%" PRIu64 " (64bits)", mph->mh_dss_rawack);
offset += 8;
}
/* 32bits ack */
@@ -5040,7 +6246,7 @@ dissect_tcpopt_mptcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void*
if (mph->mh_dss_flags & MPTCP_DSS_FLAG_DSN_8BYTES) {
dsn = tvb_get_ntoh64(tvb,offset);
- proto_tree_add_uint64_format_value(mptcp_tree, hf_tcp_option_mptcp_data_seq_no_raw, tvb, offset, 8, dsn, "%" G_GINT64_MODIFIER "u (64bits version)", dsn);
+ proto_tree_add_uint64_format_value(mptcp_tree, hf_tcp_option_mptcp_data_seq_no_raw, tvb, offset, 8, dsn, "%" PRIu64 " (64bits version)", dsn);
/* if we have the opportunity to complete the 32 Most Significant Bits of the
*
@@ -5052,7 +6258,7 @@ dissect_tcpopt_mptcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void*
offset += 8;
} else {
dsn = tvb_get_ntohl(tvb,offset);
- proto_tree_add_uint64_format_value(mptcp_tree, hf_tcp_option_mptcp_data_seq_no_raw, tvb, offset, 4, dsn, "%" G_GINT64_MODIFIER "u (32bits version)", dsn);
+ proto_tree_add_uint64_format_value(mptcp_tree, hf_tcp_option_mptcp_data_seq_no_raw, tvb, offset, 4, dsn, "%" PRIu64 " (32bits version)", dsn);
offset += 4;
}
mph->mh_dss_rawdsn = dsn;
@@ -6038,7 +7244,7 @@ dissect_tcpopt_rvbd_trpy(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, vo
/* Started as a copy of dissect_ip_tcp_options(), but was changed to support
options as a dissector table */
static void
-tcp_dissect_options(tvbuff_t *tvb, int offset, guint length, int eol,
+tcp_dissect_options(tvbuff_t *tvb, int offset, guint length,
packet_info *pinfo, proto_tree *opt_tree,
proto_item *opt_item, void * data)
{
@@ -6050,9 +7256,16 @@ tcp_dissect_options(tvbuff_t *tvb, int offset, guint length, int eol,
tvbuff_t *next_tvb;
struct tcpheader *tcph = (struct tcpheader *)data;
gboolean mss_seen = FALSE;
+ gboolean eol_seen = FALSE;
+ gboolean sack_perm_seen = FALSE;
while (length > 0) {
opt = tvb_get_guint8(tvb, offset);
+ if (eol_seen && opt != TCPOPT_EOL) {
+ proto_tree_add_expert_format(opt_tree, pinfo, &ei_tcp_non_zero_bytes_after_eol, tvb, offset, length,
+ "Non-zero header padding");
+ return;
+ }
--length; /* account for type byte */
if ((opt == TCPOPT_EOL) || (opt == TCPOPT_NOP)) {
int local_proto;
@@ -6064,6 +7277,7 @@ tcp_dissect_options(tvbuff_t *tvb, int offset, guint length, int eol,
the next option by using the length in the option. */
if (opt == TCPOPT_EOL) {
local_proto = proto_tcp_option_eol;
+ eol_seen = true;
} else if (opt == TCPOPT_NOP) {
local_proto = proto_tcp_option_nop;
@@ -6092,7 +7306,7 @@ tcp_dissect_options(tvbuff_t *tvb, int offset, guint length, int eol,
name = wmem_strdup_printf(pinfo->pool, "Unknown (0x%02x)", opt);
option_dissector = tcp_opt_unknown_handle;
} else {
- name = dissector_handle_get_short_name(option_dissector);
+ name = dissector_handle_get_protocol_short_name(option_dissector);
}
/* Option has a length. Is it in the packet? */
@@ -6125,6 +7339,9 @@ tcp_dissect_options(tvbuff_t *tvb, int offset, guint length, int eol,
if (opt == TCPOPT_MSS)
{
mss_seen = TRUE;
+ } else if (opt == TCPOPT_SACK_PERM)
+ {
+ sack_perm_seen = TRUE;
}
next_tvb = tvb_new_subset_length(tvb, offset, optlen);
@@ -6134,14 +7351,18 @@ tcp_dissect_options(tvbuff_t *tvb, int offset, guint length, int eol,
offset += optlen;
length -= (optlen-2); //already accounted for type and len bytes
}
-
- if (opt == eol)
- break;
}
- if ((tcph->th_flags & TH_SYN) && (mss_seen != TRUE))
+ if (tcph->th_flags & TH_SYN)
{
- expert_add_info(pinfo, opt_item, &ei_tcp_option_mss_absent);
+ if (mss_seen == FALSE)
+ {
+ expert_add_info(pinfo, opt_item, &ei_tcp_option_mss_absent);
+ }
+ if (sack_perm_seen == FALSE)
+ {
+ expert_add_info(pinfo, opt_item, &ei_tcp_option_sack_perm_absent);
+ }
}
}
@@ -6163,6 +7384,7 @@ decode_tcp_ports(tvbuff_t *tvb, int offset, packet_info *pinfo,
tvbuff_t *next_tvb;
int low_port, high_port;
int save_desegment_offset;
+ gboolean try_low_port, try_high_port, try_server_port;
guint32 save_desegment_len;
heur_dtbl_entry_t *hdtbl_entry;
exp_pdu_data_t *exp_pdu_data;
@@ -6199,13 +7421,67 @@ decode_tcp_ports(tvbuff_t *tvb, int offset, packet_info *pinfo,
/* determine if this packet is part of a conversation and call dissector */
/* for the conversation if available */
- if (try_conversation_dissector(&pinfo->src, &pinfo->dst, ENDPOINT_TCP,
+ if (try_conversation_dissector(&pinfo->src, &pinfo->dst, CONVERSATION_TCP,
src_port, dst_port, next_tvb, pinfo, tree, tcpinfo, 0)) {
pinfo->want_pdu_tracking -= !!(pinfo->want_pdu_tracking);
handle_export_pdu_conversation(pinfo, next_tvb, src_port, dst_port, tcpinfo);
return TRUE;
}
+ /* If the user has manually configured one of the server, low, or high
+ * ports to a dissector other than the default (via Decode As or the
+ * preferences associated with Decode As), try those first, in that order.
+ */
+ try_server_port = FALSE;
+ if (tcpd && tcpd->server_port != 0) {
+ if (dissector_is_uint_changed(subdissector_table, tcpd->server_port)) {
+ if (dissector_try_uint_new(subdissector_table, tcpd->server_port, next_tvb, pinfo, tree, TRUE, tcpinfo)) {
+ pinfo->want_pdu_tracking -= !!(pinfo->want_pdu_tracking);
+ handle_export_pdu_dissection_table(pinfo, next_tvb, tcpd->server_port, tcpinfo);
+ return TRUE;
+ }
+ } else {
+ /* The default; try it later */
+ try_server_port = TRUE;
+ }
+ }
+
+ if (src_port > dst_port) {
+ low_port = dst_port;
+ high_port = src_port;
+ } else {
+ low_port = src_port;
+ high_port = dst_port;
+ }
+
+ try_low_port = FALSE;
+ if (low_port != 0) {
+ if (dissector_is_uint_changed(subdissector_table, low_port)) {
+ if (dissector_try_uint_new(subdissector_table, low_port, next_tvb, pinfo, tree, TRUE, tcpinfo)) {
+ pinfo->want_pdu_tracking -= !!(pinfo->want_pdu_tracking);
+ handle_export_pdu_dissection_table(pinfo, next_tvb, low_port, tcpinfo);
+ return TRUE;
+ }
+ } else {
+ /* The default; try it later */
+ try_low_port = TRUE;
+ }
+ }
+
+ try_high_port = FALSE;
+ if (high_port != 0) {
+ if (dissector_is_uint_changed(subdissector_table, high_port)) {
+ if (dissector_try_uint_new(subdissector_table, high_port, next_tvb, pinfo, tree, TRUE, tcpinfo)) {
+ pinfo->want_pdu_tracking -= !!(pinfo->want_pdu_tracking);
+ handle_export_pdu_dissection_table(pinfo, next_tvb, high_port, tcpinfo);
+ return TRUE;
+ }
+ } else {
+ /* The default; try it later */
+ try_high_port = TRUE;
+ }
+ }
+
if (try_heuristic_first) {
/* do lookup with the heuristic subdissector table */
if (dissector_try_heuristic(heur_subdissector_list, next_tvb, pinfo, tree, &hdtbl_entry, tcpinfo)) {
@@ -6232,28 +7508,20 @@ decode_tcp_ports(tvbuff_t *tvb, int offset, packet_info *pinfo,
XXX - we ignore port numbers of 0, as some dissectors use a port
number of 0 to disable the port. */
- if (tcpd && tcpd->server_port != 0 &&
+ if (try_server_port &&
dissector_try_uint_new(subdissector_table, tcpd->server_port, next_tvb, pinfo, tree, TRUE, tcpinfo)) {
pinfo->want_pdu_tracking -= !!(pinfo->want_pdu_tracking);
handle_export_pdu_dissection_table(pinfo, next_tvb, tcpd->server_port, tcpinfo);
return TRUE;
}
- if (src_port > dst_port) {
- low_port = dst_port;
- high_port = src_port;
- } else {
- low_port = src_port;
- high_port = dst_port;
- }
-
- if (low_port != 0 &&
+ if (try_low_port &&
dissector_try_uint_new(subdissector_table, low_port, next_tvb, pinfo, tree, TRUE, tcpinfo)) {
pinfo->want_pdu_tracking -= !!(pinfo->want_pdu_tracking);
handle_export_pdu_dissection_table(pinfo, next_tvb, low_port, tcpinfo);
return TRUE;
}
- if (high_port != 0 &&
+ if (try_high_port &&
dissector_try_uint_new(subdissector_table, high_port, next_tvb, pinfo, tree, TRUE, tcpinfo)) {
pinfo->want_pdu_tracking -= !!(pinfo->want_pdu_tracking);
handle_export_pdu_dissection_table(pinfo, next_tvb, high_port, tcpinfo);
@@ -6284,7 +7552,7 @@ decode_tcp_ports(tvbuff_t *tvb, int offset, packet_info *pinfo,
pinfo->want_pdu_tracking -= !!(pinfo->want_pdu_tracking);
if (have_tap_listener(exported_pdu_tap)) {
- exp_pdu_data = export_pdu_create_common_tags(pinfo, "data", EXP_PDU_TAG_PROTO_NAME);
+ exp_pdu_data = export_pdu_create_common_tags(pinfo, "data", EXP_PDU_TAG_DISSECTOR_NAME);
exp_pdu_data->tvb_captured_length = tvb_captured_length(next_tvb);
exp_pdu_data->tvb_reported_length = tvb_reported_length(next_tvb);
exp_pdu_data->pdu_tvb = next_tvb;
@@ -6462,8 +7730,7 @@ dissect_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
struct tcpinfo tcpinfo;
struct tcpheader *tcph;
proto_item *tf_syn = NULL, *tf_fin = NULL, *tf_rst = NULL, *scaled_pi;
- conversation_t *conv=NULL, *other_conv;
- guint32 save_last_frame = 0;
+ conversation_t *conv=NULL;
struct tcp_analysis *tcpd=NULL;
struct tcp_per_packet_data_t *tcppd=NULL;
proto_item *item;
@@ -6471,6 +7738,7 @@ dissect_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
gboolean icmp_ip = FALSE;
guint8 conversation_completeness = 0;
gboolean conversation_is_new = FALSE;
+ guint8 ace;
tcph = wmem_new0(pinfo->pool, struct tcpheader);
tcph->th_sport = tvb_get_ntohs(tvb, offset);
@@ -6542,20 +7810,14 @@ dissect_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
/* find(or create if needed) the conversation for this tcp session
* This is a slight deviation from find_or_create_conversation so it's
- * done manually. This is done to save the last frame of the conversation
- * in case a new conversation is found and the previous conversation needs
- * to be adjusted,
+ * done manually. This is done to avoid conversation overlapping when
+ * reusing ports (see issue 15097), as find_or_create_conversation automatically
+ * extends the conversation found. This extension is done later.
*/
- if((conv = find_conversation_pinfo(pinfo, 0)) != NULL) {
- /* Update how far the conversation reaches */
- if (pinfo->num > conv->last_frame) {
- save_last_frame = conv->last_frame;
- conv->last_frame = pinfo->num;
- }
- }
- else {
+ conv = find_conversation(pinfo->num, &pinfo->src, &pinfo->dst, CONVERSATION_TCP, pinfo->srcport, pinfo->destport, 0);
+ if(!conv) {
conv = conversation_new(pinfo->num, &pinfo->src,
- &pinfo->dst, ENDPOINT_TCP,
+ &pinfo->dst, CONVERSATION_TCP,
pinfo->srcport, pinfo->destport, 0);
/* we need to know when a conversation is new then we initialize the completeness correctly */
conversation_is_new = TRUE;
@@ -6565,78 +7827,100 @@ dissect_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
/* If this is a SYN packet, then check if its seq-nr is different
* from the base_seq of the retrieved conversation. If this is the
* case, create a new conversation with the same addresses and ports
- * and set the TA_PORTS_REUSED flag. If the seq-nr is the same as
- * the base_seq, then restore some flow values to avoid buggy
- * analysis. In the latter case, it will also be marked as a
- * retransmission later.
+ * and set the TA_PORTS_REUSED flag. (XXX: There is a small chance
+ * that this is an old duplicate SYN received after the connection
+ * is ESTABLISHED on both sides, the other side will respond with
+ * an appropriate ACK, and this SYN ought to be ignored rather than
+ * create a new conversation.)
+ *
+ * If the seq-nr is the same as the base_seq, it might be a simple
+ * retransmission, reattempting a handshake that was reset (due
+ * to a half-open connection) with the same sequence number, or
+ * (unlikely) a new connection that happens to use the same sequence
+ * number as the previous one.
+ *
+ * If we have received a RST or FIN on the retrieved conversation,
+ * create a new conversation in order to clear out the follow info,
+ * sequence analysis, desegmentation, etc.
+ * If not, it's probably a retransmission, and will be marked
+ * as one later, but restore some flow values to reduce the
+ * sequence analysis warnings if our capture file is missing a RST
+ * or FIN segment that was present on the network.
+ *
* XXX - Is this affected by MPTCP which can use multiple SYNs?
*/
- if(tcpd && ((tcph->th_flags&(TH_SYN|TH_ACK))==TH_SYN) &&
- (tcpd->fwd->static_flags & TCP_S_BASE_SEQ_SET)) {
- if(tcph->th_seq!=tcpd->fwd->base_seq) {
- if (!(pinfo->fd->visited)) {
- /* Reset the last frame seen in the conversation */
- if (save_last_frame > 0)
- conv->last_frame = save_last_frame;
-
- conv=conversation_new(pinfo->num, &pinfo->src, &pinfo->dst, ENDPOINT_TCP, pinfo->srcport, pinfo->destport, 0);
- tcpd=get_tcp_conversation_data(conv,pinfo);
-
- if(!tcpd->ta)
- tcp_analyze_get_acked_struct(pinfo->num, tcph->th_seq, tcph->th_ack, TRUE, tcpd);
- tcpd->ta->flags|=TCP_A_REUSED_PORTS;
-
- /* As above, a new conversation starting with a SYN implies conversation completeness value 1 */
- tcpd->conversation_completeness = 1;
+ if (tcpd != NULL && (tcph->th_flags & (TH_SYN|TH_ACK)) == TH_SYN) {
+ if (tcpd->fwd->static_flags & TCP_S_BASE_SEQ_SET) {
+ if(tcph->th_seq!=tcpd->fwd->base_seq || (tcpd->conversation_completeness & TCP_COMPLETENESS_RST) || (tcpd->conversation_completeness & TCP_COMPLETENESS_FIN)) {
+ if (!(pinfo->fd->visited)) {
+
+ conv=conversation_new(pinfo->num, &pinfo->src, &pinfo->dst, CONVERSATION_TCP, pinfo->srcport, pinfo->destport, 0);
+ tcpd=get_tcp_conversation_data(conv,pinfo);
+
+ if(!tcpd->ta)
+ tcp_analyze_get_acked_struct(pinfo->num, tcph->th_seq, tcph->th_ack, TRUE, tcpd);
+ tcpd->ta->flags|=TCP_A_REUSED_PORTS;
+
+ /* As above, a new conversation starting with a SYN implies conversation completeness value 1 */
+ conversation_is_new = TRUE;
+ }
+ } else {
+ if (!(pinfo->fd->visited)) {
+ /*
+ * Sometimes we need to restore the nextseq value.
+ * As stated in RFC 793 3.4 a RST packet might be
+ * sent with SEQ being equal to the ACK received,
+ * thus breaking our flow monitoring. (issue 17616)
+ */
+ if(tcp_analyze_seq && tcpd->fwd->tcp_analyze_seq_info) {
+ tcpd->fwd->tcp_analyze_seq_info->nextseq = tcpd->fwd->tcp_analyze_seq_info->maxseqtobeacked;
+ }
+
+ if(!tcpd->ta)
+ tcp_analyze_get_acked_struct(pinfo->num, tcph->th_seq, tcph->th_ack, TRUE, tcpd);
+ }
}
}
else {
- if (!(pinfo->fd->visited)) {
- /*
- * Sometimes we need to restore the nextseq value.
- * As stated in RFC 793 3.4 a RST packet might be
- * sent with SEQ being equal to the ACK received,
- * thus breaking our flow monitoring. (issue 17616)
- */
- tcpd->fwd->tcp_analyze_seq_info->nextseq = tcpd->fwd->tcp_analyze_seq_info->maxseqtobeacked;
-
- if(!tcpd->ta)
- tcp_analyze_get_acked_struct(pinfo->num, tcph->th_seq, tcph->th_ack, TRUE, tcpd);
- tcpd->ta->flags|=TCP_A_REUSED_PORTS;
- }
+ /*
+ * TCP_S_BASE_SEQ_SET being not set, we are dealing with a new conversation,
+ * either created ad hoc above (general case), or by a higher protocol such as FTP.
+ * Track this information, as the Completeness value will be initialized later.
+ * See issue 19092.
+ */
+ if (!(pinfo->fd->visited))
+ conversation_is_new = TRUE;
}
+ tcpd->had_acc_ecn_setup_syn = (tcph->th_flags & (TH_AE|TH_CWR|TH_ECE)) == (TH_AE|TH_CWR|TH_ECE);
}
/* If this is a SYN/ACK packet, then check if its seq-nr is different
* from the base_seq of the retrieved conversation. If this is the
- * case, try to find a conversation with the same addresses and ports
- * and set the TA_PORTS_REUSED flag. If the seq-nr is the same as
- * the base_seq, then do nothing so it will be marked as a retrans-
- * mission later.
+ * case, set the TA_PORTS_REUSED flag and override the base seq.
+ * (XXX: Should this create a new conversation, as above with a
+ * SYN packet? We might have received the new connection's SYN/ACK before
+ * the SYN packet, or the SYN might be missing from the capture file.)
+ * If the seq-nr is the same as the base_seq, then do nothing so it
+ * will be marked as a retransmission later.
* XXX - Is this affected by MPTCP which can use multiple SYNs?
*/
- if(tcpd && ((tcph->th_flags&(TH_SYN|TH_ACK))==(TH_SYN|TH_ACK)) &&
- (tcpd->fwd->static_flags & TCP_S_BASE_SEQ_SET) &&
- (tcph->th_seq!=tcpd->fwd->base_seq) ) {
- if (!(pinfo->fd->visited)) {
- /* Reset the last frame seen in the conversation */
- if (save_last_frame > 0)
- conv->last_frame = save_last_frame;
- }
-
- other_conv = find_conversation(pinfo->num, &pinfo->dst, &pinfo->src, ENDPOINT_TCP, pinfo->destport, pinfo->srcport, 0);
- if (other_conv != NULL)
- {
- conv = other_conv;
- tcpd=get_tcp_conversation_data(conv,pinfo);
+ if (tcpd != NULL && (tcph->th_flags & (TH_SYN|TH_ACK)) == (TH_SYN|TH_ACK)) {
+ if ((tcpd->fwd->static_flags & TCP_S_BASE_SEQ_SET) &&
+ (tcph->th_seq != tcpd->fwd->base_seq)) {
/* the retrieved conversation might have a different base_seq (issue 16944) */
+ /* XXX: Shouldn't this create a new conversation? Changing the
+ * base_seq will change how the previous packets in the conversation
+ * are processed in the second pass.
+ */
tcpd->fwd->base_seq = tcph->th_seq;
- }
- if(!tcpd->ta)
- tcp_analyze_get_acked_struct(pinfo->num, tcph->th_seq, tcph->th_ack, TRUE, tcpd);
- tcpd->ta->flags|=TCP_A_REUSED_PORTS;
+ if(!tcpd->ta)
+ tcp_analyze_get_acked_struct(pinfo->num, tcph->th_seq, tcph->th_ack, TRUE, tcpd);
+ tcpd->ta->flags|=TCP_A_REUSED_PORTS;
+ }
+ tcpd->had_acc_ecn_setup_syn_ack = ((tcph->th_flags & (TH_AE|TH_CWR)) == TH_CWR) ||
+ ((tcph->th_flags & (TH_AE|TH_ECE)) == TH_AE);
}
if (tcpd) {
@@ -6644,7 +7928,23 @@ dissect_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
proto_item_set_generated(item);
/* Display the completeness of this TCP conversation */
- item = proto_tree_add_uint(tcp_tree, hf_tcp_completeness, NULL, 0, 0, tcpd->conversation_completeness);
+ static int* const completeness_fields[] = {
+ &hf_tcp_completeness_rst,
+ &hf_tcp_completeness_fin,
+ &hf_tcp_completeness_data,
+ &hf_tcp_completeness_ack,
+ &hf_tcp_completeness_syn_ack,
+ &hf_tcp_completeness_syn,
+ NULL};
+
+ item = proto_tree_add_bitmask_value_with_flags(tcp_tree, NULL, 0,
+ hf_tcp_completeness, ett_tcp_completeness, completeness_fields,
+ tcpd->conversation_completeness, BMT_NO_APPEND);
+ proto_item_set_generated(item);
+ field_tree = proto_item_add_subtree(item, ett_tcp_completeness);
+
+ flags_str_first_letter = tcpd->conversation_completeness_str;
+ item = proto_tree_add_string(field_tree, hf_tcp_completeness_str, tvb, 0, 0, flags_str_first_letter);
proto_item_set_generated(item);
/* Copy the stream index into the header as well to make it available
@@ -6670,6 +7970,12 @@ dissect_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
tcp_calculate_timestamps(pinfo, tcpd, tcppd);
}
+ /* is there any manual analysis waiting ? */
+ if(pinfo->fd->tcp_snd_manual_analysis > 0) {
+ tcppd = (struct tcp_per_packet_data_t *)p_get_proto_data(wmem_file_scope(), pinfo, proto_tcp, pinfo->curr_layer_num);
+ tcppd->tcp_snd_manual_analysis = pinfo->fd->tcp_snd_manual_analysis;
+ }
+
/*
* If we've been handed an IP fragment, we don't know how big the TCP
* segment is, so don't do anything that requires that we know that.
@@ -6700,13 +8006,53 @@ dissect_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
tcph->th_seglen = reported_len - tcph->th_hlen;
tcph->th_have_seglen = TRUE;
- pi = proto_tree_add_uint(ti, hf_tcp_len, tvb, offset+12, 1, tcph->th_seglen);
+ pi = proto_tree_add_uint(ti, hf_tcp_len, tvb, 0, 0, tcph->th_seglen);
proto_item_set_generated(pi);
+ /* initialize base_seq numbers */
+ if(!(pinfo->fd->visited) && tcpd) {
+ /* if this is the first segment for this list we need to store the
+ * base_seq
+ * We use TCP_S_SAW_SYN/SYNACK to distinguish between client and server
+ *
+ * Start relative seq and ack numbers at 1 if this
+ * is not a SYN packet. This makes the relative
+ * seq/ack numbers to be displayed correctly in the
+ * event that the SYN or SYN/ACK packet is not seen
+ * (this solves bug 1542)
+ */
+ if( !(tcpd->fwd->static_flags & TCP_S_BASE_SEQ_SET)) {
+ if(tcph->th_flags & TH_SYN) {
+ tcpd->fwd->base_seq = tcph->th_seq;
+ tcpd->fwd->static_flags |= (tcph->th_flags & TH_ACK) ? TCP_S_SAW_SYNACK : TCP_S_SAW_SYN;
+ }
+ else {
+ tcpd->fwd->base_seq = tcph->th_seq-1;
+ }
+ tcpd->fwd->static_flags |= TCP_S_BASE_SEQ_SET;
+ }
+
+ /* Only store reverse sequence if this isn't the SYN
+ * There's no guarantee that the ACK field of a SYN
+ * contains zeros; get the ISN from the first segment
+ * with the ACK bit set instead (usually the SYN/ACK).
+ *
+ * If the SYN and SYN/ACK were received out-of-order,
+ * the ISN is ack-1. If we missed the SYN/ACK, but got
+ * the last ACK of the 3WHS, the ISN is ack-1. For all
+ * other packets the ISN is unknown, so ack-1 is
+ * as good a guess as ack.
+ */
+ if( !(tcpd->rev->static_flags & TCP_S_BASE_SEQ_SET) && (tcph->th_flags & TH_ACK) ) {
+ tcpd->rev->base_seq = tcph->th_ack-1;
+ tcpd->rev->static_flags |= TCP_S_BASE_SEQ_SET;
+ }
+ }
+
/* handle TCP seq# analysis parse all new segments we see */
if(tcp_analyze_seq) {
if(!(pinfo->fd->visited)) {
- tcp_analyze_sequence_number(pinfo, tcph->th_seq, tcph->th_ack, tcph->th_seglen, tcph->th_flags, tcph->th_win, tcpd);
+ tcp_analyze_sequence_number(pinfo, tcph->th_seq, tcph->th_ack, tcph->th_seglen, tcph->th_flags, tcph->th_win, tcpd, tcppd);
}
if(tcpd && tcp_relative_seq) {
(tcph->th_seq) -= tcpd->fwd->base_seq;
@@ -6735,6 +8081,16 @@ dissect_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
} else
tcph->th_have_seglen = FALSE;
+ /*
+ * Decode the ECN related flags as ACE if it is not a SYN segment,
+ * and an AccECN-setup SYN and SYN ACK have been observed, or an
+ * AccECN option was observed (this covers the case where Wireshark
+ * did not observe the initial handshake).
+ */
+ tcph->th_use_ace = (tcph->th_flags & TH_SYN) == 0 &&
+ tcpd != NULL &&
+ ((tcpd->had_acc_ecn_setup_syn && tcpd->had_acc_ecn_setup_syn_ack) ||
+ tcpd->had_acc_ecn_option);
flags_str = tcp_flags_to_str(pinfo->pool, tcph);
flags_str_first_letter = tcp_flags_to_str_first_letter(pinfo->pool, tcph);
@@ -6754,7 +8110,16 @@ dissect_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
if (!icmp_ip) {
if(tcp_relative_seq && tcp_analyze_seq) {
proto_tree_add_uint_format_value(tcp_tree, hf_tcp_seq, tvb, offset + 4, 4, tcph->th_seq, "%u (relative sequence number)", tcph->th_seq);
- proto_tree_add_uint(tcp_tree, hf_tcp_seq_abs, tvb, offset + 4, 4, tcph->th_rawseq);
+ item = proto_tree_add_uint(tcp_tree, hf_tcp_seq_abs, tvb, offset + 4, 4, tcph->th_rawseq);
+ if (read_seq_as_syn_cookie) {
+ proto_item* syncookie_ti = NULL;
+ proto_item_append_text(item, " (syn cookie)");
+ syncookie_ti = proto_item_add_subtree(item, ett_tcp_syncookie);
+ proto_tree_add_bits_item(syncookie_ti, hf_tcp_syncookie_time, tvb, (offset + 4) * 8, 5, ENC_NA);
+ proto_tree_add_bits_item(syncookie_ti, hf_tcp_syncookie_mss, tvb, (offset + 4) * 8 + 5, 3, ENC_NA);
+ proto_tree_add_item(syncookie_ti, hf_tcp_syncookie_hash, tvb, offset + 4 + 1, 3, ENC_NA);
+ }
+
} else {
proto_tree_add_uint(tcp_tree, hf_tcp_seq, tvb, offset + 4, 4, tcph->th_seq);
hide_seqack_abs_item = proto_tree_add_uint(tcp_tree, hf_tcp_seq_abs, tvb, offset + 4, 4, tcph->th_rawseq);
@@ -6789,35 +8154,62 @@ dissect_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
}
}
else {
+ /* Explicitly and immediately move forward the conversation last_frame,
+ * although it would one way or another be changed later
+ * in the conversation helper functions.
+ */
+ if (!(pinfo->fd->visited)) {
+ if (pinfo->num > conv->last_frame) {
+ conv->last_frame = pinfo->num;
+ }
+ }
+
conversation_completeness = tcpd->conversation_completeness ;
+ }
- /* SYN-ACK */
- if((tcph->th_flags&(TH_SYN|TH_ACK))==(TH_SYN|TH_ACK)) {
- conversation_completeness |= TCP_COMPLETENESS_SYNACK;
- }
+ /* SYN-ACK */
+ if((tcph->th_flags&(TH_SYN|TH_ACK))==(TH_SYN|TH_ACK)) {
+ conversation_completeness |= TCP_COMPLETENESS_SYNACK;
+ }
- /* ACKs */
- if((tcph->th_flags&(TH_SYN|TH_ACK))==(TH_ACK)) {
- if(tcph->th_seglen>0) { /* transporting some data */
- conversation_completeness |= TCP_COMPLETENESS_DATA;
- }
- else { /* pure ACK */
- conversation_completeness |= TCP_COMPLETENESS_ACK;
- }
+ /* ACKs */
+ if((tcph->th_flags&(TH_SYN|TH_ACK))==(TH_ACK)) {
+ if(tcph->th_seglen>0) { /* transporting some data */
+ conversation_completeness |= TCP_COMPLETENESS_DATA;
}
-
- /* FIN-ACK */
- if((tcph->th_flags&(TH_FIN|TH_ACK))==(TH_FIN|TH_ACK)) {
- conversation_completeness |= TCP_COMPLETENESS_FIN;
+ else { /* pure ACK */
+ conversation_completeness |= TCP_COMPLETENESS_ACK;
}
+ }
+
+ /* FIN-ACK */
+ if((tcph->th_flags&(TH_FIN|TH_ACK))==(TH_FIN|TH_ACK)) {
+ conversation_completeness |= TCP_COMPLETENESS_FIN;
+ }
+
+ /* RST */
+ /* XXX: A RST segment should be validated (RFC 9293 3.5.3),
+ * and if not valid should not change the conversation state.
+ */
+ if(tcph->th_flags&(TH_RST)) {
+ conversation_completeness |= TCP_COMPLETENESS_RST;
+ }
- /* RST */
- if(tcph->th_flags&(TH_RST)) {
- conversation_completeness |= TCP_COMPLETENESS_RST;
+ /* Store the completeness at the conversation level,
+ * both as numerical and as Flag First Letters string, to avoid
+ * computing many times the same thing.
+ */
+ if (tcpd->conversation_completeness) {
+ if (tcpd->conversation_completeness != conversation_completeness) {
+ tcpd->conversation_completeness = conversation_completeness;
+ tcpd->conversation_completeness_str = completeness_flags_to_str_first_letter(wmem_file_scope(), tcpd->conversation_completeness) ;
}
}
+ else {
+ tcpd->conversation_completeness = conversation_completeness;
+ tcpd->conversation_completeness_str = completeness_flags_to_str_first_letter(wmem_file_scope(), tcpd->conversation_completeness) ;
+ }
}
- tcpd->conversation_completeness = conversation_completeness;
if (tcp_summary_in_tree) {
if(tcph->th_flags&TH_ACK) {
@@ -6875,9 +8267,19 @@ dissect_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
tcph->th_flags, "Flags: 0x%03x (%s)", tcph->th_flags, flags_str);
field_tree = proto_item_add_subtree(tf, ett_tcp_flags);
proto_tree_add_boolean(field_tree, hf_tcp_flags_res, tvb, offset + 12, 1, tcph->th_flags);
- proto_tree_add_boolean(field_tree, hf_tcp_flags_ns, tvb, offset + 12, 1, tcph->th_flags);
- proto_tree_add_boolean(field_tree, hf_tcp_flags_cwr, tvb, offset + 13, 1, tcph->th_flags);
- proto_tree_add_boolean(field_tree, hf_tcp_flags_ecn, tvb, offset + 13, 1, tcph->th_flags);
+ if (tcph->th_use_ace) {
+ ace = tcp_get_ace(tcph);
+ proto_tree_add_uint_format(field_tree, hf_tcp_flags_ace, tvb, 12, 2, ace,
+ "...%c %c%c.. .... = ACE: %u",
+ ace & 0x04 ? '1' : '0',
+ ace & 0x02 ? '1' : '0',
+ ace & 0x01 ? '1' : '0',
+ ace);
+ } else {
+ proto_tree_add_boolean(field_tree, hf_tcp_flags_ae, tvb, offset + 12, 1, tcph->th_flags);
+ proto_tree_add_boolean(field_tree, hf_tcp_flags_cwr, tvb, offset + 13, 1, tcph->th_flags);
+ proto_tree_add_boolean(field_tree, hf_tcp_flags_ece, tvb, offset + 13, 1, tcph->th_flags);
+ }
proto_tree_add_boolean(field_tree, hf_tcp_flags_urg, tvb, offset + 13, 1, tcph->th_flags);
proto_tree_add_boolean(field_tree, hf_tcp_flags_ack, tvb, offset + 13, 1, tcph->th_flags);
proto_tree_add_boolean(field_tree, hf_tcp_flags_push, tvb, offset + 13, 1, tcph->th_flags);
@@ -7042,8 +8444,12 @@ dissect_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
DISSECTOR_ASSERT_NOT_REACHED();
break;
}
+ /* See discussion in packet-udp.c of partial checksums used in
+ * checksum offloading in Linux and Windows (and possibly others.)
+ */
+ uint16_t partial_cksum;
SET_CKSUM_VEC_TVB(cksum_vec[3], tvb, offset, reported_len);
- computed_cksum = in_cksum(cksum_vec, 4);
+ computed_cksum = in_cksum_ret_partial(cksum_vec, 4, &partial_cksum);
if (computed_cksum == 0 && th_sum == 0xffff) {
item = proto_tree_add_uint_format_value(tcp_tree, hf_tcp_checksum, tvb,
offset + 16, 2, th_sum,
@@ -7065,11 +8471,23 @@ dissect_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
desegment_ok = TRUE;
} else {
proto_item* calc_item;
- item = proto_tree_add_checksum(tcp_tree, tvb, offset+16, hf_tcp_checksum, hf_tcp_checksum_status, &ei_tcp_checksum_bad, pinfo, computed_cksum,
- ENC_BIG_ENDIAN, PROTO_CHECKSUM_VERIFY|PROTO_CHECKSUM_IN_CKSUM);
-
- calc_item = proto_tree_add_uint(tcp_tree, hf_tcp_checksum_calculated, tvb,
- offset + 16, 2, in_cksum_shouldbe(th_sum, computed_cksum));
+ uint16_t shouldbe_cksum = in_cksum_shouldbe(th_sum, computed_cksum);
+ if (computed_cksum != 0 && th_sum == g_htons(partial_cksum)) {
+ /* Don't use PROTO_CHECKSUM_IN_CKSUM because we expect the value
+ * to match what we pass in. */
+ item = proto_tree_add_checksum(tcp_tree, tvb, offset+16, hf_tcp_checksum, hf_tcp_checksum_status, &ei_tcp_checksum_bad, pinfo, g_htons(partial_cksum),
+ ENC_BIG_ENDIAN, PROTO_CHECKSUM_VERIFY);
+ proto_item_append_text(item, " (matches partial checksum, not 0x%04x, likely caused by \"TCP checksum offload\")", shouldbe_cksum);
+ expert_add_info(pinfo, item, &ei_tcp_checksum_partial);
+ computed_cksum = 0;
+ /* XXX Add a new status, e.g. PROTO_CHECKSUM_E_PARTIAL? */
+ } else {
+ item = proto_tree_add_checksum(tcp_tree, tvb, offset+16, hf_tcp_checksum, hf_tcp_checksum_status, &ei_tcp_checksum_bad, pinfo, computed_cksum,
+ ENC_BIG_ENDIAN, PROTO_CHECKSUM_VERIFY|PROTO_CHECKSUM_IN_CKSUM);
+ }
+ checksum_tree = proto_item_add_subtree(item, ett_tcp_checksum);
+ calc_item = proto_tree_add_uint(checksum_tree, hf_tcp_checksum_calculated, tvb,
+ offset + 16, 2, shouldbe_cksum);
proto_item_set_generated(calc_item);
/* Checksum is valid, so we're willing to desegment it. */
@@ -7165,8 +8583,8 @@ dissect_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
rvbd_option_data* option_data;
tcp_dissect_options(tvb, offset + 20, optlen,
- TCPOPT_EOL, pinfo, options_tree,
- options_item, tcph);
+ pinfo, options_tree,
+ options_item, tcph);
/* Do some post evaluation of some Riverbed probe options in the list */
option_data = (rvbd_option_data*)p_get_proto_data(pinfo->pool, pinfo, proto_tcp_option_rvbd_probe, pinfo->curr_layer_num);
@@ -7257,6 +8675,7 @@ dissect_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
* need to explicitly check for that here.
*/
if(tcph->th_have_seglen && tcpd && (tcph->th_flags & TH_FIN)
+ && pinfo->can_desegment
&& (tcpd->fwd->flags&TCP_FLOW_REASSEMBLE_UNTIL_FIN) ) {
struct tcp_multisegment_pdu *msp;
@@ -7269,16 +8688,16 @@ dissect_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
* for this flow, terminate reassembly and dissect the
* results. */
tcpd->fwd->fin = pinfo->num;
- msp=(struct tcp_multisegment_pdu *)wmem_tree_lookup32_le(tcpd->fwd->multisegment_pdus, tcph->th_seq-1);
+ msp=(struct tcp_multisegment_pdu *)wmem_tree_lookup32_le(tcpd->fwd->multisegment_pdus, tcph->th_seq);
if(msp) {
fragment_head *ipfd_head;
ipfd_head = fragment_add(&tcp_reassembly_table, tvb, offset,
- pinfo, msp->first_frame, NULL,
+ pinfo, msp->first_frame, msp,
tcph->th_seq - msp->seq,
tcph->th_seglen,
FALSE );
- if(ipfd_head) {
+ if(ipfd_head && ipfd_head->reassembled_in == pinfo->num && ipfd_head->reas_in_layer_num == pinfo->curr_layer_num) {
tvbuff_t *next_tvb;
/* create a new TVB structure for desegmented data
@@ -7407,6 +8826,40 @@ proto_register_tcp(void)
BASE_CUSTOM, CF_FUNC(conversation_completeness_fill), 0x0,
"The completeness of the conversation capture", HFILL }},
+ { &hf_tcp_completeness_syn,
+ { "SYN", "tcp.completeness.syn", FT_BOOLEAN, 8,
+ TFS(&tfs_present_absent), TCP_COMPLETENESS_SYNSENT,
+ "Conversation has a SYN packet", HFILL}},
+
+ { &hf_tcp_completeness_syn_ack,
+ { "SYN-ACK", "tcp.completeness.syn-ack", FT_BOOLEAN, 8,
+ TFS(&tfs_present_absent), TCP_COMPLETENESS_SYNACK,
+ "Conversation has a SYN-ACK packet", HFILL}},
+
+ { &hf_tcp_completeness_ack,
+ { "ACK", "tcp.completeness.ack", FT_BOOLEAN, 8,
+ TFS(&tfs_present_absent), TCP_COMPLETENESS_ACK,
+ "Conversation has an ACK packet", HFILL}},
+
+ { &hf_tcp_completeness_data,
+ { "Data", "tcp.completeness.data", FT_BOOLEAN, 8,
+ TFS(&tfs_present_absent), TCP_COMPLETENESS_DATA,
+ "Conversation has payload DATA", HFILL}},
+
+ { &hf_tcp_completeness_fin,
+ { "FIN", "tcp.completeness.fin", FT_BOOLEAN, 8,
+ TFS(&tfs_present_absent), TCP_COMPLETENESS_FIN,
+ "Conversation has a FIN packet", HFILL}},
+
+ { &hf_tcp_completeness_rst,
+ { "RST", "tcp.completeness.rst", FT_BOOLEAN, 8,
+ TFS(&tfs_present_absent), TCP_COMPLETENESS_RST,
+ "Conversation has a RST packet", HFILL}},
+
+ { &hf_tcp_completeness_str,
+ { "Completeness Flags", "tcp.completeness.str", FT_STRING, BASE_NONE, NULL, 0x0,
+ NULL, HFILL }},
+
{ &hf_tcp_seq,
{ "Sequence Number", "tcp.seq", FT_UINT32, BASE_DEC, NULL, 0x0,
NULL, HFILL }},
@@ -7441,16 +8894,20 @@ proto_register_tcp(void)
{ "Reserved", "tcp.flags.res", FT_BOOLEAN, 12, TFS(&tfs_set_notset), TH_RES,
"Three reserved bits (must be zero)", HFILL }},
- { &hf_tcp_flags_ns,
- { "Nonce", "tcp.flags.ns", FT_BOOLEAN, 12, TFS(&tfs_set_notset), TH_NS,
- "ECN concealment protection (RFC 3540)", HFILL }},
+ { &hf_tcp_flags_ae,
+ { "Accurate ECN", "tcp.flags.ae", FT_BOOLEAN, 12, TFS(&tfs_set_notset), TH_AE,
+ NULL, HFILL }},
{ &hf_tcp_flags_cwr,
- { "Congestion Window Reduced (CWR)", "tcp.flags.cwr", FT_BOOLEAN, 12, TFS(&tfs_set_notset), TH_CWR,
+ { "Congestion Window Reduced", "tcp.flags.cwr", FT_BOOLEAN, 12, TFS(&tfs_set_notset), TH_CWR,
+ NULL, HFILL }},
+
+ { &hf_tcp_flags_ece,
+ { "ECN-Echo", "tcp.flags.ece", FT_BOOLEAN, 12, TFS(&tfs_set_notset), TH_ECE,
NULL, HFILL }},
- { &hf_tcp_flags_ecn,
- { "ECN-Echo", "tcp.flags.ecn", FT_BOOLEAN, 12, TFS(&tfs_set_notset), TH_ECN,
+ { &hf_tcp_flags_ace,
+ { "ACE", "tcp.flags.ace", FT_UINT8, BASE_DEC, NULL, 0x0,
NULL, HFILL }},
{ &hf_tcp_flags_urg,
@@ -7478,7 +8935,7 @@ proto_register_tcp(void)
NULL, HFILL }},
{ &hf_tcp_flags_str,
- { "TCP Flags", "tcp.flags.str", FT_STRING, STR_UNICODE, NULL, 0x0,
+ { "TCP Flags", "tcp.flags.str", FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL }},
{ &hf_tcp_window_size_value,
@@ -7638,9 +9095,9 @@ proto_register_tcp(void)
{ "Data", "tcp.options.experimental.data", FT_BYTES,
BASE_NONE, NULL, 0x0, NULL, HFILL}},
- { &hf_tcp_option_exp_magic_number,
- { "Magic Number", "tcp.options.experimental.magic_number", FT_UINT16,
- BASE_HEX, NULL, 0x0, NULL, HFILL}},
+ { &hf_tcp_option_exp_exid,
+ { "Experiment Identifier", "tcp.options.experimental.exid", FT_UINT16,
+ BASE_HEX, &tcp_exid_vs, 0x0, NULL, HFILL}},
{ &hf_tcp_option_unknown_payload,
{ "Payload", "tcp.options.unknown.payload", FT_BYTES,
@@ -7866,6 +9323,29 @@ proto_register_tcp(void)
{ "QS Rate", "tcp.options.qs.ttl_diff", FT_UINT8, BASE_DEC,
NULL, 0x0, NULL, HFILL}},
+ { &hf_tcp_option_tarr_rate,
+ { "TARR Rate", "tcp.options.tarr.rate", FT_UINT8, BASE_DEC,
+ NULL, TCPOPT_TARR_RATE_MASK, NULL, HFILL}},
+
+ { &hf_tcp_option_tarr_reserved,
+ { "TARR Reserved", "tcp.options.tar.reserved", FT_UINT8, BASE_DEC,
+ NULL, TCPOPT_TARR_RESERVED_MASK, NULL, HFILL}},
+
+ { &hf_tcp_option_acc_ecn_ee0b,
+ { "Accurate ECN Echo ECT(0) Byte Counter", "tcp.options.acc_ecn.ee0b",
+ FT_UINT24, BASE_DEC, NULL, 0x0,
+ NULL, HFILL}},
+
+ { &hf_tcp_option_acc_ecn_eceb,
+ { "Accurate ECN Echo CE Byte Counter", "tcp.options.acc_ecn.eceb",
+ FT_UINT24, BASE_DEC, NULL, 0x0,
+ NULL, HFILL}},
+
+ { &hf_tcp_option_acc_ecn_ee1b,
+ { "Accurate ECN Echo ECT(1) Byte Counter", "tcp.options.acc_ecn.ee1b",
+ FT_UINT24, BASE_DEC, NULL, 0x0,
+ NULL, HFILL}},
+
{ &hf_tcp_option_scps_vector,
{ "TCP SCPS Capabilities Vector", "tcp.options.scps.vector",
FT_UINT8, BASE_HEX, NULL, 0x0,
@@ -8139,7 +9619,7 @@ proto_register_tcp(void)
{ &hf_tcp_segment_data,
{ "TCP segment data", "tcp.segment_data", FT_BYTES, BASE_NONE, NULL, 0x0,
- "A data segment used in reassembly of a lower-level protocol", HFILL}},
+ "A data segment used in reassembly of an upper-layer protocol (ULP)", HFILL}},
{ &hf_tcp_payload,
{ "TCP payload", "tcp.payload", FT_BYTES, BASE_NONE, NULL, 0x0,
@@ -8160,10 +9640,39 @@ proto_register_tcp(void)
{ &hf_tcp_reset_cause,
{ "Reset cause", "tcp.reset_cause", FT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL }},
+
+ { &hf_tcp_syncookie_time,
+ { "SYN Cookie Time", "tcp.syncookie.time", FT_UINT8, BASE_DEC, NULL, 0x0,
+ NULL, HFILL }},
+
+ { &hf_tcp_syncookie_mss,
+ { "SYN Cookie Maximum Segment Size", "tcp.syncookie.mss", FT_UINT8, BASE_DEC, NULL, 0x0,
+ NULL, HFILL }},
+
+ { &hf_tcp_syncookie_hash,
+ { "SYN Cookie hash", "tcp.syncookie.hash", FT_UINT24, BASE_HEX, NULL, 0x0,
+ NULL, HFILL }},
+
+ { &hf_tcp_syncookie_option_timestamp,
+ { "SYN Cookie Timestamp", "tcp.options.timestamp.tsval.syncookie.timestamp", FT_UINT32, BASE_DEC, NULL, 0x0,
+ NULL, HFILL }},
+
+ { &hf_tcp_syncookie_option_ecn,
+ { "SYN Cookie ECN", "tcp.options.timestamp.tsval.syncookie.ecn", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
+ NULL, HFILL }},
+
+ { &hf_tcp_syncookie_option_sack,
+ { "SYN Cookie SACK", "tcp.options.timestamp.tsval.syncookie.sack", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
+ NULL, HFILL }},
+
+ { &hf_tcp_syncookie_option_wscale,
+ { "SYN Cookie WScale", "tcp.options.timestamp.tsval.syncookie.wscale", FT_UINT8, BASE_DEC, NULL, 0x0,
+ NULL, HFILL }},
};
static gint *ett[] = {
&ett_tcp,
+ &ett_tcp_completeness,
&ett_tcp_flags,
&ett_tcp_options,
&ett_tcp_option_timestamp,
@@ -8176,6 +9685,7 @@ proto_register_tcp(void)
&ett_tcp_option_scps_extended,
&ett_tcp_option_user_to,
&ett_tcp_option_exp,
+ &ett_tcp_option_acc_ecn,
&ett_tcp_option_sack_perm,
&ett_tcp_option_mss,
&ett_tcp_opt_rvbd_probe,
@@ -8197,7 +9707,9 @@ proto_register_tcp(void)
&ett_tcp_unknown_opt,
&ett_tcp_opt_recbound,
&ett_tcp_opt_scpscor,
- &ett_tcp_option_other
+ &ett_tcp_option_other,
+ &ett_tcp_syncookie,
+ &ett_tcp_syncookie_option
};
static gint *mptcp_ett[] = {
@@ -8225,6 +9737,15 @@ proto_register_tcp(void)
{NULL, NULL, -1}
};
+ static const enum_val_t override_analysis_vals[] = {
+ {"0", "0 (none)", OverrideAnalysis_0},
+ {"1", "1 (Out-of-Order)", OverrideAnalysis_1},
+ {"2", "2 (Retransmission)", OverrideAnalysis_2},
+ {"3", "3 (Fast Retransmission)", OverrideAnalysis_3},
+ {"4", "4 (Spurious Retransmission)",OverrideAnalysis_4},
+ {NULL, NULL, -1}
+ };
+
static ei_register_info ei[] = {
{ &ei_tcp_opt_len_invalid, { "tcp.option.len.invalid", PI_SEQUENCE, PI_NOTE, "Invalid length for option", EXPFILL }},
{ &ei_tcp_analysis_retransmission, { "tcp.analysis.retransmission", PI_SEQUENCE, PI_NOTE, "This frame is a (suspected) retransmission", EXPFILL }},
@@ -8253,6 +9774,8 @@ proto_register_tcp(void)
{ &ei_tcp_option_wscale_shift_invalid, { "tcp.options.wscale.shift.invalid", PI_PROTOCOL, PI_WARN, "Window scale shift exceeds 14", EXPFILL }},
{ &ei_tcp_option_mss_absent, { "tcp.options.mss.absent", PI_PROTOCOL, PI_NOTE, "The SYN packet does not contain a MSS option", EXPFILL }},
{ &ei_tcp_option_mss_present, { "tcp.options.mss.present", PI_PROTOCOL, PI_WARN, "The non-SYN packet does contain a MSS option", EXPFILL }},
+ { &ei_tcp_option_sack_perm_absent, { "tcp.options.sack_perm.absent", PI_PROTOCOL, PI_NOTE, "The SYN packet does not contain a SACK PERM option", EXPFILL }},
+ { &ei_tcp_option_sack_perm_present, { "tcp.options.sack_perm.present", PI_PROTOCOL, PI_WARN, "The non-SYN packet does contain a SACK PERM option", EXPFILL }},
{ &ei_tcp_short_segment, { "tcp.short_segment", PI_MALFORMED, PI_WARN, "Short segment", EXPFILL }},
{ &ei_tcp_ack_nonzero, { "tcp.ack.nonzero", PI_PROTOCOL, PI_NOTE, "The acknowledgment number field is nonzero while the ACK flag is not set", EXPFILL }},
{ &ei_tcp_connection_synack, { "tcp.connection.synack", PI_SEQUENCE, PI_CHAT, "Connection establish acknowledge (SYN+ACK)", EXPFILL }},
@@ -8263,10 +9786,12 @@ proto_register_tcp(void)
*/
{ &ei_tcp_connection_rst, { "tcp.connection.rst", PI_SEQUENCE, PI_WARN, "Connection reset (RST)", EXPFILL }},
{ &ei_tcp_checksum_ffff, { "tcp.checksum.ffff", PI_CHECKSUM, PI_WARN, "TCP Checksum 0xffff instead of 0x0000 (see RFC 1624)", EXPFILL }},
+ { &ei_tcp_checksum_partial, { "tcp.checksum.partial", PI_CHECKSUM, PI_NOTE, "Partial (pseudo header) checksum (likely caused by \"TCP checksum offload\")", EXPFILL }},
{ &ei_tcp_checksum_bad, { "tcp.checksum_bad.expert", PI_CHECKSUM, PI_ERROR, "Bad checksum", EXPFILL }},
{ &ei_tcp_urgent_pointer_non_zero, { "tcp.urgent_pointer.non_zero", PI_PROTOCOL, PI_NOTE, "The urgent pointer field is nonzero while the URG flag is not set", EXPFILL }},
{ &ei_tcp_suboption_malformed, { "tcp.suboption_malformed", PI_MALFORMED, PI_ERROR, "suboption would go past end of option", EXPFILL }},
{ &ei_tcp_nop, { "tcp.nop", PI_PROTOCOL, PI_WARN, "4 NOP in a row - a router may have removed some options", EXPFILL }},
+ { &ei_tcp_non_zero_bytes_after_eol, { "tcp.non_zero_bytes_after_eol", PI_PROTOCOL, PI_ERROR, "Non zero bytes in option space after EOL option", EXPFILL }},
{ &ei_tcp_bogus_header_length, { "tcp.bogus_header_length", PI_PROTOCOL, PI_ERROR, "Bogus TCP Header length", EXPFILL }},
};
@@ -8358,6 +9883,7 @@ proto_register_tcp(void)
proto_tcp = proto_register_protocol("Transmission Control Protocol", "TCP", "tcp");
tcp_handle = register_dissector("tcp", dissect_tcp, proto_tcp);
+ tcp_cap_handle = register_capture_dissector("tcp", capture_tcp, proto_tcp);
proto_register_field_array(proto_tcp, hf, array_length(hf));
proto_register_subtree_array(ett, array_length(ett));
expert_tcp = expert_register_protocol(proto_tcp);
@@ -8366,7 +9892,7 @@ proto_register_tcp(void)
/* subdissector code */
subdissector_table = register_dissector_table("tcp.port",
"TCP port", proto_tcp, FT_UINT16, BASE_DEC);
- heur_subdissector_list = register_heur_dissector_list("tcp", proto_tcp);
+ heur_subdissector_list = register_heur_dissector_list_with_description("tcp", "TCP heuristic", proto_tcp);
tcp_option_table = register_dissector_table("tcp.option",
"TCP Options", proto_tcp, FT_UINT8, BASE_DEC);
@@ -8392,6 +9918,7 @@ proto_register_tcp(void)
proto_tcp_option_qs = proto_register_protocol_in_name_only("TCP Option - Quick-Start", "Quick-Start", "tcp.options.qs", proto_tcp, FT_BYTES);
proto_tcp_option_user_to = proto_register_protocol_in_name_only("TCP Option - User Timeout", "User Timeout", "tcp.options.user_to", proto_tcp, FT_BYTES);
proto_tcp_option_tfo = proto_register_protocol_in_name_only("TCP Option - TCP Fast Open", "TCP Fast Open", "tcp.options.tfo", proto_tcp, FT_BYTES);
+ proto_tcp_option_acc_ecn = proto_register_protocol_in_name_only("TCP Option - Accurate ECN", "Accurate ECN", "tcp.options.acc_ecn", proto_tcp, FT_BYTES);
proto_tcp_option_rvbd_probe = proto_register_protocol_in_name_only("TCP Option - Riverbed Probe", "Riverbed Probe", "tcp.options.rvbd.probe", proto_tcp, FT_BYTES);
proto_tcp_option_rvbd_trpy = proto_register_protocol_in_name_only("TCP Option - Riverbed Transparency", "Riverbed Transparency", "tcp.options.rvbd.trpy", proto_tcp, FT_BYTES);
proto_tcp_option_exp = proto_register_protocol_in_name_only("TCP Option - Experimental", "Experimental", "tcp.options.experimental", proto_tcp, FT_BYTES);
@@ -8428,6 +9955,12 @@ proto_register_tcp(void)
"Make the TCP dissector use relative sequence numbers instead of absolute ones. "
"To use this option you must also enable \"Analyze TCP sequence numbers\". ",
&tcp_relative_seq);
+
+ prefs_register_custom_preference_TCP_Analysis(tcp_module, "default_override_analysis",
+ "Force interpretation to selected packet(s)",
+ "Override the default analysis with this value for the selected packet",
+ &tcp_default_override_analysis, override_analysis_vals, FALSE);
+
prefs_register_enum_preference(tcp_module, "default_window_scaling",
"Scaling factor to use when not available from capture",
"Make the TCP dissector use this scaling factor for streams where the signalled scaling factor "
@@ -8470,24 +10003,29 @@ proto_register_tcp(void)
"Do not call any subdissectors for Retransmitted or OutOfOrder segments",
&tcp_no_subdissector_on_error);
- prefs_register_bool_preference(tcp_module, "dissect_experimental_options_with_magic",
- "TCP Experimental Options with a Magic Number",
- "Assume TCP Experimental Options (253, 254) have a Magic Number and use it for dissection",
- &tcp_exp_options_with_magic);
+ prefs_register_bool_preference(tcp_module, "dissect_experimental_options_rfc6994",
+ "TCP Experimental Options using the format of RFC 6994",
+ "Assume TCP Experimental Options (253, 254) have an Experiment Identifier and use it for dissection",
+ &tcp_exp_options_rfc6994);
prefs_register_bool_preference(tcp_module, "display_process_info_from_ipfix",
"Display process information via IPFIX",
"Collect and store process information retrieved from IPFIX dissector",
&tcp_display_process_info);
+ prefs_register_bool_preference(tcp_module, "read_seq_as_syn_cookie",
+ "Read the seq no. as syn cookie",
+ "Read the sequence number as it was a syn cookie",
+ &read_seq_as_syn_cookie);
+
register_init_routine(tcp_init);
reassembly_table_register(&tcp_reassembly_table,
- &addresses_ports_reassembly_table_functions);
+ &tcp_reassembly_table_functions);
register_decode_as(&tcp_da);
- register_conversation_table(proto_tcp, FALSE, tcpip_conversation_packet, tcpip_hostlist_packet);
- register_conversation_filter("tcp", "TCP", tcp_filter_valid, tcp_build_filter);
+ register_conversation_table(proto_tcp, FALSE, tcpip_conversation_packet, tcpip_endpoint_packet);
+ register_conversation_filter("tcp", "TCP", tcp_filter_valid, tcp_build_filter, NULL);
register_seq_analysis("tcp", "TCP Flows", proto_tcp, NULL, 0, tcp_seq_analysis_packet);
@@ -8526,16 +10064,14 @@ proto_register_tcp(void)
"You need to enable DSS mapping analysis for this option to work",
&mptcp_intersubflows_retransmission);
- register_conversation_table(proto_mptcp, FALSE, mptcpip_conversation_packet, tcpip_hostlist_packet);
+ register_conversation_table(proto_mptcp, FALSE, mptcpip_conversation_packet, tcpip_endpoint_packet);
register_follow_stream(proto_tcp, "tcp_follow", tcp_follow_conv_filter, tcp_follow_index_filter, tcp_follow_address_filter,
- tcp_port_to_display, follow_tcp_tap_listener);
+ tcp_port_to_display, follow_tcp_tap_listener, get_tcp_stream_count, NULL);
}
void
proto_reg_handoff_tcp(void)
{
- capture_dissector_handle_t tcp_cap_handle;
-
dissector_add_uint("ip.proto", IP_PROTO_TCP, tcp_handle);
dissector_add_for_decode_as_with_preference("udp.port", tcp_handle);
data_handle = find_dissector("data");
@@ -8543,7 +10079,6 @@ proto_reg_handoff_tcp(void)
tcp_tap = register_tap("tcp");
tcp_follow_tap = register_tap("tcp_follow");
- tcp_cap_handle = create_capture_dissector_handle(capture_tcp, proto_tcp);
capture_dissector_add_uint("ip.proto", IP_PROTO_TCP, tcp_cap_handle);
/* Create dissection function handles for all TCP options */
@@ -8568,6 +10103,8 @@ proto_reg_handoff_tcp(void)
dissector_add_uint("tcp.option", TCPOPT_TFO, create_dissector_handle( dissect_tcpopt_tfo, proto_tcp_option_tfo ));
dissector_add_uint("tcp.option", TCPOPT_RVBD_PROBE, create_dissector_handle( dissect_tcpopt_rvbd_probe, proto_tcp_option_rvbd_probe ));
dissector_add_uint("tcp.option", TCPOPT_RVBD_TRPY, create_dissector_handle( dissect_tcpopt_rvbd_trpy, proto_tcp_option_rvbd_trpy ));
+ dissector_add_uint("tcp.option", TCPOPT_ACC_ECN_0, create_dissector_handle( dissect_tcpopt_acc_ecn, proto_tcp_option_acc_ecn ));
+ dissector_add_uint("tcp.option", TCPOPT_ACC_ECN_1, create_dissector_handle( dissect_tcpopt_acc_ecn, proto_tcp_option_acc_ecn ));
dissector_add_uint("tcp.option", TCPOPT_EXP_FD, create_dissector_handle( dissect_tcpopt_exp, proto_tcp_option_exp ));
dissector_add_uint("tcp.option", TCPOPT_EXP_FE, create_dissector_handle( dissect_tcpopt_exp, proto_tcp_option_exp ));
dissector_add_uint("tcp.option", TCPOPT_MPTCP, create_dissector_handle( dissect_tcpopt_mptcp, proto_mptcp ));