Downloads

ICMP Ping in Java
Wednesday, 17 June 2009 18:58

Open a shell and ping a host is easy - doing the same in Java is hard.

Before Java 5 (1.5) there was no native way to send an ICMP ping to test whether a machine is up and running. Since Java 5 there is a method in the InetAddress class which should make sending pings a trivial task.

 
InetAddress.getByName("192.168.1.1").isReachable(4000);
 

However the behaviour of this call seems to be platform dependent (based on my wireshark observations). Calling this function in a Linux/MacOS environment, the JVM tries to establish a TCP connection on port 7. The function returns true if the TCP handshake is successful. Executing the Java program with root rights, a correct ICMP request is sent and the function returns true if an ICMP reply is received. In a Windows XP environment, a TCP handshake is always used to test whether a machine is up, no matter if the program has admin rights or not.

alt

Since this is not satisfying, an approach is presented here which is based on the jpcap library. Using jpcap, it is possible to assemble and send ICMP Packets. The example presented below is meant to be a proof-of-concept and is far from being used in productive environments. So use it at your own risk.

Two techniques are applied to test whether a host is reachable whereas the first technique is a prerequisite for the second one. First, an ARP request packet is assembled and sent to obtain the MAC address of the host under test. If no ARP reply is obtained within a predefined period of time, then the host seems to be unreachable and the ping() function returns false. Receiving an ARP reply is a necessary, but not sufficient precondition. If a valid ARP reply is received then an ICMP packet is assembled using the MAC address obtained by applying the ARP protocol. The function returns true if a valid ICMP reply is received within a predefined period of time.

Here's both the sourcecode and the binary (Note that you need to have the latest winpcap/libpcap library installed. For MacOS you also need to compile the Jpcap JNI library).

Download: binary and source

Here's the sourcecode:

 
import java.io.IOException;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.EventListener;
import java.util.EventObject;
import javax.swing.event.EventListenerList;
import jpcap.JpcapCaptor;
import jpcap.JpcapSender;
import jpcap.NetworkInterface;
import jpcap.NetworkInterfaceAddress;
import jpcap.packet.ARPPacket;
import jpcap.packet.EthernetPacket;
import jpcap.packet.ICMPPacket;
import jpcap.packet.IPPacket;
import jpcap.packet.Packet;
/**
 * @author Michael Netter
 */
 
public class Ping {
  public static int ARPTIMEOUT = 4000;
  public static int ICMPTIMEOUT = 4000;  
 
  public static void main(String[] argv){
    System.out.println(new Ping().ping(argv[0]));
  }
 
  /**
   * Send an ICMP packet (type 8) (ping) to the provided address
   * @param dst_ip Destination IP / hostname
   * @return true/false
   */
  public boolean ping(String dst_ip){
    return new PacketSender().ping(dst_ip);
  }
 
  /**
   * Send an ICMP packet (type 8) (ping) to the provided address
   * @param dst_ip Destination IP / hostname
   * @param timeout Timeout for receiving a response in milliseconds
   * @return
   */
  public boolean ping(String dst_ip, int timeout){
    ARPTIMEOUT = timeout;
    ICMPTIMEOUT = timeout;
    return new PacketSender().ping(dst_ip);
  }
 
