A ping utility is used to check the availability of a remote host. I wanted to implement the function in my project. But it is not so easy as I had expected. Since administrator/root privilege is required to create a raw socket under windows/linux, and this is not what I want.

Finally, I chose to utilize system’s ping via CreateProcess()/execve() function. Under windows, ping may used IcmpSendEcho() API to wrap the creation of a raw socket, and this does not require administrator privilege. But it is still not working when logged in as a guest. Under linux, ping is a +s(setuid) utility, which means it is always run with root privilege. Anyway, I still tried to implement ping by using raw socket and sending raw ICMP(Internet Control Message Protocol) messages.

A raw ICMP echo request message has a ICMP header, while a raw ICMP echo reply message has an additional IP header in front of the ICMP header. Say:

There are several ICMP message types defined in RFC 792, but we only care about the echo type. So here’s our definition of a IP header and a ICMP header:

Our customized ICMP echo request/reply definition with self-defined data field:

The raw socket is created with:

Sending a ICMP echo request:

We simply use the checksum algorithm found in the original ping program from Mike Muuss.

Now, receiving a ICMP echo reply:

You may have noticed the if/else clause in the receive function. The use_icmp_socket flag is used to tell which socket type is used when sending a ICMP message. In linux kernel 3.0, a new socket type is introduced to reduce the possibility to use a raw socket that only send ICMP echo messages. Thus, the classic ping utility can be no longer a +s(setuid) one. A ICMP socket can be created with:

Note the difference in the second parameter. A kernel parameter(/proc/sys/net/ipv4/ping_group_range) in comment above should be set to indicate which UID range is allowed to use a ICMP socket.

When using a raw socket, the TTL value is in the IP header. While, the TTL value is in the socket ancillary data when using a ICMP socket, the reply data does not contain IP header any more. And we must set a socket option explicitly to retrieve the TTL value:

Let’s put them all together:

All code compiles and works under Ubuntu 12.04(gcc4.6), Windows XP(VS2005) and Windows 7(VS2010) with administrator/root privilege. After enabling the ICMP socket parameter, root privilege is not required under linux. The output may look like:

Reference:
– RFC 791: http://tools.ietf.org/html/rfc791
– RFC 792: http://tools.ietf.org/html/rfc792
– Implement ping in C: http://www.ibm.com/developerworks/cn/linux/network/ping/
– Raw Socket and ICMP: http://courses.cs.vt.edu/cs4254/fall04/slides/raw_6.pdf
– Linux Kernel 3.0: http://kernelnewbies.org/Linux_3.0
– IPv4: Add ICMP Socket Kind: http://lwn.net/Articles/420800/
– Patch for Userspace ping: ftp://ftp.intelib.org/pub/segoon/iputils-ss020927-pingsock.diff
– Wine Implementation: http://fossies.org/dox/wine-1.4.1/icmp_8c_source.html