Package org.jnetpcap.packet

Packet decoding framework.

See:
          Description

Interface Summary
JBinding A bindinding between two protocol headers.
JCompoundHeader<B extends JHeader>  
JDependency Lists binding's protocol dependencies.
JHeaderAccessor Accessor to get a structured header from underlying buffer.
JHeaderType  
JPacketHandler<T> A dispatchable packet hadler.
JPayloadAccessor Interface which provides access to payload portion of the packet data.
PcapPacketHandler<T>  
 

Class Summary
AbstractBinding<H extends JHeader>  
AbstractMessageHeader  
JBinding.DefaultJBinding An abstract adaptor that provides a default implementation for a binding.
JFlow  
JFlowKey A unique key that identifies a flow of related packets.
JFlowMap  
JHeader A base class for all protocol header definitions.
JHeader.State This class is peered state of a header a native state structure
JHeaderMap<B extends JHeader>  
JHeaderPool A thread local pool of instances of headers.
JHeaderScanner A header scanner, there is one per header, that is able to scan raw memory buffer and determine the length of the header and the next header ID after examining the current header's structure.
JMappedHeader  
JMemoryPacket A heap based packet.
JMemoryPacket.JMemoryHeader A capture header that stores information about the creation of the packet.
JPacket A native packet buffer object.
JPacket.State Class maintains the decoded packet state.
JRegistry A registry of protocols, their classes, runtime IDs and bindings.
JScan A inprogress working scan structure.
JScanner JMemory with struct scanner_t, binding_t, packet_t and header_t structures.
JSubHeader<T extends JHeader>  
Payload Builtin header type that is a catch all for all unmatch data within a packet buffer
PcapPacket A pcap packet.
 

Enum Summary
AbstractMessageHeader.MessageType  
 

Exception Summary
PeeringException  
RegistryException  
RegistryHeaderErrors  
RegistryRuntimeException  
UnregisteredHeaderException Thrown when a lookup on a header in JRegistry fails
 

Package org.jnetpcap.packet Description

Packet decoding framework. Packet buffers are scanned and decoded.

Overview

The package adds new loop and dispatch methods that deliver decoded packets to the registered handler. The class JPacket allows the programmer to check for existance of certain protocol headers within the packet's data buffer. The programmer can use a header to then access any of the fields and logic for that protocol.

Here is an example of what a JPacketHandler might look like:

JPacketHandler handler = new JPacketHandler() {
  private Ethernet eth = new Ethernet();
  private Ip4 ip = new Ip4();
  
  public void nextPacket(JPacket packet, String user) {
    if (packet.hasHeader(Ethernet.ID)) {
      eth = packet.getHeader(eth);
      byte[] dstMac = eth.destination();
      byte[] srcMac = eth.source();
      int type = eth.type();
      // Do something
    }
    
    /*
     * For convenience there is a hasHeader method that takes a header instance
     * and combines hasHeader with getHeader methods if the header exists.
     */
    if (packet.hasHeader(ip)) {      
      int flags = ip.flags();
      int offset = ip.offset();
      byte[] srcIp = ip.source();
      byte[] dstIp = ip.destination();
      int protocol = ip.protocol();
    }
    
    // Or can simply print out the contents of the packet
    System.out.println(packet.toString());
  }
}
The example demonstates a JPacketHandler implementation, which gets passed into loop/dispatch methods and receives packets. The handler's nextPacket method receives a fully decoded packet. The information about which headers exist, where in the buffer and how long they are is recorded in native structures which JPacket, JPacket.State, JHeader, JHeader.State classes access. The method JPacket.hasHeader is a simply bitwise operation on an unsigned integer that is very efficient and fast.

The typical steps for working with the packet objects is first to check if the header the user is insterested in exists in the packet, has been found by the packet scanner, and then request that user supplied header instance is peered with the native structures that describe that header.(Peering means that the physical address of the native structure is recorded somewhere in the java class beeing peered and afterwards that class uses that pointer to access and retrieve data from the physical native structure or buffer.)

Feature #2292402: A packet decoding framework

This feature "packet decoding framework" extends basic jNetPcap capabilities of capturing a network packet or reading it from an offline file, then delivering that packet as a raw byte buffer to a PcapHandler which is a user provided callback object. The packet framework adds additional capabilities where the raw byte buffer is decoded into an array of headers contained within a PcapPacket or more commonly a super class JPacket.

