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 ts.direction = COMPARE_ANY_DIR;
149 /* rescan all the packets and pick up all interesting tcp headers.
150 * we only filter for TCP here for speed and do the actual compare
151 * in the tap listener
153 ts.current = ¤t;
156 error_string = register_tap_listener("tcp", &ts, "tcp", 0, NULL, tapall_tcpip_packet, NULL);
158 fprintf(stderr, "wireshark: Couldn't register tcp_graph tap: %s\n",
160 g_string_free(error_string, TRUE);
161 exit(1); /* XXX: fix this */
163 cf_retap_packets(cf);
164 remove_tap_listener(&ts);
168 graph_segment_list_free(struct tcp_graph *tg)
170 struct segment *segment;
172 while (tg->segments) {
173 segment = tg->segments->next;
174 g_free(tg->segments);
175 tg->segments = segment;
181 compare_headers(address *saddr1, address *daddr1, guint16 sport1, guint16 dport1, const address *saddr2, const address *daddr2, guint16 sport2, guint16 dport2, int dir)
185 dir1 = ((!(CMP_ADDRESS(saddr1, saddr2))) &&
186 (!(CMP_ADDRESS(daddr1, daddr2))) &&
190 if (dir == COMPARE_CURR_DIR) {
193 dir2 = ((!(CMP_ADDRESS(saddr1, daddr2))) &&
194 (!(CMP_ADDRESS(daddr1, saddr2))) &&
195 (sport1 == dport2) &&
202 get_num_dsegs(struct tcp_graph *tg)
207 for (tmp=tg->segments, count=0; tmp; tmp=tmp->next) {
208 if (compare_headers(&tg->src_address, &tg->dst_address,
209 tg->src_port, tg->dst_port,
210 &tmp->ip_src, &tmp->ip_dst,
211 tmp->th_sport, tmp->th_dport,
220 get_num_acks(struct tcp_graph *tg, int *num_sack_ranges)
225 for (tmp = tg->segments, count=0; tmp; tmp = tmp->next) {
226 if (!compare_headers(&tg->src_address, &tg->dst_address,
227 tg->src_port, tg->dst_port,
228 &tmp->ip_src, &tmp->ip_dst,
229 tmp->th_sport, tmp->th_dport,
232 *num_sack_ranges += tmp->num_sack_ranges;
240 typedef struct _th_t {
242 #define MAX_SUPPORTED_TCP_HEADERS 8
243 struct tcpheader *tcphdrs[MAX_SUPPORTED_TCP_HEADERS];
247 tap_tcpip_packet(void *pct, packet_info *pinfo _U_, epan_dissect_t *edt _U_, const void *vip)
250 gboolean is_unique = TRUE;
251 th_t *th = (th_t *)pct;
252 const struct tcpheader *header = (const struct tcpheader *)vip;
254 /* Check new header details against any/all stored ones */
255 for (n=0; n < th->num_hdrs; n++) {
256 struct tcpheader *stored = th->tcphdrs[n];
258 if (compare_headers(&stored->ip_src, &stored->ip_dst,
259 stored->th_sport, stored->th_dport,
260 &header->ip_src, &header->ip_dst,
261 header->th_sport, stored->th_dport,
269 /* Add address if unique and have space for it */
270 if (is_unique && (th->num_hdrs < MAX_SUPPORTED_TCP_HEADERS)) {
271 /* Need to take a deep copy of the tap struct, it may not be valid
272 to read after this function returns? */
273 th->tcphdrs[th->num_hdrs] = (struct tcpheader *)g_malloc(sizeof(struct tcpheader));
274 *(th->tcphdrs[th->num_hdrs]) = *header;
275 COPY_ADDRESS(&th->tcphdrs[th->num_hdrs]->ip_src, &header->ip_src);
276 COPY_ADDRESS(&th->tcphdrs[th->num_hdrs]->ip_dst, &header->ip_dst);
284 /* XXX should be enhanced so that if we have multiple TCP layers in the trace
285 * then present the user with a dialog where the user can select WHICH tcp
289 select_tcpip_session(capture_file *cf, struct segment *hdrs)
294 GString *error_string;
296 th_t th = {0, {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}};
301 fdata = cf->current_frame;
303 /* no real filter yet */
304 if (!dfilter_compile("tcp", &sfcode)) {
305 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", dfilter_error_msg);
309 /* dissect the current frame */
310 if (!cf_read_frame(cf, fdata))
311 return NULL; /* error reading the frame */
314 error_string=register_tap_listener("tcp", &th, NULL, 0, NULL, tap_tcpip_packet, NULL);
316 fprintf(stderr, "wireshark: Couldn't register tcp_graph tap: %s\n",
318 g_string_free(error_string, TRUE);
322 epan_dissect_init(&edt, cf->epan, TRUE, FALSE);
323 epan_dissect_prime_dfilter(&edt, sfcode);
324 epan_dissect_run_with_taps(&edt, &cf->phdr, frame_tvbuff_new_buffer(fdata, &cf->buf), fdata, NULL);
325 rel_ts = edt.pi.rel_ts;
326 epan_dissect_cleanup(&edt);
327 remove_tap_listener(&th);
329 if (th.num_hdrs == 0) {
330 /* This "shouldn't happen", as our menu items shouldn't
331 * even be enabled if the selected packet isn't a TCP
332 * segment, as tcp_graph_selected_packet_enabled() is used
333 * to determine whether to enable any of our menu items. */
334 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
335 "Selected packet isn't a TCP segment");
338 /* XXX fix this later, we should show a dialog allowing the user
339 to select which session he wants here
341 if (th.num_hdrs > 1) {
342 /* can only handle a single tcp layer yet */
343 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
344 "The selected packet has more than one TCP unique conversation "
349 /* For now, still always choose the first/only one */
350 hdrs->num = fdata->num;
351 hdrs->rel_secs = (guint32) rel_ts.secs;
352 hdrs->rel_usecs = rel_ts.nsecs/1000;
354 hdrs->abs_secs = (guint32) fdata->abs_ts.secs;
355 hdrs->abs_usecs = fdata->abs_ts.nsecs/1000;
357 hdrs->th_seq = th.tcphdrs[0]->th_seq;
358 hdrs->th_ack = th.tcphdrs[0]->th_ack;
359 hdrs->th_win = th.tcphdrs[0]->th_win;
360 hdrs->th_flags = th.tcphdrs[0]->th_flags;
361 hdrs->th_sport = th.tcphdrs[0]->th_sport;
362 hdrs->th_dport = th.tcphdrs[0]->th_dport;
363 hdrs->th_seglen = th.tcphdrs[0]->th_seglen;
364 COPY_ADDRESS(&hdrs->ip_src, &th.tcphdrs[0]->ip_src);
365 COPY_ADDRESS(&hdrs->ip_dst, &th.tcphdrs[0]->ip_dst);
366 return th.tcphdrs[0];
369 int rtt_is_retrans(struct unack *list, unsigned int seqno)
373 for (u=list; u; u=u->next) {
374 if (u->seqno == seqno)
380 struct unack *rtt_get_new_unack(double time_val, unsigned int seqno)
384 u = (struct unack * )g_malloc(sizeof(struct unack));
391 void rtt_put_unack_on_list(struct unack **l, struct unack *new_unack)
393 struct unack *u, *list = *l;
395 for (u=list; u; u=u->next) {
405 void rtt_delete_unack_from_list(struct unack **l, struct unack *dead)
407 struct unack *u, *list = *l;
416 for (u=list; u; u=u->next) {
417 if (u->next == dead) {
418 u->next = u->next->next;
432 * indent-tabs-mode: nil
435 * ex: set shiftwidth=4 tabstop=8 expandtab:
436 * :indentSize=4:tabSize=8:noTabs=true: