先月、二人の人が私に正反対の結果を求めました。一人はIPv6をVPNで覆いたいと考え、もう一人はオフィスのプリンターを使い続けたいと考えていました。単一の ::/0 ルートでは、その両方を自力で満たすことはできません。そのルートがトンネル内に属するかどうかは三つの条件で決まり、最初の条件が大半の商用出口でそれを除外します。出口がIPv6を運ばないなら、デフォルトルートをそこへ向けると、送る先のないトンネルの中にトラフィックを隠すことになります。

三つの条件が答えを決める

デフォルトルートは一行、::/0 であり、より具体的なルートを持たないあらゆるIPv6宛先に一致します(RFC 4291)。インストールは簡単です。それをすべきかどうかの判断は三つの条件に帰着します。

条件一:接続する出口に動作するIPv6があり、それを転送する用意があること。条件二:クライアントがトンネルのエンドポイントへの経路を保てること。エンドポイントは通常、これからルートしようとする範囲の内側にあります。条件三:トンネルの外に置くべきローカルIPv6宛先を挙げられること。

各条件はそれぞれの仕方で失敗するので、当て推量には一晩かかります。条件一が失敗すると、沈黙と、正しく見えるルートテーブルが残ります。条件二が失敗すると、開始から数秒でハンドシェイクが死にます。条件三が失敗すると、動くトンネルと、プリンターの前の不機嫌な同僚が残ります。

What you seeDecision
Exit answers IPv6 probes, no local IPv6 devices to protectRoute ::/0 into the tunnel
Exit answers IPv6 probes, printer or NAS reachable by a global addressRoute ::/0, then give those prefixes a route of their own
Exit is IPv4-only inside the tunnelDo not route ::/0; block IPv6 on the client
Endpoint is reached over IPv6 and sits inside ::/0Install a host route for the endpoint first

出口は本当にIPv6を運んでいるか

IPv6アドレスを持つトンネルインターフェースは、向こう側について何も証明しません。多くの商用プロファイルは内側がIPv4のみです。サーバーはv4アドレスを渡し、それ以外はすべて捨てます。その上に AllowedIPs からのデフォルトルートを載せると、カーネルは喜んでIPv6を、相手が決して見ないインターフェースへ送り込みます。

トンネルが上がっている間にクライアントから試してください。ping -6 -c 3 2001:4860:4860::8888 と ip -6 route get 2001:4860:4860::8888 は別の問いに答えます。応答が返るかどうか、そしてカーネルがどのデバイスを選んだかです。後者がトンネルを指名し、前者が何も返さないなら、問題はルートではなく出口です。

出口を自分で運用している場合、クライアント側のテストは通っても経路が行き止まりになることがあります。ゲートウェイで net.ipv6.conf.all.forwarding=1 が有効で、そのファイアウォールがトンネルから転送されるパケットを受け入れることを確認してください。転送を拒む出口へ正しくルートされたパケットは、クライアント側からはまさにルーティングのバグに見えます。

クライアントはエンドポイントへの経路を保てるか

これが、IPv6専用のWireGuardエンドポイントを持つノートPCで私を捕まえた事例です。エンドポイントのアドレスは通常のグローバルアドレスなので、::/0 の内側に入ります。プレフィックス全体をインターフェースにルートすると、ハンドシェイクのパケットは、まだ構築中のトンネルの中へ自分自身を追い込んでしまいます。トンネルは決して上がらず、クライアントの出力には理由が何も出ません。

wg-quick がLinuxでこれを扱います。AllowedIPs からルートを導き、独自のテーブルにインストールし、エンドポイントへのトラフィックがまずメインテーブルで解決されるようポリシールールを追加します。それが suppress_prefixlength 0 ルールで、ip -6 rule にあります。だからこそ、wg-quick up は手書きの ip -6 route add default dev wg0 が切断する場面で成功します。

トンネルを上げる前と後に、ip -6 route get <endpoint-v6> で両方の状態を確認してください。答えがトンネルデバイスに変わったなら、トンネルが必要とする経路を削除したことになります。ルートを自分で書くときは、元のゲートウェイ経由でエンドポイントへのホストルートを追加するか、IPv4でエンドポイントに接続してIPv6はペイロードに任せてください。

何をトンネルの外に残すべきか

より具体的なルートはデフォルトに勝つので、リンク上のプレフィックスや独自ルートを持つULA範囲は追加の手間なく動き続けます。これが、多くのフルトンネルIPv4構成が生き残る静かな理由です。LANプレフィックスはデフォルトより具体的で、決してトンネルに入りません。

ローカル機器がグローバルアドレスで到達できると破綻します。同じルーターからパブリックプレフィックスを与えられたプリンター、NAS、テレビではよくあることです。それらのアドレスは ::/0 に一致し、他には何も一致しないので、他のすべてと一緒にトンネルへ移動して消えます。

プロトコルではなく宛先ごとに決めてください。ローカルホストが二、三台重要なら、そのプレフィックスに物理インターフェースから出るルートを与えるか、ネゴシエートされた集合から外してください。リストが増え続けるなら、正直な答えは書面の例外リストを伴うIPv6のスプリットトンネリングであり、継ぎ足し続けるデフォルトではありません。

答えが「いいえ」なら、ブロックする

IPv6をルートしないままにするのは、放っておくのとは違います。動作するIPv6デフォルトを持つホストは、トンネルがIPv4を運んでいる間もそれを使い続けるので、トラフィックはVPNの横から出て、IPv6を優先するあらゆるサービスが本当のアドレスを見ます。それはリークであり、スプリットトンネルではありません。

どちらかを選び、選んだ方を書き留めてください。デフォルトをルートして上のトレードオフを受け入れるか、トンネルの間だけIPv6を無効にしてください。AllowedIPs からプレフィックスを外すだけでは不十分です。OSは独自のデフォルトを保持するからです。Linuxでは乱暴な版が sysctl -w net.ipv6.conf.all.disable_ipv6=1 です。より細かい版はファイアウォールルールでIPv6をトンネルの外へ落とします。スマートフォンのクライアントには、同じ働きをする明示的なIPv6ブロックの切り替えがあります。

リークのように検証する

トンネルを上げ、切り替えを信じるのではなく状態を読んでください。ip -6 route show default は一つのデフォルトを示すはずで、LANの外の宛先に対して ip -6 route get はトンネルデバイスを指名するはずです。切断後にもう一度両方を確認してください。トンネルより長生きするデフォルトが、探していたバグです。

次に逆方向を試します。ローカルネットワークのホストから、クライアントのローカルIPv6アドレスに到達してください。それが動かなくなったなら、条件三は決して満たされていませんでした。そのときルートテーブルが何を主張していてもです。

結果は次の人が見つけられる場所に書いてください。どのプレフィックスがローカルに残るのか、なぜか、切断後にクライアントがどうするのか。ランブックの二行が、次のエンジニアが真夜中にエンドポイントループを再発見するのを防ぎます。

関連記事