no bytes returned on raw socket;trying to implement ping

  • Thread starter Thread starter Rob Snell
  • Start date Start date
R

Rob Snell

Hi all,

I hope this is the correct group, if not, please let me know. I am
trying to implement ping. I found some excellent code that builds a
raw socket from scratch and it works great against localhost but
nothing external. The complete code is below but I seem to timeout
trying to receive bytes. When I look to see how many bytes I have in
the buffer, I don't have any.

Any ideas would be greatly appreciated.

'the form
Option Explicit On

Public Class TestPing
Inherits System.Windows.Forms.Form

#Region " Windows Form Designer generated code "

Public Sub New()

MyBase.New()

'This call is required by the Windows Form Designer.
InitializeComponent()


End Sub

'Form overrides dispose to clean up the component list.
Protected Overloads Overrides Sub Dispose(ByVal disposing As
Boolean)
If disposing Then
If Not (components Is Nothing) Then
components.Dispose()
End If
End If
MyBase.Dispose(disposing)
End Sub

'Required by the Windows Form Designer
Private components As System.ComponentModel.IContainer

'NOTE: The following procedure is required by the Windows Form
Designer
'It can be modified using the Windows Form Designer.
'Do not modify it using the code editor.
Friend WithEvents RichTextBox1 As System.Windows.Forms.RichTextBox
Friend WithEvents Button1 As System.Windows.Forms.Button
<System.Diagnostics.DebuggerStepThrough()> Private Sub
InitializeComponent()
Me.RichTextBox1 = New System.Windows.Forms.RichTextBox
Me.Button1 = New System.Windows.Forms.Button
Me.SuspendLayout()
'
'RichTextBox1
'
Me.RichTextBox1.Anchor =
CType((((System.Windows.Forms.AnchorStyles.Top Or
System.Windows.Forms.AnchorStyles.Bottom) _
Or System.Windows.Forms.AnchorStyles.Left) _
Or System.Windows.Forms.AnchorStyles.Right),
System.Windows.Forms.AnchorStyles)
Me.RichTextBox1.Location = New System.Drawing.Point(8, 8)
Me.RichTextBox1.Name = "RichTextBox1"
Me.RichTextBox1.Size = New System.Drawing.Size(584, 416)
Me.RichTextBox1.TabIndex = 0
Me.RichTextBox1.Text = ""
'
'Button1
'
Me.Button1.Location = New System.Drawing.Point(8, 432)
Me.Button1.Name = "Button1"
Me.Button1.Size = New System.Drawing.Size(232, 23)
Me.Button1.TabIndex = 1
Me.Button1.Text = "Ping"
'
'TestPing
'
Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
Me.ClientSize = New System.Drawing.Size(600, 462)
Me.Controls.Add(Me.Button1)
Me.Controls.Add(Me.RichTextBox1)
Me.Name = "TestPing"
Me.Text = "TestPing"
Me.ResumeLayout(False)

End Sub

#End Region

Private Sub TestPing()
Try
Me.Show()

Application.DoEvents()

RichTextBox1.Clear()

Dim vChk As String
Dim vHost As String

For ChkLoop As Integer = 0 To 2
Select Case ChkLoop
Case 0
vHost = "localhost"
Case 1
vHost = "yahoo.com"
Case 2
vHost = "192.168.10.110"
Case 3
vHost = "www.google.com"
Case 4
vHost = "www.znet.com"
Case 5
vHost = "www.google.com"
Case 6
vHost = "localhost"
Case 7
vHost = "www.compaq.com"
End Select

Try
Dim cPing As ClsPing
cPing = New ClsPing
cPing.Host = (vHost)
vChk = CStr(cPing.Ping(RichTextBox1))
RichTextBox1.AppendText(vHost & ": ticks taken:" &
vChk & ControlChars.NewLine)
Application.DoEvents()
Catch ex As Exception
If vChk = -2 Then
RichTextBox1.AppendText(vHost & ": unable to
ping server." & ControlChars.NewLine)
End If
RichTextBox1.AppendText(ex.ToString() &
ControlChars.NewLine)
Application.DoEvents()
End Try
Next

Catch ex As Exception
RichTextBox1.AppendText(Err.Description &
ControlChars.NewLine)
Application.DoEvents()
End Try
End Sub

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e
As System.EventArgs) Handles Button1.Click
TestPing()
End Sub

