TCP: Add support for TCP Conversation Completeness tracking

TCP Conversations are modified to track Completeness, particularly initial and closing handshakes

Fixes #16716
This commit is contained in:
Eugene Adell 2020-07-19 20:23:38 +02:00 committed by Wireshark GitLab Utility
parent 0a3eba683d
commit 2f235deb3e
4 changed files with 165 additions and 0 deletions

View File

@ -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.

View File

@ -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

View File

@ -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 }},

View File

@ -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