C# 在C语言中侦听ICMP数据包

C# 在C语言中侦听ICMP数据包,c#,icmp,C#,Icmp,我有一个SIP应用程序,需要发送UDP数据包来设置SIP调用。SIP有一个超时机制来处理传递失败。我希望能够做的另一件事是检测UDP套接字是否关闭,以便等待SIP使用的32秒重传间隔 我所指的情况是,尝试发送到UDP套接字导致远程主机生成ICMP目标无法访问的数据包。如果我尝试将UDP数据包发送到启动的主机,但该端口未侦听,我可以看到ICMP消息通过数据包跟踪器返回,但问题是如何从我的C代码访问该数据包 我正在使用原始套接字,但到目前为止,我的程序还无法接收ICMP数据包。下面的示例从未收到数据

我有一个SIP应用程序,需要发送UDP数据包来设置SIP调用。SIP有一个超时机制来处理传递失败。我希望能够做的另一件事是检测UDP套接字是否关闭,以便等待SIP使用的32秒重传间隔

我所指的情况是,尝试发送到UDP套接字导致远程主机生成ICMP目标无法访问的数据包。如果我尝试将UDP数据包发送到启动的主机,但该端口未侦听,我可以看到ICMP消息通过数据包跟踪器返回,但问题是如何从我的C代码访问该数据包

我正在使用原始套接字,但到目前为止,我的程序还无法接收ICMP数据包。下面的示例从未收到数据包,即使ICMP消息到达我的电脑

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());
下面是wireshark跟踪,显示了ICMP响应从我的电脑10.0.0.100发送UDP数据包到我的路由器10.0.0.138的过程中进入我的电脑的端口,我知道它没有监听。我的问题是如何利用这些ICMP数据包来实现UDP发送失败,而不是仅仅等待应用程序在任意时间段后超时

Icmp使用的标识符对于每个Icmp套接字的每个Icmp会话似乎都不同。因此,对不是由同一套接字发送的icmp数据包的回复将被有效地过滤掉。这就是为什么那段代码不起作用。我对此不确定。这只是在查看了一些ICMP流量之后的一个假设

您可以简单地ping主机,看看是否可以访问它,然后尝试SIP。但是,如果另一个主机正在筛选出icmp,则这将不起作用

一个丑陋但有效的解决方案是使用winpcap。将此作为唯一有效的解决方案似乎太糟糕了,不可能是真的

我使用winpcap的意思是,您可以捕获ICMP流量,然后查看捕获的数据包是否与UDP数据包无法传递有关

以下是捕获tcp数据包的示例:
在ICMP上做同样的事情应该不会太难。

我写这篇文章是作为一个单独的答案,因为细节与我之前写的完全不同

因此,根据Kalmi对会话ID的评论,我开始思考为什么我可以在同一台机器上打开两个ping程序,而响应不会交叉。它们都是ICMP,因此都使用无端口原始套接字。这意味着IP堆栈中的某些东西必须知道这些响应用于哪个套接字。对于ping,ICMP包的数据中使用了一个ID作为ECHO请求和ECHO应答的一部分

然后我在维基百科上看到了这样一条评论:

尽管包含ICMP消息 在标准IP数据报中,ICMP 消息通常作为一个整体进行处理 特殊情况,区别于 正常的IP处理,而不是 作为的正常子协议处理 知识产权。在许多情况下,有必要 检查ICMP的内容 传达信息并交付适当的 发送到应用程序的错误消息 生成原始IP数据包时 提示发送消息的人 ICMP消息

这是间接阐述的:

internet标头加上前64个 原始数据报数据的位。 主机使用此数据进行匹配 将消息发送到相应的 过程如果是更高级别的协议 如果使用端口号,则假定它们为 位于 原始数据报的数据

由于您使用的是使用端口的UDP,因此网络堆栈可能正在将ICMP消息路由回原始套接字。这就是为什么新的、独立的套接字永远不会接收这些消息。我想UDP会吃掉ICMP消息

如果我是正确的,一个解决方案是打开一个原始套接字并手动创建UDP数据包,侦听返回的任何内容,并酌情处理UDP和ICMP消息。我不确定这在代码中会是什么样子,但我不认为这太难,而且可能被认为比winpcap解决方案更优雅

此外,此链接似乎是低级网络协议的一个重要资源


我希望这会有所帮助。

那么您想通过编程方式获取dest unreachable return icmp数据包吗?艰难的一个。我要说的是,网络堆栈在你接近它之前就已经吸收了它

我认为纯C的方法在这里行不通。你需要使用一个驱动级截获来获得一个钩子。看看这个应用程序,它使用windows的ipfiltdrv.sys捕获icmp、tcp、udp等数据包,并使用托管代码c读取/播放这些数据包

莪相 更新:我想我快疯了。。。。你发布的那段代码也对我有用

以下代码适用于mexp 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's 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");
                }
            }

        }
    }
}

网上有很多帖子提到了ICMP端口无法访问的数据包在Vista上无法访问的问题

堆栈在收到ICMP时应返回一个异常。但事实并非如此,至少在Vista上是如此。因此,您正在尝试解决方法

我不喜欢那些说不可能的答案,但似乎是这样。因此,我建议您回到最初的问题,即SIP中的长时间超时

您可以让用户配置 超时,因此在某种程度上符合 规格。 您可以开始做其他事情,如检查其他代理之前 超时结束。 您可以缓存已知的坏目的地,但这需要 缓存的良好管理。 如果icmp和udp没有给出正确的错误消息,请尝试tcp或其他协议。只是为了得到想要的信息。
任何事情都是可能的,只是可能需要大量资源。

只需使用连接的udp套接字,操作系统将匹配无法访问的icmp,并在udp套接字中返回错误


谷歌搜索已连接的udp套接字。

近3年后,我偶然发现了一个足够的提示,可以帮助我找到在Windows 7上接收ICMP数据包的解决方案。我不知道最初的问题是关于Vista的,但我怀疑这个解决方案会起作用

两个关键点是套接字必须绑定到单个特定的IP地址,而不是IPAddress.Any和设置SIO_RCVALL标志的IOControl调用

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();
我还必须设置防火墙规则,以允许接收ICMP端口无法访问的数据包

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

我同意。如果没有其他可用的方法,这是一个很好的解决方案。我只是忍不住想还有别的办法。实际上,当收到ICMP消息时,UDP发送应该引发异常。UDP是无状态的,所以我认为不应该。向上投票是为了确保正确的人得到赏金,而不是因为这篇文章取消了赏金。你关于ICMP会话id的观点对我来说非常有意义。这实际上是问题的症结所在,为什么ICMP消息没有指示不可交付的UDP数据包交付给我的应用程序?由于某些原因,Windows似乎没有将它们与应用程序匹配,因为它们用于UDP数据包。我曾考虑尝试在同一原始套接字上多路复用UDP和ICMP,但您可能需要执行所有UDP处理,但除此之外,当您使用Windows创建原始套接字时,您必须选择是IP还是ICMP,你不能两者都有。是的,我想你必须把它变成一个原始的IP套接字,并处理所有的ICMP消息。这可能比它的价值更复杂,但从我所读到的关于IP和ICMP的内容来看,这可能是唯一一个非winpcap风格的答案。澄清一下:我认为您的应用程序正在接收ICMP消息,但UDP堆栈正在抑制它。当然,这是猜测,因为我不知道您是如何为原始UDP消息创建和使用套接字的。当您说works时,您的意思是如果您发送ping或其他消息,您正在接收ICMP数据包吗?我很确定这就是我在做UDP发送到非交换端口时使用的,并且无法接收ICMP。我必须再次检查。我只测试了无法访问的主机和tcp。。。我会试试portsyep。。。可以与udp和现有主机配合使用。。。我添加了注释掉的测试部分。我也可以确认这一点。我在XP和2k3上使用的代码和Kalmi的示例工作,但在Vista上没有。我的应用程序注定是2k3,所以这是个好消息!我不知道这是否有帮助。但是使用我一直在使用Socket.BeginReceiveFrom,它会不时失败,出现SocketError.ConnectionReset 10054的SocketException。在UDP协议下,这是套接字接收到ICMP端口不可访问消息的通知。无论您如何从套接字接收,都会引发相同的异常。我一直遇到的问题是,异常没有告诉您ICMP响应来自哪个远程主机。这就是ICMP侦听器所能做的。迟到+1表示在3年后仍然关心提供解决方案!UDP客户端将为无法访问的ICMP端口引发套接字异常,但不会为无法访问的ICMP主机或任何其他ICMP无法访问的消息引发套接字异常