End Class

'the class
Option Explicit On

Imports System
Imports System.Net
Imports System.Net.Sockets

Public Class ClsPing

Public Structure stcError
Dim Number As Integer
Dim Description As String
End Structure

Public Const PING_SUCCESS As Long = 0
Public Const PING_ERROR As Long = (-1)
Public Const PING_ERROR_BASE As Long = &H8000
Public Const PING_ERROR_HOST_NOT_FOUND As Long = PING_ERROR_BASE +
1
Public Const PING_ERROR_SOCKET_DIDNT_SEND As Long =
PING_ERROR_BASE + 2
Public Const PING_ERROR_HOST_NOT_RESPONDING As Long =
PING_ERROR_BASE + 3
Public Const PING_ERROR_TIME_OUT As Long = PING_ERROR_BASE + 4

Private Const ICMPType_ECHO As Integer = 8
Private Const SOCKET_ERROR As Integer = -1

Private udtError As stcError

Private Const intPortICMP As Integer = 7
Private Const intBufferHeaderSize As Integer = 8
Private Const intPackageHeaderSize As Integer = 28

Private intDataSize As Byte
Private ipheLocalHost As System.Net.IPHostEntry
Private ipheHost As System.Net.IPHostEntry

Public Property DataSize() As Byte
Get
Return intDataSize
End Get
Set(ByVal Value As Byte)
intDataSize = Value
End Set
End Property

Public ReadOnly Property Identifier() As Byte
Get
Return 0
End Get
End Property

Public ReadOnly Property Sequence() As Byte
Get
Return 0
End Get
End Property

Public ReadOnly Property LocalHost() As System.Net.IPHostEntry
Get
Return ipheLocalHost
End Get
End Property

Public Property Host() As Object
Get
Return ipheHost
End Get
Set(ByVal Value As Object)
If (Value.GetType.ToString.ToLower = "system.string") Then
'*
'* Find the Host's IP address
'*
Try
ipheHost = System.Net.Dns.GetHostByName(Value)
'MsgBox(System.Net.Dns.GetHostByName(Value))
Catch
ipheHost = Nothing
udtError.Number = PING_ERROR_HOST_NOT_FOUND
udtError.Description = "Host " & ipheHost.HostName
& " not found."
End Try
ElseIf (Value.GetType.ToString.ToLower =
"system.net.iphostentry") Then
ipheHost = (Value)
Else
ipheHost = Nothing
End If
End Set
End Property

'*
'* Class Constructor
'*
Public Sub New()
'*
'* Initializes the parameters
'*
intDataSize = 32
udtError = New stcError

'*
'* Get local IP and transform in EndPoint
'*
ipheLocalHost =
System.Net.Dns.GetHostByName(System.Net.Dns.GetHostName())

'*
'* Defines Host
'*
ipheHost = Nothing
End Sub

'*
'* Function : PingHost
'* Author : Paulo dos Santos Silva Jr
'* Date : 05/09/2002
'* Objective : Pings a specified host
'*
'* Parameters : Host as String
'*
'* Returns : Response time in milliseconds
'* (-1) if error
'*
Public Function Ping(ByVal rtb As RichTextBox) As Long
Dim intCount As Integer
Dim aReplyBuffer(255) As Byte

Dim intNBytes As Integer = 0

Dim intEnd As Integer
Dim intStart As Integer

Dim epFrom As System.Net.EndPoint
Dim epServer As System.Net.EndPoint
Dim ipepServer As System.Net.IPEndPoint
Dim sckSocket As System.Net.Sockets.Socket
'*
'* Transforms the IP address in EndPoint
'*
ipepServer = New
System.Net.IPEndPoint(ipheHost.AddressList(0), 0)
epServer = CType(ipepServer, System.Net.EndPoint)

epFrom = New
System.Net.IPEndPoint(ipheLocalHost.AddressList(0), 0)

