Question

J'ai une application SIP qui doit envoyer des paquets UDP pour configurer les appels SIP. SIP dispose d'un mécanisme de délai d'attente pour faire face aux échecs de livraison. Une autre chose que j'aimerais pouvoir faire est de détecter si un socket UDP est fermé afin de devoir attendre l'intervalle de retransmission de 32 s utilisé par SIP.

Les cas dont je parle sont ceux où une tentative d'envoi vers une socket UDP aboutit à la génération d'un paquet inaccessible de destination ICMP par l'hôte distant. Si j'essaie d'envoyer un paquet UDP à un hôte actif mais que le port n'écoute pas, je peux voir le message ICMP arriver avec un traceur de paquet, mais la question est de savoir comment puis-je y accéder à partir de mon code C #?

Je joue avec des sockets brutes mais je n’ai pas encore réussi à faire recevoir les paquets ICMP par mon programme. L’échantillon ci-dessous ne reçoit jamais de paquet même si des messages ICMP arrivent sur mon 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());

Ci-dessous, une trace de sauts de fils montrant les réponses ICMP provenant de la tentative d’envoi d’un paquet UDP de 10.0.0.100 (mon PC) à 10.0.0.138 (mon routeur) sur mon PC sur un port sur lequel je n’écoute pas. Mon problème est de savoir comment utiliser ces paquets ICMP pour comprendre que l'envoi UDP a échoué plutôt que d'attendre que l'application expire après une période arbitraire?

Réponses ICMP à l'envoi UDP

Était-ce utile?

La solution

Près de trois ans plus tard, je suis tombé sur http://www.codeproject.com / Articles / 17031 / A-Network-Sniffer-in-C , qui m'a permis de trouver une solution pour recevoir des paquets ICMP sous Windows 7 (je ne connais pas Vista, question originale). était sur le point, mais je pense que cette solution fonctionnerait).

Les deux points clés sont que le socket doit être lié à une adresse IP spécifique unique plutôt qu’à IPAddress.Any et à l’appel IOControl qui définit l’indicateur 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();

J'ai également dû définir une règle de pare-feu pour autoriser la réception des paquets inaccessibles au port ICMP.

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

Autres conseils

Icmp utilise un identifiant qui semble différent pour chaque icmp "session". (pour chaque socket icmp). Ainsi, la réponse à un paquet icmp non envoyé par le même socket est utilement filtrée pour vous. C'est pourquoi ce morceau de code ne fonctionnera pas. (Je ne suis pas sûr de cela. Ce n'est qu'une hypothèse après avoir examiné du trafic ICMP.)

Vous pouvez simplement envoyer une requête ping à l'hôte pour voir s'il peut ou non y accéder, puis essayer votre solution SIP. Cependant, cela ne fonctionnera pas si l'autre hôte filtre les fichiers icmp.

Une solution laide (mais efficace) utilise winpcap . (Avoir cela comme la seule solution possible semble être trop mauvais pour être vrai.)

Ce que je veux dire par l'utilisation de winpcap est que vous pouvez capturer le trafic ICMP , puis voir si le paquet capturé concerne le fait que votre paquet UDP soit non distribuable ou non . >

Voici un exemple de capture de paquets TCP: http://www.tamirgal.com/home/ SourceView.aspx? Item = SharpPcap & amp; File = Example6.DumpTCP.cs (Il ne devrait pas être trop difficile de faire la même chose avec ICMP.)

MISE À JOUR: Je pense que je vais devenir folle ... Ce morceau de code que vous avez posté fonctionne également pour moi ...

Le code suivant fonctionne bien pour moi (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");
                }
            }

        }
    }
}

Il suffit d'utiliser des sockets udp connectés et le système d'exploitation correspond à l'icmp inaccessible et renvoie une erreur dans le socket udp.

Google pour les sockets udp connectés.

Un certain nombre de messages sur le Web mentionnent le problème du transfert des paquets inaccessibles par le port ICMP sur Vista.

