QUIC-TRANSPORT
RFC 9000 QUIC: A UDP-Based Multiplexed and Secure Transport
https://tex2e.github.io/rfc-translater/html/rfc9000.html
https://www.rfc-editor.org/info/rfc9000
QUIC: UDPベースの多重化および安全なトランスポート
概要
このドキュメントは、QUICトランスポートプロトコルの中核を定義します。QUICは、構造化通信、低遅延接続の確立、ネットワークパスの移行を実現するフロー制御されたストリームをアプリケーションに提供します。QUICには、様々な導入環境において機密性、整合性、可用性を保証するセキュリティ対策が組み込まれています。付属ドキュメントでは、鍵ネゴシエーション、損失検出、そして輻輳制御アルゴリズムの例となるTLSの統合について説明しています。
1. 概要
QUICは、セキュアな汎用トランスポートプロトコルです。本文書では、QUICのバージョン1を定義します。これは、QUIC-INVARIANTSで定義されているQUICのバージョン非依存のプロパティに準拠しています。
QUICは、クライアントとサーバー間のステートフルな相互作用を実現するコネクション指向プロトコルです。QUICハンドシェイクは、暗号パラメータとトランスポートパラメータのネゴシエーションを組み合わせます。QUICはTLSハンドシェイクTLS13を統合していますが、パケットの保護にはカスタマイズされたフレーミングを使用しています。TLSとQUICの統合については、QUIC-TLSで詳しく説明されています。このハンドシェイクは、アプリケーションデータを可能な限り迅速に交換できるように構成されています。これには、クライアントがデータを即時送信するオプション(0-RTT)が含まれており、このオプションを有効にするには、事前の何らかの通信または設定が必要です。エンドポイントは、QUICパケットを交換することでQUIC通信を行います。ほとんどのパケットにはフレームが含まれており、エンドポイント間で制御情報とアプリケーションデータを伝送します。 QUICは各パケット全体を認証し、可能な限り各パケットの多くの部分を暗号化します。QUICパケットは、既存のシステムやネットワークへの導入を容易にするために、UDPデータグラムUDPで伝送されます。アプリケーションプロトコルは、順序付けられたバイト列であるストリームを介してQUIC接続上で情報を交換します。ストリームには、両方のエンドポイントからデータを送信できる双方向ストリームと、単一のエンドポイントからデータを送信できる単方向ストリームの2種類があります。クレジットベースの方式を使用して、ストリームの作成を制限し、送信可能なデータ量を制限します。QUICは、信頼性の高い配信と輻輳制御を実現するために必要なフィードバックを提供します。データ損失を検出して回復するためのアルゴリズムは、QUIC-RECOVERYのセクション6に記載されています。QUICは、ネットワーク輻輳を回避するために輻輳制御に依存しています。輻輳制御アルゴリズムの例は、QUIC-RECOVERYのセクション7に記載されています。QUIC接続は、単一のネットワークパスに厳密に束縛されるわけではありません。接続移行では、接続識別子を使用して、接続を新しいネットワークパスに転送できるようにします。このバージョンのQUICでは、クライアントのみが移行可能です。この設計により、NATの再バインドなどによるネットワークトポロジやアドレスマッピングの変更後も接続を継続できます。接続が確立された後は、複数の接続終了オプションが提供されます。アプリケーションは正常なシャットダウンを管理でき、エンドポイントはタイムアウト期間をネゴシエートでき、エラー発生時には即座に接続を切断できます。また、ステートレスメカニズムにより、一方のエンドポイントの状態が失われた後でも接続を終了できます。
1.1. ドキュメントの構成
このドキュメントは、QUIC プロトコルの中核部分について解説し、以下の構成になっています。
ストリームは、QUIC が提供する基本的なサービス抽象化です。
セクション 2 では、ストリームに関連する中核概念について説明します。
セクション 3 では、ストリームの状態に関するリファレンスモデルを示します。
セクション 4 では、フロー制御の動作の概要を示します。
コネクションは、QUIC エンドポイントが通信を行うコンテキストです。
セクション 5 では、コネクションに関連する中核概念について説明します。
セクション 6 では、バージョンネゴシエーションについて説明します。
セクション 7 では、コネクションを確立するプロセスについて詳しく説明します。
セクション 8 では、アドレス検証と重要なサービス拒否攻撃の軽減策について説明します。
セクション 9 では、エンドポイントが接続を新しいネットワークパスに移行する方法について説明します。
セクション 10 では、オープン接続を終了するためのオプションを列挙します。
セクション 11 では、ストリームおよびコネクションのエラー処理に関するガイダンスを示します。
パケットとフレームは、QUIC が通信に使用する基本単位です。
セクション12では、パケットとフレームに関する概念について説明します。
セクション13では、データの送信、再送信、および確認応答のモデルを定義し、
セクション14では、QUICパケットを伝送するデータグラムのサイズ管理規則を規定します。
最後に、QUICプロトコル要素のエンコードの詳細については、以下のセクションを参照してください。
セクション15(バージョン)、
セクション16(整数エンコード)、
セクション17(パケットヘッダー)、
セクション18(トランスポートパラメータ)、
セクション19(フレーム)、
およびセクション20(エラー)。
付属文書では、QUICの損失検出と輻輳制御QUIC-RECOVERY、およびTLSおよびその他の暗号化メカニズムの使用QUIC-TLSについて説明します。
本文書は、QUIC-INVARIANTSのプロトコル不変条件に準拠するQUICバージョン1を定義します。
QUICバージョン1を参照するには、本文書を引用してください。 QUIC のバージョンに依存しないプロパティの限定されたセットへの参照には、QUIC-INVARIANTS を引用できます。
1.2. 用語と定義
本文書におけるキーワード「MUST(しなければならない)」「MUST NOT(してはならない)」「REQUIRED(必須)」「SHALL(するべき)」「SHALL NOT(すべきでない)」「SHOULD(すべきでない)」「SHOULD NOT(すべきでない)」「RECOMMENDED(推奨される)」「NOT RECOMMENDED(推奨されない)」「MAY(してもよい)」「OPTIONAL(選択できる)」は、ここに示すようにすべて大文字で表記されている場合に限り、BCP 14 RFC2119 RFC8174 の規定に従って解釈されます。
本文書でよく使用される用語を以下に示します。
QUIC: 本文書で規定するトランスポートプロトコル。QUICは名称であり、頭字語ではありません。
エンドポイント: QUICパケットを生成、受信、処理することでQUIC接続に参加できるエンティティ。QUICには、クライアントとサーバーの2種類のエンドポイントしかありません。
クライアント: QUIC接続を開始するエンドポイント。
サーバー: QUIC接続を受け入れるエンドポイント。
QUICパケット: UDPデータグラムにカプセル化できる、QUICの処理可能な完全な単位。1つ以上のQUICパケットを1つのUDPデータグラムにカプセル化できます。
ACK取得パケット: ACK、PADDING、CONNECTION_CLOSE以外のフレームを含むQUICパケット。これらのフレームは、受信者に確認応答を送信させます。セクション13.2.1を参照してください。
フレーム: 構造化されたプロトコル情報の単位。複数のフレームタイプがあり、それぞれが異なる情報を伝送します。フレームはQUICパケットに含まれます。
アドレス: 修飾子なしで使用される場合、ネットワークパスの一端を表すIPバージョン、IPアドレス、UDPポート番号の組み合わせ。
接続ID: エンドポイントでQUIC接続を識別するために使用される識別子。各エンドポイントは、ピアがエンドポイントに送信するパケットに含める接続IDを1つ以上選択します。この値はピアには非公開です。
ストリーム: QUIC接続における、順序付けられたバイト列の単方向または双方向チャネル。QUIC接続は複数のストリームを同時に伝送できます。
アプリケーション: QUICを使用してデータを送受信するエンティティ。
このドキュメントでは、「QUICパケット」、「UDPデータグラム」、「IPパケット」という用語を、それぞれのプロトコルの単位として使用します。つまり、1つ以上のQUICパケットをUDPデータグラムにカプセル化し、UDPデータグラムをIPパケットにカプセル化することができます。
1.3. 表記規則
本文書のパケット図およびフレーム図では、独自の形式を使用しています。この形式の目的は、プロトコル要素を定義することではなく、要約することです。構造の完全な意味と詳細は、Prose で定義されています。
複雑なフィールドには名前が付けられ、その後に、対応する中括弧で囲まれたフィールドのリストが続きます。このリスト内の各フィールドはカンマで区切られます。
各フィールドには、長さ情報に加え、固定値、オプション、または繰り返しの有無が示されます。各フィールドでは、以下の表記規則が使用されます。長さはすべてビット単位です。
x (A): x が A ビット長であることを示します。
x (i): x が、セクション 16 で説明されている可変長符号化を使用して整数値を保持することを示します。
x (A..B): x が A から B までの任意の長さであることを示します。A を省略すると、最小値が 0 ビットであることを示し、B を省略すると、上限が設定されていないことを示します。この形式の値は常にバイト境界で終わります。
x (L) = C: x の値は C に固定され、x の長さは L で表されます。L は上記の長さ形式のいずれかを使用できます。
x (L) = C..D: x の値は C から D までの範囲(両端を含む)で、長さは上記と同様に L で表されます。
$ [x (L)] : x はオプションであり、長さは L であることを示します。
x (L) ...: x は 0 回以上繰り返され、各インスタンスの長さは L であることを示します。
このドキュメントでは、ネットワークバイトオーダー(ビッグエンディアン)の値を使用します。フィールドは各バイトの上位ビットから配置されます。
慣例により、個々のフィールドは複素数フィールドの名前を使用して複素数フィールドを参照します。図 1 に例を示します。
code:図1
Example Structure {
1ビットフィールド (1),
7ビットフィールド with 固定値 (7) = 61,
可変長整数のフィールド (i),
任意長フィールド (..),
可変長フィールド (8..24),
最小長フィールド (16..),
最大長フィールド (..128),
オプションフィールド (64),
繰り返しフィールド (8) ...,
}
図 1: フォーマット例
1 ビットフィールドを文章中で参照する場合、そのフィールドの値が設定されているバイトの値を使用することで、そのフィールドの位置を明確にすることができます。例えば、図 1 の 1 ビットフィールドのように、値 0x80 は、バイトの最上位ビットにある 1 ビットフィールドを参照するために使用できます。
2. ストリーム
QUICのストリームは、アプリケーションに軽量で順序付けされたバイトストリームの抽象化を提供します。ストリームは単方向または双方向にすることができます。
ストリームはデータを送信することで作成できます。ストリーム管理に関連するその他のプロセス(終了、キャンセル、フロー制御の管理など)はすべて、オーバーヘッドを最小限に抑えるように設計されています。例えば、単一のSTREAMフレーム(セクション19.8)で、ストリームを開き、データを伝送し、ストリームを閉じることができます。ストリームは長期間存続することもでき、接続の全期間にわたって存続できます。ストリームはどちらのエンドポイントでも作成でき、他のストリームとインターリーブして同時にデータを送信したり、キャンセルしたりできます。QUICは、異なるストリーム間のバイト順序を保証する手段を提供していません。QUICでは、任意の数のストリームを同時に動作させ、任意の量のデータを任意のストリームに送信できます。ただし、フロー制御の制約とストリーム制限が適用されます。セクション4を参照してください。
2.1. ストリームの種類と識別子
ストリームは単方向または双方向にすることができます。単方向ストリームは、ストリームの開始元からそのピアへの一方向にデータを伝送します。双方向ストリームでは、双方向にデータを送信できます。
ストリームは、接続内でストリームIDと呼ばれる数値によって識別されます。ストリームIDは、接続上のすべてのストリームで一意の62ビット整数(0~262-1)です。ストリームIDは可変長整数としてエンコードされます。詳細はセクション16を参照してください。QUICエンドポイントは、接続内でストリームIDを再利用してはなりません(MUST NOT)。ストリームIDの最下位ビット(0x01)は、ストリームの開始元を識別します。クライアント開始ストリームは偶数ストリームID(ビットが0に設定)を持ち、サーバー開始ストリームは奇数ストリームID(ビットが1に設定)を持ちます。ストリームIDの最下位2ビット(0x02)は、双方向ストリーム(ビットが0に設定)と単方向ストリーム(ビットが1に設定)を区別します。したがって、ストリームIDの最下位2ビットは、表1にまとめられているように、ストリームを4つのタイプのいずれかとして識別します。
table:表1:ストリームIDのタイプ
ビット ストリームタイプ
0x00 クライアント開始、双方向
0x01 サーバー開始、双方向
0x02 クライアント開始、単方向
0x03 サーバー開始、単方向
各タイプのストリーム空間は最小値(それぞれ0x00から0x03)から始まり、各タイプの後続のストリームは、数値が増加するストリームIDで作成されます。ストリーム ID が順序どおりに使用されない場合は、そのタイプのストリーム ID 番号が小さいストリームもすべて開かれます。
2.2. データの送受信
STREAMフレーム(セクション19.8)は、アプリケーションから送信されるデータをカプセル化します。エンドポイントは、STREAMフレームのストリームIDとオフセットフィールドを使用して、データを順序付けます。
エンドポイントは、ストリームデータを順序付けされたバイトストリームとしてアプリケーションに配信できなければなりません(MUST)。順序付けされたバイトストリームを配信するには、エンドポイントは、アドバタイズされたフロー制御制限まで、順序通りに受信されなかったデータをバッファリングする必要があります。QUICでは、ストリームデータの順序外配信を特に許可していません。ただし、実装は、受信側アプリケーションに順序外データを配信する機能を提供することを選択できます(MAY)。エンドポイントは、同じストリームオフセットでストリームのデータを複数回受信できます。すでに受信したデータは破棄できます。特定のオフセットのデータは、複数回送信された場合、変更してはなりません(MUST)。エンドポイントは、ストリーム内の同一オフセットで異なるデータを受信した場合、PROTOCOL_VIOLATIONタイプの接続エラーとして処理してもよい(MAY)。ストリームは、QUICが認識できる他の構造を持たない、順序付けされたバイトストリームの抽象化です。ストリームフレームの境界は、データの送信時、パケットロス後の再送信時、または受信側のアプリケーションへの配信時に維持されることは想定されていません。エンドポイントは、ピアによって設定されたフロー制御制限内に収まっていることを確認せずに、ストリーム上でデータを送信してはいけません(MUST NOT)。フロー制御については、セクション4で詳しく説明します。
2.3. ストリームの優先順位付け
ストリームに割り当てられたリソースが適切に優先順位付けされている場合、ストリームの多重化はアプリケーションのパフォーマンスに大きな影響を与える可能性があります。
QUICは優先順位付け情報を交換するメカニズムを提供していません。代わりに、アプリケーションから優先順位情報を受信することに依存しています。QUIC実装は、アプリケーションがストリームの相対的な優先順位を示す方法を提供すべきです(SHOULD)。実装は、アプリケーションから提供される情報を使用して、アクティブなストリームにリソースを割り当てる方法を決定します。
2.4. ストリームに対する操作
本文書は、QUICのAPIを定義するものではなく、アプリケーションプロトコルが利用できるストリーム上の関数群を定義します。アプリケーションプロトコルは、QUIC実装がこのセクションで説明する操作を含むインターフェースを提供していると想定できます。特定のアプリケーションプロトコル向けに設計された実装は、そのプロトコルで使用される操作のみを提供する場合があります。
ストリームの送信側では、アプリケーションプロトコルは以下の操作を実行できます。
ストリームフロー制御クレジット(セクション4.1)が書き込まれたデータを送信するために正常に予約されたことを確認した上で、データを書き込む。
ストリームを終了(クリーン終了)し、FINビットがセットされたSTREAMフレーム(セクション19.8)を送信する。
ストリームをリセット(突然終了)し、ストリームがまだ終了状態でない場合は、RESET_STREAMフレーム(セクション19.4)を送信する。
ストリームの受信側では、アプリケーションプロトコルは以下の操作を実行できます。
データを読み取る。そして、
ストリームの読み取りを中止し、クローズを要求します。その結果、STOP_SENDINGフレーム(セクション19.5)が送信される可能性があります。
アプリケーションプロトコルは、ピアがストリームをオープンまたはリセットしたとき、ピアがストリームの読み取りを中止したとき、新しいデータが利用可能になったとき、フロー制御によりストリームにデータを書き込めるとき、または書き込めないときなど、ストリームの状態変化の通知を要求することもできます。
3. ストリームの状態
このセクションでは、ストリームを送信コンポーネントまたは受信コンポーネントの観点から説明します。2つの状態マシンについて説明します。1つはエンドポイントがデータを送信するストリーム用(セクション3.1)で、もう1つはエンドポイントがデータを受信するストリーム用(セクション3.2)です。
単方向ストリームは、ストリームの種類とエンドポイントの役割に応じて、送信側または受信側の状態マシンを使用します。双方向ストリームは、両方のエンドポイントで両方の状態マシンを使用します。これらの状態マシンの使用方法は、ストリームが単方向か双方向かに関係なく、ほとんどの場合同じです。双方向ストリームの場合、ストリームを開く条件はやや複雑です。これは、送信側または受信側のいずれかを開くと、ストリームが両方向で開かれるためです。
このセクションで示す状態マシンは、主に参考情報です。このドキュメントでは、ストリーム状態を使用して、さまざまな種類のフレームをいつどのように送信できるか、およびさまざまな種類のフレームを受信した際に期待される反応について説明します。これらの状態マシンはQUICの実装に役立つことを目的としていますが、実装を制限するものではありません。実装は、これらの状態を実装する実装と動作が一致する限り、異なるステートマシンを定義できます。
注: 場合によっては、単一のイベントまたはアクションが複数の状態遷移を引き起こすことがあります。例えば、FINビットをセットしたSTREAMを送信すると、送信ストリームに2つの状態遷移が発生する可能性があります。「Ready」状態から「Send」状態への遷移と、「Send」状態から「Data Sent」状態への遷移です。
3.1. 送信ストリームの状態
図2は、ピアにデータを送信するストリーム部分の状態を示しています。
code:図2
o
| Create Stream (Sending)
| Peer Creates Bidirectional Stream
v
+-------+
| Ready | Send RESET_STREAM
| |-----------------------.
+-------+ |
| |
| Send STREAM / |
| STREAM_DATA_BLOCKED |
v |
+-------+ |
| Send | Send RESET_STREAM |
| |---------------------->|
+-------+ |
| |
| Send STREAM + FIN |
v v
+-------+ +-------+
| Data | Send RESET_STREAM | Reset |
| Sent |------------------>| Sent |
+-------+ +-------+
| |
| Recv All ACKs | Recv ACK
v v
+-------+ +-------+
| Data | | Reset |
| Recvd | | Recvd |
+-------+ +-------+
図2:ストリームの送信部分の状態
エンドポイントが開始するストリームの送信部分(クライアントの場合はタイプ0と2、サーバーの場合はタイプ1と3)は、アプリケーションによって開かれます。「Ready」状態は、アプリケーションからのデータを受け入れることができる、新しく作成されたストリームを表します。ストリームデータは、送信の準備としてこの状態でバッファリングされる場合があります。
最初のSTREAMフレームまたはSTREAM_DATA_BLOCKEDフレームを送信すると、ストリームの送信部分は「Send」状態になります。実装によっては、最初のSTREAMフレームを送信してこの状態になるまで、ストリームIDの割り当てを延期することもできます。これにより、ストリームの優先順位付けを向上できます。
ピアによって開始される双方向ストリームの送信部分(サーバーの場合はタイプ0、クライアントの場合はタイプ1)は、受信部分が作成された時点で「Ready」状態から開始されます。
「Send」状態では、エンドポイントはSTREAMフレームでストリームデータを送信し、必要に応じて再送信します。エンドポイントは、ピアによって設定されたフロー制御制限を尊重し、MAX_STREAM_DATAフレームの受信と処理を継続します。「送信」状態のエンドポイントは、ストリームフロー制御制限(セクション4.1)によって送信がブロックされている場合、STREAM_DATA_BLOCKEDフレームを生成します。
アプリケーションがすべてのストリームデータの送信完了を示し、FINビットを含むSTREAMフレームが送信されると、ストリームの送信部分は「データ送信」状態に移行します。この状態から、エンドポイントは必要に応じてストリームデータを再送信するだけです。エンドポイントは、この状態のストリームに対してフロー制御制限を確認したり、STREAM_DATA_BLOCKEDフレームを送信したりする必要はありません。ピアが最終ストリームオフセットを受信するまで、MAX_STREAM_DATAフレームを受信する可能性があります。エンドポイントは、この状態のストリームについてピアから受信したMAX_STREAM_DATAフレームをすべて無視できます。
すべてのストリームデータが正常に確認されると、ストリームの送信側は「Data Recvd」状態(終端状態)に移行します。
「Ready」、「Send」、「Data Sent」のいずれかの状態から、アプリケーションはストリームデータの送信を中止する旨を通知できます。あるいは、エンドポイントはピアからSTOP_SENDINGフレームを受信することもあります。いずれの場合も、エンドポイントはRESET_STREAMフレームを送信し、これによりストリームは「Reset Sent」状態に移行します。
エンドポイントは、ストリームに言及する最初のフレームとしてRESET_STREAMを送信できます(MAY)。これにより、そのストリームの送信側がオープンになり、すぐに「Reset Sent」状態に遷移します。
RESET_STREAMを含むパケットの確認が完了すると、ストリームの送信側は「Reset Recvd」状態(終端状態)に移行します。
3.2. 受信ストリームの状態
図3は、ピアからデータを受信するストリームの部分の状態を示しています。ストリームの受信部分の状態は、ピアにおけるストリームの送信部分の状態の一部のみを反映しています。ストリームの受信部分は、「Ready」状態など、送信部分で観測できない状態は追跡しません。代わりに、ストリームの受信部分は、送信側では観測できないデータの一部を含む、アプリケーションへのデータの配信を追跡します。
code:図3
o
| Recv STREAM / STREAM_DATA_BLOCKED / RESET_STREAM
| 双方向ストリームの作成 (送信)
| 受信 MAX_STREAM_DATA / STOP_SENDING (双方向)
| 上位番号のストリームの作成
v
+-------+
| 受信 | RESET_STREAMの受信
| |-----------------------.
+-------+ |
| |
| STREAM + FINの受信 |
v |
+-------+ |
| Size | RESET_STREAMの受信 |
| Known |---------------------->|
+-------+ |
| |
| 全データの受信 |
v v
+-------+ RESET_STREAM 受信 +-------+
| データ |--- (オプション) -->|リセット|
| 受信 | 全データ受信 | 受信 |
+-------+<-- (オプション) ---+-------+
| |
| アプリ全データ読み取り | アプリリセット読み取り
v v
+-------+ +-------+
| Data | | Reset |
| Read | | Read |
+-------+ +-------+
図3: ストリームの一部を受信する際の状態
ピアによって開始されたストリーム(クライアントの場合はタイプ1と3、サーバーの場合はタイプ0と2)の受信部は、そのストリームの最初のSTREAM、STREAM_DATA_BLOCKED、またはRESET_STREAMフレームを受信したときに作成されます。ピアによって開始された双方向ストリームの場合、ストリームの送信部でMAX_STREAM_DATAまたはSTOP_SENDINGフレームを受信すると、受信部も作成されます。ストリームの受信部の初期状態は「Recv」です。
双方向ストリームの場合、エンドポイント(クライアントの場合はタイプ0、サーバーの場合はタイプ1)によって開始された送信部が「Ready」状態になると、受信部は「Recv」状態になります。
エンドポイントは、そのストリームについてピアからMAX_STREAM_DATAまたはSTOP_SENDINGフレームを受信すると、双方向ストリームを開始します。未オープンのストリームに対してMAX_STREAM_DATAフレームを受信すると、リモートピアがストリームをオープンし、フロー制御クレジットを提供していることを示します。未オープンのストリームに対してSTOP_SENDINGフレームを受信すると、リモートピアがこのストリームでデータを受信しなくなったことを示します。パケットが失われたり順序が変わったりした場合、いずれかのフレームがSTREAMフレームまたはSTREAM_DATA_BLOCKEDフレームよりも先に届く可能性があります。
ストリームが作成される前に、同じタイプでストリームIDが小さいすべてのストリームを作成する必要があります。これにより、両方のエンドポイントでストリームの作成順序が一貫していることが保証されます。
「Recv」状態では、エンドポイントはSTREAMフレームとSTREAM_DATA_BLOCKEDフレームを受信します。受信データはバッファリングされ、正しい順序に再構成されてアプリケーションに配信されます。アプリケーションによってデータが消費され、バッファスペースが利用可能になると、エンドポイントはピアがさらにデータを送信できるようにMAX_STREAM_DATAフレームを送信します。
FINビットを含むSTREAMフレームを受信すると、ストリームの最終的なサイズが判明します(セクション4.5を参照)。その後、ストリームの受信側は「サイズ判明」状態になります。この状態では、エンドポイントはMAX_STREAM_DATAフレームを送信する必要がなくなり、ストリームデータの再送のみを受信します。
ストリームのすべてのデータを受信すると、受信側は「データ受信」状態になります。これは、「サイズ判明」状態への移行を引き起こした同じSTREAMフレームを受信した結果として発生する可能性があります。すべてのデータが受信された後、そのストリームのすべてのSTREAMフレームまたはSTREAM_DATA_BLOCKEDフレームは破棄できます。
「データ受信」状態は、ストリームデータがアプリケーションに配信されるまで継続します。ストリームデータの配信が完了すると、ストリームは「データ読み取り」状態(終端状態)に移行します。
「受信」または「サイズ既知」状態でRESET_STREAMフレームを受信すると、ストリームは「リセット受信」状態に移行します。これにより、アプリケーションへのストリームデータの配信が中断される可能性があります。
RESET_STREAMフレームを受信した時点で、すべてのストリームデータが既に受信されている可能性があります(つまり、「データ受信」状態)。同様に、RESET_STREAMフレームを受信した後に残りのストリームデータが到着する可能性もあります(「リセット受信」状態)。実装は、この状況を自由に管理できます。
RESET_STREAMを送信すると、エンドポイントはストリームデータの配信を保証できなくなりますが、RESET_STREAMを受信して​​もストリームデータが配信されないようにする必要はありません。実装は、ストリームデータの配信を中断し、消費されなかったデータを破棄し、RESET_STREAMの受信を通知することができます(MAY)。ストリームデータが完全に受信され、アプリケーションによる読み取りのためにバッファリングされている場合、RESET_STREAM信号は抑制または保留される可能性があります。RESET_STREAMが抑制された場合、ストリームの受信部分は「データ受信」状態のままになります。
アプリケーションがストリームがリセットされたことを示す信号を受信すると、ストリームの受信部分は「リセット読み取り」状態(つまり終端状態)に遷移します。
3.3. 許可されるフレームタイプ
ストリームの送信者は、送信側または受信側のいずれかのストリームの状態に影響を与えるフレームタイプを3つだけ送信します。STREAM(セクション19.8)、STREAM_DATA_BLOCKED(セクション19.13)、およびRESET_STREAM(セクション19.4)です。
送信者は、これらのフレームを終端状態(「Data Recvd」または「Reset Recvd」)から送信してはなりません(MUST NOT)。送信者は、「Reset Sent」状態または任意の終端状態(つまり、RESET_STREAMフレームを送信した後)のストリームに対して、STREAMまたはSTREAM_DATA_BLOCKEDフレームを送信してはなりません(MUST NOT)。受信側は、これらのフレームを運ぶパケットの遅延配信の可能性があるため、どの状態でもこれらの3種類のフレームを受信する可能性があります。
ストリームの受信側は、MAX_STREAM_DATAフレーム(セクション19.10)とSTOP_SENDINGフレーム(セクション19.5)を送信します。
受信側は、「Recv」状態でのみMAX_STREAM_DATAフレームを送信します。受信側は、RESET_STREAMフレームを受信して​​いない状態、つまり「Reset Recvd」または「Reset Read」以外の状態であれば、STOP_SENDINGフレームを送信できます(MAY)。ただし、ストリームデータはすべて受信済みであるため、「Data Recvd」状態でSTOP_SENDINGフレームを送信してもあまり意味がありません。送信側は、パケットの遅延配信の結果として、どの状態でもこれらの2種類のフレームを受信する可能性があります。
3.4. 双方向ストリームの状態
双方向ストリームは、送信部分と受信部分から構成されます。実装では、双方向ストリームの状態を、送信ストリームの状態と受信ストリームの状態の複合として表現できます。最も単純なモデルでは、送信部分または受信部分のいずれかが非終端状態の場合にストリームを「オープン」、送信ストリームと受信ストリームの両方が終端状態の場合に「クローズ」と表現します。
表2は、HTTP/2 HTTP2 で定義されているストリーム状態に大まかに対応する、双方向ストリームの状態のより複雑なマッピングを示しています。これは、ストリームの送信部分または受信部分の複数の状態が同じ複合状態にマッピングされていることを示しています。これは、このようなマッピングの1つの可能性に過ぎないことに注意してください。このマッピングでは、「クローズ」または「ハーフクローズ」状態に遷移する前に、データに確認応答する必要があります。
table:表2
送信部 受信部 複合状態
ストリームなし / 準備完了 ストリームなし / 受信 (*1) アイドル
準備完了 / 送信 / データ送信 受信 / サイズ既知 オープン
準備完了 / 送信 / データ送信 データ受信 / データ読み取り 半クローズ (リモート)
準備完了 / 送信 / データ送信 リセット受信 / リセット読み取り 半クローズ (リモート)
データ受信 受信 / サイズ既知 半クローズ (ローカル)
リセット送信 / リセット受信 受信 / サイズ既知 半クローズ (ローカル)
リセット送信 / リセット受信 データ受信 / データ読み取り クローズ
リセット送信 / リセット受信 リセット受信 / リセット読み取り クローズ
データ受信 データ受信 / データ読み取り クローズ
データ受信 リセット受信 / リセット読み取り クローズ
表2 ストリーム状態から HTTP/2 へのマッピング
注 (*1): ストリームがまだ作成されていない場合、またはストリームの受信側がまだフレームを受信せずに「Recv」状態にある場合、ストリームは「アイドル」であると見なされます。
3.5. 要請された状態遷移
アプリケーションがストリーム上で受信しているデータを必要としなくなった場合、ストリームの読み取りを中止し、アプリケーションエラーコードを指定できます。
ストリームが「Recv」または「Size Known」状態の場合、トランスポートはSTOP_SENDINGフレームを送信してこれを通知し、逆方向のストリームを閉じるよう促すべきです(SHOULD)。これは通常、受信側アプリケーションがストリームから受信したデータを読み取っていないことを示しますが、受信データが無視されることを保証するものではありません。
STOP_SENDINGフレームの送信後に受信したSTREAMフレームは、受信時に破棄できる場合でも、接続およびストリームフロー制御の対象としてカウントされます。
STOP_SENDINGフレームは、受信側エンドポイントにRESET_STREAMフレームの送信を要求します。STOP_SENDINGフレームを受信したエンドポイントは、ストリームが「Ready」または「Send」状態の場合、RESET_STREAMフレームを送信しなければなりません(MUST)。ストリームが「Data Sent」状態の場合、エンドポイントは、未送信データを含むパケットが確認されるか、損失と宣言されるまで、RESET_STREAMフレームの送信を延期してもよい(MAY)。未送信データが損失と宣言された場合、エンドポイントはデータを再送信する代わりに、RESET_STREAMフレームを送信すべき(SHOULD)。
エンドポイントは、送信するRESET_STREAMフレームにSTOP_SENDINGフレームのエラーコードをコピーすべき(SHOULD)だが、任意のアプリケーションエラーコードを使用することもできる。STOP_SENDINGフレームを送信したエンドポイントは、そのストリームでその後に受信するRESET_STREAMフレームのエラーコードを無視してもよい(MAY)。
STOP_SENDINGは、ピアによってリセットされていないストリームに対してのみ送信すべき(SHOULD)。STOP_SENDINGは、「Recv」または「Size Known」状態のストリームで最も有効である。
エンドポイントは、以前に送信されたSTOP_SENDINGを含むパケットが損失した場合、再度STOP_SENDINGフレームを送信することが期待される。ただし、ストリームのすべてのストリーム データまたは RESET_STREAM フレームのいずれかが受信されると、つまり、ストリームが「Recv」または「Size Known」以外の状態になると、STOP_SENDING フレームの送信は不要です。
双方向ストリームの両方向を終了したいエンドポイントは、RESET_STREAM フレームを送信することで一方の方向を終了でき、STOP_SENDING フレームを送信することで反対方向の迅速な終了を促すことができます。
4. フロー制御
受信側は、高速な送信者による過負荷や、悪意のある送信者による大量のメモリ消費を防ぐために、バッファリングが必要なデータ量を制限する必要があります。受信側が接続におけるメモリ使用量を制限できるように、ストリームは個別に、また接続全体でフロー制御されます。QUIC受信側は、セクション4.1および4.2で説明されているように、送信側がストリーム上で、またすべてのストリーム全体で、いつでも送信できるデータの最大量を制御します。
同様に、接続内の同時実行性を制限するために、QUICエンドポイントは、セクション4.6で説明されているように、ピアが開始できるストリームの最大累計数を制御します。
CRYPTOフレームで送信されるデータは、ストリームデータと同じ方法でフロー制御されません。QUICは、データの過剰なバッファリングを回避するために暗号プロトコルの実装に依存しています。QUIC-TLSを参照してください。複数のレイヤーでの過剰なバッファリングを回避するために、QUIC 実装では、暗号化プロトコル実装がバッファリング制限を伝達するためのインターフェースを提供する必要があります。
4.1. データフロー制御
QUICは、受信側が特定のストリームまたは接続全体で受信可能な総バイト数の制限を通知する、制限ベースのフロー制御方式を採用しています。これにより、QUICには2つのレベルのデータフロー制御が存在します。
ストリームフロー制御:各ストリームで送信可能なデータ量を制限することにより、単一のストリームが接続の受信バッファ全体を消費することを防ぎます。
コネクションフロー制御:すべてのストリームにおいてSTREAMフレームで送信されるストリームデータの総バイト数を制限することにより、送信側が受信側の接続バッファ容量を超えることを防ぎます。
送信側は、どちらの制限値を超えてデータを送信してはなりません(MUST NOT)。
受信側は、ハンドシェイク中にトランスポートパラメータを通じて、すべてのストリームの初期制限値を設定します(セクション7.4)。その後、受信側は送信側に MAX_STREAM_DATA フレーム (セクション 19.10) または MAX_DATA フレーム (セクション 19.9) を送信して、より大きな制限を通知します。
受信側は、対応するストリーム ID を含む MAX_STREAM_DATA フレームを送信することで、ストリームのより大きな制限を通知できます。MAX_STREAM_DATA フレームは、ストリームの最大絶対バイト オフセットを示します。受信側は、そのストリームで消費されたデータの現在のオフセットに基づいて、通知するフロー制御オフセットを決定できます。
受信側は、すべてのストリームの絶対バイト オフセットの合計の最大値を示す MAX_DATA フレームを送信することで、接続のより大きな制限を通知できます。受信側は、すべてのストリームで受信したバイトの累積合計を保持し、通知された接続またはストリームのデータ制限の違反を確認するために使用されます。受信者は、すべてのストリームで消費されたバイトの合計に基づいて、通知する最大データ制限を決定できます。
受信者が接続またはストリームの制限を通知した後で、より小さい制限を通知してもエラーにはなりませんが、小さい制限は効果がありません。
送信者が通知された接続またはストリームのデータ制限に違反した場合、受信者は FLOW_CONTROL_ERROR タイプのエラーで接続を閉じる必要があります(MUST)。エラー処理の詳細については、セクション 11 を参照してください。
送信者は、フロー制御制限を増加させない MAX_STREAM_DATA または MAX_DATA フレームを無視する必要があります(MUST)。
送信者が制限までデータを送信した場合、新しいデータを送信できなくなり、ブロックされていると見なされます。送信者は、書き込むデータがあるがフロー制御制限によってブロックされていることを受信者に通知するために、STREAM_DATA_BLOCKED または DATA_BLOCKED フレームを送信する必要があります。送信者がアイドルタイムアウト(セクション10.1)よりも長い期間ブロックされた場合、送信者が送信可能なデータを持っているにもかかわらず、受信側は接続を閉じる可能性があります。接続が閉じないようにするために、フロー制御が制限されている送信者は、ACKを誘発するパケットが送信中でないときに、定期的にSTREAM_DATA_BLOCKEDまたはDATA_BLOCKEDフレームを送信する必要があります(SHOULD)。
4.2. フロー制御制限の増加
実装は、MAX_STREAM_DATA フレームおよび MAX_DATA フレームでいつ、どのくらいのクレジットをアドバタイズするかを決定しますが、このセクションではいくつかの考慮事項を示します。
送信者のブロックを回避するために、受信側は MAX_STREAM_DATA フレームまたは MAX_DATA フレームをラウンドトリップ内で複数回送信するか、フレームの損失とその後の回復に要する時間を考慮して早めに送信することができます(MAY)。
制御フレームは接続のオーバーヘッドの原因となります。したがって、小さな変更を含む MAX_STREAM_DATA フレームおよび MAX_DATA フレームを頻繁に送信することは望ましくありません。一方、更新頻度が低い場合は、送信者のブロックを回避するために制限値を大きく増加させる必要があり、受信側でより多くのリソースコミットメントが必要になります。アドバタイズする制限値を決定する際には、リソースコミットメントとオーバーヘッドの間にトレードオフがあります。
受信側は、一般的な TCP 実装と同様に、ラウンドトリップ時間の推定値と受信側アプリケーションのデータ消費速度に基づいて、アドバタイズする追加クレジットの頻度と量を調整する自動調整メカニズムを使用できます。最適化として、エンドポイントは、フロー制御に関連するフレームを、送信する他のフレームがある場合にのみ送信することで、フロー制御によって余分なパケットが送信されないようにすることができます。
ブロックされた送信者は、STREAM_DATA_BLOCKED または DATA_BLOCKED フレームを送信する必要はありません。したがって、受信側は、MAX_STREAM_DATA または MAX_DATA フレームを送信する前に、STREAM_DATA_BLOCKED または DATA_BLOCKED フレームを待ってはなりません(MUST NOT)。そうすると、送信者が接続の残りの間ブロックされる可能性があります。送信者がこれらのフレームを送信したとしても、それらを待つと、送信者は少なくともラウンドトリップ全体でブロックされることになります。
送信者がブロックされた後にクレジットを受け取ると、大量のデータを応答として送信できる可能性があり、その結果、短期的な輻輳が発生します。送信者がこの輻輳を回避する方法については、QUIC-RECOVERY のセクション 7.7 を参照してください。
4.3. フロー制御パフォーマンス
エンドポイントが、当該接続においてピアの帯域幅遅延積よりも大きいフロー制御クレジットを常に確保できない場合、受信スループットはフロー制御によって制限されます。
パケットロスにより受信バッファにギャップが生じ、アプリケーションがデータを消費できず、受信バッファスペースが解放されなくなる可能性があります。
フロー制御制限の更新情報をタイムリーに送信すると、パフォーマンスが向上します。フロー制御の更新情報を提供するためだけにパケットを送信すると、ネットワーク負荷が増加し、パフォーマンスに悪影響を与える可能性があります。フロー制御の更新情報をACKフレームなどの他のフレームと共に送信することで、更新コストを削減できます。
4.4. ストリームのキャンセル処理
エンドポイントは、コネクションレベルのフロー制御に必要なすべてのバイト数を把握するために、各ストリームで消費されたフロー制御クレジットの量について最終的に合意する必要があります。
RESET_STREAMフレームを受信すると、エンドポイントは該当するストリームの状態を破棄し、そのストリームに着信する以降のデータを無視します。
RESET_STREAMは、ストリームの一方向を突然終了します。双方向ストリームの場合、RESET_STREAMは反対方向のデータフローには影響を与えません。両方のエンドポイントは、終了していない方向のストリームのフロー制御状態を、その方向が終了状態になるまで維持しなければなりません(MUST)。
4.5. ストリームの最終サイズ
最終サイズとは、ストリームによって消費されるフロー制御クレジットの量です。ストリーム上の連続するすべてのバイトが一度だけ送信されたと仮定すると、最終サイズは送信されたバイト数になります。より一般的には、これはストリーム上で送信されたオフセットが最も大きいバイトのオフセットより1大きい値、またはバイトが送信されなかった場合は0になります。
送信側は、ストリームの終了方法に関わらず、常にストリームの最終サイズを受信側に確実に伝えます。最終サイズは、FINフラグが設定されたSTREAMフレームのオフセットフィールドと長さフィールドの合計です。ただし、これらのフィールドは暗黙的に設定される場合もあります。あるいは、RESET_STREAMフレームの最終サイズフィールドにこの値が設定されます。これにより、両方のエンドポイントで、送信側がそのストリームで消費したフロー制御クレジットの量について合意していることが保証されます。
エンドポイントは、ストリームの受信側が「サイズ既知」または「リセット受信」状態(セクション3)に入ったときに、ストリームの最終サイズを認識します。受信側は、接続レベルのフロー コントローラでストリームで送信されたすべてのバイト数を把握するために、ストリームの最終サイズを使用する必要があります (MUST)。
エンドポイントは、最終サイズ以上のデータをストリームに送信してはいけません (MUST NOT)。
ストリームの最終サイズは、一度判明したら変更できません。ストリームの最終サイズの変更を示す RESET_STREAM または STREAM フレームを受信した場合、エンドポイントは FINAL_SIZE_ERROR のエラーで応答する必要があります (SHOULD)。エラー処理の詳細については、セクション 11 を参照してください。受信側は、ストリームが閉じられた後であっても、最終サイズ以上のデータの受信を FINAL_SIZE_ERROR のエラーとして扱う必要があります (SHOULD)。これらのエラーの生成は必須ではありません。エンドポイントがこれらのエラーを生成することを要求することは、エンドポイントが閉じられたストリームの最終サイズの状態を維持する必要があることも意味し、それは重要な状態コミットメントを意味する可能性があるためです。
4.6. 同時実行制御
エンドポイントは、ピアがオープンできる受信ストリームの累計数を制限します。オープンできるのは、ストリームIDが(max_streams * 4 + first_stream_id_of_type)未満のストリームのみです。表1を参照してください。初期の制限はトランスポートパラメータで設定されます。セクション18.2を参照してください。それ以降の制限は、MAX_STREAMSフレームを使用して通知されます。セクション19.11を参照してください。単方向ストリームと双方向ストリームには、それぞれ異なる制限が適用されます。
$ 2^{60}を超える値を持つmax_streamsトランスポートパラメータまたはMAX_STREAMSフレームを受信した場合、可変長整数で表現できない最大ストリームIDが許可されます。セクション16を参照してください。どちらかを受信した場合、問題となる値がトランスポートパラメータで受信された場合はTRANSPORT_PARAMETER_ERROR、フレームで受信された場合はFRAME_ENCODING_ERRORのコネクションエラーを生成して、直ちに接続を切断しなければなりません(MUST)。セクション10.2を参照してください。
エンドポイントは、ピアによって設定された制限を超えてはなりません(MUST NOT)。送信した制限を超えるストリームIDを持つフレームを受信したエンドポイントは、これをSTREAM_LIMIT_ERRORタイプのコネクションエラーとして扱わなければなりません(MUST)。エラー処理の詳細については、セクション11を参照してください。
受信者がMAX_STREAMSフレームを使用してストリーム制限を通知すると、より小さな制限を通知しても効果はありません。ストリーム制限を増加させないMAX_STREAMSフレームは無視されなければなりません(MUST)。
ストリームおよびコネクションフロー制御と同様に、このドキュメントでは、MAX_STREAMSを介してピアにいつ、いくつのストリームを通知するかを実装に委ねています。実装は、ピアが利用できるストリーム数をほぼ一定に保つために、ストリームが閉じられる際に制限を増やすことを選択できます。
ピアの制限のために新しいストリームを開くことができないエンドポイントは、STREAMS_BLOCKEDフレーム(セクション19.14)を送信する必要があります(SHOULD)。このシグナルはデバッグに有用であると考えられています。エンドポイントは、追加のクレジットをアドバタイズする前にこの信号を受信するのを待ってはなりません。そうすると、ピアが少なくともラウンドトリップ全体でブロックされ、ピアが STREAMS_BLOCKED フレームを送信しないことを選択した場合は無期限にブロックされる可能性があります。
5. 接続
QUIC接続は、クライアントとサーバー間の共有状態です。
各接続はハンドシェイクフェーズから開始されます。このフェーズでは、2つのエンドポイントが暗号ハンドシェイクプロトコルQUIC-TLSを使用して共有秘密鍵を確立し、アプリケーションプロトコルをネゴシエートします。ハンドシェイク(セクション7)では、両方のエンドポイントが通信可能であることを確認し(セクション8.1)、接続のパラメータを設定します(セクション7.4)。
アプリケーションプロトコルは、ハンドシェイクフェーズ中に、いくつかの制限付きで接続を使用できます。0-RTTでは、サーバーからの応答を受信する前に、クライアントがアプリケーションデータを送信できます。ただし、0-RTTはリプレイ攻撃に対する保護を提供しません(QUIC-TLSのセクション9.2を参照)。サーバーは、クライアントのIDと生存性を確認するための最終的な暗号ハンドシェイクメッセージを受信する前に、クライアントにアプリケーションデータを送信することもできます。これらの機能により、アプリケーションプロトコルは、セキュリティ保証の一部を犠牲にしてレイテンシを短縮するオプションを提供できます。
接続ID(セクション5.1)の使用により、エンドポイントの直接選択による場合と、ミドルボックスの変更によって強制された場合の両方で、接続を新しいネットワークパスに移行できます。セクション9では、移行に伴うセキュリティとプライバシーの問題の軽減策について説明します。
不要になった、または望ましくなくなった接続については、セクション10で説明するように、クライアントとサーバーが接続を終了する方法はいくつかあります。
5.1. 接続ID
各接続は、接続識別子(接続ID)のセットを持ち、各接続IDは接続を識別します。接続IDはエンドポイントによって独立して選択され、各エンドポイントはピアが使用する接続IDを選択します。
接続IDの主な機能は、下位プロトコル層(UDP、IP)でのアドレス指定の変更によって、QUIC接続のパケットが誤ったエンドポイントに配信されないようにすることです。各エンドポイントは、実装固有(場合によってはデプロイメント固有)の方法を使用して接続IDを選択します。これにより、その接続IDを持つパケットはエンドポイントにルーティングされ、エンドポイントは受信時にそのパケットを識別できます。
複数の接続IDを使用するのは、エンドポイントの協力なしに、オブザーバーが同じ接続のパケットであると識別できないパケットをエンドポイントが送信できるようにするためです。セクション9.5を参照してください。
接続IDには、外部オブザーバー(つまり、発行元と協力しないオブザーバー)が、同じ接続の他の接続IDと関連付けるために使用できる情報を含めてはなりません(MUST NOT)。単純な例として、これは同じ接続で同じ接続IDを複数回発行してはならないことを意味します(MUST NOT)。
長いヘッダーを持つパケットには、送信元接続IDフィールドと宛先接続IDフィールドが含まれます。これらのフィールドは、新しい接続の接続IDを設定するために使用されます。詳細はセクション7.2を参照してください。
短いヘッダーを持つパケット(セクション17.3)には、宛先接続IDのみが含まれ、明示的な長さは省略されます。宛先接続IDフィールドの長さは、エンドポイントが認識していることが想定されています。接続IDに基づいてルーティングするロードバランサーを使用するエンドポイントは、ロードバランサーと接続IDの固定長について合意するか、エンコード方式について合意することができます。固定部分には明示的な長さをエンコードすることができ、これにより接続ID全体の長さが変化してもロードバランサで使用できます。
バージョンネゴシエーション(セクション17.2.1)パケットは、クライアントが選択した接続IDをエコーし​​ます。これは、クライアントへの正しいルーティングを保証するためと、パケットがクライアントから送信されたパケットへの応答であることを示すためです。
正しいエンドポイントへのルーティングに接続IDが不要な場合は、長さ0の接続IDを使用できます。ただし、長さ0の接続IDを使用しながら同じローカルIPアドレスとポートで接続を多重化すると、ピア接続の移行、NATの再バインド、クライアントポートの再利用が発生する場合に障害が発生します。エンドポイントは、これらのプロトコル機能が使用されていないことが確実である場合を除き、長さ0の接続IDを持つ複数の同時接続に同じIPアドレスとポートを使用してはなりません(MUST NOT)。
エンドポイントが長さ0以外の接続IDを使用する場合、エンドポイントに送信するパケットに選択できる接続IDがピアに提供されていることを確認する必要があります。これらの接続 ID は、NEW_CONNECTION_ID フレーム (セクション 19.15) を使用してエンドポイントによって提供されます。