The framework provides additional Pcap.loop and Pcap.dispatch methods which allow the user to supply a JPacketHandler, as opposed to PcapHandler, which delivers a decoded JPacket object to the user's callback method JPacketHandler.nextPacket(JPacket packet, Object user)

JPacket class is the main interface to the decoded packet information, specifically; which protocol headers exist in the packet, found by scanning the raw byte buffer, and detailed information on those headers. The user can query a JPacket using methods JPacket.hasHeader(int id), JPacket.getHeader(T header):<T extends JHeader> T and JPacket.getCaptureHeader() which returns the capture information such as timestamp and wirele (length of the packet as seen on the network wire.)

There are also numerous support and other implementation classes such as

Advanced: Scanner implementation details

It is time to discuss the JScanner class and how it works behind the scenes. The scanner is actually implemented completely in native code. It is a tight loop that scans the raw packet buffer (an array of raw bytes or chars using C symantics) starting at the begining of the buffer and assuming what the first header is, which is specified to it from java space (can be automatically discovered using Pcap.datalink() method or user supplied). The scanner uses numerical ids, assigned and maintained by JRegistry, in the scanning and recoding process. As headers are discovered information about those headers is recorded.

The scanner implementation is almost completely in native code. The scanner when its initialized allocates a large native memory block to hold its state and state about every packet it decodes. A pointer into the buffer is maintained which points to memory space where packet state can be recorded. Pointer is advanced for each packet that is being scanned. When the buffer is exhausted and the end is reached, the buffer simply wraps around to the beginning and the buffer is reused. This is the same algorithm that native libpcap uses for its capture buffer. So both the raw packet buffer, maintained by libpcap, and the packet decoded state information, maintained by JScanner, are kept in temporary, roundrobin buffers. This is something that also applies to other Pcap.loop and Pcap.dispatch methods that only dispatch raw byte buffers.

As packets arrive and the scanner is asked to scan them, it increments a pointer to a packet_state_t structure, within its buffer. The packet_state_t structure is where information is recorded about each headers. First there is a compress bitmap which using header's numerical IDs, records information about the presence of a header by setting a bit in this bitmap (an unsigned integer). Calls to JPacket.hasHeader(int id) simply check if a bit is set for a specific header ID and return true or false. Therefore JPacket.hasHeader checks are very light and fast and can be made frequently without having to worry about performance. Secondly, the scanner also records header's position within the buffer and the header's length. That information is also recorded in packet_state_t which has a header_state_t array member. The last header is typically a payload header, a special header that is catch all and captures the remainder of the packet that was not matched with any protocol header.

Native JScanner structures

Before we begin on native structures, lets look at the class inheritance tree.

jMemory
+-> JBuffer 
|   +-> JPacket - peered with libpcap packet data buffer
|
+-> JStruct
    +-> JPacket.State - peered with JScanners packet_state_t structure
    +-> JHeader.State - peered with JScanners header_state_t structure
Further more, JPacket maintains an internal reference to a JPacket.State object as a private field. That is JPacket is peered with libpcap packet buffer, while a separate JPacket.State object is peered with the JScanner packet state structure. Notice that JPacket extends JBuffer, therefore any JPacket can be used as a normal JBuffer providing full access to all of the packets raw contents. The state information is allocated and maintained by JScanner.

Packets are assigned a packet_state_t structure by peering a java class with this native memory structure. jNetPcap uses the concept of native peers by holding the physical memory address as a internal private field within the java object. Native functions when called upon, retrieve this address location, cast a local pointer to that physical memory location for the structure that the class is peered with and then access that information. So a JPacket.State object is peered with a packet_state_t structure (residing in native memory) by simply changing a single java field. Java objects that are peered, hold very little additional information about the structure. There are some additional fields that are maintained for security purposes such as the size of the memory block and who the java owner of that memory block is. This information is only maintained for security and deallocation of memory purposes when the java parent object is garbage collected. This peering mechanism allows previously allocated packets to be reused extrememly efficiently, by simply changing a single pointer to a packet_state_t structure within it and changing the state of the packet immediately. JHeaders also maintain their state in this way.