3.1.1 - Accessing headers in a packet

Once you have a fully decoded packet you can access its state and retrieve headers. All packets delivered to PcapPacketHandler are fully decoded and ready for access. Most of the header accessor methods reside in the base class of PcapPacket which is JPacket.

You use JPacket.hasHeader() and JPacket.getHeader() methods to first check if a header is present in the packet, and then retrieve an instance of that header. You must always check if a header is present before trying to retrieve the header. If you do not your code will fail very quickly if not immediately. Note that checking for presence of a header is extremely efficient. The scanner, which is responsible for decoding the top-level state information, simply sets a single flag in a integer that represents a particular header. So a check is simply a single binary bitmask operation which is very very fast and efficient. If the bit is set, the header is present, if the bit is not set, the header is not there. This was a key design goal, to ensure that virtually no penalty is received for checking for presence of headers.

Once we know for a fact that there is a header we can use JPacket.getHeader() method to retrieve an instance of that particular header. Again, for performance reasons, the getHeader(<? extends JHeader> header) expects you to supply a header object of the correct type. The header retrieval is also a simple and fast operation. The packet's state contains an byte offset into the packet's buffer for the required header and its length. These are stored in an native array and are looked up very efficiently. The supplied user header is then peered with the exact memory location the header occupies. No copies of state or data actually take place. Simply a the native memory reference is set to which points at exactly where the header resides in the packet's buffer. Once peered, the header can only access its that particular memory segment, and an exception will be thrown if it tries to go out if its bounds.

There is a convenience method JPacket.hasHeader(<? extends JHeader> header): boolean that will combine the
normal hasHeader() and getHeader() in a single call. It will only return true when the requested header is found and when it does, the supplied user header will be peered with the physical header data. This call is very suitable for putting into java if statements.

Here is an example:

Tcp tcp = new Tcp(); // Preallocate a Tcp header

public void nextPacket(PcapPacket packet, Queue<PcapPacket>) {
  if (packet.hasHeader(tcp)) {
    System.out.println("found packet with tcp port=" + tcp.destination());
    queue.offset(new PcapPacket(packet)); // Make deep copy

In this example, first we pre allocate a Tcp header object, found in org.jnetpcap.packet.header package. This is an empty header which doesn't reference any data. If we try to access any of its methods, we will immediately recieve a NullPointerException as the tcp variable may not be null, but the native memory location where the header resides is still set to null. It gets initialized in the handler in the Pcap.hasHeader(tcp) call.

We receive packets from a libpcap dispatch loop, in our handler, where we first check using the pre-allocated tcp header if the TCP header exists in the packet. The tcp header instance is uninitialized but it does supply its numerical header id, maintained and assigned by JRegistry so that a proper hasHeader() check can be performed. If the header exists, the tcp header object is peered with the packet and more specifically the packet buffer where it is supposed to reside. We then enter the "if" body. There we access one of the tcp fields, the Tcp.source() field and we print out its value. All fields with all headers provide an accessor method for reading the value. This saves you from having to know and understand to minutiae detail the structure and position within the structure of the tcp header. Especially when dealing with optional parts of the header.

As a last step in the "if" statement, we put a copy of the packet onto a queue. This is a deep copy of the packet. We can not put the dispatch loop supplied packet, as that is a temporary packet that could be changed or disappear at any moment. By making a deep copy of the packet, we are sure that it will persist until we not longer needed. Deep copy means that the packet data and its decoded state is copied to new more permanent memory location using the packet's default memory allocation mechanism.