質問
SIPコールをセットアップするためにUDPパケットを送信する必要があるSIPアプリケーションがあります。 SIPには、配信エラーに対処するためのタイムアウトメカニズムがあります。さらに、SIPが使用する32秒の再送信間隔を待機するためにUDPソケットが閉じられているかどうかを検出することもできます。
言及しているのは、UDPソケットに送信しようとすると、リモートホストによってICMP Destination Unreachableパケットが生成される場合です。稼働中のホストにUDPパケットを送信しようとしたが、ポートがリッスンしていない場合、ICMPメッセージがパケットトレーサーで戻ってくるのを見ることができますが、質問はC#コードからどのようにアクセスするのですか? p>
生のソケットで遊んでいますが、まだプログラムで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.100(私のPC)から10.0.0.138(私のルーター)にUDPパケットを送信する試みから私のPCに送信されるICMP応答を示すWiresharkトレースです。私の問題は、これらのICMPパケットを利用して、アプリケーションが任意の期間後にタイムアウトするのを待つのではなく、UDP送信が失敗したことを認識する方法ですか?
解決
ほぼ3年後、私は http://www.codeproject.comに出くわしました/ Articles / 17031 / A-Network-Sniffer-in-C は、Windows 7でICMPパケットを受信するための解決策を見つけるのに十分なヒントを与えてくれました(Vistaについては知らない、元の質問についてでしたが、私はこの解決策がうまくいくと思います)。
2つの重要な点は、ソケットをIPAddress.AnyおよびSIO_RCVALLフラグを設定するIOControl呼び出しではなく、単一の特定のIPアドレスにバインドする必要があることです。
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
他のヒント
Icmpは識別子を使用していますが、これはicmp" session"ごとに異なるようです。 (すべてのicmpソケットに対して)。したがって、同じソケットによって送信されていないicmpパケットへの返信は、除外されます。これが、そのコードが機能しない理由です。 (これについてはわかりません。ICMPトラフィックを見た後の仮定にすぎません。)
ホストにpingを実行し、ホストに到達できるかどうかを確認してから、SIPを試すことができます。ただし、他のホストがicmpを除外している場合は機能しません。
い(しかし機能している)ソリューションは winpcap を使用しています。 (これが唯一の有効なソリューションであるというのは、あまりにも悪いと思われるためです。)
winpcapを使用するということは、 ICMPトラフィックをキャプチャしてから、キャプチャしたパケットがUDPパケットが配信不能かどうかを確認することができるということです。
>tcpパケットをキャプチャする例は次のとおりです。 http://www.tamirgal.com/home/ SourceView.aspx?Item = SharpPcap& File = Example6.DumpTCP.cs (ICMPで同じことをするのはそれほど難しくないはずです。)
更新:夢中になっていると思う....あなたが投稿したコードの一部も私のために働いています...
次のコードは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");
}
}
}
}
}
接続されたudpソケットを使用するだけで、OSは到達不能なicmpと一致し、udpソケットでエラーを返します。
接続されたudpソケットのGoogle。
VistaではICMPポート到達不能パケットにアクセスできなくなる問題について言及しているウェブ上の投稿が多数あります。
- http://www.eggheadcafe.com /software/aspnet/31961998/icmp-port-unreachable-and.aspx
- http: //social.msdn.microsoft.com/Forums/en-US/Offtopic/thread/5bd8b275-cc6f-43cd-949d-7c411973b2f3/
ICMPを受信すると、スタックは例外を返します。しかし、少なくともVistaではそうではありません。したがって、回避策を試みています。
それは不可能だと言う答えは好きではありませんが、そのように思われます。したがって、元の問題に戻ることをお勧めします。元の問題は、SIPの長いタイムアウトでした。
- ユーザーに設定させることができます タイムアウト(したがって、 仕様)。
- 前に他のこと(他のプロキシのチェックなど)を開始できます タイムアウトが終了します。
- 既知の不正な宛先をキャッシュできます(ただし、 キャッシュの適切な管理。
- icmpおよびudpで適切なエラーメッセージが表示されない場合は、tcpまたは別のプロトコルを試してください。目的の情報を引き出すだけです。
(何でも可能です。多くのリソースが必要になる場合があります。)
詳細は以前に書いたものとは完全に異なるため、これを別の回答として書いています。
KalmiからのセッションIDに関するコメントに基づいて、同じマシンで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つは、生のソケットを開いてUDPパケットを手動で作成し、戻ってくるものをリッスンし、必要に応じてUDPおよびICMPメッセージを処理することです。それがコードでどのように見えるかはわかりませんが、それが難しすぎるとは思いませんし、より「エレガント」とみなされるかもしれません。 winpcapソリューションよりも。
さらにこのリンク、 http://www.networksorcery.com/enp/default1003.htm は、低レベルネットワークプロトコルの優れたリソースのようです。
これが役立つことを願っています。
では、プログラムでdest unreachable return icmpパケットを取得したいですか?難しいもの。ネットワークスタックは、近くに行く前にそれを吸収してしまうと思います。
ここでは、純粋なC#アプローチが機能するとは思わない。フックを取得するには、ドライバーレベルのインターセプトを使用する必要があります。Windowsのipfiltdrv.sysを使用してパケット(icmp、tcp、udpなど)をトラップし、マネージコードで読み取り/再生するこのアプリをご覧ください( c#)。
http://www.codeproject.com/KB/IP /firewall_sniffer.aspx?display=Print
- オイシン