Domanda

Ho un'applicazione SIP che deve inviare pacchetti UDP per configurare le chiamate SIP. SIP ha un meccanismo di timeout per far fronte agli errori di consegna. Un'ulteriore cosa che vorrei poter fare è rilevare se un socket UDP è chiuso per dover attendere l'intervallo di ritrasmissione di 32 secondi utilizzato da SIP.

I casi a cui mi riferisco sono quando un tentativo di invio a un socket UDP si traduce in un pacchetto ICR non raggiungibile di destinazione generato dall'host remoto. Se provo a inviare un pacchetto UDP a un host che è attivo ma che la porta non è in ascolto, posso vedere il messaggio ICMP che ritorna con un tracciante di pacchetti, ma la domanda è: come posso accedervi dal mio codice C #?

Sto giocando con i socket grezzi ma non sono ancora riuscito a ricevere i pacchetti ICMP dal mio programma. L'esempio seguente non riceve mai un pacchetto anche se i messaggi ICMP arrivano sul mio 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());

Di seguito una traccia diarshark che mostra le risposte ICMP che arrivano nel mio PC dal tentativo di inviare un pacchetto UDP da 10.0.0.100 (il mio PC) a 10.0.0.138 (il mio router) su una porta che so che non sta ascoltando. Il mio problema è come utilizzare quei pacchetti ICMP per rendersi conto che l'invio UDP è fallito piuttosto che aspettare il timeout dell'applicazione dopo un periodo arbitrario?

Risposte ICMP all'invio UDP

È stato utile?

Soluzione

Quasi 3 anni dopo e sono incappato in http://www.codeproject.com / Articoli / 17031 / A-Network-Sniffer-in-C che mi ha dato un suggerimento sufficiente per aiutarmi a trovare una soluzione per ricevere pacchetti ICMP su Windows 7 (non conosco Vista, che la domanda originale era circa ma sospetto che questa soluzione avrebbe funzionato).

I due punti chiave sono che il socket deve essere associato a un singolo indirizzo IP specifico anziché a IPAddress. Qualsiasi chiamata IOControl che imposta il flag 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();

Ho anche dovuto impostare una regola firewall per consentire la ricezione di pacchetti non raggiungibili porta ICMP.

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

Altri suggerimenti

Icmp utilizza un identificatore che sembra essere diverso per ogni sessione " icmp; (per ogni presa icmp). Quindi la risposta a un pacchetto icmp non inviato dallo stesso socket viene utilmente filtrata per te. Questo è il motivo per cui quel pezzo di codice non funzionerà. (Non ne sono sicuro. È solo un presupposto dopo aver esaminato un po 'di traffico ICMP.)

Potresti semplicemente eseguire il ping dell'host e vedere se puoi raggiungerlo o meno e quindi provare la tua cosa SIP. Tuttavia, ciò non funzionerà se l'altro host sta filtrando icmp.

Una brutta (ma funzionante) soluzione sta usando winpcap . (Avere questo come unica soluzione funzionante sembra essere troppo brutto per essere vero.)

Quello che intendo con winpcap è che potresti catturare il traffico ICMP e quindi vedere se il pacchetto catturato riguarda il tuo pacchetto UDP non consegnabile o meno .

Ecco un esempio per l'acquisizione di pacchetti tcp: http://www.tamirgal.com/home/ ? SourceView.aspx Item = SharpPcap & amp; file = Example6.DumpTCP.cs (Non dovrebbe essere troppo difficile fare lo stesso con ICMP.)

AGGIORNAMENTO: Penso che sto impazzendo .... Quel pezzo di codice che hai pubblicato funziona anche per me ...

Il seguente pezzo di codice funziona bene per me (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");
                }
            }

        }
    }
}

Usa solo i socket udp collegati e il sistema operativo corrisponderà a icmp non raggiungibile e restituirà un errore nel socket udp.

Google per socket udp collegati.

Esistono numerosi post sul Web che menzionano il problema dei pacchetti non raggiungibili porta ICMP non più accessibili su Vista.

