QUICは新しいトランスポートであり、IETFで標準化が進められています。
UDPベースのプロトコルであり、QUIC Connection IDと呼ばれるIDでコネクションを管理しており、IPアドレス・ポート番号が変わってもコネクションを維持することが出来ます。また、ほとんどのデータが暗号化されているため、ACKや切断要求なども経路上からは観測できません。
このように様々な機能改善が行われており、ファイアウォールといった経路上で機能をもつ装置ではQUICを適切に扱う必要があります。
IETFのQUIC WGでは、QUICトラフィックを管理するネットワークオペレータやミドルウェアの管理者向けに「Manageability of the QUIC Transport Protocol」を出しています。この文書では、経路上でどのような情報が見え、どのような情報は見えないのか、既存のプラクティスがどのような影響をうけるかについて記述しています。
前半はQUICのワイヤイメージと機能に関して、後半はファイアウォールやDDoS対策などネットワークオペレーションへの注意事項が記述されています。ただしすべき/すべきでないといったプラクティスは環境に依存するため、この文書では書かれていない
かいつまんで紹介する
ワイヤイメージ
QUICのトランスポートを定義する「QUIC: A UDP-Based Multiplexed and Secure Transport」では、ロングヘッダとショートヘッダを定義しています。ロングヘッダはバージョンネゴシエーション、サーバリトライ、0-RTTデータを含むコネクションの最初に使用されます。一方、ショートヘッダはハンドシェイク後に使用され、ヘッダのオーバヘッドを抑えるために殆どのデータパケットで使用されます。
0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+ |1| Type (7) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | + Connection ID (64) + | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Packet Number (32) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Version (32) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Payload (*) ... +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+ |0|C|K| Type (5)| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | + [Connection ID (64)] + | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Packet Number (8/16/32) ... +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Protected Payload (*) ... +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- Type: ロングヘッダはヘッダタイプフィールドを持ちます。現在のQUICバージョンは6つのヘッダタイプを定義しており、Version Negotiation, Client Initial, Server Stateless Retry, Server Cleartext, Client Cleartext, 0-RTT Protected です(将来増える可能性があります)。
- Connection ID: コネクションの識別子。QUICでは、5タプル(送信先/送信元IPアドレス、送信先/送信元ポート、IPヘッダのプロトコル)ではなくConnection IDでコネクションを管理します。サーバから採番もできます。ショートヘッダではオプショナルのフィールドです。
- Packet Number: 各パケットはバケット番号を持ち、送信する毎に増加していきます。乱数で初期化された内部カウンターのうち最下位ビットの8, 16, 32bitのいづれかを長さで格納します。再送の場合でも値は増加すること、値はスキップ出来ることに注意が必要です。
- Version Number: QUICのバージョンです。バージョンはVersion Negotiationパケットでサポートしているバージョンを送りますが、暗号化されていないため送信者側のサポートしているバージョンは経路上からも確認できます。
- K(key phase):ショートヘッダはKey Phaseフラグを持ち、暗号化に用いた鍵を識別するのに使用されます。
これらのフィールドの位置は将来のQUICでも固定されているでしょうが、フィールドは増える可能性がある点に注意が必要です。
また、暗号化されていないフィールドにおいてもコネクションが確立した後に値は検証されるため、経路上では1bitも変更はできません。変更した場合は、パケットが破棄されるか、コネクションは切断されます。
バージョンネゴシエーションにおいては、TLSのGREASE拡張のように未知のバージョンを含められる点に注意が必要です。
経路上で出来ること
QUICトラフィックの識別
QUICトラフィックは特別、その他のUDPパケットと区別出来るようには設計されていません。
現在ターゲットなっているアプリケーションプロトコルはHTTPであり、HTTP over QUICは通常UDPポートの443を使用しますが、他のポートも使用できます。
バージョンの識別
上記のように確実ではありませんがパケットがQUICだとして観測するならば、ハンドシェイクを観測することでバージョンを推定できます。バージョン番号を持つClient Initialパケットと、サーバからそのバージョン番号を持つ平文のパケットが送り返されていれば、バージョン番号が受理された事を意味します。
ただしクライアントやサーバのIPアドレスが変わってもコネクションは維持されるため、その際ハンドシェイクは行われません。Connection IDをトラックし、最初のハンドシェイクの情報が必要になります。
不正なパケットの拒否
ネットワークフィルタで、反射攻撃やバックスキャッタといった不正なパケットを拒否したい場合があります。
パケットの最初のバイト(パケットタイプ)に基づくヒューリスティックな手法で、そのパケットタイプが届くことが正しいのか分類することが出来ます。
ただし、パケットタイプは将来のバージョンでは増えたり、異なる意味を有する可能性があります。
コネクション確立の確認
コネクションを確立するには平文のパケットが必要であり、ストリーム0上でTLSハンドシェイクを行います。そのため、TLS over TCPど同じような手法を用いて検出することが出来ます。
通信フローの切断検出(不可)
QUICはコネクションの終了は経路上からは分かりません。
通信フローが終了した場合、経路上の装置からはパケットが観測されないだけです。NATやファイアウォールのような状態を持つ経路上の装置は内部に持つQUICのフロー状態を消すアイドル時間を設定する必要があります。
ラウンドトリップタイムの計測
QUICではACKも暗号化され経路上では確認できないため、ハンドシェイク中にのみ通信を観測し測定できます。
この振る舞いに関する変更は議論中です。https://github.com/quicwg/base-drafts/issues/631 参照。
パケットロスの計測
QUICパケットは平文でパケット番号を送信します。パケット番号のギャップを観測することで観測点より上流でのロスを推定できますが、通常推奨はされませんがパケット番号を飛ばすことも許可されています。
また、ACKや再送は経路上で観察できないため、観測点より下流でのロスについては正確に推定できません。
ネットワーク経路上の装置への影響
QUICトラフィックの状態を扱う装置
ファイアウォールなどの、QUICトラフィックの状態扱うのはコネクションの確立を観測することで可能です。
ただし、終了は経路上からはわからないので最後の通信からのタイマーや、LRU(Least Recently Used)方式で状態は削除されるべきです。ただし、アプリケーションの要件に依存します。
ネットワークのパフォーマンス測定とトラブルシューティング
QUICトラフィックを受動的に観測することにより、極めて限定された損失とRTT測定が可能です。
サーバと協調したロードバランシング
Connection IDはサーバから払い出すことが出来ます。Connection ID(64bit)は任意のバイト列ですので、特定の情報を埋め込む事もできます。複数ロードバランサがある場合などでも、最初に処理した(通信を復号できる)ロードバランサの識別子などを埋め込んでおくことで、DNSラウンドロビンなどで別のサーバにパケットが届いても、適切なロードバランサにルーティングすることもできます。
ただしクライアントが悪意を持って変更しても大丈夫なように、HMACなどを用いてで不正なパケットを検出できるようにすべきです。