'*
'* Builds the packet to send
'*
DataSize = Convert.ToByte(DataSize + intBufferHeaderSize)

'*
'* The packet must by an even number, so if the DataSize is
and odd number adds one
'*
If (DataSize Mod 2 = 1) Then
DataSize += Convert.ToByte(1)
End If
Dim aRequestBuffer(DataSize - 1) As Byte

'*
'* --- ICMP Echo Header Format ---
'* (first 8 bytes of the data buffer)
'*
'* Buffer (0) ICMP Type Field
'* Buffer (1) ICMP Code Field
'* (must be 0 for Echo and Echo Reply)
'* Buffer (2) checksum hi
'* (must be 0 before checksum calc)
'* Buffer (3) checksum lo
'* (must be 0 before checksum calc)
'* Buffer (4) ID hi
'* Buffer (5) ID lo
'* Buffer (6) sequence hi
'* Buffer (7) sequence lo
'* Buffer (8)..(n) Ping Data
'*

'*
'* Set Type Field
'*
aRequestBuffer(0) = Convert.ToByte(ICMPType_ECHO)

'*
'* Set ID field
'*
BitConverter.GetBytes(Identifier).CopyTo(aRequestBuffer, 4)

'*
'* Set Sequence
'*
BitConverter.GetBytes(Sequence).CopyTo(aRequestBuffer, 6)

'*
'* Load some data into buffer
'*
Dim i As Integer
For i = 8 To DataSize - 1
aRequestBuffer(i) = Convert.ToByte(i Mod 8)
Next i

'*
'* Calculate Checksum
'*
Call CreateChecksum(aRequestBuffer, DataSize,
aRequestBuffer(2), aRequestBuffer(3))


'*
'* Try send the packet
'*
Try
'*
'* Create the socket
'*
sckSocket = New System.Net.Sockets.Socket( _

Net.Sockets.AddressFamily.InterNetwork, _

Net.Sockets.SocketType.Raw, _

Net.Sockets.ProtocolType.Icmp)
sckSocket.Blocking = False

'rjs
sckSocket.Bind(epFrom)

'*
'* Records the Start Time
'*
intStart = System.Environment.TickCount
sckSocket.SendTo(aRequestBuffer, 0, DataSize,
SocketFlags.None, ipepServer)

Dim bTimedOut As Boolean = False
Dim iMaxTicks As Integer = 30 * 1000

'use Available instead of all this
While (Not bTimedOut) And (intNBytes = 0)
Try
intNBytes = sckSocket.ReceiveFrom(aReplyBuffer,
SocketFlags.Peek, epServer)
Catch ex As SocketException
If ex.ErrorCode <> 10035 Then
Throw ex
End If
End Try

If System.Environment.TickCount >= (intStart +
iMaxTicks) Then
bTimedOut = True
rtb.AppendText("Timeout. Start:" & intStart & "
Timeout@" & intStart + iMaxTicks & " Now:" &
System.Environment.TickCount & ControlChars.NewLine)
End If
End While

rtb.AppendText("Peek: " & intNBytes.ToString & " bytes" &
ControlChars.NewLine)

If Not bTimedOut Then
intNBytes = sckSocket.ReceiveFrom(aReplyBuffer,
SocketFlags.None, epServer)
End If
intEnd = System.Environment.TickCount

If (intNBytes > 0) Then
'*
'* Informs on GetLastError the state of the server
'*
udtError.Number = (aReplyBuffer(19) * &H100) +
aReplyBuffer(20)
Select Case aReplyBuffer(20)
Case 0 : udtError.Description = "Success"
Case 1 : udtError.Description = "Buffer too Small"
Case 2 : udtError.Description = "Destination
Unreahable"
Case 3 : udtError.Description = "Dest Host Not
Reachable"
Case 4 : udtError.Description = "Dest Protocol Not
Reachable"
Case 5 : udtError.Description = "Dest Port Not
Reachable"
Case 6 : udtError.Description = "No Resources
Available"
Case 7 : udtError.Description = "Bad Option"
Case 8 : udtError.Description = "Hardware Error"
Case 9 : udtError.Description = "Packet too Big"
Case 10 : udtError.Description = "Reqested Timed
Out"
Case 11 : udtError.Description = "Bad Request"
Case 12 : udtError.Description = "Bad Route"
Case 13 : udtError.Description = "TTL Exprd In
Transit"
Case 14 : udtError.Description = "TTL Exprd
Reassemb"
Case 15 : udtError.Description = "Parameter
Problem"
Case 16 : udtError.Description = "Source Quench"
Case 17 : udtError.Description = "Option too Big"
Case 18 : udtError.Description = "Bad Destination"
Case 19 : udtError.Description = "Address Deleted"
Case 20 : udtError.Description = "Spec MTU Change"
Case 21 : udtError.Description = "MTU Change"
Case 22 : udtError.Description = "Unload"
Case Else : udtError.Description = "General
Failure"
End Select
End If

