web-dev-qa-db-ja.com

Java UDPホールパンチングの例-ファイアウォールを介した接続

私が2台のコンピュータを持っているとしましょう。

彼らはice4jを介してお互いにパブリックIPとプライベートIPを知っています。

1つのクライアントがリッスンし、もう1つのクライアントが文字列を送信します。

これがUPDホールパンチングによって発生することを確認します。

Let A be the client requesting the connection

Let B be the client that is responding to the request

Let S be the ice4j STUN server that they contact to initiate the connection
--
A sends a connection request to S

S responds with B's IP and port info, and sends A's IP and port info to B

A sends a UDP packet to B, which B's router firewall drops but it still
punches a hole in A's own firewall where B can connect

B sends a UDP packet to A, that both punches a hole in their own firewall,
and reaches A through the hole that they punched in their own firewall

A and B can now communicate through their established connection without 
the help of S

対称NATを介してホールパンチングを実行する方法の疑似例を投稿できますか?ポートSを推測し、クライアントAとBの間の接続を確立するのに役立つサーバーSがあると仮定します。

二重のNATも考慮に入れれば、いいでしょう。

注意:

STUNを使用してIPとポートを検出できますが、keepalive手法を介してIP:Portをサーバーに送信する独自​​のコードを記述する必要があります。

1つのクライアントがサーバー上の一意のIDを介して他のクライアントを識別すると、送受信する必要のあるデータをUDPホールパンチする他のクライアントのIP:ポート情報が提供されます。

少し更新:

Javaの地平線に表示されているライブラリがありますので、チェックしてください:
https://github.com/htwg/UCE#readme

36
Mat B.

この例はJavaではなくC#にありますが、NATトラバーサルの概念は言語に依存しません。

NATトラバーサルが組み込まれたMichael Lidgrenのネットワークライブラリを参照してください。

リンク: http://code.google.com/p/lidgren-network-gen3/ NATを処理する特定のC#ファイルトラバーサル: http:// code .google.com/p/lidgren-network-gen3/source/browse/trunk/Lidgren.Network/NetNatIntroduction.cs

あなたが投稿したプロセスは正しいです。 NATデバイス の4つの一般的なタイプのうち3つだけで機能します(NATの動作は実際には標準化されていないため、一般的に言っています):フルコーンNAT、制限付きコーンNAT、およびポート制限コーンNAT。 NATトラバーサルは、セキュリティを強化するために主に企業ネットワークで見られる対称NATで機能しません。一方がSymmetric NATを使用し、もう一方が使用しない場合でも、NATをトラバースすることは可能ですが、より多くの推測が必要です。 Symmetric NATからSymmetric NATへのトラバーサルは非常に困難です- これに関する論文をここで読むことができます

