From 2f235deb3ed3cf4909b79e9fd3e0902f220ef69f Mon Sep 17 00:00:00 2001 From: Eugene Adell Date: Sun, 19 Jul 2020 20:23:38 +0200 Subject: [PATCH] TCP: Add support for TCP Conversation Completeness tracking TCP Conversations are modified to track Completeness, particularly initial and closing handshakes Fixes #16716 --- docbook/release-notes.adoc | 3 + docbook/wsug_src/WSUG_chapter_advanced.adoc | 22 ++++ epan/dissectors/packet-tcp.c | 133 ++++++++++++++++++++ epan/dissectors/packet-tcp.h | 7 ++ 4 files changed, 165 insertions(+) diff --git a/docbook/release-notes.adoc b/docbook/release-notes.adoc index 4ffc9a6fb7..2cbcef5628 100644 --- a/docbook/release-notes.adoc +++ b/docbook/release-notes.adoc @@ -31,6 +31,9 @@ See the “New and Updated Features” section below for more details. The following features are new (or have been significantly updated) since version 3.4.0: +* TCP conversations now support a completeness criteria, which facilitates the identification of TCP streams having any + of opening or closing handshakes, a payload, in any combination. It is accessed with the new tcp.completeness filter. + * Protobuf fields that are not serialized on the wire (missing in capture files) can now be displayed with default values by setting the new 'add_default_value' preference. The default values might be explicitly declared in 'proto2' files, or false for bools, first value for enums, zero for numeric types. diff --git a/docbook/wsug_src/WSUG_chapter_advanced.adoc b/docbook/wsug_src/WSUG_chapter_advanced.adoc index 561d7c2941..161f087902 100644 --- a/docbook/wsug_src/WSUG_chapter_advanced.adoc +++ b/docbook/wsug_src/WSUG_chapter_advanced.adoc @@ -608,6 +608,28 @@ Set when the all of the following are true: Supersedes “TCP Dup ACK”. +// TCP_A_CONVERSATION_COMPLETENESS +[discrete] +==== TCP Conversation Completeness + +TCP conversations are said to be complete when they have both opening and closing +handshakes, independently of any data transfer. However we might be interested in +identifying complete conversations with some data sent, and we are using the +following bit values to build a filter value on the tcp.completeness field : + +* 1 : SYN +* 2 : SYN-ACK +* 4 : ACK +* 8 : DATA +* 16 : FIN +* 32 : RST + +For example, a conversation containing only a three-way handshake will be found +with the filter 'tcp.completeness==7' (1+2+4) while a complete conversation with +data transfer will be found with a longer filter as closing a connection can be +associated with FIN or RST packets, or even both : +'tcp.completeness==31 or tcp.completeness==47 tcp.completeness==63' + [[ChAdvTimestamps]] === Time Stamps diff --git a/epan/dissectors/packet-tcp.c b/epan/dissectors/packet-tcp.c index ae5a7b557f..6bc1915e82 100644 --- a/epan/dissectors/packet-tcp.c +++ b/epan/dissectors/packet-tcp.c @@ -140,6 +140,7 @@ 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; @@ -510,6 +511,16 @@ static gboolean tcp_display_process_info = FALSE; #define TCPOPT_MPTCP_MP_FAIL 0x6 /* Multipath TCP Fallback */ #define TCPOPT_MPTCP_MP_FASTCLOSE 0x7 /* Multipath TCP Fast Close */ +/* + * Conversation Completeness values + */ +#define TCP_COMPLETENESS_SYNSENT 0x01 /* TCP SYN SENT */ +#define TCP_COMPLETENESS_SYNACK 0x02 /* TCP SYN ACK */ +#define TCP_COMPLETENESS_ACK 0x04 /* TCP ACK */ +#define TCP_COMPLETENESS_DATA 0x08 /* TCP data */ +#define TCP_COMPLETENESS_FIN 0x10 /* TCP FIN */ +#define TCP_COMPLETENESS_RST 0x20 /* TCP RST */ + static const true_false_string tcp_option_user_to_granularity = { "Minutes", "Seconds" }; @@ -1334,6 +1345,71 @@ handle_export_pdu_conversation(packet_info *pinfo, tvbuff_t *tvb, int src_port, } } +/* + * display the TCP Conversation Completeness + * we of course pay much attention on complete conversations but also incomplete ones which + * have a regular start, as in practice we are often looking for such thing + */ +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); + break; + case (TCP_COMPLETENESS_SYNSENT| + TCP_COMPLETENESS_SYNACK): + g_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); + break; + case (TCP_COMPLETENESS_SYNSENT| + TCP_COMPLETENESS_SYNACK| + TCP_COMPLETENESS_ACK| + TCP_COMPLETENESS_DATA): + g_snprintf(buf, ITEM_LABEL_LENGTH, "Incomplete, DATA (%u)", value); + break; + case (TCP_COMPLETENESS_SYNSENT| + TCP_COMPLETENESS_SYNACK| + TCP_COMPLETENESS_ACK| + TCP_COMPLETENESS_DATA| + TCP_COMPLETENESS_FIN): + case (TCP_COMPLETENESS_SYNSENT| + TCP_COMPLETENESS_SYNACK| + TCP_COMPLETENESS_ACK| + TCP_COMPLETENESS_DATA| + TCP_COMPLETENESS_RST): + case (TCP_COMPLETENESS_SYNSENT| + TCP_COMPLETENESS_SYNACK| + TCP_COMPLETENESS_ACK| + TCP_COMPLETENESS_DATA| + TCP_COMPLETENESS_FIN| + TCP_COMPLETENESS_RST): + g_snprintf(buf, ITEM_LABEL_LENGTH, "Complete, WITH_DATA (%u)", value); + break; + case (TCP_COMPLETENESS_SYNSENT| + TCP_COMPLETENESS_SYNACK| + TCP_COMPLETENESS_ACK| + TCP_COMPLETENESS_FIN): + case (TCP_COMPLETENESS_SYNSENT| + TCP_COMPLETENESS_SYNACK| + TCP_COMPLETENESS_ACK| + TCP_COMPLETENESS_RST): + case (TCP_COMPLETENESS_SYNSENT| + TCP_COMPLETENESS_SYNACK| + TCP_COMPLETENESS_ACK| + TCP_COMPLETENESS_FIN| + TCP_COMPLETENESS_RST): + g_snprintf(buf, ITEM_LABEL_LENGTH, "Complete, NO_DATA (%u)", value); + break; + default: + g_snprintf(buf, ITEM_LABEL_LENGTH, "Incomplete (%u)", value); + break; + } +} + /* TCP structs and definitions */ /* ************************************************************************** @@ -6185,6 +6261,8 @@ dissect_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_) proto_item *item; proto_tree *checksum_tree; gboolean icmp_ip = FALSE; + guint8 conversation_completeness = 0; + gboolean conversation_is_new = FALSE; tcph = wmem_new0(wmem_packet_scope(), struct tcpheader); tcph->th_sport = tvb_get_ntohs(tvb, offset); @@ -6271,6 +6349,8 @@ dissect_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_) conv = conversation_new(pinfo->num, &pinfo->src, &pinfo->dst, ENDPOINT_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; } tcpd=get_tcp_conversation_data(conv,pinfo); @@ -6292,6 +6372,9 @@ dissect_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_) conv=conversation_new(pinfo->num, &pinfo->src, &pinfo->dst, ENDPOINT_TCP, pinfo->srcport, pinfo->destport, 0); tcpd=get_tcp_conversation_data(conv,pinfo); + + /* As above, a new conversation starting with a SYN implies conversation completeness value 1 */ + tcpd->conversation_completeness = 1; } if(!tcpd->ta) tcp_analyze_get_acked_struct(pinfo->num, tcph->th_seq, tcph->th_ack, TRUE, tcpd); @@ -6330,6 +6413,10 @@ dissect_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_) item = proto_tree_add_uint(tcp_tree, hf_tcp_stream, tvb, offset, 0, tcpd->stream); 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); + proto_item_set_generated(item); + /* Copy the stream index into the header as well to make it available * to tap listeners. */ @@ -6456,6 +6543,47 @@ dissect_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_) return offset+12; } + /* initialize or move forward the conversation completeness */ + if(tcpd) { + if(conversation_is_new) { /* pure SYN must be sought in new conversations only */ + if((tcph->th_flags&(TH_SYN|TH_ACK))==TH_SYN) { + conversation_completeness |= TCP_COMPLETENESS_SYNSENT; + if(tcph->th_seglen > 0) { /* TCP Fast Open */ + conversation_completeness |= TCP_COMPLETENESS_DATA; + } + } + } + else { + conversation_completeness = tcpd->conversation_completeness ; + + /* 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; + } + } + + /* FIN-ACK */ + if((tcph->th_flags&(TH_FIN|TH_ACK))==(TH_FIN|TH_ACK)) { + conversation_completeness |= TCP_COMPLETENESS_FIN; + } + + /* RST */ + if(tcph->th_flags&(TH_RST)) { + conversation_completeness |= TCP_COMPLETENESS_RST; + } + } + } + tcpd->conversation_completeness = conversation_completeness; + if (tcp_summary_in_tree) { if(tcph->th_flags&TH_ACK) { proto_item_append_text(ti, ", Ack: %u", tcph->th_ack); @@ -7017,6 +7145,11 @@ proto_register_tcp(void) { "Stream index", "tcp.stream", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_tcp_completeness, + { "Conversation completeness", "tcp.completeness", FT_UINT8, + BASE_CUSTOM, CF_FUNC(conversation_completeness_fill), 0x0, + "The completeness of the conversation capture", HFILL }}, + { &hf_tcp_seq, { "Sequence Number", "tcp.seq", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }}, diff --git a/epan/dissectors/packet-tcp.h b/epan/dissectors/packet-tcp.h index a7587174c7..7ba2583f44 100644 --- a/epan/dissectors/packet-tcp.h +++ b/epan/dissectors/packet-tcp.h @@ -456,6 +456,13 @@ struct tcp_analysis { * can exist without any meta */ struct mptcp_analysis* mptcp_analysis; + + /* Track the TCP conversation completeness, as the capture might + * contain all parts of a TCP flow (establishment, data, clearing) or + * just some parts if we jumped on the bandwagon of an already established + * connection or left before it was terminated explicitly + */ + guint8 conversation_completeness; }; /* Structure that keeps per packet data. First used to be able