tcp: Fix Follow TCP tap data and when its tapped.

Use the model from the 2.0 branch and earlier that only "tapped" the
follow data in a single location. This fixes duplicate data for
reassembled data and handles out-of-order packets.

Bug: 12855
Change-Id: I5268f13e3c08e9271acf026b859de693ad794c94
Reviewed-on: https://code.wireshark.org/review/18368
Petri-Dish: Pascal Quantin <pascal.quantin@gmail.com>
Tested-by: Petri Dish Buildbot <buildbot-no-reply@wireshark.org>
Reviewed-by: Pascal Quantin <pascal.quantin@gmail.com>
This commit is contained in:
Michael Mann 2016-10-18 20:34:37 -04:00 committed by Pascal Quantin
parent b489b7ff7d
commit 66fa31415f
7 changed files with 309 additions and 50 deletions

View File

@ -582,6 +582,7 @@ libwireshark.so.0 libwireshark0 #MINVER#
find_stream_circ@Base 1.9.1
find_tap_id@Base 1.9.1
follow_get_stat_tap_string@Base 2.1.0
follow_info_free@Base 2.3.0
follow_iterate_followers@Base 2.1.0
follow_reset_stream@Base 2.1.0
follow_tvb_tap_listener@Base 2.1.0

View File

@ -812,6 +812,225 @@ gchar* tcp_follow_address_filter(address* src_addr, address* dst_addr, int src_p
}
typedef struct tcp_follow_tap_data
{
tvbuff_t *tvb;
struct tcpheader* tcph;
struct tcp_analysis *tcpd;
} tcp_follow_tap_data_t;
static gboolean
check_follow_fragments(follow_info_t *follow_info, gboolean is_server, guint32 acknowledged, guint32 packet_num)
{
GList *fragment_entry;
follow_record_t *fragment, *follow_record;
guint32 lowest_seq;
gchar *dummy_str;
fragment_entry = g_list_first(follow_info->fragments[is_server]);
if (fragment_entry == NULL)
return FALSE;
for (; fragment_entry != NULL; fragment_entry = g_list_next(fragment_entry))
{
fragment = (follow_record_t*)fragment_entry->data;
lowest_seq = fragment->seq;
if( GT_SEQ(lowest_seq, fragment->seq) ) {
lowest_seq = fragment->seq;
}
if( LT_SEQ(fragment->seq, follow_info->seq[is_server]) ) {
guint32 newseq;
/* this sequence number seems dated, but
check the end to make sure it has no more
info than we have already seen */
newseq = fragment->seq + fragment->data->len;
if( GT_SEQ(newseq, follow_info->seq[is_server]) ) {
guint32 new_pos;
/* this one has more than we have seen. let's get the
payload that we have not seen. This happens when
part of this frame has been retransmitted */
new_pos = follow_info->seq[is_server] - fragment->seq;
if ( fragment->data->len > new_pos ) {
guint32 new_frag_size = fragment->data->len - new_pos;
follow_record = g_new0(follow_record_t,1);
follow_record->is_server = is_server;
follow_record->packet_num = fragment->packet_num;
follow_record->seq = follow_info->seq[is_server] + new_frag_size;
follow_record->data = g_byte_array_append(g_byte_array_new(),
fragment->data->data + new_pos,
new_frag_size);
follow_info->payload = g_list_append(follow_info->payload, follow_record);
}
follow_info->seq[is_server] += (fragment->data->len - new_pos);
}
/* Remove the fragment from the list as the "new" part of it
* has been processed or its data has been seen already in
* another packet. */
g_byte_array_free(fragment->data, TRUE);
g_free(fragment);
follow_info->fragments[is_server] = g_list_delete_link(follow_info->fragments[is_server], fragment_entry);
return TRUE;
}
if( EQ_SEQ(fragment->seq, follow_info->seq[is_server]) ) {
/* this fragment fits the stream */
if( fragment->data->len > 0 ) {
follow_info->payload = g_list_append(follow_info->payload, fragment);
}
follow_info->seq[is_server] += fragment->data->len;
follow_info->fragments[is_server] = g_list_delete_link(follow_info->fragments[is_server], fragment_entry);
return TRUE;
}
}
if( GT_SEQ(acknowledged, lowest_seq) ) {
/* There are frames missing in the capture file that were seen
* 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]",
(int)(lowest_seq - follow_info->seq[is_server]) );
follow_record = g_new0(follow_record_t,1);
follow_record->data = g_byte_array_append(g_byte_array_new(),
dummy_str,
(guint)strlen(dummy_str)+1);
g_free(dummy_str);
follow_record->is_server = is_server;
follow_record->packet_num = packet_num;
follow_record->seq = lowest_seq;
follow_info->seq[is_server] = lowest_seq;
follow_info->payload = g_list_append(follow_info->payload, follow_record);
return TRUE;
}
return FALSE;
}
static gboolean
follow_tcp_tap_listener(void *tapdata, packet_info *pinfo,
epan_dissect_t *edt _U_, const void *data)
{
follow_record_t *follow_record, *frag_follow_record;
follow_info_t *follow_info = (follow_info_t *)tapdata;
tcp_follow_tap_data_t *follow_data = (tcp_follow_tap_data_t *)data;
guint32 sequence = follow_data->tcph->th_seq;
guint32 length = follow_data->tcph->th_seglen;
guint32 data_length = tvb_captured_length(follow_data->tvb);
guint32 newseq;
follow_record = g_new0(follow_record_t, 1);
follow_record->data = g_byte_array_append(g_byte_array_new(),
tvb_get_ptr(follow_data->tvb, 0, -1),
data_length);
follow_record->packet_num = pinfo->fd->num;
if (follow_info->client_port == 0) {
follow_info->client_port = pinfo->srcport;
copy_address(&follow_info->client_ip, &pinfo->src);
follow_info->server_port = pinfo->destport;
copy_address(&follow_info->server_ip, &pinfo->dst);
}
if (addresses_equal(&follow_info->client_ip, &pinfo->src) && follow_info->client_port == pinfo->srcport)
follow_record->is_server = FALSE;
else
follow_record->is_server = TRUE;
/* update stream counter */
if (follow_info->bytes_written[follow_record->is_server] == 0) {
follow_info->seq[follow_record->is_server] = sequence + length;
if (follow_data->tcph->th_flags & TH_SYN)
follow_info->seq[follow_record->is_server]++;
follow_info->bytes_written[follow_record->is_server] += follow_record->data->len;
follow_info->payload = g_list_append(follow_info->payload, follow_record);
return FALSE;
}
/* Before adding data for this flow, check whether this frame acks
* fragments that were already seen. This happens when frames are
* not in the capture file, but were actually seen by the
* receiving host (Fixes bug 592).
*/
if (follow_info->fragments[follow_record->is_server] != NULL)
{
while(check_follow_fragments(follow_info, follow_record->is_server, follow_data->tcph->th_ack, pinfo->fd->num));
}
/* if we are here, we have already seen this src, let's
try and figure out if this packet is in the right place */
if( LT_SEQ(sequence, follow_info->seq[follow_record->is_server]) ) {
/* this sequence number seems dated, but
check the end to make sure it has no more
info than we have already seen */
newseq = sequence + length;
if( GT_SEQ(newseq, follow_info->seq[follow_record->is_server])) {
guint32 new_len;
/* this one has more than we have seen. let's get the
payload that we have not seen. */
new_len = follow_info->seq[follow_record->is_server] - sequence;
if ( data_length <= new_len ) {
data_length = 0;
} else {
data_length -= new_len;
}
sequence = follow_info->seq[follow_record->is_server];
length = newseq - follow_info->seq[follow_record->is_server];
/* this will now appear to be right on time :) */
}
}
if ( EQ_SEQ(sequence, follow_info->seq[follow_record->is_server]) ) {
/* right on time */
follow_info->seq[follow_record->is_server] += length;
if (follow_data->tcph->th_flags & TH_SYN)
follow_info->seq[follow_record->is_server]++;
if (data_length > 0) {
follow_info->bytes_written[follow_record->is_server] += follow_record->data->len;
follow_info->payload = g_list_append(follow_info->payload, follow_record);
}
/* done with the packet, see if it caused a fragment to fit */
while(check_follow_fragments(follow_info, follow_record->is_server, 0, pinfo->fd->num));
} else {
/* out of order packet */
if(data_length > 0 && GT_SEQ(sequence, follow_info->seq[follow_record->is_server]) ) {
frag_follow_record = g_new0(follow_record_t,1);
frag_follow_record->data = g_byte_array_append(g_byte_array_new(),
tvb_get_ptr(follow_data->tvb, 0 /* POTENTIAL OFFSET COMPUTED */, -1),
data_length);
frag_follow_record->packet_num = pinfo->fd->num;
frag_follow_record->is_server = follow_record->is_server;
frag_follow_record->seq = sequence;
follow_info->fragments[follow_record->is_server] = g_list_append(follow_info->fragments[follow_record->is_server], frag_follow_record);
}
}
return FALSE;
}
#define EXP_PDU_TCP_INFO_DATA_LEN 19
#define EXP_PDU_TCP_INFO_VERSION 1
@ -2686,11 +2905,6 @@ again:
nbytes = tvb_reported_length_remaining(tvb, offset);
/* Give the follow tap what we've currently dissected */
if(have_tap_listener(tcp_follow_tap)) {
tap_queue_packet(tcp_follow_tap, pinfo, tvb_new_subset_length(tvb, offset, nbytes));
}
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"));
@ -2770,11 +2984,6 @@ again:
*/
tcpinfo->seq = seq;
/* Give the follow tap what we've currently dissected */
if(have_tap_listener(tcp_follow_tap)) {
tap_queue_packet(tcp_follow_tap, pinfo, tvb_new_subset_remaining(tvb, offset));
}
process_tcp_payload(tvb, offset, pinfo, tree, tcp_tree,
sport, dport, 0, 0, FALSE, tcpd, tcpinfo);
called_dissector = TRUE;
@ -2838,11 +3047,6 @@ again:
/* indicate that this is reassembled data */
tcpinfo->is_reassembled = TRUE;
/* Give the follow tap the payload */
if(have_tap_listener(tcp_follow_tap)) {
tap_queue_packet(tcp_follow_tap, pinfo, next_tvb);
}
/* call subdissector */
process_tcp_payload(next_tvb, 0, pinfo, tree, tcp_tree, sport,
dport, 0, 0, FALSE, tcpd, tcpinfo);
@ -3058,11 +3262,6 @@ again:
*/
nbytes = tvb_reported_length_remaining(tvb, deseg_offset);
/* Give the follow tap what we've currently dissected */
if(have_tap_listener(tcp_follow_tap)) {
tap_queue_packet(tcp_follow_tap, pinfo, tvb_new_subset_length(tvb, deseg_offset, nbytes));
}
proto_tree_add_bytes_format(tcp_tree, hf_tcp_segment_data, tvb, deseg_offset,
-1, NULL, "TCP segment data (%u byte%s)", nbytes,
plurality(nbytes, "", "s"));
@ -5333,11 +5532,6 @@ dissect_tcp_payload(tvbuff_t *tvb, packet_info *pinfo, int offset, guint32 seq,
save_fragmented = pinfo->fragmented;
pinfo->fragmented = TRUE;
/* Give the follow tap what we've currently dissected */
if(have_tap_listener(tcp_follow_tap)) {
tap_queue_packet(tcp_follow_tap, pinfo, tvb_new_subset_remaining(tvb, offset));
}
process_tcp_payload(tvb, offset, pinfo, tree, tcp_tree, sport, dport,
seq, nxtseq, TRUE, tcpd, tcpinfo);
pinfo->fragmented = save_fragmented;
@ -6080,6 +6274,18 @@ dissect_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
(it could be an ACK-only packet) */
captured_length_remaining = tvb_captured_length_remaining(tvb, offset);
if (tcph->th_have_seglen) {
if(have_tap_listener(tcp_follow_tap)) {
tcp_follow_tap_data_t* follow_data = wmem_new0(wmem_packet_scope(), tcp_follow_tap_data_t);
follow_data->tvb = tvb_new_subset_remaining(tvb, offset);
follow_data->tcph = tcph;
follow_data->tcpd = tcpd;
tap_queue_packet(tcp_follow_tap, pinfo, follow_data);
}
}
tap_queue_packet(tcp_tap, pinfo, tcph);
/* if it is an MPTCP packet */
@ -6134,11 +6340,6 @@ dissect_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
*/
pinfo->can_desegment = 0;
/* Give the follow tap the payload */
if(have_tap_listener(tcp_follow_tap)) {
tap_queue_packet(tcp_follow_tap, pinfo, next_tvb);
}
process_tcp_payload(next_tvb, 0, pinfo, tree, tcp_tree, tcph->th_sport, tcph->th_dport, tcph->th_seq,
nxtseq, FALSE, tcpd, &tcpinfo);
@ -7300,7 +7501,7 @@ proto_register_tcp(void)
register_conversation_table(proto_mptcp, FALSE, mptcpip_conversation_packet, tcpip_hostlist_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_tvb_tap_listener);
tcp_port_to_display, follow_tcp_tap_listener);
}
void

View File

@ -162,6 +162,47 @@ follow_reset_stream(follow_info_t* info)
info->client_ip.len = 0;
info->server_ip.type = FT_NONE;
info->server_ip.len = 0;
info->fragments[0] = info->fragments[1] = NULL;
info->seq[0] = info->seq[1] = 0;
}
void
follow_info_free(follow_info_t* follow_info)
{
GList *cur;
follow_record_t *follow_record;
for(cur = follow_info->payload; cur; cur = g_list_next(cur)) {
if(cur->data) {
follow_record = (follow_record_t *)cur->data;
if(follow_record->data)
g_byte_array_free(follow_record->data, TRUE);
g_free(follow_record);
}
}
g_list_free(follow_info->payload);
//Only TCP stream uses fragments
for (cur = follow_info->fragments[0]; cur; cur = g_list_next(cur)) {
follow_record = (follow_record_t *)cur->data;
if(follow_record->data) {
g_byte_array_free(follow_record->data, TRUE);
}
g_free(follow_record);
}
for (cur = follow_info->fragments[1]; cur; cur = g_list_next(cur)) {
follow_record = (follow_record_t *)cur->data;
if(follow_record->data) {
g_byte_array_free(follow_record->data, TRUE);
}
g_free(follow_record);
}
free_address(&follow_info->client_ip);
free_address(&follow_info->server_ip);
g_free(follow_info->filter_out_filter);
g_free(follow_info);
}
gboolean

