UDPホールパンチングについて知る

WebRTCでP2Pは身近になりつつある。だが一般的なユーザーの端末がNATの元でどのようにP2Pを行っているかは理解しづらい部分も多い。今回はその手法の一つであるUDPホールパンチングについて解説する。

おことわり

以下、記事中で登場する、デバイスサーバーNATは次のとおりの意味で用いている。

ワード意味
デバイススマートフォンなど、LAN内にいることを想定した端末。
サーバーグローバルIPを持っている機器。
NATLANと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 NATcone 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である。

punch

項目内容
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が外部ポートに割り当てる値の規則性から接続を試みるというやり方もあるが、これも完全ではない。試行錯誤はしているうようなので興味があれば調べてみるのもよいだろう。

参考

Built with Hugo
Theme Stack designed by Jimmy