nexmon – Blame information for rev 1
?pathlinks?
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, ¤t); |
||
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, ¤t.ip_src); |
||
136 | tg->src_port = current.th_sport; |
||
137 | copy_address(&tg->dst_address, ¤t.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 = ¤t; |
||
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 |