⚒️

Multipath Extension for QUICを読んでみる

2023/05/14に公開

注意事項

本記事は自分の研究用の記録をメインの目的として残しています。
中には仕様が定かではないソースや自分の勉強不足からくる誤解が書いてある可能性があります。あらかじめご了承ください。

Introduction

Multipath Transport Protocolという考えは
近年の通信デバイスは4G/Wifiとかいろんな通信手段があるから全部同時に使ったろ!
というもので、これをQUICに適用したものにMultipath-QUICがあります。
TCPにもMultipath-TCPがあり、こちらはIETFによって標準化されています。
(SiriなどはMP-TCPを使っているようですね。)

これにより

  • 帯域の有効活用
  • 安定した通信の品質提供
  • 冗長性の確保
    等が見込める(そりゃ経路複数使ってんだからそうだろ、という感じですが。)

また、QUIC特有の要素として、TCPとは違いカーネル空間ではなくユーザ空間の実装が可能なので、OSが対応していないと動作しないというようなMP-TCPの壁がなくなっています。

MP-QUICに関してはまだDraftの状態ですが、変遷も辿りながら簡単なQUICの説明とMultipath-QUICの入門を行なってみます。

Basic Knowledges

MP-QUICはQUICの仕組みを再利用しながら作られているため、QUICの仕組みの理解が必要です。
詳しい説明は今度するとして、とりあえず要所だけかいつまんでみます。(他の方の書いてる記事の方が圧倒的に詳しいです。おさらいまで。)

HTTP HoL Blocking


HTTP/1で問題視されていた問題です。
図のような状況でクライアントはサーバに対してA,B,Cのリソースをリクエストしたとします、HTTP/1においては、リクエストは常に順番を保つ必要があります。
そのため、A,B,Cのデータを取り扱うときにそれぞれのレスポンスを待ってからでないと送信を行うことができず、メモリを持て余してしまうといった問題があります

Stream

HTTP/2で上記を解決するために複数のリクエストレスポンスを多重化するために作られたものがストリームです。ストリーム(ID)は1つのHTTPリクエストに対して1つ割り当てられます。
そのため、一つのTCPコネクションの中にストリームという形で新しい経路を作ります。
例えば100個リクエストがあれば100個ストリームを作り、クライアントはサーバのレスポンスを待たず100個のリクエストを送れるだけ送れます(送れるだけっていうと厳密には違いますが)。

Frame

フレームはHTTP/2における通信の最小単位です。
フレームは自身の所属するストリームIDを持ち、効率的にデータのやり取りを使うために利用されます。フレームはヘッダの中に左図のような識別子を持ちます。
これにより、リクエストレスポンスの双方向多重化を可能にしているわけですね。

図のように、一つのストリーム内でいろんなフレームをパケットにまとめて送信することができます。
フレームにはいくつかの種類があらかじめ定義されています。

https://qiita.com/nozmiz/items/6a5126d379a8c6e1524c#フレーム より抜粋

QUICにも独自のフレームがあります。

TCP HoL Blocking

HTTP/2がTCPベースのプロトコルであることに由来する問題です。

原因は、TCPはアプリケーションレイヤにデータを渡す際に順序を保証することです。
例えば、TCPはパケットが届かなかった場合に再送を行わせ、その間は待機させます。(この領域はアプリケーションレイヤであるHTTP/2は手出しできません)
そのため、パケロスが1つでも発生してしまうと、そのパケットに関係ないストリームを含めたレスポンスデータが遅延するという問題が起きます。
そのため、ここまでのHTTPの歴史を辿ると、従来はトランスポートレイヤとアプリケーションレイヤでHoL Blocking問題が起きていたということですね。

QUICについて(ちょっとだけ...)

ちょっとだけQUICについてお話しします。(のちの説明に関わる部分だけです)
詳しい内容は他の方が書いているものの方が圧倒的にわかりやすいです。

QUIC Handshake

QUICのハンドシェイクはTCPのハンドシェイクとTLSのハンドシェイクの役割を一気に担います。
Client Helloによってクライアントの情報と鍵の情報を交換し、
Server Helloによって共有鍵の情報を生成し、返答を行うといった形ですね。

これを行うことでコネクションが成立するわけです。

Connection Migration

通信の途中で、クライアントのIPアドレスやポート番号が変更されることがあります。

例として以下のものがあげられます。

  • 利用するネットワークが携帯網からWi-Fiに変わった(エンドポイントが意図した通信環境の変化)
  • 中間のNATが、別のポート番号を割り当てた(NATリバインディング,エンドポイントが意図しないミドルボックスでの変化

そのため、クライアントはパス検証(後述)を通して新しいパスで通信を再開できるようにQUICでは設計されています。これがConnection Migrationです。

使用の主な条件は

  • クライアントであること
  • disable_active_migrationというパラメータが有効化されていないこと
  • ハンドシェイクが終わった後であること
    です。

パス検証

サーバにとって、初見のIPアドレスへパケットを送信するのはリスクがあります。
そのため、QUICには相手を検証する機構が存在しています。

QUICにおいてアドレス検証が行われるのは2つのパターンで、どちらも増幅攻撃を防御するために行われます

コネクション確立時のアドレス検証

相手からHandshakeパケットを受け取って、それを問題なく処理できたときに検証完了

コネクションマイグレーション時のアドレス検証(パス検証)

それぞれ8バイトの乱数を格納し。PATH_CHALLENGEフレームに収められた乱数と同じバイト列がPATH_RESPONSEフレームに格納されて戻って来れば、検証完了

それではConnection MigrationとPath Validationを図式化してみます。
それぞれCIDがC0とS0を使用している環境下です。

赤線は新しい経路です。これらのプロセスが終わった後、端末は今までやっていた接続を再開できます。

今回は意図してCMする例で、NATリバインディングの際は既存のCIDのまま赤線以降が始まります(この辺はセキュリティとかの兼ね合いとかで色々議論されているようですが、割愛します)

Multipath QUIC

長くなりましたが本題です。

変遷

QUIC及びHTTP/3のドラフト仕様がLast Callになった2020年、標準化として大詰めを迎えた時期に3つの提案仕様が出ました。

Quentin De ConickらによるMultipath Extentions for QUIC

2017年頃から提案している、Multipath拡張仕様のうち一番古くからとりくまれている仕様。初期に、MPTCP v0のデザインの影響を受けている。Connection ID毎に単方向のUniflowsを持つ

Private Octopus IncのChristian Huitema氏 による提案

その他のMultipath拡張とは異なりPathごとの通信識別子を持たず、QUIC v1がすでに持っているコネクションマイグレーションを利用します。マイグレーションとは異なり、複数のPathをactiveなままに維持する

AlibabaによるMP-QUIC

Quentin De ConinckらによるMP-QUICを参考にしつつも、PathごとにSubConnectionとする構成を持つ。どうやら、すでに中国の特定動画ストリーミングサービスでユーザが使えるようになっているらしく、規模を拡大しつつ実験中らしい。

こちらの情報丸パクリです
https://asnokaze.hatenablog.com/entry/2020/10/26/063502

ここから、提案仕様の著者が合同で作成したインターネットドラフトが作成されました(25 October 2021)
それがこの
Multipath Extension for QUIC (draft-ietf-quic-multipath) です

提案初期の基本的な設計は以下の通りです

  • RFC 9000 QUICv1の仕組みを可能な限り再利用する。
  • パケットはQUICv1と同じパケットヘッダ形式を使用する
  • 輻輳制御、RTT測定、PMTU検出はパスごとに行う。
  • パスは送信元/送信先IP・送信元/送信先ポートの4タプルごとに一つとなる
  • パスごとにコネクションIDは異なるものが使用される

上記を設計するにあたって新しく生み出されたものが以下の通りです。(トランスポートパラメータやエラーコード含めると、他にもあります)

  • パスを破棄する PATH_ABANDONフレームの定義
  • シングルパケット番号空間/マルチパケット番号空間の定義
  • マルチパケット番号空間利用時の、ACK_MPフレームの定義
  • パスの状態を確認するPATH_STATUSフレームの定義
    詳しくはdraftのIANA Considerationsをみてみてください。

今回説明するのはこのドラフトのv4になります。

トラッカーを見るとこんな感じです。
どうでもいいですが、IETFドラフトの前身となっているdraft-lmbdhk-quicのlmbdhkはこれまでの提案仕様の著者の頭文字をとっているようですね。

IETFのものはこれらのdraftからコア機能を抽出して提案することを目指しているようです。

実装状況ですが、
MP-QUIC based on quic-go というものがあります。
1つめのmp-quic提案者のdeconinck氏による実装ですが、ぱっとコード眺めた感じ現在のIETF仕様ではないようです。

MP-QUICのハンドシェイク(複数のパス使っていい?)

MP-QUIC対応端末は、相手の端末がMP対応しているかを確認する新しいトランスポートパラメータ(後述)を用意します。
enable_multipathについては以下の通りで、可能か不可能かの2パターンのみです

以前はパケット空間(後述)の方式によってとかで4つほどパターンがありましたが、最新版ではこれに落ち着いているようです。

ハンドシェイクの様子を以下に示します。

流れとしてはQUICのハンドシェイクと同じです。一つ違うのは、トランスポートパラメータに上記のものが増えていることです。
クライアントがinitial_packetに載せてMPしていい?と聞いてサーバはその返答でいいか悪いかを返すわけですね。

トランスポートパラメータとは

説明が遅れました。QUICではハンドシェイク時に、通信に関するパラメータとしてquic_transport_parametersというデータを送信します。これは、TLSハンドシェイクメッセージに付随するデータであるTLS拡張として送られるようです。

用法としては以下の通りのようです

  • クライアントからサーバへは、ClientHelloの拡張として格納される
  • サーバからクライアントへは、EncryptedExtensionsに格納される

他にも純正QUICのパラメータには以下のものがあるようです

  • max_idle_timeout 最大アイドル時間
  • stateless_reset_token ステートレスリセット用トークン
  • max_ack_delay Ackを送るまでの最大遅延時間
  • initial_max_streams_bidi オープンできる双方向ストリーム数の初期値
  • initial_max_streams_uni オープンできる単方向ストリーム数の初期値

パスを追加しよう

さて、ハンドシェイクでお互いにMP-QUIC対応だと分かったならばパス追加のプロセスが始まります。
下の図では、新しいパス(赤線)の確立が行われます。パス検証はQUIC従来の仕組みを利用(青の通信は前回同様C0,S0を利用しています。

あらかじめNew_connection_IDのリクエストを出してサーバに新しいCIDを発行してもらい、それを新しい経路から繋ぎに行くことで複数のパスを使えるようにしよう。というものです。

あれ、これコネクションマイグレーションとそのパス検証だよね、と思うかもしれません。その通りです。QUICの仕組みを再利用しよう!ということなのでこのような形になっているわけですね。

Connection Migrationとの区別は?

移行とパス追加の区別はどうやってするの?という疑問が当然起こると思います。

パスの情報は送信元IP、アドレスと宛先IP、アドレスの4つ(4-tuple)によって管理されています。

そのため、通信をする上で新しいCIDを使用せずに4-tupleが変更された場合、通常のコネクションマイグレーションと同じように動作させるという方式をMP-QUICではとっています。

並べてみるとこんな感じですね。(多分)

  • 意図せずにCM、MP対応 ー 上記の場合に相当。接続移行を開始
  • 意図してCM、MP対応  ー 意図している場合、接続移行の必要はない。パス追加を開始。
  • 意図せずにCM、MP非対応ー 接続移行を開始
  • 意図してCM、MP非対応 ー 接続移行を開始

パスを閉じよう

パスのクローズには以下の2種類のシチュエーションがあります。

  • コネクション(全体)を閉じる時(従来のQUIC 作法があるが今回は省略)
  • 特定のパスだけを閉じる時(初期パスを閉じたいときや、パスの品質悪化時に発生します)

パスを閉じたいときは、PATH_ABANDONフレームを使います。

説明は図の中にある通りです。

異なるパスからのパケット、どう区別する?

それぞれのコネクションから受け取るデータをどのように識別していけばいいか?という問題があります。
パケットが二つの経路から受け取られるのでサーバもクライアントも管理するのが大変です。
このため以下の方式が考えらました。
-> MP-QUIC特有の要素をQUICに追加・変更をかけて区別できるようにしよう
->複数のパケット番号空間と改造ACK(MP_ACK)の追加

Single Packet Number Space

パケット番号空間はパケットをエンドポイントで管理するための番号です。従来のシーケンス番号的な役割を果たします。
まずは単一のパケット番号空間です。

右図は従来のQUICと同じような管理をします。複数の経路でも同じ番号空間を共有します。
マルチパスではラウンドロビンでパケット番号を増加させるようです。

ただ、この方式だとマルチパスにおいてはうまくACKされない恐れがあるようです。

Mulitiple Number Space

こちらはパスごとに番号空間を管理する方法です。

こちらは先ほどの問題に対応できます。しかし、パスごとに番号空間を使用する都合上
それぞれのパスに対して別のACKを出す必要があります。
そのため、パス情報を盛り込んだMP_ACKの誕生しました。

提案仕様1人目のDe coninck氏が
The Packet Number Space Debate in Multipath QUIC
という論文を発表しました。こちらでは、パスごとに番号空間を使用した場合の性能をns-3で評価しています。
https://like-cat.hatenablog.com/entry/2021/12/18/131621  (とても詳しい解説記事)
この甲斐あってかIETFドラフトからはMultiple Number SpaceとMP_ACKの仕組み(後述)が採用されています。

しかし、この仕組みを利用するためには以下の条件が必要になります。

  • エンドホストは、パケット番号空間を識別するために長さが0ではないConnection IDの使用が必須になる
  • QUIC-TLSに必要となるAEADの仕組みを調整する必要がある(今回は省略)
    この辺はまたいつか別の記事で...

まとめ

QUICはHTTP/3だけのものではなく、汎用性があるトランスポートプロトコルとして作られています。
そのため、IoTやV2-Xなど、さまざまな分野で期待されているものです。
そうした中、QUICの強みが生かされるこういった拡張仕様がどんどん決まってくるとよりどんなことができるんだろうか、と考えてしまいますよね。
このドラフト、標準化されるといいな〜
では!

参考文献

https://qiita.com/nozmiz/items/6a5126d379a8c6e1524c
https://asnokaze.hatenablog.com/entry/2020/10/26/063502
https://like-cat.hatenablog.com/entry/2021/12/18/131621
https://eng-blog.iij.ad.jp/archives/10892
https://eng-blog.iij.ad.jp/archives/14383
https://datatracker.ietf.org/doc/draft-ietf-quic-multipath/

Discussion