Witness: fix notify change
[metze/wireshark/wip.git] / ui / tap-tcp-stream.c
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
5  *
6  * $Id$
7  *
8  * Wireshark - Network traffic analyzer
9  * By Gerald Combs <gerald@wireshark.org>
10  * Copyright 1998 Gerald Combs
11  *
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.
16  *
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.
21  *
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.
25  */
26
27 #include "config.h"
28
29 #include <glib.h>
30
31 #include <stdlib.h>
32
33 #include <file.h>
34 #include <frame_tvbuff.h>
35
36 #include <epan/address.h>
37 #include <epan/epan_dissect.h>
38 #include <epan/packet.h>
39 #include <epan/tap.h>
40
41 #include <epan/dissectors/packet-tcp.h>
42
43 #include "ui/simple_dialog.h"
44
45 #include "tap-tcp-stream.h"
46
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;
53
54
55 static gboolean
56 tapall_tcpip_packet(void *pct, packet_info *pinfo, epan_dissect_t *edt _U_, const void *vip)
57 {
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;
61
62     if (tg->stream == tcphdr->th_stream
63             && (tg->src_address.type == AT_NONE || tg->dst_address.type == AT_NONE)) {
64         /*
65          * We only know the stream number. Fill in our connection data.
66          * We assume that the server response is more interesting.
67          */
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;
72     }
73
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)
80     {
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;
89         */
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);
99
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));
105         }
106
107         if (ts->tg->segments) {
108             ts->last->next = segment;
109         } else {
110             ts->tg->segments = segment;
111         }
112         ts->last = segment;
113     }
114
115     return FALSE;
116 }
117
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)
121 {
122     struct segment current;
123     GString    *error_string;
124     tcp_scan_t  ts;
125
126     g_log(NULL, G_LOG_LEVEL_DEBUG, "graph_segment_list_get()");
127
128     if (!cf || !tg) return;
129
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;
137
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;
144     }
145     else {
146             ts.direction = COMPARE_ANY_DIR;
147     }
148
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
152      */
153     ts.current = &current;
154     ts.tg      = tg;
155     ts.last    = NULL;
156     error_string = register_tap_listener("tcp", &ts, "tcp", 0, NULL, tapall_tcpip_packet, NULL);
157     if (error_string) {
158         fprintf(stderr, "wireshark: Couldn't register tcp_graph tap: %s\n",
159                 error_string->str);
160         g_string_free(error_string, TRUE);
161         exit(1);   /* XXX: fix this */
162     }
163     cf_retap_packets(cf);
164     remove_tap_listener(&ts);
165 }
166
167 void
168 graph_segment_list_free(struct tcp_graph *tg)
169 {
170     struct segment *segment;
171
172     while (tg->segments) {
173         segment = tg->segments->next;
174         g_free(tg->segments);
175         tg->segments = segment;
176     }
177     tg->segments = NULL;
178 }
179
180 int
181 compare_headers(address *saddr1, address *daddr1, guint16 sport1, guint16 dport1, const address *saddr2, const address *daddr2, guint16 sport2, guint16 dport2, int dir)
182 {
183     int dir1, dir2;
184
185     dir1 = ((!(CMP_ADDRESS(saddr1, saddr2))) &&
186             (!(CMP_ADDRESS(daddr1, daddr2))) &&
187             (sport1==sport2)                 &&
188             (dport1==dport2));
189
190     if (dir == COMPARE_CURR_DIR) {
191         return dir1;
192     } else {
193         dir2 = ((!(CMP_ADDRESS(saddr1, daddr2))) &&
194                 (!(CMP_ADDRESS(daddr1, saddr2))) &&
195                 (sport1 == dport2)               &&
196                 (dport1 == sport2));
197         return dir1 || dir2;
198     }
199 }
200
201 int
202 get_num_dsegs(struct tcp_graph *tg)
203 {
204     int count;
205     struct segment *tmp;
206
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,
212                             COMPARE_CURR_DIR)) {
213             count++;
214         }
215     }
216     return count;
217 }
218
219 int
220 get_num_acks(struct tcp_graph *tg, int *num_sack_ranges)
221 {
222     int count;
223     struct segment *tmp;
224
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,
230                              COMPARE_CURR_DIR)) {
231             count++;
232             *num_sack_ranges += tmp->num_sack_ranges;
233         }
234     }
235     return count;
236 }
237
238
239
240 typedef struct _th_t {
241     int num_hdrs;
242     #define MAX_SUPPORTED_TCP_HEADERS 8
243     struct tcpheader *tcphdrs[MAX_SUPPORTED_TCP_HEADERS];
244 } th_t;
245
246 static gboolean
247 tap_tcpip_packet(void *pct, packet_info *pinfo _U_, epan_dissect_t *edt _U_, const void *vip)
248 {
249     int       n;
250     gboolean  is_unique = TRUE;
251     th_t     *th        = (th_t *)pct;
252     const struct tcpheader *header = (const struct tcpheader *)vip;
253
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];
257
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,
262                             COMPARE_CURR_DIR))
263         {
264             is_unique = FALSE;
265             break;
266         }
267     }
268
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);
277
278         th->num_hdrs++;
279     }
280
281     return FALSE;
282 }
283
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
286  * session to graph.
287  */
288 struct tcpheader *
289 select_tcpip_session(capture_file *cf, struct segment *hdrs)
290 {
291     frame_data     *fdata;
292     epan_dissect_t  edt;
293     dfilter_t      *sfcode;
294     GString        *error_string;
295     nstime_t        rel_ts;
296     th_t th = {0, {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}};
297
298     if (!cf || !hdrs)
299         return NULL;
300     
301     fdata = cf->current_frame;
302
303     /* no real filter yet */
304     if (!dfilter_compile("tcp", &sfcode)) {
305         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", dfilter_error_msg);
306         return NULL;
307     }
308
309     /* dissect the current frame */
310     if (!cf_read_frame(cf, fdata))
311         return NULL;    /* error reading the frame */
312
313
314     error_string=register_tap_listener("tcp", &th, NULL, 0, NULL, tap_tcpip_packet, NULL);
315     if (error_string) {
316         fprintf(stderr, "wireshark: Couldn't register tcp_graph tap: %s\n",
317                 error_string->str);
318         g_string_free(error_string, TRUE);
319         exit(1);
320     }
321
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);
328
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");
336         return NULL;
337     }
338     /* XXX fix this later, we should show a dialog allowing the user
339        to select which session he wants here
340     */
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 "
345                       "in it.");
346         return NULL;
347     }
348
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;
353     /* Currently unused
354     hdrs->abs_secs  = (guint32) fdata->abs_ts.secs;
355     hdrs->abs_usecs = fdata->abs_ts.nsecs/1000;
356     */
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];
367 }
368
369 int rtt_is_retrans(struct unack *list, unsigned int seqno)
370 {
371     struct unack *u;
372
373     for (u=list; u; u=u->next) {
374         if (u->seqno == seqno)
375             return TRUE;
376     }
377     return FALSE;
378 }
379
380 struct unack *rtt_get_new_unack(double time_val, unsigned int seqno)
381 {
382     struct unack *u;
383
384     u = (struct unack * )g_malloc(sizeof(struct unack));
385     u->next  = NULL;
386     u->time  = time_val;
387     u->seqno = seqno;
388     return u;
389 }
390
391 void rtt_put_unack_on_list(struct unack **l, struct unack *new_unack)
392 {
393     struct unack *u, *list = *l;
394
395     for (u=list; u; u=u->next) {
396         if (!u->next)
397             break;
398     }
399     if (u)
400         u->next = new_unack;
401     else
402         *l = new_unack;
403 }
404
405 void rtt_delete_unack_from_list(struct unack **l, struct unack *dead)
406 {
407     struct unack *u, *list = *l;
408
409     if (!dead || !list)
410         return;
411
412     if (dead == list) {
413         *l = list->next;
414         g_free(list);
415     } else {
416         for (u=list; u; u=u->next) {
417             if (u->next == dead) {
418                 u->next = u->next->next;
419                 g_free(dead);
420                 break;
421             }
422         }
423     }
424 }
425
426 /*
427  * Editor modelines
428  *
429  * Local Variables:
430  * c-basic-offset: 4
431  * tab-width: 8
432  * indent-tabs-mode: nil
433  * End:
434  *
435  * ex: set shiftwidth=4 tabstop=8 expandtab:
436  * :indentSize=4:tabSize=8:noTabs=true:
437  */
438