Classes | Public Member Functions | Private Member Functions | Private Attributes

Traceroute Class Reference

Encapsulates ICMP interface that can be used to ping or trace route remote hosts. More...

Collaboration diagram for Traceroute:
Collaboration graph
[legend]

List of all members.

Classes

interface  Context
 Provides message presentation context to instance of Traceroute. More...

Public Member Functions

 Traceroute (Context context)
 Creates new instance of Traceroute
void startPinging (int deviceNo, String hostName, int initialTTL)
 Starts thread that will trace route to given host.
void stopTrace ()
 Stops on-going trace route or ping.
boolean isIdle ()
 Returns if IDLE, i.e.
void dumpInterfaceInfo (String title, NetworkInterface ni)
 Dumps details about particular Jpcap network interface into log area.
String[] getInterfaceList ()
 Gets array of interface descriptions (suitable for the JComboBox)
boolean tcpPortScan (int localPort, String remoteHost, int rp1, int rp2) throws UnknownHostException, IOException
 Scan TCP ports.
void run ()
 Traces route to given host.

Private Member Functions

void println (String str)
 Prints line to log area prefixed with timestamp.
void println ()
 Advances to new line in log area.
void print (String str, boolean timestamp)
 Prints characters to log area optionally prefixed with timestamp.
void openDeviceOnInterface (int deviceNo)
void interruptibleSleep (int millis)
 Interruptible sleep (replacement for Thread.sleep).
byte[] obtainDefaultGatewayMac (String httpHostToCheck)
 Obtains MAC address of the default gateway for captor interface.

Private Attributes

Thread tracingThread
 Instance of the thread that sends ICMP packets.
volatile boolean running = false
 Indicates if thread is (or should be) running.
volatile boolean completed
 Indicates that thread has been completed.
int deviceCount = 0
 Jpcap NetworkInterface device count.
JpcapCaptor captor = null
 Instance of the Jpcap capturing class.
NetworkInterface device = null
 Instance of the Jpcap network interface used for sending and receiving ICMP packets.
InetAddress localIP = null
 Local IP address.
boolean resolveNames = false
 Indicates whether to resolve addresses to names or not.
String hostName
 Host name or IP address to ping.
int initialTTL
 Initial TTL (time to live or hop count).
Context context
 Presentation context for messages.

Detailed Description

Encapsulates ICMP interface that can be used to ping or trace route remote hosts.

Author:
Mikica B Kocic

Definition at line 27 of file Traceroute.java.


Constructor & Destructor Documentation

Traceroute.Traceroute ( Context  context )

Creates new instance of Traceroute

Parameters:
contextwhere to log messages

Definition at line 120 of file Traceroute.java.

References completed, context, deviceCount, running, and tracingThread.

    {
        this.context       = context;
        this.running       = false;
        this.completed     = true;
        this.tracingThread = null;
        
        deviceCount = JpcapCaptor.getDeviceList ().length;
    }

Member Function Documentation

void Traceroute.dumpInterfaceInfo ( String  title,
NetworkInterface  ni 
)

Dumps details about particular Jpcap network interface into log area.

Parameters:
titletitle line
ninetwork interface to show

Definition at line 223 of file Traceroute.java.

References println().

Referenced by getInterfaceList().

    {
        println( title );
        println( "    Desc: " + ni.description );
        println( "    Name: " + ni.name );
        for( NetworkInterfaceAddress na : ni.addresses )
        {
            println( "    Addr: " + na.address );
        }
    }
String [] Traceroute.getInterfaceList (  )

Gets array of interface descriptions (suitable for the JComboBox)

Returns:
array of strings with descriptions

Definition at line 239 of file Traceroute.java.

References deviceCount, and dumpInterfaceInfo().

