API Examples

A list of examples that demonstrate the various ways jNetPcap SDK is used.

jNetPcap follows very similar programming paradigm as the native libpcap library. In most cases you will need to follow similar steps in getting ready to use the jNetPcap functionality. First, you will need to acquire a list of available network interfaces for working with the live network (see Pcap.findAllDevs()). Second, you will need to use one of the static open calls found in the Pcap class (see Pcap.openXXXX()). Third, after the open call succeeds, do something through the return Pcap class instance such as read packets, write packets or acquire some information about the network interface (see Pcap.sendPacket(), Pcap.loop(), Pcap.dispatch().)

The most common way of receiving packets is to use Pcap.dispatch() to which you supply a handler, a call back function, that will receive all the incoming packets. The call back function then does the programs logic. In more complex and advanced programs the handler puts the incoming packets on a queue shared with another thread, that awaits for the queue to have some packets. It then retrieves those packets and does something with them.

Here are several examples and code samples that present various features and usage of jNetPcap SDK.

Classic Example

This example is the classic libpcap example in its entirety, shown in nearly every tutorial on libpcap. It gets a list of network devices, presents a simple ASCII based menu and waits for user to select one of those interfaces. We will just select the first interface in the list instead of taking input to shorten the example. Then it opens that interface for live capture. Using a packet handler it goes into a loop to catch a few packets, say 10. Prints some simple info about the packets, and then closes the pcap handle and exits.

Download Source from SVN:

package org.jnetpcap.examples;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.jnetpcap.Pcap;
import org.jnetpcap.PcapIf;
import org.jnetpcap.packet.PcapPacket;
import org.jnetpcap.packet.PcapPacketHandler;

/**
 * Here is the output generated by this example :
 * 
 *  Network devices found:
 *  #0: \Device\NPF_{BC81C4FC-242F-4F1C-9DAD-EA9523CC992D} [Intel(R) PRO/100 VE] 
 *  #1: \Device\NPF_{E048DA7F-D007-4EEF-909D-4238F6344971} [VMware Virtual Ethernet Adapter]
 *  #2: \Device\NPF_{5B62B373-3EC1-460D-8C71-54AA0BF761C7} [VMware Virtual Ethernet Adapter]
 *  #3: \Device\NPF_GenericDialupAdapter [Adapter for generic dialup and VPN capture]
 * 
 *  Choosing 'Intel(R) PRO/100 VE) ' on your behalf:
 *  Received packet at Tue Nov 03 18:52:42 EST 2009 caplen=1362 len=1362 jNetPcap rocks!
 *  Received packet at Tue Nov 03 18:52:45 EST 2009 caplen=82   len=82   jNetPcap rocks!
 *  Received packet at Tue Nov 03 18:52:45 EST 2009 caplen=145  len=145  jNetPcap rocks!
 *  Received packet at Tue Nov 03 18:52:45 EST 2009 caplen=62   len=62   jNetPcap rocks!
 *  Received packet at Tue Nov 03 18:52:45 EST 2009 caplen=164  len=164  jNetPcap rocks!
 *  Received packet at Tue Nov 03 18:52:45 EST 2009 caplen=62   len=62   jNetPcap rocks!
 *  Received packet at Tue Nov 03 18:52:45 EST 2009 caplen=54   len=54   jNetPcap rocks!
 *  Received packet at Tue Nov 03 18:52:45 EST 2009 caplen=1073 len=1073 jNetPcap rocks!
 *  Received packet at Tue Nov 03 18:52:45 EST 2009 caplen=1514 len=1514 jNetPcap rocks!
 *  Received packet at Tue Nov 03 18:52:45 EST 2009 caplen=279  len=279  jNetPcap rocks!
 */
public class ClassicPcapExample {

	/**
	 * Main startup method
	 * 
	 * @param args
	 *          ignored
	 */
	public static void main(String[] args) {
		List<PcapIf> alldevs = new ArrayList<PcapIf>(); // Will be filled with NICs
		StringBuilder errbuf = new StringBuilder(); // For any error msgs

		/***************************************************************************
		 * First get a list of devices on this system
		 **************************************************************************/
		int r = Pcap.findAllDevs(alldevs, errbuf);
		if (r == Pcap.NOT_OK || alldevs.isEmpty()) {
			System.err.printf("Can't read list of devices, error is %s", errbuf
			    .toString());
			return;
		}

		System.out.println("Network devices found:");

		int i = 0;
		for (PcapIf device : alldevs) {
			String description =
			    (device.getDescription() != null) ? device.getDescription()
			        : "No description available";
			System.out.printf("#%d: %s [%s]\n", i++, device.getName(), description);
		}

		PcapIf device = alldevs.get(0); // We know we have atleast 1 device
		System.out
		    .printf("\nChoosing '%s' on your behalf:\n",
		        (device.getDescription() != null) ? device.getDescription()
		            : device.getName());

		/***************************************************************************
		 * Second we open up the selected device
		 **************************************************************************/
		int snaplen = 64 * 1024;           // Capture all packets, no trucation
		int flags = Pcap.MODE_PROMISCUOUS; // capture all packets
		int timeout = 10 * 1000;           // 10 seconds in millis
		Pcap pcap =
		    Pcap.openLive(device.getName(), snaplen, flags, timeout, errbuf);

		if (pcap == null) {
			System.err.printf("Error while opening device for capture: "
			    + errbuf.toString());
			return;
		}

		/***************************************************************************
		 * Third we create a packet handler which will receive packets from the
		 * libpcap loop.
		 **************************************************************************/
		PcapPacketHandler<String> jpacketHandler = new PcapPacketHandler<String>() {

			public void nextPacket(PcapPacket packet, String user) {

				System.out.printf("Received packet at %s caplen=%-4d len=%-4d %s\n",
				    new Date(packet.getCaptureHeader().timestampInMillis()), 
				    packet.getCaptureHeader().caplen(),  // Length actually captured
				    packet.getCaptureHeader().wirelen(), // Original length 
				    user                                 // User supplied object
				    );
			}
		};

		/***************************************************************************
		 * Fourth we enter the loop and tell it to capture 10 packets. The loop
		 * method does a mapping of pcap.datalink() DLT value to JProtocol ID, which
		 * is needed by JScanner. The scanner scans the packet buffer and decodes
		 * the headers. The mapping is done automatically, although a variation on
		 * the loop method exists that allows the programmer to sepecify exactly
		 * which protocol ID to use as the data link type for this pcap interface.
		 **************************************************************************/
		pcap.loop(10, jpacketHandler, "jNetPcap rocks!");

		/***************************************************************************
		 * Last thing to do is close the pcap handle
		 **************************************************************************/
		pcap.close();
	}
}

Create TCP Packet

This example demonstrates how you can create a new packet from an in-memory buffer, modify few fields within various headers and recalculate necessary checksums for the updated values.

Download Source from SVN:

JPacket packet =
  new JMemoryPacket(JProtocol.ETHERNET_ID,
      " 001801bf 6adc0025 4bb7afec 08004500 "
    + " 0041a983 40004006 d69ac0a8 00342f8c "
    + " ca30c3ef 008f2e80 11f52ea8 4b578018 "
    + " ffffa6ea 00000101 080a152e ef03002a "
    + " 2c943538 322e3430 204e4f4f 500d0a");

Ip4 ip = packet.getHeader(new Ip4());
Tcp tcp = packet.getHeader(new Tcp());

tcp.destination(80);

ip.checksum(ip.calculateChecksum());
tcp.checksum(tcp.calculateChecksum());
packet.scan(Ethernet.ID);

System.out.println(packet);

The packet template is created based upon a different packet that was previously captured. We supply the packet contents as a string hexdump. The hexdump is parsed and turned into a byte[] which servers as the basis for the packet. JMemoryPacket allocates storage for the new packet and scans it.

At this point we can peer our protocol headers which allow us, using their convenient getters and setter methods, to manipulate their contents more precisely.

After our modifications, we scan the packet one more time to make sure that any structural changes we made, which we didn't in our simple example, are recalculated and the packet state appropriately updated.

Lastly we print out our newly modified packet:

Frame:
Frame:          number = 1
Frame:       timestamp = 2010-08-23 10:39:23.803
Frame:     wire length = 79 bytes
Frame: captured length = 79 bytes
Frame:
Eth:  ******* Ethernet - "Ethernet" - offset=0 (0x0) length=14 
Eth: 
Eth:      destination = 0:18:1:bf:6a:dc
Eth:                    .... ..0. .... .... = [0] LG bit
Eth:                    .... ...0 .... .... = [0] IG bit
Eth:           source = 0:25:4b:b7:af:ec
Eth:                    .... ..0. .... .... = [0] LG bit
Eth:                    .... ...0 .... .... = [0] IG bit
Eth:             type = 0x800 (2048) [ip version 4]
Eth: 
Ip:  ******* Ip4 - "ip version 4" - offset=14 (0xE) length=20 protocol suite=NETWORK
Ip: 
Ip:          version = 4
Ip:             hlen = 5 [5 * 4 = 20 bytes, No Ip Options]
Ip:         diffserv = 0x0 (0)
Ip:                    0000 00.. = [0] code point: not set
Ip:                    .... ..0. = [0] ECN bit: not set
Ip:                    .... ...0 = [0] ECE bit: not set
Ip:           length = 65
Ip:               id = 0xA983 (43395)
Ip:            flags = 0x2 (2)
Ip:                    0.. = [0] reserved
Ip:                    .1. = [1] DF: do not fragment: set
Ip:                    ..0 = [0] MF: more fragments: not set
Ip:           offset = 0
Ip:              ttl = 64 [time to live]
Ip:             type = 6 [next: Transmission Control]
Ip:         checksum = 0xD69A (54938) [correct]
Ip:           source = 192.168.0.52
Ip:      destination = 47.140.202.48
Ip: 
Tcp:  ******* Tcp offset=34 (0x22) length=32 
Tcp: 
Tcp:           source = 50159
Tcp:      destination = 80
Tcp:              seq = 0x2E8011F5 (780145141)
Tcp:              ack = 0x2EA84B57 (782781271)
Tcp:             hlen = 8
Tcp:         reserved = 0
Tcp:            flags = 0x18 (24)
Tcp:                    0... .... = [0] cwr: reduced (cwr)
Tcp:                    .0.. .... = [0] ece: ECN echo flag
Tcp:                    ..0. .... = [0] ack: urgent, out-of-band data
Tcp:                    ...1 .... = [1] ack: acknowledgment
Tcp:                    .... 1... = [1] ack: push current segment of data
Tcp:                    .... .0.. = [0] ack: reset connection
Tcp:                    .... ..0. = [0] ack: synchronize connection, startup
Tcp:                    .... ...0 = [0] fin: closing down connection
Tcp:           window = 65535
Tcp:         checksum = 0xA729 (42793) [correct]
Tcp:           urgent = 0
Tcp: 
Data:  ******* Payload offset=66 (0x42) length=13 
Data: 
0042: 35 38 32 2e  34 30 20 4e  4f 4f 50 0d  0a             582.40 NOOP..   

New libpcap 1.0.0 API calls example

This example demonstrates how to check for and use various API version specific 'pcap' library functions. 'pcap' library, from tcpdump.org team, is evolving and typically adds new function calls to its public API every year. This creates a problem for jnetpcap library integrators as some needed functionality may or may not be present on various client systems. Operating system package installers can typically check for pre-requisite package requirements and make sure that minimum versions of 'pcap' library are installed first before jnetpcap package can be installed. However most users of jnetpcap prefer to use ZIP or TAR format, which does not provide dependency checking mechanism and its up to the user or JVM to resolve and report problems are runtime. Java is also platform independent and writing the code once and run anywhere is a bit more tricky to accomplish when you have such native library dependencies.

jNetPcap version 1.4 introduces a new "native library" management package which facilitates additional checks and information about the state of the loaded native libraries. It allows very specific checks, at runtime, for native symbols without causing exceptions or errors. The library package is exposed through a small set of methods in the Pcap class. These methods make it convenient and easy to check if specific 'pcap' library functionality exists at runtime on any platform, client and even with specific runtime setup.

