2009-03-09 18 views
7

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

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

私は生のソケットで遊んでいますが、ICMPパケットを私のプログラムで受信することができませんでした。 ICMPメッセージが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()); 

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

ICMP responses to UDP send

答えて

13

ほぼ3年後、私はhttp://www.codeproject.com/Articles/17031/A-Network-Sniffer-in-Cを見つけたので、私はWindows 7でICMPパケットを受信するための解決策を見つけるのに役立つヒントを得ました(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 Port Unreachableパケットを受信できるようにするためのファイアウォールルールを設定する必要がありました。

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

私はそれが役立つかどうかわかりません。しかし、私はSocket.BeginReceiveFromを使用していますが、SocketException(10054)のSocketExceptionで失敗することがあります。 UDPプロトコルの下で、ソケットがICMP Port Unreachableメッセージを受信したという通知。 – SilverX

+1

ソケットからの受信方法に関係なく、同じ例外がスローされます。私がいつも持っていた問題は、例外がICMP応答がどのリモートホストから来たのかを教えてくれなかったことです。それはICMPリスナーができることです。 – sipwiz

+8

3年後に解決策を提供するためにまだ気をつけている場合は、後1にしてください! –

3

ICMPは(すべてのICMPソケットの)すべてのICMP "セッション" ごとに異なると思わ識別子を使用しています。同じソケットで送信されていないicmpパケットにの返信がありますは役に立ちましたこれが、そのコードが機能しない理由です。 は(私はこれについてはよく分からない。それはちょうど、いくつかのICMPトラフィックを見た後に仮定のです。)

あなたは、単にホストにpingを実行し、あなたがそれを達するかないと、あなたのSIPの事を試みることができるかどうかを見ることができました。しかし、他のホストがicmpをフィルタリングしている場合は、これは機能しません。

醜い(ただし動作​​する)解決策は、winpcapを使用しています。 (唯一の作業溶液としてこれを持つことだけで本当であるには余りにも悪いことのようです。)

私はWinPcapのを使って、意味することは、あなたはキャプチャしたパケットを約あるかどうかを確認その後、ICMPトラフィックをキャプチャしてできていますUDPパケットが配信不能かどうかを確認してください。詳細は、完全なので、私は、別の答えとしてこれを書いています http://www.tamirgal.com/home/SourceView.aspx?Item=SharpPcap&File=Example6.DumpTCP.cs (ICMPと同じことを行うにはあまりにも難しいことではありません。)

+1

私は同意します。他に何も利用できない場合、それは良い解決策です。私は別の方法があると考えるのを助けることができません。実際には、ICMPメッセージを受信したときにUDP送信が例外をスローする必要があります。 – sipwiz

+0

UDPはステートレスなので、私はそれがすべきだとは思わない。 –

+0

アップして、適切な人が賞金を得ていることを確認してください。 – Joshua

1

:ここ

は、TCPパケットをキャプチャするための一例です前に書いたものとは違う。

セッションIDについてのKalmiからのコメントに基づいて、私は同じマシン上で2つのpingプログラムを開くことができる理由について考えさせてくれました。どちらもICMPであり、どちらもポートレスのRAWソケットを使用しています。これは、IPスタック内の何かを意味し、それらの応答が意図されたソケットを知る必要があります。 pingには、ECHO REQUESTとECHO REPLYの一部としてICMPパッケージのデータに使用されているIDがあります。

は、その後私は約ICMPウィキペディアにこのコメントに出くわしました:

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

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

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

正しい場合は、未処理のソケットを開いて手動でUDPパケットを作成し、戻ってくるものをリッスンし、必要に応じてUDPおよびICMPメッセージを処理する方法があります。私はコード内でどのように見えるか分かりませんが、それが難しいとは思わないし、winpcapソリューションよりも「エレガント」と考えることができます。

さらに、このリンクhttp://www.networksorcery.com/enp/default1003.htmは、低レベルのネットワークプロトコルにとって大きなリソースと思われます。

こちらがお役に立てば幸いです。

+0

ICMPセッションIDに関するあなたの意見は、私には理にかなっています。それは実際に問題の要点です。なぜ、ICMPメッセージが、配信不能なUDPパケットが私のアプリケーションに配信されたのかを示していないのですか?何らかの理由で、UDPパケットのためにWindowsがアプリケーションと一致しないようです。 – sipwiz

+1

私は、同じrawソケット上でUDPとICMPをマルチプレックスしようと考えましたが、おそらくUDP処理をすべて行う必要がありますが、Windowsとrawソケットを作成するときは、IPかIPかを選択する必要がありますICMPでは、両方を持つことはできません。 – sipwiz

+0

はい私はそれを生のIPソケットにして、すべてのICMPメッセージを処理しなければならないと思います。これは価値があるよりも複雑かもしれませんが、私はIPとICMPについて読むことができたので、winpcapスタイル以外の唯一の答えかもしれません。 – grieve

1

したがって、到達不能の戻りicmpパケットをプログラムで取得したいとしますか?タフなもの。私はネットワークスタックがあなたの近くのどこにでも入る前にそれを吸収すると言いたいと思います。

純粋なC#アプローチはここでは機能しません。 Windowsのipfiltdrv.sysを使ってパケット(icmp、tcp、udpなど)をトラップし、管理されたコードでそれらを読み書きするこのアプリケーションを見てみましょうc#)。

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

  • Oisin
3

UPDATE:私はおかしくなりそうだと思います....あなたが投稿したコードのその部分はまた、私のために働いています...

次のコードは、私のために正常に動作します(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

あなたは、あなたがpingや何かを送るとICMPパケットを受け取っているということを作品といいますか?私は、UDPがunreachbaleポートに送信していて、ICMPを受信できなかったときに使用していたことを確認しています。もう一度チェックしなければならない。 – sipwiz

+0

私は到達不能なホストとtcpでしかテストしていません...私はポート –

+0

を試してみました... udpと既存のホストでうまくいきます...テスト用にコメントアウトしました.. –

2

ICMPポート到達不能パケットは、もはやVista上でアクセス可能であることの問題に言及するウェブ上の記事の数があります。それはICMPを受信したとき

スタックは、例外をあなたに戻って与える必要があります。しかし、Vistaでは少なくともそれはありません。したがって回避策を試しています。

私はそれが不可能だと言う答えは好きではありませんが、そういう感じです。ですから、元の問題への一歩を踏み出すことをお勧めします。元の問題は、SIPでは長いタイムアウトでした。

  • ユーザーが(それゆえソートの スペックに準拠) タイムアウトを設定してみましょうことができます。
  • タイムアウトが終了する前に他のプロキシをチェックするなどの作業を開始できます。
  • は、あなたが悪いの宛先を知らキャッシュすることができ(それがキャッシュの 適切な管理が必要になります。
  • ICMP、およびUDPは、適切なエラーメッセージを与えるtcpまたは別のプロトコルを試していない場合。ただ、必要な情報を引き出すために。

3

ちょうど接続されているuDPソケットを使用すると、OSが到達不能ICMPにマッチし、uDPソケットのエラーを返します(何でも、それだけ。多くのリソースを取ることが可能である)。

Googleは接続されたudpソケット用です。

+0

UDPクライアントは、ICMP到達不能のためのソケット例外をスローしますが、ICMPホスト到達不能または実際には他のICMP到達不能メッセージのためにソケット例外をスローします。 – trampster

関連する問題