2009-03-09 4 views
7

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

Случаи, о которых я говорю, это когда попытка отправить в сокет UDP приводит к сбою ICMP Destination Unreachable, который создается удаленным хостом. Если я попытаюсь отправить 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

ответ

13

Почти 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 Port Unreachable.

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

Я не знаю, помогает ли это или нет. Но используя я использовал Socket.BeginReceiveFrom, и время от времени он будет сбой с SocketException SocketError.ConnectionReset (10054). В соответствии с протоколом UDP это уведомление о том, что сокет получил сообщение ICMP Port Unreachable. – SilverX

+1

То же исключение выбрасывается независимо от того, как вы получаете из сокета. Проблема, с которой я всегда сталкивался, заключалась в том, что исключение не сообщило вам, какой удаленный хост получил ответ ICMP. Это то, что может сделать слушатель ICMP. – sipwiz

+8

Late +1 для по-прежнему заботы о предоставлении решения через 3 года! –

3

ИКМП использует в идентификатор который, кажется, различны для каждого ИКМП "сеанса" (для каждой розетки) ICMP. Поэтому ответ на пакет icmp, не отправленный одним и тем же сокетом, полезен отфильтрован для вас. Вот почему этот фрагмент кода не будет работать. (Я не уверен в этом. Это просто предположение, если вы посмотрите на некоторый трафик ICMP.)

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

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

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

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

+1

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

+0

UDP не имеет гражданства, поэтому я не думаю, что это нужно. –

+0

Продвижение, чтобы убедиться, что нужный человек получает щедрость, а не то, что этот пост его не уважает. – Joshua

1

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

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

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

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

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

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

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

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

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

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

+0

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

+1

Я действительно думал о попытке мультиплексирования UDP и ICMP через один и тот же raw-сокет, но вам, вероятно, потребуется выполнить всю обработку UDP, но кроме того, когда вы создаете сырой сокет с Windows, вам нужно выбрать, будет ли он IP или ICMP, вы не можете иметь оба. – sipwiz

+0

Да, я думаю, вам нужно будет сделать его сырым IP-сокетом и обработать все сообщения ICMP. Это может быть более сложным, чем того стоит, но из того, что я смог прочитать о IP и ICMP, это может быть единственный ответ типа не winpcap. – grieve

1

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

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

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

  • Oisin
3

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: " + 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"); 
       } 
      } 

     } 
    } 
} 
+0

Когда вы говорите, что работает, вы имеете в виду, что получаете ICMP-пакеты, если вы отправляете пинг или что-то еще? Я почти уверен, что это то, что я использовал, когда я делал UDP-отправляет порты unreachbale и не смог получить ICMP. Я должен снова проверить. – sipwiz

+0

Я только тестировал с недостижимыми хостами и tcp ... Я попробую порты –

+0

yep ... отлично работает с udp и существующими хостами ... Я добавил, что прокомментировал часть для тестирования. –

2

Есть целый ряд сообщений на веб-упомянув проблему пакетов ICMP Port Unreachable больше не доступны на Vista.

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

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

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

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

3

Просто используйте подключенные UdP розетки и ОС будет соответствовать ICMP недостижим и возвращает ошибку в УДП розеткой.

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

+0

UDP-клиент будет генерировать исключение сокета для ICMP-порта недоступным, но не для ICMP-хоста Недостижимым или фактически для любого другого сообщения ICMP «недоступно» – trampster

Смежные вопросы