Lo stack dovrebbe restituire un'eccezione quando riceve l'ICMP. Ma non lo è, almeno su Vista. E quindi stai provando una soluzione alternativa.

Non mi piacciono le risposte che affermano che non è possibile, ma sembra così. Quindi ti suggerisco di tornare indietro al problema originale, che era lunghi timeout in SIP.

  • Puoi consentire all'utente di configurare il file timeout (quindi una sorta di rispetto le specifiche).
  • Puoi iniziare a fare altre cose (come controllare altri proxy) prima il timeout termina.
  • È possibile memorizzare nella cache destinazioni non valide conosciute (ma ciò sarebbe necessario buona gestione della cache.
  • Se icmp e udp non forniscono messaggi di errore corretti, provare tcp o un altro protocollo. Solo per ottenere le informazioni desiderate.

(Tutto è possibile, potrebbero essere necessarie molte risorse.)

Sto scrivendo questo come una risposta separata, poiché i dettagli sono completamente diversi da quello che ho scritto in precedenza.

Quindi, in base al commento di Kalmi sull'ID di sessione, mi è venuto in mente il motivo per cui posso aprire due programmi di ping sulla stessa macchina e le risposte non si incrociano. Sono entrambi ICMP, quindi entrambi utilizzano socket raw senza porte. Ciò significa che qualcosa nello stack IP deve sapere a quale socket erano destinate quelle risposte. Per il ping risulta che esiste un ID usato nei dati del pacchetto ICMP come parte di ECHO REQUEST e ECHO REPLY.

Poi ho incontrato questo commento su wikipedia su ICMP :

  

Sebbene i messaggi ICMP siano contenuti   all'interno di datagrammi IP standard, ICMP   i messaggi vengono generalmente elaborati come a   caso speciale, distinto da   normale elaborazione IP, anziché   elaborato come un sotto-protocollo normale di   IP. In molti casi, è necessario   ispezionare il contenuto dell'ICMP   messaggio e consegna l'appropriato   messaggio di errore all'applicazione che   generato il pacchetto IP originale, il   uno che ha richiesto l'invio di   Messaggio ICMP.

Che è stato elaborato (indirettamente) qui :

  

L'intestazione Internet più i primi 64   bit dei dati del datagramma originale.   Questi dati vengono utilizzati dall'host per la corrispondenza   il messaggio per l'appropriato   processi. Se un protocollo di livello superiore   usa i numeri di porta, si presume   essere nei primi 64 bit di dati di   dati del datagramma originale.

Poiché si utilizza UDP, che utilizza le porte, è possibile che lo stack di rete stia reindirizzando il messaggio ICMP al socket originale. Ecco perché il tuo nuovo socket separato non riceve mai quei messaggi. Immagino che UDP mangi il messaggio ICMP.

Se ho ragione, una soluzione a ciò è aprire un socket non elaborato e creare manualmente i pacchetti UDP, ascoltare tutto ciò che ritorna e gestire i messaggi UDP e ICMP nel modo appropriato. Non sono sicuro di come apparirebbe nel codice, ma non immagino che sarebbe troppo difficile e potrebbe essere considerato più "elegante". rispetto alla soluzione winpcap.

Inoltre questo link, http://www.networksorcery.com/enp/default1003.htm , sembra essere un'ottima risorsa per i protocolli di rete di basso livello.

Spero che questo aiuti.

Quindi vuoi prelevare programmaticamente il pacchetto icmp di ritorno irraggiungibile dest? Una dura. Direi che lo stack di rete lo assorbe prima che tu possa avvicinarti ovunque.

Non credo che un approccio C # puro funzionerà qui. Dovrai utilizzare un'intercettazione a livello di driver per ottenere un hook. Dai un'occhiata a questa app che utilizza ipfiltdrv.sys di Windows per intercettare i pacchetti (icmp, tcp, udp ecc) e leggi / gioca con loro con il codice gestito ( c #).

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

  • Oisin
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top