- Tutorials
- API Examples
- User Guide
- Ch 1 - The Basics
- Ch 2 - libpcap
- 2.1 - The Main libpcap API Overview
- 2.2 - Getting a List of Interfaces
- 2.3 - Opening a Network Interface for Capture
- 2.4 - Opening offline capture
- 2.5 - Setting a packet filter
- 2.6 - Reading one packet at a time
- 2.7 - Reading multiple packets with dispatch loops
- 2.8 - Dumping captured packet to an offline file
- 2.9 - Transmitting packets
- 2.10 - Close Pcap and PcapDumper handles
- Ch 3 - Packet Decoding
- Ch 4 - Internals
- Ch 5 - Protocols
- Ch 6 - Native API
There are 4 ways that you can write a protocol to protocol binding:
- declare a static method in a header definition annotated with @Bind annotation
- declare a static method in any class file annotated with @Bind annotation, but must also provide the "from=" parameter
- declare an instance method (non-static) in an anonymous class that extends Object annotated with @Bind annotation
- Create a JBinding object directly by extending AbstractBinding class and implementing its
isBound(JPacket, JHeader>:booleanabstract method.
The first 3 use other classes as a surrogate hosts for our binding methods. The methods are invoke directly not by accessing the class they were declared in. This allows a single class to host any number of bindings. They are simply static methods that have @Bind annotation.
After you have written your bindings, they need to be registered with JRegistry which manages protocol to protocol bindings. Bindings that are contained within a protocol header (see case #1 below) will automatically be registered when that protocol is registered with JRegistry, so no need to register it again. For other cases the class or the anonymous object needs to be passed to JRegistry which will find the binding methods and register them with appropriate protocol headers.
Lets take a look at each one these declarations.
#1 - declare a static method in a header definition
@Header
public class MyHeader extends JHeader {
@Field(offset = 0, length = 8)
public int fieldA() {
return super.getUByte(0);
}
@Bind(to = Ip4.class)
public static boolean bindToIp4(JPacket packet, Ip4 ip) {
return ip.type() == 0x100; // Our dummy protocol number
}
@Bind(to = Ethernet.class)
public static boolean bindToEthernet(JPacket packet, Ethernet eth) {
return eth.type() == 0x200; // Our dummy protocol number
}
}
We declare a simple header with 1 field in it named "fieldA". Then we declare 2 bindings that bind our protocol to Ip4 and then Ethernet.
#2 - declare a static method in any classfile
public class MyBindings {
@Bind(from = MyHeader.class, to = Ip4.class)
public static boolean bindMyClassToIp4(JPacket packet, Ip4 ip) {
return ip.type() == 0x100; // Our dummy protocol number
}
@Bind(from = MyHeader.class, to = Ethernet.class)
public static boolean bindMyClassToEthernet(JPacket packet, Ethernet eth) {
return eth.type() == 0x200; // Our dummy protocol number
}
}
Here we drop any header definition stuff and simply create a class file with 2 static methods. The annotation and method signature allow them to be used as binding methods. Notice we had to include an extra parameter to the annotation, the "from" parameter. In case #1, the runtime is able to determine which header the binding is written in and thus the from or source of the binding. In #2 you have to specify which headers you are binding. The from parameter can be different for every method.
#3 - declare an instance method in annonymous class
Object myBindings = new Object() {
@SuppressWarnings("unused")
@Bind(from = MyHeader.class, to = Ip4.class)
public boolean bindMyClassToIp4(JPacket packet, Ip4 ip) {
return ip.type() == 0x100; // Our dummy protocol number
}
@SuppressWarnings("unused")
@Bind(from = MyHeader.class, to = Ethernet.class)
public boolean bindMyClassToEthernet(JPacket packet, Ethernet eth) {
return eth.type() == 0x200; // Our dummy protocol number
}
}
The only difference between #3 and #2 is that we dropped the static modifier from method's signatures. They are instance methods, vs. static methods in previous case. We are now instantiating an object that has these two seemingly unreachable methods. That is why we are also adding the @SupressWarnings annotation so we don't get those java compiler warnings. Those methods are reachable from library's runtime and will be executed in object context at the appropriate time.
You should also note that only Object can be used for annonymous class extension purpose. Although the java compiler will allow you to extend any java class and add your own anonymous methods, the library's runtime checks the that the direct super class is the Object.class and an error is generated. The reason for this restriction is that since these are instance methods (part of a running object) I want to ensure that no one tries to access internal state. For example if someone were to extends JHeader and add annonymous methods to it (just like in the example above), then it would be very temping to try and read something out of the underlying header, if the user can even determine what header it is. This is just too ungly and only asks for trouble. So its not allowed by the runtime.
#4 - Create a JBinding object directly
- JBinding bindIp4ToEthernet = new AbstractBinding<Ethernet>(Ip4.class, Ethernet.class) {
- @Override
- public boolean isBound(JPacket packet, Ethernet header) {
- return header.type() == 0x800;
- }
- };
This is the more traditional way of creating a binding object. The method overrides an abstract method defined in AbstractBinding. This method is called at the appropriate time to find out if source protocol is bound to target protocol. Alternative would be actually implement the JBinding interface and all its declared methods, but its easier to use the adaptor class AbstractBinding since it does all the mundate and repetitive tasks for you.
What future may hold?
I'm actually considering one more binding type, one that utilizes the power of libpcap filtering expressions. This type of binding would allow the user to supply a filter expression as a string, have it compile to BPF byte code and executed within the right packet context to make a binding decision. That is currently on the drawing board, but can't say when this will become reality.
How to register bindings, now that you have them
Now that you have your bindings, you need to register them with JRegistry. The bindings defined in JHeader classes or subclasses are automatically added to the registry when the protocol header is registered as well. The remaining you have to register manually yourself. There are 3 static methods in registry all with the same name, but have different overrides addBindings. You supply either the Object instance, class with bindings in it or an array of JBinding[] objects. Each binding knows which is the source class and the target and the registry associates them with the right header. That is, you supply from case #2, #3 or #4 directly to JRegistry.addBinding. Case #1 is automatic.
The registry process will check the signature of your binding methods and will throw exceptions if something is not right. The 2 most common mistakes being made are forgetting to make the method "static" or not using the matching formal parameter in the binding function. The Bind(to=protocol_class) parameter used must be the same "protocol_class" used in the method signature. This is a condition that java compiler will not detect. It will only be detected at runtime when the method is being registered with JRegistry. It is at that time that the binding annotations are inspected as well as the methods signature for any errors.
»
Printer-friendly- Login or register to post comments
Send via Email
PDF Convert