By now, we are all familiar with the static methods Pcap.openLive, Pcap.openOffline and Pcap.close. These calls have been part of public 'pcap' library API since the beginning and a user can correctly assume that they are always present. The only time these calls are not present is when the native 'pcap' library, on which jnetpcap library is dependent on, failed to load, which can happen for any number of reasons; libpcap library not installed, system library loading path variable incorrectly set, permissions, etc. All 3 of these calls rely on a 'pcap' library functions which are exported as part of public API. These are pcap_open_live, pcap_open_offline and pcap_close. jNetPcap native library calls on these native functions and relies on them to be linked in by the JVM when the application starts up.

However the 'pcap' library continues to evolve and has been steadily adding new functions to its public API. The most recent new functions are actually quiet numerous and are only available with libpcap version 1.0.0 or above and any of its derivatives such as WinPcap 4.1.1 and above. Since these symbols are available only in the newest 'pcap' libraries, many existing and especially older systems, may not have the latest 'pcap' library installed. Two such new functions added to 'pcap' library are pcap_create and pcap_activate. These functions create an unactivated pcap handle and then activate it. This defers the activation to a later time and allows other parameters to be set on the handle. Its a bit more flexible way of setting various options, parameters and properties on the pcap handle before it becomes active. Alternative would be to keep on expanding the single static 'pcap_open_live' function call or provide others like it that take additional parameters. This is not scalable and splitting the 'create' handle and 'activate' into separate steps allows any number of new properties, through their own setter functions, to be added to the API.

So how can we check if pcap_create and pcap_activate function calls are available to us, on any particular client? Our example program could be running on a windows, linux, solaris or freebsd and few others. The Pcap class, provides boolean methods that allow us to check for specific API functionality and let us make appropriate decisions at runtime, if we can rely on the new API or do we have to fallback on the ever present old API.

First off, our jnetpcap version has to be 1.4 or above in order to support this new API in the Pcap class. This is easy enough since library integrators typically supply the correct jnetpcap version with their application. The 'pcap' library installed on the system may or may not be at the correct version however, as we may have no control over the installed packages on that particular system. Especially in client production environments, changing or upgrading libraries is a lengthy process. So as long as we have the jnetpcap version 1.4 or above. Our example application will at least run.

In our example we first check, using Pcap.isPcap100Loaded method, if 'pcap' API level 1.0.0 is currently loaded and available to us. If yes, we can assume that new 'pcap' library calls, pcap_create and pcap_activate, are available to us and we can use them. These calls are exported in java via the Pcap.create and Pcap.activate methods. With out the an explicit check with Pcap.isPcap100Loaded these methods may work on some platforms and throw UnsatisfiedLinkException on others, unless we have very strict control over pre-installed 'pcap' libraries, as described in the above paragraphs. So if we make the check first and fallback on Pcap.openLive method to open the 'pcap' handle, we can write code that will work on any platform.

Pcap pcap;
if (Pcap.isPcap100Loaded()) {
	pcap = Pcap.create(deviceName, errbuf);
	// Rest of the logic goes here
	pcap.activate();
} else {
	pcap = Pcap.openLive(deviceName, snaplen, timeout, flags, errbuf);
}

try {
	// Our main application logic goes here
} finally {
	pcap.close();
}

Our example uses Pcap.isPcap100Loaded to check if the new 'pcap' library API 1.0.0 is available. If it is, it uses Pcap.create and Pcap.activate to open the 'pcap' handle to our live network interface in 2 separate steps. It also sets the familiar 'snaplen', 'timeout' and other familiar properties manually and explicitly. The real power of the new API are the new functions which allow changing the size of the ring buffer that 'pcap' uses to store packets. This is especially important call on systems that are run on virtual machines as the default buffer size does not seem to be large enough under most circumstances. So lets get started with out example.

public class LibpcapAPIVersionsExample {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		List alldevs = new ArrayList(); // Will be filled with NICs
		StringBuilder errbuf = new StringBuilder(); // For any error msgs

		/***************************************************************************
		 * First get a list of devices on this system
		 **************************************************************************/
		int r = Pcap.findAllDevs(alldevs, errbuf);
		if (r != Pcap.OK || alldevs.isEmpty()) {
			System.err.printf("Can't read list of devices, error is %s",
					errbuf.toString());
			return;
		}

		System.out.println("Network devices found:");

		int i = 0;
		for (PcapIf device : alldevs) {
			String description =
					(device.getDescription() != null) ? device.getDescription()
							: "No description available";
			System.out.printf("#%d: %s [%s]\n", i++, device.getName(), description);
		}

		PcapIf device = alldevs.get(0); // We know we have atleast 1 device
		System.out
				.printf("\nChoosing '%s' on your behalf:\n",
						(device.getDescription() != null) ? device.getDescription()
								: device.getName());

		/***************************************************************************
		 * Second we open up the selected device
		 **************************************************************************/
		int snaplen = 64 * 1024; // Capture all packets, no trucation
		int flags = Pcap.MODE_PROMISCUOUS; // capture all packets
		int timeout = 10 * 1000; // 10 seconds in millis

		/*
		 * Display a little message so that we know which API we are using in this
		 * example
		 */
		System.out.printf("Is 'pcap' library API 1.0.0 or above loaded? %s%n",
				Pcap.isPcap100Loaded());

		/*
		 * Now lets open a 'pcap' handle to a live network interface and start
		 * capturing, no matter which API version is available to us.
		 */
		Pcap pcap;
		if (Pcap.isPcap100Loaded()) {
			pcap = Pcap.create(device.getName(), errbuf);
			if (pcap == null) {
				System.err.printf("Error while opening device for capture: "
						+ errbuf.toString());
				return;
			}

			/* Set our standard properties */
			pcap.setSnaplen(snaplen);
			pcap.setPromisc(flags);
			pcap.setTimeout(timeout);

			/* Here are some new ones */
			pcap.setDirection(Direction.INOUT); // We now have IN, OUT or INOUT
			pcap.setBufferSize(128 * 1024 * 1024); // Set ring-buffer to 128Mb

			pcap.activate(); // Make our handle active and start capturing

		} else {
			pcap = Pcap.openLive(device.getName(), snaplen, flags, timeout, errbuf);

			if (pcap == null) {
				System.err.printf("Error while opening device for capture: "
						+ errbuf.toString());
				return;
			}
		}

		/*
		 * The rest of the example is normal and we correctly handled different
		 * versions of 'pcap' library API available to us at runtime, on any
		 * platform.
		 */

		/***************************************************************************
		 * Third we create a packet handler which will receive packets from the
		 * libpcap loop.
		 **************************************************************************/
		PcapPacketHandler jpacketHandler = new PcapPacketHandler() {

			public void nextPacket(PcapPacket packet, String user) {

				System.out.printf("Received packet at %s caplen=%-4d len=%-4d %s\n",
						new Date(packet.getCaptureHeader().timestampInMillis()),
						packet.getCaptureHeader().caplen(), // Length actually captured
						packet.getCaptureHeader().wirelen(), // Original length
						user // User supplied object
						);
			}
		};

		try {
			/*************************************************************************
			 * Fourth we enter the loop and tell it to capture 10 packets. The loop
			 * method does a mapping of pcap.datalink() DLT value to JProtocol ID,
			 * which is needed by JScanner. The scanner scans the packet buffer and
			 * decodes the headers. The mapping is done automatically, although a
			 * variation on the loop method exists that allows the programmer to
			 * specify exactly which protocol ID to use as the data link type for this
			 * pcap interface.
			 ************************************************************************/
			pcap.loop(10, jpacketHandler, "jNetPcap rocks!");

			/*************************************************************************
			 * Last thing to do is close the pcap handle
			 ************************************************************************/
		} finally {
			pcap.close();
		}
	}
}

Offline Capture

This example is the classic libpcap example in its entirety, shown in nearly every tutorial on libpcap. It opens up an offline capture (a file with packets in it). Using a packet handler it goes into a loop to catch a few packets, say 10. Prints some simple info about the packets, and then closes the pcap handle and exits.

Download Source from SVN:

package org.jnetpcap.examples;

import java.util.Date;

import org.jnetpcap.Pcap;
import org.jnetpcap.packet.PcapPacket;
import org.jnetpcap.packet.PcapPacketHandler;

/**
 * This example is similar to the classic libpcap example shown in nearly every
 * tutorial on libpcap. The main difference is that a file is opened instead of
 * a live network interface. Using a packet handler it goes into a loop to read
 * a few packets, say 10. Prints some simple info about the packets, and then
 * closes the pcap handle and exits.
 * 
 * Here is the output generated by this example :
 * 
 * Opening file for reading: tests/test-l2tp.pcap
 * Received at Tue Jan 27 16:17:17 EST 2004 caplen=114  len=114  jNetPcap rocks!
 * Received at Tue Jan 27 16:17:17 EST 2004 caplen=114  len=114  jNetPcap rocks!
 * Received at Tue Jan 27 16:17:18 EST 2004 caplen=114  len=114  jNetPcap rocks!
 * Received at Tue Jan 27 16:17:18 EST 2004 caplen=114  len=114  jNetPcap rocks!
 * Received at Tue Jan 27 16:17:19 EST 2004 caplen=114  len=114  jNetPcap rocks!
 * Received at Tue Jan 27 16:17:19 EST 2004 caplen=114  len=114  jNetPcap rocks!
 * Received at Tue Jan 27 16:17:20 EST 2004 caplen=114  len=114  jNetPcap rocks!
 * Received at Tue Jan 27 16:17:20 EST 2004 caplen=114  len=114  jNetPcap rocks!
 * Received at Tue Jan 27 16:17:21 EST 2004 caplen=114  len=114  jNetPcap rocks!
 * Received at Tue Jan 27 16:17:21 EST 2004 caplen=114  len=114  jNetPcap rocks!
 * 
 * @author Mark Bednarczyk
 * @author Sly Technologies, Inc.
 */
public class ClassicPcapExampleOfflineCapture {

	/**
	 * Main startup method
	 * 
	 * @param args
	 *          ignored
	 */
	public static void main(String[] args) {
		/***************************************************************************
		 * First we setup error buffer and name for our file
		 **************************************************************************/
		final StringBuilder errbuf = new StringBuilder(); // For any error msgs
		final String file = "tests/test-l2tp.pcap";

		System.out.printf("Opening file for reading: %s%n", file);

		/***************************************************************************
		 * Second we open up the selected file using openOffline call
		 **************************************************************************/
		Pcap pcap = Pcap.openOffline(file, errbuf);

		if (pcap == null) {
			System.err.printf("Error while opening device for capture: "
			    + errbuf.toString());
			return;
		}

		/***************************************************************************
		 * Third we create a packet handler which will receive packets from the
		 * libpcap loop.
		 **************************************************************************/
		PcapPacketHandler<String> jpacketHandler = new PcapPacketHandler<String>() {

			public void nextPacket(PcapPacket packet, String user) {

				System.out.printf("Received at %s caplen=%-4d len=%-4d %s\n", 
				    new Date(packet.getCaptureHeader().timestampInMillis()), 
				    packet.getCaptureHeader().caplen(), // Length actually captured
				    packet.getCaptureHeader().wirelen(), // Original length
				    user // User supplied object
				    );
			}
		};

		/***************************************************************************
		 * Fourth we enter the loop and tell it to capture 10 packets. The loop
		 * method does a mapping of pcap.datalink() DLT value to JProtocol ID, which
		 * is needed by JScanner. The scanner scans the packet buffer and decodes
		 * the headers. The mapping is done automatically, although a variation on
		 * the loop method exists that allows the programmer to sepecify exactly
		 * which protocol ID to use as the data link type for this pcap interface.
		 **************************************************************************/
		try {
		    pcap.loop(10, jpacketHandler, "jNetPcap rocks!");
		} finally {
		/***************************************************************************
		 * Last thing to do is close the pcap handle
		 **************************************************************************/
		   pcap.close();
		}
	}
}

