Вопрос

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

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

Двумя ключевыми моментами является то, что сокет должен быть привязан к одному конкретному IP-адресу, а не к IPAddress.Any, и к вызову 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 & амп; Файл = Example6.DumpTCP.cs (Это не должно быть слишком сложно сделать то же самое с ICMP.)

ОБНОВЛЕНИЕ: я думаю, что схожу с ума .... Этот фрагмент кода, который вы опубликовали, также работает для меня ...

Следующий фрагмент кода хорошо работает для меня (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: " + 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");
                }
            }

        }
    }
}

Просто используйте подключенные сокеты udp, и ОС совпадет с недоступным icmp и вернет ошибку в сокете udp.

Google для подключенных сокетов udp.

В Интернете есть несколько сообщений, в которых упоминается проблема недоступности пакетов ICMP-порта, которые больше не доступны в Vista.

Стек должен выдать вам исключение при получении ICMP.Но это не так, по крайней мере, в Vista.И, следовательно, вы пытаетесь найти обходной путь.

Мне не нравятся ответы, в которых говорится, что это невозможно, но, похоже, так оно и есть.Поэтому я предлагаю вам вернуться на шаг назад к исходной проблеме, которая заключалась в длительных тайм-аутах в SIP.

  • Вы могли бы позволить пользователю настроить время ожидания (следовательно, в некотором роде соответствовать спецификации).
  • Вы можете начать выполнять другие действия (например, проверять другие прокси) до того, как истечет время ожидания.
  • Вы могли бы кэшировать известные поврежденные места назначения (но для этого потребуется хорошее управление кэшем.
  • Если icmp и udp не выдают надлежащих сообщений об ошибках, попробуйте tcp или другой протокол.Просто для того, чтобы получить желаемую информацию.

(Все возможно, просто для этого может потребоваться много ресурсов.)

Я пишу это как отдельный ответ, поскольку детали полностью отличаются от того, что я написал ранее.

Итак, основываясь на комментарии Kalmi об идентификаторе сеанса, я подумал о том, почему я могу открыть две программы ping на одном компьютере, и ответы не пересекаются. Они оба ICMP, поэтому оба используют необработанные сокеты без портов. Это означает, что что-то в стеке IP должно знать, для какого сокета предназначались эти ответы. Для проверки связи выясняется, что в данных пакета ICMP используется идентификатор, являющийся частью ECHO REQUEST и ECHO REPLY.

Затем я наткнулся на этот комментарий в Википедии о ICMP :

  

Хотя сообщения ICMP содержатся   в рамках стандартных дейтаграмм IP, ICMP   сообщения обычно обрабатываются как   особый случай, отличающийся от   обычная обработка IP, а не   обрабатывается как обычный подпротокол   IP. Во многих случаях необходимо   проверить содержимое ICMP   сообщение и доставить соответствующий   сообщение об ошибке в приложении, которое   сгенерировал исходный IP-пакет,   тот, который вызвал отправку   ICMP-сообщение.

Который был разработан (косвенно) здесь :

  

Интернет-заголовок плюс первые 64   биты исходных данных дейтаграммы.   Эти данные используются хостом для соответствия   сообщение соответствующему   процесс. Если протокол более высокого уровня   использует номера портов, предполагается, что они   быть в первых 64 битах данных   исходные данные дейтаграммы.

Поскольку вы используете UDP, который использует порты, возможно, сетевой стек перенаправляет сообщение ICMP обратно в исходный сокет. Вот почему ваш новый отдельный сокет никогда не получает эти сообщения. Я полагаю, UDP использует ICMP-сообщение.

Если я прав, одно из решений этой проблемы - открыть необработанный сокет и вручную создать UDP-пакеты, прослушать все, что возвращается, и обработать сообщения UDP и ICMP соответствующим образом. Я не уверен, как это будет выглядеть в коде, но я не думаю, что это будет слишком сложно, и его можно считать более «элегантным» чем решение winpcap.

Кроме этой ссылки, http://www.networksorcery.com/enp/default1003.htm - отличный ресурс для сетевых протоколов низкого уровня.

Надеюсь, это поможет.

Итак, вы хотите получить возвращаемый icmp-пакет dest unreachable программным путем?Трудный вопрос.Я бы сказал, что сетевой стек впитывает это до того, как вы сможете к нему приблизиться.

Я не думаю, что чистый подход C # здесь сработает.Вам нужно будет использовать перехват на уровне драйвера, чтобы подключиться.Взгляните на это приложение, которое использует Windows ' ipfiltdrv.sys для захвата пакетов (icmp, tcp, udp и т.д.) И чтения / воспроизведения с ними с помощью управляемого кода (c #).

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

  • Ойсин
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top