ディストロの内側から内部サーバーへの ping はタイムアウトするのに、同じアドレスは Windows から応答します。VPN は接続済みで Windows のルートも正しいため、欠けているのは WSL の境界です。失敗したコマンドからホストのインターフェースまで経路をたどってください。修正は設定ファイルに固定の CIDR を書くことではなく、現在のアドレスを読んでディストロが使える場所に追加することです。

経路がどこで止まるかを確認する

両側で同じテストを実行します。Windows から内部ホストを名前解決して接続し、それをディストロで繰り返します。Windows での成功とディストロでのタイムアウトが揃えば、障害は両者の間にあり、VPN クライアントや内部サーバーではありません。どのルートも変更する前にこれを行い、二つの結果が同じ宛先を指すようにしてください。

ディストロの内側では、ip -4 addr が WSL の NAT 範囲のアドレスを持つ仮想アダプターを示し、ip route が WSL ゲートウェイを指すデフォルトルートを示します。社内プレフィックスのルートは存在しません。ホストのトンネルアダプターにはディストロ内に対応するものがありません。デフォルトゲートウェイは WSL の NAT へ向かい、社内ネットワークへは向かいません。

到達可能なリンクにローカルアドレスを先に追加せずに社内ルートを追加すると、タイムアウトまたはネットワーク到達不能エラーになります。ルートを使えるようにするのはアドレスのほうなので、先にアドレスを読み、ルートは二番目の手順として扱ってください。

ミラー型ネットワークかスクリプトによるルートを選ぶ

ミラー型ネットワークはホスト自身のインターフェースをディストロに渡すため、トンネルとそのルートがコピーなしで現れます。この機能は新しい Windows ビルドと .wslconfig の networkingMode=mirrored に依存し、有効にすると localhost 転送のような無関係な動作も変わります。ルートの微調整ではなくプラットフォームの変更として扱ってください。ビルドが対応していて副作用が許容できるなら、これが最短の道です。

より狭い方法はデフォルトの NAT を保ち、トンネルが上がるたびにトンネルのアドレスとルートをディストロへコピーします。より多くのビルドで動作し、localhost の動作を変えず、VPN が落ちているときは目に見えて失敗します。代償は接続のたびに実行する小さなスクリプトです。

どちらか一つを選び、書き留めてください。ミラー構成と手動ルートを混ぜると、同じプレフィックスへの経路が二本あり、どちらの所有者も明確でないディストロになります。文書化された一つの方法のほうが、中途半端に設定された二つよりデバッグしやすいです。

ホストから現在のトンネル情報を取得する

アダプターは名前ではなく MAC アドレスで照合してください。名前は VPN クライアントと接続順で変わります。アダプターを一度一覧し、トンネルアダプターの MAC を控えてから、そのアドレスとプレフィックスを問い合わせます。MAC は再接続をまたいで仮想アダプターに残りますが、名前とインターフェースインデックスは残りません:

powershell.exe 'Get-NetAdapter | Where-Object { $_.MacAddress -eq "3C-52-82-0A-1B-7C" } | Get-NetIPAddress -AddressFamily IPv4 | Select-Object IPAddress, PrefixLength'

ゲートウェイはインターフェース構成から得ます。先にインターフェースインデックスを取得し、次にネクストホップを読みます。インデックスは再起動やアダプターのリセットで変わるため、スクリプトは保存せずに読むべきです:

powershell.exe '(Get-NetAdapter | Where-Object { $_.MacAddress -eq "3C-52-82-0A-1B-7C" }).InterfaceIndex'
powershell.exe '(Get-NetIPConfiguration -InterfaceIndex 27).IPv4DefaultGateway.NextHop'

出力を変数に代入する前に tr -d '\r' を通してください。PowerShell は行をキャリッジリターンで終えるため、取り除かないと最後のフィールドに隠れた文字が残り、ルートコマンドが不可解なパースエラーで失敗します。

ディストロの内側でアドレスとルートを適用する

トンネルのアドレスをプレフィックス付きのアドレスとしてディストロのインターフェースに設定し、社内ブロックをホストのゲートウェイ経由でルーティングします:

sudo ip addr add 172.20.14.37/22 dev vpn
sudo ip route add 172.20.0.0/16 via 172.20.12.1 dev vpn

接続が循環すると NetworkManager は手動設定のアドレスを捨て、DHCP クライアントも代替を要求しません。方式を自動に設定して再接続のたびにアドレスを更新させ、そのうえで接続を再度立ち上げてください。手動方式は最初の再接続までは安定して見えます:

nmcli con show vpn
sudo nmcli con mod vpn ipv4.method auto
sudo nmcli con up vpn

両方の手順は VPN を開始するフックから実行し、対話シェルからは実行しないでください。アドレスを読み、追加し、ルートを追加し、値をログに残すスクリプトは、繰り返す手作業の修理を再現可能な手順に変えます。プレフィックスが変わったときに最初に読むべきはそのログです。

名前を固定し、経路を証明する

ネットワーク切り替えの後、カーネルはディストロのインターフェース名を eth1 と eth2 の間で変えることがあり、名前を前提にしたスクリプトはそのとき間違ったデバイスを編集します。MAC に紐づけた udev ルールはインターフェースに安定したラベルを与えます:

SUBSYSTEM=="net", ACTION=="add", ATTRS{address}=="3c:52:82:0a:1b:7c", NAME="vpn"

次にアドレス、ルート、実際の TCP 接続で確認します。ip -4 addr show dev vpn、ip route get 172.20.4.10、そして既知の内部ポートへの接続です。ルート検索は vpn デバイスと、追加した送信元アドレスを示すはずです。

三つすべてがディストロの内側で通り、それでも接続が失敗するなら、パケットは WSL を出て Windows 側で破棄されています。スクリプトを変える前にホストのファイアウォールと VPN クライアント自身のフィルタリングを確認してください。別のルート編集では、すでにホストに届いているパケットは直りません。

関連記事