Referenced by TraceroutePane.TraceroutePane().

    {
        this.deviceCount = JpcapCaptor.getDeviceList ().length;
        String[] devList = new String[ this.deviceCount ];
        
        int ni_index = 0;
        for( NetworkInterface ni : JpcapCaptor.getDeviceList () )
        {
            String ourDescription = ni.description;
            for( NetworkInterfaceAddress addr : ni.addresses )
            {
                if( addr.address instanceof Inet4Address ) {
                    ourDescription = addr.address.toString () + " -- " + ni.description;
                    break;
                }
            }

            devList[ ni_index ] = " iface" + ni_index + " -- " + ourDescription;

            dumpInterfaceInfo( "Interface " + (ni_index++), ni );
        }
        
        return devList;
    }
void Traceroute.interruptibleSleep ( int  millis ) [private]

Interruptible sleep (replacement for Thread.sleep).

Parameters:
millis- the length of time to sleep in milliseconds.

Definition at line 306 of file Traceroute.java.

References running.

Referenced by obtainDefaultGatewayMac(), run(), and tcpPortScan().

    {
        synchronized( this )
        {
            try {
                this.wait( millis );
            }
            catch( InterruptedException ie ) {
                running = false; // kills the thread
            }
        }
    }
boolean Traceroute.isIdle (  )

Returns if IDLE, i.e.

if tracing thread does not exist or previous thread has been completed.

Definition at line 212 of file Traceroute.java.

References completed.

Referenced by TraceroutePane.parseCommand().

    {
        return completed;
    }
byte [] Traceroute.obtainDefaultGatewayMac ( String  httpHostToCheck ) [private]

Obtains MAC address of the default gateway for captor interface.

Returns:
MAC address as byte array

Definition at line 324 of file Traceroute.java.

References captor, device, interruptibleSleep(), print(), println(), and running.

Referenced by run(), and tcpPortScan().

    {
        print( "Obtaining default gateway MAC address... ", true );
        
        byte[] gatewayMAC = null;
        
        if ( captor != null ) try
        {
            InetAddress hostAddr = InetAddress.getByName( httpHostToCheck );
            captor.setFilter( "tcp and dst host " + hostAddr.getHostAddress(), true );
            
            int timeoutTimer = 0;
            new URL("http://" + httpHostToCheck ).openStream().close();
            
            while( running )
            {
                Packet ping = captor.getPacket ();
                
                if( ping == null )
                {
                    if ( timeoutTimer < 20 ) { // max 2 sec
                        interruptibleSleep( 100  /*millis*/ );
                        ++timeoutTimer;
                        continue;
                    }
                    /* else: Timeout exceeded
                     */
                    print( "<timeout>", /*timestamp*/ false );
                    println( "ERROR: Cannot obtain MAC address for default gateway." );
                    println( "Maybe there is no default gateway on selected interface?" );
                    return gatewayMAC;
                }
                
                byte[] destinationMAC = ((EthernetPacket)ping.datalink).dst_mac; 
                
                if( ! Arrays.equals( destinationMAC, device.mac_address ) ) {
                    gatewayMAC = destinationMAC;
                    break;
                }

                timeoutTimer = 0; // restart timer
                new URL("http://" + httpHostToCheck ).openStream().close();
            }
        }
        catch( MalformedURLException e )
        {
            println( "Invalid URL: " + e.toString () );
        }
        catch( UnknownHostException e )
        {
            println( "Unknown host: " + httpHostToCheck );
        }
        catch( IOException e )
        {
            println( "ERROR: " + e.toString () );
        }
        
        print( " OK.", /*timestamp*/ false );
        println ();
        return gatewayMAC;
    }
void Traceroute.openDeviceOnInterface ( int  deviceNo ) [private]

Definition at line 269 of file Traceroute.java.

References captor, device, and localIP.

Referenced by startPinging().

    {
        // Open specified device from the list
        //
        device = JpcapCaptor.getDeviceList()[ deviceNo ];
        localIP = null;
        captor = null;
        
        try
        {
            captor = JpcapCaptor.openDevice( device, 
                    /*MTU*/ 2000, /*promiscuous*/ false, /*timeout*/ 1 );
            
            // captor.setNonBlockingMode( true );
            // captor.setPacketReadTimeout( 1000 );

            for( NetworkInterfaceAddress addr : device.addresses )
            {
                if( addr.address instanceof Inet4Address ) {
                    localIP = addr.address;
                    break;
                }
            }
        }
        catch ( IOException e )
        {
            device  = null;
            localIP = null;
            captor  = null;
        }
    }
void Traceroute.print ( String  str,
boolean  timestamp 
) [private]

Prints characters to log area optionally prefixed with timestamp.

Parameters:
strmessage to log
timestampwhether to prefix message with timestamp or not

Definition at line 153 of file Traceroute.java.

References context, and Traceroute.Context.logMessage().

Referenced by obtainDefaultGatewayMac(), run(), stopTrace(), and tcpPortScan().

    {
        context.logMessage( str,timestamp );
    }
void Traceroute.println (  ) [private]

Advances to new line in log area.

Definition at line 142 of file Traceroute.java.

References context, and Traceroute.Context.logNewLine().

Referenced by dumpInterfaceInfo(), obtainDefaultGatewayMac(), run(), and tcpPortScan().

void Traceroute.println ( String  str ) [private]

Prints line to log area prefixed with timestamp.

Definition at line 133 of file Traceroute.java.

References context, Traceroute.Context.logMessage(), and Traceroute.Context.logNewLine().

    {
        context.logMessage( str, /*timestamp*/ true );
        context.logNewLine ();
    }
void Traceroute.run (  )

Traces route to given host.

The instance is locked during in the mean time so other trace routes could not start (completed == false suppresses other threads).

Definition at line 514 of file Traceroute.java.

References captor, completed, context, device, hostName, initialTTL, interruptibleSleep(), localIP, obtainDefaultGatewayMac(), Traceroute.Context.onTracerouteCompleted(), print(), println(), resolveNames, and running.

    {
        /* Release instance to other threads
         */
        if ( ! running ) {
            completed = true;
            context.onTracerouteCompleted ();
            return;
        }

        /* Make sure that capturing device is configured
         */
        if ( captor == null ) {
            println( "Capturing device is not configured..." );
            running   = false;
            completed = true;
            context.onTracerouteCompleted ();
            return;
        }

        /* Starts sending ICMP packets and tracing route...
         */
        try
        {
/*            
            if ( ! tcpPortScan( 15000, hostName, 1, 1000 ) ) {
                completed = true;
                context.onTracerouteCompleted ();
                return;
            }
*/
            println( "-------------------------------------------------" );
            print( "Looking up " + hostName + "...", /*timestamp*/ true );
            
            InetAddress remoteIP = InetAddress.getByName( hostName );
            
            print( "  " + remoteIP.getHostAddress (), /*timestamp*/ false );
            println ();

            byte[] defaultGatewayMAC = obtainDefaultGatewayMac( "dsv.su.se" );
            if ( defaultGatewayMAC == null )
            {
                running = false;
                completed = true;
                context.onTracerouteCompleted ();
                return;
            }
  
            if ( initialTTL == 0 ) {
                println( "Tracing route to " + remoteIP + "..." );
            } else {
                println( "Pinging host " + remoteIP + "..." );
            }
            
            /* Create ICMP packet
             */
            ICMPPacket icmp = new ICMPPacket ();

            icmp.type       = ICMPPacket.ICMP_ECHO;
            icmp.seq        = 100;
            icmp.id         = 0;
            icmp.data       = "data".getBytes ();
            
            icmp.setIPv4Parameter(
                    0,          // int priority - Priority
                    false,      // boolean: IP flag bit: Delay
                    false,      // boolean: IP flag bit: Through
                    false,      // boolean: IP flag bit: Reliability
                    0,          // int: Type of Service (TOS)
                    false,      // boolean: Fragmentation Reservation flag
                    false,      // boolean: Don't fragment flag
                    false,      // boolean: More fragment flag
                    0,          // int: Offset
                    0,          // int: Identifier
                    0,          // int: Time To Live
                    IPPacket.IPPROTO_ICMP, // Protocol 
                    localIP,    // Source IP address
                    remoteIP    // Destination IP address
                    );

            EthernetPacket ether = new EthernetPacket ();
            ether.frametype = EthernetPacket.ETHERTYPE_IP;
            ether.src_mac   = device.mac_address;
            ether.dst_mac   = defaultGatewayMAC;
            icmp.datalink   = ether;

            /* Send ICMP packets...
             */
            JpcapSender sender = captor.getJpcapSenderInstance ();
            captor.setFilter( "icmp and dst host " + localIP.getHostAddress(), true );
            
            icmp.hop_limit  = (short)initialTTL;
            print( icmp.hop_limit + ": ", /*timestamp*/ true );
            int timeoutTimer = 0;
            int timeoutCounter = 0;
            long tStart = System.nanoTime ();
            sender.sendPacket( icmp );
            
            while( running )
            {
                ICMPPacket p = (ICMPPacket)captor.getPacket ();
                int tDelay = (int)( ( System.nanoTime () - tStart ) / 1000000l );

                if( p == null ) // TIMEOUT
                {
                    /* Continue waiting until ~2 sec elapses
                     */
                    if ( timeoutTimer < 30 ) 
                    {
                        interruptibleSleep( timeoutTimer < 10 ? 1 : 100 );
                        ++timeoutTimer;
                        if ( timeoutTimer >= 10 ) {
                            print( ".", /*timestamp*/ false );
                        }
                        continue;
                    }

                    /* Increase timeout counter and either retry or advance Hop limit
                     */
                    ++timeoutCounter;
                    print( " Timeout #" + timeoutCounter, /*timestamp*/ false );
                    
                    if ( timeoutCounter < 3 ) // Retry send to the same Hop
                    {
                        print( icmp.hop_limit + ": ", /*timestamp*/ true );
                        tStart = System.nanoTime ();
                        timeoutTimer = 0;
                        sender.sendPacket( icmp );
                    }
                    else // Advance Hop limit and send to next hop
                    {
                        ++icmp.hop_limit;
                        print( icmp.hop_limit + ": ", /*timestamp*/ true );
                        timeoutTimer = 0;
                        timeoutCounter = 0;
                        tStart = System.nanoTime ();
                        sender.sendPacket( icmp );
                    }
                    continue;
                }
                
                /* We are here because we got some ICMP packet... resolve name first.
                 */
                String hopID = p.src_ip.getHostAddress ();
                if ( resolveNames ) {
                    p.src_ip.getHostName ();
                    hopID = p.src_ip.toString ();
                }

                /* Now, in case if we received 'time exceeded' packet we should advance
                 * to the next Hop limit. Otherwise if host is either unreachable or 
                 * we got echo reply, we should quit.
                 */
                if( p.type == ICMPPacket.ICMP_TIMXCEED ) // Time exceeded
                {
                    print( hopID + ", " + tDelay + " ms", /*ts*/ false  );
                    ++icmp.hop_limit;
                    print( icmp.hop_limit + ": ", /*timestamp*/ true );
                    timeoutTimer = 0;
                    timeoutCounter = 0;
                    tStart = System.nanoTime ();
                    sender.sendPacket( icmp );
                }
                else if( p.type == ICMPPacket.ICMP_UNREACH ) // Host unreachable
                {
                    print( hopID + " unreachable", /*ts*/ false  );
                    running = false;
                }
                else if( p.type == ICMPPacket.ICMP_ECHOREPLY ) // Echo reply (pong)
                {
                    print( hopID + ", " + tDelay + " ms", /*ts*/ false );
                    if ( initialTTL != 0 ) {
                        println( hopID + " is alive." );
                    }
                    running = false;
                }
            }
        }
        catch( UnknownHostException e )
        {
            println( "Unknown host: " + hostName );
            completed = true;
            context.onTracerouteCompleted ();
            return;
        }
        catch( IOException e )
        {
            println( "ERROR: " + e.toString () );
            completed = true;
            context.onTracerouteCompleted ();
            return;
        }

        /* Release instance to other threads
         */
        println( initialTTL == 0 ? "Traceroute completed." : "Ping completed." );
        completed = true;
        context.onTracerouteCompleted ();
     }
void Traceroute.startPinging ( int  deviceNo,
String  hostName,
int  initialTTL 
)

Starts thread that will trace route to given host.

The instance is locked in the mean time, so other trace routes could not start in parallel. To start trace-route initialTTL must be set to 0. To start ping instead, set initialTTL to 64.

Parameters:
deviceNonetwork interface on which to start pinging
hostNametarget host address or host name
initialTTLinitial hop limit (or time-to-live)

Definition at line 168 of file Traceroute.java.

References completed, hostName, initialTTL, openDeviceOnInterface(), running, and tracingThread.

Referenced by TraceroutePane.parseCommand().

    {
        synchronized( this )
        {
            if ( ! completed ) {  // Allows only one thread per instance
                return;
            }
    
            /* Set thread parameters
             */
            openDeviceOnInterface( deviceNo );
            this.hostName   = hostName;
            this.initialTTL = initialTTL;

            /* Enable thread
             */
            running   = true;
            completed = false;

            /* Start thread
             */
            tracingThread = new Thread( this );
            tracingThread.start ();
        }
    }
void Traceroute.stopTrace (  )

Stops on-going trace route or ping.

Definition at line 197 of file Traceroute.java.

References print(), and running.

Referenced by TraceroutePane.parseCommand().

    {
        synchronized( this )
        {
            running = false; // signal thread to exit
            this.notifyAll (); // interrupt any sleep
        }

        print( " <interrupt> ", /*timestamp*/ false );
    }
