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 
13 #include "config.h"
14 
15 
16 #include <stdlib.h>
17 
18 #include <file.h>
19 #include <frame_tvbuff.h>
20 
21 #include <epan/epan_dissect.h>
22 #include <epan/packet.h>
23 #include <epan/tap.h>
24 
25 #include <epan/dissectors/packet-tcp.h>
26 
27 #include "ui/simple_dialog.h"
28 
29 #include "tap-tcp-stream.h"
30 
31 typedef struct _tcp_scan_t {
32     int                     direction;
33     struct tcp_graph       *tg;
34     struct segment         *last;
35 } tcp_scan_t;
36 
37 
38 static tap_packet_status
tapall_tcpip_packet(void * pct,packet_info * pinfo,epan_dissect_t * edt _U_,const void * vip)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  = 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 TAP_PACKET_DONT_REDRAW;
99 }
100 
101 /* here we collect all the external data we will ever need */
102 void
graph_segment_list_get(capture_file * cf,struct tcp_graph * tg)103 graph_segment_list_get(capture_file *cf, struct tcp_graph *tg)
104 {
105     GString    *error_string;
106     tcp_scan_t  ts;
107 
108     if (!cf || !tg) {
109         return;
110     }
111 
112     /* rescan all the packets and pick up all interesting tcp headers.
113      * we only filter for TCP here for speed and do the actual compare
114      * in the tap listener
115      */
116     ts.direction = COMPARE_ANY_DIR;
117     ts.tg      = tg;
118     ts.last    = NULL;
119     error_string = register_tap_listener("tcp", &ts, "tcp", 0, NULL, tapall_tcpip_packet, NULL, NULL);
120     if (error_string) {
121         fprintf(stderr, "wireshark: Couldn't register tcp_graph tap: %s\n",
122                 error_string->str);
123         g_string_free(error_string, TRUE);
124         exit(1);   /* XXX: fix this */
125     }
126     cf_retap_packets(cf);
127     remove_tap_listener(&ts);
128 }
129 
130 void
graph_segment_list_free(struct tcp_graph * tg)131 graph_segment_list_free(struct tcp_graph *tg)
132 {
133     struct segment *segment;
134 
135     free_address(&tg->src_address);
136     free_address(&tg->dst_address);
137 
138     while (tg->segments) {
139         segment = tg->segments->next;
140         free_address(&tg->segments->ip_src);
141         free_address(&tg->segments->ip_dst);
142         g_free(tg->segments);
143         tg->segments = segment;
144     }
145 }
146 
147 int
compare_headers(address * saddr1,address * daddr1,guint16 sport1,guint16 dport1,const address * saddr2,const address * daddr2,guint16 sport2,guint16 dport2,int dir)148 compare_headers(address *saddr1, address *daddr1, guint16 sport1, guint16 dport1, const address *saddr2, const address *daddr2, guint16 sport2, guint16 dport2, int dir)
149 {
150     int dir1, dir2;
151 
152     dir1 = ((!(cmp_address(saddr1, saddr2))) &&
153             (!(cmp_address(daddr1, daddr2))) &&
154             (sport1==sport2)                 &&
155             (dport1==dport2));
156 
157     if (dir == COMPARE_CURR_DIR) {
158         return dir1;
159     } else {
160         dir2 = ((!(cmp_address(saddr1, daddr2))) &&
161                 (!(cmp_address(daddr1, saddr2))) &&
162                 (sport1 == dport2)               &&
163                 (dport1 == sport2));
164         return dir1 || dir2;
165     }
166 }
167 
168 int
get_num_dsegs(struct tcp_graph * tg)169 get_num_dsegs(struct tcp_graph *tg)
170 {
171     int count;
172     struct segment *tmp;
173 
174     for (tmp=tg->segments, count=0; tmp; tmp=tmp->next) {
175         if (compare_headers(&tg->src_address, &tg->dst_address,
176                             tg->src_port, tg->dst_port,
177                             &tmp->ip_src, &tmp->ip_dst,
178                             tmp->th_sport, tmp->th_dport,
179                             COMPARE_CURR_DIR)) {
180             count++;
181         }
182     }
183     return count;
184 }
185 
186 int
get_num_acks(struct tcp_graph * tg,int * num_sack_ranges)187 get_num_acks(struct tcp_graph *tg, int *num_sack_ranges)
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             *num_sack_ranges += tmp->num_sack_ranges;
200         }
201     }
202     return count;
203 }
204 
205 typedef struct _th_t {
206     int num_hdrs;
207     #define MAX_SUPPORTED_TCP_HEADERS 8
208     struct tcpheader *tcphdrs[MAX_SUPPORTED_TCP_HEADERS];
209 } th_t;
210 
211 static tap_packet_status
tap_tcpip_packet(void * pct,packet_info * pinfo _U_,epan_dissect_t * edt _U_,const void * vip)212 tap_tcpip_packet(void *pct, packet_info *pinfo _U_, epan_dissect_t *edt _U_, const void *vip)
213 {
214     int       n;
215     gboolean  is_unique = TRUE;
216     th_t     *th        = (th_t *)pct;
217     const struct tcpheader *header = (const struct tcpheader *)vip;
218 
219     /* Check new header details against any/all stored ones */
220     for (n=0; n < th->num_hdrs; n++) {
221         struct tcpheader *stored = th->tcphdrs[n];
222 
223         if (compare_headers(&stored->ip_src, &stored->ip_dst,
224                             stored->th_sport, stored->th_dport,
225                             &header->ip_src, &header->ip_dst,
226                             header->th_sport, stored->th_dport,
227                             COMPARE_CURR_DIR)) {
228             is_unique = FALSE;
229             break;
230         }
231     }
232 
233     /* Add address if unique and have space for it */
234     if (is_unique && (th->num_hdrs < MAX_SUPPORTED_TCP_HEADERS)) {
235         /* Need to take a deep copy of the tap struct, it may not be valid
236            to read after this function returns? */
237         th->tcphdrs[th->num_hdrs] = g_new(struct tcpheader, 1);
238         *(th->tcphdrs[th->num_hdrs]) = *header;
239         copy_address(&th->tcphdrs[th->num_hdrs]->ip_src, &header->ip_src);
240         copy_address(&th->tcphdrs[th->num_hdrs]->ip_dst, &header->ip_dst);
241 
242         th->num_hdrs++;
243     }
244 
245     return TAP_PACKET_DONT_REDRAW;
246 }
247 
248 /* XXX should be enhanced so that if we have multiple TCP layers in the trace
249  * then present the user with a dialog where the user can select WHICH tcp
250  * session to graph.
251  */
252 guint32
select_tcpip_session(capture_file * cf)253 select_tcpip_session(capture_file *cf)
254 {
255     frame_data     *fdata;
256     epan_dissect_t  edt;
257     dfilter_t      *sfcode;
258     guint32         th_stream;
259     gchar          *err_msg;
260     GString        *error_string;
261     th_t th = {0, {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}};
262 
263     if (!cf) {
264         return G_MAXUINT32;
265     }
266 
267     /* no real filter yet */
268     if (!dfilter_compile("tcp", &sfcode, &err_msg)) {
269         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", err_msg);
270         g_free(err_msg);
271         return G_MAXUINT32;
272     }
273 
274     /* dissect the current record */
275     if (!cf_read_current_record(cf)) {
276         return G_MAXUINT32;    /* error reading the record */
277     }
278 
279     fdata = cf->current_frame;
280 
281     error_string = register_tap_listener("tcp", &th, NULL, 0, NULL, tap_tcpip_packet, NULL, NULL);
282     if (error_string) {
283         fprintf(stderr, "wireshark: Couldn't register tcp_graph tap: %s\n",
284                 error_string->str);
285         g_string_free(error_string, TRUE);
286         exit(1);
287     }
288 
289     epan_dissect_init(&edt, cf->epan, TRUE, FALSE);
290     epan_dissect_prime_with_dfilter(&edt, sfcode);
291     epan_dissect_run_with_taps(&edt, cf->cd_t, &cf->rec,
292                                frame_tvbuff_new_buffer(&cf->provider, fdata, &cf->buf),
293                                fdata, NULL);
294     epan_dissect_cleanup(&edt);
295     remove_tap_listener(&th);
296     dfilter_free(sfcode);
297 
298     if (th.num_hdrs == 0) {
299         /* This "shouldn't happen", as our menu items shouldn't
300          * even be enabled if the selected packet isn't a TCP
301          * segment, as tcp_graph_selected_packet_enabled() is used
302          * to determine whether to enable any of our menu items. */
303         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
304                       "Selected packet isn't a TCP segment or is truncated");
305         return G_MAXUINT32;
306     }
307     /* XXX fix this later, we should show a dialog allowing the user
308        to select which session he wants here
309     */
310     if (th.num_hdrs > 1) {
311         /* can only handle a single tcp layer yet */
312         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
313                       "The selected packet has more than one TCP unique conversation "
314                       "in it.");
315         return G_MAXUINT32;
316     }
317 
318     /* For now, still always choose the first/only one */
319     th_stream = th.tcphdrs[0]->th_stream;
320 
321     for (int n = 0; n < th.num_hdrs; n++) {
322         free_address(&th.tcphdrs[n]->ip_src);
323         free_address(&th.tcphdrs[n]->ip_dst);
324         g_free(th.tcphdrs[n]);
325     }
326 
327     return th_stream;
328 }
329 
rtt_is_retrans(struct rtt_unack * list,unsigned int seqno)330 int rtt_is_retrans(struct rtt_unack *list, unsigned int seqno)
331 {
332     struct rtt_unack *u;
333 
334     for (u=list; u; u=u->next) {
335         if (tcp_seq_eq_or_after(seqno, u->seqno) &&
336             tcp_seq_before(seqno, u->end_seqno)) {
337             return TRUE;
338         }
339     }
340     return FALSE;
341 }
342 
343 struct rtt_unack *
rtt_get_new_unack(double time_val,unsigned int seqno,unsigned int seglen)344 rtt_get_new_unack(double time_val, unsigned int seqno, unsigned int seglen)
345 {
346     struct rtt_unack *u;
347 
348     u = g_new(struct rtt_unack, 1);
349     u->next  = NULL;
350     u->time  = time_val;
351     u->seqno = seqno;
352     u->end_seqno = seqno + seglen;
353     return u;
354 }
355 
rtt_put_unack_on_list(struct rtt_unack ** l,struct rtt_unack * new_unack)356 void rtt_put_unack_on_list(struct rtt_unack **l, struct rtt_unack *new_unack)
357 {
358     struct rtt_unack *u, *list = *l;
359 
360     for (u=list; u; u=u->next) {
361         if (!u->next) {
362             break;
363         }
364     }
365     if (u) {
366         u->next = new_unack;
367     } else {
368         *l = new_unack;
369     }
370 }
371 
rtt_delete_unack_from_list(struct rtt_unack ** l,struct rtt_unack * dead)372 void rtt_delete_unack_from_list(struct rtt_unack **l, struct rtt_unack *dead)
373 {
374     struct rtt_unack *u, *list = *l;
375 
376     if (!dead || !list) {
377         return;
378     }
379 
380     if (dead == list) {
381         *l = list->next;
382         g_free(list);
383     } else {
384         for (u=list; u; u=u->next) {
385             if (u->next == dead) {
386                 u->next = u->next->next;
387                 g_free(dead);
388                 break;
389             }
390         }
391     }
392 }
393 
rtt_destroy_unack_list(struct rtt_unack ** l)394 void rtt_destroy_unack_list(struct rtt_unack **l ) {
395     while (*l) {
396         struct rtt_unack *head = *l;
397         *l = head->next;
398         g_free(head);
399     }
400 }
401