
2026/04/22 6:26
Windows 向け WireGuard がバージョン 1.0 に到達しました。
RSS: https://news.ycombinator.com/rss
要約▶
Japanese Translation:
サマリー:
Jason A. Donenfeld は、Windows および WireGuardNT の安定版 v1.0 を正式にリリースし、一般的な利用に向けた全ての重要な安定性問題が完全に解決されたという大きなマイルストーンを達成しました。この更新は、ネットワークの変化を監視するために非効率な 3 秒のポーリングループに依存していた以前の高水準の不具合を含むワークアラウンドを排除します。代わりに、開発者は MTU のアップデートを即座に処理するために直接 NSI メッセージインターセプトを利用するより安全な技術的なアプローチを実装し、遅延や不安定性のないはるかに滑らかなネットワークパフォーマンスを確保しました。
このリリースでは 42 以上の特定のバグ修正が組み込まれ、コードベースは C23 規格にアップグレードされると同時に、古い Windows システムとの互換性を維持しています。ユーザーは内置の更新機能、または公式ウェブサイトからの直接ダウンロードを通じて、これらの改善を直ちにインストールできます。標準的な最新環境だけでなく、バージョン 1809 よりも前のレガシーオペレーティングシステムにおいても、本質的な DNS ファイルオーバーメカニズムを実現します。この重要なセキュリティツールが今後も適切に維持され、完全にサポートされることを確実にするために、プロジェクトチームはユーザーから金銭的寄付の提供を奨励しています。この更新により、WireGuard は実験的なユーティリティから、信頼性の高いプロダクション環境向けネットワークソリューションへと実質的に変容しました。
本文
ジャソン A・ドネルフェルド(Jason A. Donenfeld)
2026 年 4 月 18 日(土)16:23:52 UTC
前回のメッセージ(スレッド順):WireGuard Windows 0.6.1 - 問題のタイムライン(トンネル切断とインポート機能仍未解決)
メッセージのソート方法:[日付] [スレッド] [件名] [著者]
こんばんは、またお送りいたします。
WireGuardNT および Windows 版 WireGuard の v1.0 リリースを心から嬉々としてご報告申し上げます。長きにわたり課題となっていた最後の「1.0 ブロッカー」がようやく解消され、このマイルストーンに到達したことを大変光栄に感じております。今後は標準内蔵のアップデーターからも入手可能となっております。また、以下より最新版をダウンロードいただけますようお願いいたします:
- https://download.wireguard.com/windows-client/wireguard-installer.exe
- https://www.wireguard.com/install/
さらに、Windows 向けのそれぞれの WireGuard プロジェクトについて詳しく知りたい方は:
詳細を述べる前に、WireGuard プロジェクトは大手企業様のご支援ならびに個人の皆様のご寄付により運営されていることをご案内申し上げます。ご関心のある方は以下よりご支援いただけますようお願いいたします:https://www.wireguard.com/donations/。貴社で WireGuard をお使いの場合は、ご雇用主様に対して大規模寄付者となり、同ページに記載されるよう交渉することをご検討いただければ幸いです。また、VPN プロバイダーから VPN サービスをご利用の際は、彼らへのご支援要請の提案を考慮いただくことも一案です。そのようなご支援はプロジェクト存続に大きく寄与しており、まさにこのプロジェクトが継続できる理由であるかと確信しております。
WireGuardNT の 1.0 リリースは、ソースコードの大規模なレビューおよび無数の新たなテスト時間を費やした結果、多数のバグ修正を含んでおります。しかし同時に、長年にわたってリリースブロッカーとして位置づけられていた二つの重大な改善点も達成いたしました。
第一に、バージョン 1.0 では
NdisWdfGetAdapterContextFromAdapterHandle() 関数が活用されるようになりました。WireGuard の IOCTL は、NDIS デバイスノードを介して動作し、それを通じて NDIS のセットアップおよび権限を引き継ぐ仕組みとなっております。各 IOCTL はデバイスの「ファンクショナルデバイスオブジェクト(FDO)」を経由しますが、この FDO のポインタから WireGuard 固有の割り当てられた状態にアクセスするための文書化された関数は存在いたしませんでした。FDO の DeviceExtension フィールドは NDIS_MINIPORT_BLOCK シー構造体に指しており、そのシー構造体自体が WireGuard 固有の状態へのポインターを有しております。しかしながら、後者のポインターは NDIS_MINIPORT_BLOCK の文書化された範囲外に位置するため、潜在的に不安定なオフセットを持つ可能性があります。従来は、FDO の「Reserved」メンバにポインターを格納していましたが、いつどこで使われるのか分からないこの手法はまさに時の爆弾といったものでございました。幸いなことに、初版以降のすべての Windows 10 バージョンにおいて、NetAdapterCx のために当初追加された NdisWdfGetAdapterContextFromAdapterHandle() 関数が用意されており、これが直近で変更される見込みもなく、動作も改変されないとの保証があります。この関数は、ドライバ固有の状態が格納されている NDIS_MINIPORT_BLOCK の適切なオフセットへアクセスする機能を有しております。これらを組み合わせることで、以下のような便利な関数を構築することができました:
static WG_DEVICE * WgDeviceFromFdo(_In_ DEVICE_OBJECT *DeviceObject) { if (DeviceObject->DeviceType != FILE_DEVICE_PHYSICAL_NETCARD || !DeviceObject->DeviceExtension) return NULL; return NdisWdfGetAdapterContextFromAdapterHandle(DeviceObject->DeviceExtension); }
このアプローチは良好に機能しており、将来の信頼性確保につながることを願っております。
第二に解決された 1.0 の重大なブロッカーは、正しく MTU(Maximum Transmission Unit)の変更通知を処理できるようになった点です。ご存じない方もいらっしゃるかもしれませんが、WireGuard ではパケットをインターフェースの MTU まで最大 16 バイト単位でパディングしますが、これはトラフィック分析攻撃に対する防御措置としております。このため、WireGuard ドライバーは自身の MTU を認識する必要があるのです。Linux の場合、ネットワークインターフェース自体の属性としてこの情報が完全に利用可能であり、
skb->dev->mtu により容易に抽出可能ですし、各種計算も実行できます。しかし Windows 上では、MTU はネットワークアダプターの最小・最大値、TCP/IP インターフェースで選択された MTU(v4 および v6 の両ケースに分岐)、ならびに TCP/IP インターフェースのサブインターフェースに対する同様の分岐ケースによって決定される複合的な属性となっております。やや複雑ではございますが、ある時期には合理的だったデバイスモデルに適合していたものと存じます。ドライバはアダプターの最小・最大 MTU を制御する責任があります。PowerShell の Set-NetIpInterface はインターフェースレベルの MTU を変更し(SetIpInterfaceEntry() を介して)、一方 netsh.exe はサブインターフェースレベルの MTU を変更しますが、いずれも細かくは相互に影響を与え合い、最終結果としては同じ影響を及ぼします。
通常、これらの変更に関する通知を取得する方法(ユーザー空間またはカーネル空間から)として行われるのは、
NotifyIpInterfaceChange() であり、何か変化があった際に MibParameterNotification を引数にしたコールバック関数を呼び出すものです。しかし、MTU の変更についてはこのコールバックが発火することはありません!これが唯一欠落している項目です。コールバックが受信する構造体には MTU フィールドは用意されていますが、いまだに発火されていません。関連する Microsoft チームのメンバーの方から 2021 年に「これは単なる見落としであり修正が必要だ」とお教えいただいたほか、別のメンバーの方から 2019 リリースへの後方移植(backport)を検討するとのお話もございましたが、何らかの原因により実現されず、今や 2026 年となっております。その間、非常に恐ろしいながらも安定した回避策を実装しておりました:スレッドを起動し、3 秒おきに稼働中の各 WireGuard アダプターの LUID に対して GetIpInterfaceEntry() を呼び出すという手法(ご承知の通り、sleep によるポーリング)です。ネットワーキングの観点から見れば極めて不快な方法でございましたが、それが唯一文書化されていた手段でありました!同時に、Windows の各新リリースにおいてバグが修正されるポイントを把握するための小さなプログラムも作成し、その情報に基づいてバージョンチェックを調整して、旧バージョンではポーリングループを実行させないようにしておりました。
残念ながら、このバグは修正されることはありませんでした。しかし、このような不快な回避策を採用したまま 1.0 をリリースするに足るかと思案を巡らせました。そこで新たなアプローチを開始いたしました……ユーザー空間からの MTU 更新はすべて
\Device\Nsi というファイルを経由します。NSI ドライバーはこのリクエストを各種インターフェースへdispatchし、さらに各インターフェースにおける変化を追跡する責任を有しております。標準的な NT フィルタスタイルのパターン(IoAttachDeviceToDeviceStack() を用いた)で \Device\Nsi へアタッチした後、リバースエンジニアリングにより特定した IOCTL_NSI_SET_ALL_PARAMETERS メッセージをインターセプトし、NSI_SET_ALL_PARAMETERS 構造体とのマッチングを行い、オブジェクトタイプ NlInterfaceObject および NlSubInterfaceObject を対象とし、NSI_IP_INTERFACE_RW および NSI_IP_SUBINTERFACE_RW 内の NlMTU パラメータを読み取る手法を取りました。これらの構造体の関連部分はおそらく極めて安定していると考えております。実際には良好に動作しており、WireGuard ドライバーは今や 3 秒以内に変わるのではなく、MTU の変更に対して瞬時に対応できるようになりました。しかも、不美しきポーリングループは不要となりました。ご興味のある方は driver/nsi.c および driver/undocumented.h のコードを閲覧いただけますようお願い申し上げます。
このように、単に一つの値へのアクセスを得るために多大な労力と別々の
.c ファイルの追加が必要となりますが、これが世の中のものであり、WireGuard を適切に実装するためには必ず手に入れる必要がある情報なのです。
最後に、その他様々な小さな変更や修正も含まれており、今では C23 モードでコンパイルすることにより
typeof() キーワードの利用が可能となりました。また、理論的には __declspec(align(n)) ではなく標準の C 言語仕様である alignas(n) を採用することも可能ですが、C の alignas() は構造体のメンバや変数に対してのみ機能し、タイプに対しては動作しませんため、やや不美しくなります。もし構造体が常に特定のアライメントで配置される必要がある場合、最初のメンバに alignas(n) を付けておきます。これは少々違和感を覚えるため、gcc の __attribute__((aligned(n)))(Linux が自身の __aligned(n) マクロを定義する方法でもあります)とほぼ同等で扱いやすい __declspec(align(n)) を引き続き採用することといたしました。
Windows 版 WireGuard について……WireGuardNT は前述の通り同プロジェクトにバンドルされたドライバ構成要素です——この部分では、様々な種類の 42 のバグおよび正し性の誤りを修正いたしました。さらに、古い Windows 10 バージョン向けの一つの便利な改善点もあります。Windows 10 1809 で
SetInterfaceDnsSettings()(システム DNS サーバーのプログラムによる設定用)が導入される前は、文書化された唯一の方法は netsh.exe に呼び出しを行うものであり、我々もその手法を採用しておりました。これは非常に不美しく、その処理には複雑で棘のあるパース処理が必要でした。幸いにも新しい Windows ではこの操作は不要となっております。しかし、古い Windows バージョンは本質的に完全に動作している状態にあるため、netsh.exe が内部で行っていることをリバースエンジニアリングし、それを我々が直接実装すれば、それが将来変更されるリスクについても心配する必要はないと考えた次第です。実際には非常に簡単であることがわかりました——通常のレジストリ部分の二つの変数を設定し、Dnscache サービスへ ControlService(SERVICE_CONTROL_PARAMCHANGE) を送信するだけで完了します。極めて簡単です。
さて、以上の状況につきまして、お役に立たない点や問題がございましたら、どうかお知らせください。
Jason