WebRTCでP2Pは身近になりつつある。だが一般的なユーザーの端末がNATの元でどのようにP2Pを行っているかは理解しづらい部分も多い。今回はその手法の一つであるUDPホールパンチングについて解説する。
おことわり
以下、記事中で登場する、デバイス
、サーバー
、NAT
は次のとおりの意味で用いている。
ワード | 意味 |
---|---|
デバイス | スマートフォンなど、LAN内にいることを想定した端末。 |
サーバー | グローバルIPを持っている機器。 |
NAT | LANとWANを隔てている。NAPTを含む。 |
NATの種類について
まずは攻略すべきNATについて知ることが重要である。NATには(RFCによれば)大まかに以下の4種類が存在する。
- (full) cone NAT
- restricted cone NAT
- port-restricted cone NAT
- symmetric NAT
上に挙げたものは後に行けば行くほどNAT超えが難しくなる。ここでそれぞれについて簡単に説明しておこう。
NAT | 手法 |
---|---|
(full) cone NAT | 一番シンプル。{デバイスのローカルIP}:{デバイスのポート} と{NATのグローバルIP}:{デバイスのポートと同じポート} を割り当てる。外から入ってくるパケットに制約はない。 |
restricted cone NAT | ↑と比較すると、NATが宛先のサーバーのIPも監視しており、全く関係のない(デバイスが直近にパケットを送信していない)IPからのアクセスはブロックする点が異なる。それ以外は同じ。 |
port-restricted cone NAT | ↑と比較すると、NATはIPだけでなくポートも含めて監視しブロックする点が異なる。たとえ同じIPからのパケットでもポートが異なればブロックしてしまう。それ以外は同じ。 |
symmetric NAT | cone NATの共通点であった、デバイスのポート とNAT変換後のポート が等しいという制約がない。つまり、デバイスによって変換後のポートが制御できないことを意味している。 |
ここでcone NAT系のNATはデバイスのポート
とNAT変換後のポート
が一致するという特徴から、内部のデバイスからNAT変換後のポートを制御可能であることは重要なので意識していてほしい。
ちなみにcone系のNATでは複数のデバイスが同じポートを用いて通信しようとしたとき、NATのマッピングが衝突することは明らかである。その場合NATはsymmetric NATに動作を切り替えるなど、柔軟に動作している。
上の衝突の例もそうだが、すべての現実のルーターを上記4つの方法に分類できるとは限らないため注意が必要である。
NAT超えの手法
上に上げた4つのうち、
- (full) cone NAT
- restricted cone NAT
- port-restricted cone NAT
の3つに関しては確立された手法が存在する。一般にUDPホールパンチングと呼ばれるものだが、これはSTUN
プロトコル内でも言及されており、比較的由緒正しい方法である。ここではその方法を一つ解説する。
full cone NATに関しては以下の解説よりも、もっと簡単な方法でNAT超えが可能ではあるが、すべて説明するのは大変なのでport-restricted cone NATの場合についてのみ解説する。port-restricted cone NATで利用可能な手法がfull cone NATとrestricted cone NATで利用可能なのは明らかであろう。
デバイスAとデバイスBがP2P通信をしたい場合を考える。ここでそれぞれのデバイスはport-restricted cone NATの管理下にあり、外部には利用可能な一台のグローバルIPをもったサーバーがいるとする。
1.デバイスの変換後のIP/ポートをデバイスA、デバイスBが知る
これについて詳しく説明する必要はないであろう。サーバーはデバイスA、BのグローバルIPを観測できるので、それぞれのIP(とポート)を知っている。これらをサーバーがそれぞれ逆のデバイスに伝えてやれば良い。ここで、cone NATであるから、変換後のポートはそれぞれのデバイスで制御されていることは意識していてほしい。
2.実際にP2Pを行う
手順1でIP/ポートを交換後、サーバーは既に不要である。ここからはデバイスA、デバイスBを図を用いて説明する。
以下に登場するパケットはすべてUDPである。
項目 | 内容 |
---|---|
① | Aは変換後のポートをBに手順1で伝えたポートに送信源を固定し、かつBの予め伝えられたIP/ポートに対してパケットを送信する。当然、パケットはRejectされる。しかし、ここでAはBからのパケットを待っている(=受け付けている)状態になっている。 |
② | BはAからのパケットを受け取ったわけではないが、AのIP/ポートを予め知っている。そのためそこに対してAに手順1で伝えたポートに送信源を固定してAにパケットを送信する。Aは①で返信待ちなのでパケットはデバイスまで到達する。さらに、ここでBはAからの返信待ちとなる。 |
③ | Aはもう一度①と同じようにしてBにパケットを送る。Bは②で返信待ちなのでパケットはデバイスまで到達する。 |
これによりA,Bは交互にパケットを送信することで半永久的に通信が可能である。(もちろんUDPだが。)これがUDPホールパンチングと呼ばれる手法となる。
WebRTCではこの上に暗号化のプロトコルなどを載せて使用しているらしい。
symmetric NATの場合は
NAT変換後のポートが制御できないsymmetric NATは、上記の方法ではうまく行かないことは分かるだろう。そういった場合TURN
という手法を用いることが多い。
ここで、「なんだ別の方法があるならいいじゃないか」思われた方もいるかもしれないが、TURN
をP2Pと呼んでいいのか怪しい部分はある。TURN
はすべての通信をグローバルIPを持ったサーバーがプロキシする。そのためP2Pにおけるメリットがほとんど生かされていないのだ。
一応調査はされていて、NATが外部ポートに割り当てる値の規則性から接続を試みるというやり方もあるが、これも完全ではない。試行錯誤はしているうようなので興味があれば調べてみるのもよいだろう。