12 ポイント 投稿者 GN⁺ 2025-08-02 | 1件のコメント | WhatsAppで共有
  • QUIC プロトコルを Linuxカーネル に正式統合する最初のパッチが提出された
  • 既存の TCP が抱える限界点(遅延、head-of-line blocking、中間機器によるプロトコルの硬直化など)を改善することが目的
  • QUIC は UDP ベースでマルチストリーム対応とエンドツーエンド暗号化機能を提供し、カーネルに導入されれば、より幅広いプラットフォームやハードウェアを活用できる可能性が高まる
  • 初期のカーネル実装の性能は既存の TCP やカーネル TLS より低いと測定されたが、今後 ハードウェアオフロード と最適化によって性能向上が期待できる
  • 現在 Samba、カーネルベースの SMB/NFS、curl などで対応の議論が活発に進んでいるが、メインラインへのマージにはなお時間を要する見込み

QUICプロトコル登場の背景とTCPの限界

  • QUIC は、従来のインターネットで TCP が抱えてきたさまざまな問題を解決する目的で作られた
  • TCP の接続過程で発生する 3-wayハンドシェイク による遅延、マルチストリーム対応の不十分さ、パケット損失に伴う head-of-line blocking 現象などによって、Web 利用体験が低下していた
  • TCP のメタデータ は暗号化されないまま送信されるため情報漏えいのリスクがあり、インターネット上の ミドルボックス(中間機器) が接続情報を基にトラフィックをフィルタリングし、その結果 プロトコルの硬直化(ossification) が進んだ
  • TCP の改善の試み(例: Multipath TCP など)も、既存の TCP に偽装しなければ正常に動作できない状況にある

QUICの特徴と技術的な利点

  • QUICUDP 上で動作し、接続時に別途 3-way ハンドシェイクを必要とせず高速に接続を確立できる
  • パケット損失がストリーム全体に影響しないよう、マルチストリーム 転送の設計が取り入れられている
  • QUIC 関連の転送データ は常にエンドツーエンドで 暗号化(TLS ベース) され、中間機器は内部メッセージにアクセスできない
  • UDP パケットが通過できるネットワーク環境であれば、QUIC も正常に動作可能

Linuxカーネル内へのQUIC統合パッチ概要

  • 提出されたパッチでは IPPROTO_QUIC という新しいプロトコルタイプが導入され、既存の socket() システムコールを利用できる
  • TCP と同様に bind()connect()listen()accept() などのコールを使用できるが、その後の処理方式には違いがある
  • TLS セッション管理および認証・暗号化処理 はユーザー空間で行われ、接続後に各端点で TLS ハンドシェイク が完了してはじめてデータ送受信が可能になる
  • 初回接続後は TLS ネゴシエーション結果をキャッシュ し、2 つのシステム間で再接続する際の速度を大きく高められる

性能面の課題と見通し

  • 提出された カーネル内 QUIC 実装 は、現時点では性能面で既存の カーネル TLS および TCP に劣る
    • インカーネル TLS と比べてスループットは 3 分の 1 以下で、暗号化を無効化した場合でも TCP と比べて最大 4 倍低いスループットとなった
  • 原因として セグメンテーションオフロード未対応、送信経路での追加データコピー、ヘッダー暗号化処理などが指摘されている
  • 今後 ハードウェアオフロード 対応が追加され、インカーネル実装が最適化されれば性能向上が期待される

採用状況と今後の展望

  • Samba サーバー/クライアント、カーネル SMB および NFS ファイルシステム、curl など、さまざまな プロジェクトでインカーネル QUIC 対応の議論 が活発に進んでいる
  • パッチは約 9,000 行規模で、現時点では低水準のサポートコードのみが含まれている。完全な実装は追加パッチで予告されている状態
  • コードレビューとマージの議論は始まったばかりの段階であり、実運用までにはさらに時間を要する見込み
    • 最近 Homa プロトコルのカーネルマージに 9 か月で 11 回の提出が必要だった前例を見ると、QUIC についてもメインライン入りは 2026 年以降になると予想される

1件のコメント

 
GN⁺ 2025-08-02
Hacker Newsのコメント
  • 最近、NGINX の設定で ssl_preread_server_name を追加し、特定ドメインへのリクエストを別の NGINX インスタンスに proxy_pass するようにした
    最初のインスタンスは単に生の TLS ストリームを転送し(proxy_protocol を含む)、2 番目のインスタンスが実際の TLS 終端を担当する
    この方法はフェイルオーバー実装時に効果的
    サーバーの基本経路がダウンしたら、DNS A レコードをフェイルオーバー用マシンの NGINX に更新し、そのインスタンスが特定ドメインへのリクエストを別経路で元のバックエンドへルーティングする
    TLS 設定全体を複製する必要がないので便利
    ただし、この方法は HTTP/3 には適用できない
    HTTP/3 は QUIC ベースで、UDP 上で動作し、ハンドシェイク時に SNI を暗号化するため、ssl_preread_server_name ではドメインベースのルーティングができない
    HTTP/3 で SNI ベースのルーティングをサポートする代替手段があるのか、それともこの機能が必要なら引き続き HTTP/1.1 や HTTP/2 over TLS を使うべきなのか気になっている
    • QUIC をサポートするクライアントの大半は HTTPS DNS レコードもサポートしているので、低優先度レコードをフェイルオーバー用として追加し、クライアントに任せる方法がある
      実際にはクライアント実装ごとに挙動が異なるが(Chromium の HTTPS レコード対応状況については イシューへのリンク を参照)、QUIC 接続に失敗するとクライアントは透過的に HTTP/1.1/2 にフォールバックし、Alt-Svcヘッダーの説明 ヘッダーも尊重する
      計画的なフェイルオーバーであれば、Alt-Svc ヘッダーを送らずに待ち、代替インスタンスへタイムアウトするのを待つこともできる
      QUIC ルーティングが本当に必要なら、幸い SNI 情報は常に最初のパケットにあるので、パケット検査によるルーティングが可能
      Cloudflare の udpgrm が参考になり、これは ECH (Encrypted Client Hello) がない場合に使える
      ECH がある場合、ルーターが復号鍵を持っていないとルーティング判断ができず、プロトコル上はカスケードフェイルオーバーも設計可能
      具体的なコード実装は udpgrm の例 で確認できる
    • TLS をエッジ(たとえば NGINX)で直接終端することは、今日の Let's Encrypt 環境ではそれほど大きなリスクポイントではない
      攻撃者がそのサーバーにアクセスできるなら SSL 証明書を新規発行するのも容易なので、わざわざ複雑なフェイルオーバーシステムを考えるより、直接 TLS を終端したほうが理にかなっている
      個人的には QUIC の性能や信頼性の利点を自分で再現できたことがない
      何年も繰り返しテストしているが、性能などの理由でたいてい無効化している
      DNS ベースのフェイルオーバーも実際に反映されるまで数分かかり、ブラウザーのような単純なクライアントではフェイルオーバーがうまく機能しない
      そのため、onerror ハンドラーを使って 2 番目の経路を読み込む方法を使っている
      たとえば広告トラッキング用途でこの種のコードを使い、fetch API も同じ方法でラップして提供している
      この方法は他のどんな試みよりもはるかに効率的
    • フェイルオーバー環境なら QUIC のフェイルオーバーは気にしない
      ブラウザーは QUIC 接続に失敗しても(DNS で広告されていても)HTTP/1 あるいは HTTP/2 over TLS に自動でフォールバックするので、既存のフェイルオーバー方式をそのまま使える
    • この問題は「バグではない」類のもの
      HTTP/3 の設計上の特徴は、TLS 層までエンドポイント情報を露出させないことにある
      個人的にはその点は利点だと思う
      HAProxy は生の TLS プロキシは可能だが、ホスト名ベースのルーティングはできない
      Cloudflare tunnel には TLS 終端なしでホスト名ベースのルーティングができる特別な機能があるが、これを使うには DNS も Cloudflare に向ける必要がある
      関連する xkcd の漫画 の表現が参考になる
    • 興味深い質問だ
      TCP+TLS 環境で Encrypted Client Hello が使われる場合にも同じ制約があるのか気になる
      回答はほぼ同じになると思う
  • QUIC の欠点に関する 関連投稿 を以前から覚えている
    今回の議論は、そうした問題を少しずつ解決していく方向に見える
    今後はネットワークカードのハードウェアサポートの可能性もある
    • QUIC はマシン間トラフィックにはあまり向いていない
      しかし最近のインターネットトラフィックの大半はモバイルとサーバーの間を流れているので、その区間では QUIC と HTTP/3 が真価を発揮する
      それ以外の用途では TCP を使い続ければよい
  • マルチストリーム向けソケット API がどうなるのか気になる
    従来どおり複数コネクションに見えるが内部ではキャッシュされるような形になりそうだ
    自分としては明示的にコネクションオブジェクトを受け取り、そこから別のストリームを開く形が望ましいが、とりあえず現在の方式でも受け入れられる
    関連する議論 を見ると、これが拡張機能でないならサーバー側でもコネクション確立後に新しいストリームを作れる
    クライアント側では実体はストリームでも「コネクション」のように分離抽象化するのは難しく、根本的にまったく新しい API 抽象化が必要に見える
    おそらく新しいストリームごとに recvmsg でファイルディスクリプターを受け取る構造になるのではないか
    • マルチストリーム機能は SCTP ソケット API がサポートしているので、SCTP インターフェースが参考になる
  • カーネル実装かどうかに関係なく、OpenSSH に QUIC サポートがあるとよいと思う
    Mosh のようにネットワーク障害に強く、それでいて OpenSSH のすべての機能(SFTP、SOCKS、ポートフォワーディング、ステート管理、ローミングなど)もそのまま使いたい
    OpenSSH がカーネルサポートを活用できるのか気になる
    Mosh を見る
    • SSH は暗号化と多重化のレイヤーを QUIC で完全に置き換える必要があるため、大規模な作業になる
      むしろ QUIC ベースの別個のログインプロトコルを新たに作るほうがよさそうだ
      いくつかのアプローチがプロトタイプ段階で進行中
    • OpenSSH は OpenBSD プロジェクトなので、Linux API にはあまり関心を持たない可能性がある
  • TCP のボトルネックはハンドシェイクだと言われるが、コネクション再利用や多重化で解決できると理解している
    しかし現在の QUIC カーネル実装は Linux と比べて 3〜4 倍遅く、その性能差も近いうちに縮まると言われている
    速度が QUIC の利点なのに実際には遅いなら、QUIC を使う理由は何なのか気になる
    PR の作成者の話では、性能低下の一部原因はプロトコル設計にあるというが、TCP 側で別途改善すべき問題があるのかとも質問している
    • 記事でも QUIC が遅い理由は多く触れられている
      ほとんどは「まだ最適化していない」に要約できる
      たとえばセグメントオフロード非対応、送信経路での追加データコピー、ヘッダー暗号化によるオーバーヘッドなどがあり、どれも解決できる可能性が高い
      ここでのベンチマークは非常に理想的な環境で行われていた
      現実のモバイル環境ではネットワーク変動が大きいため、TCP の構造的限界がより目立つ
      実際、HTTP/2 のように TCP 上でもすでに QUIC に似た機能を実装している場合が多い
      結局 QUIC は OSI 第 5 層以上で動作する総合的なネットワーキングスタックであり、TCP は第 3 層レベルのエンジンなので、構造的に比較しにくい
      何より QUIC の利点は、より素早いコネクション確立・再確立にあり、IP 変更時でもセッション継続性を保証できること
      多重化・ノンブロッキングストリーム構造は上位プロトコル設計を飛躍的に単純化する
      こうした構造がカーネルに入れば、性能最適化の余地も非常に大きい
      今後は TCP の限界の上に多層ソリューションを作るより、日常的に QUIC のような進歩した基盤技術をもっと使うべきだ
    • TCP のボトルネックはハンドシェイクだけではない
      パケット損失が発生すると、その後の送信分全体が復旧するまで遅延してしまうため(HOL blocking)、構造的な限界が大きい
    • 「存在しないコネクション」を再利用することはできないので、多くの議論はレイテンシ短縮に焦点を当てている
      単なる速度の問題ではなく、遅延改善の話だ
    • QUIC の利点は、サーバーが提供する connection ID ベースの追跡機能にある
      技術説明ドキュメント を参照
  • ネットワーキングスタックをユーザースペースに移して性能を上げるという話と、今回のようにカーネル側へ移そうという議論があり、混乱する
    • ほとんどの QUIC スタックは、カーネル内の UDP の上で動作している
      カーネルとユーザースペース間のコンテキストスイッチが主なボトルネック
      ユーザースペースネットワーキング(たとえば NIC への直接アクセス)はカーネル進入をなくしてくれる
      逆にカーネル空間で機能を提供すること(たとえば sendfile、in-kernel TLS、NIC オフロード、ディスクから NIC への直接 DMA)も、全体のコンテキストスイッチとデータコピーを減らす
      現在の QUIC スタックは両方の利点を活かせていない
      パケット入出力が syscall ベースで、データコピーも避けられない
      io_uring などでバッチ I/O を行えばスイッチ回数は減るが、コピー自体は減らせない
    • そのとおりだ
      カーネルバイパス + DMA、あるいは sendfile/ktls のようにユーザースペースを介さない構造という 2 つの方法がある
      QUIC のカーネル実装にはその両方の利点がまだない
    • 結局 NIC バッファにデータを渡さなければならない点は同じ構造だ
      DMA で NIC を直接書けるか、カーネル syscall で渡すかでは性能差が大きいため、ユーザースペースネットワーキングはこの種の特権切り替えや DMA 構造がある場合にのみ説得力がある
    • ユーザースペースがネットワークデータへ直接アクセスする方式(おそらく syscall なし)が一般的なユーザー向けソフトウェアに適用されても、あまり意味はない
      活用できるのは大規模企業(MOFAANG など)くらいだ
      理論上は io_uring がこうした恩恵を一般化することが期待されているが、まだ実運用の段階ではない
    • ハードウェアアクセス時のコンテキスト切り替えは遅すぎる
      だから TCP/IP は主要 OS でカーネルに残っている
  • 「なぜカーネルにどんどん機能を追加するのか?」
    カーネルの役割はメモリ、ハードウェア、タスク管理だと思っていたが、IP 上のプロトコルはユーザーランドで処理すべきではないのかという疑問
    • ネットワーキング、ルーティング、VPN などをすべてカーネル空間で処理すると、一部のユースケースでは性能向上が見込める
      逆に、こうしたスタックをユーザースペースへ分離しても、一部のケースでは性能向上効果がある
    • カーネルに入ることで、ハードニング(セキュリティ強化)、LTS サポート、そしてカーネルおよびネットワークレベルでの最適化という利点がある
    • 主な理由は DMA 転送と NIC オフロードの最適化だ
    • IP 上のプロトコル全体をユーザースペースに置くと、マルチプロセス利用ができなくなる
      TCP/UDP はカーネルでポートベースのソケットルーティングを仲介しているため、複数のプログラムが同時に TCP/UDP を利用できる
      QUIC は UDP 上で動作するので、したがってこの議論には依然として説得力がある
      IP 直上のプロトコルだけはユーザースペースで動かせない、という点を強調している
  • QUIC は多くの人にとって画期的な変化をもたらすと思う
    今後インターネットは少し速くなるのではと期待している
    5G のような通信環境では違いを感じにくいかもしれないが、価値のある進歩だ
    別個のリンクハンドシェイク構造があるのは興味深い
    もともと QUIC は TLS を独自に内蔵しているのだと思っていたので、それとは違っていた
  • Web 全体が遅くなった主な理由は、Web サイトが重くなりすぎたことだ
    それでもゲームのレイテンシは今回の技術でさらに下げられると思う
    • ジェボンズのパラドックス はここにも当てはまる
      コンピューティングリソースとネットワーク効率が上がれば、需要そのものも増える
      ゲームや科学計算では「より良い結果」のためなら問題ないが
      Web では広告、トラッキング、JavaScript の増加によって逆効果が多い
  • 記事では QUIC は TCP のように bind()、connect()、listen()、accept() などでコネクションを確立するが、その後は sendmsg() と recvmsg() syscall を使う構造へ変わるとある
    なぜこのアプローチを採用したのか、なぜ QUIC に合った専用システムコールを作らなかったのかという説明も欲しい