Pcap Dumper

The pcap dumper example expands on the classic example and creates a PcapDumper object which facilitates copying of captured packets to a file. This is exactly how packet dumping is done with native libpcap library as well.

Download source from SVN:

package org.jnetpcap.examples;

import java.io.File;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;

import org.jnetpcap.Pcap;
import org.jnetpcap.PcapDumper;
import org.jnetpcap.PcapHandler;
import org.jnetpcap.PcapIf;


public class PcapDumperExample {
  public static void main(String[] args) {
    List<PcapIf> alldevs = new ArrayList<PcapIf>(); // Will be filled with NICs
    StringBuilder errbuf = new StringBuilder();     // For any error msgs

    /***************************************************************************
     * First get a list of devices on this system
     **************************************************************************/
    int r = Pcap.findAllDevs(alldevs, errbuf);
    if (r == Pcap.NOT_OK || alldevs.isEmpty()) {
      System.err.printf("Can't read list of devices, error is %s\n", 
        errbuf.toString());
      return;
    }
    PcapIf device = alldevs.get(1); // We know we have atleast 1 device

    /***************************************************************************
     * Second we open up the selected device
     **************************************************************************/
    int snaplen = 64 * 1024;           // Capture all packets, no trucation
    int flags = Pcap.MODE_PROMISCUOUS; // capture all packets
    int timeout = 10 * 1000;           // 10 seconds in millis
    Pcap pcap = Pcap.openLive(device.getName(), snaplen, flags, timeout, errbuf);
    if (pcap == null) {
      System.err.printf("Error while opening device for capture: %s\n", 
        errbuf.toString());
      return;
    }
		
    /***************************************************************************
     * Third we create a PcapDumper and associate it with the pcap capture
     ***************************************************************************/
    String ofile = "tmp-capture-file.cap";
    PcapDumper dumper = pcap.dumpOpen(ofile); // output file

    /***************************************************************************
     * Fouth we create a packet handler which receives packets and tells the 
     * dumper to write those packets to its output file
     **************************************************************************/
    PcapHandler<PcapDumper> dumpHandler = new PcapHandler<PcapDumper>() {

      public void nextPacket(PcapDumper dumper, long seconds, int useconds,
        int caplen, int len, ByteBuffer buffer) {

        dumper.dump(seconds, useconds, caplen, len, buffer);
      }
    };

    /***************************************************************************
     * Fifth we enter the loop and tell it to capture 10 packets. We pass
     * in the dumper created in step 3
     **************************************************************************/
    pcap.loop(10, dumpHandler, dumper);
		
    File file = new File(ofile);
    System.out.printf("%s file has %d bytes in it!\n", ofile, file.length());
		

    /***************************************************************************
     * Last thing to do is close the dumper and pcap handles
     **************************************************************************/
    dumper.close(); // Won't be able to delete without explicit close
    pcap.close();
		
    if (file.exists()) {
      file.delete(); // Cleanup
    }
  }
}

For more information please visit jNetPcap userguide.

Send Packet

This example sends a packet using a network interface, one at a time. The packet is bogus, but none the less it will be sent as it uses the data link layer, a low level layer that expects the programmer to create the entire packets from scratch including the data link layer (i.e. the ethernet header.)

Download source from SVN: Packet Send Example

package org.jnetpcap.examples;

import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.jnetpcap.Pcap;
import org.jnetpcap.PcapIf;

public class PcapSendPacketExample {
  public static void main(String[] args) {
    List<PcapIf> alldevs = new ArrayList<PcapIf>(); // Will be filled with NICs
    StringBuilder errbuf = new StringBuilder(); // For any error msgs

    /***************************************************************************
     * First get a list of devices on this system
     **************************************************************************/
    int r = Pcap.findAllDevs(alldevs, errbuf);
    if (r == Pcap.NOT_OK || alldevs.isEmpty()) {
      System.err.printf("Can't read list of devices, error is %s", errbuf.toString());
      return;
    }
    PcapIf device = alldevs.get(0); // We know we have atleast 1 device

    /*****************************************
     * Second we open a network interface
     *****************************************/
    int snaplen = 64 * 1024; // Capture all packets, no trucation
    int flags = Pcap.MODE_PROMISCUOUS; // capture all packets
    int timeout = 10 * 1000; // 10 seconds in millis
    Pcap pcap = Pcap.openLive(device.getName(), snaplen, flags, timeout, errbuf);


    /*******************************************************
     * Third we create our crude packet we will transmit out
     * This creates a broadcast packet
     *******************************************************/
    byte[] a = new byte[14];
    Arrays.fill(a, (byte) 0xff);
    ByteBuffer b = ByteBuffer.wrap(a);

    /*******************************************************
     * Fourth We send our packet off using open device
     *******************************************************/
    if (pcap.sendPacket(b) != Pcap.OK) {
      System.err.println(pcap.getErr());
    }

    /********************************************************
     * Lastly we close
     ********************************************************/
    pcap.close();
  }
}

Send Queue

WinPcap Send Queue Transmit

Download source from SVN: Send Queue Transmit on Win32 platform Example

package org.jnetpcap.examples;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.jnetpcap.Pcap;
import org.jnetpcap.PcapIf;
import org.jnetpcap.PcapPktHdr;
import org.jnetpcap.winpcap.WinPcap;
import org.jnetpcap.winpcap.WinPcapSendQueue;

public class WinPcapSendQueueTransmitExample {
  public static void main(String[] args) {
    List<PcapIf> alldevs = new ArrayList<PcapIf>(); // Will be filled with NICs
    StringBuilder errbuf = new StringBuilder(); // For any error msgs

    /***************************************************************************
     * First get a list of devices on this system
     **************************************************************************/
    int r = Pcap.findAllDevs(alldevs, errbuf);
    if (r == Pcap.NOT_OK || alldevs.isEmpty()) {
      System.err.printf("Can't read list of devices, error is %s", errbuf.toString());
      return;
    }
    PcapIf device = alldevs.get(0); // We know we have atleast 1 device

    /***************************************************************************
     * Second we open a network interface
     **************************************************************************/
    int snaplen = 64 * 1024; // Capture all packets, no trucation
    int flags = Pcap.MODE_PROMISCUOUS; // capture all packets
    int timeout = 10 * 1000; // 10 seconds in millis
    WinPcap pcap = WinPcap.openLive(device.getName(), snaplen, flags, timeout, errbuf);

    /***************************************************************************
     * Third we create our crude packet queue we will transmit out 
     * This creates a small queue full of broadcast packets
     **************************************************************************/
    WinPcapSendQueue queue = WinPcap.sendQueueAlloc(512);
    PcapPktHdr hdr = new PcapPktHdr(128, 128);
    byte[] pkt = new byte[128];
		
    Arrays.fill(pkt, (byte) 255); // Broadcast
    queue.queue(hdr, pkt); // Packet #1
    queue.queue(hdr, pkt); // Packet #2

    Arrays.fill(pkt, (byte) 0x11); // Junk packet
    queue.queue(hdr, pkt); // Packet #3

    /***************************************************************************
     * Fourth We send our packet off using open device
     **************************************************************************/
    r = pcap.sendQueueTransmit(queue, WinPcap.TRANSMIT_SYNCH_ASAP);
    if (r != queue.getLen()) {
      System.err.println(pcap.getErr());
      return;
    }
    /***************************************************************************
     * Lastly we close
     **************************************************************************/
    pcap.close();
  }
}

MAC Address

Get interface's hardware address (MAC address)

This example gets a list of interfaces then iterates through the list. For each interface it acquires MAC address, formats it and prints it out. The example skips any interfaces that do not have a MAC address assigned such as loop interfaces non datalink interfaces.

Source code: GetInterfaceHardwareAddress.java

package org.jnetpcap.examples;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.jnetpcap.Pcap;
import org.jnetpcap.PcapIf;

public class GetInterfaceHardwareAddress {

  public static void main(String[] args) throws IOException {

    List<PcapIf> alldevs = new ArrayList<PcapIf>(); // Will be filled with NICs
    StringBuilder errbuf = new StringBuilder(); // For any error msgs

    /***************************************************************************
     * First get a list of devices on this system
     **************************************************************************/
    int r = Pcap.findAllDevs(alldevs, errbuf);
    if (r == Pcap.NOT_OK || alldevs.isEmpty()) {
      System.err.printf("Can't read list of devices, error is %s", errbuf
        .toString());
      return;
    }

    /***************************************************************************
     * Second iterate through all the interface and get the HW addresses
     **************************************************************************/
    for (final PcapIf i : alldevs) {
      final byte[] mac = i.getHardwareAddress();
      if (mac == null) {
        continue; // Interface doesn't have a hardware address
      }
      System.out.printf("%s=%s\n", i.getName(), asString(mac));
    }
  }

  private static String asString(final byte[] mac) {
    final StringBuilder buf = new StringBuilder();
    for (byte b : mac) {
      if (buf.length() != 0) {
        buf.append(':');
      }
      if (b >= 0 && b < 16) {
        buf.append('0');
      }
      buf.append(Integer.toHexString((b < 0) ? b + 256 : b).toUpperCase());
    }

    return buf.toString();
  }
}

Pcap.nextEx

Here is an example that demonstrates how to use Pcap.nextEx method. The example uses various peering methods, Libpcap DLT to jNetPcap protocol ID mapping, initiating a new PcapPacket object and invoking the scanner on a newly created packet.
Download Source from SVN:


package org.jnetpcap.examples;

import org.jnetpcap.Pcap;
import org.jnetpcap.PcapHeader;
import org.jnetpcap.nio.JBuffer;
import org.jnetpcap.nio.JMemory;
import org.jnetpcap.packet.JRegistry;
import org.jnetpcap.packet.PcapPacket;
import org.jnetpcap.packet.format.FormatUtils;
import org.jnetpcap.protocol.lan.Ethernet;
import org.jnetpcap.protocol.network.Ip4;

/**
 * This example opens up a capture file found in jNetPcap's installation
 * directory for of the "source" distribution package and iterates over every
 * packet. The example also demonstrates how to property peer
 * PcapHeader, JBuffer and initialize a new
 * PcapPacket object which will contain a copy of the peered
 * packet and header data. The libpcap provide header and data are stored in
 * libpcap private memory buffer, which will be overriden with each iteration of
 * the loop. Therefore we use the constructor in PcapPacket to
 * allocate new memory to store header and packet buffer data and perform the
 * copy. The we
 * 
 * @author Mark Bednarczyk
 * @author Sly Technologies, Inc.
 */
public class NextExExample {

	/**
	 * Start of our example.
	 * 
	 * @param args
	 *          ignored
	 */
	public static void main(String[] args) {
		final String FILE_NAME = "tests/test-l2tp.pcap";
		StringBuilder errbuf = new StringBuilder(); // For any error msgs

		/***************************************************************************
		 * First - we open up the selected device
		 **************************************************************************/
		Pcap pcap = Pcap.openOffline(FILE_NAME, errbuf);

		if (pcap == null) {
			System.err.printf("Error while opening file for capture: "
			    + errbuf.toString());
			return;
		}

		/***************************************************************************
		 * Second - we create our main loop and our application We create some
		 * objects we will be using and reusing inside the loop
		 **************************************************************************/
		Ip4 ip = new Ip4();
		Ethernet eth = new Ethernet();
		PcapHeader hdr = new PcapHeader(JMemory.POINTER);
		JBuffer buf = new JBuffer(JMemory.POINTER);

		/***************************************************************************
		 * Third - we must map pcap's data-link-type to jNetPcap's protocol IDs.
		 * This is needed by the scanner so that it knows what the first header in
		 * the packet is.
		 **************************************************************************/
		int id = JRegistry.mapDLTToId(pcap.datalink());

		/***************************************************************************
		 * Fourth - we peer header and buffer (not copy, think of C pointers)
		 **************************************************************************/
		while (pcap.nextEx(hdr, buf) == Pcap.NEXT_EX_OK) {

			/*************************************************************************
			 * Fifth - we copy header and buffer data to new packet object
			 ************************************************************************/
			PcapPacket packet = new PcapPacket(hdr, buf);

			/*************************************************************************
			 * Six- we scan the new packet to discover what headers it contains
			 ************************************************************************/
			packet.scan(id);

			/*
			 * We use FormatUtils (found in org.jnetpcap.packet.format package), to
			 * convert our raw addresses to a human readable string.
			 */
			if (packet.hasHeader(eth)) {
				String str = FormatUtils.mac(eth.source());
				System.out.printf("#%d: eth.src=%s\n", packet.getFrameNumber(), str);
			}
			if (packet.hasHeader(ip)) {
				String str = FormatUtils.ip(ip.source());
				System.out.printf("#%d: ip.src=%s\n", packet.getFrameNumber(), str);
			}
		}

		/*************************************************************************
		 * Last thing to do is close the pcap handle
		 ************************************************************************/
		pcap.close();
	}
}