boolean Traceroute.tcpPortScan ( int  localPort,
String  remoteHost,
int  rp1,
int  rp2 
) throws UnknownHostException, IOException

Scan TCP ports.

Exceptions:
UnknownHostException

Definition at line 391 of file Traceroute.java.

References captor, completed, context, device, hostName, interruptibleSleep(), localIP, obtainDefaultGatewayMac(), Traceroute.Context.onTracerouteCompleted(), print(), println(), resolveNames, and running.

    {
        println( "-------------------------------------------------" );
        print( "Looking up " + hostName + "...", /*timestamp*/ true );
        
        InetAddress remoteIP = InetAddress.getByName( remoteHost );
        
        print( "  " + remoteIP.getHostAddress (), /*timestamp*/ false );
        println ();

        byte[] defaultGatewayMAC = obtainDefaultGatewayMac( "dsv.su.se" );
        if ( defaultGatewayMAC == null )
        {
            running = false;
            completed = true;
            context.onTracerouteCompleted ();
            return running;
        }

        TCPPacket tcp = new TCPPacket(
                localPort,  // int src_port 
                0,          // int dst_port 
                701,        // long sequence 
                0,          // long ack_num 
                false,      // boolean urg 
                false,      // boolean ack 
                false,      // boolean psh 
                false,      // boolean rst 
                true,       // boolean syn 
                false,      // boolean fin 
                true,       // boolean rsv1 
                true,       // boolean rsv2 
                10,         // int window
                10          // int urgent
                );
        
        tcp.setIPv4Parameter(
                0,          // int priority - Priority
                false,      // boolean: IP flag bit: Delay
                false,      // boolean: IP flag bit: Through
                false,      // boolean: IP flag bit: Reliability
                0,          // int: Type of Service (TOS)
                false,      // boolean: Fragmentation Reservation flag
                false,      // boolean: Don't fragment flag
                false,      // boolean: More fragment flag
                0,          // int: Offset
                (int)(Math.random () * 65000), // int: Identifier
                100,        // int: Time To Live
                IPPacket.IPPROTO_TCP, // Protocol 
                localIP,    // Source IP address
                remoteIP    // Destination IP address
                );
        
        tcp.data=("").getBytes();
        
        EthernetPacket ether = new EthernetPacket ();
        ether.frametype = EthernetPacket.ETHERTYPE_IP;
        ether.src_mac   = device.mac_address;
        ether.dst_mac   = defaultGatewayMAC;
        tcp.datalink    = ether;

        /* Send TCP packets...
         */
        JpcapSender sender = captor.getJpcapSenderInstance ();
        captor.setFilter( "tcp and dst port " + localPort 
                          + " and dst host " + localIP.getHostAddress(), 
                          true );
        
        for ( int remotePort = rp1; remotePort <= rp2; ++remotePort ) {
            tcp.src_port = localPort;
            tcp.dst_port = remotePort;
            sender.sendPacket( tcp );
            //println( "SENT: " + tcp );
        }

        while( running )
        {
            TCPPacket p = (TCPPacket)captor.getPacket ();

            if( p == null ) // TIMEOUT
            {
                interruptibleSleep( 100 );
                continue;
            }

            /* We are here because we got some ICMP packet... resolve name first.
             */
            String hopID = p.src_ip.getHostAddress ();
            if ( resolveNames ) {
                p.src_ip.getHostName ();
                hopID = p.src_ip.toString ();
            }

            // println( "---------------------------------------------------------" );
            // println( "RECEIVED: " + p.toString () );
            
            if ( ! p.rst ) {
                if ( p.ack_num == 702 ) {
                    println( "---- " + hopID + " : " + p.src_port );
                }
                tcp.dst_port = p.src_port;
                tcp.fin = p.ack_num == 702 ? true : false;
                tcp.ack = true;
                tcp.rst = false;
                tcp.syn = false;
                tcp.sequence = p.ack_num;
                tcp.ack_num = p.sequence + 1;
                sender.sendPacket( tcp );
                //println( "SENT: " + tcp );
            }
            // running = false;
        }
        
        return running;
    }

