Pregunta

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 los fallos de entrega. Otra cosa que me gustaría poder hacer es detectar si un socket UDP está cerrado para tener que esperar el intervalo de retransmisión de 32 s que usa SIP.

Los casos a los que me refiero son cuando un intento de envío a un socket UDP da como resultado que el host remoto no genere un paquete de Destino ICMP Destacado. 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 llega nuevamente con un rastreador de paquetes, pero la pregunta es ¿cómo puedo acceder a eso desde mi código C #?

Estoy jugando con sockets en bruto, pero aún no he podido obtener los paquetes ICMP para que los reciba mi programa. La muestra a continuación 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 un rastro de Wirehark que muestra las respuestas de ICMP que llegan a mi PC desde un intento de enviar un paquete UDP desde 10.0.0.100 (mi PC) a 10.0.0.138 (mi enrutador) en un puerto que sé que no está 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 se agote después de un período arbitrario.

Respuestas de ICMP al envío UDP

¿Fue útil?

Solución

Casi 3 años después, me topé 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é acerca de Vista, que es la pregunta original fue sobre pero sospecho que 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 a 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 firewall para permitir que se reciban los paquetes del puerto ICMP.

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

Otros consejos

Icmp usa un identificador que parece ser diferente para cada sesión de icmp " " (para cada zócalo icmp). Por lo tanto, la respuesta a un paquete icmp no enviado por el mismo socket es filtrada para usted. Es por eso que ese pedazo de código no funcionará. (No estoy seguro de esto. Es solo una suposición después de ver un poco de tráfico ICMP).

Puedes simplemente hacer ping al host y ver si puedes alcanzarlo o no y luego probar tu SIP. Sin embargo, eso no funcionará si el otro host filtra icmp.

Una solución fea (pero que funciona) está utilizando winpcap . (Teniendo esto como las únicas soluciones de trabajo parece ser demasiado malo para ser verdad.)

Lo que quiero decir con winpcap es que puedes capturar el tráfico ICMP y luego ver si el paquete capturado se trata de que tu paquete UDP no se pueda entregar o no .

Aquí hay un ejemplo para capturar paquetes tcp: http://www.tamirgal.com/home/ SourceView.aspx? Item = SharpPcap & amp; File = Example6.DumpTCP.cs (No debería ser demasiado difícil hacer lo mismo con ICMP).

ACTUALIZACIÓN: Creo que me estoy volviendo loca ... Ese fragmento de código que publicaste también está funcionando 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");
                }
            }

        }
    }
}

Simplemente use sockets udp conectados y el sistema operativo coincidirá con el icmp inalcanzable y devolverá un error en el socket udp.

Google para sockets udp conectados.

Hay una serie de publicaciones en la web que mencionan el problema de que los paquetes inalcanzables del puerto ICMP ya no son accesibles en Vista.

La pila debería devolverle una excepción cuando reciba el ICMP. Pero no es así, al menos en Vista. Y por lo tanto, estás intentando una solución.

No me gustan las respuestas que dicen que no es posible, pero parece ser así. Por lo tanto, le sugiero que retroceda un paso hacia el problema original, que fue un tiempo de espera largo en SIP.

  • Podrías dejar que el usuario configure el tiempo de espera (por lo tanto, una especie de cumplir con la especificación).
  • Puedes comenzar a hacer otras cosas (como verificar otros proxies) antes el tiempo de espera termina.
  • Usted podría almacenar en la caché destinos mal conocidos (pero eso necesitaría buena gestión de la caché.
  • Si icmp, y udp no dan los mensajes de error adecuados, intente tcp u otro protocolo. Solo para obtener la información deseada.

(Cualquier cosa es posible, solo puede requerir muchos recursos).

Estoy escribiendo esto como una respuesta por separado, ya que los detalles son completamente diferentes de los que escribí anteriormente.

Entonces, basado en el comentario de Kalmi sobre la identificación de la sesión, me puse a pensar 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 sockets sin puerto. Eso significa algo en la pila de IP, tiene que saber para qué socket estaban destinadas esas respuestas. Para hacer ping, resulta que hay una identificación utilizada en los datos del paquete ICMP como parte de ECHO REQUEST y ECHO REPLY.

Luego encontré este comentario en la wikipedia sobre ICMP :

  

Aunque los mensajes ICMP están contenidos   Dentro de los datagramas IP estándar, ICMP   los mensajes se procesan generalmente como una   caso especial, distinguido de   procesamiento normal de IP, en lugar de   procesado como un subprotocolo normal de   IP En muchos casos, es necesario   inspeccionar los contenidos del ICMP   mensaje y entregar el apropiado   mensaje de error a la aplicación que   generado el paquete IP original, el   uno que motivó el envío de la   Mensaje ICMP.

Que se elaboró ??en (indirectamente) aquí :

  

El encabezado de internet más los primeros 64   bits de los datos del datagrama original.   Estos datos son utilizados por el anfitrión para hacer coincidir   el mensaje al apropiado   proceso. Si un protocolo de nivel superior   utiliza números de puerto, se supone que   estar en los primeros 64 bits de datos de la   Datos del datagrama original.

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

Si estoy en lo cierto, una solución para esto es abrir un socket en bruto y crear manualmente sus paquetes UDP, escuchar si todo vuelve 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 se puede considerar 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 los protocolos de red de bajo nivel.

Espero que esto ayude.

¿Así que quieres recoger el paquete de retorno icmp no alcanzable programáticamente? Una dura Yo diría que la pila de red absorbe eso antes de que puedas acercarte a ella.

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

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

  • Oisin
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top