Sub Headers

Working with sub-headers Example

This example demonstrates how to work with subheaders. Certain protocols such as ICMP in this example, have either optional sub headers or headers that are not optional but part of the main protocol definition. Icmp protocol definition defines the main part of the header in the base Icmp class while all the specific ICMP types each in its own sub-header. A sub header is a normal header with the exception it has a parent header and can not exist unless that part exists.

Here is Icmp header hierachy as defined in Icmp class:

Download Source from SVN:

Here is a jUnit test case which demonstrates how to check for specific Icmp types and access their fields:

/**
 * Packet dump:
 * 
 * Ethernet:  ******* Ethernet (Eth) offset=0 length=14
 * Ethernet: 
 * Ethernet:      destination = 16-03-78-01-16-03
 * Ethernet:           source = 00-60-08-9F-B1-F3
 * Ethernet:         protocol = 0x800 (2048) [ip version 4]
 * Ethernet: 
 * ip4:  ******* ip4 (ip) offset=14 length=20
 * ip4: 
 * ip4:          version = 4
 * ip4:             hlen = 5 [*4 = 20 bytes]
 * ip4:            diffs = 0xC0 (192)
 * ip4:                    1100 00..  = [48] reserved bit: code point 48
 * ip4:                    .... ..0.  = [0] ECN bit: ECN capable transport: no
 * ip4:                    .... ...0  = [0] ECE bit: ECE-CE: no
 * ip4:           length = 468
 * ip4:            flags = 0x0 (0)
 * ip4:                    0..  = [0] reserved bit: not set
 * ip4:                    .0.  = [0] don't fragment: not set
 * ip4:                    ..0  = [0] more fragments: not set
 * ip4:               id = 0xE253 (57939)
 * ip4:           offset = 0
 * ip4:     time to live = 255 router hops
 * ip4:         protocol = 1 [icmp - internet message control protocol]
 * ip4:  header checksum = 0xAE96 (44694)
 * ip4:           source = 131.151.32.21
 * ip4:      destination = 131.151.1.59
 * ip4: 
 * icmp:  ******* icmp (icmp) offset=34 length=8
 * icmp: 
 * icmp:             type = 3 [destination unreachable]
 * icmp:             code = 3 [destination port unreachable]
 * icmp:         checksum = 0x2731 (10033)
 * icmp: 
 * icmp: + DestUnreachable: offset=4 length=4
 * icmp:         reserved = 0
 * icmp: 
 * ip4:  ******* ip4 (ip) offset=42 length=20
 * ip4: 
 * ip4:          version = 4
 * ip4:             hlen = 5 [*4 = 20 bytes]
 * ip4:            diffs = 0x0 (0)
 * ip4:                    0000 00..  = [0] reserved bit: not set
 * ip4:                    .... ..0.  = [0] ECN bit: ECN capable transport: no
 * ip4:                    .... ...0  = [0] ECE bit: ECE-CE: no
 * ip4:           length = 440
 * ip4:            flags = 0x2 (2)
 * ip4:                    0..  = [0] reserved bit: not set
 * ip4:                    .1.  = [1] don't fragment: set
 * ip4:                    ..0  = [0] more fragments: not set
 * ip4:               id = 0xCB91 (52113)
 * ip4:           offset = 0
 * ip4:     time to live = 254 router hops
 * ip4:         protocol = 17 [udp - unreliable datagram protocol]
 * ip4:  header checksum = 0x8724 (34596)
 * ip4:           source = 131.151.1.59
 * ip4:      destination = 131.151.32.21
 * ip4: 
 * udp:  ******* udp (udp) offset=62 length=8
 * udp: 
 * udp:           source = 7003
 * udp:      destination = 1792
 * udp:           length = 420
 * udp:         checksum = 44574
 * udp: 
 * payload:  ******* payload (data) offset=70 length=412
 * payload: 
 * payload: 0046: 382b3948 e09dbee8 00000001 00000001   8  +  9  H  \e0\9d\be\e8\0 \0 \0 \1 \0 \0 \0 \1 
 * payload: 0056: 00000002 01060000 00000034 00000072   \0 \0 \0 \2 \1 \6 \0 \0 \0 \0 \0 4  \0 \0 \0 r  
 * [truncated...]
 */
public void testIcmpDestUnreachable() {
	// Wireshark packet # 29 (1-based)
	PcapPacket packet = TestUtils.getPcapPacket("tests/test-afs.pcap", 29 - 1);
	
	Ip4 ip = new Ip4();
	Icmp icmp = new Icmp(); // Need an instance so we can check on sub header
	Icmp.DestinationUnreachable unreach = new Icmp.DestinationUnreachable();

	assertTrue(packet.hasHeader(Ethernet.ID));
	assertTrue(packet.hasHeader(Ip4.ID, 0)); // 1st IP header
	assertTrue(packet.hasHeader(icmp));
	assertTrue(icmp.hasSubHeader(IcmpType.DESTINATION_UNREACHABLE.getId()));
	assertTrue(icmp.hasSubHeader(unreach));
	assertTrue(packet.hasHeader(ip, 1)); // 2nd IP header
	assertTrue(packet.hasHeader(Udp.ID));
	assertTrue(packet.hasHeader(Payload.ID));

	// Check specific values
	assertEquals(3, icmp.type());
	assertEquals(3, icmp.code());
	assertEquals(0x2731, icmp.checksum());
	assertEquals(0, unreach.reserved());

	assertEquals(0x8724, ip.checksum());
	assertEquals(440, ip.length());

	// Devil's advocate
	assertFalse(icmp.hasSubHeader(IcmpType.ECHO_REPLY.getId()));
	assertFalse(icmp.hasSubHeader(IcmpType.PARAM_PROBLEM.getId()));

}


Explanation

There are a lot of subtleties in the example we should explore.

    PcapPacket packet = TestUtils.getPcapPacket("tests/test-afs.pcap", 29 - 1);  

First line#76 uses a predefined utility method part of the jnetpcap tests that facilitates easy packet access into files. So we are reading a single packet at index 29 from file tests/test-afs.pcap.

	Ip4 ip = new Ip4();
	Icmp icmp = new Icmp(); // Need an instance so we can check on sub header
	Icmp.DestinationUnreachable unreach = new Icmp.DestinationUnreachable();

Lines#78-80 we allocate uninitialized headers that will be peered with portions of the packet buffer that contain their respective headers. Peered in the sense, that they will point to and address the physical native memory within the packet buffer. For now, they are empty and will throw null pointer exception when any of their accessors are called on.

Then we do various asserts to check values within the packet and more specifically within the decoded state of the packet. The packet state contains information about each header that is present. Each header has a unique numerical ID that is used as a lookup index into various native structures which maintain the packet decoded state.

	assertTrue(packet.hasHeader(Ethernet.ID));

Line#82 checks if ethernet header exists in the packet. The Ethernet.ID is a static final constant defined by jnetpcap API as this is one of the core protocols part of the package. Other protocols can be dynamically registered and will receive a unique runtime ID for the duration of the running program.

	assertTrue(packet.hasHeader(Ip4.ID, 0)); // 1st IP header

Line#83 again checks for the presence of Ip4 header, but with the caveat that we are looking for the first Ip4 header in the packet, incase there are more then one, which is the case in this particular icmp packet.

	assertTrue(packet.hasHeader(icmp));

Line#84 does 2 things. First it checks if icmp header is present (just like previous 2 lines but using some dynamic methods for accessing the id behind the scene) and second, it also peers the icmp header object if icmp header is found and returns a boolean true to let the user know if the peering took place or not (or another words, if the header exists does the peer or returns false and no peering.) This is a shortcut method which essentially equates to the following expression

packet.hasHeader(Icmp.ID) && (packet.getHeader(icmp) != null)

	assertTrue(icmp.hasSubHeader(IcmpType.DESTINATION_UNREACHABLE.getId()));

Line#85 simply checks if sub-header dest-ureachable exists and uses a dynamic method to lookup the sub-header's ID. Sub headers always get their IDs dynamically, therefore we don't have a predefined constant we can use. No peering with this type of call.

	assertTrue(icmp.hasSubHeader(unreach));

Line#86 does both header exists check and peers at the same time in one call. Since we use this type of call here, line #85 is redundant, but its in the testcase anyhow, since are using a slightly different part of the API.

	assertTrue(packet.hasHeader(ip, 1)); // 2nd IP header

Line#87 looks almost identical to the line#83, except we are now looking for the second instance of Ip4 header within the packet, that is the Ip4 header we expect to find under icmp-unreach header. This is the check and peer version of the call, so our ip object is peered and can read values out of the buffer that correspond to the second Ip4 header.

	assertTrue(packet.hasHeader(Udp.ID));
	assertTrue(packet.hasHeader(Payload.ID));

Line#88-89 we check for presence of udp and a special builtin payload header which is a catch all header at the end of every packet. It acts like a real header, and you use it as such. Payload only contains a single field which is essentially all of its data. You can access any part of any header, not just the payload header, using the super class JBuffer methods, but the field also has a special purpose. It allows the payload or the last part of the packet to be formatted for user output when the packet is converted to a string or dumped to a stream.

	// Check specific values
	assertEquals(3, icmp.type());
	assertEquals(3, icmp.code());
	assertEquals(0x2731, icmp.checksum());
	assertEquals(0, unreach.reserved());

	assertEquals(0x8724, ip.checksum());
	assertEquals(440, ip.length());

Lines#91-98 demonstrate how our peered objects can be used to read values and fields out of the packet.

	assertFalse(icmp.hasSubHeader(IcmpType.ECHO_REPLY.getId()));
	assertFalse(icmp.hasSubHeader(IcmpType.PARAM_PROBLEM.getId()));

Lines#101-102 play the devil's advocate and check for sub-headers we know for sure do not exist in the packet. They check for their absence not their presence with assertFalse calls.

40+ Misc.

Here is a large jUnit test case that shows off numerous capabilities of jNetPcap:

package org.jnetpcap;

import java.io.File;
import java.io.IOException;
import java.net.SocketException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

import junit.framework.TestCase;
import junit.textui.TestRunner;

import org.jnetpcap.nio.JBuffer;
import org.jnetpcap.nio.JMemory;
import org.jnetpcap.nio.JNumber;
import org.jnetpcap.nio.JNumber.Type;

/**
 * @author Mark Bednarczyk
 * @author Sly Technologies, Inc.
 */
