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