しかし、実際には、あなたが説明したプロセスは正確に機能します。 自分のリモート画面共有プログラム のために実装しました(残念ながらC#でも)。 Windowsファイアウォール(Windowsを使用している場合)とサードパーティのファイアウォールが無効になっていることを確認してください。しかし、はい、私はそれがうまくいくことを喜んで確認できます。

NATトラバーサルのプロセスの明確化

このアップデートは、あなたと将来の読者のためのNATトラバーサルのプロセスを明確にするために作成しています。うまくいけば、これは歴史とプロセスの明確な要約になることができます。

いくつかの参照ソース: http://think-like-a-computer.com/2011/09/16/types-of-nat/ 、および http://en.wikipedia .org/wiki/Network_address_translationhttp://en.wikipedia.org/wiki/IPv4http://en.wikipedia.org/wiki/IPv4_address_exhaustion =。

約43億台のコンピューターを一意に命名できる容量を持つIPv4アドレスが不足しています。賢い人々はこの問題を予測し、他の理由の中で、自身に接続されたコンピューターのネットワークに1つの共有IPアドレスを割り当てることにより、ルーターがIPv4アドレスの枯渇に対処することを発明しました。

LAN IPがあります。そしてWAN IPがあります。 LAN IPはローカルエリアネットワークIPで、ローカルネットワーク内のコンピューターを一意に識別します。たとえば、ホームルーターに接続されているデスクトップ、ラップトップ、プリンター、スマートフォンなどです。 WAN IPは、ワイドエリアネットワーク内のローカルエリアネットワーク外のコンピューターを一意に識別します。一般的にはインターネットを意味します。したがって、これらのルーターはコンピューターのグループに1 WAN IPを割り当てます。各コンピューターには、独自のLAN IPがまだあります。 LAN IPは、コマンドプロンプトにipconfigと入力して_IPv4 Address . . . . . . . . 192.168.1.101_を取得したときに表示されるものです。 WAN IPは、_cmyip.com_に接続して_128.120.196.204_を取得したときに表示されるものです。

無線スペクトルが購入された と同様に、IP範囲全体が購入され、代理店や組織によって予約されます およびポート番号 。短いメッセージは、繰り返しますが、これ以上IPv4アドレスを節約する必要はありません。

これはNATトラバーサルとどのような関係がありますか?まあ、ルーターが発明されて以来、直接接続( エンドツーエンド接続 )は、いくつかのハックなしでは、いくらか...不可能でした。 2台のコンピューター(コンピューターAとコンピューターB)のネットワークで、WAN IPを_128.120.196.204_で共有している場合、どのコンピューターに接続しますか?外部コンピューター(google.comなど)について話している initiating _128.120.196.204_への接続。答えは、 nobody knows であり、ルーターもそうではないため、ルーターが接続をドロップします。コンピュータAがを開始たとえば_google.com_への接続を開始する場合、それは別の話です。次にルーターは、LAN IP _192.168.1.101_のコンピューターAが_74.125.227.64_(google.com)への接続を開始したことを記憶します。コンピューターAの要求パケットがルーターを離れると、ルーターは実際にを書き直し LAN IP _192.168.1.101_からルーターのWAN IP of _128.120.196.204_に。そのため、google.comがコンピュータAのリクエストパケットを受信すると、ルータが再書き込みした送信元IPが認識され、コンピュータAのLAN IPは認識されません(google.comは、応答するIPとして_128.120.196.204_を認識します)。 google.comが最終的に応答すると、パケットはルーターに到達し、ルーターはルーター remembers (状態テーブルがある)がgoogle.comからの応答を予期していたことを示し、適切に転送します。コンピューターAへのパケット.

つまり、 you が接続を開始しても、ルーターは問題ありません。ルーターは、応答パケットを(上記のプロセス全体を通じて)コンピュータに転送して戻すことを覚えています。ただし、外部サーバーがあなたへの接続を開始すると、コンピューターAとコンピューターBの両方がWAN _128.120.196.204_のIP ...元の宛先ポートXに向かうすべてのパケットを転送し、コンピューターAの宛先ポートYに転送するようにルーターに指示する明確なルールがない限り。これは port-forwarding として知られています。残念ながら、ネットワークアプリケーションにポート転送を使用することを考えている場合、ユーザーがそれを有効にする方法を理解していない可能性があり、セキュリティリスクであると考える場合は有効にすることに消極的である可能性があるため、実用的ではありません。 UPnP は、プログラムでポート転送を有効にすることを可能にするテクノロジーを単に指します。残念ながら、UPnPを使用してネットワークアプリケーションをポート転送することを考えている場合も、UPnPが常に使用できるとは限らないため、実用的ではありません。

それでは解決策は何ですか?解決策は、トラフィック全体を自分のコンピューター(グローバルに到達できるように慎重に事前構成されている)経由でプロキシするか、システムに勝つ方法を考え出すことです。最初の解決策は(私は信じています) [〜#〜] turn [〜#〜] と呼ばれ、サーバーのファームに利用可能な帯域幅を提供するという代償を払って、すべての接続の問題を魔法のように解決します。 2番目のソリューションはNATトラバーサルと呼ばれ、これを次に調査します。

以前、外部サーバー(たとえばgoogle.com)が_128.120.196.204_への接続を開始するプロセスについて説明しました。私は、ルーターがグーグルの接続要求をどのコンピュータに転送するかを理解するための特定のルールがなければ、ルーターは単に接続をドロップするだろうと述べました。これは一般的なシナリオであり、NATにはさまざまなタイプがあるため、正確ではありません。 (注:ルーターは、床に落とすことができる実際の物理デバイスです。NAT(ネットワークアドレス変換)は、ルーターにプログラムされたソフトウェアプロセスで、ツリーのようにIPv4アドレスを保存できます)。したがって、ルーターが使用する which NATによって、接続シナリオは異なります。ルーターは、 combine NATプロセスすらあります。

標準化された動作を持つ4つのタイプのNATがあります。フルコーンNAT、制限付きコーンNAT、ポート制限付きコーンNAT、および対称NATです。これらのタイプの他に、標準化されていない動作を持つ他のタイプのNATが存在する可能性がありますが、それはまれです。

注:私はNATについてあまり詳しくありません...ルーターを見る方法はたくさんあるようで、このトピックについてはインターネット上の情報が非常に広まっています。完全な、制限された、およびポート制限されたコーンによるNATの分類は、いくらか非推奨になりました、とウィキペディアは言いますか?静的および動的NATと呼ばれるものがあります...調整できないさまざまな概念の集まりにすぎません。それでも、次のモデルは自分のアプリケーションで機能しました。 NATの詳細については、以下のリンク、上記のリンク、およびこの投稿全体を参照してください。それらについてあまり理解していないので、これ以上投稿することはできません。

一部のネットワークの教祖が入力を修正/追加して、この神秘的なプロセスの詳細を学べることを期待しています。

各クライアントの外部IPとポートの収集に関する質問に回答するには:

すべてのUDPパケットのヘッダーは同じ構造 one ソースIPおよび one ソースポート。 UDPパケットヘッダーには、「内部」送信元IPと「外部」送信元IPが含まれていません。 UDPパケットヘッダーには、1つのソースIPのみが含まれます。 「内部」と「外部」のソースIPを取得したい場合は、実際にペイロードの一部として内部ソースIPを送信する必要があります。しかし、内部ソースIPが必要で、港。あなたの質問が述べたように、あなたはあなたが外部IPとポートだけを必要とするように思えます。つまり、ソリューションは、ソースIPを読み取り、フィールドのようにパケットからポートを外すことです。

以下の2つのシナリオ(実際には何も説明されていません):

LAN通信

コンピューターAのLAN IPは192.168.1.101です。コンピューターBのLAN IPは192.168.1.102です。コンピューターAはポート3000からコンピューターBのポート6000にパケットを送信します。UDPパケットのソースIPは192.168.1.101です。そして、それが唯一のIPになります。ネットワークは純粋にローカルエリアネットワークであるため、「外部」にはここではコンテキストがありません。この例では、広域ネットワーク(インターネットなど)は存在しません。ただし、ポートについては、NATがわからないため、パケットに記載されているポートが3000になるかどうかはわかりません。NATデバイス may re -パケットのポートを3000から49826のようなランダムなものに書き込みます。どちらの方法でも、パケットに記載されているポートを使用して返信する必要があります。これは、返信に使用するものです。したがって、このLAN通信の例では、1つのIP-LAN IPのみを送信する必要があります。ポートについて心配する必要はありません。ルーターが自動的に処理します。パケットを受信すると、パケットから読み取るだけで、IPとポートのみを収集します。

WAN通信

コンピュータAのLAN IPも192.168.1.101です。コンピュータBのLAN IPも192.168.1.102です。コンピュータAとコンピュータBはどちらも、128.120.196.204のWAN IPを共有します。サーバーSはサーバー、つまりAmazon EC2サーバー上のグローバルに到達可能なコンピューターであり、WAN IPが1.1.1.1です。サーバーSはLAN IPを持っているかもしれませんが、それは無関係です。コンピュータBも無関係です。

コンピューターAは、ポート3000からサーバーSにパケットを送信します。ルーターから出る途中で、コンピューターAからのパケットの送信元LAN IPは、ルーターのWAN IPに書き換えられます。ルーターはまた、300の送信元ポートを32981に書き換えます。外部IPとポートに関して、サーバーSは何を認識していますか?サーバーSは192.168.1.101ではなくIPとして128.120.196.204を認識し、サーバーSは3000ではなくポートとして32981を認識します。これらは元のIPではなく、コンピューターAがパケットの送信に使用したポートですが、これらは正しいIPですと返信するポート。パケットを受信すると、WAN IPと書き換えられたポートしか知ることができません。それが必要な場合( external IPとポートのみを要求した場合)、設定は完了です。それ以外の場合、送信者の内部IPも必要な場合は、ヘッダーから通常のデータ separate として送信する必要があります。

コード:

上記のように(以下外部IPの収集に関する質問に回答するには)、各クライアントの外部IPとポートを収集するには、それらを読み取るだけですパケット。送信される各データグラムには、 always に送信元の送信元IPと送信元ポートがあります。これらの2つのフィールドは常に含まれているため、特別なカスタムプロトコルも必要ありません。すべてのUDPパケットには、定義上、これらの2つのフィールドが必要です。

_// Java language
// Buffer for receiving incoming data
byte[] inboundDatagramBuffer = new byte[1024];
DatagramPacket inboundDatagram = new DatagramPacket(inboundDatagramBuffer, inboundDatagramBuffer.length);
// Source IP address
InetAddress sourceAddress = inboundDatagram.getAddress();
// Source port
int sourcePort = inboundDatagram.getPort();
// Actually receive the datagram
socket.receive(inboundDatagram);
_

getAddress()およびgetPort()は、クライアント(送信)マシンで、設定に応じて宛先ポートまたは送信元ポートを返すことができるため、setAddress()を呼び出します。およびsetPort()をサーバー(受信)マシンに送信し、サーバー(受信)マシンでsetAddress()およびsetPort()を呼び出してクライアント(送信)マシンに送信します。 receive()でこれを行う方法が必要です。これ(getAddress()およびgetPort()が期待するソースIPおよびポートを返さない)が実際のロードブロッキングである場合は、詳しく説明してください。これは、サーバーが「標準」のUDPサーバーであると想定しています(STUNサーバーではありません)。

更なる更新:

STUNを使用して1つのクライアントからIPとポートを取得し、それを他のクライアントに提供する方法/」に関する更新を読みましたか? STUNサーバーは、エンドポイントを交換したり、NATトラバーサルを実行するようには設計されていません。 STUNサーバーは、パブリックIP、パブリックポート、およびNATデバイスのタイプ(フルコーンNAT、制限付きコーンNAT、ポート制限付きコーンNATのいずれか)を通知するように設計されています。エンドポイントの交換と実際のNATトラバースの実行を担当する仲介者サーバーを「イントロデューサー」と呼びます。 私の個人プロジェクト では、実際にNATトラバースを実行するためにSTUNを使用する必要はありません。私の「紹介者」(クライアントAとBを紹介する仲介サーバー)は、UDPデータグラムをリッスンする標準サーバーです。クライアントAとBの両方が紹介者に自分自身を登録すると、紹介者はパブリックIPとポート、およびプライベートIP(LANに接続している場合)を読み取ります。パブリックIPは、すべての標準UDPデータグラムと同様に、データグラムヘッダーから読み取られます。プライベートIPはデータグラムペイロードの一部として書き込まれ、イントロデューサはそれをペイロードの一部として読み取るだけです。したがって、STUNの有用性については、各クライアントのパブリックIPとパブリックポートを取得するためにSTUNに依存する必要はありません。接続されているソケットはこれを通知します。 STUNは、クライアントが使用しているNATデバイスのタイプを判別するためにのみ役立つので、NATトラバーサルを実行するかどうかがわかります(NATデバイスタイプはフルコーン、制限付き、またはポート制限)、または全面的なTURNトラフィックプロキシを実行します(NATデバイスタイプが対称の場合)。

ロードブロッキングについて詳しく説明してください。アプリケーションメッセージングプロトコルを設計するためのベストプラクティスに関するアドバイス、および受信したメッセージのフィールドを規則的かつ体系的な方法で読むことに関するアドバイス(以下に投稿したコメントに基づく)が必要な場合は、現在の状況を共有してください。方法?

52
Jason

あなたの質問は本当に広いです-私は例を提供できませんが、次のリンクが役立つかもしれません(仕様、ライブラリ、サンプルなど):

10
Yahia

あなたの問題はJava関連ではありません。UDP接続を開く方法を知っている場合はそれで十分です。次の link の内容を読んでください。タイトルに怖がって、UDPもカバーしています。残りはJavaコーディングです。

P.S。:あなたのシナリオでは、足りないステップがあります。 Sは、AがBに到達しようとしていることをBに伝える必要があるため、AとBの両方がSへのオープン接続を持っている必要があります。 BにSへのオープン接続がない場合、AとBが一緒に通信を開始する方法はありません。

[〜#〜]更新[〜#〜]

Jasonの回答には、NATトラバーサルに関する誤解と誤解が含まれています。 Saikat Guha(mpi-sws.org/~francis/imc05-tcpnat。 pdf) この問題を本当に理解するために。ウィキペディアのコーン分類は完全に時代遅れで誤解を招くものです。

6

STUNは基本的に次のように機能します。ファイアウォールの背後にあるクライアントは、ファイアウォールの外側にあるSTUNサーバーに接続します。 STUNサーバーは、クライアントから受信したパケットを検査し、クライアントのIPとポートを含む応答をクライアントに送信して、STUNサーバーに表示されます。

これは、ファイアウォールの背後にあるクライアントが独自の外部IPとポートを検出する方法です。私の知る限り、STUNサーバーは通常、あるクライアントから別のクライアントにアドレス情報を渡しません。

ファイアウォールがすでにシグナリングトラフィックに開放されている場合、通常、STUNはファイアウォールを通過するメディアストリームをセットアップするために使用されます。 VoIPの場合:クライアントは、STUNサーバーに接続してUDPトラフィック用の独自の外部IPおよびポートを検出し、既知のオープンポート(他の外部UDPアドレス情報を含む)で他のクライアントにシグナリング要求(SIP INVITEなど)を送信します。ペイロード(SDPなど)。そのため、一般に、ピアツーピア通信のシグナリングのために、開いているポートを介して1つのクライアントに到達できる必要があります。

6
vladimir e.