  /**
   * Class for assembling and sending ARP and ICMP packets
   */
  private class PacketSender implements IARPPacketListener, IICMPPacketListener{
    private boolean arpReplyArrived = false;
    private boolean icmpReplyArrived = false;
    private InetAddress src_ip = null;
    private ARPPacket arpReplyPacket = null;
    private String dest_ip = null;
 
 
    public boolean ping(String dest_ip){
      this.dest_ip = dest_ip;
      NetworkInterface device = determineDevice();
      src_ip = determineSRCIP(device);
      ARPPacket arpPacket = assembleARPPacket(dest_ip,src_ip,device);
 
      JpcapCaptor captor = openDevice(device);
      JpcapSender sender = getSender(captor);
 
      // create packet receiver and register observer
      PacketReceiver receiver = new PacketReceiver(captor);
      receiver.addARPListener(this);
      receiver.addICMPListener(this);
      Thread packetReceiverThread = new Thread(receiver);
      packetReceiverThread.start();
      sender.sendPacket(arpPacket);
 
      // wait some milliseconds for a arp response
      try {
        Thread.sleep(ARPTIMEOUT);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
      if(arpReplyArrived && arpReplyPacket != null){
        // MAC detection successful continue with ICMP Packet construction
        ICMPPacket icmpPacket = assembleICMPPacket(device, arpReplyPacket.sender_hardaddr);
 
        // send ping
        sender.sendPacket(icmpPacket);
 
        // wait some milliseconds for a icmp response
        try {
          Thread.sleep(ICMPTIMEOUT);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
 
        packetReceiverThread.stop();  
        return (icmpReplyArrived ? true : false);
 
      }
      else{
        packetReceiverThread.stop();
        return false;
      }
    }
 
    /**
     * Function required by the  IARPPacketListener interface
     */
    public void arpPacketArrived(ARPPacketArrivedEvent event){
      ARPPacket arp = event.getARPPacket();
 
      if(Arrays.equals(arp.target_protoaddr,src_ip.getAddress()) && arp.operation == ARPPacket.ARP_REPLY){
        arpReplyPacket = arp;
        arpReplyArrived = true;
      }      
    }
 
    /**
     * Function required by the  IICMPPacketListener interface
     */
    public void icmpPacketArrived(ICMPPacketArrivedEvent event){      
      ICMPPacket icmpPacket = event.getICMPPacket();
      InetAddress dst_ip =null;
      try {
        dst_ip = InetAddress.getByName(dest_ip);
      } catch (UnknownHostException e) {
        e.printStackTrace();
      }
      if(icmpPacket.src_ip.equals(dst_ip) && icmpPacket.type == ICMPPacket.ICMP_ECHOREPLY){
        icmpReplyArrived = true;
      }
    }
 
    /**
     * Construct a valid PING packet (ICMP type 8)
     * @param device
     * @param dst_mac
     * @return
     */
    public ICMPPacket assembleICMPPacket(NetworkInterface device, byte[] dst_mac){
      ICMPPacket p=new ICMPPacket();
      p.type=ICMPPacket.ICMP_ECHO;
      p.seq=1000;
      p.id=999;
      p.orig_timestamp=123;
      p.trans_timestamp=456;
      p.recv_timestamp=789;
      try {
        p.setIPv4Parameter(0,false,false,false,0,false,false,false,0,1010101,100,IPPacket.IPPROTO_ICMP,
            src_ip,InetAddress.getByName(dest_ip));
      } catch (UnknownHostException e) {
        e.printStackTrace();
      }
      p.data="data".getBytes();
      EthernetPacket ether=new EthernetPacket();
      ether.frametype=EthernetPacket.ETHERTYPE_IP;
      ether.src_mac=device.mac_address;      
      ether.dst_mac=dst_mac;
      p.datalink=ether;      
      return p;
    }
 
    /**
     * Select the probable correct interface
     * @return NetworkInterface
     */
    public NetworkInterface determineDevice(){
      NetworkInterface dev = null;
      for(NetworkInterface device : JpcapCaptor.getDeviceList()){      
        for(NetworkInterfaceAddress address : device.addresses){
          if(address.address instanceof Inet4Address && !address.address.toString().equalsIgnoreCase("/0.0.0.0") ){
            dev = device;    
            break;
          }
        }        
      }
      return dev;
    }
 
    /**
     * get src_ip
     * @param device
     * @return
     */
    public InetAddress determineSRCIP(NetworkInterface device){
      // determine src_ip
      InetAddress srcip=null;
      for(NetworkInterfaceAddress addr:device.addresses){
        if(addr.address instanceof Inet4Address){
          srcip=addr.address;
          break;
        }
      }
      return srcip;
    }
 
    /**
     * Construct a valid ARP Request
     * @param dst_ip - destination ip
     * @param device - NetworkInterface
     * @return ARPPacket
     */
    public ARPPacket assembleARPPacket(String dst_ip, InetAddress src_ip, NetworkInterface device ){
 
      byte[] broadcast=new byte[]{(byte)255,(byte)255,(byte)255,(byte)255,(byte)255,(byte)255};
      ARPPacket arp=new ARPPacket();
      arp.hardtype=ARPPacket.HARDTYPE_ETHER;
      arp.prototype=ARPPacket.PROTOTYPE_IP;
      arp.operation=ARPPacket.ARP_REQUEST;
      arp.hlen=6;
      arp.plen=4;
      arp.sender_hardaddr=device.mac_address;
      arp.sender_protoaddr=src_ip.getAddress();
      arp.target_hardaddr=broadcast;
      try {
        arp.target_protoaddr=Inet4Address.getByName(dst_ip).getAddress();
      } catch (UnknownHostException e) {
        e.printStackTrace();
      }    
      EthernetPacket ether=new EthernetPacket();
      ether.frametype=EthernetPacket.ETHERTYPE_ARP;
      ether.src_mac=device.mac_address;
      ether.dst_mac=broadcast;
      arp.datalink=ether;    
      return arp;
    }
 
    /**
     * Open JpcapCaptor
     * @param device
     * @return JpcapCaptor
     */
    public JpcapCaptor openDevice(NetworkInterface device){
      JpcapCaptor captor = null;
      try {
        captor = JpcapCaptor.openDevice(device,2000,false,3000);
      } catch (IOException e) {
        e.printStackTrace();
      }
      return captor;
    }
 
    /**
     * Get JpcapSender
     * @param captor
     * @return JpcapSender
     */
    public JpcapSender getSender(JpcapCaptor captor){
      return captor.getJpcapSenderInstance();
    }
  }
 
 
  /**
   * Packet receiver implemented as a seperate thread
   * Register observers to get notified of received packets
   */
  private class PacketReceiver implements Runnable{
    private JpcapCaptor captor;
    private EventListenerList listeners = new EventListenerList();  
 
    public PacketReceiver(JpcapCaptor captor){
      this.captor = captor;
    }  
    public void addARPListener( IARPPacketListener listener ) {
      listeners.add( IARPPacketListener.class, listener );
    }
 
    public void removeARPListener( IARPPacketListener listener ){
      listeners.remove( IARPPacketListener.class, listener );
    }
    protected synchronized void notifyARPListeners( ARPPacketArrivedEvent event )  {
      for ( IARPPacketListener l : listeners.getListeners( IARPPacketListener.class )){
        l.arpPacketArrived(event);
      }
    } 
 
    public void addICMPListener( IICMPPacketListener listener ) {
      listeners.add( IICMPPacketListener.class, listener );
    }
 
    public void removeICMPListener( IICMPPacketListener listener ){
      listeners.remove( IICMPPacketListener.class, listener );
    }
    protected synchronized void notifyICMPListeners( ICMPPacketArrivedEvent event )  {
      for ( IICMPPacketListener l : listeners.getListeners( IICMPPacketListener.class )){
        l.icmpPacketArrived(event);
      }
    } 
 
    public void run(){
      while(true){
        Packet packet = captor.getPacket();
        if(packet instanceof ARPPacket){
          ARPPacket arpPacket = (ARPPacket)packet;
          notifyARPListeners(new ARPPacketArrivedEvent(this,arpPacket));
        }
        if(packet instanceof ICMPPacket){
          ICMPPacket icmpPacket = (ICMPPacket)packet;
          notifyICMPListeners(new ICMPPacketArrivedEvent(this,icmpPacket));
        }
      }
    }
  }
 
  private interface IARPPacketListener extends EventListener{
    void arpPacketArrived(ARPPacketArrivedEvent event);
  }
 
  private interface IICMPPacketListener extends EventListener{
    void icmpPacketArrived(ICMPPacketArrivedEvent event);
  }
 
  private class ARPPacketArrivedEvent extends EventObject{
    private ARPPacket arpPacket;
    public ARPPacketArrivedEvent(Object source, ARPPacket arpPacket){
      super(source);
      this.arpPacket = arpPacket;
    }
    public ARPPacket getARPPacket(){
      return arpPacket;
    }
  }
 
  private class ICMPPacketArrivedEvent extends EventObject{
    private ICMPPacket icmpPacket;
    public ICMPPacketArrivedEvent(Object source, ICMPPacket icmpPacket){
      super(source);
      this.icmpPacket = icmpPacket;
    }
    public ICMPPacket getICMPPacket(){
      return icmpPacket;
    }
  }
}
 
 
Comments (5)
5 Monday, 12 December 2011 20:26
anju
hello, i needed some help in all these... I need to do a ping system using icmp, udp, jpcap.... can u please provide me some advice and help? im a beginner... and im confused... plz help...

thx in ad... anju
4 Monday, 07 September 2009 21:27
code full fast!
i proved both source codes, but the result is the same!
we need to know how it could be more fast!
3 Friday, 04 September 2009 23:15
hello
can you send your code, it need to fix it!
2 Thursday, 13 August 2009 12:32
Herman Gomez
Forst of all thanks for your example source code.
It has been very interesting.

I modified it looking for performance improvement so I added some tracing to see where the time was being consumed.

The answer is in the two cycles (ARP and ICMP). I'm using virtual machines, and making ping from local to virtual. The surprise is that the results are 6 seconds in average, what is a lot!.

Here is your coded with my modifications. Can you tell me something?

Other comment: your routine to determinate a device to use as source simple takes the first device available. Do you have any optimization to get a better device if it is the case?

Regards,
Herman Gomez
herman_internet@yahoo.es
Spain.

//SOURCE CODE STARTS HERE

import java.io.IOException;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.EventListener;
import java.util.EventObject;
import javax.swing.event.EventListenerList;
import jpcap.JpcapCaptor;
import jpcap.JpcapSender;
import jpcap.NetworkInterface;
import jpcap.NetworkInterfaceAddress;
import jpcap.packet.ARPPacket;
import jpcap.packet.EthernetPacket;
import jpcap.packet.ICMPPacket;
import jpcap.packet.IPPacket;
import jpcap.packet.Packet;
/**
* @author Michael Netter
* Source: http://www.0x13.de/index.php/code-snippets/74-icmp-ping-in-java.html
* Date: 13-AUG-2009.
*/

public class Ping {
public static int ARPTIMEOUT = 4000; //ARP cycle limit
public static int ICMPTIMEOUT = 4000; //ICMP cycle limit
public static int ARPTIMEOUT2 = 400; //for ARP cycle step
public static int ICMPTIMEOUT2 = 400; //for ICMP cycle step

//HGC 13-AUG-2009
private static int TRACE = 1; //use 0 to avoid tracing

public static void main(String[] argv){
if (Ping.TRACE > 0) {
System.out.println(new Ping().ping("192.168.18.1"));
//System.out.println(new Ping().ping("127.0.0.1"));
} else {
System.out.println(new Ping().ping(argv[0]));
}
}

/**
* Send an ICMP packet (type 8) (ping) to the provided address
* @param dst_ip Destination IP / hostname
* @return true/false
*/
public boolean ping(String dst_ip){
if (Ping.TRACE > 0) {
long start, stop, elapsed;
start = System.currentTimeMillis();
boolean res = new PacketSender().ping(dst_ip);
stop = System.currentTimeMillis();
elapsed = stop - start;
System.out.println("Elapsed: " + elapsed + " ms.");
return res;
} else {
return new PacketSender().ping(dst_ip);
}
}

/**
* Send an ICMP packet (type 8) (ping) to the provided address
* @param dst_ip Destination IP / hostname
* @param timeout Timeout for receiving a response in milliseconds
* @return
*/
public boolean ping(String dst_ip, int timeout){
ARPTIMEOUT = timeout;
ICMPTIMEOUT = timeout;
return new PacketSender().ping(dst_ip);
}

/**
* Class for assembling and sending ARP and ICMP packets
*/
private class PacketSender implements IARPPacketListener, IICMPPacketListener{
private boolean arpReplyArrived = false;
private boolean icmpReplyArrived = false;
private InetAddress src_ip = null;
private ARPPacket arpReplyPacket = null;
private String dest_ip = null;


public boolean ping(String dest_ip){
this.dest_ip = dest_ip;
NetworkInterface device = determineDevice();
src_ip = determineSRCIP(device);
ARPPacket arpPacket = assembleARPPacket(dest_ip,src_ip,device);

JpcapCaptor captor = openDevice(device);
JpcapSender sender = getSender(captor);
//HGC 13-AUG-2009
if (0 }
}

/**
* Construct a valid PING packet (ICMP type 8)
* @param device
* @param dst_mac
* @return
*/
public ICMPPacket assembleICMPPacket(NetworkInterface device, byte[] dst_mac){
ICMPPacket p=new ICMPPacket();
p.type=ICMPPacket.ICMP_ECHO;
p.seq=1000;
p.id=999;
p.orig_timestamp=123;
p.trans_timestamp=456;
p.recv_timestamp=789;
try {
p.setIPv4Parameter(0,false,false,false,0,false,false,false,0,1010101,100,IPPacket.IPPROTO_ICMP,
src_ip,InetAddress.getByName(dest_ip));
} catch (UnknownHostException e) {
e.printStackTrace();
}
p.data="data".getBytes();
EthernetPacket ether=new EthernetPacket();
ether.frametype=EthernetPacket.ETHERTYPE_IP;
ether.src_mac=device.mac_address;
ether.dst_mac=dst_mac;
p.datalink=ether;
return p;
}

/**
* Select the probable correct interface
* @return NetworkInterface
*/
public NetworkInterface determineDevice(){
NetworkInterface dev = null;
for(NetworkInterface device : JpcapCaptor.getDeviceList()){
for(NetworkInterfaceAddress address : device.addresses){
if(address.address instanceof Inet4Address && !address.address.toString().equalsIgnoreCase("/0.0.0.0") ){
dev = device;
break;
}
}
}

//HGC 13-AUG-2009
if (0 EthernetPacket ether=new EthernetPacket();
ether.frametype=EthernetPacket.ETHERTYPE_ARP;
ether.src_mac=device.mac_address;
ether.dst_mac=broadcast;
arp.datalink=ether;
return arp;
}

/**
* Open JpcapCaptor
* @param device
* @return JpcapCaptor
*/
public JpcapCaptor openDevice(NetworkInterface device){
JpcapCaptor captor = null;
try {
captor = JpcapCaptor.openDevice(device,2000,false,3000);
} catch (IOException e) {
e.printStackTrace();
}
return captor;
}

/**
* Get JpcapSender
* @param captor
* @return JpcapSender
*/
public JpcapSender getSender(JpcapCaptor captor){
return captor.getJpcapSenderInstance();
}
}


/**
* Packet receiver implemented as a seperate thread
* Register observers to get notified of received packets
*/
private class PacketReceiver implements Runnable{
private JpcapCaptor captor;
private EventListenerList listeners = new EventListenerList();

public PacketReceiver(JpcapCaptor captor){
this.captor = captor;
}
public void addARPListener( IARPPacketListener listener ) {
listeners.add( IARPPacketListener.class, listener );
}

public void removeARPListener( IARPPacketListener listener ){
listeners.remove( IARPPacketListener.class, listener );
}
protected synchronized void notifyARPListeners( ARPPacketArrivedEvent event ) {
for ( IARPPacketListener l : listeners.getListeners( IARPPacketListener.class )){
l.arpPacketArrived(event);
}
}

public void addICMPListener( IICMPPacketListener listener ) {
listeners.add( IICMPPacketListener.class, listener );
}

public void removeICMPListener( IICMPPacketListener listener ){
listeners.remove( IICMPPacketListener.class, listener );
}
protected synchronized void notifyICMPListeners( ICMPPacketArrivedEvent event ) {
for ( IICMPPacketListener l : listeners.getListeners( IICMPPacketListener.class )){
l.icmpPacketArrived(event);
}
}

public void run(){
while(true){
Packet packet = captor.getPacket();
if(packet instanceof ARPPacket){
ARPPacket arpPacket = (ARPPacket)packet;
notifyARPListeners(new ARPPacketArrivedEvent(this,arpPacket));
}
if(packet instanceof ICMPPacket){
ICMPPacket icmpPacket = (ICMPPacket)packet;
notifyICMPListeners(new ICMPPacketArrivedEvent(this,icmpPacket));
}
}
}
}

private interface IARPPacketListener extends EventListener{
void arpPacketArrived(ARPPacketArrivedEvent event);
}

private interface IICMPPacketListener extends EventListener{
void icmpPacketArrived(ICMPPacketArrivedEvent event);
}

private class ARPPacketArrivedEvent extends EventObject{
private ARPPacket arpPacket;
public ARPPacketArrivedEvent(Object source, ARPPacket arpPacket){
super(source);
this.arpPacket = arpPacket;
}
public ARPPacket getARPPacket(){
return arpPacket;
}
}

private class ICMPPacketArrivedEvent extends EventObject{
private ICMPPacket icmpPacket;
public ICMPPacketArrivedEvent(Object source, ICMPPacket icmpPacket){
super(source);
this.icmpPacket = icmpPacket;
}
public ICMPPacket getICMPPacket(){
return icmpPacket;
}
}
}

//SOURCE CODE ENDS HERE
1 Friday, 10 July 2009 18:33
vladimir
sorry, but!
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 0
at pruebas.Ping.main(Ping.java:33)
Java Result: 1

Add your comment

Your name:
Comment:
  The word for verification. Lowercase letters only with no spaces.
Word verification: