タグ: QUIC

  • QUICの接続移行、通信を保ちながら接続IDを使い分ける理由

    QUICの接続移行、通信を保ちながら接続IDを使い分ける理由

    Wi-Fiから携帯回線へ切り替わると、端末が通信に使うIPアドレスやポート番号が変わることがあります。QUICには、この変化をまたいで一つの接続を続けるための接続移行(connection migration)があります。ただし「同じ接続だと分かること」「次の送信先を選ぶこと」「新しい経路へ実際に届くと確かめること」は、別々の役目です。

    簡単にまとめると?

    • QUICの接続IDは、IPアドレスとポートの組だけに頼らず、受信パケットを既存の接続へ結び付けます。一つの接続で複数のIDを使えます。
    • 意図的に新しいローカルアドレスから送るときは、相手が発行した未使用の接続IDを使います。同じIDの使い回しを避け、異なる経路の通信を結び付ける手掛かりを減らします。
    • 新しいアドレスの選択と経路検証は別です。遅れて来た古いパケットで送信先を戻さない規則もあります。

    接続の「住所」と「識別子」を分ける

    IPアドレスとUDPポート番号は、パケットを届ける先を表します。一方、接続ID(Connection ID)は、届いたパケットをどのQUIC接続として扱うか判断するための識別子です。RFC 9000の5.1節は、この分離によって下位のアドレスが変わっても接続を識別できる仕組みを説明しています。

    たとえばサーバーが、同じ接続を指すID「S1」と「S2」をクライアントへ渡したとします。クライアントはサーバーへ送るパケットの宛先接続IDに、そのどちらかを入れます。S1からS2へ変えても、それだけで新しい接続を作ったことにはなりません。IDは相手が発行して受信時に解釈するものであり、利用者の名前や端末の恒久的な番号ではありません。

    接続を続けるのに、なぜIDを変えるのか

    旧アドレスAと新アドレスBで同じ接続IDを使い続ければ、途中でパケットを観察する人にも両者の結び付きが見えやすくなります。RFC 9000の9.5節は、複数のローカルアドレスから送信するときに同じ接続IDを再利用してはならないと定めています。意図的な移行だけでなく、新しい経路を試すパケットにも関わる規則です。

    したがって、AではS1、Bではあらかじめ受け取っていた未使用のS2、という使い分けが必要になります。相手はNEW_CONNECTION_IDフレームなどで追加のIDを用意します。新しいIDを勝手に作って送ればよいわけではありません。

    これは匿名性を保証するものではありません。IDを変えても、送信の時刻や大きさなどから通信が関連付けられる可能性は残ります。また、ネットワーク内のNATが外側のポートなどを変えるNATリバインディングは、端末が意図して移行する場合とは事情が違います。端末が変化を知らないため、受信側には新しいアドレスから同じIDのパケットが届くこともあります。

    遅れて届いた旧経路のパケットで、宛先を戻さない

    経路が変わる前後では、送った順と届く順が入れ替わることがあります。移行が許され、ハンドシェイクも確認済みの接続で、サーバーが正当なデータ入りパケットを次の順に受け取ったとします。番号は説明用で、同じアプリケーションデータのパケット番号空間を使います。

    同じQUIC接続で、旧経路Aの宛先接続ID S1から新経路BのS2へ移る例。受信順40、42、41では宛先はA、B、Bとなり、経路検証は別に確認する。
    受信した順番の例。接続ID S1とS2は、サーバーが同じ接続用に発行したもの。経路検証の成否は、この宛先選択とは別に管理する。

    最初はAから番号40が届き、次にBから番号42が届きます。Bのパケットはそれまでで最大の番号なので、サーバーは以後の送信先をBへ変えます。その後、遅れてAから番号41が来ても、41は42より小さいため、宛先をAへ戻す理由にはしません。

    これは9.3節にある、送信先のアドレス変更はそれまでで最大の番号を持つ非プロービングパケットに応じて行う、という規則の例です。非プロービングとは、経路を試すためだけのパケットではない、という意味です。番号41の中身を受信処理することと、送信先を切り替えることは区別します。

    経路検証は「その先へ届くか」を確かめる

    接続IDが一致しても、新しいアドレスへ確実に通信できるとは限りません。経路検証(path validation)では、PATH_CHALLENGEに予測しにくい値を入れて送り、同じ値を返すPATH_RESPONSEを確認します。単にチャレンジを含むパケットがACKで確認された、というだけでは検証完了になりません。

    この確認は、相手がそのアドレスでパケットを受け取れるか調べるためのものです。パケットの暗号学的な認証を接続IDで代用する仕組みではありません。移行前に経路を試すことはできますが、常に検証が終わってから最初のデータを送る、という単純な順番でもありません。検証前には送信量の制限などを守りながら扱います。

    そのためログを読むときは、IDがS2へ変わった時刻だけで「移行成功」と決めず、新アドレスの採用、チャレンジへの応答、実際のデータの継続を分けて見ると、どこで止まったかを追いやすくなります。

    移行できる条件と、維持されない性能

    ここで扱ったのはQUIC version 1の基本仕様です。ハンドシェイクの確認前に移行を始めることはできません。相手がdisable_active_migrationを示した場合など、能動的な移行が許されない条件もあります。サーバーが通知したpreferred_addressへ移る仕組みには別の条件があり、自由にどちらの端点でも住所を変えられる、という意味ではありません。

    接続が続いても、回線の往復時間や使える帯域は変わります。新しい経路では輻輳制御や往復時間の見積もりを見直す必要があり、ポートだけの変更などには例外があります。「接続を作り直さずに続けられる」と「速度も待ち時間も変わらない」は別の性質です。

    この例は仕様の一部を小さな時系列で示したものです。実パケットを送る試験でも、特定アプリの回線切替を保証する測定でもありません。運用上は、移行の許可、利用できる接続ID、経路検証、切替後の性能を別々に確かめることが判断の材料になります。

    出典

  • QUICのストリーム、パケット欠落による待ちを分ける仕組み

    QUICのストリーム、パケット欠落による待ちを分ける仕組み

    Webページを開くとき、文章、画像、スクリプトなど、いくつものデータが並行して届きます。その途中で通信の小さなまとまりであるパケットが一つ失われたら、関係のない画像まで待つのでしょうか。この「どこまで待ちが広がるか」を変えるのが、HTTP/3の土台となるQUICのストリームです。

    QUICは、一つの接続の中に複数のデータの流れを作り、それぞれで順序を管理します。ある流れの欠落を埋めている間も、別の流れでは届いたデータをアプリケーションへ渡せます。ただし、回線の混雑やアプリケーション側の依存関係まで消えるわけではありません。RFC 9000のQUIC v1とHTTP/3の仕様から、分けられる待ちと、なお共有する制約を見ていきます。[1][2]

    簡単にまとめると?

    • QUICはストリームごとにデータの位置を管理するため、一つのストリームの欠落が、別のストリームの順序待ちに直結しません。
    • 同じストリームの中では、欠けた手前のデータを待つ場面が残ります。再送するのは必要な情報であり、失われたパケット全体をそのまま送り直す仕組みではありません。
    • 輻輳制御や接続全体の受信枠は共有します。HTTP/3でもヘッダー圧縮の依存はあり、「パケットが落ちても常に速い」とは言えません。

    HTTP/2では、分かれた要求も一つのTCPの順番に並ぶ

    HTTP/2は、画像Aの応答を全部送り終えてから画像Bへ進むのではなく、複数の要求・応答のデータを一つの接続に交ぜて送れます。これを多重化と呼びます。ただし、その下で使うTCPから見えるのは、一続きのバイト列です。HTTP/2のどの要求に属するかは、TCPの再送処理には見えません。[2][3]

    途中のデータが抜けると、TCPはその穴より後ろのデータを順序通りに渡すために待ちます。後ろに別の要求のデータが届いていても、同じTCP接続上では一緒に足止めされることがあります。これが、転送層でのhead-of-line blocking(先頭の欠落による後続の待ち)です。HTTP/2に多重化がないのではなく、TCPの順序保証が要求の境界を区別しない点が問題になります。

    QUICは「どの流れの、何バイト目か」を別々に管理する

    QUICのストリームは、順序のあるバイト列です。QUICはUDPの上で動きますが、ストリームのデータを単に送り放しにするわけではありません。ストリームIDで流れを識別し、オフセットで、その流れの先頭から何バイト目のデータかを表します。受信側はこの二つを使い、各ストリームのデータを並べ直します。[1]

    HTTP/3では、通常の一組の要求と応答に、クライアントが開始する双方向のQUICストリームを一つ使います。複数の要求を別々のストリームに載せることで、一方の順序待ちをもう一方へ広げずに済みます。ただし、異なるストリーム同士のデータが届く順番は保証されません。処理Bを必ず処理Aの後に行いたい場合は、その依存をアプリケーションが扱う必要があります。[2]

    次の表は、ストリームAとBを同じ接続で受信する仮定の例です。接続とストリームは利用可能で、ここでは受信データを順序通りに渡します。説明のため、Aの最初の1,000バイトだけが失われたとします。

    二つのストリームで異なる待ちが生じる例。数値と到着順は仕様の説明用に設定したもので、実測やHTTP/3メッセージの再現ではありません。
    順番 送受信の出来事 アプリケーションへ渡せるデータ
    1 Aのオフセット0、長さ1,000バイトを含むパケットが失われる Aの0〜999バイトがまだ届かない
    2 Bのオフセット0、長さ1,000バイトが届く Bの0〜999バイトは渡せる。Aの穴埋めを待たない
    3 Aのオフセット1,000、長さ1,000バイトが届く Aの1,000〜1,999バイトは保持し、手前の到着を待つ
    4 Aのオフセット0、長さ1,000バイトが新しいパケットで再び届く Aの0〜1,999バイトがつながり、順序通りに渡せる

    違いは、Aの欠落そのものが消えたことではありません。Aには待ちが残り、Bには同じ待ちを負わせずに済むことです。逆に、AとBのデータを同じパケットへまとめ、そのパケットが失われれば、両方のデータが欠けます。ストリームは別の物理回線ではなく、一つの接続の中で順序を分ける単位です。[1]

    パケットの再送と、データの再送を区別する

    QUICのパケットには、データや制御情報を表すフレームが入ります。ストリームのデータはSTREAMフレームで運ばれ、一つのパケットに複数のフレームを入れることもできます。

    失われたパケットを、QUICが丸ごと同じ形で送り直すわけではありません。必要な情報を新しいフレームに載せ、新しいパケットとして送ります。ストリームがリセットされていなければ、欠けたストリームデータもこの方法で再送されます。受信側にとって大切なのは、「前と同じパケットが戻ったか」ではなく、「そのストリームの必要な位置にデータがそろったか」です。[1]

    このため、パケットの区切りと、アプリケーションが読むデータの区切りを同一視できません。STREAMフレームの境界は、送信、再送、受信側への引き渡しを通して保たれる保証がありません。アプリケーションのメッセージの区切りは、その上のプロトコルで定めます。

    別ストリームでも残る、三つの待ち

    一つ目は、ネットワークの混雑に応じた送信量の調整です。 輻輳制御はストリームごとに独立した送信枠を与える仕組みではありません。通常の同じ経路を使う接続では、各ストリームのデータが同じ制約を受けます。RFC 9002は経路ごとに輻輳制御を行い、損失などを受けて送信量を抑える方法を示しています。Bがすでに受信したデータを渡せることと、Bの次のデータをすぐ送れることは別です。[4]

    二つ目は、受信側が許可するデータ量です。 これは回線の混雑を扱う輻輳制御とは目的が異なり、受信側のバッファーをあふれさせないためのフロー制御です。QUICにはストリーム単位の上限と、接続全体の上限があります。Aだけがストリーム上限に達しても、Bに余裕があり接続全体の枠も残っていれば、Bは新しいデータを送れます。一方、接続全体の枠を使い切ると、別のストリームでも新しいデータを追加できません。[1]

    三つ目は、HTTP/3など上位の処理にある依存です。 HTTP/3のヘッダー圧縮にはQPACKを使います。圧縮されたヘッダーが、別に送られる動的テーブルの更新を参照している場合、その更新を受信・処理するまでメッセージを利用できません。QUICで別々に運べることと、アプリケーションが別々に解釈できることも区別する必要があります。[2]

    比較するときは「何の完了が遅れたか」を見る

    HTTP/2とHTTP/3を比べるなら、平均の転送速度だけでなく、複数の要求のうち、欠落の影響を受けたものと受けなかったものの完了時間を分けると、ストリームの独立性が何に効いたかを考えやすくなります。同じ接続内の話なのか、別接続を並べた結果なのかも確認したい点です。

    例えば、ある画像のデータに欠落が起きても、別の画像を受信して処理できれば、その要求は先へ進めます。しかし、画面表示に必要なスクリプト自体が遅れていたり、ヘッダーの解釈が待ちになっていたりすれば、ページ全体の表示が同じように早まるとは限りません。これは仕様から考えられる条件の整理であり、特定サイトを測定した結果ではありません。

    実際の比較では、同時要求数、データ量、パケット損失や往復時間、サーバー側の処理時間、キャッシュの状態をそろえ、要求ごとの完了時間とページ全体の指標を分けて記録する必要があります。QUICの強みを捉える手がかりは、「待たなくなる」という一語よりも、あるデータの欠落を、無関係なデータの順序待ちへ広げないという仕組みにあります。

    参考資料

    1. RFC 9000, QUIC: A UDP-Based Multiplexed and Secure Transport(2021年5月)。2節・2.2節(ストリームと順序)、4.1節(フロー制御)、12.4節(フレーム)、13.3節(情報の再送)、19.8節(STREAMフレーム)。
    2. RFC 9114, HTTP/3(2022年6月)。1.1〜1.2節(TCPとの違い)、2節・4.1節・6.1節(要求とストリーム)、4.1節・4.2.1節(QPACKによる依存)。
    3. RFC 9113, HTTP/2(2022年6月)。1節(多重化と残るTCPのhead-of-line blocking)。
    4. RFC 9002, QUIC Loss Detection and Congestion Control(2021年5月)。7節(経路ごとの輻輳制御と送信制約)。