HACK: pinfo->private_data points to smb_info again
[wireshark-wip.git] / ui / tap-tcp-stream.c
blob7ca0b4342f404d80cb75af9575bcfe8c604d7ae4
1 /* tap-tcp-stream.c
2 * TCP stream statistics
3 * Originally from tcp_graph.c by Pavel Mores <pvl@uh.cz>
4 * Win32 port: rwh@unifiedtech.com
6 * $Id$
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.
27 #include "config.h"
29 #include <glib.h>
31 #include <stdlib.h>
33 #include <file.h>
34 #include <frame_tvbuff.h>
36 #include <epan/address.h>
37 #include <epan/epan_dissect.h>
38 #include <epan/packet.h>
39 #include <epan/tap.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;
49 int direction;
50 struct tcp_graph *tg;
51 struct segment *last;
52 } tcp_scan_t;
55 static gboolean
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,
78 ts->direction)
79 && tg->stream == tcphdr->th_stream)
81 struct segment *segment = (struct segment *)g_malloc(sizeof(struct segment));
82 segment->next = NULL;
83 segment->num = pinfo->fd->num;
84 segment->rel_secs = (guint32)pinfo->rel_ts.secs;
85 segment->rel_usecs = pinfo->rel_ts.nsecs/1000;
86 /* Currently unused
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;
109 } else {
110 ts->tg->segments = segment;
112 ts->last = segment;
115 return FALSE;
118 /* here we collect all the external data we will ever need */
119 void
120 graph_segment_list_get(capture_file *cf, struct tcp_graph *tg, gboolean stream_known)
122 struct segment current;
123 GString *error_string;
124 tcp_scan_t ts;
126 g_log(NULL, G_LOG_LEVEL_DEBUG, "graph_segment_list_get()");
128 if (!cf || !tg) return;
130 if (!stream_known) {
131 struct tcpheader *header = select_tcpip_session(cf, &current);
132 if (!header) return;
133 if (tg->type == GRAPH_THROUGHPUT)
134 ts.direction = COMPARE_CURR_DIR;
135 else
136 ts.direction = COMPARE_ANY_DIR;
138 /* Remember stream info in graph */
139 COPY_ADDRESS(&tg->src_address, &current.ip_src);
140 tg->src_port = current.th_sport;
141 COPY_ADDRESS(&tg->dst_address, &current.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 = &current;
151 ts.tg = tg;
152 ts.last = NULL;
153 error_string = register_tap_listener("tcp", &ts, "tcp", 0, NULL, tapall_tcpip_packet, NULL);
154 if (error_string) {
155 fprintf(stderr, "wireshark: Couldn't register tcp_graph tap: %s\n",
156 error_string->str);
157 g_string_free(error_string, TRUE);
158 exit(1); /* XXX: fix this */
160 cf_retap_packets(cf);
161 remove_tap_listener(&ts);
164 void
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;
174 tg->segments = NULL;
178 compare_headers(address *saddr1, address *daddr1, guint16 sport1, guint16 dport1, const address *saddr2, const address *daddr2, guint16 sport2, guint16 dport2, int dir)
180 int dir1, dir2;
182 dir1 = ((!(CMP_ADDRESS(saddr1, saddr2))) &&
183 (!(CMP_ADDRESS(daddr1, daddr2))) &&
184 (sport1==sport2) &&
185 (dport1==dport2));
187 if (dir == COMPARE_CURR_DIR) {
188 return dir1;
189 } else {
190 dir2 = ((!(CMP_ADDRESS(saddr1, daddr2))) &&
191 (!(CMP_ADDRESS(daddr1, saddr2))) &&
192 (sport1 == dport2) &&
193 (dport1 == sport2));
194 return dir1 || dir2;
199 get_num_dsegs(struct tcp_graph *tg)
201 int count;
202 struct segment *tmp;
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,
209 COMPARE_CURR_DIR)) {
210 count++;
213 return count;
217 get_num_acks(struct tcp_graph *tg, int *num_sack_ranges)
219 int count;
220 struct segment *tmp;
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,
227 COMPARE_CURR_DIR)) {
228 count++;
229 *num_sack_ranges += tmp->num_sack_ranges;
232 return count;
237 typedef struct _th_t {
238 int num_hdrs;
239 #define MAX_SUPPORTED_TCP_HEADERS 8
240 struct tcpheader *tcphdrs[MAX_SUPPORTED_TCP_HEADERS];
241 } th_t;
243 static gboolean
244 tap_tcpip_packet(void *pct, packet_info *pinfo _U_, epan_dissect_t *edt _U_, const void *vip)
246 int n;
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,
259 COMPARE_CURR_DIR))
261 is_unique = FALSE;
262 break;
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);
275 th->num_hdrs++;
278 return FALSE;
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
283 * session to graph.
285 struct tcpheader *
286 select_tcpip_session(capture_file *cf, struct segment *hdrs)
288 frame_data *fdata;
289 epan_dissect_t edt;
290 dfilter_t *sfcode;
291 GString *error_string;
292 nstime_t rel_ts;
293 th_t th = {0, {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}};
295 if (!cf || !hdrs)
296 return 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);
303 return NULL;
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);
312 if (error_string) {
313 fprintf(stderr, "wireshark: Couldn't register tcp_graph tap: %s\n",
314 error_string->str);
315 g_string_free(error_string, TRUE);
316 exit(1);
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");
333 return NULL;
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 "
342 "in it.");
343 return NULL;
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;
350 /* Currently unused
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)
368 struct unack *u;
370 for (u=list; u; u=u->next) {
371 if (u->seqno == seqno)
372 return TRUE;
374 return FALSE;
377 struct unack *rtt_get_new_unack(double time_val, unsigned int seqno)
379 struct unack *u;
381 u = (struct unack * )g_malloc(sizeof(struct unack));
382 u->next = NULL;
383 u->time = time_val;
384 u->seqno = seqno;
385 return u;
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) {
393 if (!u->next)
394 break;
396 if (u)
397 u->next = new_unack;
398 else
399 *l = new_unack;
402 void rtt_delete_unack_from_list(struct unack **l, struct unack *dead)
404 struct unack *u, *list = *l;
406 if (!dead || !list)
407 return;
409 if (dead == list) {
410 *l = list->next;
411 g_free(list);
412 } else {
413 for (u=list; u; u=u->next) {
414 if (u->next == dead) {
415 u->next = u->next->next;
416 g_free(dead);
417 break;
424 * Editor modelines
426 * Local Variables:
427 * c-basic-offset: 4
428 * tab-width: 8
429 * indent-tabs-mode: nil
430 * End:
432 * ex: set shiftwidth=4 tabstop=8 expandtab:
433 * :indentSize=4:tabSize=8:noTabs=true: