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

Ping and Traceroute

By , 21 Feb 2008
 
Ping_and_Tracert_Codes

Ping

Ping is an old Unix utility which is pretty useful even today. The basic idea behind Ping is to find out whether the network is reachable and alive or not. Ping basically sends an ICMP echo request and then waits for an ICMP echo response; the roundtrip time is calculated from the time at which the request was sent and the time at which a response is received. For more information on ICMP, please see RFC 792.

To send out ICMP requests, we create a socket which will use ICMP as the protocol:

SOCKET sock;

//Create a raw socket which will use ICMP
sock = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP);

Now to send an ICMP request, first of all we populate the ICMP header structure:

struct ICMPheader
{
    unsigned char    byType;
    unsigned char    byCode;
    unsigned short    nChecksum;
    unsigned short    nId;
    unsigned short    nSequence;
};

ICMPheader sendHdr;
sendHdr.byCode = 0;       //Zero for ICMP echo and reply messages

//Sequence number is incremented with each request, this should be different
//so we can match a response with a request
sendHdr.nSequence = htons (nSequence++);    
sendHdr.byType = 8;       //Eight for ICMP echo message
sendHdr.nChecksum = 0;    //Checksum is calculated later on

Next we fill the ICMP header and message data into a buffer to be sent out:

//Create the message buffer, which is big enough to store the header and the 
//message data
pSendBuffer = new char [sizeof (ICMPheader) + nMessageSize];

//Copy the message header in the buffer
memcpy_s (pSendBuffer, sizeof (ICMPheader), &sendHdr, sizeof (ICMPheader));

//Fill the message with some arbitrary value
memset (pSendBuffer + sizeof (ICMPheader), 'x', nMessageSize);

Now we calculate the checksum of the ICMP header and the message being sent with it. The checksum for ICMP messages is calculated in the same way as for IP headers.

//Calculate checksum over ICMP header and message data
sendHdr.nChecksum = 
    htons (CalcChecksum (pSendBuffer, sizeof (ICMPheader) + nMessageSize)); 
   
//Copy the message header back into the buffer
memcpy_s (pSendBuffer, sizeof (ICMPheader), &sendHdr, sizeof (ICMPheader));

We are all ready to send the ICMP echo request, so here we do that:

nResult = sendto (sock, pSendBuffer, sizeof (ICMPheader) + 
            nMessageSize, 0, (SOCKADDR *)&dest, sizeof (SOCKADDR_IN));

With an ICMP request sent out, we now wait for the timeout interval (default is 5 seconds) to get a response. We use I/O socket model for this.

timeval timeInterval = {0, 0};
//Set the timeout interval for the select call to expire
timeInterval.tv_usec = nTimeOut * 1000;

FD_ZERO (&fdRead);
FD_SET (sock, &fdRead);

select (0, &fdRead, NULL, NULL, &timeInterval);

If we get a response, then the response is parsed to see if it has the correct checksum, the sequence ID is the same as in the request which was sent out, etc. One point to note here is that the bytes received contain the IP header + ICMP header + message, so we need to take them out accordingly and then proceed. The following code explains it further:

//We got a response so we construct the ICMP header and message out of it
ICMPheader recvHdr;
char *pICMPbuffer = NULL;

//The response includes the IP header as well, so we move 20 bytes ahead to 
//read the ICMP header
pICMPbuffer = pRecvBuffer + sizeof(IPheader);

//ICMP message length is calculated by subtracting the IP header size from 
//the total bytes received
int nICMPMsgLen = nResult - sizeof(IPheader);

//Construct the ICMP header
memcpy_s (&recvHdr, sizeof (recvHdr), pICMPbuffer, sizeof (recvHdr));

//Construct the IP header from the response
IPheader ipHdr;
memcpy_s (&ipHdr, sizeof (ipHdr), pRecvBuffer, sizeof (ipHdr));

recvHdr.nId = recvHdr.nId;
recvHdr.nSequence = recvHdr.nSequence;
recvHdr.nChecksum = ntohs (recvHdr.nChecksum);

//Check if the response is an echo reply, transaction ID and sequence number 
//are same as for the request, and that the checksum is correct
if (recvHdr.byType == 0 &&
    recvHdr.nId == sendHdr.nId &&
    recvHdr.nSequence == sendHdr.nSequence &&
    ValidateChecksum (pICMPbuffer, nICMPMsgLen) && 
memcmp (pSendBuffer + sizeof(ICMPheader), pRecvBuffer + sizeof(ICMPheader) + 
        sizeof(IPheader), nResult - sizeof (ICMPheader) - sizeof(IPheader)) == 0)
{
    //All's OK
    //So just calculate the round trip time and print it
}

And lastly, we repeat the above process (sending ICMP echo request and receiving responses) three or four times, as asked by the user, to calculate Ping statistics.

Tracert

Tracert is another networking utility which determines the route taken by packets across an IP network. The basic idea behind tracert is that it sends ICMP requests and increments their TTL value in the IP header by one on each request. The first request has a TTL of one, the next one two, and so on. When the first request reaches the next host, the host ignores the request and sends an ICMP time exceeded response. By seeing this response we are able to determine the host and thus able to produce a lists of hosts by sending incremented TTL values.

The code for Tracert is very similar to Ping. The only point of interest here is that we need to modify the IP header to change the TTL values. This is done using setsockopt with IPPROTO_IP and IP_TTL values as shown below:

setsockopt (sock, IPPROTO_IP, IP_TTL, (char *)&nTTL, sizeof (nTTL));

Conclusion

I mainly wrote this article for learning purposes and I hope that the code in this article is useful to others as well. Please be kind enough to notify me about any mistakes or a better way to do things.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Hitesh Sharma
United States United States
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
Generalclosesocket(sock); missing!memberStoneHead7 Dec '10 - 13:59 
For beginners, in ping, closesocket(sock); is missing before every return;
GeneralNice article - one problemmemberSamNorris15 Apr '10 - 9:18 
I noticed that the call to getnameinfo is checking "== SOCKET_ERROR". According to MSDN that should be checking "!= 0" otherwise szHostName can be left uninitialized for any other failures.
 
This minor change matches with the output from WinXP's version of tracert:
   if (getnameinfo((SOCKADDR*)&remoteAddr,
                   sizeof (SOCKADDR_IN),
                   szHostName,
                   NI_MAXHOST, 
                   NULL, 
                   0, 
                   NI_NUMERICSERV) == 0)
      cout << '\t' << szHostName << " [" << pszSrcAddr << "]";
   else
      cout << '\t' << pszSrcAddr;

GeneralNEED HELP! An Error has occur in gethostbyname operaion: WSAGetLastError <> = 11004memberbkimp1 Mar '10 - 22:09 
An Error has occur in gethostbyname operaion: WSAGetLastError <> = 11004
 
Hi. I'm running the program in vista basic using visual studio 2008.
GeneralAnother ICMP packetmemberwelcomyou4 Nov '09 - 22:14 
Thanks you very much for this Ping & Tracer Application Wink | ;)
 
When I run program Tracer . I found that Tracer is run wrongly because of some kind of ICMP packets: "Destination unreachable : port unreachable". These packets maybe result of getnameinfo() with IP which can't resolve name
And when I test with 2 instances of Tracer program . It receives ICMP packet each other --> make tracer program run wrongly
So , your program should check these kind of packet ...
 
Smile | :) Good day to you
( forgive me for my poor English Frown | :( )
Questionregarding raw socketsmemberN.siva subrahmanyam22 Oct '09 - 20:09 
hi i am siva.
i must implement traceroute application as an assignment in my training period in a campany
 
so in raw sockets it is necessary to check the check sum after recieving it
this should work on a switch ...
AnswerRe: regarding raw socketsmemberwelcomyou4 Nov '09 - 22:02 
"so in raw sockets it is necessary to check the check sum after recieving it
this should work on a switch ..."
 
==> It is necessary to check the check sum after receiving it . You doesn't need to check the checksum in IPheader but you have to check the checksum in ICMPHeader
Generalcompile errormemberalaa naeem2 Aug '09 - 5:58 
thanks for this useful article, when I tried to compile the file an error of the function (memcpy_s) appeared, I use VC6.0, what does this function do?
QuestionI couldn't execute the project !!! Please Help!membermecrop2 Dec '08 - 14:46 
First i must to say it is a very good thought project and i really need this project. Thanks for it.
But when i try to execute your code.I opened the "Ping and Tracert.sln" and i couldn't execute it,there were 2 errors and an information box.
First i have this;
These projects are out of date;
Tracert- ReleaseWin32
Ping- ReleaseWin32
And 2 errors in the code at these lines;
1-)for (int i = 2; i < argc; ++i)
2-)for (i = 0; i < nLen; i = i + 2)
And at error link i have this message;
Error spawning 'cmd.exe'.
Please Help ME. I am using Microsoft Visual Studio 2005.
GeneralVista business version will failmemberowenskao25 Apr '08 - 2:35 
Dear:
I try this ping ok on XP platform and Vista professional version.
But in Vista business version, it always fail....
Could you modify this code to let Vista business version can work?
GeneralRe: Vista business version will failmemberyouval vaknin28 Sep '08 - 5:20 
Disable windows vista firewall (or create a specific exception for your application)
QuestionNetwork ping ????memberwege26 Feb '08 - 23:42 
Hi.
Great example...;)
I'm wondering, how to make this "network ping", like some linux systems have...
Eg. "ping 192.168.1.0" and then all clients in network will answer...
 
Wege
AnswerRe: Network ping ????memberHitesh Sharma28 Feb '08 - 3:43 
Hello Wege,
 
I'm not aware of Linux systems Frown | :( I will try to educate myself on this.
 
BTW, I guess if you just want to enumerate the machines on the network then there are a few Win32 API's out there for this.
 
Thanks.
 
Best Regards,
Hitesh
AnswerRe: Network ping ????member emilio_grv 10 Mar '08 - 7:47 
Hi, wedge.
"network ping" is not only a feature of a product but a something required all node to implement.
 
Actually, if the mask associated to "192.168.1.0" is "255.255.255.0", then a ping is translated by the TCP/IP driver to a MAC broadcast all ping servers matching the subnet broadcast should respond.
 
The problem, here, is that this sample doesn't check from multiple answers, and from answers coming from IP-s other than the ping-ed one, so you don't see the answers.
 

2 bugs found.
> recompile ...
65534 bugs found.
D'Oh! | :doh:


GeneralRe: Network ping ????memberwege10 Mar '08 - 22:51 
Hi, emilio_grv.
I agree that, cause those linux broadcast pings work in every network.
I have also seen some freeware Windows network ping software.
This feature might be useful to add to some software component.
Maybe I have time in summer study to howto do it...
Have you or anyone tested this, if you send eg. ping 192.168.1.0(this or normal ping), will you see answers in Wireshark/Ethereal....?
 
WegeConfused | :confused:
GeneralRe: Network ping ????member emilio_grv 12 Mar '08 - 0:48 
wege wrote:
Have you or anyone tested this, if you send eg. ping 192.168.1.0(this or normal ping), will you see answers in Wireshark/Ethereal....?

 
This depends of at least two independent and unrelated things:
  • On the sender: the TCP/IP driver must be able to translate the network address into a MAC broadcast. That's true for W2K on, but - for example - for certain W3.11 drivers, and for certain linux drivers (yes: does not depend on linux itself, but on the driver of the NIC)
  • On the responder: when receiving a MAC broadcast to a "subnet" IP address the driver must not drop the packet.
If these two conditions aren't met, you can write whatever program, but you'll never be able to get what you want.
If these conditions are met, then, yes: this program sends a "network ping" and the supporting nodes will respond. It just drops the answers.
 

2 bugs found.
> recompile ...
65534 bugs found.
D'Oh! | :doh:


GeneralEmprovement suggestionsmember emilio_grv 22 Feb '08 - 2:02 
Very nice article with pretty simple examples to make programmers aware of certain "raw" networking mechanism. A good 5, but that can be improved with a more proper networking terminology.
 
Ping is an old Unix utility which is pretty useful even today
That' not exact: ping is an utility to test the "ECHO" functionality of ICMP. It is implemented historically as a unix utility, but it is completely useless if all networked node don't implement a "listener and responder" (a server, never less).
Actually your code works just because the ping-ed node cooperates by answering, and this is because the most of the TCP/IP implementation have the ICMP-ECHO server implemented inside. Essentially ping does not test if "the network is alive" (that's a nonsense: a netowrk is an unlimited set of interconnected node; it is never completely alive nor dead) but if a given host is reachable and responding.
 
The basic idea behind tracert is that it sends ICMP requests and increments their TTL value in the IP header by one on each request
There are actually at least two flavors of tracert: one is "popular" in windows and linux systems (mainly: PCs) and behaves as you describe, another is popular on legacy UNIX (mainly IBM and Sun pre-solaris systems) and is not based on a "short living ping" but on a "short living impossible UDP connection attempt". The principle is similar, but firewalls and proxies may treat them with considerable differences. No one is "better": it depends which kind of networking environment are expected to be traversed.
 
When the first request reaches the next host, the host ignores the request and sends an ICMP time exceeded response. By seeing this response we are able to determine the host and thus able to produce a lists of hosts by sending incremented TTL values
This terminology sounds "arcaic": although still popular in academics environments - especially with strong UNIX culture - all network engineers distinguish from "host" (final destination of a packet) and "router" (intermediate node, receiving packets also for others and re-sending them towards a more proper network interface and destination) Routing functionality (IP-forwarding) is not necessarily implemented / active on every host: tracert gives the list of IP routers that are traversed and that are also ICMP servers.
 
Hope this can be useful to anyone to better understand networking issues.
 

2 bugs found.
> recompile ...
65534 bugs found.
D'Oh! | :doh:


GeneralRe: Emprovement suggestionsmemberHitesh Sharma22 Feb '08 - 2:25 
Thanks, emilio_grv Smile | :) .
 
