1 /*
2
3 Copyright (C) 2002 - 2007 Christian Kreibich
4
5 Permission is hereby granted, free of charge, to any person obtaining a copy
6 of this software and associated documentation files (the "Software"), to
7 deal in the Software without restriction, including without limitation the
8 rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9 sell copies of the Software, and to permit persons to whom the Software is
10 furnished to do so, subject to the following conditions:
11
12 The above copyright notice and this permission notice shall be included in
13 all copies of the Software and its documentation and acknowledgment shall be
14 given in the documentation and software packages that this Software was
15 used.
16
17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21 IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
24 */
25 #if HAVE_CONFIG_H
26 # include <config.h>
27 #endif
28
29 #include <string.h>
30 #include <errno.h>
31 #include "pcapnav_private.h"
32 #include "pcapnav_debug.h"
33 #include "pcapnav_private.h"
34 #include "pcapnav_macros.h"
35 #include "pcapnav_header.h"
36 #include "pcapnav_util.h"
37 #include "pcapnav_trace.h"
38
39
40 /**
41 * trace_get_interpolated_position - estimates offset of a timestamp in trace.
42 * @min_time: start of time frame.
43 * @min_pos: offset in file of @min_time packet.
44 * @max_time: end of time frame.
45 * @max_pos: offset in file of @max_time packet.
46 * @desired_time: timestamp of packet whose offset is interpolated.
47 *
48 * Given a timestamp on each side of desired_time and their offsets
49 * in the file, the function returns the interpolated position of the
50 * @desired_time packet. Nothing is looked at on disk, the value is
51 * calculated only.
52 *
53 * Returns: a negative value if @desired_time is outside the [@min_time, @max_time]
54 * interval.
55 */
56 static off_t
trace_get_interpolated_position(struct bpf_timeval * min_time,off_t min_pos,struct bpf_timeval * max_time,off_t max_pos,struct bpf_timeval * desired_time)57 trace_get_interpolated_position( struct bpf_timeval *min_time, off_t min_pos,
58 struct bpf_timeval *max_time, off_t max_pos,
59 struct bpf_timeval *desired_time )
60 {
61 double full_span = __pcapnav_util_timeval_diff(max_time, min_time);
62 double desired_span = __pcapnav_util_timeval_diff(desired_time, min_time);
63 off_t full_span_pos = max_pos - min_pos;
64 double fractional_offset = desired_span / full_span;
65
66 if ( fractional_offset + 0.0000005 < 0.0 || fractional_offset - 0.0000005 > 1.0 )
67 return -1;
68
69 return (min_pos + (off_t) (fractional_offset * (double) full_span_pos));
70 }
71
72
73 /**
74 * trace_read_up_to_timestamp - reads packets until timestamp is found.
75 * @pn: pcapnav handle.
76 * @desired_time: timestamp that stops the read.
77 *
78 * The function reads packets linearly until one with a timestamp equal
79 * or larger than @desired_time is found. Then, it positions the stream
80 * so that the next read will start at that packet.
81 *
82 * Returns: PCAPNAV_DEFINITELY if timestamp found, or PCAPNAV_ERROR.
83 */
84 static pcapnav_result_t
trace_read_up_to_timestamp(pcapnav_t * pn,struct bpf_timeval * desired_time)85 trace_read_up_to_timestamp(pcapnav_t *pn, struct bpf_timeval *desired_time)
86 {
87 struct pcap_pkthdr hdr;
88 const u_char *buf;
89 off_t pos;
90 int status = PCAPNAV_NONE;
91
92 for ( ; ; )
93 {
94 struct bpf_timeval *timestamp;
95
96 pos = FTELL(pn->fp);
97 buf = pcapnav_next(pn, &hdr);
98
99 if (buf == NULL)
100 {
101 if (feof(pn->fp))
102 {
103 status = PCAPNAV_ERROR;
104 clearerr(pn->fp);
105 }
106
107 break;
108 }
109
110 timestamp = &hdr.ts;
111
112 if ( ! __pcapnav_util_timeval_less_than(timestamp, desired_time))
113 {
114 status = PCAPNAV_DEFINITELY;
115 break;
116 }
117 }
118
119 if (FSEEK(pn->fp, pos, SEEK_SET) < 0)
120 status = PCAPNAV_ERROR;
121
122 return status;
123 }
124
125
126 pcapnav_result_t
__pcapnav_trace_find_packet_at_timestamp(pcapnav_t * pn,struct bpf_timeval * desired_time)127 __pcapnav_trace_find_packet_at_timestamp(pcapnav_t *pn,
128 struct bpf_timeval *desired_time)
129 {
130 struct bpf_timeval min_time, max_time;
131 off_t desired_pos, present_pos, min_pos, max_pos;
132 u_char *hdrpos;
133 struct pcap_pkthdr hdr;
134 pcapnav_result_t status = PCAPNAV_NONE;
135
136 min_time = pn->start_time;
137 min_pos = pn->start_offset;
138 max_time = pn->end_time;
139 max_pos = pn->end_offset;
140
141 /* Handle the special cases -- requested timestamp beyond
142 * end of trace or below start of it.
143 */
144
145 if (__pcapnav_util_timeval_less_than(&max_time, desired_time))
146 {
147 if (FSEEK(pn->fp, max_pos, SEEK_SET) < 0)
148 return PCAPNAV_ERROR;
149
150 return PCAPNAV_NONE;
151 }
152
153 if (__pcapnav_util_timeval_less_than(desired_time, &min_time))
154 {
155 if (FSEEK(pn->fp, min_pos, SEEK_SET) < 0)
156 return PCAPNAV_ERROR;
157
158 return PCAPNAV_NONE;
159 }
160
161 /* We actually need to look for the right spot in the trace.
162 * Interpolate the position in the trace based upon the
163 * timestamps, and loop until positioned correctly. During
164 * each iteration, the interpolation interval shrinks and
165 * thus becomes more accurate, reflecting local values.
166 */
167
168 for ( ; ; )
169 {
170 D(("find_packet iteration ...\n"));
171 desired_pos =
172 trace_get_interpolated_position(&min_time, min_pos,
173 &max_time, max_pos,
174 desired_time);
175
176 if (desired_pos < 0)
177 {
178 status = PCAPNAV_ERROR;
179 D(("Negative desired_pos\n"));
180 break;
181 }
182
183 present_pos = FTELL(pn->fp);
184
185 if ((present_pos <= desired_pos) &&
186 (desired_pos - present_pos < (off_t)STRAIGHT_SCAN_THRESHOLD(pn)))
187 {
188 /* we're close enough to just blindly read ahead */
189
190 status = trace_read_up_to_timestamp(pn, desired_time);
191 D(("Blind read-ahead\n"));
192 break;
193 }
194
195 /* Undershoot the target a little bit - it's much easier to
196 * then scan straight forward than to try to read backwards ...
197 */
198 desired_pos -= STRAIGHT_SCAN_THRESHOLD(pn) / 2;
199
200 if (desired_pos < min_pos)
201 desired_pos = min_pos;
202
203 if (FSEEK(pn->fp, desired_pos, SEEK_SET) < 0)
204 {
205 D(("fseek() failed: %s\n",strerror(errno)));
206 status = PCAPNAV_ERROR;
207 break;
208 }
209
210 D(("SEEK AT BUFFER START: %lu\n", (long unsigned) desired_pos));
211
212 if (__pcapnav_buf_fill(pn->search_buf, pn->fp, 0, 0, pn->search_buf->size) == 0)
213 {
214 /* This shouldn't ever happen because we try to
215 * undershoot, unless the dump file has only a
216 * couple packets in it ...
217 */
218
219 status = PCAPNAV_ERROR;
220 D(("Buffer fill failed.\n"));
221 break;
222 }
223
224
225 if ( (status = __pcapnav_header_search(pn, &hdrpos, &hdr)) !=
226 PCAPNAV_DEFINITELY)
227 {
228 D(("can't find header at position %lu in dump file -- result is %i\n",
229 (long unsigned) desired_pos, status));
230
231 break;
232 }
233
234 /* desired_pos is the beginning of the buffer that was
235 * filled above. hdrpos is the actual beginning of a header
236 * in that chunk, so adjust desired_pos to match the
237 * actual beginning of the packet.
238 */
239 desired_pos += (hdrpos - pn->search_buf->buf);
240
241 /* Seek to the beginning of the header. */
242 if (FSEEK(pn->fp, desired_pos, SEEK_SET) < 0)
243 {
244 D(("fseek() failed: %s\n", strerror(errno)));
245 status = PCAPNAV_ERROR;
246 break;
247 }
248
249 if (__pcapnav_util_timeval_less_than(&hdr.ts, desired_time))
250 {
251 /* We're too early in the file. */
252 min_time = hdr.ts;
253 min_pos = desired_pos;
254 }
255 else if (__pcapnav_util_timeval_less_than(desired_time, &hdr.ts))
256 {
257 /* We're too late in the file. */
258 max_time = hdr.ts;
259 max_pos = desired_pos;
260 }
261 else
262 {
263 /* got it! */
264 D(("Success!\n"));
265 break;
266 }
267 }
268
269 D(("Return from find_packet, %i\n", status));
270 return status;
271 }
272
273
274 /* NOTE -- offset 0 means first packet -- offsets are relative to
275 * the end of the pcap file header.
276 */
277 pcapnav_result_t
__pcapnav_trace_find_packet_at_offset(pcapnav_t * pn,off_t offset,pcapnav_cmp_t boundary)278 __pcapnav_trace_find_packet_at_offset(pcapnav_t *pn,
279 off_t offset,
280 pcapnav_cmp_t boundary)
281 {
282 off_t current, next_off;
283 pcapnav_result_t status = PCAPNAV_NONE;
284 struct pcap_pkthdr hdr;
285 u_char *hdrpos = NULL;
286
287 current = offset;
288 D(("Trying to find packet at %lu with policy %i\n",
289 (long unsigned) offset, boundary));
290
291 /* Handle the special cases -- requested offset beyond
292 * end of trace or below beginning of actual packets.
293 */
294 if (offset + (off_t) sizeof(struct pcap_file_header) <= pn->start_offset)
295 {
296 D(("Setting to offset 0 as given offset too small\n"));
297 pcapnav_set_offset(pn, 0);
298 return PCAPNAV_DEFINITELY;
299 }
300
301 /* If an offset is requested that is too close to the end of the
302 * trace, we return the offset of the last valid packet.
303 */
304 if (offset + (off_t) sizeof(struct pcap_file_header) >= pn->size)
305 {
306 D(("Setting to last valid offset (%llu) as given offset %llu too large\n",
307 pn->end_offset, offset));
308
309 pcapnav_set_offset(pn, pn->end_offset);
310 return PCAPNAV_DEFINITELY;
311 }
312
313 for ( ; ; )
314 {
315 /* Undershoot the target a little bit - it's much easier to
316 * then scan straight forward than to try to read backwards ...
317 */
318 current -= STRAIGHT_SCAN_THRESHOLD(pn);
319
320 if (current + (off_t) sizeof(struct pcap_file_header) < pn->start_offset)
321 current = 0;
322
323 D(("Offset seek iteration: %lu\n", (long unsigned) current));
324
325 if (FSEEK(pn->fp, current + sizeof(struct pcap_file_header), SEEK_SET) < 0)
326 {
327 D(("fseek() failed: %s\n", strerror(errno)));
328 status = PCAPNAV_ERROR;
329 break;
330 }
331
332 if (__pcapnav_buf_fill(pn->search_buf, pn->fp, 0, 0, pn->search_buf->size) == 0)
333 {
334 /* This shouldn't ever happen because we try to
335 * undershoot, unless the dump file has only a
336 * couple packets in it ...
337 */
338
339 status = PCAPNAV_ERROR;
340 D(("Buffer fill failed.\n"));
341 break;
342 }
343
344 if ( (status = __pcapnav_header_search(pn, &hdrpos, &hdr)) !=
345 PCAPNAV_DEFINITELY)
346 {
347 D(("Can't find header at position %lu in dump file -- result is %i\n",
348 (long unsigned) current, status));
349
350 return PCAPNAV_NONE;
351 }
352
353 if (current + (hdrpos - pn->search_buf->buf) <= offset)
354 {
355 current += (hdrpos - pn->search_buf->buf);
356 break;
357 }
358 }
359
360 /* Now follow the chain up as close as
361 * possible to the desired offset.
362 */
363 pcapnav_set_offset(pn, current);
364 D(("Starting scan from packet at %lu, aiming at %lu\n",
365 (long unsigned) current, (long unsigned) offset));
366
367 switch (boundary)
368 {
369 case PCAPNAV_CMP_LEQ:
370 while (pcapnav_get_offset(pn) <= offset)
371 {
372 current = pcapnav_get_offset(pn);
373
374 if (!pcapnav_next(pn, &hdr))
375 break;
376
377 D(("Packet at %lu, next one at %lu\n",
378 (long unsigned) current, (long unsigned) pcapnav_get_offset(pn)));
379 }
380 break;
381
382 case PCAPNAV_CMP_GEQ:
383 while (current < offset)
384 {
385 if (!pcapnav_next(pn, &hdr))
386 break;
387
388 current = pcapnav_get_offset(pn);
389 D(("Packet at %lu\n", (long unsigned) current));
390 }
391 break;
392
393 case PCAPNAV_CMP_ANY:
394 default:
395
396 while (pcapnav_get_offset(pn) <= offset)
397 {
398 current = pcapnav_get_offset(pn);
399
400 if (!pcapnav_next(pn, &hdr))
401 break;
402
403 /* If we scan past the goal, check if the offset we get
404 * to is closer to the goal as the one we found below
405 * the goal. Then use whichever packet is closer.
406 */
407 if ( (next_off = pcapnav_get_offset(pn)) > offset)
408 {
409 if ((next_off - offset) < (offset - current))
410 {
411 current = next_off;
412 break;
413 }
414 }
415
416 D(("Packet at %lu, next one at %lu\n",
417 (long unsigned) current, (long unsigned) pcapnav_get_offset(pn)));
418 }
419 }
420
421 pcapnav_set_offset(pn, current);
422
423 return status;
424 }
425
426
427 void
__pcapnav_trace_find_start(pcapnav_t * pn)428 __pcapnav_trace_find_start(pcapnav_t *pn)
429 {
430 struct pcap_pkthdr hdr, hdr2;
431 off_t old_pos;
432
433 memset(&pn->start_time, 0, sizeof(struct bpf_timeval));
434
435 if ((old_pos = FTELL(pn->fp)) < 0)
436 {
437 D(("ftell() failed: %s\n", strerror(errno)));
438 return;
439 }
440
441 if (FSEEK(pn->fp, sizeof(struct pcap_file_header), SEEK_SET) < 0)
442 {
443 D(("fseek() failed: %s\n", strerror(errno)));
444 return;
445 }
446
447 if (fread((void *) &hdr, sizeof(struct pcap_pkthdr), 1, pn->fp) != 1)
448 {
449 D(("fread() failed: %s\n", strerror(errno)));
450 return;
451 }
452
453 if (FSEEK(pn->fp, old_pos, SEEK_SET) < 0)
454 {
455 D(("fseek() to old position failed: %s\n", strerror(errno)));
456 return;
457 }
458
459 pn->start_offset = sizeof(struct pcap_file_header);
460
461 __pcapnav_header_extract(pn, (u_char*) &hdr, &hdr2);
462 pn->start_time.tv_sec = hdr2.ts.tv_sec;
463 pn->start_time.tv_usec = hdr2.ts.tv_usec;
464 }
465
466
467 void
__pcapnav_trace_find_end(pcapnav_t * pn)468 __pcapnav_trace_find_end(pcapnav_t *pn)
469 {
470 off_t num_bytes;
471 u_char *hdrpos;
472 struct pcap_pkthdr hdr;
473 off_t offset_orig, tmp_offset;
474
475 if ((pn->start_time.tv_sec == 0) &&
476 (pn->start_time.tv_usec == 0))
477 __pcapnav_trace_find_start(pn);
478
479 pn->end_offset = 0;
480 memset(&pn->end_time, 0, sizeof(struct bpf_timeval));
481
482 /* We go back in the trace far enough to see MAX_CHAIN_LENGTH
483 * consecutive packets, but still use only the (smaller) search_buf.
484 * Once we've found a valid packet, we use pcap to iterate to
485 * the last valid header. This'll involve disk I/O, but is a
486 * safer method than jumping near the end of the trace were we
487 * cannot scan enough packets to be really sure.
488 */
489
490 /* Remember current position */
491 offset_orig = pcapnav_get_offset(pn);
492
493 if (pn->trace.length < (int) MAX_PACKET_SIZE(pn) * MAX_CHAIN_LENGTH)
494 num_bytes = pn->trace.length;
495 else
496 num_bytes = MAX_PACKET_SIZE(pn) * MAX_CHAIN_LENGTH;
497
498 __pcapnav_buf_fill(pn->search_buf, pn->fp, -num_bytes, SEEK_END, pn->search_buf->size);
499 if (__pcapnav_header_search(pn, &hdrpos, &hdr) != PCAPNAV_DEFINITELY)
500 {
501 D(("Header search failed\n"));
502 goto cleanup_return;
503 }
504
505 tmp_offset = pn->search_buf->offset + (hdrpos - pn->search_buf->buf);
506 D(("Definite header at offset %lu\n", (long unsigned) tmp_offset));
507 pcapnav_set_offset(pn, tmp_offset - sizeof(struct pcap_file_header));
508
509 pn->end_time = hdr.ts;
510 pn->end_caplen = hdr.caplen;
511 pn->end_offset = pcapnav_get_offset(pn);
512
513 /* Select last packet so that the offset is pointing
514 * AT the last packet, not to the offset following it!
515 */
516
517 for ( ; ; )
518 {
519 tmp_offset = pcapnav_get_offset(pn) + sizeof(struct pcap_file_header);
520
521 if (!pcap_next(pn->pcap, &hdr))
522 break;
523
524 pn->end_time = hdr.ts;
525 pn->end_caplen = hdr.caplen;
526 pn->end_offset = tmp_offset;
527 }
528
529 D(("Finished -- last valid packet is at %lu, at %u.%u, captured %u bytes.\n",
530 (long unsigned) pn->end_offset, (unsigned) pn->end_time.tv_sec,
531 (unsigned) pn->end_time.tv_usec, pn->end_caplen));
532
533 cleanup_return:
534 /* Rewind to old position */
535 pcapnav_set_offset(pn, offset_orig);
536 }
537