Listen to ICMP packets in C #

I have a SIP application that needs to send UDP packets to configure SIP calls. SIP has a timeout mechanism to resolve delivery failures. Another thing I would like to do is determine if the UDP socket is closed in order to wait for the 32 SIP retransmission interval.

The cases I'm talking about are when an attempt to send to a UDP socket causes the ICMP Destination Unreachable packet to be generated by the remote host. If I try to send a UDP packet to the host, but the port is not listening, I see an ICMP message returned using the packet tracer, but the question is, how to access this from my C # code?

I play with raw sockets, but so far I have not been able to receive ICMP packets for my program. The example below never receives a packet, even if ICMP messages arrive on my computer.

Socket icmpListener = new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.Icmp); icmpListener.Bind(new IPEndPoint(IPAddress.Any, 0)); byte[] buffer = new byte[4096]; EndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0); int bytesRead = icmpListener.ReceiveFrom(buffer, ref remoteEndPoint); logger.Debug("ICMPListener received " + bytesRead + " from " + remoteEndPoint.ToString()); 

Below is a wire track showing ICMP responses coming to my computer from an attempt to send a UDP packet from 10.0.0.100 (my computer) to 10.0.0.138 (my router) to a port that I know that it is not listening. My problem is how to use these ICMP packets to implement UDP failure, and not just wait for the application to time out after an arbitrary period?

ICMP responses to UDP send

+7
c # icmp
source share
7 answers

Almost 3 years later, and I came across http://www.codeproject.com/Articles/17031/A-Network-Sniffer-in-C , which gave me enough hints to help me find a solution for receiving ICMP packets in Windows 7 (don’t know about Vista what the original was about, but I suspect this solution will work).

Two key points: the socket must be bound to one specific IP address, not IPAddress.Any and the IOControl call, which sets the SIO_RCVALL flag.

 Socket icmpListener = new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.Icmp); icmpListener.Bind(new IPEndPoint(IPAddress.Parse("10.1.1.2"), 0)); icmpListener.IOControl(IOControlCode.ReceiveAll, new byte[] { 1, 0, 0, 0 }, new byte[] { 1, 0, 0, 0 }); byte[] buffer = new byte[4096]; EndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0); int bytesRead = icmpListener.ReceiveFrom(buffer, ref remoteEndPoint); Console.WriteLine("ICMPListener received " + bytesRead + " from " + remoteEndPoint); Console.ReadLine(); 

I also had to set up a firewall rule to receive ICMP Port Unreachable packets.

 netsh advfirewall firewall add rule name="All ICMP v4" dir=in action=allow protocol=icmpv4:any,any 
+13
source share

Icmp uses an identifier that seems to be different for each icmp "session (for each icmp socket). Thus, the response to the icmp packet not sent by the same socket is filtered out for you. That's why this piece of code will not work. (I'm not sure about this. This is just an assumption by looking at some ICMP traffic.)

You can just ping the host and see if you can reach it or not, and then try using SIP. However, this will not work if another host filters icmp.

An ugly (but working) solution uses winpcap . (Having it as the only working solutions seems too bad to be true.)

What I mean by using winpcap, you can capture ICMP traffic and then see if the captured packet got into your UDP packet, which is not available or not .

Here is an example of tcp packet capture: http://www.tamirgal.com/home/SourceView.aspx?Item=SharpPcap&File=Example6.DumpTCP.cs (This should not be too hard to do with ICMP.)

+3
source share

UPDATE: I think I'm going crazy ... This piece of code that you posted also works for me ...

The following code snippet works great for me (xp sp3):

 using System; using System.Net; using System.Net.Sockets; namespace icmp_capture { class Program { static void Main(string[] args) { IPEndPoint ipMyEndPoint = new IPEndPoint(IPAddress.Any, 0); EndPoint myEndPoint = (ipMyEndPoint); Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.Icmp); socket.Bind(myEndPoint); while (true) { /* //SEND SOME BS (you will get a nice infinite loop if you uncomment this) var udpClient = new UdpClient("192.168.2.199", 666); //**host must exist if it in the same subnet (if not routed)** Byte[] messagebyte = Encoding.Default.GetBytes("hi".ToCharArray()); int s = udpClient.Send(messagebyte, messagebyte.Length); */ Byte[] ReceiveBuffer = new Byte[256]; var nBytes = socket.ReceiveFrom(ReceiveBuffer, 256, 0, ref myEndPoint); if (ReceiveBuffer[20] == 3)// ICMP type = Delivery failed { Console.WriteLine("Delivery failed"); Console.WriteLine("Returned by: " + myEndPoint.ToString()); Console.WriteLine("Destination: " + ReceiveBuffer[44] + "." + ReceiveBuffer[45] + "." + ReceiveBuffer[46] + "." + ReceiveBuffer[47]); Console.WriteLine("---------------"); } else { Console.WriteLine("Some (not delivery failed) ICMP packet ignored"); } } } } } 
+3
source share

Just use the connected udp sockets and the OS will match icmp unreachable and will return an error in the udp socket.

Google for connected udp sockets.

+3
source share

There are several reports on the Internet that mention the issue of ICMP Port Unreachable packets that are no longer available for Vista.

The stack should return an exception to you when it receives ICMP. But this is not the case, at least on Vista. And so you are trying a workaround.

I don’t like the answers that say this is impossible, but it seems so. Therefore, I suggest you return to the original problem, which is a long timeout in SIP.

  • You can allow the user to set a timeout (hence the compliance with the specification).
  • You can start doing other things (for example, checking other proxies) before the timeout ends.
  • You can cache known bad addresses (but this requires good cache management.
  • If icmp and udp do not give the correct error messages, try tcp or another protocol. Just to get the information you want.

(Anything is possible, it can take a lot of resources.)

+2
source share

I write this as a separate answer, since the details are completely different from the one I wrote earlier.

So, based on Kalmi's comment about the session ID, I wondered why I can open two ping programs on the same machine, and the answers do not intersect. They are both ICMP, so both use ports without ports. That means something in the IP stack, you need to know what socket these answers are for. For ping, it turns out that the identifier used in the ICMP packet data is part of ECHO REQUEST and ECHO REPLY.

Then I looked at this wikipedia comment about ICMP :

Although ICMP messages are contained in standard IP datagrams, ICMP messages are usually treated as a special case, different from regular IP processing, and not processed as a regular IP sub-protocol. In many cases, you need to check the contents of the ICMP message and deliver the appropriate error message to the application that generated the original IP packet, which prompted the ICMP message to be sent.

which was developed (indirectly) here :

Internet header plus the first 64 bits of raw datagram data. This data is used by the host to match the message to the appropriate process. If a higher-level protocol uses port numbers, they are supposed to be in the first 64 bits of the data source datagram data.

Since you are using UDP, which uses ports, it is possible that the network stack redirects the ICMP message back to the original socket. This is why your new and separate socket never receives these messages. I assume UDP is eating an ICMP message.

If I'm right, one solution to this is to open a raw socket and manually create your UDP packets, listen to everything that comes back, and process UDP and ICMP messages, if necessary. I’m not sure what this would look like in the code, but I don’t think it would be too complicated and could be considered more “elegant” than the winpcap solution.

Also, this link, http://www.networksorcery.com/enp/default1003.htm , seems like a great resource for low-level network protocols.

Hope this helps.

+1
source share

So you want to programmatically select the icmp unreachable return icmp package? Hard. I would say that the network stack is absorbed before you can get close to it.

I do not think that pure C # will work here. You will need to use driver level interception to connect. Have a look at this application that uses ipfiltdrv.sys windows to capture packets (icmp, tcp, udp, etc.) and read / play with them with managed code (C #).

http://www.codeproject.com/KB/IP/firewall_sniffer.aspx?display=Print

  • Oisin
+1
source share

All Articles