@SuppressWarnings("deprecation")
public class TestPcapJNI
    extends TestCase {

	// private final static String device =
	// "\\Device\\NPF_{BC81C4FC-242F-4F1C-9DAD-EA9523CC992D}";

	private final static String win =
	    "\\Device\\NPF_{BC81C4FC-242F-4F1C-9DAD-EA9523CC992D}";

	private final static String linux = "any";

	private final static boolean isWindows =
	    "Windows XP".equals(System.getProperty("os.name"));

	private final static String device = (isWindows) ? win : linux;

	private final static String fname = "tests/test-l2tp.pcap";

	private static final int OK = 0;

	private static final int snaplen = 64 * 1024;

	private static final int promisc = 1;

	private static final int oneSecond = 1000;

	/**
	 * Will generate HTTP traffic to a website. Use start() to start in a test
	 * method, and always put stop() in tearDown. Safe to call stop even when
	 * never started.
	 */
	private static final HttpTrafficGenerator gen = new HttpTrafficGenerator();

	private static File tmpFile;

	static {
		try {
			tmpFile = File.createTempFile("temp-", "-TestPcapJNI");
		} catch (IOException e) {
			tmpFile = null;
			System.err.println("Unable to initialize a temporary file");
		}

	}

	private StringBuilder errbuf = new StringBuilder();

	private final PcapHandler<?> doNothingHandler = new PcapHandler() {

		public void nextPacket(Object userObject, long seconds, int useconds,
		    int caplen, int len, ByteBuffer buffer) {
			// Do nothing handler
		}
	};

	/**
	 * @throws java.lang.Exception
	 */
	protected void setUp() throws Exception {

		errbuf = new StringBuilder();

		if (tmpFile.exists()) {
			assertTrue(tmpFile.delete());
		}

	}

	/**
	 * Test disabled, as it requires live packets to capture. To enable the test
	 * just rename the method, by removing the prefix SKIP. Then make sure there
	 * are live packets to be captured.
	 */
	public void SKIPtestOpenLiveAndDispatch() {

		Pcap pcap = Pcap.openLive(device, 10000, 1, 60 * 1000, errbuf);
		assertNotNull(errbuf.toString(), pcap);

		PcapHandler handler = new PcapHandler() {

			public void nextPacket(String user, long seconds, int useconds,
			    int caplen, int len, ByteBuffer buffer) {

				// System.out.printf("%s, ts=%s caplen=%d len=%d capacity=%d\n", user
				// .toString(), new Date(seconds * 1000).toString(), caplen, len,
				// buffer.capacity());

			}

		};

		pcap.dispatch(10, handler, "Hello");

		pcap.close();
	}

	/**
	 * @throws java.lang.Exception
	 */
	protected void tearDown() throws Exception {
		errbuf = null;

		/*
		 * Stop the traffic generator, even when not running need to call to make
		 * sure its not running.
		 */
		gen.stop();

		if (tmpFile != null && tmpFile.exists()) {
			tmpFile.delete();
		}
	}

	public void testCompileNoPcapNullPtrHandling() {
		try {
			Pcap.compileNoPcap(1, 1, null, null, 1, 1);
			fail("Expected a NULL pointer exception.");
		} catch (NullPointerException e) {
			// OK
		}
	}

	public void testCompileNullPtrHandling() {
		Pcap pcap = Pcap.openOffline(fname, errbuf);
		try {
			pcap.compile(null, null, 1, 0);
			fail("Expected a NULL pointer exception.");
		} catch (NullPointerException e) {
			// OK
		} finally {
			pcap.close();
		}
	}

	public void testDataLinkNameToValNullPtrHandling() {
		try {
			Pcap.datalinkNameToVal(null);
			fail("Expected a NULL pointer exception.");
		} catch (NullPointerException e) {
			// OK
		}
	}

	public void testPcapClosedExceptionHandling() {
		Pcap pcap = Pcap.openOffline(fname, errbuf);
		pcap.close();

		try {
			pcap.breakloop();
			fail("Expected PcapClosedException");
		} catch (PcapClosedException e) {
			// Success
		}
	}

	public void testDatalinkNameToValue() {
		assertEquals(1, Pcap.datalinkNameToVal("EN10MB"));
	}

	public void testDatalinkValueToDescription() {
		assertEquals("Ethernet", Pcap.datalinkValToDescription(1));

	}

	public void testDatalinkValueToName() {
		assertEquals("EN10MB", Pcap.datalinkValToName(1));

	}

	public void testDispatchNullPtrHandling() {
		Pcap pcap = Pcap.openOffline(fname, errbuf);
		try {
			pcap.dispatch(1, (ByteBufferHandler) null, "");
			fail("Expected a NULL pointer exception.");
		} catch (NullPointerException e) {
			// OK
		} finally {
			pcap.close();
		}
	}

	public void testErrbuf() throws SocketException, InterruptedException {

		// Test using a bogus device name that's sure to fail
		errbuf.append("def"); // Set dummy message and it should be replaced
		Pcap pcap = Pcap.openLive("abc", 101, 1, 60, errbuf);
		assertNull(pcap);

		assertFalse("Our pre-initialized error message should have been cleared",
		    "def".equals(errbuf.toString()));

		assertTrue("Error buffer should contain an error message",
		    errbuf.length() != 0);
	}

	public void testFilterCompileAndSetFilter() {
		PcapBpfProgram bpf = new PcapBpfProgram();
		String str = "host 192.168.101";

		// System.out.println("trying to compiler the filter() OK\n");
		// System.out.flush();
		Pcap pcap = Pcap.openOffline(fname, errbuf);
		// System.out.println("filter was compiled OK\n"); System.out.flush();
		assertNotNull(errbuf.toString(), pcap);

		int r = pcap.compile(bpf, str, 0, 0);
		assertEquals(pcap.getErr(), 0, r);

		PcapHandler handler = new PcapHandler() {
			public void nextPacket(String user, long seconds, int useconds,
			    int caplen, int len, ByteBuffer buffer) {

				// System.out.printf("%s, ts=%s caplen=%d len=%d capacity=%d\n", user
				// .toString(), new Date(seconds * 1000).toString(), caplen, len,
				// buffer.capacity());
			}
		};

		// System.out.println("trying to set the filter() OK\n");
		// System.out.flush();
		assertEquals(OK, pcap.setFilter(bpf));
		// System.out.println("filter was set OK\n"); System.out.flush();
		assertEquals(OK, pcap.loop(10, handler, str));

		Pcap.freecode(bpf);

		pcap.close();
	}

	public void testFilterCompileNoPcapAndAccessors() {
		PcapBpfProgram bpf = new PcapBpfProgram();

		String str = "host 192.168.1.1";

		int r = Pcap.compileNoPcap(1024, 1, bpf, str, 0, 0);
		assertEquals(OK, r);

		assertEquals(26, bpf.getInstructionCount());
		assertEquals(120259084320L, bpf.getInstruction(10));

		// Boundary checks
		try {
			bpf.getInstruction(-10);
			fail("Failed to generate exception on low index boundary");
		} catch (IndexOutOfBoundsException e) {
			// OK
		}

		// Boundary checks
		try {
			bpf.getInstruction(26);
			fail("Failed to generate exception on upper index boundary");
		} catch (IndexOutOfBoundsException e) {
			// OK
		}

		Pcap.freecode(bpf);
	}

	public void testFindAllDevs() {
		List devs = new ArrayList(); // List filled in by
		// findAllDevs

		int r = Pcap.findAllDevs(devs, errbuf);
		assertEquals(errbuf.toString(), 0, r);
		assertFalse(devs.isEmpty());

		// System.out.println(devs);
	}

	public void testFindAllDevsNullPtrHandling() {
		try {
			Pcap.findAllDevs(null, null);
			fail("Expected a NULL pointer exception.");
		} catch (NullPointerException e) {
			// OK
		}
	}

	public void testFreeAllDevsNullPtrHandling() {
		try {
			Pcap.freeAllDevs(null, (StringBuilder) null);
			fail("Expected a NULL pointer exception.");
		} catch (NullPointerException e) {
			// OK
		}
	}

	public void testFreeCodeNullPtrHandling() {
		try {
			Pcap.freecode(null);
			fail("Expected a NULL pointer exception.");
		} catch (NullPointerException e) {
			// OK
		}
	}

	public void testGetNonBlockNullPtrHandling() {
		Pcap pcap = Pcap.openOffline(fname, errbuf);
		try {
			pcap.getNonBlock(null);
			fail("Expected a NULL pointer exception.");
		} catch (NullPointerException e) {
			// OK
		} finally {
			pcap.close();
		}
	}

	public void testLibVersion() {
		assertNotNull(Pcap.libVersion());
	}

	public void testLoopNullPtrHandling() {
		Pcap pcap = Pcap.openOffline(fname, errbuf);
		try {
			pcap.loop(1, (ByteBufferHandler) null, null);
			fail("Expected a NULL pointer exception.");
		} catch (NullPointerException e) {
			// OK
		} finally {
			pcap.close();
		}
	}

	public void testNextExNullPtrHandling() {
		Pcap pcap = Pcap.openOffline(fname, errbuf);
		try {
			pcap.nextEx((PcapHeader) null, null);
			fail("Expected a NULL pointer exception.");
		} catch (NullPointerException e) {
			// OK
		} finally {
			pcap.close();
		}
	}

	public void testNextNullPtrHandling() {
		Pcap pcap = Pcap.openOffline(fname, errbuf);
		try {
			pcap.next((PcapHeader) null, null);
			fail("Expected a NULL pointer exception.");
		} catch (NullPointerException e) {
			// OK
		} finally {
			pcap.close();
		}
	}

	public void testNextEx() {
		Pcap pcap = Pcap.openOffline(fname, errbuf);
		try {
			PcapHeader header = new PcapHeader(JMemory.Type.POINTER);
			JBuffer buffer = new JBuffer(JMemory.Type.POINTER);

			assertEquals(1, pcap.nextEx(header, buffer));

			assertEquals(114, header.caplen());
			assertEquals(114, buffer.size());

		} finally {
			pcap.close();
		}
	}

	public void testNext() {
		Pcap pcap = Pcap.openOffline(fname, errbuf);
		try {
			PcapHeader header = new PcapHeader(); // allocated memory
			JBuffer buffer = new JBuffer(JMemory.Type.POINTER);

			buffer = pcap.next(header, buffer);
			
			assertNotNull(buffer);
			assertEquals(114, header.caplen());
			assertEquals(114, buffer.size());
		} finally {
			pcap.close();
		}
	}

	public void testOpenDeadAndClose() {

		Pcap pcap = Pcap.openDead(1, 10000); // DLT, SNAPLEN
		assertNotNull(errbuf.toString(), pcap);

		pcap.close();
	}

	public void testOpenLiveAndDatalinkAndClose() throws SocketException,
	    InterruptedException {

		// System.out.println(System.getProperty("os.name"));
		Pcap pcap = Pcap.openLive(device, 101, 1, 60, errbuf);
		assertNotNull(errbuf.toString(), pcap);

		// Physical field initialized from JNI space
		assertFalse("0".equals(pcap.toString()));

		// Check linklayer 1 is for DLT_EN10MB
		// assertEquals(113, pcap.datalink());

		pcap.close();

		try {
			pcap.close();
			fail();
		} catch (IllegalStateException e) {
			// Expecting this exception on second call to close()
		}
	}

	public void testOpenLiveAndLoopWithBreakloop() {

		Pcap pcap = Pcap.openLive(device, 10000, 1, 60 * 1000, errbuf);
		assertNotNull(errbuf.toString(), pcap);

		PcapHandler handler = new PcapHandler() {

			public void nextPacket(String user, long seconds, int useconds,
			    int caplen, int len, ByteBuffer buffer) {

				// System.out.printf("%s, ts=%s caplen=%d len=%d capacity=%d\n", user
				// .toString(), new Date(seconds * 1000).toString(), caplen, len,
				// buffer.capacity());

			}

		};

		pcap.breakloop(); // Should cause it to exit immediately
		assertEquals(
		    "Error code does not indicate breakloop interrupted the loop when it should have",
		    -2, pcap.loop(10, handler, "Hello"));

		pcap.close();
	}

	public void testOpenOfflineAndClose() {

		Pcap pcap = Pcap.openOffline(fname, errbuf);
		assertNotNull(errbuf.toString(), pcap);

		PcapHandler handler = new PcapHandler() {

			public void nextPacket(String user, long seconds, int useconds,
			    int caplen, int len, ByteBuffer buffer) {

				// System.out.printf("%s, ts=%s caplen=%d len=%d capacity=%d\n", user
				// .toString(), new Date(seconds * 1000).toString(), caplen, len,
				// buffer.capacity());

			}

		};

		assertEquals("Expected to receive exactly 10 packets", 10, pcap.dispatch(
		    10, handler, "Hello"));
		pcap.close();
	}

	public void testOpenOfflineAndLoop() {

		Pcap pcap = Pcap.openOffline(fname, errbuf);
		assertNotNull(errbuf.toString(), pcap);

		PcapHandler handler = new PcapHandler() {

			public void nextPacket(String user, long seconds, int useconds,
			    int caplen, int len, ByteBuffer buffer) {

				// System.out.printf("%s, ts=%s caplen=%d len=%d capacity=%d\n", user
				// .toString(), new Date(seconds * 1000).toString(), caplen, len,
				// buffer.capacity());

			}

		};

		assertEquals(OK, pcap.loop(10, handler, "Hello"));

		pcap.close();
	}

	public void testOpenOfflineAndNext() {

		Pcap pcap = Pcap.openOffline(fname, errbuf);
		assertNotNull(errbuf.toString(), pcap);
		PcapPktHdr hdr = new PcapPktHdr();

		ByteBuffer buffer = pcap.next(hdr);

		assertEquals(114, buffer.capacity()); // length of the packet should match
		assertEquals(114, hdr.getCaplen()); // Should match within the header too
		assertEquals(114, hdr.getLen()); // Should match within the header too

		// System.out.println(new Date(hdr.getSeconds() * 1000).toString());

		pcap.close();
	}

	public void testOpenOfflineAndNextEx() {

		Pcap pcap = Pcap.openOffline(fname, errbuf);
		assertNotNull(errbuf.toString(), pcap);
		PcapPktHdr hdr = new PcapPktHdr();
		PcapPktBuffer buf = new PcapPktBuffer();

		int r = pcap.nextEx(hdr, buf);
		assertEquals(1, r);
		assertNotNull(buf.getBuffer());

		assertEquals(114, buf.getBuffer().capacity()); // length of the packet
		// should match
		assertEquals(114, hdr.getCaplen()); // Should match within the header too
		assertEquals(114, hdr.getLen()); // Should match within the header too

		// System.out.println(new Date(hdr.getSeconds() * 1000).toString());

		pcap.close();
	}

	public void testPcapDLTAndDoNameToValueComparison() {
		int match = 0; // counts how many constants compared OK

		for (PcapDLT c : PcapDLT.values()) {
			int dlt = c.value;
			String libName = Pcap.datalinkValToName(dlt);
			if (libName == null) {
				// System.out.printf("no dlt: dlt=%d enum=%s\n", dlt, c.toString());
				continue;
			}

			if (libName.equals(c.name())) {
				match++;

				// System.out.printf("matched: dlt=%d enum=%s pcap=%s desc=%s\n", dlt, c
				// .toString(), libName, c.description);
			} else {
				// System.out.printf("unmatched: dlt=%d enum=%s pcap=%s desc=%s\n", dlt,
				// c
				// .toString(), libName, c.description);
			}
		}

		// System.out.println("Have " + match + " matches out of "
		// + PcapDLT.values().length);

		assertTrue(
		    "Something is wrong, most constants should match native pcap library",
		    match > 20);

		// for (int dlt = 0; dlt < 100; dlt ++) {
		// String libName = Pcap.datalinkValToName(dlt);
		// PcapDLT c = PcapDLT.valueOf(dlt);
		//			
		// if (c == null && libName != null) {
		// System.out.printf("We don't have dlt=%d pcap=%s\n", dlt, libName);
		// }
		// }
	}

	public void testPcapOpenLiveNullPtrHandling() {
		try {
			Pcap.openLive(null, 1, 1, 1, null);
			fail("Expected a NULL pointer exception.");
		} catch (NullPointerException e) {
			// OK
		}
	}

	public void testPcapOpenOfflineNullPtrHandling() {
		try {
			Pcap.openOffline(null, null);
			fail("Expected a NULL pointer exception.");
		} catch (NullPointerException e) {
			// OK
		}
	}

	public void testSetAndGetNonblock() {
		Pcap pcap = Pcap.openLive(device, 10000, 1, 60 * 1000, errbuf);
		assertNotNull(errbuf.toString(), pcap);

		assertEquals(OK, pcap.getNonBlock(errbuf));

		pcap.close();
	}

	public void testSetFilterNullPtrHandling() {
		Pcap pcap = Pcap.openOffline(fname, errbuf);
		try {
			pcap.setFilter(null);
			fail("Expected a NULL pointer exception.");
		} catch (NullPointerException e) {
			// OK
		} finally {
			pcap.close();
		}
	}

	public void testSetNonBlockNullPtrHandling() {
		Pcap pcap = Pcap.openOffline(fname, errbuf);
		try {
			pcap.setNonBlock(1, null);
			fail("Expected a NULL pointer exception.");
		} catch (NullPointerException e) {
			// OK
		} finally {
			pcap.close();
		}
	}

	/**
	 * 

* Test case in response to * Bug #1767744 - PcapHandler object ptr error in loop() and dispatch(). * The bug was that PcapHandler jobject ptr in JNI jnetpcap.cpp, was set * incorrectly to jobject of the parent which is the Pcap object itself. The * neccessary method "nextPacket" was looked up correctly using the proper * object but method execution was based on the parent Pcap object not the * PcapHandler object passed in. Therefore Java code when it was setting and * accessing properties within the PcapHandler sub-class, it was actually * clobering data within the Pcap object. Both object's states were terribly * incosinstent, private fields had bogus values, that changed for no reason, * etc... Very easy fix, the jobject for 'jhandler' was substituted for * jobject 'obj' that was used to fix this problem. The problem was both in * dispatch() and loop() methods since they are nearly an identical copy of * each other. *

*

* To test this we have to create a PcapHandler object set private fields * within it, we'll use an anonymous class, then read in the contents of the * entire contents of a test datafile, while updating the value of the field. * Then at the end we should have consitent value in that private field. Since * the problem seemed complex, but was actually a very easy fix, this should * never really break again, but we will check for it anyhow. *

*/ public void testPcapHandlerParentOverrideBugUsingLoop() { Pcap pcap = Pcap.openOffline(fname, errbuf); if (pcap == null) { fail("Unable to open test data file because " + errbuf.toString()); } final Pcap parent = pcap; // Tracking variable #1 final AtomicInteger pcapCount = new AtomicInteger(); final PcapHandler<?> handler = new PcapHandler() { // Tracking variable #2 private int count = 0; public void nextPacket(Object userObject, long seconds, int useconds, int caplen, int len, ByteBuffer buffer) { pcapCount.addAndGet(1); count++; if (pcapCount.get() != count) { parent.breakloop(); // We exit with breakloop which means FAIL } } }; int r = pcap.loop(Pcap.LOOP_INFINATE, handler, null); if (r == Pcap.LOOP_INTERRUPTED) { /* * Tracking variables are used to make sure they can sustain their * assigned values. The bug caused fields and object state to be overriden * by object of PcapHandler type. */ fail("Handler indicates that 2 tracking variables in 2 objects, " + "did not match"); } else if (r != Pcap.OK) { fail("Error occured: " + pcap.getErr()); } pcap.close(); } /** *

* Test case in response to * Bug #1767744 - PcapHandler object ptr error in loop() and dispatch(). * The bug was that PcapHandler jobject ptr in JNI jnetpcap.cpp, was set * incorrectly to jobject of the parent which is the Pcap object itself. The * neccessary method "nextPacket" was looked up correctly using the proper * object but method execution was based on the parent Pcap object not the * PcapHandler object passed in. Therefore Java code when it was setting and * accessing properties within the PcapHandler sub-class, it was actually * clobering data within the Pcap object. Both object's states were terribly * incosinstent, private fields had bogus values, that changed for no reason, * etc... Very easy fix, the jobject for 'jhandler' was substituted for * jobject 'obj' that was used to fix this problem. The problem was both in * dispatch() and loop() methods since they are nearly an identical copy of * each other. *

*

* To test this we have to create a PcapHandler object set private fields * within it, we'll use an anonymous class, then read in the contents of the * entire contents of a test datafile, while updating the value of the field. * Then at the end we should have consitent value in that private field. Since * the problem seemed complex, but was actually a very easy fix, this should * never really break again, but we will check for it anyhow. *

*/ public void testPcapHandlerParentOverrideBugUsingDispatch() { Pcap pcap = Pcap.openOffline(fname, errbuf); if (pcap == null) { fail("Unable to open test data file because " + errbuf.toString()); } final Pcap parent = pcap; // Tracking variable #1 final AtomicInteger pcapCount = new AtomicInteger(); final PcapHandler<?> handler = new PcapHandler() { // Tracking variable #2 private int count = 0; public void nextPacket(Object userObject, long seconds, int useconds, int caplen, int len, ByteBuffer buffer) { pcapCount.addAndGet(1); count++; if (pcapCount.get() != count) { parent.breakloop(); // We exit with breakloop which means FAIL } } }; int r = pcap.dispatch(Pcap.DISPATCH_BUFFER_FULL, handler, null); if (r == Pcap.LOOP_INTERRUPTED) { /* * Tracking variables are used to make sure they can sustain their * assigned values. The bug caused fields and object state to be overriden * by object of PcapHandler type. */ fail("Handler indicates that 2 tracking variables in 2 objects, " + "did not match"); } else if (r != Pcap.OK) { fail("Error occured: " + pcap.getErr()); } pcap.close(); } public void testPcapDumperUsingLoop() { Pcap pcap = Pcap.openOffline(fname, errbuf); assertNotNull(errbuf.toString(), pcap); PcapDumper dumper = pcap.dumpOpen(tmpFile.getPath()); assertNotNull(pcap.getErr(), dumper); PcapHandler handler = new PcapHandler() { public void nextPacket(PcapDumper dumper, long seconds, int useconds, int caplen, int len, ByteBuffer buffer) { dumper.dump(seconds, useconds, caplen, len, buffer); } }; int r = pcap.loop(Pcap.LOOP_INFINATE, handler, dumper); assertTrue("Something happened in the loop", r == Pcap.OK); dumper.close(); pcap.close(); // System.out.printf("%s: tmp=%d, source=%d\n", tmpFile.getName(), tmpFile // .length(), new File(fname).length()); // assertEquals("dumped file and source file lengths don't match", tmpFile .length(), new File(fname).length()); } public void testPcapDumperUsingDispatch() { Pcap pcap = Pcap.openOffline(fname, errbuf); assertNotNull(errbuf.toString(), pcap); PcapDumper dumper = pcap.dumpOpen(tmpFile.getPath()); assertNotNull(pcap.getErr(), dumper); PcapHandler handler = new PcapHandler() { public void nextPacket(PcapDumper dumper, long seconds, int useconds, int caplen, int len, ByteBuffer buffer) { dumper.dump(seconds, useconds, caplen, len, buffer); } }; /* * Our test file is small, about 24K bytes in size, should fit inside a * buffer full. */ int r = pcap.dispatch(Pcap.DISPATCH_BUFFER_FULL, handler, dumper); assertTrue("Something happened in dispatch", r == Pcap.OK); dumper.close(); pcap.close(); // System.out.printf("%s: tmp=%d, source=%d\n", tmpFile.getName(), tmpFile // .length(), new File(fname).length()); // assertEquals("dumped file and source file lengths don't match", tmpFile .length(), new File(fname).length()); } public void testStats() { PcapStat stats = new PcapStat(); Pcap pcap = Pcap.openLive(device, snaplen, promisc, oneSecond, errbuf); assertNotNull(errbuf.toString(), pcap); pcap.loop(5, doNothingHandler, null); pcap.stats(stats); // System.out.printf("stats=%s\n", stats.toString()); pcap.loop(5, doNothingHandler, null); pcap.stats(stats); // System.out.printf("stats=%s\n", stats.toString()); pcap.close(); } public void SKIPtestDumper() { gen.start(); // Generate network traffic - async method System.out.printf("tmpFile=%s\n", tmpFile.getAbsoluteFile()); Pcap pcap = Pcap.openLive(device, snaplen, promisc, oneSecond, errbuf); assertNotNull(errbuf.toString(), pcap); PcapDumper dumper = pcap.dumpOpen(tmpFile.getAbsolutePath()); assertNotNull(pcap.getErr(), dumper); PcapHandler dumpHandler = new PcapHandler() { public void nextPacket(PcapDumper dumper, long seconds, int useconds, int caplen, int len, ByteBuffer buffer) { dumper.dump(seconds, useconds, caplen, len, buffer); } }; pcap.loop(10, dumpHandler, dumper); assertTrue("Empty dump file " + tmpFile.getAbsolutePath(), tmpFile.length() > 0); // System.out.printf("Temp dumpfile size=%s\n", tmpFile.length()); pcap.close(); } @SuppressWarnings("deprecation") public void testLookupDevAndLookupNetDeprecatedAPI() { String device = Pcap.lookupDev(errbuf); assertNotNull(errbuf.toString(), device); JNumber netp = new JNumber(Type.INT); JNumber maskp = new JNumber(Type.INT); int r = Pcap.lookupNet(device, netp, maskp, errbuf); assertEquals(errbuf.toString(), 0, r); System.out.printf("device=%s netp=%X maskp=%X errbuf=%s\n", device, netp .intValue(), maskp.intValue(), errbuf.toString()); } /** * Bug#1855589 */ public void testIsInjectSupportedWin32() { if (System.getProperty("os.name").toLowerCase().contains("win")) { assertFalse(Pcap.isInjectSupported()); } else { assertTrue(true); // Be explicit } } public void testIsSendpacketSupportedWin32() { if (System.getProperty("os.name").toLowerCase().contains("win")) { assertTrue(Pcap.isSendPacketSupported()); } else { assertTrue(true); // Be explicit } } }

Advanced

This section contains some more advanced examples that demonstrate and stress jNetPcap's capabilities.

WebImage reassembly from HTTP packets

For those looking for more advanced capabilities. Here is an example, that using older jNetPcap release (1.3.b0001) is using TCP reassembly and HttpAnalyzer objects to reconstruct all images that are transmitted and captured via http protocol.

The example using a GUI application, part of the test source tree under tests/java1.5 directory, that displays all images found in a file full of http packets that were transmitting various images. All images span multiple TCP segments. They are seamlessly reassembled and passed over to my handler for processing. The handler creates an AWT image out of them and adds it to a SWING application.

The SWING application is very simple. It creates a single panel using BoxLayout and puts the list of images up on top of the window using regular swing List component. In the mid section of the panel it displays the image that is selected. Here is what it looks like:

And here is the entire application (minus the GUI stuff). You need the dev snapshot jnetpcap-1.3.b0001-milestone1 (downloadable from SourceForge.net download section) to run this, incase you are wondering:

package org.jnetpcap.protocol.tcpip;

import java.awt.Image;

import org.jnetpcap.packet.JPacket;
import org.jnetpcap.packet.JRegistry;
import org.jnetpcap.packet.TestUtils;
import org.jnetpcap.packet.analysis.JController;
import org.jnetpcap.protocol.application.WebImage;
import org.jnetpcap.protocol.tcpip.Http.ContentType;
import org.jnetpcap.protocol.tcpip.Http.Request;
import org.jnetpcap.protocol.tcpip.Http.Response;

/**
 * @author Mark Bednarczyk
 * @author Sly Technologies, Inc.
 */
public class TestWebImage
    extends
    TestUtils {

	public static void main(String[] args) {
		new TestWebImage().test1();
	}

	public void test1() {

		/*
		 * This is part of our SWING application. It takes a list of images and
		 * labels and puts them up in 2 different areas of a panel using BoxLayout.
		 * When you click on any item in the list, it changes the image.
		 */
		final ListOfPanels swingDisplay = new ListOfPanels();

		/*
		 * Now display our SWING application with images already in it. Remember
		 * these images were reconstructed from packets within the capture file.
		 */
		swingDisplay.init();

		javax.swing.SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				TestUtils.displayInFrame(swingDisplay);
			}
		});

		/*
		 * Step 1 - add our Http handler to HttpAnalyzer. Get HttpAnalyzer from
		 * registry, it should already be registered.
		 */
		HttpAnalyzer httpAnalyzer = JRegistry.getAnalyzer(HttpAnalyzer.class);
		httpAnalyzer.add(new HttpHandler() {
			private WebImage web = new WebImage();

			/*
			 * Step 2 - our handler routine.
			 */
			public void processHttp(Http http) {
				if (http.getMessageType() != Http.MessageType.RESPONSE) {
					return;
				}

				JPacket packet = http.getPacket(); // Packet this http belongs to
				final long frame = packet.getFrameNumber();
				final String cmd = http.fieldValue(Request.RequestMethod);
				final String code = http.fieldValue(Response.ResponseCode);
				final String ct = http.fieldValue(Response.Content_Type);
				String cl = http.fieldValue(Response.Content_Length);
				final int payload = http.getPayloadLength();

				if ((code != null && code.equals("200") == false)) {
					return; // Skip error messages
				}

				if (cl == null) {
					cl = Integer.toString(payload);
				}

				/*
				 * Responses always have a content type, since we are looking for
				 * specific content that has been predefined, we can use enum constants.
				 * We're not interested in anything else, otherwise we'd have to use
				 * http.contentType() method which returns a string.
				 */
				ContentType type = http.contentTypeEnum();

				switch (type) {
					case GIF:
					case PNG:
					case JPEG:
						/*
						 * WebImage header has been integrated as a core protocol.
						 */
						WebImage image = packet.getHeader(web);
						Image img = image.getAWTImage();

						/*
						 * Now add image to our SWING application. Label it with content
						 * type for now.
						 */
						String label = "#" + frame + " " + ct + " " + cl + " bytes";
						swingDisplay.add(img, label);

						break;
				}
			}

		});

		/*
		 * TestUtils.openLive is a short cut method used by many jUnit tests during
		 * testing, there others such as openOffline.
		 */
		openLive(JRegistry.getAnalyzer(JController.class));
	}
}