I was too lazy or wanted to keep things as simple to understand as possible Unsure | :~ . Anyways, you have explained the stuff so many thanks Smile | :) .
GeneralMessage BodymvpJeffrey Walton22 Feb '08 - 1:10 
Hi Hitesh,
 
Very nice. I like these articles.
 
//Fill the message with some arbitrary value
memset (pSendBuffer + sizeof (ICMPheader), 'x', nMessageSize);

I believe both Windows and Linux place the PID in the message body of the ICMP request.
 
Jeff
GeneralRe: Message BodymemberHitesh Sharma22 Feb '08 - 2:36 
Hello Jeff,
 
Thanks for your kind remarks.
 
Actually, PID is used in the Transaction ID field of the ICMP request. However, I used a random number for the Transaction ID. The message data in the ICMP request contains some arbitiary charachters depending on the implementation, for example Ping on Windows XP fills charachters from a-w.
 
Thanks.
 
Best Regards,
Hitesh
GeneralRe: Message Bodymember ThatsAlok 10 Mar '08 - 5:28 
nice article dude!
 

"Opinions are neither right nor wrong. I cannot change your opinion. I can, however, change what influences your opinion." - David Crow
Never mind - my own stupidity is the source of every "problem" - Mixture

cheers,
Alok Gupta
VC Forum Q&A :- I/IV
Support CRY- Child Relief and You

GeneralRe: Message BodymemberHitesh Sharma10 Mar '08 - 7:29 
Thank you, Alok Smile | :) .
 
You might not remember but I long back wrote an email to you asking how to pick up in COM. It has been really long time now, but I still remember it Big Grin | :-D .
GeneralRe: Message Bodymember ThatsAlok 10 Mar '08 - 7:40 
ohh i got it.. your mail still reside in my mail box Wink | ;-) .. sorry i really don't able to recognize you... really very great article. if you remember, i said earlier also that you really a genius Wink | ;-)
 

"If it were machines, the pair_programming seem to work, but for humans it is pair_crackdown that seems to work! " - Nisamudheen

"Opinions are neither right nor wrong. I cannot change your opinion. I can, however, change what influences your opinion." - David Crow
Never mind - my own stupidity is the source of every "problem" - Mixture

cheers,
Alok Gupta
VC Forum Q&A :- I/IV
Support CRY- Child Relief and You

GeneralRe: Message BodymemberHitesh Sharma10 Mar '08 - 12:03 
Alok,
 
Thanks for your kind words bur I wish I was smart Smile | :) .
 
Drop me a mail and would love to know where you work and what you upto now.
 
Regards,
Hitesh
GeneralRe: Message Bodymember ThatsAlok 10 Mar '08 - 16:55 
Hitesh Sharma wrote:
Drop me a mail and would love to know where you work and what you upto now.

hai i live in gurgaon ! what about you!
 

"If it were machines, the pair_programming seem to work, but for humans it is pair_crackdown that seems to work! " - Nisamudheen

"Opinions are neither right nor wrong. I cannot change your opinion. I can, however, change what influences your opinion." - David Crow
Never mind - my own stupidity is the source of every "problem" - Mixture

cheers,
Alok Gupta
VC Forum Q&A :- I/IV
Support CRY- Child Relief and You

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

Permalink | Advertise | Privacy | Mobile
Web02 | 2.6.130523.1 | Last Updated 21 Feb 2008
Article Copyright 2008 by Hitesh Sharma
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid