summaryrefslogtreecommitdiff
path: root/epan
diff options
context:
space:
mode:
authorMichael Mann <mmann78@netscape.net>2016-10-18 20:34:37 -0400
committerPascal Quantin <pascal.quantin@gmail.com>2016-11-10 20:48:18 +0000
commit66fa31415ff8d520c71dc66718599e941ed05c29 (patch)
tree4ecb47b8faabeea22471682e85771410ec6e5d5e /epan
parentb489b7ff7d4ba9f000f29c608515eb43059855ab (diff)
downloadwireshark-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.c263
-rw-r--r--epan/follow.c41
-rw-r--r--epan/follow.h10
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 */