Прослушивание пакетов ICMP в C#

У меня есть приложение SIP, которое должно отправлять UDP-пакеты для настройки SIP-вызовов. SIP имеет механизм тайм-аута, чтобы справиться с сбоями доставки. Еще одна вещь, которую я хотел бы иметь возможность сделать, - это определить, закрыт ли сокет UDP, чтобы ждать использования интервала ретрансляции 32s.

случаи, о которых я говорю, - это когда попытка отправить сокет 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 ответы, поступающие на мой компьютер от попытки отправить UDP-пакет с 10.0.0.100 (мой компьютер) на 10.0.0.138 (мой маршрутизатор) на порту, я знаю, что он не слушает. Моя проблема заключается в том, как использовать эти пакеты ICMP для реализации отправки UDP, а не просто ждать тайм-аута приложения после произвольного периода?

ICMP responses to UDP send

3 ответов


почти 3 года спустя, и я наткнулся на http://www.codeproject.com/Articles/17031/A-Network-Sniffer-in-C что дало мне достаточно подсказки, чтобы помочь мне найти решение для получения пакетов ICMP в Windows 7 (Не знаю о Vista, о которой был первоначальный вопрос, но я подозреваю, что это решение будет работать).

два ключевых момента заключаются в том, что сокет должен быть привязан к одному определенному IP-адресу, а не IPAddress.Любой и вызов IOControl, который устанавливает флаг SIO_RCVALL.

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 использует идентификатор который, кажется, отличается для каждого "сеанса" icmp (для каждого сокета icmp). Так что ответ на пакет icmp, не отправленный тем же сокетом услужливо отфильтрованы для вас. Вот почему этот фрагмент кода не работает. (Я не уверен в этом. Это просто предположение после просмотра некоторого трафика ICMP.)

вы можете просто пинговать хост и посмотреть, можете ли вы достичь его или нет и затем попробовать ваш SIP вещь. Однако это не сработает, если другой хост отфильтровывает icmp.

уродливое (но рабочее) решение использует утилита WinPcap. (имея это как единственные рабочие решения, кажется, слишком плохо, чтобы быть правдой.)

что я имею в виду, используя winpcap, вы могли бы захват ICMP трафика а то посмотрите, является ли захваченный пакет о вашем UDP-пакете недоставленным или нет.

здесь пример захвата пакетов tcp: http://www.tamirgal.com/home/SourceView.aspx?Item=SharpPcap&File=Example6.DumpTCP.cs (Это не должно быть слишком сложно сделать то же самое с ICMP.)


UPDATE: я думаю, что схожу с ума.... Тот фрагмент кода, который вы разместили, тоже работает на меня...

следующий фрагмент кода отлично работает для меня (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'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: " +