nexmon – Blame information for rev 1

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