View File

@ -88,6 +88,7 @@ typedef frs_return_t (*follow_read_stream_func)(struct _follow_info *follow_info
typedef struct {
gboolean is_server;
guint32 packet_num;
guint32 seq; /* TCP only */
GByteArray *data;
} follow_record_t;
@ -96,6 +97,8 @@ typedef struct _follow_info {
char *filter_out_filter;
GList *payload;
guint bytes_written[2]; /* Index with FROM_CLIENT or FROM_SERVER for readability. */
guint32 seq[2]; /* TCP only */
GList *fragments[2]; /* TCP only */
guint client_port;
guint server_port;
address client_ip;
@ -201,6 +204,13 @@ WS_DLL_PUBLIC gchar* follow_get_stat_tap_string(register_follow_t* follower);
*/
WS_DLL_PUBLIC void follow_reset_stream(follow_info_t* info);
/** Free follow_info_t structure
* Free everything except the GUI element
*
* @param follow_info [in] follower info
*/
WS_DLL_PUBLIC void follow_info_free(follow_info_t* follow_info);
#ifdef __cplusplus
}
#endif /* __cplusplus */

View File

@ -93,8 +93,7 @@ follow_free(follow_info_t *follow_info)
cli_follow_info_t* cli_follow_info = (cli_follow_info_t*)follow_info->gui_data;
g_free(cli_follow_info);
g_free(follow_info->filter_out_filter);
g_free(follow_info);
follow_info_free(follow_info);
}
#define BYTES_PER_LINE 16

View File

@ -1038,29 +1038,14 @@ static void
follow_destroy_cb(GtkWidget *w, gpointer data _U_)
{
follow_info_t *follow_info;
follow_record_t *follow_record;
gtk_follow_info_t *gtk_follow_info;
GList *cur;
follow_info = (follow_info_t *)g_object_get_data(G_OBJECT(w), E_FOLLOW_INFO_KEY);
gtk_follow_info = (gtk_follow_info_t *)follow_info->gui_data;
for(cur = follow_info->payload; cur; cur = g_list_next(cur))
if(cur->data) {
follow_record = (follow_record_t *)cur->data;
if(follow_record->data)
g_byte_array_free(follow_record->data, TRUE);
g_free(follow_record);
}
g_list_free(follow_info->payload);
g_free(follow_info->filter_out_filter);
free_address(&follow_info->client_ip);
forget_follow_info(follow_info);
g_free(gtk_follow_info);
g_free(follow_info);
follow_info_free(follow_info);
gtk_widget_destroy(w);
}

View File

@ -389,6 +389,7 @@ void FollowStreamDialog::removeStreamControls()
void FollowStreamDialog::resetStream()
{
GList *cur;
follow_record_t *follow_record;
filter_out_filter_.clear();
text_pos_to_packet_.clear();
@ -396,13 +397,34 @@ void FollowStreamDialog::resetStream()
ws_unlink(data_out_filename_.toUtf8().constData());
}
for (cur = follow_info_.payload; cur; cur = g_list_next(cur)) {
follow_record_t *follow_record = (follow_record_t *)cur->data;
follow_record = (follow_record_t *)cur->data;
if(follow_record->data) {
g_byte_array_free(follow_record->data, TRUE);
}
g_free(follow_record);
}
g_list_free(follow_info_.payload);
//Only TCP stream uses fragments
if (follow_type_ == FOLLOW_TCP) {
for (cur = follow_info_.fragments[0]; cur; cur = g_list_next(cur)) {
follow_record = (follow_record_t *)cur->data;
if(follow_record->data) {
g_byte_array_free(follow_record->data, TRUE);
}
g_free(follow_record);
}
follow_info_.fragments[0] = NULL;
for (cur = follow_info_.fragments[1]; cur; cur = g_list_next(cur)) {
follow_record = (follow_record_t *)cur->data;
if(follow_record->data) {
g_byte_array_free(follow_record->data, TRUE);
}
g_free(follow_record);
}
follow_info_.fragments[1] = NULL;
}
follow_info_.payload = NULL;
follow_info_.client_port = 0;
}