2 * TCP stream statistics
3 * Originally from tcp_graph.c by Pavel Mores <pvl@uh.cz>
4 * Win32 port: rwh@unifiedtech.com
8 * Wireshark - Network traffic analyzer
9 * By Gerald Combs <gerald@wireshark.org>
10 * Copyright 1998 Gerald Combs
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
34 #include <frame_tvbuff.h>
36 #include <epan/address.h>
37 #include <epan/epan_dissect.h>
38 #include <epan/packet.h>
41 #include <epan/dissectors/packet-tcp.h>
43 #include "ui/simple_dialog.h"
45 #include "tap-tcp-stream.h"
47 typedef struct _tcp_scan_t
{
48 struct segment
*current
;
56 tapall_tcpip_packet(void *pct
, packet_info
*pinfo
, epan_dissect_t
*edt _U_
, const void *vip
)
58 tcp_scan_t
*ts
= (tcp_scan_t
*)pct
;
59 struct tcp_graph
*tg
= ts
->tg
;
60 const struct tcpheader
*tcphdr
= (const struct tcpheader
*)vip
;
62 if (tg
->stream
== tcphdr
->th_stream
63 && (tg
->src_address
.type
== AT_NONE
|| tg
->dst_address
.type
== AT_NONE
)) {
65 * We only know the stream number. Fill in our connection data.
66 * We assume that the server response is more interesting.
68 COPY_ADDRESS(&tg
->src_address
, &tcphdr
->ip_dst
);
69 tg
->src_port
= tcphdr
->th_dport
;
70 COPY_ADDRESS(&tg
->dst_address
, &tcphdr
->ip_src
);
71 tg
->dst_port
= tcphdr
->th_sport
;
74 if (compare_headers(&tg
->src_address
, &tg
->dst_address
,
75 tg
->src_port
, tg
->dst_port
,
76 &tcphdr
->ip_src
, &tcphdr
->ip_dst
,
77 tcphdr
->th_sport
, tcphdr
->th_dport
,
79 && tg
->stream
== tcphdr
->th_stream
)
81 struct segment
*segment
= (struct segment
*)g_malloc(sizeof(struct segment
));
83 segment
->num
= pinfo
->fd
->num
;
84 segment
->rel_secs
= (guint32
)pinfo
->rel_ts
.secs
;
85 segment
->rel_usecs
= pinfo
->rel_ts
.nsecs
/1000;
87 segment->abs_secs = (guint32)pinfo->fd->abs_ts.secs;
88 segment->abs_usecs = pinfo->fd->abs_ts.nsecs/1000;
90 segment
->th_seq
= tcphdr
->th_seq
;
91 segment
->th_ack
= tcphdr
->th_ack
;
92 segment
->th_win
= tcphdr
->th_win
;
93 segment
->th_flags
= tcphdr
->th_flags
;
94 segment
->th_sport
= tcphdr
->th_sport
;
95 segment
->th_dport
= tcphdr
->th_dport
;
96 segment
->th_seglen
= tcphdr
->th_seglen
;
97 COPY_ADDRESS(&segment
->ip_src
, &tcphdr
->ip_src
);
98 COPY_ADDRESS(&segment
->ip_dst
, &tcphdr
->ip_dst
);
100 segment
->num_sack_ranges
= MIN(MAX_TCP_SACK_RANGES
, tcphdr
->num_sack_ranges
);
101 if (segment
->num_sack_ranges
> 0) {
102 /* Copy entries in the order they happen */
103 memcpy(&segment
->sack_left_edge
, &tcphdr
->sack_left_edge
, sizeof(segment
->sack_left_edge
));
104 memcpy(&segment
->sack_right_edge
, &tcphdr
->sack_right_edge
, sizeof(segment
->sack_right_edge
));
107 if (ts
->tg
->segments
) {
108 ts
->last
->next
= segment
;
110 ts
->tg
->segments
= segment
;
118 /* here we collect all the external data we will ever need */
120 graph_segment_list_get(capture_file
*cf
, struct tcp_graph
*tg
, gboolean stream_known
)
122 struct segment current
;
123 GString
*error_string
;
126 g_log(NULL
, G_LOG_LEVEL_DEBUG
, "graph_segment_list_get()");
128 if (!cf
|| !tg
) return;
131 struct tcpheader
*header
= select_tcpip_session(cf
, ¤t
);
133 if (tg
->type
== GRAPH_THROUGHPUT
)
134 ts
.direction
= COMPARE_CURR_DIR
;
136 ts
.direction
= COMPARE_ANY_DIR
;
138 /* Remember stream info in graph */
139 COPY_ADDRESS(&tg
->src_address
, ¤t
.ip_src
);
140 tg
->src_port
= current
.th_sport
;
141 COPY_ADDRESS(&tg
->dst_address
, ¤t
.ip_dst
);
142 tg
->dst_port
= current
.th_dport
;
143 tg
->stream
= header
->th_stream
;
146 /* rescan all the packets and pick up all interesting tcp headers.
147 * we only filter for TCP here for speed and do the actual compare
148 * in the tap listener
150 ts
.current
= ¤t
;
153 error_string
= register_tap_listener("tcp", &ts
, "tcp", 0, NULL
, tapall_tcpip_packet
, NULL
);
155 fprintf(stderr
, "wireshark: Couldn't register tcp_graph tap: %s\n",
157 g_string_free(error_string
, TRUE
);
158 exit(1); /* XXX: fix this */
160 cf_retap_packets(cf
);
161 remove_tap_listener(&ts
);
165 graph_segment_list_free(struct tcp_graph
*tg
)
167 struct segment
*segment
;
169 while (tg
->segments
) {
170 segment
= tg
->segments
->next
;
171 g_free(tg
->segments
);
172 tg
->segments
= segment
;
178 compare_headers(address
*saddr1
, address
*daddr1
, guint16 sport1
, guint16 dport1
, const address
*saddr2
, const address
*daddr2
, guint16 sport2
, guint16 dport2
, int dir
)
182 dir1
= ((!(CMP_ADDRESS(saddr1
, saddr2
))) &&
183 (!(CMP_ADDRESS(daddr1
, daddr2
))) &&
187 if (dir
== COMPARE_CURR_DIR
) {
190 dir2
= ((!(CMP_ADDRESS(saddr1
, daddr2
))) &&
191 (!(CMP_ADDRESS(daddr1
, saddr2
))) &&
192 (sport1
== dport2
) &&
199 get_num_dsegs(struct tcp_graph
*tg
)
204 for (tmp
=tg
->segments
, count
=0; tmp
; tmp
=tmp
->next
) {
205 if (compare_headers(&tg
->src_address
, &tg
->dst_address
,
206 tg
->src_port
, tg
->dst_port
,
207 &tmp
->ip_src
, &tmp
->ip_dst
,
208 tmp
->th_sport
, tmp
->th_dport
,
217 get_num_acks(struct tcp_graph
*tg
, int *num_sack_ranges
)
222 for (tmp
= tg
->segments
, count
=0; tmp
; tmp
= tmp
->next
) {
223 if (!compare_headers(&tg
->src_address
, &tg
->dst_address
,
224 tg
->src_port
, tg
->dst_port
,
225 &tmp
->ip_src
, &tmp
->ip_dst
,
226 tmp
->th_sport
, tmp
->th_dport
,
229 *num_sack_ranges
+= tmp
->num_sack_ranges
;
237 typedef struct _th_t
{
239 #define MAX_SUPPORTED_TCP_HEADERS 8
240 struct tcpheader
*tcphdrs
[MAX_SUPPORTED_TCP_HEADERS
];
244 tap_tcpip_packet(void *pct
, packet_info
*pinfo _U_
, epan_dissect_t
*edt _U_
, const void *vip
)
247 gboolean is_unique
= TRUE
;
248 th_t
*th
= (th_t
*)pct
;
249 const struct tcpheader
*header
= (const struct tcpheader
*)vip
;
251 /* Check new header details against any/all stored ones */
252 for (n
=0; n
< th
->num_hdrs
; n
++) {
253 struct tcpheader
*stored
= th
->tcphdrs
[n
];
255 if (compare_headers(&stored
->ip_src
, &stored
->ip_dst
,
256 stored
->th_sport
, stored
->th_dport
,
257 &header
->ip_src
, &header
->ip_dst
,
258 header
->th_sport
, stored
->th_dport
,
266 /* Add address if unique and have space for it */
267 if (is_unique
&& (th
->num_hdrs
< MAX_SUPPORTED_TCP_HEADERS
)) {
268 /* Need to take a deep copy of the tap struct, it may not be valid
269 to read after this function returns? */
270 th
->tcphdrs
[th
->num_hdrs
] = (struct tcpheader
*)g_malloc(sizeof(struct tcpheader
));
271 *(th
->tcphdrs
[th
->num_hdrs
]) = *header
;
272 COPY_ADDRESS(&th
->tcphdrs
[th
->num_hdrs
]->ip_src
, &header
->ip_src
);
273 COPY_ADDRESS(&th
->tcphdrs
[th
->num_hdrs
]->ip_dst
, &header
->ip_dst
);
281 /* XXX should be enhanced so that if we have multiple TCP layers in the trace
282 * then present the user with a dialog where the user can select WHICH tcp
286 select_tcpip_session(capture_file
*cf
, struct segment
*hdrs
)
291 GString
*error_string
;
293 th_t th
= {0, {NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
}};
298 fdata
= cf
->current_frame
;
300 /* no real filter yet */
301 if (!dfilter_compile("tcp", &sfcode
)) {
302 simple_dialog(ESD_TYPE_ERROR
, ESD_BTN_OK
, "%s", dfilter_error_msg
);
306 /* dissect the current frame */
307 if (!cf_read_frame(cf
, fdata
))
308 return NULL
; /* error reading the frame */
311 error_string
=register_tap_listener("tcp", &th
, NULL
, 0, NULL
, tap_tcpip_packet
, NULL
);
313 fprintf(stderr
, "wireshark: Couldn't register tcp_graph tap: %s\n",
315 g_string_free(error_string
, TRUE
);
319 epan_dissect_init(&edt
, cf
->epan
, TRUE
, FALSE
);
320 epan_dissect_prime_dfilter(&edt
, sfcode
);
321 epan_dissect_run_with_taps(&edt
, &cf
->phdr
, frame_tvbuff_new_buffer(fdata
, &cf
->buf
), fdata
, NULL
);
322 rel_ts
= edt
.pi
.rel_ts
;
323 epan_dissect_cleanup(&edt
);
324 remove_tap_listener(&th
);
326 if (th
.num_hdrs
== 0) {
327 /* This "shouldn't happen", as our menu items shouldn't
328 * even be enabled if the selected packet isn't a TCP
329 * segment, as tcp_graph_selected_packet_enabled() is used
330 * to determine whether to enable any of our menu items. */
331 simple_dialog(ESD_TYPE_ERROR
, ESD_BTN_OK
,
332 "Selected packet isn't a TCP segment");
335 /* XXX fix this later, we should show a dialog allowing the user
336 to select which session he wants here
338 if (th
.num_hdrs
> 1) {
339 /* can only handle a single tcp layer yet */
340 simple_dialog(ESD_TYPE_ERROR
, ESD_BTN_OK
,
341 "The selected packet has more than one TCP unique conversation "
346 /* For now, still always choose the first/only one */
347 hdrs
->num
= fdata
->num
;
348 hdrs
->rel_secs
= (guint32
) rel_ts
.secs
;
349 hdrs
->rel_usecs
= rel_ts
.nsecs
/1000;
351 hdrs->abs_secs = (guint32) fdata->abs_ts.secs;
352 hdrs->abs_usecs = fdata->abs_ts.nsecs/1000;
354 hdrs
->th_seq
= th
.tcphdrs
[0]->th_seq
;
355 hdrs
->th_ack
= th
.tcphdrs
[0]->th_ack
;
356 hdrs
->th_win
= th
.tcphdrs
[0]->th_win
;
357 hdrs
->th_flags
= th
.tcphdrs
[0]->th_flags
;
358 hdrs
->th_sport
= th
.tcphdrs
[0]->th_sport
;
359 hdrs
->th_dport
= th
.tcphdrs
[0]->th_dport
;
360 hdrs
->th_seglen
= th
.tcphdrs
[0]->th_seglen
;
361 COPY_ADDRESS(&hdrs
->ip_src
, &th
.tcphdrs
[0]->ip_src
);
362 COPY_ADDRESS(&hdrs
->ip_dst
, &th
.tcphdrs
[0]->ip_dst
);
363 return th
.tcphdrs
[0];
366 int rtt_is_retrans(struct unack
*list
, unsigned int seqno
)
370 for (u
=list
; u
; u
=u
->next
) {
371 if (u
->seqno
== seqno
)
377 struct unack
*rtt_get_new_unack(double time_val
, unsigned int seqno
)
381 u
= (struct unack
* )g_malloc(sizeof(struct unack
));
388 void rtt_put_unack_on_list(struct unack
**l
, struct unack
*new_unack
)
390 struct unack
*u
, *list
= *l
;
392 for (u
=list
; u
; u
=u
->next
) {
402 void rtt_delete_unack_from_list(struct unack
**l
, struct unack
*dead
)
404 struct unack
*u
, *list
= *l
;
413 for (u
=list
; u
; u
=u
->next
) {
414 if (u
->next
== dead
) {
415 u
->next
= u
->next
->next
;
429 * indent-tabs-mode: nil
432 * ex: set shiftwidth=4 tabstop=8 expandtab:
433 * :indentSize=4:tabSize=8:noTabs=true: