背景

VPNを使ってトラフィックを中継する際、すべての通信をVPN経由にしたい──そんなときに現れるのが謎のルート 0.0.0.0/1 と 128.0.0.0/1。

一見すると「なんだこの中途半端なルートは?」と思われがちですが、これはOpenVPNが採用する非常に巧妙なテクニックです。本記事では、その目的、動作、実際のルーティングテーブルを紐解きながら、この仕組みの裏側を解説します。

ルーティングとは?ip route の基本

Linuxのルーティングテーブルは ip route で確認できます。このテーブルは、「どのIPアドレス宛の通信を、どのネットワークインターフェースから、どのゲートウェイ経由で送るか」という配送ルール一覧です。

$ ip route
192.168.1.0/24 dev eth0 proto kernel scope link src 192.168.1.100
default via 192.168.1.1 dev eth0

この場合、

  1. 192.168.1.0/24 に属する通信は eth0 から直接送られる
  2. それ以外(つまりインターネットなど)は 192.168.1.1(ゲートウェイ)を経由して送られる

パケットを受信すると、カーネルはルートテーブルの上から順に一致するエントリを探し、一番一致度が高い(= プレフィックスが長い)ルートに従って出力先を決定します。

つまり、 どの通信もまずは「最も具体的な(最長一致の)ルート」を優先する これがVPNのルーティング操作にも大きく関わってきます。

defaultルートとは?

default ルート(または 0.0.0.0/0)とは、どのルーティングエントリにも一致しない宛先のパケットをどこに送るかを決めるルールです。いわば「最後の砦」。

# 例: よくある default ルート
default via 192.168.1.1 dev eth0

この例では、「どのルートにもマッチしなければ 192.168.1.1 に送れ」となっています。

通常、インターネットへの出口(ISPや上位ルータ)に向かうために使われます。

A (100.64.1.61)
   ↓
B (100.64.1.27) — VPN Client
   ↓
C (162.x.x.x) — VPN Server
   ↓
D (192.168.40.10)
   ↓
E (192.168.40.130)

AからD・Eに通信を通したい。しかし同時に、Aのインターネット通信もVPN(サーバC)経由で出したい。

そのためには、「すべての通信(=defaultルート)」をVPNへ流す必要があります。

普通にやると危険:「defaultルートの上書き」

default ルートとは 0.0.0.0/0、つまり「どこにも該当しなかった通信はここへ行け」というルールです。

VPNクライアントで default を書き換えることもできますが、

  1. 既存のdefaultルートが消えて戻せなくなる
  2. VPN切断時に通信不能になるリスクがある

という危険があります。

そこで登場するのが、OpenVPNが push “redirect-gateway def1” で送ってくる2つのルートです:

0.0.0.0/1        → IP範囲: 0.0.0.0 〜 127.255.255.255
128.0.0.0/1      → IP範囲: 128.0.0.0 〜 255.255.255.255

この2つを合わせると、全IPv4アドレス空間(0.0.0.0/0) をカバーします。 そして重要なのは、プレフィックス長が /1 なので、/0 より優先度が高いこと。

つまり: 既存の default ルートを潰さず、全通信をVPNに向けさせることができる これが redirect-gateway def1 の真の力です。

実際のrouting table

hoge@alpha:~ $ ip r
default via 100.64.1.1 dev eth0 proto dhcp src 100.64.1.27 metric 202 
0.0.0.0/1 via 10.8.0.5 dev tun0 
128.0.0.0/1 via 10.8.0.5 dev tun0 
10.8.0.0/24 via 10.8.0.5 dev tun0 
10.8.0.5 dev tun0 proto kernel scope link src 10.8.0.6 
100.64.0.0/22 dev eth0 proto dhcp scope link src 100.64.1.27 metric 202 
162.x.x.x via 100.64.1.1 dev eth0 

このように、VPN経由のルートが追加されると同時に、VPNサーバ自身(162.x.x.x)への通信は物理NICから送るように例外ルートが明示的に追加されています。

これがなければ、VPNサーバへの通信自体がVPNトンネルを通ってしまい、VPN接続そのものがループして破綻する危険性があるためです。この特別なルートもOpenVPNクライアントによって自動で挿入されるもので、VPN全トラフィック中継時の重要な裏方処理の1つです。 これは、vpnの設定、server.conf内の以下で定義されています。

# client側から通信させたい、server側のLanセグメントを指定
push "redirect-gateway def1 bypass-dhcp"
push "route 10.8.0.0 255.255.255.0"
push "dhcp-option DNS 8.8.8.8"

🔬 実際の通信の流れ

たとえば、VPNクライアントがどこかインターネット上のホストにTCP通信をしようとした場合、

0.0.0.0/1 via 10.8.0.5 dev tun0
128.0.0.0/1 via 10.8.0.5 dev tun0

のルートにマッチするため、そのパケットは仮想インターフェース tun0(VPNトンネル)に送られます。

tun0 では OpenVPN ソフトウェアが動いており、送られてきた TCP パケットを暗号化し、OpenVPN サーバ(例: 162.x.x.x)宛ての UDP パケットのペイロードとしてカプセル化します。

その結果、実際に物理NICから送信されるのは、

送信元: 100.64.1.27 → 宛先: 162.x.x.x の UDPパケット(暗号化されたVPNトラフィック)

このようにして、ユーザーの通信はあたかも「VPNサーバから発信されたかのように」外部に出ていくのです。

おまけ

通常、Linuxのパケット転送は ip route コマンドで確認できる カーネルのルーティングテーブルに従って行われます。

しかし、OpenVPNを使っていると「ルーティングテーブルにそのネットワークへの経路が載っていないのに通信できる」という現象に遭遇します。

本記事では、この一見矛盾した挙動の正体──OpenVPNが行うユーザー空間でのセッションベースルーティングについて解説します。

例:構成のおさらい

A (100.64.1.61)
   ↓
B (100.64.1.27, VPN Client)
   ↓
Router1
   ↓
C (OpenVPN Server)
   ↓
Router2
   ↓
D (192.168.1.4, VPN Client)

VPNサーバ(C)の ip route を見ると、たとえばBのIP(100.64.1.27)への明示的なルートがありません。それでもAからD(192.168.1.4)にパケットを送ると、ちゃんとDに届きます。

これ、なぜ可能なのでしょうか?

セッションベースルーティングとは?

OpenVPNは、各クライアントとの通信をセッション単位でユーザー空間で管理しています。

具体的には: クライアントが接続したとき、その仮想IP(例:10.8.0.6)と物理IP(例:100.64.1.27)を紐付けて保持 さらにクライアントが iroute や ccd 設定でネットワークをアドバタイズすれば、 「192.168.1.0/24はこのクライアントが担当」と内部で把握 つまり: OpenVPNは、各クライアントがどのネットワークを背負っていて、どの物理アドレス経由で到達するかをセッションで記憶している

この情報はLinuxの ip route には出ませんが、OpenVPNの中には存在します。

パケットがどう流れるか

たとえばA→D(192.168.1.4)に向けてパケットを送ると:

A → B → C(OpenVPNサーバ)と届く

Cではその宛先(192.168.1.4)を「Dが管理しているネットワーク」と認識

CはDとのセッションを見て、UDPでパケットをカプセル化し、router2→Dへ送信

Dはトンネルを復号して、192.168.1.4で受信する

この一連のやりとりは、ルーティングテーブルに依存せず、OpenVPNのユーザープロセスが制御しているのです。

なぜルートが要らないのか?

OpenVPNでは次のような仕組みが働いています:

tun0 は Point-to-Point(仮想的な対向接続)

通信先が 10.8.0.6 なら、どのセッションが保持しているかをOpenVPNが知っている

カーネルが知らない宛先でも、OpenVPNがソケットで相手を知っているので送れる

このため:ip route にそのIPへの経路がなくても、VPNセッションが生きていれば到達可能

conntrackとの連携もカギ

Linuxのiptablesは、接続追跡(conntrack) 機能によって戻り通信の経路を記憶します。 OpenVPNから出ていったパケットに対し、戻ってきたパケットも「この接続の戻りだ」と認識され、iptablesで –ctstate ESTABLISHED,RELATED によって許可されます。 これもまた、ip route がなくても通信できる補助要因になっています。