diff options
author | Michael Mann <mmann78@netscape.net> | 2016-10-18 20:34:37 -0400 |
---|---|---|
committer | Pascal Quantin <pascal.quantin@gmail.com> | 2016-11-10 20:48:18 +0000 |
commit | 66fa31415ff8d520c71dc66718599e941ed05c29 (patch) | |
tree | 4ecb47b8faabeea22471682e85771410ec6e5d5e /epan | |
parent | b489b7ff7d4ba9f000f29c608515eb43059855ab (diff) | |
download | wireshark-66fa31415ff8d520c71dc66718599e941ed05c29.tar.gz |
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>
Diffstat (limited to 'epan')
-rw-r--r-- | epan/dissectors/packet-tcp.c | 263 | ||||
-rw-r--r-- | epan/follow.c | 41 | ||||
-rw-r--r-- | epan/follow.h | 10 |
3 files changed, 283 insertions, 31 deletions
diff --git a/epan/dissectors/packet-tcp.c b/epan/dissectors/packet-tcp.c index 8a0e711f9e..697c3901f6 100644 --- a/epan/dissectors/packet-tcp.c +++ b/epan/dissectors/packet-tcp.c @@ -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 diff --git a/epan/follow.c b/epan/follow.c index 4523d2dd57..c7859736e3 100644 --- a/epan/follow.c +++ b/epan/follow.c @@ -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 diff --git a/epan/follow.h b/epan/follow.h index 00d26fc763..9101b32e2d 100644 --- a/epan/follow.h +++ b/epan/follow.h @@ -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 */ |