WebRTCにおけるNAT越えの課題へのアプローチ 

WebRTCは、音声映像メディアやデータをP2Pで通信する能力をブラウザに提供します。しかし、既存のインターネットの世界ではNATが広く用いられており、容易にP2Pを利用することはできません。この記事では、NATに対してどのような課題がWebRTCに存在し、どのようなアプローチで解決を試みているかについて解説します。

## インターネットの世界におけるNATの存在

今日のインターネットにおいて、エンドユーザが用いるホストのほとんどは何らかのNATデバイスの配下に設置されています。これはWebに携わるエンジニアの間では広く知られている事実でしょう。全世界に広がった広大なインターネットの世界に対応するためにIPv6が新たなインターネットプロトコルとして発案されてかなりの時刻が経過しました。にもかかわらず、古いインターネットプロトコルであるIPv4が一般的に用いられており、しかもそのアドレスは既にとっくに枯渇し始めています。NATの存在はIPv4の延命に加担しているかもしれません。NATは単一のIPアドレスを複数のホストで共有することを可能としています。

インターネット上でのP2Pの利用においてNAT越え（またはNATトラバーサルとも呼ばれる）の技術は重要な関心事項となり得るでしょう。サーバクライアント型のサービスを用いる場合では、NATの存在はそれほど問題とはなりませんが、NAT配下のホスト同士で通信しようとする場合では問題となってしまいます。なぜならば、NATデバイスの外側から、その配下のホストに対して直接パケットを送信する手段は存在していないからです。NAT超えの技術は、あらかじめNATデバイスにホールパンチ（穴あけ）を行うことによって、NATの外側からNAT配下のホストへのパケットの送信を実現します。

## NAT超えの技術：STUNとICE

![reflexive.png](https://qiita-image-store.s3.amazonaws.com/0/10409/0d5c0071-efa1-3f2c-0834-235da5719999.png)

WebRTCでは、STUNとICEを用いることによってNATデバイスに対するホールパンチを試みます。ホールパンチのテクニックはP2P通信が必要なインターネットゲーマーたちによって開拓され、現在ではIETFで標準化された技術となり、その仕様はRFCとして公開されています。この技術は、一部のNATデバイスの特性を用いています。サーバクライアント型のサービス利用時の動作を想定すると理分かりやすいでしょう。NAT配下のホストが外部のサーバへとパケットを送信すると、NATデバイスはその応答を送信元に転送することを許容します。つまり、NATの外側から見た（すなわちサーバ側から見た）送信元IPアドレスとポートのペアを知ることが可能となれば、そこに穴を開けられる可能性があるという訳です。このアドレスは「鏡写し」の意味から、リフレクシブアドレス（Reflexive Address）と呼ばれます。

STUNクライアントは、STUNサーバからの応答によってリフレクシブアドレスを知ることができます。ここで得られたアドレスとポートの組を用いて、ピア間で互いにパケットを送信しあってホールパンチを成功させようと試みます。[RFC3489](https://www.ietf.org/rfc/rfc3489.txt)で規定された旧来のSTUNではリフレクシブアドレスを`MAPPED-ADDRESS`として平文でやりとりしていました。ところが、一部のアプリケーションレベルゲートウェイやエンタプライズ向けのファイアウォールでは、通信の内容を解析してIPアドレスらしき情報を動的に書き換えてしまう場合があります。それに対処するために、WebRTCでは[RFC5389](http://tools.ietf.org/html/rfc5389)で規定された最新のSTUNの仕様に基いて、排他的論理和を施した情報を`XOR-MAPPED-ADDRESS`として用いています。

ホールパンチに用いるための「ピア間で利用できる可能性のある」アドレスのペアを収集するためにはICEが用いられ、ここで得られた情報を候補（candidate）と呼びます。候補は多くの場合で複数存在しており、ICEエージェントが「一番良い」ペアの選定を試みます。極端な例を挙げると、IPv4とIPv6のアドレスの双方を持つクライアント同士が通信しあう場合では、シグナリングは双方にIPv4アドレスを用いながら、メディアトランスポートにはIPv6を用いるということもあり得るでしょう。現状のWebRTCの実装ではこの例はまだ動作しませんが、ともかくとして、ICEはホールパンチを行う上で不可欠な技術でしょう。他に、ICEエージェントは他にDoS攻撃を防ぐための通信開始の同意（communication consent）やキープアライブのための機能を持っており、安全に安定して通信を開始する機能を備えています。

WebRTCでは、STUNクライアントとICEエージェントはブラウザ内に含まれ、アプリケーションはJavaScriptの[ピアコネクションAPI](http://www.w3.org/TR/webrtc/#rtcpeerconnection-interface)越しに制御することができます。新たなRTCPeerconnectionオブジェクトを生成して通信を確立しようとすると、ホールパンチが完了するまでの処理は自動的に進行します。ICEエージェントはSTUNによって得られた候補をシグナリングチャネル上で相手のピアと交換し、次々にホールパンチを試みます。

## NAT超えの2つの課題と、それに対するアプローチ

WebRTCにおけるNAT越えには、以下に挙げる大きく2つの課題が存在しています。

1. 全く接続不可であるケースを低減させること
2. オファーを送信してからセッション開始までの時間のNAT超えによる追加を短縮すること
 
もし企業の中から外のホストと通信しようとした場合、エンタプライズ向けのFWやNATは堅牢なセキュリティのために、信頼されたとされる通信以外の全てを遮断するかもしれません。そのような場合、WebRTCでは中継サーバを用いたメディアリレーのアプローチを採っています。

また、電話のような通話アプリケーションにおいて、ダイヤルしてからリングバックトーンが開始するまで数十秒もの間待たされるというのは、いくらNAT配下にホストがあるとしても、それが「壊れている」とされても文句のつけようが無いでしょう。これに対するアプローチのひとつとして、WebRTCでは通信の多重化を採っています。

### TURNによるメディアリレー

WebRTCでは、ホールパンチに失敗してピア間で直接通信することが不可能な場合、TURNをメディアリレーに用います。まず前提として、ホールパンチは全てのネットワークで成功する訳ではありません。たとえば、Symmetric NATは全てのNAT越えを許容しませんし、STUNはその特性からNATの最も外側のリフレクシブアドレスしか知り得ないので、多段NATにも対応できません。それらのような場合、インターネット上のTURNサーバがピア間のパケットを中継することによってピア間の通信を実現します。TURNはSTUNを拡張した技術であり、ICEエージェントはメディアリレーアドレスとしてTURNサーバを候補に追加します。TURNは通信確立の最後の砦と成り得るかもしれませんが、まだ全てのネットワークでのリレーに対応している訳ではありません。そのため、現在も新たな拡張に向けた議論が進行しており、主に[TURN Revised and Modernized (tram)](https://datatracker.ietf.org/wg/tram/documents/)WGで進んでいます。

### 最低限のホールパンチで済ませるための通信の多重化

WebRTCでは、NAT越えを用いた場合のセッション開始までの時間短縮へのアプローチのひとつとして通信の多重化を採っており、メディア通信の多重化には[RFC5761](http://tools.ietf.org/html/rfc5761)に規定されるRTP多重を用いています。そもそも、なぜ多重化がこのケースで有効に働くのでしょうか？まずは、RTP多重を用いないシーケンスについて考えてみましょう。

![duplicated_rtp.png](https://qiita-image-store.s3.amazonaws.com/0/10409/e4cf804d-4bfc-cfe9-fef6-5db1c319c008.png)

通常では、通信の多重化はトランスポート層が担うこととなっています。RTPのトランスポートにUDPを用いる場合、ストリームごとに異なるポートを用いることによって通信を多重化します。双方向の通話を行う場合では、相互のRTPセッションとそれぞれに対応するRTCPセッションの合計4つものUDPポートが必要となってしまします。ここでホールパンチを試みる場合、4つのポート全てに対して処理しなければなりません。セッション確立のためには、全てのホールパンチ完了を待つ必要があります。一方で、WebRTCは迅速にセッションを開始するために、「ただ1つ」のポートだけを用います。その結果、ホールパンチたった1度だけで済み、複数のストリームを扱うことが可能となっています。このような、WebRTCでのRTPの利用方は[draft-ietf-rtcweb-rtp-usage](https://tools.ietf.org/html/draft-ietf-rtcweb-rtp-usage)で議論されており、他にセッション情報の交換や送受信双方での品質制御や輻輳通知のためのRTCP利用方法も規定しようとされてます。

WebRTCはメディア以外にデータの通信のための[データチャネル](http://www.w3.org/TR/webrtc/#rtcdatachannel)をもサポートしており、そのプロトコルにSCTPを用いることとし、多重化もSCTPの機能を用いることとしています。SCTPの多重化機能はSCTPアソシエーションと呼ばれており、複数のストリームを同時に扱うことができます。

SCTPの採用は、データ通信に「より良いプロトコル」を用いようとした背景があります。この議論は[draft-ietf-rtcweb-data-protocol](https://tools.ietf.org/html/draft-ietf-rtcweb-data-protocol)で進行中です。SCTPは[SS7](http://ja.wikipedia.org/wiki/共通線信号No.7)のために規定されたトランスポート層のプロトコルであり、信頼性（reliability）の柔軟な設定や、パケットの順序性の保証の有無の設定、そしてメッセージ送信機能などの便利な機能を提供しています。

機能性の観点からSCTPは、現在主流なトランスポート層であるTCPとUDPの双方を置き換えることが可能ですが、世に普及しているNATデバイスのほとんどは、TCPとUDP以外のパケットを破棄してしまうため、通信キャリア内部以外でほとんど使われることがありませんでした。ところが、WebRTCではDTLS上にSCTPを通すことで、すなわちトランスポートにUDPを用いるSCTPを実装することによって、既存のインターネットの世界でSCTPを用いることを可能としてしまいました。RTP多重にせよDTLS上のSCTPにせよ、実サービスでは必ずしもOSI参照モデルのように綺麗に階層構造を区切ることができないのかもしれません。

これらのWebRTCにおけるデータチャネルおよびSCTPの議論は[draft-ietf-rtcweb-data-channel](https://tools.ietf.org/html/draft-ietf-rtcweb-data-channel)で行われており、拙作ですが日本語の解説が[WebRTCのデータチャネル解説](http://qiita.com/udonchan/items/7f5ffa9e8982ae1636c3)にあります。

その他、セッション開始までの時間の短縮化のための取組みとして主要なものにTrickle ICEが存在しています。通常のICEエージェントは全ての候補が揃うまでホールパンチを試みませんが、Trickle ICEでは候補を収集しながら次々にホールパンチを試みます。滴(Trickle)がしたたることに掛けて名付けられています。Trickle ICE は、すでに Google Chrome上で実装されていますが、[draft-ietf-mmusic-trickle-ice](https://tools.ietf.org/html/draft-ietf-mmusic-trickle-ice)における仕様の議論は、残念ながらは2014年8月11日に期限切れ（Expire）となってしまっています。

## まとめ

以上、インターネット上のP2P通信におけるNAT越えの課題と、WebRTCにおけるそのアプローチを解説しました。NAT越えの課題に対する取組みは現在進行形であり、様々な拡張がまだまだ議論されています。ブラウザの実装は流動的でありメディアフローの挙動は定まっていません。WebRTCを用いるWeb開発者は、JavaScriptのAPIを用いることがほとんどですので、このような通信の内容や設定の詳細に触れる必要はないかもしれません。しかし、トラブルシュートにおいてこれらの知識を持ちあわせておくことはきっと役に立つでしょう。また、Webが未来永劫テレフォニーの世界と無縁であるとも限らず、そのような時代が到来したとすれば、既存VoIP機器とWebRTCデバイスの差異に対する知識が、まず何をすべきかの判断に役立つと考えられます。このWebRTCについての追加の知識が、誰かの何らかの参考になれば幸いです。