Member Data Documentation

JpcapCaptor Traceroute.captor = null [private]

Instance of the Jpcap capturing class.

Definition at line 78 of file Traceroute.java.

Referenced by obtainDefaultGatewayMac(), openDeviceOnInterface(), run(), and tcpPortScan().

volatile boolean Traceroute.completed [private]

Indicates that thread has been completed.

Definition at line 68 of file Traceroute.java.

Referenced by isIdle(), run(), startPinging(), tcpPortScan(), and Traceroute().

Presentation context for messages.

Definition at line 111 of file Traceroute.java.

Referenced by print(), println(), run(), tcpPortScan(), and Traceroute().

NetworkInterface Traceroute.device = null [private]

Instance of the Jpcap network interface used for sending and receiving ICMP packets.

Definition at line 84 of file Traceroute.java.

Referenced by obtainDefaultGatewayMac(), openDeviceOnInterface(), run(), and tcpPortScan().

int Traceroute.deviceCount = 0 [private]

Jpcap NetworkInterface device count.

Definition at line 73 of file Traceroute.java.

Referenced by getInterfaceList(), and Traceroute().

String Traceroute.hostName [private]

Host name or IP address to ping.

Definition at line 100 of file Traceroute.java.

Referenced by run(), startPinging(), and tcpPortScan().

int Traceroute.initialTTL [private]

Initial TTL (time to live or hop count).

When set to 0, thread will do trace route. When set to e.g. 64, thread will do ping.

Definition at line 106 of file Traceroute.java.

Referenced by run(), and startPinging().

InetAddress Traceroute.localIP = null [private]

Local IP address.

Definition at line 89 of file Traceroute.java.

Referenced by openDeviceOnInterface(), run(), and tcpPortScan().

boolean Traceroute.resolveNames = false [private]

Indicates whether to resolve addresses to names or not.

By default disabled, because resolving will slow down trace route presentation.

Definition at line 95 of file Traceroute.java.

Referenced by run(), and tcpPortScan().

volatile boolean Traceroute.running = false [private]

Indicates if thread is (or should be) running.

Definition at line 63 of file Traceroute.java.

Referenced by interruptibleSleep(), obtainDefaultGatewayMac(), run(), startPinging(), stopTrace(), tcpPortScan(), and Traceroute().

Thread Traceroute.tracingThread [private]

Instance of the thread that sends ICMP packets.

Definition at line 58 of file Traceroute.java.

Referenced by startPinging(), and Traceroute().


The documentation for this class was generated from the following file: