Ping Part II: Adventures in Socket programming using System.Net
If you see the ping utility that comes with your OS, you will notice that it has many options. However, the one we are going to develop will just take one argument:
C:\ping>ping <hostname> | <ipaddress>
Example: ping www.contoso.com
Example: ping 127.0.0.1
Let us start out by looking at requirements of the Ping client. Basically, the task of the tool is to find out if a specified server is alive, and on the network. The way it achieves this is by sending an “echo” packet to the server. The server responds with an “echo” response. If the server responds within a certain time interval, then we can assume that there is network connectivity from the client to the server. If it doesn’t respond, then it could indicate a variety of things that could be wrong, for eg:
- The destination server is not up.
- The destination server is up but does not have networking enabled.
- The destination server is up and on the network, but is configured to drop Ping requests
- Etc.
We will not concern ourselves with “why” a server is not responding to the Ping request, as it could be another topic in itself.
Where was I? Oh yeah, the Ping client. It so happens that we have a protocol (ICMP) that has the echo request and reply operations that we need to implement the Ping client. Read the ICMP RFC to get information about all the features offered by the ICMP protocol. As we know, the ICMP protocol is at the same layer as the IP protocol in the OSI layering scheme.
For the ping utility, we will be using the ICMP Echo Request and response messages. The client will send the ICMP echo request message, and the server will reply with an Echo response message. If you look at the Rfc, it describes how an RFC message looks like:
Offset |
Field Name |
Size (octets) |
Description |
0 |
Type |
1 |
Specified type of the operation. It should be set to 8 for request message and 0 for reply messages |
8 |
Code |
1 |
This field is Zero. |
16 |
Checksum |
2 |
One’s complement checksum of the ICMP message |
32 |
Identifier |
2 |
Used to correlate request and reply |
48 |
Sequence Number |
2 |
Used to correlate request and reply |
64 |
Data |
Variable |
Optional data to be sent with the request. This must be sent back by the server. |
Looking at the table above, we can come up with a C# class that corresponds to what the packet will look like:
public class ICMP_PACKET
{
public Byte i_type; // type of message
public Byte i_code; // sub code
public UInt16 i_cksum; // ones complement checksum of header
public UInt16 i_id; // identifier
public UInt16 i_seq; // sequence number
public Byte[] data;
}
I used Ethereal to sniff the network while doing a Ping, to see what a Ping request/response looks like on the wire. The request packet looks like this:
0020 59 99 08 00 b5 58 02 00 96 03 61 62 63 64 65 66 Y....X.. ..abcdef
0030 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 ghijklmn opqrstuv
0040 77 61 62 63 64 65 66 67 68 69 wabcdefghi
Let us dissect the packet:
i_type = 0x08 ( Echo Request)
i_code = 0x00
i_cksum = 0xb558
i_id = 0x0200
i_seq = 0x9603
data = 32 bytes ( abcedf…. )
The ICMP echo reply looks as follows:
0020 59 9a 00 00 bd 58 02 00 96 03 61 62 63 64 65 66 Y....X.. ..abcdef
0030 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 ghijklmn opqrstuv
0040 77 61 62 63 64 65 66 67 68 69 wabcdefghi
Let us dissect the packet:
i_type = 0x00 ( Echo Reply)
i_code = 0x00
i_cksum = 0xbd58
i_id = 0x0200
i_seq = 0x9603
data = 32 bytes ( abcedf…. )
Given what we have learned so far, we can now come up with a skeleton for the program that we will develop:
using System;
using System.Text;
using System.Globalization;
namespace ping
{
public class ICMP_PACKET
{
Byte i_type; // type of message
Byte i_code; // sub code
UInt16 i_cksum; // ones complement checksum of header
UInt16 i_id; // identifier
UInt16 i_seq; // sequence number
Byte[] data;
private const int headerLength = 8;
// Total size of the packet (header + payload)
public int Length
{
get { return HeaderLength + PayloadLength; }
}
// Size of the packet header (excluding data)
public int HeaderLength
{
get { return headerLength; }
}
// Size of the payload
public int PayloadLength
{
get { return (data == null) ? 0 : data.Length; }
}
public byte PacketType
{
get { return i_type; }
}
public byte Code
{
get { return i_code; }
}
public UInt16 Identifier
{
get { return i_id; }
}
public UInt16 SequenceNumber
{
get { return i_seq; }
}
public ICMP_PACKET(Byte kind, Byte code, UInt16 id, UInt16 seq, byte[] data)
{
this.i_type = kind;
this.i_code = code;
this.i_id = id;
this.i_seq = seq;
this.data = data;
this.i_cksum = 0;
}
public static ICMP_PACKET CreateRequestPacket(UInt16 id, UInt16 seq, byte[] data)
{
return new ICMP_PACKET(8, 0, id, seq, data);
}
};
class Program
{
static void Main(string[] args)
{
if (args.Length != 1)
{
Usage();
}
if (0 == String.Compare(args[0], "/?", true, CultureInfo.InvariantCulture)
|| 0 == String.Compare(args[0], "-h", true, CultureInfo.InvariantCulture)
|| 0 == String.Compare(args[0], "-?", true, CultureInfo.InvariantCulture))
{
Usage();
}
}
static void Usage()
{
Console.WriteLine("ping <hostname> | <ipaddress>");
Console.WriteLine("\tExample: ping www.contoso.com");
Console.WriteLine("\tExample: ping 127.0.0.1");
Environment.Exit(0);
}
}
}
Comments
- Anonymous
October 23, 2005
This is TrackBack: http://nayyeri.net/archive/2005/10/23/243.aspx - Anonymous
March 24, 2006
PingBack from http://nayyeri.net/archive/2005/10/23/243.aspx - Anonymous
July 15, 2006
Feroze Daushas posted about socket programmingusing System.Net.Sockets and implemented a ping client