sckSocket.Close()

Return (intEnd - intStart)
Catch oExcept As Exception
rtb.AppendText(oExcept.ToString())
sckSocket.Close()
Return -2
End Try
End Function

Public Function GetLastError() As stcError
Return udtError
End Function

' ICMP requires a checksum that is the one's
' complement of the one's complement sum of
' all the 16-bit values in the data in the
' buffer.
' Use this procedure to load the Checksum
' field of the buffer.
' The Checksum Field (hi and lo bytes) must be
' zero before calling this procedure.
Private Sub CreateChecksum(ByRef data() As Byte, ByVal Size As
Integer, ByRef HiByte As Byte, ByRef LoByte As Byte)
Dim i As Integer
Dim chk As Integer = 0

For i = 0 To Size - 1 Step 2
chk += Convert.ToInt32(data(i) * &H100 + data(i + 1))
Next

chk = Convert.ToInt32((chk And &HFFFF&) + Fix(chk / &H10000&))
chk += Convert.ToInt32(Fix(chk / &H10000&))
chk = Not (chk)

HiByte = Convert.ToByte((chk And &HFF00) / &H100)
LoByte = Convert.ToByte(chk And &HFF)
End Sub
End Class
 
I am still stumped on this problem. Is it possible that something is
screwed up with the packet and how I am constructing it such that it
will work against localhost but not other machines? Is that a
possibility?
 
Okay, my apologies but I don't know what I did but I fixed it...in
some circumstances. I was still getting intermittent timeouts with
some ping targets. However, I noticed while testing that if I had a
ping -n 100 yahoo.com running (from the command prompt using ping.exe)
that I would have success every time! This is clearly a clue that
will lead to solving my problem but what does it mean?
 
A lot of external sites will setup their network infrastructure to not
respond to ping requests.

Also, in these cases, to debug, you should run a network sniffer like Netmon
(included in windows). If the packet is not formatted correctly, or the
checksum is wrong, then either the source or destination will drop the
packet. Netmon will show you if the packet checksum is wrong.

feroze.
======================
this posting is provided as-is.
 
Feroze,

Thanks, I will try that. Something else I should have said more
explicitly is that the sites I am pinging work fine with ping.exe via
the command line.

Cheers,
Rob
 
Feroze et al,

I have a theory as to what is going on. You all can respond and tell
me if you think I am on the right track or not.

I think the reason that I can only get it to work when I have a
command-line ping.exe running in the background is this: since ICMP
is a connectionless (port-less) protocol, I am actually receiving the
echos from the ping that is running in the background. Since I am not
checking the sequence number or source, I am interpreting this in my
app as a received packet for me. My packets must be somehow malformed
and thus are not getting an echo.

The one thing this doesn't explain is why it works against localhost
though, because it always seemed to work against localhost. I guess
my theory there is that the ip header is moot since I am just pinging
myself, the router, the modem, and a machine on my network. This
might point to some sort of problem with the ip header.

What do you all think?

Cheers,
Rob
 
Feroze,

By the way, I looked for netmon on my windows 2000 PE machine and
didn't find it. I saw a couple of headers in VC but that is it. I
also looked on my Windows XP Professional machine and nothing there
either. Looking on the internet, Netmon seems to be a tool published
by Distinct.

Cheers,
Rob
 
I got a better piece of code from codeproject. I think I was building
the ICMP packet incorrectly. Of course, now I have another problem.
I was trying to send a sequence of pings to see if I was dropping any
and exactly every other one seems to time out.
 
Can you ping from a command prompt without losing any packets? It may be a
network issue as opposed to a coding issue.

John
 
John,

Yes, I can ping from the command line without losing any packets. It
is very strange. Exactly every other packet fails.

Her is the code that calls it:

Private Function QOSTest(ByVal lPings As Long, ByVal sHost As
String) As Double
Try
Dim cPing As Trace = New Trace
Dim iRet As Integer
Dim lEchoRcvd As Long = 0
Dim lEchoDropped As Long = 0

Dim j As Integer
For j = 0 To lPings - 1
iRet = cPing.ping(sHost, Convert.ToUInt16(j))

If iRet >= 0 Then
lEchoRcvd += 1
Else
lEchoDropped += 1
End If

Logrtb(iRet & "ms (" & lEchoRcvd & "/" & lEchoRcvd +
lEchoDropped & ")" & ControlChars.NewLine)
Next

Return CDbl(lEchoRcvd) / CDbl(lPings)
Catch ex As Exception
MsgBox(ex.ToString)
Throw ex
End Try
End Function

And here is the class that is being invoked:
/******************************************************************************************************************
* Class: Trace
* Description: Traces path of an ip packet with its respond time
* Author: Sanjay Ahuja
* Date: 5/15/2002
* Copyright©: © 2002, Sanjay Ahuja ([email protected]). Use it as
you want till you leave my name intact
* Modified: R. Snell 09/09/04 ([email protected])
* Modified main to be a public function that exposes ping and
returns ms or -1 on unreachable.
* Modified to be in own assembly so I can call it from vb.net app
* Modified to send in sequence_id.
/******************************************************************************************************************/


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
public 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
public struct REQUEST
{
public ICMP m_icmp;
public byte []m_data;
}

public class Trace
{
const int PACKET_SIZE = 32;

public static long ping(string sHost, ushort seq)
{
try
{
//Create Raw ICMP Socket
Socket s= new Socket(AddressFamily.InterNetwork, SocketType.Raw,
ProtocolType.Icmp);
//destination
IPEndPoint ipdest= new
IPEndPoint(Dns.Resolve(sHost).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;
ip.id = (ushort)DateTime.Now.Millisecond; //any number you feel is
kinda unique :)
ip.seq = seq;

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 = (byte)'S';
}

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

Byte[] ByteRecv = new Byte[256];
//Socket options to set TTL and Timeouts
//s.SetSocketOption(SocketOptionLevel.IP,
SocketOptionName.IpTimeToLive, 100);
s.SetSocketOption(SocketOptionLevel.Socket,
SocketOptionName.SendTimeout,1000);
s.SetSocketOption(SocketOptionLevel.Socket,
SocketOptionName.ReceiveTimeout,1000);

//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");
return -1;
}

//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");
return -1;
}

Console.WriteLine("IP= {0,-20} Time=
{1,3}ms",((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))
{
Console.WriteLine("1) iRet({0})==PACKET_SIZE({1})+ 8 +20...",
iRet, PACKET_SIZE);
Console.WriteLine("2) Bytes 24-25 in recv buf ({0})== Bytes 4-5 in
send buf ({1})...", BitConverter.ToInt16(ByteRecv, 24),
BitConverter.ToInt16(ByteSend,4));
Console.WriteLine("3) Byte 20 in recv buf({0}) ==
ICMPConstants.ICMP_ECHOREPLY({1})...", ByteRecv[20],
ICMPConstants.ICMP_ECHOREPLY);
Console.WriteLine("unexpected reply, quitting...");
return -1;
}
//time out
//Console.WriteLine("Type({0})...", ByteRecv[20]);
if(ByteRecv[20] == ICMPConstants.ICMP_TIMEEXCEEDED)
{
Console.WriteLine("ICMPConstants.ICMP_TIMEEXCEEDED...");
return -1;
}

return ts.Milliseconds;
}
catch(SocketException e)
{
Console.WriteLine(e.Message);
throw e;
//return -1;
}
catch(Exception e)
{
Console.WriteLine(e.Message);
throw e;
//return -1;
}
}

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;

