[Automatic update for 2018-04-08]
[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  * Wireshark - Network traffic analyzer
7  * By Gerald Combs <gerald@wireshark.org>
8  * Copyright 1998 Gerald Combs
9  *
10  * SPDX-License-Identifier: GPL-2.0-or-later*/
11
12 #include "config.h"
13
14
15 #include <stdlib.h>
16
17 #include <file.h>
18 #include <frame_tvbuff.h>
19
20 #include <epan/epan_dissect.h>
21 #include <epan/packet.h>
22 #include <epan/tap.h>
23
24 #include <epan/dissectors/packet-tcp.h>
25
26 #include "ui/simple_dialog.h"
27
28 #include "tap-tcp-stream.h"
29
30 typedef struct _tcp_scan_t {
31     struct segment         *current;
32     int                     direction;
33     struct tcp_graph       *tg;
34     struct segment         *last;
35 } tcp_scan_t;
36
37
38 static gboolean
39 tapall_tcpip_packet(void *pct, packet_info *pinfo, epan_dissect_t *edt _U_, const void *vip)
40 {
41     tcp_scan_t   *ts = (tcp_scan_t *)pct;
42     struct tcp_graph *tg  = ts->tg;
43     const struct tcpheader *tcphdr = (const struct tcpheader *)vip;
44
45     if (tg->stream == tcphdr->th_stream
46             && (tg->src_address.type == AT_NONE || tg->dst_address.type == AT_NONE)) {
47         /*
48          * We only know the stream number. Fill in our connection data.
49          * We assume that the server response is more interesting.
50          */
51         copy_address(&tg->src_address, &tcphdr->ip_dst);
52         tg->src_port = tcphdr->th_dport;
53         copy_address(&tg->dst_address, &tcphdr->ip_src);
54         tg->dst_port = tcphdr->th_sport;
55     }
56
57     if (compare_headers(&tg->src_address, &tg->dst_address,
58                         tg->src_port, tg->dst_port,
59                         &tcphdr->ip_src, &tcphdr->ip_dst,
60                         tcphdr->th_sport, tcphdr->th_dport,
61                         ts->direction)
62         && tg->stream == tcphdr->th_stream)
63     {
64         struct segment *segment = g_new(struct segment, 1);
65         segment->next      = NULL;
66         segment->num       = pinfo->num;
67         segment->rel_secs  = (guint32)pinfo->rel_ts.secs;
68         segment->rel_usecs = pinfo->rel_ts.nsecs/1000;
69         /* Currently unused
70         segment->abs_secs  = (guint32)pinfo->abs_ts.secs;
71         segment->abs_usecs = pinfo->abs_ts.nsecs/1000;
72         */
73         segment->th_seq    = tcphdr->th_seq;
74         segment->th_ack    = tcphdr->th_ack;
75         segment->th_win    = tcphdr->th_win;
76         segment->th_flags  = tcphdr->th_flags;
77         segment->th_sport  = tcphdr->th_sport;
78         segment->th_dport  = tcphdr->th_dport;
79         segment->th_seglen = tcphdr->th_seglen;
80         copy_address(&segment->ip_src, &tcphdr->ip_src);
81         copy_address(&segment->ip_dst, &tcphdr->ip_dst);
82
83         segment->num_sack_ranges = MIN(MAX_TCP_SACK_RANGES, tcphdr->num_sack_ranges);
84         if (segment->num_sack_ranges > 0) {
85             /* Copy entries in the order they happen */
86             memcpy(&segment->sack_left_edge, &tcphdr->sack_left_edge, sizeof(segment->sack_left_edge));
87             memcpy(&segment->sack_right_edge, &tcphdr->sack_right_edge, sizeof(segment->sack_right_edge));
88         }
89
90         if (ts->tg->segments) {
91             ts->last->next = segment;
92         } else {
93             ts->tg->segments = segment;
94         }
95         ts->last = segment;
96     }
97
98     return FALSE;
99 }
100
101 /* here we collect all the external data we will ever need */
102 void
103 graph_segment_list_get(capture_file *cf, struct tcp_graph *tg, gboolean stream_known)
104 {
105     struct segment current;
106     GString    *error_string;
107     tcp_scan_t  ts;
108
109     g_log(NULL, G_LOG_LEVEL_DEBUG, "graph_segment_list_get()");
110
111     if (!cf || !tg) {
112         return;
113     }
114
115     if (!stream_known) {
116         struct tcpheader *header = select_tcpip_session(cf, &current);
117         if (!header) return;
118         if (tg->type == GRAPH_THROUGHPUT) {
119             ts.direction = COMPARE_CURR_DIR;
120         } else {
121             ts.direction = COMPARE_ANY_DIR;
122         }
123
124         /* Remember stream info in graph */
125         copy_address(&tg->src_address, &current.ip_src);
126         tg->src_port = current.th_sport;
127         copy_address(&tg->dst_address, &current.ip_dst);
128         tg->dst_port = current.th_dport;
129         tg->stream = header->th_stream;
130     } else {
131             ts.direction = COMPARE_ANY_DIR;
132     }
133
134     /* rescan all the packets and pick up all interesting tcp headers.
135      * we only filter for TCP here for speed and do the actual compare
136      * in the tap listener
137      */
138     ts.current = &current;
139     ts.tg      = tg;
140     ts.last    = NULL;
141     error_string = register_tap_listener("tcp", &ts, "tcp", 0, NULL, tapall_tcpip_packet, NULL);
142     if (error_string) {
143         fprintf(stderr, "wireshark: Couldn't register tcp_graph tap: %s\n",
144                 error_string->str);
145         g_string_free(error_string, TRUE);
146         exit(1);   /* XXX: fix this */
147     }
148     cf_retap_packets(cf);
149     remove_tap_listener(&ts);
150 }
151
152 void
153 graph_segment_list_free(struct tcp_graph *tg)
154 {
155     struct segment *segment;
156
157     while (tg->segments) {
158         segment = tg->segments->next;
159         g_free(tg->segments);
160         tg->segments = segment;
161     }
162     tg->segments = NULL;
163 }
164
165 int
166 compare_headers(address *saddr1, address *daddr1, guint16 sport1, guint16 dport1, const address *saddr2, const address *daddr2, guint16 sport2, guint16 dport2, int dir)
167 {
168     int dir1, dir2;
169
170     dir1 = ((!(cmp_address(saddr1, saddr2))) &&
171             (!(cmp_address(daddr1, daddr2))) &&
172             (sport1==sport2)                 &&
173             (dport1==dport2));
174
175     if (dir == COMPARE_CURR_DIR) {
176         return dir1;
177     } else {
178         dir2 = ((!(cmp_address(saddr1, daddr2))) &&
179                 (!(cmp_address(daddr1, saddr2))) &&
180                 (sport1 == dport2)               &&
181                 (dport1 == sport2));
182         return dir1 || dir2;
183     }
184 }
185
186 int
187 get_num_dsegs(struct tcp_graph *tg)
188 {
189     int count;
190     struct segment *tmp;
191
192     for (tmp=tg->segments, count=0; tmp; tmp=tmp->next) {
193         if (compare_headers(&tg->src_address, &tg->dst_address,
194                             tg->src_port, tg->dst_port,
195                             &tmp->ip_src, &tmp->ip_dst,
196                             tmp->th_sport, tmp->th_dport,
197                             COMPARE_CURR_DIR)) {
198             count++;
199         }
200     }
201     return count;
202 }
203
204 int
205 get_num_acks(struct tcp_graph *tg, int *num_sack_ranges)
206 {
207     int count;
208     struct segment *tmp;
209
210     for (tmp = tg->segments, count=0; tmp; tmp = tmp->next) {
211         if (!compare_headers(&tg->src_address, &tg->dst_address,
212                              tg->src_port, tg->dst_port,
213                              &tmp->ip_src, &tmp->ip_dst,
214                              tmp->th_sport, tmp->th_dport,
215                              COMPARE_CURR_DIR)) {
216             count++;
217             *num_sack_ranges += tmp->num_sack_ranges;
218         }
219     }
220     return count;
221 }
222
223 typedef struct _th_t {
224     int num_hdrs;
225     #define MAX_SUPPORTED_TCP_HEADERS 8
226     struct tcpheader *tcphdrs[MAX_SUPPORTED_TCP_HEADERS];
227 } th_t;
228
229 static gboolean
230 tap_tcpip_packet(void *pct, packet_info *pinfo _U_, epan_dissect_t *edt _U_, const void *vip)
231 {
232     int       n;
233     gboolean  is_unique = TRUE;
234     th_t     *th        = (th_t *)pct;
235     const struct tcpheader *header = (const struct tcpheader *)vip;
236
237     /* Check new header details against any/all stored ones */
238     for (n=0; n < th->num_hdrs; n++) {
239         struct tcpheader *stored = th->tcphdrs[n];
240
241         if (compare_headers(&stored->ip_src, &stored->ip_dst,
242                             stored->th_sport, stored->th_dport,
243                             &header->ip_src, &header->ip_dst,
244                             header->th_sport, stored->th_dport,
245                             COMPARE_CURR_DIR)) {
246             is_unique = FALSE;
247             break;
248         }
249     }
250
251     /* Add address if unique and have space for it */
252     if (is_unique && (th->num_hdrs < MAX_SUPPORTED_TCP_HEADERS)) {
253         /* Need to take a deep copy of the tap struct, it may not be valid
254            to read after this function returns? */
255         th->tcphdrs[th->num_hdrs] = g_new(struct tcpheader, 1);
256         *(th->tcphdrs[th->num_hdrs]) = *header;
257         copy_address(&th->tcphdrs[th->num_hdrs]->ip_src, &header->ip_src);
258         copy_address(&th->tcphdrs[th->num_hdrs]->ip_dst, &header->ip_dst);
259
260         th->num_hdrs++;
261     }
262
263     return FALSE;
264 }
265
266 /* XXX should be enhanced so that if we have multiple TCP layers in the trace
267  * then present the user with a dialog where the user can select WHICH tcp
268  * session to graph.
269  */
270 struct tcpheader *
271 select_tcpip_session(capture_file *cf, struct segment *hdrs)
272 {
273     frame_data     *fdata;
274     epan_dissect_t  edt;
275     dfilter_t      *sfcode;
276     gchar          *err_msg;
277     GString        *error_string;
278     nstime_t        rel_ts;
279     th_t th = {0, {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}};
280
281     if (!cf || !hdrs) {
282         return NULL;
283     }
284
285     fdata = cf->current_frame;
286
287     /* no real filter yet */
288     if (!dfilter_compile("tcp", &sfcode, &err_msg)) {
289         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", err_msg);
290         g_free(err_msg);
291         return NULL;
292     }
293
294     /* dissect the current record */
295     if (!cf_read_record(cf, fdata)) {
296         return NULL;    /* error reading the record */
297     }
298
299
300     error_string = register_tap_listener("tcp", &th, NULL, 0, NULL, tap_tcpip_packet, NULL);
301     if (error_string) {
302         fprintf(stderr, "wireshark: Couldn't register tcp_graph tap: %s\n",
303                 error_string->str);
304         g_string_free(error_string, TRUE);
305         exit(1);
306     }
307
308     epan_dissect_init(&edt, cf->epan, TRUE, FALSE);
309     epan_dissect_prime_with_dfilter(&edt, sfcode);
310     epan_dissect_run_with_taps(&edt, cf->cd_t, &cf->rec,
311                                frame_tvbuff_new_buffer(&cf->provider, fdata, &cf->buf),
312                                fdata, NULL);
313     rel_ts = edt.pi.rel_ts;
314     epan_dissect_cleanup(&edt);
315     remove_tap_listener(&th);
316
317     if (th.num_hdrs == 0) {
318         /* This "shouldn't happen", as our menu items shouldn't
319          * even be enabled if the selected packet isn't a TCP
320          * segment, as tcp_graph_selected_packet_enabled() is used
321          * to determine whether to enable any of our menu items. */
322         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
323                       "Selected packet isn't a TCP segment or is truncated");
324         return NULL;
325     }
326     /* XXX fix this later, we should show a dialog allowing the user
327        to select which session he wants here
328     */
329     if (th.num_hdrs > 1) {
330         /* can only handle a single tcp layer yet */
331         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
332                       "The selected packet has more than one TCP unique conversation "
333                       "in it.");
334         return NULL;
335     }
336
337     /* For now, still always choose the first/only one */
338     hdrs->num   = fdata->num;
339     hdrs->rel_secs  = (guint32) rel_ts.secs;
340     hdrs->rel_usecs = rel_ts.nsecs/1000;
341     /* Currently unused
342     hdrs->abs_secs  = (guint32) fdata->abs_ts.secs;
343     hdrs->abs_usecs = fdata->abs_ts.nsecs/1000;
344     */
345     hdrs->th_seq    = th.tcphdrs[0]->th_seq;
346     hdrs->th_ack    = th.tcphdrs[0]->th_ack;
347     hdrs->th_win    = th.tcphdrs[0]->th_win;
348     hdrs->th_flags  = th.tcphdrs[0]->th_flags;
349     hdrs->th_sport  = th.tcphdrs[0]->th_sport;
350     hdrs->th_dport  = th.tcphdrs[0]->th_dport;
351     hdrs->th_seglen = th.tcphdrs[0]->th_seglen;
352     copy_address(&hdrs->ip_src, &th.tcphdrs[0]->ip_src);
353     copy_address(&hdrs->ip_dst, &th.tcphdrs[0]->ip_dst);
354     return th.tcphdrs[0];
355 }
356
357 int rtt_is_retrans(struct rtt_unack *list, unsigned int seqno)
358 {
359     struct rtt_unack *u;
360
361     for (u=list; u; u=u->next) {
362         if (tcp_seq_eq_or_after(seqno, u->seqno) &&
363             tcp_seq_before(seqno, u->end_seqno)) {
364             return TRUE;
365         }
366     }
367     return FALSE;
368 }
369
370 struct rtt_unack *
371 rtt_get_new_unack(double time_val, unsigned int seqno, unsigned int seglen)
372 {
373     struct rtt_unack *u;
374
375     u = g_new(struct rtt_unack, 1);
376     u->next  = NULL;
377     u->time  = time_val;
378     u->seqno = seqno;
379     u->end_seqno = seqno + seglen;
380     return u;
381 }
382
383 void rtt_put_unack_on_list(struct rtt_unack **l, struct rtt_unack *new_unack)
384 {
385     struct rtt_unack *u, *list = *l;
386
387     for (u=list; u; u=u->next) {
388         if (!u->next) {
389             break;
390         }
391     }
392     if (u) {
393         u->next = new_unack;
394     } else {
395         *l = new_unack;
396     }
397 }
398
399 void rtt_delete_unack_from_list(struct rtt_unack **l, struct rtt_unack *dead)
400 {
401     struct rtt_unack *u, *list = *l;
402
403     if (!dead || !list) {
404         return;
405     }
406
407     if (dead == list) {
408         *l = list->next;
409         g_free(list);
410     } else {
411         for (u=list; u; u=u->next) {
412             if (u->next == dead) {
413                 u->next = u->next->next;
414                 g_free(dead);
415                 break;
416             }
417         }
418     }
419 }
420
421 void rtt_destroy_unack_list(struct rtt_unack **l ) {
422     while (*l) {
423         struct rtt_unack *head = *l;
424         *l = head->next;
425         g_free(head);
426     }
427 }
428
429 /*
430  * Editor modelines
431  *
432  * Local Variables:
433  * c-basic-offset: 4
434  * tab-width: 8
435  * indent-tabs-mode: nil
436  * End:
437  *
438  * ex: set shiftwidth=4 tabstop=8 expandtab:
439  * :indentSize=4:tabSize=8:noTabs=true:
440  */
441