8

SIP コールを設定するために UDP パケットを送信する必要がある SIP アプリケーションがあります。SIP には、配信の失敗に対処するためのタイムアウト メカニズムがあります。私ができるようにしたい追加のことは、SIPが使用する32秒の再送信間隔を待たなければならないためにUDPソケットが閉じられているかどうかを検出することです。

私が言及しているケースは、UDP ソケットに送信しようとした結果、リモート ホストによって ICMP Destination Unreachable パケットが生成された場合です。起動しているがポートがリッスンしていないホストに UDP パケットを送信しようとすると、ICMP メッセージがパケット トレーサーと共に戻ってくるのを確認できますが、C# コードからそのホストにアクセスするにはどうすればよいでしょうか?

生のソケットをいじっていますが、プログラムが ICMP パケットを受信することはまだできていません。以下のサンプルでは、​​PC に 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());

以下は、10.0.0.100 (私の PC) から 10.0.0.138 (私のルーター) への UDP パケットをリッスンしていないことがわかっているポートで送信しようとして、私の PC に着信する ICMP 応答を示す Wireshark トレースです。私の問題は、任意の期間後にアプリケーションがタイムアウトするのを待つのではなく、これらの ICMP パケットを利用して UDP 送信が失敗したことを認識する方法です。

UDP 送信に対する ICMP 応答

4

7 に答える 7

18

ほぼ3年後、私はhttp://www.codeproject.com/Articles/17031/A-Network-Sniffer-in-Cに出くわしました。これは、WindowsでICMPパケットを受信するための解決策を見つけるのに役立つ十分なヒントを与えてくれました。 7(元の質問に関するVistaについてはわかりませんが、このソリューションが機能すると思います)。

2つの重要なポイントは、ソケットをIPAddress.Anyではなく単一の特定のIPアドレスにバインドする必要があることと、SIO_RCVALLフラグを設定するIOControl呼び出しです。

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ポート到達不能パケットを受信できるようにファイアウォールルールを設定する必要がありました。

netsh advfirewall firewall add rule name="All ICMP v4" dir=in action=allow protocol=icmpv4:any,any
于 2012-02-07T10:13:11.300 に答える
4

更新:私は夢中になっていると思います....あなたが投稿したそのコードも私のために働いています...

次のコードは私にとってはうまく機能します(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");
                }
            }

        }
    }
}
于 2009-03-20T02:00:57.910 に答える
3

接続された udp ソケットを使用するだけで、OS は icmp unreachable と一致し、udp ソケットでエラーを返します。

接続された udp ソケットの Google。

于 2011-08-08T15:40:14.167 に答える
3

Icmp は、icmp「セッション」ごとに (icmp ソケットごとに) 異なるように見える識別子を使用しています。そのため、同じソケットから送信されていない icmp パケットへの応答は、フィルターで除外されるので便利です。これが、そのコードが機能しない理由です。(これについてはよくわかりません。ICMP トラフィックを調べた後の単なる仮定です。)

ホストに ping を実行し、到達できるかどうかを確認してから、SIP を試すことができます。ただし、他のホストが icmp を除外している場合は機能しません。

醜い(しかし機能する)解決策はwinpcapを使用することです。(これを唯一の実用的な解決策として持つことは、真実であるにはあまりにも悪いようです。)

winpcap を使用すると、ICMP トラフィックをキャプチャして、キャプチャしたパケットが UDP パケットが配信不能かどうかを確認できるということです。

tcp パケットをキャプチャする例を次に示し ます

于 2009-03-15T07:20:55.357 に答える
2

では、プログラムで dest unreachable return icmp パケットを取得したいですか? タフなもの。私は、ネットワークスタックがそれを吸収してから、それに近づくことができると思います.

純粋な C# アプローチがここで機能するとは思わない。フックを取得するには、ドライバー レベルのインターセプトを使用する必要があります。Windows の ipfiltdrv.sys を使用してパケット (icmp、tcp、udp など) をトラップし、マネージ コードで読み取り/再生するこのアプリを見てください ( c#)。

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

  • オイシン
于 2009-03-18T17:14:19.750 に答える
2

Vista で ICMP Port Unreachable パケットにアクセスできなくなる問題について言及している Web 上の投稿が多数あります。

スタックは、ICMP を受け取ると例外を返します。しかし、少なくとも Vista ではそうではありません。したがって、回避策を試しています。

ありえないという回答は好きではありませんが、そのように思えます。そのため、SIP の長いタイムアウトであった元の問題に戻ることをお勧めします。

  • ユーザーにタイムアウトを設定させることができます(したがって、仕様に準拠しています)。
  • タイムアウトが終了する前に、他のこと (他のプロキシのチェックなど) を開始できます。
  • 既知の不正な宛先をキャッシュすることもできます (ただし、キャッシュを適切に管理する必要があります。
  • icmp と udp が適切なエラー メッセージを表示しない場合は、tcp または別のプロトコルを試してください。必要な情報を引き出すためだけに。

(何でも可能ですが、多くのリソースが必要になる場合があります。)

于 2009-03-20T09:25:19.353 に答える
1

以前書いたものとは内容が全然違うので別回答として書いておきます。

セッション ID に関する Kalmi からのコメントに基づいて、なぜ同じマシンで 2 つの ping プログラムを開くことができ、応答がクロスオーバーしないのかを考えさせられました。どちらも ICMP であるため、どちらもポートレス raw ソケットを使用します。つまり、IP スタック内の何かが、それらの応答が意図されたソケットを認識している必要があります。ping の場合、ECHO REQUEST および ECHO REPLY の一部として ICMP パッケージのデータで使用される ID があることがわかります。

次に、ウィキペディアでICMPに関する次のコメントに出くわしました。

ICMP メッセージは標準の IP データグラム内に含まれていますが、ICMP メッセージは通常、IP の通常のサブプロトコルとして処理されるのではなく、通常の IP 処理とは区別される特殊なケースとして処理されます。多くの場合、ICMP メッセージの内容を検査し、元の IP パケットを生成したアプリケーション (ICMP メッセージの送信を促したアプリケーション) に適切なエラー メッセージを配信する必要があります。

ここで(間接的に)詳しく説明されたもの:

インターネット ヘッダーと、元のデータグラムのデータの最初の 64 ビット。このデータは、メッセージを適切なプロセスに一致させるためにホストによって使用されます。より高いレベルのプロトコルがポート番号を使用する場合、それらは元のデータグラムのデータの最初の 64 データ ビットにあると見なされます。

ポートを使用する UDP を使用しているため、ネットワーク スタックが ICMP メッセージを元のソケットにルーティングしている可能性があります。これが、新しい別のソケットがこれらのメッセージを受信しない理由です。UDPがICMPメッセージを食べると思います。

私が正しければ、これに対する 1 つの解決策は、raw ソケットを開いて手動で UDP パケットを作成し、戻ってくるものをリッスンし、UDP および ICMP メッセージを適切に処理することです。コードでそれがどのように見えるかはわかりませんが、難しすぎるとは思いませんし、winpcap ソリューションよりも「エレガント」と見なされるかもしれません。

さらに、このリンクhttp://www.networksorcery.com/enp/default1003.htmは、低レベルのネットワーク プロトコルに関する優れたリソースのようです。

これが役立つことを願っています。

于 2009-03-18T16:53:45.247 に答える