Click here to Skip to main content
Click here to Skip to main content

Trace Utility to Trace IP Packet Path

, 29 May 2002
Rate this:
Please Sign up or sign in to vote.
Using the ICMP protocol to trace the routing of packets.

Overview

The Trace utility traces the path of an IP packet. To understand the process, its important to understand ICMP protocol we are going to use for this purpose.

ICMP Protocol

The Internet Protocol (IP) is used for host-to-host datagram service in a system of interconnected networks. Occasionally a destination host will communicate with a source host, for example, to report an error in datagram processing. For such purposes the Internet Control Message Protocol (ICMP), is used. ICMP, uses the basic support of IP as if it were a higher level protocol, however, ICMP is actually an integral part of IP, and must be implemented by every IP module.

ICMP messages are sent in several situations: for example, when a datagram cannot reach its destination, when the gateway does not have the buffering capacity to forward a datagram, and when the gateway can direct the host to send traffic on a shorter route.

The Internet Protocol is not designed to be absolutely reliable. The purpose of these control messages is to provide feedback about problems in the communication environment, not to make IP reliable. There are still no guarantees that a datagram will be delivered or a control message will be returned. Some datagrams may still be undelivered without any report of their loss. The higher level protocols that use IP must implement their own reliability procedures if reliable communication is required.

The ICMP messages typically report errors in the processing of datagrams. To avoid the infinite regress of messages about messages etc., no ICMP messages are sent about ICMP messages. Also ICMP messages are only sent about errors in handling fragment zero of fragemented datagrams. (Fragment zero has the fragment offeset equal zero).

Basics of the Trace utility

Apart from other fields each ICMP header consists of a field called Time to Live (TTL). TTL field is decremented at each machine in which the datagram is processed. Thus if my packet routes through Machine A-> Machine B-> Machine C, and if I set initial TTL to 3 then TTL at B would be 2 and at C would be 1. If the gateway processing a datagram finds TTL field is zero it discards the datagram. The gateway also notifies the source host via the time exceeded message.

Thus to get our utility working we send a packet containing echo request to the destination machine with increasing number of TTL starting from 1. Each time TTL goes to zero the machine that was currently processing datagram returns the packet with time exceeded message. We remeber the ip of this machine and send the packet back with incremented TTL. We repeat this till we successfully receive an echo reply.

You can compile the .cs file on command line by command

csc Trace.cs

and run it

Trace www.codeproject.com

The Code

