2009-03-09 14 views
7

Tengo una aplicación SIP que necesita enviar paquetes UDP para configurar las llamadas SIP. SIP tiene un mecanismo de tiempo de espera para hacer frente a las fallas en la entrega. Otra cosa que me gustaría hacer es detectar si un socket UDP está cerrado para tener que esperar el intervalo de retransmisión de 32 segundos que usa SIP.Escuchar paquetes ICMP en C#

Los casos a los que me refiero son cuando un intento de enviar a un socket UDP da como resultado que el host remoto genere un paquete de ICMP Destination Uncachable. Si intento enviar un paquete UDP a un host que está activo pero que el puerto no está escuchando, puedo ver que el mensaje ICMP vuelve con un trazador de paquetes, pero la pregunta es ¿cómo puedo acceder a eso desde mi código C#?

Estoy jugando con sockets sin formato, pero aún no he podido recibir los paquetes ICMP para que los reciba mi programa. El ejemplo siguiente nunca recibe un paquete a pesar de que los mensajes ICMP llegan a mi PC.

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()); 

A continuación se muestra una traza Wireshark muestra las respuestas ICMP que entran en mi PC de un intento de enviar un paquete UDP a 10.0.0.100 (mi PC) a 10.0.0.138 (mi router) en un puerto Sé que es no escuchando Mi problema es cómo hacer uso de esos paquetes ICMP para darse cuenta de que el envío UDP ha fallado en lugar de simplemente esperar a que la aplicación expire el tiempo después de un período arbitrario.

ICMP responses to UDP send

Respuesta

13

Casi 3 años después y tropecé con http://www.codeproject.com/Articles/17031/A-Network-Sniffer-in-C que me dio una pista suficiente para ayudarme a encontrar una solución para recibir paquetes ICMP en Windows 7 (no sé de Vista, de lo que se trataba la pregunta original pero sospecho esta solución funcionaría).

Los dos puntos clave son que el socket debe estar vinculado a una única dirección IP específica en lugar de IPAddress.Any y la llamada IOControl que establece el indicador 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(); 

También tuve que establecer una regla de cortafuegos para permitir paquetes ICMP de puerto inalcanzable a ser recibidas.

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

No sé si ayuda o no. Pero usando He estado usando Socket.BeginReceiveFrom y de vez en cuando fallará con SocketException de SocketError.ConnectionReset (10054). En el protocolo UDP, esta es una notificación de que el socket recibió un mensaje ICMP Port Unreachable. – SilverX

+1

Esa misma excepción se lanza sin importar cómo se recibe desde el socket. El problema que siempre tuve fue que la excepción no te decía de qué host remoto provenía la respuesta ICMP. Eso es lo que el oyente de ICMP puede hacer. – sipwiz

+8

¡Último +1 para seguir ofreciendo soluciones después de 3 años! –

3

Icmp está utilizando un identificador que parece ser diferente para cada ICMP "sesión" (para cada socket ICMP). Así que la respuesta a un paquete icmp no enviado por el mismo socket es útil filtrada para usted. Es por eso que esa pieza de código no funcionará. (no estoy seguro de esto. Es sólo una suposición después de mirar un poco de tráfico ICMP.)

Usted simplemente puede hacer ping al host y ver si se puede llegar o no y luego probar su cosa SIP. Sin embargo, eso no funcionará si el otro host está filtrando icmp.

Una solución fea (pero funcional) está utilizando winpcap. (Tener presente que las soluciones sólo se trabajan sólo parece ser demasiado malo para ser verdad.)

Lo que quiero decir mediante el uso de WinPcap es la que podría captura de tráfico ICMP y luego ver si el paquete capturado está a punto su paquete UDP no se puede entregar o no.

Aquí se muestra un ejemplo para la captura de paquetes TCP: http://www.tamirgal.com/home/SourceView.aspx?Item=SharpPcap&File=Example6.DumpTCP.cs (No debería ser demasiado difícil de hacer lo mismo con ICMP.)

+1

Estoy de acuerdo. Si no hay nada más disponible, esa es una buena solución. Simplemente no puedo evitar pensar que hay otra manera. En realidad, el envío UDP debería arrojar una excepción cuando se recibe el mensaje ICMP. – sipwiz

+0

UDP no tiene estado, por lo que no creo que deba serlo. –

+0

Votación ascendente para asegurarse de que la persona adecuada obtenga la recompensa, no porque esta publicación lo preserve. – Joshua

1

Estoy escribiendo esto como una respuesta por separado, ya que los detalles son completamente diferente de la que escribí antes.

Basado en el comentario de Kalmi sobre la identificación de la sesión, me hizo pensar en por qué puedo abrir dos programas de ping en la misma máquina, y las respuestas no se cruzan. Ambos son ICMP, por lo tanto, ambos usan conectores RAW sin puertos. Eso significa algo en la pila de IP, tiene que saber para qué socket estaban destinadas esas respuestas. Para ping, resulta que hay una ID utilizada en los datos del paquete ICMP como parte de ECHO REQUEST y ECHO REPLY.

Entonces me encontré con este comentario en la wikipedia sobre ICMP:

Aunque los mensajes ICMP están contenidas dentro de datagramas IP estándar, ICMP mensajes se procesan generalmente como un caso especial , distingue de procesamiento IP normal , en lugar de procesado como un sub protocolo normal de IP. En muchos casos, es necesario inspeccionar el contenido del mensaje ICMP y entregar el mensaje de error apropiado a la aplicación que genera el paquete IP original, el uno que hizo que el envío del mensaje ICMP .

que se elaboró ​​en (indirectamente) here:

La cabecera Internet más los primeros 64 bits de datos del datagrama original. El host utiliza estos datos para hacer coincidir el mensaje con el proceso correspondiente. Si un protocolo de nivel superior usa números de puerto, se supone que son en los primeros 64 bits de datos de los datos del datagrama original .

Dado que está utilizando UDP, que usa puertos, es posible que la pila de red enrute el mensaje ICMP al socket original. Esta es la razón por la cual su socket nuevo y separado nunca recibe esos mensajes. Me imagino que UDP come el mensaje ICMP.

Si estoy en lo correcto, una solución para esto es abrir un socket sin procesar y crear manualmente los paquetes UDP, escuchar lo que vuelva y manejar los mensajes UDP e ICMP según corresponda. No estoy seguro de cómo se vería en el código, pero no creo que sea demasiado difícil, y puede considerarse más "elegante" que la solución winpcap.

Además, este enlace, http://www.networksorcery.com/enp/default1003.htm, parece ser un gran recurso para protocolos de red de bajo nivel.

Espero que esto ayude.

+0

Su punto acerca de los ID de sesión de ICMP tiene mucho sentido para mí. Ese es en realidad el quid del problema, ¿por qué los mensajes ICMP no indican los paquetes UDP no entregables entregados a mi aplicación? Por alguna razón, Windows no parece coincidir con la aplicación porque eran para un paquete UDP. – sipwiz

+1

Pensé en intentar multiplexar UDP e ICMP sobre el mismo socket crudo, pero probablemente necesitarías hacer todo el procesamiento UDP, pero aparte de eso, cuando creas un socket crudo con Windows, debes seleccionar si es IP o no. ICMP, no puedes tener ambos. – sipwiz

+0

Sí, creo que tendrías que convertirlo en un zócalo IP sin procesar y manejar todos los mensajes ICMP. Esto puede ser más complejo de lo que vale, pero por lo que pude leer sobre IP e ICMP puede ser la única respuesta de estilo no winpcap. – grieve

1

¿Desea recuperar el paquete icmp de destino inalcanzable programáticamente? Una difícil. Yo diría que la pila de la red absorbe todo eso antes de que puedas acercarte a él.

No creo que un enfoque C# puro funcionará aquí. Tendrá que usar una intercepción a nivel de controlador para obtener un gancho. Eche un vistazo a esta aplicación que usa ipfiltdrv.sys de Windows para atrapar paquetes (icmp, tcp, udp, etc.) y leer/reproducir con ellos con código administrado (do#).

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

  • Oisin
3

ACTUALIZACIÓN: Creo que me estoy volviendo loco .... Ese pedazo de código que ha publicado también está trabajando para mí ...

El siguiente fragmento de código funciona bien para mí (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

Cuando dice trabajos, ¿quiere decir que está recibiendo paquetes ICMP si envía un ping o algo así? Estoy bastante seguro de que eso es lo que estaba usando cuando estaba enviando UDP a puertos no accesibles y no pude recibir el ICMP. Tendré que volver a verificar. – sipwiz

+0

Solo probé con hosts inalcanzables y tcp ... Voy a probar los puertos –

+0

sí ... funciona bien con udp y hosts existentes ... Agregué esa parte comentada para probar ... –

2

Hay una serie de mensajes en la web mencionar el problema de los paquetes ICMP de puerto inalcanzable ya no es accesible en Vista.

La pila debería darle vuelta una excepción cuando recibe la ICMP. Pero no lo hace, al menos en Vista. Y por lo tanto, estás probando una solución alternativa.

No me gustan las respuestas que dicen que no es posible, pero parece que sí. Así que sugiero que retrocedan un paso al problema original, que fue un tiempo de espera largo en SIP.

  • Usted puede dejar que el usuario configure el tiempo de espera (de ahí especie de cumplir con la especificación ).
  • Puede comenzar a hacer otras cosas (como comprobar otros proxies) antes de el tiempo de espera finaliza.
  • Usted podría caché conocido malos destinos (pero que necesitaría buena gestión de la caché.
  • Si ICMP, UDP y no dan mensajes de error adecuado, intente TCP u otro protocolo. Sólo para obtener la información deseada.

(Todo es posible, sólo puede tomar una gran cantidad de recursos.)

3

Sólo tiene que utilizar sockets UDP conectados y el sistema operativo va a coincidir con el ICMP inalcanzable y devolver un error en el socket UDP.

Google para conectores udp conectados.

+0

El cliente UDP lanzará una excepción de socket para un puerto ICMP inalcanzable pero no para un host ICMP inalcanzable o de hecho para cualquier otro mensaje "inalcanzable" de ICMP – trampster

Cuestiones relacionadas