Packet org.jnetpcap.packet - Overview

Printer-friendly

Class Hierarchies

The class hierarchies are as follows:

JMemory
 +-> JStruct
 |   +-> JPacket.State // peered with "struct packet_state_t"
 |   +-> JHeader.State // peered with "struct header_t"
 |   +-> JScanner      // peered with "struct scanner_t"
 |   +-> PcapHeader    // peered with "struct pcap_pkthdr"
 |   +->[AircapHeader] // Possibility in the future
 |
 +-> JBuffer           // peered with "unsigned char *"
     +-> JHeader
     |   +-> Ethernet  
     |   +-> Ip4
     |   +-> Ip6
     |   +-> Tcp
     |   ...
     |
     +-> JPacket
         +-> PcapPacket
         +->[AircapPacket] // Possibility in the future

JHeader class - overview

Headers (also referred to as protocols) in jNetPcap extends the class JHeader, a base class of all of the headers. This class is one of the NIO classes that can be redirected toward any native memory location, specifically within a JPacket data buffer where we would like to start interpreting any specific sequence of bytes as a protocol header. JHeader class is a subclass of JBuffer. This provides greater low level access to the content of the data buffer as primary purpose of JHeader API, while it also contains a separate JHeader.State object reference that provides the header's state or java runtime information. This information is typically produced by JScanner. You can use JHeader.getState() method which returns the State object. State object is peered with a native struct header_t structure, produced and filled in by the scanner, which provides certain information about the header. Specifically the length of the header and its offset into the packet data buffer are most important. The JHeader is actually directed by pointer reference directly into the buffer, so any reads out of the JHeader's JBuffer are done starting with offset 0.

If we look at the sample Ethernet header. We can discuss further details of the

package org.jnetpcap.packet.header;

import java.nio.ByteOrder;

import org.jnetpcap.packet.JHeader;
import org.jnetpcap.packet.JProtocol;

public class Ethernet
    extends JHeader {

	public static final int ID = JProtocol.ETHERNET_ID;

	public static final ByteOrder BYTE_ORDER = ByteOrder.BIG_ENDIAN;

	public static final int LENGTH = 14; // Ethernet header is 14 bytes long

	public Ethernet() {
		super(ID);
		order(BYTE_ORDER);
	}

	public byte[] destination() {
		return getByteArray(0, 6);
	}

	public byte[] destinationToByteArray(byte[] array) {
		return getByteArray(0, array);
	}

	public void destination(byte[] array) {
		setByteArray(0, array);
	}

	public byte[] source() {
		return getByteArray(0 + 6, 6);
	}

	public void source(byte[] array) {
		setByteArray(0 + 6, array);
	}

	public byte[] sourceToByteArray(byte[] array) {
		return getByteArray(0 + 6, array);
	}

	public int type() {
		return getUShort(0 + 12);
	}

	public void type(int type) {
		setUShort(0 + 12, type);
	}

JBuffer super class is what is providing all those get/set primitive methods in Ethernet. JBuffer is peered with a packet data buffer.

JPacket class - overview

JPacket is also a subclass of JBuffer. Therefore you can treat a packet or a header just like any normal buffer. The JScanner and JPacket API takes care of the mundane details of actually figuring out which headers known to jNetPcap exist in the buffer. You can use JPacket to access any part of the entire buffer and read any value out using standard JBuffer setter and getter methods for primitives. JHeader's though, provide you with a structure to those seemingly random sequence of bytes. JPacket works with a JScanner class to arrive at a decoded packet state. This state contains information about which headers within the buffer are present, at exactly which offset and how long those headers are.

The state information is stored in a separate object defined by JPacket.State class. This is a public class but specific to JPackets. The State class maintains header information in compacted form. It provides various methods for accessing this information. JPacket also provides accessor methods which simplify dealing with actual State class API.

The JPacket.State information is actually stored in native memory in struct packet_state_t that is peered with JPacket.State class. This structure is produced and written to by JScanner when it performs its scan of the packet data buffer.

The packet API has been optimized to utilize the speed, efficiency and power of the native platform. Therefore all the scans and recording of the state information is done native user land, while the java JPacket and JHeader classes simply provide ways of accessing that information in java land.

JScanner class - overview

JScanner class is a one of the larger and more complex classes and native structures in jNetPcap API. It is responsible for taking a raw packet data buffer, scanning it, figuring out which headers are present, where and how long they are in the each packet buffer and storing that information so that JPacket and JHeader java objects can access it and pass it along to the user.

JScanner utilizes and produces data for various native structures

  • struct scanner_t - main structure that maintains the state of JScanner, including downloaded bindings, optimization information and large round-robin state buffer that holds the output per packet scanned
  • struct packet_state_t - packet state and its header information
  • struct header_t - per header state information
  • struct binding_t - java binding information which extend JScanner scanning capabilities to java land

JScanner class uses the same philosophy as the rest of libpcap library and uses a large round-robin buffer to store information about packets that are being scanned. This avoids having to allocate memory to store on a per packet basis. Instead the space is efficiently allocated out of the scanner's state buffer and assigned to the current packet being scanned. The scan information is written and the packet state object is pointed to at this information. When the next packet is scanned, the pointer in the state buffer is increased passed the previous packet state and the new packet is scanned and recorded. When the end of the scanner's state buffer is reached, it is rewound to the beginning, and the process is started all over again, overriding any previously stored data there.

If the packet state needs to be preserved more permanently, a couple of approaches can be used. The primary one is the use of JPacket.transferState method to copy the entire JPacket state structure, which includes all of the sub structures for all the decoded headers in one swift copy to new more permanent native memory location. Another approach would be to preallocate permanent memory block and pass it into the scanner. The scanner would then record that information in the new memory location instead of its own internal buffer. Saving the need to do any copies of the state structures. The packet data buffer comes from libpcap itself and jNetPcap has no control over that memory at all, therefore that data, if needed to be preserved has to be copied in all cases.

JScanner is more complex than other classes. Once of the things that makes it more complex is the flexibility of providing user supplied protocol to protocol bindings. These bindings determine which protocol header appears in the packet. By default, JScanner has all of the core headers found in org.jnetpcap.packet.header package builtin or hardcoded into its scan function. The default values for bindings for each of those headers are used. This provides maximum speed and efficiency when scanning a packet. JScanner also provides the ability to supply additional bindings written to a simple JBinding java interface. JScanner invokes the JBinding.checkLength method which returns the length of the header or -1 if the binding failed to find suitable conditions to for a header it is trying to determine to bind. The scanner algorithm first applies all of the core builtin bindings and then once they are exhausted and the next header in sequence of headers is unknown, it reverts to its internal binding table and try to apply a java binding, to determine if there are still more user defined headers that follow the last know header. Each JBinding will be applied in sequence for any given last header.

JScanner also provides a way to override any of the builtin bindings of the CORE protocols and override it with a user supplied java JBinding. So for example of you want to bind Ip4 to eithernet in a different way, you can override it and the java binding will be applied instead.

JScanner works closely with JRegistry class which maintains the protocol to protocol binding information. More on that in the next section.

JRegistry class - overview

[UNDER CONSTRUCTION]