Ip Assembly

Working Ip Fragment Reassembly Example

This is a bit more advanced example of jNetPcap usage. This example is an application that reads packets out of a capture file and reassembles fragmented Ip4 packets.

Tutorial:

Download Source from SVN:

import java.util.HashMap;
import java.util.Map;
import java.util.PriorityQueue;
import java.util.Queue;

import org.jnetpcap.Pcap;
import org.jnetpcap.nio.JBuffer;
import org.jnetpcap.nio.JMemory.Type;
import org.jnetpcap.packet.JMemoryPacket;
import org.jnetpcap.packet.JPacket;
import org.jnetpcap.packet.PcapPacket;
import org.jnetpcap.packet.PcapPacketHandler;
import org.jnetpcap.packet.header.Ip4;

/**
 * This is a demonstration application for reassembling IP fragments.
 * The application is intended only for show purposes on how jNetPcap
 * API can be used.
 * <p>
 * This example application captures IP packets, makes sure they are
 * IPs and creates special packets that are ip only. We will use
 * JMemoryPacket which nicely allows us to construct a new custom
 * packet. Our new packets don't care about the lower OSI layers since
 * that information is irrelavent for Ip reassembly and for the user
 * as well. If we receive a packet that is not fragmented we simply
 * pass it through, no sense in doing anything special with it.
 * </p>
 * 
 * @author Mark Bednarczyk
 * @author Sly Technologies, Inc.
 */