La pile devrait vous rendre une exception lorsqu’elle reçoit le message ICMP. Mais ce n'est pas le cas, du moins sur Vista. Et par conséquent, vous essayez une solution de contournement.

Je n'aime pas les réponses qui disent que ce n'est pas possible, mais cela semble être le cas. Je vous suggère donc de revenir en arrière au problème initial, à savoir les longs délais d'attente dans SIP.

  • Vous pouvez laisser l'utilisateur configurer le timeout (donc sorte de se conformer à la spécification).
  • Vous pouvez commencer à faire d'autres choses (comme vérifier d'autres proxies) avant le délai d'attente se termine.
  • Vous pouvez mettre en cache les mauvaises destinations connues (mais cela nécessiterait bonne gestion du cache.
  • Si icmp et udp ne donnent pas les messages d'erreur appropriés, essayez TCP ou un autre protocole. Juste pour obtenir les informations souhaitées.

(Tout est possible, cela peut prendre beaucoup de ressources.)

J'écris ceci en tant que réponse distincte, car les détails sont complètement différents de ceux que j'ai écrits précédemment.

Donc, sur la base du commentaire de Kalmi à propos de l'ID de session, cela m'a amené à réfléchir à la raison pour laquelle je peux ouvrir deux programmes ping sur le même ordinateur et que les réponses ne se croisent pas. Ils sont tous deux ICMP, ils utilisent donc tous deux des sockets bruts sans port. Cela signifie que quelque chose dans la pile IP doit savoir à quel socket ces réponses étaient destinées. Pour ping, il s'avère qu'un identifiant est utilisé dans les données du package ICMP dans le cadre des requêtes ECHO REQUEST et ECHO REPLY.

Ensuite, j'ai croisé ce commentaire sur wikipedia sur ICMP :

  

Bien que les messages ICMP soient contenus   dans les datagrammes IP standard, ICMP   les messages sont généralement traités comme   cas particulier, distingué de   traitement IP normal, plutôt que   traité comme un sous-protocole normal de   IP. Dans de nombreux cas, il est nécessaire de   inspecter le contenu de l'ICMP   message et livrer le approprié   message d'erreur à l'application qui   généré le paquet IP d'origine, le   celui qui a incité l'envoi de la   Message ICMP.

Ce qui a été élaboré (indirectement) sur ici :

  

L'en-tête Internet plus les 64 premiers   bits des données du datagramme d'origine.   Ces données sont utilisées par l'hôte pour correspondre   le message au bon   processus. Si un protocole de niveau supérieur   utilise des numéros de port, ils sont supposés   être dans les 64 premiers bits de données du   les données du datagramme d'origine.

Puisque vous utilisez UDP, qui utilise des ports, il est possible que la pile réseau renvoie le message ICMP au socket d'origine. C'est pourquoi votre nouveau socket, distinct, ne reçoit jamais ces messages. J'imagine qu'UDP mange le message ICMP.

Si je ne me trompe pas, une solution à cela consiste à ouvrir une socket raw et à créer manuellement vos paquets UDP, à écouter tout ce qui va revenir et à gérer les messages UDP et ICMP selon le cas. Je ne suis pas sûr de ce à quoi cela ressemblerait dans le code, mais je n’imagine pas que ce serait trop difficile et que cela pourrait être considéré comme plus "élégant". que la solution winpcap.

De plus, ce lien, http://www.networksorcery.com/enp/default1003.htm , semble être une excellente ressource pour les protocoles réseau de bas niveau.

J'espère que cela vous aidera.

Vous voulez donc récupérer par programmation le paquet icmp renvoyé inaccessible? Un dur. Je dirais que la pile réseau absorbe tout cela avant que vous puissiez vous en approcher.

Je ne pense pas qu'une approche C # pure fonctionnera ici. Vous devez utiliser une interception au niveau du pilote pour vous connecter. Jetez un coup d'œil à cette application qui utilise ipfiltdrv.sys pour l’enregistrement des paquets (icmp, tcp, udp, etc.) et lisons / jouez avec du code géré ( c #).

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

  • Oisin
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top