LATER... ei_kerberos_kdc_session_key ...
[wireshark-sm.git] / ui / tap-tcp-stream.c
blob25ea10e24ead4c226abec3faa4aa0aed83f373cf
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
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
13 #include "config.h"
16 #include <stdlib.h>
18 #include <file.h>
19 #include <frame_tvbuff.h>
21 #include <epan/epan_dissect.h>
22 #include <epan/packet.h>
23 #include <epan/tap.h>
25 #include <epan/dissectors/packet-tcp.h>
27 #include "ui/simple_dialog.h"
29 #include "tap-tcp-stream.h"
31 typedef struct _tcp_scan_t {
32 int direction;
33 struct tcp_graph *tg;
34 struct segment *last;
35 } tcp_scan_t;
38 static tap_packet_status
39 tapall_tcpip_packet(void *pct, packet_info *pinfo, epan_dissect_t *edt _U_, const void *vip, tap_flags_t flags _U_)
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 bool server_is_src;
52 if (tcphdr->th_flags & TH_SYN) {
53 if (tcphdr->th_flags & TH_ACK) {
54 /* SYN-ACK packet, so the server is the source. */
55 server_is_src = true;
56 } else {
57 /* SYN packet, so the server is the destination. */
58 server_is_src = false;
60 } else {
61 /* Fallback to assuming the lower numbered port is the server. */
62 server_is_src = tcphdr->th_sport < tcphdr->th_dport;
64 if (server_is_src) {
65 copy_address(&tg->src_address, &tcphdr->ip_src);
66 tg->src_port = tcphdr->th_sport;
67 copy_address(&tg->dst_address, &tcphdr->ip_dst);
68 tg->dst_port = tcphdr->th_dport;
69 } else {
70 copy_address(&tg->src_address, &tcphdr->ip_dst);
71 tg->src_port = tcphdr->th_dport;
72 copy_address(&tg->dst_address, &tcphdr->ip_src);
73 tg->dst_port = tcphdr->th_sport;
77 if (compare_headers(&tg->src_address, &tg->dst_address,
78 tg->src_port, tg->dst_port,
79 &tcphdr->ip_src, &tcphdr->ip_dst,
80 tcphdr->th_sport, tcphdr->th_dport,
81 ts->direction)
82 && tg->stream == tcphdr->th_stream)
84 struct segment *segment = g_new(struct segment, 1);
85 segment->next = NULL;
86 segment->num = pinfo->num;
87 segment->rel_secs = (uint32_t)pinfo->rel_ts.secs;
88 segment->rel_usecs = pinfo->rel_ts.nsecs/1000;
89 /* Currently unused
90 segment->abs_secs = pinfo->abs_ts.secs;
91 segment->abs_usecs = pinfo->abs_ts.nsecs/1000;
93 /* tcphdr->th_rawseq is always the absolute sequence number.
94 * tcphdr->th_seq is either the relative or absolute sequence number
95 * depending on the TCP dissector preferences.
96 * The sack entries are also either the relative or absolute sequence
97 * number depending on the TCP dissector preferences.
98 * The TCP stream graphs have their own action / button press to
99 * switch between relative and absolute sequence numbers on the fly;
100 * if the TCP dissector hasn't calculated the relative sequence numbers,
101 * the tap will do so. (XXX - The calculation is cheap enough that we
102 * could do it here and store the offsets at the graph level to save
103 * memory. The TCP dissector could include its calculated base seq in
104 * the tap information to ensure consistency.)
106 segment->th_seq = tcphdr->th_seq;
107 segment->th_ack = tcphdr->th_ack;
108 segment->th_rawseq = tcphdr->th_rawseq;
109 segment->th_rawack = tcphdr->th_rawack;
110 segment->th_win = tcphdr->th_win;
111 segment->th_flags = tcphdr->th_flags;
112 segment->th_sport = tcphdr->th_sport;
113 segment->th_dport = tcphdr->th_dport;
114 segment->th_seglen = tcphdr->th_seglen;
115 copy_address(&segment->ip_src, &tcphdr->ip_src);
116 copy_address(&segment->ip_dst, &tcphdr->ip_dst);
118 segment->num_sack_ranges = MIN(MAX_TCP_SACK_RANGES, tcphdr->num_sack_ranges);
119 if (segment->num_sack_ranges > 0) {
120 /* Copy entries in the order they happen */
121 memcpy(&segment->sack_left_edge, &tcphdr->sack_left_edge, sizeof(segment->sack_left_edge));
122 memcpy(&segment->sack_right_edge, &tcphdr->sack_right_edge, sizeof(segment->sack_right_edge));
125 if (ts->tg->segments) {
126 ts->last->next = segment;
127 } else {
128 ts->tg->segments = segment;
130 ts->last = segment;
133 return TAP_PACKET_DONT_REDRAW;
136 /* here we collect all the external data we will ever need */
137 void
138 graph_segment_list_get(capture_file *cf, struct tcp_graph *tg)
140 GString *error_string;
141 tcp_scan_t ts;
143 if (!cf || !tg) {
144 return;
147 /* rescan all the packets and pick up all interesting tcp headers.
148 * we only filter for TCP here for speed and do the actual compare
149 * in the tap listener
151 ts.direction = COMPARE_ANY_DIR;
152 ts.tg = tg;
153 ts.last = NULL;
154 error_string = register_tap_listener("tcp", &ts, "tcp", 0, NULL, tapall_tcpip_packet, NULL, NULL);
155 if (error_string) {
156 fprintf(stderr, "wireshark: Couldn't register tcp_graph tap: %s\n",
157 error_string->str);
158 g_string_free(error_string, TRUE);
159 exit(1); /* XXX: fix this */
161 cf_retap_packets(cf);
162 remove_tap_listener(&ts);
165 void
166 graph_segment_list_free(struct tcp_graph *tg)
168 struct segment *segment;
170 free_address(&tg->src_address);
171 free_address(&tg->dst_address);
173 while (tg->segments) {
174 segment = tg->segments->next;
175 free_address(&tg->segments->ip_src);
176 free_address(&tg->segments->ip_dst);
177 g_free(tg->segments);
178 tg->segments = segment;
183 compare_headers(address *saddr1, address *daddr1, uint16_t sport1, uint16_t dport1, const address *saddr2, const address *daddr2, uint16_t sport2, uint16_t dport2, int dir)
185 int dir1, dir2;
187 dir1 = ((!(cmp_address(saddr1, saddr2))) &&
188 (!(cmp_address(daddr1, daddr2))) &&
189 (sport1==sport2) &&
190 (dport1==dport2));
192 if (dir == COMPARE_CURR_DIR) {
193 return dir1;
194 } else {
195 dir2 = ((!(cmp_address(saddr1, daddr2))) &&
196 (!(cmp_address(daddr1, saddr2))) &&
197 (sport1 == dport2) &&
198 (dport1 == sport2));
199 return dir1 || dir2;
204 get_num_dsegs(struct tcp_graph *tg)
206 int count;
207 struct segment *tmp;
209 for (tmp=tg->segments, count=0; tmp; tmp=tmp->next) {
210 if (compare_headers(&tg->src_address, &tg->dst_address,
211 tg->src_port, tg->dst_port,
212 &tmp->ip_src, &tmp->ip_dst,
213 tmp->th_sport, tmp->th_dport,
214 COMPARE_CURR_DIR)) {
215 count++;
218 return count;
222 get_num_acks(struct tcp_graph *tg, int *num_sack_ranges)
224 int count;
225 struct segment *tmp;
227 for (tmp = tg->segments, count=0; tmp; tmp = tmp->next) {
228 if (!compare_headers(&tg->src_address, &tg->dst_address,
229 tg->src_port, tg->dst_port,
230 &tmp->ip_src, &tmp->ip_dst,
231 tmp->th_sport, tmp->th_dport,
232 COMPARE_CURR_DIR)) {
233 count++;
234 *num_sack_ranges += tmp->num_sack_ranges;
237 return count;
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;
246 static tap_packet_status
247 tap_tcpip_packet(void *pct, packet_info *pinfo _U_, epan_dissect_t *edt _U_, const void *vip, tap_flags_t flags _U_)
249 int n;
250 bool is_unique = true;
251 th_t *th = (th_t *)pct;
252 const struct tcpheader *header = (const struct tcpheader *)vip;
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];
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 is_unique = false;
264 break;
268 /* Add address if unique and have space for it */
269 if (is_unique && (th->num_hdrs < MAX_SUPPORTED_TCP_HEADERS)) {
270 /* Need to take a deep copy of the tap struct, it may not be valid
271 to read after this function returns? */
272 th->tcphdrs[th->num_hdrs] = g_new(struct tcpheader, 1);
273 *(th->tcphdrs[th->num_hdrs]) = *header;
274 copy_address(&th->tcphdrs[th->num_hdrs]->ip_src, &header->ip_src);
275 copy_address(&th->tcphdrs[th->num_hdrs]->ip_dst, &header->ip_dst);
277 th->num_hdrs++;
280 return TAP_PACKET_DONT_REDRAW;
283 /* XXX should be enhanced so that if we have multiple TCP layers in the trace
284 * then present the user with a dialog where the user can select WHICH tcp
285 * session to graph.
287 uint32_t
288 select_tcpip_session(capture_file *cf)
290 frame_data *fdata;
291 epan_dissect_t edt;
292 dfilter_t *sfcode;
293 uint32_t th_stream;
294 df_error_t *df_err;
295 GString *error_string;
296 th_t th = {0, {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}};
298 if (!cf) {
299 return UINT32_MAX;
302 /* no real filter yet */
303 if (!dfilter_compile("tcp", &sfcode, &df_err)) {
304 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", df_err->msg);
305 df_error_free(&df_err);
306 return UINT32_MAX;
309 /* dissect the current record */
310 if (!cf_read_current_record(cf)) {
311 return UINT32_MAX; /* error reading the record */
314 fdata = cf->current_frame;
316 error_string = register_tap_listener("tcp", &th, NULL, 0, NULL, tap_tcpip_packet, NULL, NULL);
317 if (error_string) {
318 fprintf(stderr, "wireshark: Couldn't register tcp_graph tap: %s\n",
319 error_string->str);
320 g_string_free(error_string, TRUE);
321 exit(1);
324 epan_dissect_init(&edt, cf->epan, true, false);
325 epan_dissect_prime_with_dfilter(&edt, sfcode);
326 epan_dissect_run_with_taps(&edt, cf->cd_t, &cf->rec,
327 frame_tvbuff_new_buffer(&cf->provider, fdata, &cf->buf),
328 fdata, NULL);
329 epan_dissect_cleanup(&edt);
330 remove_tap_listener(&th);
331 dfilter_free(sfcode);
333 if (th.num_hdrs == 0) {
334 /* This "shouldn't happen", as our menu items shouldn't
335 * even be enabled if the selected packet isn't a TCP
336 * segment, as tcp_graph_selected_packet_enabled() is used
337 * to determine whether to enable any of our menu items. */
338 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
339 "Selected packet isn't a TCP segment or is truncated");
340 return UINT32_MAX;
342 /* XXX fix this later, we should show a dialog allowing the user
343 to select which session he wants here
345 if (th.num_hdrs > 1) {
346 /* can only handle a single tcp layer yet */
347 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
348 "The selected packet has more than one TCP unique conversation "
349 "in it.");
350 return UINT32_MAX;
353 /* For now, still always choose the first/only one */
354 th_stream = th.tcphdrs[0]->th_stream;
356 for (int n = 0; n < th.num_hdrs; n++) {
357 free_address(&th.tcphdrs[n]->ip_src);
358 free_address(&th.tcphdrs[n]->ip_dst);
359 g_free(th.tcphdrs[n]);
362 return th_stream;
365 int rtt_is_retrans(struct rtt_unack *list, unsigned int seqno)
367 struct rtt_unack *u;
369 for (u=list; u; u=u->next) {
370 if (tcp_seq_eq_or_after(seqno, u->seqno) &&
371 tcp_seq_before(seqno, u->end_seqno)) {
372 return true;
375 return false;
378 struct rtt_unack *
379 rtt_get_new_unack(double time_val, unsigned int seqno, unsigned int seglen)
381 struct rtt_unack *u;
383 u = g_new(struct rtt_unack, 1);
384 u->next = NULL;
385 u->time = time_val;
386 u->seqno = seqno;
387 u->end_seqno = seqno + seglen;
388 return u;
391 void rtt_put_unack_on_list(struct rtt_unack **l, struct rtt_unack *new_unack)
393 struct rtt_unack *u, *list = *l;
395 for (u=list; u; u=u->next) {
396 if (!u->next) {
397 break;
400 if (u) {
401 u->next = new_unack;
402 } else {
403 *l = new_unack;
407 void rtt_delete_unack_from_list(struct rtt_unack **l, struct rtt_unack *dead)
409 struct rtt_unack *u, *list = *l;
411 if (!dead || !list) {
412 return;
415 if (dead == list) {
416 *l = list->next;
417 g_free(list);
418 } else {
419 for (u=list; u; u=u->next) {
420 if (u->next == dead) {
421 u->next = u->next->next;
422 g_free(dead);
423 break;
429 void rtt_destroy_unack_list(struct rtt_unack **l ) {
430 while (*l) {
431 struct rtt_unack *head = *l;
432 *l = head->next;
433 g_free(head);