Wednesday, February 1, 2012

Create a low layer network protocol with java

Creating a protocol running at layer 3, may be a hard task. But there are times where it needs to be done quick and easy, i.e for testing protocol before implementing it directly to the operating system's kernel. Jpcap is a  open-source, multi platform Java libray that allows protocol designers to implement their protocol amazingly fast.

Before start using Jpcap, WinPcap and Java SDK must be installed. Jpcap wan be downloaded from here. The protocol that is going to be implemented is very simple. It runs over Ethernet and it uses as addresses real names. The protocol is named MyProtocol. The following topology is going to be used:

      PC1:                                   PC2:
mac address:00:03:0d:2e:79:68 -|switch|-mac address:00:90:96:1d:62:f8
MyProtocol address:Alice                MyProtocol address:Bob

A MyProtocol packet consists of a header and a body. The header has an 1 byte field for the local address length, an 1 byte field for the remote address legth, the local address and the remote address. The body just contains some data. In this example PC1 (Alice) will send "HELLO" to PC2 (Bob). The code for the client is quite straightforward. Initially a the network interface to be used is located, then the ethernet packet that will carry the MyProtocol packet is constructed and finally the MyProtocol packed is constructed. The code for the client follows


import java.util.Arrays;
import jpcap.*;
import jpcap.packet.*;

public class MyProtocol {
    public static void main (String args[]){
        //The local mac address
        final byte[] localMacAddress ={0x00,0x03,0x0d,0x2e,0x79,0x68};
        //The remote mac address
        final byte[] remoteMacAddress={0x00,(byte)0x90,(byte)0x96,0x1d,0x62,(byte)0xf8};
        //The index in the network interfaces list that holds the local device
        int localDeviceIndex =-1;
        //Obtain the list of network interfaces
        NetworkInterface[] devices = JpcapCaptor.getDeviceList();
        //Find the local device to use
        for (int i = 0; i < devices.length; i++){
            if (Arrays.equals(devices[i].mac_address,localMacAddress))
               localDeviceIndex = i; 
        }
        //Check wheather an interface with mac address same as the local mac 
        //address has been found
        if (localDeviceIndex ==-1){ //it has not been found, break execution
            System.out.println("A network interface with address equal " +
                                "to localMacAddress has not been found");
            System.exit(-1);
        }
        //Create the data link packet
        EthernetPacket ether=new EthernetPacket();
        //Set the source address
        ether.src_mac = localMacAddress;
        //Set the destination address
        ether.dst_mac = remoteMacAddress;
        //Set the type code to a not common used value
        ether.frametype = 0x0890;
        //Create our protocol packet
        /* Create a header. The header is of the form
         * 
         * local address legth|remote address legth|local address|remote address
         *     1 byte         |    1 byte          |   n bytes    |      n bytes
         * 
         * Suppose that the local address is Alice and the remote is Bob
         */
        final byte[] myProtLcAddr = "Alice".getBytes();
        final byte[] myProtRmAddr = "Bob".getBytes();
        byte[] header = new byte[myProtLcAddr.length + myProtRmAddr.length + 2];
        header[0] = (byte)myProtLcAddr.length;
        header[1] = (byte)myProtRmAddr.length;
        System.arraycopy(myProtLcAddr, 0, header, 2, myProtLcAddr.length);
        System.arraycopy(myProtRmAddr, 0, header, 2 + myProtLcAddr.length, myProtRmAddr.length);
        //Create the body. The body is just a hello message
        byte[] body ="HELLO".getBytes();
        //Build the packet
        Packet packet = new Packet();
        //Add the header and the body tou the packet
        byte[] data = new byte[header.length + body.length];
        System.arraycopy(header, 0, data, 0, header.length);
        System.arraycopy(body, 0, data, header.length, body.length);
        packet.data = data;
        //Add the data link layer
        packet.datalink = ether;
        //Send the packet
        try{
            JpcapCaptor captor=JpcapCaptor.openDevice(devices[localDeviceIndex],1514,false,50);
            JpcapSender sender = captor.getJpcapSenderInstance();
            sender.sendPacket(packet);
            System.out.println("Packet send");
        }catch(Exception e){
            System.out.println("Exception occured " + e.toString());
        }
        
    }
}

The server part is even simpler, the network interface is located and it is set up to listen for specific ethernet type packets. The code follows


import java.util.Arrays;
import jpcap.*;
import jpcap.packet.*;

public class MyProtocolServer {
    public static void main (String args[]){
        //The local mac address
        final byte[] localMacAddress ={0x00,(byte)0x90,(byte)0x96,0x1d,0x62,(byte)0xf8};
        //The index in the network interfaces list that holds the local device
        int localDeviceIndex =-1;
        //Obtain the list of network interfaces
        NetworkInterface[] devices = JpcapCaptor.getDeviceList();
        //Find the local device to use
        for (int i = 0; i < devices.length; i++){
            if (Arrays.equals(devices[i].mac_address,localMacAddress))
               localDeviceIndex = i; 
        }
        //Check wheather an interface with mac address same as the local mac 
        //address has been found
        if (localDeviceIndex ==-1){ //it has not been found, break execution
            System.out.println("A network interface with address equal " +
                                "to localMacAddress has not been found");
            System.exit(-1);
        }
        
        //Open the device and loop until a packet of type 0x0890 arrives
        try{
            JpcapCaptor captor=JpcapCaptor.openDevice(devices[localDeviceIndex],1514,false,50);
            captor.setFilter("ether proto 0x0890", true);
            boolean continueCapture = true;
            while (continueCapture){
                Packet packet = captor.getPacket();
                if (packet !=null ){ // a packet has been captured
                    //retreive the sender address. The firt byte stores the length
                    //and the address starts from the 3rd byte
                    String remoteAddress = new String(packet.data,2,packet.data[0]);
                    System.out.println("Hello received from " + remoteAddress);                
                    continueCapture = false;
                }
            }
            
            
        }catch(Exception e){
            System.out.println("Exception occured " + e.toString());
        }
        
    }

}