public class IpReassemblyExample
    implements PcapPacketHandler<Object> {

  /**
   * Our custom interface that allows listeners to get our special
   * reassembled IP packets and also provide them with the actual
   * reassembled buffer.
   * 
   * @author Mark Bednarczyk
   * @author Sly Technologies, Inc.
   * @param <T>
   */
  public interface IpReassemblyBufferHandler {
    public void nextIpDatagram(IpReassemblyBuffer buffer);
  }

  /**
   * A special buffer used for reassembling the original IP datagram.
   * The reassembled buffer contains an IP header upfront and it
   * suitable for peering with a packet and then decoding.
   * 
   * <pre>
   * +-----------+-----------+-----------+--&tilde;&tilde;&tilde;&tilde;--+
   * | Ip Header | Ip frag 1 | Ip frag 2 | etc... |
   * +-----------+-----------+-----------+--&tilde;&tilde;&tilde;&tilde;--+
   * </pre>
   * 
   * The header comes from the first segment that was seen part of
   * this IP datagram. The original header is used as a template and
   * then several fields are reset to reflect the state of reassembled
   * packet. The fields modified are:
   * <ul>
   * <li> Ip4.flags</li>
   * <li> Ip4.length</li>
   * <li> Ip4.hlen</li>
   * <li> Ip4.offset</li>
   * </ul>
   * 
   * @author Mark Bednarczyk
   * @author Sly Technologies, Inc.
   */
  public static class IpReassemblyBuffer
      extends JBuffer
      implements Comparable<IpReassemblyBuffer> {

    /**
     * The IP header found at the beggining of this buffer
     */
    private Ip4 header = new Ip4();

    /**
     * Total length of the reassembled IP fragments including the ip
     * header
     */
    private int ipDatagramLength = -1;

    /**
     * Keeps track of how many bytes have been copied into this
     * buffer. When bytesCopiedIntoBuffer == ipDatagramLength, where
     * bytesCopiedIntoBuffer keeps track of the total size of the
     * original datagram in its entirety (including Ip4 header), then
     * the reassembly is complete.
     */
    private int bytesCopiedIntoBuffer = 20;

    /**
     * Offset where the Ip payload begins in the reassembled IP
     * datagram. Always constant since our IP header is constant as
     * well.
     */
    private final int start = 20; // length Ip4 header

    /**
     * Timestamp when this buffer is officially timedout
     */
    private final long timeout;

    /**
     * A hash of Ip4.source, Ip4.destination, Ip4.id, Ip4.type
     */
    private final int hash;

    /**
     * Override the default hashcode with our special Ip4 based one
     */
    @Override
    public int hashCode() {
      return this.hash;
    }

    /**
     * Creates a new buffer for IP fragment reassebly. The buffer
     * appends an Ip4 header to the front of the buffer. The supplied
     * ip header is only used as a template and a copy is made. This
     * allows the buffer to retain all the vital Ip4 information found
     * in the original Ip4 datagram before it was fragmented.
     * 
     * @param ip
     *            ip header of one of the fragments to be used as a
     *            template for the reassembled packet
     * @param size
     *            amount of memory to allocate for reassembly
     * @param timeout
     *            timestamp in millis when this buffer should be timed
     *            out
     * @param hash
     *            special Ip4 based hash used for identifying this
     *            buffer quickly
     */
    public IpReassemblyBuffer(Ip4 ip, int size, long timeout, int hash) {
      super(size); // allocate memory

      this.timeout = timeout;
      this.hash = hash;

      transferFrom(ip); // copy fragment's Ip header to our buffer
    }

    /**
     * Deep copy the supplied Ip4 header to the front of the buffer.
     * Reset some ip fields to reflect the state of this buffer.
     * 
     * @param ip
     *            source Ip4 header to use as template
     */
    private void transferFrom(Ip4 ip) {
      /*
       * Copy ip header as a template
       */
      ip.transferTo(this, 0, 20, 0);

      /*
       * Peer a temporary working Ip4 header to the start of our
       * buffer. It contains our template Ip4 header data.
       */
      header.peer(this, 0, 20);

      /*
       * Now reset a few things that are no longer neccessary in a
       * reassembled datagram
       */
      header.hlen(5); // Clear IP optional headers
      header.clearFlags(Ip4.FLAG_MORE_FRAGEMNTS); // FRAG flag
      header.offset(0); // Offset is now 0
      header.checksum(0); // Reset header CRC, unless we calculate it
      // again
    }

    /**
     * Adds a IP fragment to the buffer. This fragment is also the
     * last framgent of the fragment series which carries special
     * information about the original IP datagram.
     * 
     * @param packet
     *            a packet buffer containing IP fragment data
     * @param offset
     *            offset into this buffer where the fragment data
     *            should be copied to
     * @param length
     *            the length of the fragment data
     * @param packetOffset
     *            offset into the packet buffer where fragment data
     *            begins
     */
    public void addLastSegment(JBuffer packet, int offset,
        int length, int packetOffset) {

      addSegment(packet, offset, length, packetOffset);

      this.ipDatagramLength = start + offset + length;

      /*
       * Trucate the size of the JBuffer to match that of ip reassebly
       * buffer now that we know that we have received the last
       * fragment and where it ends
       */
      super.setSize(this.ipDatagramLength);

      /*
       * Set Ip4 total length field, now that we know what it is
       */
      header.length(ipDatagramLength); // Set Ip4 total length field
    }

    /**
     * Adds a IP fragment to the buffer. The fragment data is copied
     * into this buffer at specified offset from the supplied packet
     * data buffer.
     * 
     * @param packet
     *            a packet buffer containing IP fragment data
     * @param offset
     *            offset into this buffer where the fragment data
     *            should be copied to
     * @param length
     *            the length of the fragment data
     * @param packetOffset
     *            offset into the packet buffer where fragment data
     *            begins
     */
    public void addSegment(JBuffer packet, int offset, int length,
        int packetOffset) {

      /*
       * Keep track of how much data we're copied so far. Needed to
       * determine if the reassembly process is complete.
       */
      this.bytesCopiedIntoBuffer += length;

      /*
       * Do the actual copy of fragment data into this buffer. The
       * transfer is done using a native copy call.
       */
      packet.transferTo(this, packetOffset, length, offset + start);
    }

    /**
     * For ordering buffers according to their timeout value. This is
     * specifically useful when using a PriorityQueue which will order
     * the buffers for us according to the timeout timestamp. The
     * oldest buffers are on top of the queue, while the youngest are
     * at the bottom.
     */
    public int compareTo(IpReassemblyBuffer o) {
      return (int) (o.timeout - this.timeout);
    }

    /**
     * Checks if the buffer reassembly is complete. If the number of
     * bytes copied into this buffer including the ip header up front,
     * equals the length of the original IP datagram, that means the
     * fragmentation succeeded and is complete.
     * 
     * @return true if fragmentation succeeded and completely done
     */
    public boolean isComplete() {
      return this.ipDatagramLength == this.bytesCopiedIntoBuffer;
    }

    /**
     * Compares the timeout timestamp against the current time. If
     * timeout timestamp is still in the future, then it returns
     * false. If the timestamp is in the past, then true is returned
     * and buffer is considered timedout.
     * 
     * @return true if buffer is timedout, otherwise false
     */
    public boolean isTimedout() {
      return this.timeout < System.currentTimeMillis(); // Future or
      // past
    }

    /**
     * Returns the working Ip4 header instance found at the front of
     * this buffer
     * 
     * @return Ip4 header for this IP datagram
     */
    public Ip4 getIpHeader() {
      return header;
    }

  }

  /**
   * Default buffer size to allocate for reassembly. Needs to be large
   * enough to hold the Ip4 header upfront and the contetents of all
   * fragment for a single fragmented Ip4 datagram.
   */
  private static final int DEFAULT_REASSEMBLY_SIZE = 8 * 1024; // 8k

  // packets

  /**
   * Our example application. Arguments are ignored. Reads 6 packets
   * from file "tests/test-ipreassembly2.pcap" and reassembles the IP
   * fragments found into a new ip-only super packet. The new packet
   * contains the Ip4 header as DLT.
   * 
   * @param args
   *            ignored
   */
  public static void main(String[] args) {

    StringBuilder errbuf = new StringBuilder();
    Pcap pcap =
        Pcap.openOffline("tests/test-ipreassembly2.pcap", errbuf);
    if (pcap == null) {
      System.err.println(errbuf.toString());
      return;
    }

    /**
     * Set the capture. We capture 6 packets, use a 5 second timeout
     * on reassembly buffers and we supply our reassembly handler (our
     * application) as the recipient of packets from libpcap. To it,
     * we supply an anonymous handler that receives the reassembly
     * buffers. We simply convert those to packets and print them out.
     * If the buffer is incomplete, meaning it was timed out before we
     * received the last IP fragment, we simply report the event as an
     * warning.
     */
    pcap.loop(6, new IpReassemblyExample(5 * 1000,
        new IpReassemblyBufferHandler() {

          public void nextIpDatagram(IpReassemblyBuffer buffer) {

            if (buffer.isComplete() == false) {
              System.err.println("WARNING: missing fragments");
            } else {

              /*
               * Create a packet pointer. Uninitialized packet.
               */
              JPacket packet = new JMemoryPacket(Type.POINTER);

              /**
               * The buffer contains Ip4 header upfront followed by
               * original Ip4 datagram payload. We peer the packet's
               * data buffer to the reassmbly buffer, starting at the
               * Ip4 header.
               */
              packet.peer(buffer);

              /*
               * Decode the packet. We know the first header is the
               * Ip4 header.
               */
              packet.scan(Ip4.ID); // decode the packet

              /*
               * Pretty print the packet
               */
              System.out.println(packet.toString());
            }

          }

        }), null);
  }

  /**
   * Keeps track of all IP datagrams being reassembled
   */
  private Map<Integer, IpReassemblyBuffer> buffers =
      new HashMap<Integer, IpReassemblyBuffer>();

  /**
   * User registered handler.
   */
  private IpReassemblyBufferHandler handler;

  /**
   * Ip4 header we use for incoming packets from libpcap
   */
  private Ip4 ip = new Ip4(); // Ip4 header

  /**
   * Amount of time in milli seconds, after which reassembly buffers
   * are timedout out of the queue.
   */
  private final long timeout;

  /**
   * Timeout queue to which all new reassembly buffers are added. The
   * queue is prioritized according to timeout timestamp of each
   * buffer.
   */
  private final Queue<IpReassemblyBuffer> timeoutQueue =
      new PriorityQueue<IpReassemblyBuffer>();

  /**
   * Creates a reassembly handler that reassembles all incoming IP
   * packet.
   * 
   * @param timeout
   *            sets the default amount of time in millis, before
   *            reassembly buffers are timedout
   * @param handler
   *            user supplied handler to call with reassembled buffers
   */
  public IpReassemblyExample(long timeout,
      IpReassemblyBufferHandler handler) {
    this.timeout = timeout;
    if (handler == null) {
      throw new NullPointerException();
    }
    this.handler = handler;
  }

  /**
   * Process an Ip4 fragment. Fragment is copied into appropriate
   * reassembly buffer
   * 
   * @param packet
   *            Ip4 fragment packet
   * @param ip
   *            our working Ip4 header already peered to the packet
   */
  private IpReassemblyBuffer bufferFragment(PcapPacket packet, Ip4 ip) {
    IpReassemblyBuffer buffer = getBuffer(ip);

    /*
     * Lets keep in mind that ip.getOffset() is a header offset into
     * the packet buffer, while ip.offset() is the Ip4.offset field
     * which is the fragment offset into the overall datagram, in
     * multiples of 8 bytes
     */
    final int hlen = ip.hlen() * 4;
    final int len = ip.length() - hlen;
    final int packetOffset = ip.getOffset() + hlen;
    final int dgramOffset = ip.offset() * 8;
    buffer.addSegment(packet, dgramOffset, len, packetOffset);

    if (buffer.isComplete()) {
      if (buffers.remove(ip.hashCode()) == null) {
        System.err
            .println("bufferFragment(): failed to remove buffer");
        System.exit(0);
      }
      timeoutQueue.remove(buffer);

      dispatch(buffer);
    }

    return buffer;
  }

  /**
   * Process an Ip4 fragment. Fragment is copied into appropriate
   * reassembly buffer. This is also a special fragment as its the
   * last fragment of the fragmented IP datagram.
   * 
   * @param packet
   *            Ip4 fragment packet
   * @param ip
   *            our working Ip4 header already peered to the packet
   */
  private IpReassemblyBuffer bufferLastFragment(PcapPacket packet,
      Ip4 ip) {
    IpReassemblyBuffer buffer = getBuffer(ip);

    /*
     * Lets keep in mind that ip.getOffset() is a header offset into
     * the packet buffer, while ip.offset() is the Ip4.offset field
     * which is the fragment offset into the overall datagram, in
     * multiples of 8 bytes
     */
    final int hlen = ip.hlen() * 4;
    final int len = ip.length() - hlen;
    final int packetOffset = ip.getOffset() + hlen;
    final int dgramOffset = ip.offset() * 8;
    buffer.addLastSegment(packet, dgramOffset, len, packetOffset);

    if (buffer.isComplete()) {
      if (buffers.remove(buffer.hashCode()) == null) {
        System.err
            .println("bufferLastFragment(): failed to remove buffer");
        System.exit(0);
      }
      timeoutQueue.remove(buffer);

      dispatch(buffer);
    }

    return buffer;
  }

  /**
   * Calls on user's handlers nextIpDatagram() callback method.
   * 
   * @param buffer
   *            reassembled buffer to send to the user's callback
   */
  private void dispatch(IpReassemblyBuffer buffer) {
    handler.nextIpDatagram(buffer);
  }

  /**
   * Retrieves a reassembly buffer for this particular Ip packet. The
   * supplied Ip4 header is used to determine if a buffer already
   * exists and if not a new one is created.
   * 
   * @param ip
   *            Ip4 header of current Ip4 fragment
   * @return a reassembly buffer used for reassembly of this Ip4
   *         datagram
   */
  private IpReassemblyBuffer getBuffer(Ip4 ip) {

    IpReassemblyBuffer buffer = buffers.get(ip.hashCode());
    if (buffer == null) { // First time we're seeing this id

      /*
       * Calculate when the buffer should be timedout due to missing
       * fragments
       */
      final long bufTimeout =
          System.currentTimeMillis() + this.timeout;
      buffer =
          new IpReassemblyBuffer(ip, DEFAULT_REASSEMBLY_SIZE,
              bufTimeout, ip.hashCode());
      buffers.put(ip.hashCode(), buffer);
    }

    return buffer;
  }

  /**
   * Catch incoming packets from libpcap and if they are Ip packets
   * reassemble them.
   * 
   * @param packet
   *            a temporary singleton packet received from libpcap
   * @param user
   *            user object
   */
  public void nextPacket(PcapPacket packet, Object user) {

    if (packet.hasHeader(ip)) {
      /*
       * Check if we have an IP fragment
       */
      if ((ip.flags() & Ip4.FLAG_MORE_FRAGEMNTS) != 0) {
        bufferFragment(packet, ip);

        /*
         * record the last fragment
         */
      } else {

        bufferLastFragment(packet, ip);

        /*
         * Here we have a non-fragmented IP packet so we just pass it
         * on
         */
      }

      /*
       * Our crude timeout mechanism, should be implemented as a
       * separate thread
       */
      timeoutBuffers();
    }
  }

  /**
   * Check the timeout queue and timeout any buffers that are
   * timedout. The timed out buffer are dispatched, incomplete to the
   * user's handler. Buffers that are still on the queue, are
   * incomplete but have not timedout yet are ignored.
   */
  private void timeoutBuffers() {
    while (timeoutQueue.isEmpty() == false) {

      if (timeoutQueue.peek().isTimedout()) {
        dispatch(timeoutQueue.poll());
      } else {
        break;
      }
    }
  }
}