/********************************************************************
*    Class:        Trace
*    Description:  Traces path of an ip packet with its respond time
*    Author:       Sanjay Ahuja
*    Date:         5/15/2002
*    Copyright©:   © 2002, Sanjay Ahuja (lparam@hotmail.com). Use it 
*                  as you want till you leave my name intact
/*********************************************************************


using System;
using System.Net;
using System.Net.Sockets;


//ICMP constants
struct ICMPConstants
{
    public const int ICMP_ECHOREPLY= 0;     // Echo reply query
    public const int ICMP_TIMEEXCEEDED= 11; // TTL exceeded error
    public const int ICMP_ECHOREQ=    8;    // Echo request query
    public const int MAX_TTL= 256;          // Max TTL
}

//ICMP header, size is 8 bytes
struct ICMP
{
    public byte    type;           // Type
    public byte    code;           // Code
    public ushort  checksum;       // Checksum
    public ushort  id;             // Identification
    public ushort  seq;            // Sequence
}

// ICMP Echo Request, size is 12+ 32 (PACKET_SIZE as 
// defined in class Trace)= 44 bytes
struct REQUEST
{
    public ICMP    m_icmp;
    public byte    []m_data;
}

class Trace
{
    const int PACKET_SIZE= 32;

    public static void Main(string[] args)
    {
        try
        {
            //verify command line
            if(args.Length== 0)
            {
                Console.WriteLine("usage: trace <hostname>");
                return;
            }

            //Create Raw ICMP Socket 
            Socket s= new Socket(AddressFamily.InterNetwork, 
                                 SocketType.Raw, ProtocolType.Icmp);
            //destination
            IPEndPoint ipdest= new IPEndPoint(
                                        Dns.Resolve(args[0]).AddressList[0],80);
            //Source
            IPEndPoint ipsrc= new IPEndPoint(Dns.GetHostByName(
                                        Dns.GetHostName()).AddressList[0],80);
            EndPoint epsrc= (EndPoint)ipsrc;
                
            ICMP ip= new ICMP();
            ip.type = ICMPConstants.ICMP_ECHOREQ; 
            ip.code = 0;
            ip.checksum = 0;

            //any number you feel is kinda unique :)
            ip.id = (ushort)DateTime.Now.Millisecond;    
            ip.seq  = 0;
            
            REQUEST req= new REQUEST();
            req.m_icmp= ip;
            req.m_data = new Byte[PACKET_SIZE];
                
            //Initialize data
            for (int i = 0; i < req.m_data.Length; i++)
            {
                req.m_data[i] = (byte)'S';
            }

            //this function would gets byte array from the REQUEST structure
            Byte[] ByteSend= CreatePacket(req);

            //send requests with increasing number of TTL
            for(int ittl=1; ittl<= ICMPConstants.MAX_TTL; ittl++)
            {
                Byte[] ByteRecv = new Byte[256];
                //Socket options to set TTL and Timeouts 
                s.SetSocketOption(SocketOptionLevel.IP, 
                                  SocketOptionName.IpTimeToLive, ittl);
                s.SetSocketOption(SocketOptionLevel.Socket, 
                                  SocketOptionName.SendTimeout,10000); 
                s.SetSocketOption(SocketOptionLevel.Socket, 
                                  SocketOptionName.ReceiveTimeout,10000); 

                //Get current time
                DateTime dt= DateTime.Now;
                //Send Request
                int iRet= s.SendTo(ByteSend, ByteSend.Length, SocketFlags.None, 
                                   ipdest);
                //check for Win32 SOCKET_ERROR
                if(iRet== -1)
                    Console.WriteLine("error sending data");

                //Receive
                iRet= s.ReceiveFrom(ByteRecv, ByteRecv.Length, 
                                    SocketFlags.None, ref epsrc);

                //Calculate time required
                TimeSpan ts= DateTime.Now- dt;;

                //check if response is OK
                if(iRet== -1)
                    Console.WriteLine("error getting data");

                Console.WriteLine("TTL= {0,-5} IP= {1,-20} Time= {2,3}ms",
                                  ittl, ((IPEndPoint)epsrc).Address,
                                  ts.Milliseconds);
                //reply size should be sizeof REQUEST + 20 (i.e sizeof IP header),
                //it should be an echo reply and id should be same
                if((iRet == PACKET_SIZE+ 8 +20)&& 
                    (BitConverter.ToInt16(ByteRecv,24) == 
                                  BitConverter.ToInt16(ByteSend,4))&& 
                    (ByteRecv[20] == ICMPConstants.ICMP_ECHOREPLY))
                    break;
                //time out
                if(ByteRecv[20] != ICMPConstants.ICMP_TIMEEXCEEDED)
                {
                    Console.WriteLine("unexpected reply, quitting...");
                    break;
                }
            }
        }
        catch(SocketException e)
        {
            Console.WriteLine(e.Message);
        }
        catch(Exception e)
        {
            Console.WriteLine(e.Message);
        }

    }

    
    public static byte[] CreatePacket( REQUEST req )
    {
        Byte[] ByteSend= new Byte[PACKET_SIZE+ 8];
        //Create Byte array from REQUEST structure
        ByteSend[0]= req.m_icmp.type;
        ByteSend[1]= req.m_icmp.code;
        Array.Copy(BitConverter.GetBytes(req.m_icmp.checksum), 
                   0, ByteSend, 2, 2);
        Array.Copy(BitConverter.GetBytes(req.m_icmp.id), 
                   0, ByteSend, 4, 2);
        Array.Copy(BitConverter.GetBytes(req.m_icmp.seq),
                   0, ByteSend, 6, 2);
        for(int i=0; i< req.m_data.Length; i++)
            ByteSend[i+8]= req.m_data[i];

        //calculate checksum
        int iCheckSum = 0;
        for (int i= 0; i < ByteSend.Length; i+= 2) 
            iCheckSum += Convert.ToInt32( BitConverter.ToUInt16(ByteSend,i));

        iCheckSum = (iCheckSum >> 16) + (iCheckSum & 0xffff);
        iCheckSum += (iCheckSum >> 16);

        //update byte array to reflect checksum 
        Array.Copy(BitConverter.GetBytes((ushort)~iCheckSum), 0, 
                   ByteSend, 2, 2);
        return ByteSend;
    }

}

Questions? Send them to me at lparam@hotmail.com

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

Sanjay Ahuja

United States United States
Sanjay Ahuja is a Bachelor of Engineer and has completed his CDAC in Pune,India. He is currently working as a consultant for Verizon.

Comments and Discussions

 
Generalit doesnt work properly Pinmemberzaaboulla30-Jun-05 5:11 
GeneralRe: it doesnt work properly Pinmemberculeadragos15-Feb-06 4:20 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.140827.1 | Last Updated 30 May 2002
Article Copyright 2002 by Sanjay Ahuja
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid