5.1.2 - jNetPcap's JPacket and JHeader classes

In jNetPcap a header definition is a plain old java class file. It is written completely in java language and compiled with a javac compiler. In order to use a header file you have to first put it in your classpath so that JRE (java runtime environment) can find it, load it and make it available to java VM.

jNetPcap comes with a new type of java buffer object called JBuffer. This buffer class provides very similar accessor methods as the normal JRE's java.nio.ByteBuffer class. You can read and write different type of java primitives directly from this ByteBuffer. JBuffer is almost identical in that it provides the same type of accessor methods for reading java primitives and a few more for reading unsigned integers which ByteBuffer class does not. But that's not why that class has been included with the API. The real reason is that it can pointed at any memory location in the system without having to make memory copies. ByteBuffer doesn't have that flexibility but JBuffer does.

A header definition file is a normal java class file that extends JHeader class which in turn extends JBuffer class we just discussed. Lets take a look at some class hierarchies to better understand this inheritance tree:

+-> JBuffer
    +-> JHeader
    |   +-> Ethernet
    |   +-> Ip4
    |   +-> Tcp
    |   +-> Http
    |   +-> Html
    +-> JPacket

Notice that all the headers and the packet, all extend JBuffer one way or the other. This is a key point understand when writing header definitions. Everything extends JBuffer. The diagram is a static class structure, so each one of those leaf classes in the diagram becomes its own object. You are usually given the JPacket object from dispatch or loop methods. You are not given the header objects. You instantiate yourself the header classes as objects. You can create as may header objects as you want but you won't be able to use them until they are peered with the packet.

Peering is a jNetPcap concept where, based on the JMemory class, any memory location can simply be point at. For headers and packets this is critical. We receive a packet that points at a memory buffer (this is real native memory, not java byte arrays). This packet has been peered with the physical memory, or repointed to like a C memory pointer can be repositioned in C language. The key concept to understand is that peer means no copy, its changing the memory reference only. The JBuffer that JPacket extends, is now pointing exactly at the start of the packet data and the buffer's length is set to the length of the packet. The buffer is not allowed to read any data outside of those bounds or you will get an exception. Another thing to notice if you down typecast the JPacket class to JBuffer, you have access to the complete packet buffer as a raw buffer. You would have to read data out of this buffer yourself.

We usually want to work with JPacket and not with JBuffer because JPacket gives us more, a lot more. It knows about about the structure of the data inside the packet. It knows what headers are there, where they begin and exactly where they end. The packet can take one of your header objects you instantiated and peer it with the buffer. Again change which memory location the buffer references. It will peer it in such a way, so that the JBuffer portion of the header is positioned exactly at the start of the header and its length is set to that of the header. So inside the header definition, it subclasses the JBuffer class and all its primitive accessor methods simply need to be referenced with super keyword. If you try and read data outside of the bound of the header, the buffer will throw an exception. So byte at offset 0 in the header is the first byte of the header, but possibly way inside the packet. You don't care about that anymore, since you know that your header is positioned properly by the packet.

Lets go back to our previous example for a second and take a look at this graphically:

0               14        34       42       86  <= offsets into the packet
 | Ethernet(14) | Ip4(20) | Udp(8) |data(44) |  <= Header name (length in bytes)
0             13 0      19 0      7 0      43   <= offsets into each header

This is our packet, a packet buffer. This time we have byte offsets for each header. On top we have the byte offsets if we were to use JPacket offsets, while on the bottom when we use our JHeader based definitions. The packet references the entire buffer, but each header only a small portion of the overall buffer. The offset start at 0 byte for each header. Notice that each header is positioned at different packet offset into the packet buffer. Once peered though, the offset for each header starts at 0.

You are supplied with a certain set of headers part of the API, but you can also add you own header files.