スキップしてメイン コンテンツに移動

P2P探訪 STUNでNat越え その4



まずは、スーパーノード候補として最有力なNATであるか調査してみましょう。
外部から見えている、「アドレスとポート」が常に一定であり。
他からのアクセスを制限していないNATのことです。(フルコーン)


○STUNでの確認方法

 どのようにして、制限があるかを確認するのでしょうか? STUNの仕組みはとても単純です。実際に通信してみて各種条件で通信ができるか試します。実際に試してみて、もしも通信できたならば、同一の条件下の端末とは通信可能といえるでしょう。


具体的には、実際に外部からメッセージを送信してもらう。そして、送信しもらったメッセージを受け取る事ができるかを確認します。

  • 接続したUDPサーバーから、メッセージを受け取れる事
  • 接続したUDPサーバーの異なるポートから、メッセージを受け取れること
  • 接続したUDPサーバーと異なるサーバーから、メッセージを受け取る事ができる事



○Bindingリクエスト

 実際にSTUNの通信内容を見ていきましょう。サーバーにレスポンスする条件(ポートとアドレス)を指定してレスポンスを返しもらいます。このレスポンスの依頼をSTUNでは、Bindingリクエストと読んでいます。

 例えば以下のような、依頼を出す事でしょう。
  1. CL はサーバーへ「受け取ったサーバーから、レスポンスを返してもらう。」依頼をだす。
  2. CL はサーバーへ「受け取ったサーバーから、ポートだけ変えてレスポンスを返してもらう。」依頼をだす。
  3. CL はサーバーへ「受け取ったサーバーから、ポートとアドレスを変えてレスポンスを返してもらう。」依頼をだす。


 3のレスポンスを受け取る事ができたならば、「フルコーン」といえます。



○ 実際に送受信するメッセージ

STUNではBindingリクエストと呼ばれるリクエストほをサーバーへサーバーへ送信します。


1. 送信先のアドレスとポートから返信するように依頼をだす。
{
0x00, 0x00,   // 最初の2byteは0
0x00, 0x01,   // Binding リクエストを意味する 0x01 
0x00, 0x08,   // Attrinuteのサイズ 8バイトを表す 0x08
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, // 16バイトのid
0x00, 0x03, // ChangeRequest 
0x00, 0x04, // attibuteのボティのサイズ 4
0x00, 0x00, 0x00, 0x00// 送信先のアドレスとポートから返信する
};

2. IPとポートを変えて返信

{
0x00, 0x00,   // 最初の2byteは0
0x00, 0x01,   // Binding リクエストを意味する 0x01 
0x00, 0x08,   // Attrinuteのサイズ 8バイトを表す 0x08
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, // 16バイトのid
0x00, 0x03, // ChangeRequest 
0x00, 0x04, // attibuteのボティのサイズ 4
0x00, 0x00, 0x00, 0x06// 送信先のアドレスとポートから返信する
};



以下のような返答をレスポンスを受け取る事ができます。

{
0x00, 0x00, // 最初の2バイトがは0
0x01, 0x01, // Bindingレスポンスを意味する。0x101 
0x00, 36, // length
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, //id
// 
0x00, 0x04// レスポンスをしてくれたサーバーのアドレスを意味する(SOURCE_ADDRESS)
0x00, 0x08, //length
0x00, 0x01, //family
0x03, (byte)(0xFF&0x20), //レスポンスしてくれたポート
127, 0, 0, 1,//レスポンスしてくれたアドレス
//
0x00, 0x01, // サーバーから見えたクライアントのアドレス(MAPPED_ADDRESS),
0x00, 0x08, //length
0x00, 0x01, //family
0x03, (byte)(0xFF&0x20), //サーバーから見えたクライアントのポート
127, 0, 0, 1,//サーバーから見えたクライアントのアドレス
//
0x00, 0x05, // STUNサーバーが持つ、異なるポートと異なるアドレス(CHANGE_ADDRESS)
0x00, 0x08, //length
0x00, 0x01, //family
0x03, (byte)(0xFF&0x20), //port
127, 0, 0, 1,


};

うけとったレスポンスを元に、自身がフルコーンなのか判定できます。判定するのは、STUNサーバーではなく。STUNクライアントであるところに注意してください。
今回の場合、このレスポンスを受け取る事ができたならば、フルコーンであると言えます。

参照検証ように作成したコード

https://github.com/kyorohiro/Hetimatan/blob/master/Hetimatan/src_nat/net/hetimatan/net/stun/HtunServer.java


まとめ/次回予告


という事で、STUNの正体は見えてきたでしょうか。
基本的な考え方は、「様々な条件で通信ができるか試してみ。どの条件で通信できたかで分類する」と行ったものである事が理解できたでしょう。

次回は、UDPパンチについて解説します。通信に細工をする事でNATをだまして、通信できるようにするテクニックです。






