libpcapnav
? Welcome! You're looking at the manual for libpcapnav
. Thanks for reading this.
libpcapnav
? libpcapnav
is a libpcap
wrapper library that allows navigation to
arbitrary locations in a tcpdump
trace file between reads.
The API is intentionally much like that of the pcap library.
You can navigate in trace files both in time and space: you
can jump to a packet which is at appr. 2/3 of the trace, or
you can jump as closely as possible to a packet with a given
timestamp, and then read packets from there. In addition, the
API provides convenience functions for manipulating timeval
structures.
Like libpcap
, this library handles things through an opaque
handle struct. For trace file navigation and reading packets,
this handle is enough. If you need to apply BPF filters or
write packets to disk, you can access the familiar pcap
handle that is used internally.
At the core of libpcapnav
is the ability to resynchronize to
the sequence of packets contained in a tcpdump
trace file
at arbitrary location of the file position indicator. The
algorithm is based on Vern Paxson's method from the the tcpslice
tool, that basically works as follows: the point near which the
file position indicator is to be synchronized with the packet
sequence is undershot a little bit, as it is much easier to
scan forwards to the desired location, once the packet sequence
has been detected. The file is scanned from that initial
offset in single-byte steps, at each step assuming a libpcap
packet header is present and sanity-checking the values read.
Several checks analyze this potential header for sane timestamps,
capture lengths etc. If the header appears valid, the next
packet header is examined in a similar function, based upon
the offset that the checked header provides. If a sequence
of three packets seems valid, the algorithm considers the
file position pointer to be synchronized with the packet flow
and scans as closely as possible to the desired location.
If the synchronization point is supposed to be a packet with
a given timestamp, some interpolation is done and the process
repeated, until the packet closest to the desired timestamp
has been found.
libpcapnav
's algorithm contains a few modifications that are explained
in gory detail in the Netdude
Freenix paper,
and briefly listed here:
libpcapnav
doesn't use Vern's state-machine approach to determine
definitive header matches. I've done a lot of my
testing with a trace that was captured while NFS-copying
another trace file, thus containing lots of "bogus" headers
to make things fun, and I've seen a number of problems in
this case. This data causes a number of nasty problems, such as
large snaplens in the captured data (where a single packet
may contain many smaller packets) or payload packets that
have a caplen that causes the next packet to be read
directly from the next valid header.
Much of this should be handled through invalid timestamps,
but this is not 100% reliable.
To rectify this, pcapnav uses a different approach: once a header is found that does not instantly appear to be invalid, the chain of packets that it starts is followed, up to a maximum number of packets or until we're out of buffer space.
For this, buffers already containing data loaded from disk
are used as much as possible, but when this buffer doesn't
suffice, more data is loaded from disk. The hope is that
most attempts will point to invalid headers anyway so that
this additional load never happens unless we have good
reason to believe we've actually found a good header. The
difference between PCAPNAV_PERHAPS
and
PCAPNAV_DEFINITELY
(explained in detail
later in this document)
is then based on the length of the chain found.
While checking headers, the best valid header (ie the one with the longest chain) is remembered, as well as the offset in the trace that'll be the successor of this packet, so that it isn't confused with a "new" good header.
The fun part without doubt are header clashes. A clash in this new system occurs when two headers have the same, maximum, chain length and the same level of reliability of the chain lengths (eg, the chain search could have been stopped because we were out of buffer space or because we have hit the limit of packets we check — the latter is considered more reliable).
If we hit a clash, we simply forget the old best match and keep looking after the clash packet. If we cannot find any better headers afterwards, we return a clash, otherwise the best match found afterwards.
I've seen traces with rather strange final packet headers, containing invalid caplen/len field values and packet data. To make sure we don't miss the last few correct packet headers, I've added some padding space and thus start looking for the last packet in the trace a bit earlier in the file. As the last-packet timestamp and offset is buffered in the pcapnav_t handle anyway, this performance hit is probably negligible.
To find the last packet in a trace, we now go back a lot more from the end of a trace, then find a packet more reliably by using the chain approach described above, and then use pcap to iterate to the last valid packet. Slower, but safer.
A buffer abstraction was introduced to help reduce the number of local variables and parameters to functions. See pcapnav_buf.h.
The original tcpslice version used the PACKET_HDR_LEN
macro,
yielding the size of a struct pcap_pkthdr, even when the
trace file at hand actually uses the extended, larger
patched headers.