//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;
}
}
 
You should run a netowkr sniffer and see what it shows. Maybe the echo
response is not being sent by the peer ?

--
feroze

-----------------
This posting is provided as-is. It offers no warranties and assigns no
rights.

See http://weblogs.asp.net/feroze_daud for System.Net related posts.
----------------

Rob Snell said:
John,

Yes, I can ping from the command line without losing any packets. It
is very strange. Exactly every other packet fails.

Her is the code that calls it:

Private Function QOSTest(ByVal lPings As Long, ByVal sHost As
String) As Double
Try
Dim cPing As Trace = New Trace
Dim iRet As Integer
Dim lEchoRcvd As Long = 0
Dim lEchoDropped As Long = 0

Dim j As Integer
For j = 0 To lPings - 1
iRet = cPing.ping(sHost, Convert.ToUInt16(j))

If iRet >= 0 Then
lEchoRcvd += 1
Else
lEchoDropped += 1
End If

Logrtb(iRet & "ms (" & lEchoRcvd & "/" & lEchoRcvd +
lEchoDropped & ")" & ControlChars.NewLine)
Next

Return CDbl(lEchoRcvd) / CDbl(lPings)
Catch ex As Exception
MsgBox(ex.ToString)
Throw ex
End Try
End Function

And here is the class that is being invoked:
/***************************************************************************
***************************************
* Class: Trace
* Description: Traces path of an ip packet with its respond time
* Author: Sanjay Ahuja
* Date: 5/15/2002
* Copyright©: © 2002, Sanjay Ahuja ([email protected]). Use it as
you want till you leave my name intact
* Modified: R. Snell 09/09/04 ([email protected])
* Modified main to be a public function that exposes ping and
returns ms or -1 on unreachable.
* Modified to be in own assembly so I can call it from vb.net app
* Modified to send in sequence_id.
/***************************************************************************
***************************************/


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
public 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
public struct REQUEST
{
public ICMP m_icmp;
public byte []m_data;
}

public class Trace
{
const int PACKET_SIZE = 32;

public static long ping(string sHost, ushort seq)
{
try
{
//Create Raw ICMP Socket
Socket s= new Socket(AddressFamily.InterNetwork, SocketType.Raw,
ProtocolType.Icmp);
//destination
IPEndPoint ipdest= new
IPEndPoint(Dns.Resolve(sHost).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;
ip.id = (ushort)DateTime.Now.Millisecond; //any number you feel is
kinda unique :)
ip.seq = seq;

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 = (byte)'S';
}

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

Byte[] ByteRecv = new Byte[256];
//Socket options to set TTL and Timeouts
//s.SetSocketOption(SocketOptionLevel.IP,
SocketOptionName.IpTimeToLive, 100);
s.SetSocketOption(SocketOptionLevel.Socket,
SocketOptionName.SendTimeout,1000);
s.SetSocketOption(SocketOptionLevel.Socket,
SocketOptionName.ReceiveTimeout,1000);

//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");
return -1;
}

//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");
return -1;
}

Console.WriteLine("IP= {0,-20} Time=
{1,3}ms",((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))
{
Console.WriteLine("1) iRet({0})==PACKET_SIZE({1})+ 8 +20...",
iRet, PACKET_SIZE);
Console.WriteLine("2) Bytes 24-25 in recv buf ({0})== Bytes 4-5 in
send buf ({1})...", BitConverter.ToInt16(ByteRecv, 24),
BitConverter.ToInt16(ByteSend,4));
Console.WriteLine("3) Byte 20 in recv buf({0}) ==
ICMPConstants.ICMP_ECHOREPLY({1})...", ByteRecv[20],
ICMPConstants.ICMP_ECHOREPLY);
Console.WriteLine("unexpected reply, quitting...");
return -1;
}
//time out
//Console.WriteLine("Type({0})...", ByteRecv[20]);
if(ByteRecv[20] == ICMPConstants.ICMP_TIMEEXCEEDED)
{
Console.WriteLine("ICMPConstants.ICMP_TIMEEXCEEDED...");
return -1;
}

return ts.Milliseconds;
}
catch(SocketException e)
{
Console.WriteLine(e.Message);
throw e;
//return -1;
}
catch(Exception e)
{
Console.WriteLine(e.Message);
throw e;
//return -1;
}
}

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;

//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;
}
}

"John Armenia" <[email protected]> wrote in message
Can you ping from a command prompt without losing any packets? It may be a
network issue as opposed to a coding issue.

John
 
Feroze,

Thanks for the suggestion. I installed ethereal and it works like a
champ. I did see slightly different behavior than I have seen before
in testing, namely of three packets sent, it wasn't the second one
that failed, but the third, which was strange.

I couldn't find anything funny looking in the decode. Maybe another
pair of eyes will help:
No. Time Source Destination
Protocol Info
30 2.679299 192.168.1.103 216.109.112.135 ICMP
Echo (ping) request

Frame 30 (74 bytes on wire, 74 bytes captured)
Arrival Time: Sep 15, 2004 01:30:43.374128000
Time delta from previous packet: 0.008961000 seconds
Time since reference or first frame: 2.679299000 seconds
Frame Number: 30
Packet Length: 74 bytes
Capture Length: 74 bytes
Ethernet II, Src: 00:e0:98:7f:30:f8, Dst: 00:04:5a:28:9b:6b
Destination: 00:04:5a:28:9b:6b (192.168.1.1)
Source: 00:e0:98:7f:30:f8 (192.168.1.103)
Type: IP (0x0800)
Internet Protocol, Src Addr: 192.168.1.103 (192.168.1.103), Dst Addr:
216.109.112.135 (216.109.112.135)
Version: 4
Header length: 20 bytes
Differentiated Services Field: 0x00 (DSCP 0x00: Default; ECN:
0x00)
0000 00.. = Differentiated Services Codepoint: Default (0x00)
.... ..0. = ECN-Capable Transport (ECT): 0
.... ...0 = ECN-CE: 0
Total Length: 60
Identification: 0x02b5 (693)
Flags: 0x00
0... = Reserved bit: Not set
.0.. = Don't fragment: Not set
..0. = More fragments: Not set
Fragment offset: 0
Time to live: 128
Protocol: ICMP (0x01)
Header checksum: 0x2d08 (correct)
Source: 192.168.1.103 (192.168.1.103)
Destination: 216.109.112.135 (216.109.112.135)
Internet Control Message Protocol
Type: 8 (Echo (ping) request)
Code: 0
Checksum: 0x4bc9 (correct)
Identifier: 0x7501
Sequence number: 0x0200
Data (32 bytes)

0000 00 04 5a 28 9b 6b 00 e0 98 7f 30 f8 08 00 45 00
...Z(.k....0...E.
0010 00 3c 02 b5 00 00 80 01 2d 08 c0 a8 01 67 d8 6d
..<......-....g.m
0020 70 87 08 00 4b c9 75 01 02 00 53 53 53 53 53 53
p...K.u...SSSSSS
0030 53 53 53 53 53 53 53 53 53 53 53 53 53 53 53 53
SSSSSSSSSSSSSSSS
0040 53 53 53 53 53 53 53 53 53 53 SSSSSSSSSS

Cheers,
Rob

Feroze said:
You should run a netowkr sniffer and see what it shows. Maybe the echo
response is not being sent by the peer ?

--
feroze

-----------------
This posting is provided as-is. It offers no warranties and assigns no
rights.

See http://weblogs.asp.net/feroze_daud for System.Net related posts.
----------------

Rob Snell said:
John,

Yes, I can ping from the command line without losing any packets. It
is very strange. Exactly every other packet fails.

Her is the code that calls it:

Private Function QOSTest(ByVal lPings As Long, ByVal sHost As
String) As Double
Try
Dim cPing As Trace = New Trace
Dim iRet As Integer
Dim lEchoRcvd As Long = 0
Dim lEchoDropped As Long = 0

Dim j As Integer
For j = 0 To lPings - 1
iRet = cPing.ping(sHost, Convert.ToUInt16(j))

If iRet >= 0 Then
lEchoRcvd += 1
Else
lEchoDropped += 1
End If

Logrtb(iRet & "ms (" & lEchoRcvd & "/" & lEchoRcvd +
lEchoDropped & ")" & ControlChars.NewLine)
Next

Return CDbl(lEchoRcvd) / CDbl(lPings)
Catch ex As Exception
MsgBox(ex.ToString)
Throw ex
End Try
End Function

And here is the class that is being invoked:
/***************************************************************************
***************************************
* Class: Trace
* Description: Traces path of an ip packet with its respond time
* Author: Sanjay Ahuja
* Date: 5/15/2002
* Copyright©: © 2002, Sanjay Ahuja ([email protected]). Use it as
you want till you leave my name intact
* Modified: R. Snell 09/09/04 ([email protected])
* Modified main to be a public function that exposes ping and
returns ms or -1 on unreachable.
* Modified to be in own assembly so I can call it from vb.net app
* Modified to send in sequence_id.
/***************************************************************************
***************************************/


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
public 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
public struct REQUEST
{
public ICMP m_icmp;
public byte []m_data;
}

public class Trace
{
const int PACKET_SIZE = 32;

public static long ping(string sHost, ushort seq)
{
try
{
//Create Raw ICMP Socket
Socket s= new Socket(AddressFamily.InterNetwork, SocketType.Raw,
ProtocolType.Icmp);
//destination
IPEndPoint ipdest= new
IPEndPoint(Dns.Resolve(sHost).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;
ip.id = (ushort)DateTime.Now.Millisecond; //any number you feel is
kinda unique :)
ip.seq = seq;

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 = (byte)'S';
}

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

Byte[] ByteRecv = new Byte[256];
//Socket options to set TTL and Timeouts
//s.SetSocketOption(SocketOptionLevel.IP,
SocketOptionName.IpTimeToLive, 100);
s.SetSocketOption(SocketOptionLevel.Socket,
SocketOptionName.SendTimeout,1000);
s.SetSocketOption(SocketOptionLevel.Socket,
SocketOptionName.ReceiveTimeout,1000);

//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");
return -1;
}

//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");
return -1;
}

Console.WriteLine("IP= {0,-20} Time=
{1,3}ms",((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))
{
Console.WriteLine("1) iRet({0})==PACKET_SIZE({1})+ 8 +20...",
iRet, PACKET_SIZE);
Console.WriteLine("2) Bytes 24-25 in recv buf ({0})== Bytes 4-5 in
send buf ({1})...", BitConverter.ToInt16(ByteRecv, 24),
BitConverter.ToInt16(ByteSend,4));
Console.WriteLine("3) Byte 20 in recv buf({0}) ==
ICMPConstants.ICMP_ECHOREPLY({1})...", ByteRecv[20],
ICMPConstants.ICMP_ECHOREPLY);
Console.WriteLine("unexpected reply, quitting...");
return -1;
}
//time out
//Console.WriteLine("Type({0})...", ByteRecv[20]);
if(ByteRecv[20] == ICMPConstants.ICMP_TIMEEXCEEDED)
{
Console.WriteLine("ICMPConstants.ICMP_TIMEEXCEEDED...");
return -1;
}

return ts.Milliseconds;
}
catch(SocketException e)
{
Console.WriteLine(e.Message);
throw e;
//return -1;
}
catch(Exception e)
{
Console.WriteLine(e.Message);
throw e;
//return -1;
}
}

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;

//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;
}
}

"John Armenia" <[email protected]> wrote in message
Can you ping from a command prompt without losing any packets? It may be a
network issue as opposed to a coding issue.

John

I got a better piece of code from codeproject. I think I was building
the ICMP packet incorrectly. Of course, now I have another problem.
I was trying to send a sequence of pings to see if I was dropping any
and exactly every other one seems to time out.
 
I am happy to announce that I have figured it out. Or at least I
think I have. I analyzed the differences between the ping.exe and my
program's packet dumps and discovered that ping was taking
significantly longer after receiving a response before sending the
next echo request. I simply put the thread to sleep for 1000 ms
before sending the next packet, and voila, works like a charm.

Thanks for all your help!
 
Back
Top