コメント

  1. 微妙に、受け渡しするメッセージが間違っている。
    todo 訂正すること

    返信削除

コメントを投稿

このブログの人気の投稿

KyoroStressの技術 -1- Low Memory Killer を意図的に発生させたい

[課題] Low Memory Killer を意図的に発生させたい Androidには、ヒープが涸渇すると使われていないアプリをKillする機能があります。 この記事では、意図的にヒープを枯渇させて、この状態をつくる方法について説明します。 単純にヒープを大量に消費するアプリを作成すれば良いように思えます。 しかし、これだけでは上手くいきません。   -A ひとつのアプリで消費できるヒープが制限されているため、ひとつのアプリで端末のヒープが涸渇している状態をつくれない。   -B ヒープを涸渇しているアプリがPFにKILLされる場合がある。 といった問題があります。 KyoroStressV2での解決方法を紹介します。 [KyoroStressでの解決方法] Kyoro Stress では、以下のような方法をとりました。 - 1. 複数のServiceを、各々異なるプロセスで起動する。 - 2. 各々Serviceで大量のヒープを消費する。 複数のプロセスを立ち上げれば、PFのヒープを枯渇させることができます。これで、(A)の問題が解決できました。 また、Bについては、「生きているプロセス」が「KILLされたプロセス」の分もヒープを消費すれば上手くいけそうです。 [BigEater(ヒープ消費サービス)の動作] KyoroStressV2で、ヒープを消費するサービスは以下のシナリオで動作しています。 - 1. 指定されたヒープを取得する。 is retry が true の時、指定されたヒープを取得できるまで、1を何度も繰り返す。 - 2. KILLされたサービスを復活させる。 is retry が true の時、Threadが死ぬまで、何度も2を繰り返す。 - 3. 終了 といった感じです。 このままでは、すべてのServiceがPFにKILLされたら上手くいかないように思うかも知れません。 しかし、時間がたつと(数秒)、PFはKILLしたServiceを再起動します。 このため、ServiceがすべてKILLされても、ヒープを大量に消費しようとする状態は保持されます。 [使い方] KyoroStressV2の操作方法…

P2P探訪 STUNでNAT越え その1

UPnPを用いて、NAT越えできました。しかし、ルータがUPnPをサポートしていなかったり。UPnPだけでは越えられないNATがあります。

本文では、その代案として前回解説できなかった。「適当なサーバーに接続してみて、相手から見えているアドレスを返してもらう方法」について解説していきます。

TCPの限界 インターネットで公開されている情報のほとんどは、TCPという通信方法でデータをやり取りされています。ですから、インターネットで情報を公開したい場合は、TCPサーバーを立ち上げる事を考える事でしょう。
 しかし、ルータがUPnPをサポートしていない場合、TCPを用いたサーバーを運用する事は困難になります。※ 基本、無理と考えもらって問題ありません。


接続相手から教えてもらう方法はどうした? 適当なサーバーに接続してみて、相手から見えているアドレスを返してもらう事で実現できないのでしょうか。前回はできそうな事を臭わせていました。しかし、TCPにおいて、これは困難です。

実際にTCPのプログラムを書き確認して見ましょう。接続相手のホストアドレスは推測できます。しかし、ポート番号を知るすべはありません。


import java.io.IOException; import java.net.Inet4Address; import java.net.ServerSocket; import java.net.Socket; import java.net.UnknownHostException; public class TCPTest { public static void main(String[] args) { TCPTest test = new TCPTest(); test.startServer(); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } test.startClient(); } private Server mServer = new Server(); public void startServer() { mServer.start(); } public v…

P2P探訪 Raider その1-2 Torrentファイルフォーマット

というわけで、前回に引き続いて、この記事ではTorrentファイルについて説明します。 [Torrent file format] 前回、Bencodingを実装したのでTorremt Fileを読み込めることができるようになりました。 今回は、Torrentファイルから必要な情報を読み込む方法について解説します。 torretファイルから取得できる情報はどんなものかは、別の機会に解説します。 ここでは、torrentファイルには 2つのフォーマットがあることとデータ構造を説明します。 たとえば、「"announce"というデータが何なのか?」については解説しません。 torrentファイルでは、ダウンロード/アップロードの対象としているファイルが、ひとつの場合と複数の場合で構造がすこしだけことなります。 ひとつの時を、「single file」 複数の時を「multi file」と呼ぶことにます。 では、データ構造を紹介します。 - single file pattern bendiction benstring "announce" beninteger "creation date" bendiction "info" beninteger "length" benstring "name" beninteger "piece length" bebstring "pieces" - multi file pattern bendiction benstring "announce" beninteger "creation date" bendiction "info" benlist "files" bendiction beninteger "length" benlist "path" benstring be…