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 that does this is based on Vern Paxson's method in the tcpslice tool, with a few modifications:
libpcapnav doesn't use Vern's state-machine approach to deter- mine 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 like
Large snaplens in the captured data, where a single packet may contain many smaller packets.
One of these payload packets may have a caplen that actually yields directly to 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 crap, 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 off- set 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.