|
|||||||||
| PREV PACKAGE NEXT PACKAGE | FRAMES NO FRAMES | ||||||||
See:
Description
| Interface Summary | |
|---|---|
| Capture<T extends CapturePacket> | Main capture session interface. |
| CaptureDevice | Describes the capture device that captured the packet. |
| CaptureDeviceCounters | |
| CaptureDeviceState | |
| CaptureListener | A listener that can be registered with a Capture object, to be notified of capture events. |
| CapturePacket | A captured network packet. |
| CapturePacketInput<T extends CapturePacket> | CapturePacketInput interface reads CapturePacket objects from underlying storage or stream which is defined by the class implementing this interface. |
| CapturePacketOutput<T extends Packet> | Allows writting of CapturePackets. |
| Captures.Factory | A factory interface for interfacing with implementation of jNetStream API. |
| Captures.LiveCaptureFactory | Factory classes which create live capture sessions and transmit packets. |
| Captures.LocalFactory | Factory interface for local sessions. |
| Captures.RemoteFactory | Factory interface for remote sessions. |
| DeserializedPacket | A packet that was sent accross a stream. |
| FastIterator | Special iterator that allows very low level iteration over large buffers of aquired packet data. |
| FileCapture<T extends FilePacket> | File capture extends the basic Capture interface and adds several capabilites which can only be done with files, as opposed to live captures for example. |
| FilePacket | A file based packet. |
| FormatType.Detail | Detailed information about a specific format. |
| InputCapture<T extends CapturePacket> | Formatted InputStream based capture session. |
| InputCapture.FormatTypeOtherFactory | Factory interface used by InputCapture factories to load NPL based files
who's format is FormatType.Other. |
| InputIterator<T> | Iterator which iterates over the elements within the stream. |
| LiveCapture | A network packet cature on a live network interface. |
| LiveCaptureDevice | |
| LiveIterator | IO based iterator over a live capture session. |
| LivePacket | A packet that was captured on a live network interface. |
| LivePacketFactory | Factory for creating new Live Packets |
| NetTransmitter | Transmitter which transmits or send low level packets using the link layer of the open network interface. |
| OutputCapture | A capture session that allows content to be sent to a formatted stream. |
| PacketIndexer<I extends CapturePacket> | All getter methods, that return a packet return subclass of CapturePacket while all mutable methods, methods that take packets, simply require any type of packet. |
| PacketIterator<I extends CapturePacket> | All getter methods, that return a packet return subclass of CapturePacket while all mutable methods, methods that take packets, simply require any type of packet. |
| PacketModifier<T extends CapturePacket> |
ElementModifier interface allows a capture session that is mutable (see
Capture.isMutable()) to add, insert, swap and remove packets from the
underlying packet dataset of the capture session. |
| RemoteSession | A remote session which allows remote capture of live packets or capture file manipulation. |
| RemoteSession.RemoteSessionFactory | Remote session handler is an object that is called by the CaptureServer to handle an incomming RemoteSession connection. |
| Class Summary | |
|---|---|
| Captures | Static factory methods for creating and accessing capture sessions. |
| Messages | |
| PacketInputStream |
A PacketInputStream deserializes Packet objects
into DeserializedPacket objects previously written using a
PacketOutputStream. |
| PacketOutputStream |
A PacketOutputStream writes
CapturePacket |
| RemoteServer | Allows remote capture sessions to be started from non-local system. |
| TestCaptureSyntax | |
| Enum Summary | |
|---|---|
| CaptureDevice.TimestampResolution | Specified the maximum supported timestamp resolution of the capture device. |
| CapturedProperty | Enum constants that describe certain properties and thus information a particular type of record may contain. |
| CaptureType | Defines the type of capture this is. |
| FileMode | Defines a file mode for either open or new files created. |
| FormatType | Some standard supplied file types with some convenience methods. |
| LiveCapture.Option | Options which can be set on open captures. |
| Exception Summary | |
|---|---|
| CaptureException | |
| CaptureOpenException | Any errors while opening a capture session. |
| FileFormatException | Thrown when Invalid format has been encountered in the capture file. |
| RemoteAuthenticationException | Indicates the remote authentication failed. |
| RemoteProtocolException | If a protocol error occures between CaptureSender and CaptureReceiver this exception will contain specific information about the failure. |
A high performance network packet capture framework. This is a
high level packet capture framework that abstracts all of the complexity
of live network captures, reading captured packets from capture files
(trace files), streaming packet data accross java.io streams or enabling
remote capture sesssion accross the network. The org.jnetstream.capture
package allows easy processing and capture of network packets. Live
packets can be captured on a local network interface or remotely.
The main class you will be dealing with is the Capture
interface. You aquire a reference to a Capture instance
using factory methods in the Captures class for local
sessions and RemoteSession for, you guessed it, remote
sessions on a remote server. (To get a RemoteSession call
the static method RemoteServer.openSession(URI) .) The
factory methods return the most specific subclass of LiveCapture
and FileCapture, all of which are subclasses of the base Capture
interface. So remember that LiveCapture and FileCapture
both extend Capture. You will see in the examples below the
use of Capture and sometimes the more specific subclass
depending if the method involved in the example need something out of
the more specific subclass such as LiveCapture.interfaceSnapshot()
method (see section "CaptureInterface state changes"
below.)
CapturePacket is a high level object that holds a
buffer with packet data as captured from a live network interface, a
file or remote sender. It also contains some additional information
needed for proper processing of the buffer. Also contains some meta data
such as capture timestamp, a reference to the CaptureDevice which holds
detailed descriptors about the network interface that the packet was
captured on.
The Captures class provides the following static factory methods which instantiate capture sessions:
Captures.openFile(File) - opens up a capture file
for readingCaptures.openLive(NetworkInterface) - opens up a
network interface for live capture from the networkCaptures.openFile(InputStream) - opens up a
capture from a stream, but exects the stream to be in supported file
format such as when you use a FileInputStream on a capture file.CaptureInputStream - this is a class that
deserializes CapturePacket written to a CaptureOutputStream.
Both these classes are subclasses of InputStream and OutputStream.
Passing a CaptureInputStream to Captures.openFile(InputStream)
method will throw an CaptureFormatException since the
stream is not formatted to like capture files.Capture instanceCapture
interaface extends the
IOIterator
interface which uses the familiar
hasNext()
and
next()
method calls. So lets just open a live capture on all network interfaces
a system has and print the packet properties to stdout:
Capture capture = Captures.openLive(); // Opens up all network interfaces, except loopback ints
while (capture.hasNext()) {
CapturePacket packet = capture.next();
System.out.println("packet properties=" + packet.toString());
}
Note:
Note that you also need to catch some exceptions that are declared, but
not included in the example.
Another common usage is to capture packets and store them in a file.
So we first setup a source of packets which is a live network capture from a specific network interface.
List<CaptureDevice> ints = Captures.listInterfaces();
if (ints.isEmpty()) {
return; // No interfaces on this system
}
CaptureInterface netint = ints.get(0); // Just grab the first network interface
Capture capture = Captures.openLive(netint);
So now we have a source of packets which is the first network interface
returned all the interfaces. Next we want to create a new file and
append all the captured packets to that file. We will create the file in
PCAP format.
Captures.newFile(new File("mycapture.pcap", SuppliedFileTypes.PCAP, capture);
The above line creates a new file called "mycapture.pcap" in PCAP format
(2nd parameter). At this point all the needed initialization is done and
a new file created with the right file header. The the last argument is
the source of packets, our capture instance. The above method iterates
through all the way to the end using Capture#hasNext() and
Capture#next() method calls until hasNext eventually returns
false. Those packets are written in the PCAP format into the capture
file.
List<CaptureDevice> ints = Captures.listInterfaces();
if (ints.isEmpty()) {
return; // No interfaces on this system
}
CaptureDevice netint = ints.get(0); // Just grab the first network interface
Filter filter = new BpfFilter(new PcapExpression("ip"));
Capture capture = Captures.openLive(filter, netint);
The filter is a BPF filter (Berkley Packet Filter) that efficiently filters incomming packets being captured and only allows packets that match the filter criteria to be returned. The filter is actually applied to operating system kernel (if OS supports this option) and the packet capture driver efficiently filters packets without any extraneous copies of packet contents. Lastly the filter is simply supplied as the first parameter to the openLive method.
An alternate way to set a filter is to set the filter on the CaptureDevice. Such as in this case:
List<CaptureDevice> ints = Captures.listInterfaces();
if (ints.isEmpty()) {
return; // No interfaces on this system
}
CaptureDevice netint = ints.get(0); // Just grab the first network interface
Filter filter = new BpfFilter(new PcapExpression("ip"));
netint.setFilter(filter);
Capture capture = Captures.openLive(netint);
This sets the filter only on this interface and not any other interfaces if they were specified. You can also change the filter after the capture has begun and even some packets returned. The only way to change a filter after the CaptureInterface has been applied, is to set it on the capture itself and not on the device instance directly.
List<CaptureDevice> ints = Captures.listInterfaces();
if (ints.isEmpty()) {
return; // No interfaces on this system
}
CaptureDevice netint = ints.get(0); // Just grab the first network interface
Filter ipFilter = new BpfFilter(new PcapExpression("ip"));
netint.setFilter(ipFilter);
Capture capture = Captures.openLive(netint);
// Get 10 IP packets
for (int i = 0; i < 10 && capture.hasNext(); i ++) {
System.out.println("CaptureInstance=" + capture.next().getCaptureInterface());
}
// After netint has been applied it becomes immutable and we can no longer call
// on its CaptureDevice.setFilter method. Use CaptureInterface.isMutable() to check for this.
Filter ipxFilter = new BpfFilter(new JpcapExpression("ipx"));
CaptureInterface ni = capture.setFilter(filter, netint); // Sets a new filter and creates a new instance of CaptureInterface
// Get 10 IPX packets
for (int i = 0; i < 10 && capture.hasNext(); i ++) {
System.out.println("CaptureInstance=" + capture.next().getCaptureInterface());
}
capture.close();
The call to
capture.setFilter(filter, netint)
does not modify the netint instance, this instance is immutable after
the call to Captures.openLive(netint). What happens is a new instance of
CaptureInterface is created based on the old one, and the new filter set
in the new instance. The call returns the new CaptureInstance reference,
notice its no longer returned as CaptureDevice as its immediately
applied to captures and becomes immutable. The reason for this strict
mutable/immutable control is that any perviously captured packets will
still reference the same CaptureInterface reference at the time they
were captured. New packets returned after the new filter was applied to
filter on IPX packets, will return references to new CaptureInterface
that has the new filter applied.
To append packets to an existing file you need to first get your
source of packets setup, open up the capture file and use the methods in
MutableFileCapture or simply call a utility method in Captures.append()
to do the copying for you. This time lets use a NAP file and do the
copying ourselves:
Capture capture = Captures.openLive(); // Opens up all network interfaces, except loopback
MutableCaptureFile file = CaptureFiles.openFile(new File("mycapture.nap");
file.last(); // advance the iterator to past the last packet in the file
while (capture.hasNext()) {
CapturePacket packet = capture.next();
file.add(packet);
}
First we create a new capture which opens up all the network interfaces
for live capture. Second we open a NAP file. Third we advance the file
position to the end of the file by calling MutableFileCapture#last which returns the last packet and advances the
position past it. We want to append our packets to the end not insert
them at the beginning although for an empty file beginning is the last
element. Lastly we go into a loop that reads one packet at a time and
using the mutable file adds or appends the packets to the file.
We could have done steps 3 and 4 with a utility method. Here is the same example using the utility method:
Capture capture = Captures.openLive(); // Opens up all network interfaces
CaptureFile file = CaptureFiles.openFile(new File("mycapture.nap"));
CaptureFiles.append(file, capture);
Or this compact notation:
CaptureFiles.catFile(new File("mycapture.nap"), Captures.openLive());
It is certainly possible for a network interface on which a capture is taking place to change state such as different set of LiveCapture.Option has been set by a separate instance of LiveCapture, someone had alterned the IP addresses assigned to the interface at runtime, a new filter applied, etc etc... All the while our capture session is still in progress. There is a simple way to record these changes by taking a snapshot of the interface config. After a snapshot is taken any packets returned will reference new CaptureInterface objects that reflect the exact config of the interface, while any previously returned packets will continue to reference the old CaptureInterface objects which contain the state before the snapshot took place. To take a snapshot simply call the LiveCapture.interfaceSnapshot method.
LiveCapture capture = Captures.openLive();
for (int i = 0; i < 10000 && capture.hasNext(); i ++) {
LivePacket packet = capture.next();
// Do something with the packet
}
// Some even caused one of the interface to change state, i.e. new IP address alias added to the interface
capture.interfaceSnapshot();
// Now capture 10000 more packets which reference new CaptureInterface objects
for (int i = 0; i < 10000 && capture.hasNext(); i ++) {
LivePacket packet = capture.next();
// Do something with the packet
}
capture.close();
The call to interfaceSnapshot() creates new CaptureInterface instances
on all currently open interfaces. The new CaptureInterfaces are
initialized to the latest state of the network interface, reflecting the
fact that new IP address alias has been added to one network interfaces.
You could more specifically target the exact interface instead of taking
a snapshot of all interface by using the variation of the method
interfaceSnapshot(CaptureInterface).
Note that using the Capture.setFilter method to set a new filter automatically takes a snapshot to reflect the changed filter. It is not required to take a second snapshot by explicitely calling another interfaceSnapshot.
List<CaptureDevice> ints;
LiveCapture live;
FileCapture file;
Captures.LocalFactory lf = Captures.getLocal();
ints = lf.listInterfaces(); // List of all interfaces on local system
live = lf.openLive(); // Open all interfaces on local system for capture
file = lf.openFile(new File("myfile.pcap")); // Open file on local system for reading and writting
live.close();
file.close();
Captures.RemoteFactory rf = RemoteServer.openSession(new URI("192.168.1.1"));
ints = rf.listInterfaces(); // List of all interfaces on remote system
live = rf.openLive(); // Open all interfaces on remote system for capture
file = rf.openFile(new File("/tmp/myfile.pcap")); // Open file on remote system for reading and writting
live.close();
file.close();
Or even more abstracted since all these above methods are actually in
the super interface Captures.Factory:
List<CaptureDevice> ints;
LiveCapture live;
FileCapture file;
Captures.Factory factory;
factory = Captures.getLocal();
ints = factory.listCaptureDevices(); // List of all interfaces on local system
live = factory.openLive(); // Open all interfaces on local system for capture
file = factory.openFile(new File("myfile.pcap")); // Open file on local system for reading and writting
live.close();
file.close();
RemoteSession session = RemoteServer.openSession(new URI("192.168.1.1"));
factory = session; // RemoteSession extends RemoteFactory
ints = factory.listCaptureDevices(); // List of all interfaces on remote system
live = factory.openLive(); // Open all interfaces on remote system for capture
file = factory.openFile(new File("/tmp/myfile.pcap")); // Open file on remote system for reading and writting
live.close();
file.close();
session.close(); // We needed to keep RemoteSession reference only so that we could explicitly close it
// because RemoteFactory itself is not Closeable.
You get the point. There is not much difference in invoking factory
methods for local or remote sessions. There are some restrictions and
they are reflected in methods included in the LocalFactory and
RemoteFactory interfaces. Certain methods that can only be invoked
locally are not included in the RemoteFactory and vise versa. The above
example assumes that you have an instance of RemoteServer running on the
remote system at IP address 192.168.1.1. It should be clear at this
point that the static Captures methods are only defined for all
LocalFactory abstract methods. RemoteSession itself adds a few
additional methods related to a remote session besides the methods
extended from RemoteFactory.
Another thing you may wish to do, is to actually control all the parameters of a capture file. So lets do an example of where we create a Pcap file with a custom block record (file header).
There are actually several ways to create an empty file in jNetStream capture framework.
Captures static methods
PcapFile pcap = Captures.newFile(FileType.Pcap, new File("capture.pcap"));
Factory for local sessions and use its
factory methods
LocalFactory lf = Captures.getLocal();
PcapFile pcap = lf.newFile(FileType.Pcap, new File("capture.pcap"));
PcapFile pcap = FileType.Pcap.newFile(File("capture.pcap"));
Once we have a Pcap capture file we can proceed to tweak its contents.
By default the capture file is created with a PcapBlockRecord already in
place with default values, there is no way to create a file without
these defaults. But its easy to clear out the file to its absolute
minimum.
PcapFile pcap = FileType.Pcap.newFile("capture.pcap"):
PcapModifier modifier = pcap.getPcapIterator();
modifier.removeAllRecords();
At this point the file size is 0 bytes, all records have been completely
removed. We can then proceed to add our custom block record with our new
values:
PcapFile pcap = FileType.Pcap.newFile("capture.pcap"):
PcapModifier modifier = pcap.getPcapIterator();
modifier.removeAllRecords();
PcapBlockRecord block = modifier.addBlockRecord(
new byte[] {0x11, 0x22, 0x33, 0x44}, // Magic Number
2, 4, // Major and minor verions
PcapDLT.EN10, // DLT
0, // Accuracy
0, // Timezone
1024 // Snaplen
);
We're convieniently returned a reference to the newly created block
record. We can then proceed to add packet records which is the only
other type of record a Pcap file can have using
PcapModifier.addPacketRecord()
method. Notice that we added a bogus magic number, which the API allowed
us to do. This creates an invalid pcap file that no program could read
in, but if that's what the user wanted to do, its allowed.
The other file formats, especially NAP file formats are a lot more involved with many different types of records possible and their Modifiers are much more comprehensive.
NapFile nap = FileType.Nap.newFile("capture.nap");
NapModifier modifier = nap.getNapIterator();
/*
* Now, using the NapModifier, we can add block, property, capture device, packet records.
* NapModifier allows addition of over a dozen different types of NAP records.
*/
You get the point.
Factory methods may be much more efficient at copying packets
around as more efficient algorithms may be used to accomplish the task
then simply iterating one packet at a time. For example if you were
appending PCAP file as sources to a destination file that was also a
PCAP file, then raw bulk buffers of source file data may be copied at
once, without the need to even do the minimal decoding that is done when
packets are iterated over. Or if the destination file was a PCAP file
and the source LiveCapture was also based on libpcap implementation (as
it is, part of the first release) the much more efficient offline
capture would be initiated which uses kernel drivers to dump packets
directly into the destination capture file; a much more efficient short
cut indeed. So the capture framework will pick the most efficient
algorithm available for the circumstances at hand.
The capture framework uses java.nio package extensively to
allocate large direct buffers, map file contents into memory using
native OS shortcuts and then, decodes only the smallest amount of data
needed to accomplish the task. The objects returned such as
CapturePacket and its subclassed variations reference into these buffers
and don't typically allocate a lot of memory themselves. This means
these objects can be created efficiently. Multiple packets are typically
prefetched into a cache in larger chunks and share a single large
buffer. Some buffers map file contents directly into buffer's memory
ensuring that the file contents are only read into memory once; by the
kernel.
Also use the methods that take entire collections instead of accomplishing the same task by using methods that take a single element and you doing the iteration. This is especially true when modifying files or inserting/deleting packets from files. The implementation algorithms are tuned for executing operations as efficiently as possible given all the information supplied. So inserting or deleting a bunch of packets at once in a file is much more efficient then inserting or deleting a single packet at a time. The same is true for all methods that accept collections or arrays.
Future enhancements may provide additional efficiencies as technologies develop and this project matures. The API is designed to hide the implementation details well and allows a lot of leaverage for the implementors to do their magic behind the scenes.
|
|||||||||
| PREV PACKAGE NEXT PACKAGE | FRAMES NO FRAMES | ||||||||