2 * TCP stream statistics
3 * Originally from tcp_graph.c by Pavel Mores <pvl@uh.cz>
4 * Win32 port: rwh@unifiedtech.com
6 * Wireshark - Network traffic analyzer
7 * By Gerald Combs <gerald@wireshark.org>
8 * Copyright 1998 Gerald Combs
10 * SPDX-License-Identifier: GPL-2.0-or-later*/
18 #include <frame_tvbuff.h>
20 #include <epan/epan_dissect.h>
21 #include <epan/packet.h>
24 #include <epan/dissectors/packet-tcp.h>
26 #include "ui/simple_dialog.h"
28 #include "tap-tcp-stream.h"
30 typedef struct _tcp_scan_t {
31 struct segment *current;
39 tapall_tcpip_packet(void *pct, packet_info *pinfo, epan_dissect_t *edt _U_, const void *vip)
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;
45 if (tg->stream == tcphdr->th_stream
46 && (tg->src_address.type == AT_NONE || tg->dst_address.type == AT_NONE)) {
48 * We only know the stream number. Fill in our connection data.
49 * We assume that the server response is more interesting.
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;
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,
62 && tg->stream == tcphdr->th_stream)
64 struct segment *segment = g_new(struct segment, 1);
66 segment->num = pinfo->num;
67 segment->rel_secs = (guint32)pinfo->rel_ts.secs;
68 segment->rel_usecs = pinfo->rel_ts.nsecs/1000;
70 segment->abs_secs = (guint32)pinfo->abs_ts.secs;
71 segment->abs_usecs = pinfo->abs_ts.nsecs/1000;
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);
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));
90 if (ts->tg->segments) {
91 ts->last->next = segment;
93 ts->tg->segments = segment;
101 /* here we collect all the external data we will ever need */
103 graph_segment_list_get(capture_file *cf, struct tcp_graph *tg, gboolean stream_known)
105 struct segment current;
106 GString *error_string;
109 g_log(NULL, G_LOG_LEVEL_DEBUG, "graph_segment_list_get()");
116 struct tcpheader *header = select_tcpip_session(cf, ¤t);
118 if (tg->type == GRAPH_THROUGHPUT) {
119 ts.direction = COMPARE_CURR_DIR;
121 ts.direction = COMPARE_ANY_DIR;
124 /* Remember stream info in graph */
125 copy_address(&tg->src_address, ¤t.ip_src);
126 tg->src_port = current.th_sport;
127 copy_address(&tg->dst_address, ¤t.ip_dst);
128 tg->dst_port = current.th_dport;
129 tg->stream = header->th_stream;
131 ts.direction = COMPARE_ANY_DIR;
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
138 ts.current = ¤t;
141 error_string = register_tap_listener("tcp", &ts, "tcp", 0, NULL, tapall_tcpip_packet, NULL);
143 fprintf(stderr, "wireshark: Couldn't register tcp_graph tap: %s\n",
145 g_string_free(error_string, TRUE);
146 exit(1); /* XXX: fix this */
148 cf_retap_packets(cf);
149 remove_tap_listener(&ts);
153 graph_segment_list_free(struct tcp_graph *tg)
155 struct segment *segment;
157 while (tg->segments) {
158 segment = tg->segments->next;
159 g_free(tg->segments);
160 tg->segments = segment;
166 compare_headers(address *saddr1, address *daddr1, guint16 sport1, guint16 dport1, const address *saddr2, const address *daddr2, guint16 sport2, guint16 dport2, int dir)
170 dir1 = ((!(cmp_address(saddr1, saddr2))) &&
171 (!(cmp_address(daddr1, daddr2))) &&
175 if (dir == COMPARE_CURR_DIR) {
178 dir2 = ((!(cmp_address(saddr1, daddr2))) &&
179 (!(cmp_address(daddr1, saddr2))) &&
180 (sport1 == dport2) &&
187 get_num_dsegs(struct tcp_graph *tg)
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,
205 get_num_acks(struct tcp_graph *tg, int *num_sack_ranges)
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,
217 *num_sack_ranges += tmp->num_sack_ranges;
223 typedef struct _th_t {
225 #define MAX_SUPPORTED_TCP_HEADERS 8
226 struct tcpheader *tcphdrs[MAX_SUPPORTED_TCP_HEADERS];
230 tap_tcpip_packet(void *pct, packet_info *pinfo _U_, epan_dissect_t *edt _U_, const void *vip)
233 gboolean is_unique = TRUE;
234 th_t *th = (th_t *)pct;
235 const struct tcpheader *header = (const struct tcpheader *)vip;
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];
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,
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);
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
271 select_tcpip_session(capture_file *cf, struct segment *hdrs)
277 GString *error_string;
279 th_t th = {0, {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}};
285 fdata = cf->current_frame;
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);
294 /* dissect the current record */
295 if (!cf_read_record(cf, fdata)) {
296 return NULL; /* error reading the record */
300 error_string = register_tap_listener("tcp", &th, NULL, 0, NULL, tap_tcpip_packet, NULL);
302 fprintf(stderr, "wireshark: Couldn't register tcp_graph tap: %s\n",
304 g_string_free(error_string, TRUE);
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),
313 rel_ts = edt.pi.rel_ts;
314 epan_dissect_cleanup(&edt);
315 remove_tap_listener(&th);
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");
326 /* XXX fix this later, we should show a dialog allowing the user
327 to select which session he wants here
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 "
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;
342 hdrs->abs_secs = (guint32) fdata->abs_ts.secs;
343 hdrs->abs_usecs = fdata->abs_ts.nsecs/1000;
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];
357 int rtt_is_retrans(struct rtt_unack *list, unsigned int seqno)
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)) {
371 rtt_get_new_unack(double time_val, unsigned int seqno, unsigned int seglen)
375 u = g_new(struct rtt_unack, 1);
379 u->end_seqno = seqno + seglen;
383 void rtt_put_unack_on_list(struct rtt_unack **l, struct rtt_unack *new_unack)
385 struct rtt_unack *u, *list = *l;
387 for (u=list; u; u=u->next) {
399 void rtt_delete_unack_from_list(struct rtt_unack **l, struct rtt_unack *dead)
401 struct rtt_unack *u, *list = *l;
403 if (!dead || !list) {
411 for (u=list; u; u=u->next) {
412 if (u->next == dead) {
413 u->next = u->next->next;
421 void rtt_destroy_unack_list(struct rtt_unack **l ) {
423 struct rtt_unack *head = *l;
435 * indent-tabs-mode: nil
438 * ex: set shiftwidth=4 tabstop=8 expandtab:
439 * :indentSize=4:tabSize=8:noTabs=true: