以前の記事
https://blog.ingenboy.com/post/problem_site2site/
こちらの記事でVPNクライアントを介して、別々のNWセグメントに所属するサーバ間で通信をする方法を軽く説目しました。 この問題は、リターンパス問題だったのですが、今回、改めてリターンパス問題について書きたい。
まず、
1. IP層での基本ルール
- 送信元IP と 宛先IP はパケットに埋め込まれていて、転送中は変わらない(※NATしない限り)。
- 各ホストやルータは「自分のルーティングテーブル」を参照して、この宛先IPはどこに渡すべきか? を決める。
- 宛先が自分の直接つながっているネットワークじゃなければ、**次のホップ(ゲートウェイ)**に渡す。
今回問題が起こった構成
[jhonny] 192.168.1.4 tun0:10.8.0.14
│ (LAN:192.168.1.0/24)
│ デフォルトほぼVPN (0.0.0.0/1,128.0.0.0/1 → 10.8.0.13)
▼
[家庭ルータ(1系)] 192.168.1.1
│ (インターネット側へ)
▼
[VPN Server] tun0:10.8.0.1 gw:162.x.xxx.xxx
ルート: 192.168.2.0/24 → 10.8.0.2(tun0)
▼
[kami] 192.168.2.10 tun0:10.8.0.30
ルート: 192.168.2.0/24 → enp6s0(直結)
▼
[Mac] 192.168.2.3 (LAN:192.168.2.0/24)
デフォルトGW: 192.168.2.1(※ここがキモ)
2. 今回の例で流れを追う(リターンパスがなくてpingが返ってこないパターン)
サーバから(jhonny)Mac に ping
- サーバ(10.8.0.1) が「192.168.2.3にICMPを送りたい」と思う
- VPNトンネルを経由して kaminogou(192.168.2.10) に届く
- kaminogou の Linux がルーティングを見て「192.168.2.3は自分のLAN直結だからそのまま出す」
- Mac の en0 で「送信元=10.8.0.1 宛先=192.168.2.3」の ICMP が見える(ここまではOK)
Mac が返信するとき
- Mac が「宛先=10.8.0.1に返したい」と思う
- Mac のルーティングテーブルを見る
- 10.8.0.0/24 の経路が無い
- なのでデフォルトゲートウェイ(192.168.2.1)に投げる
- 192.168.2.1(家庭用ルータなど)が「10.8.0.0/24 なんて知らん」と捨てる これがリターンパス問題
問い
なぜ送信元が10.8.0.1になるのか? -> サーバから送ったからだな。完全理解。
jhonny (vpn client) -> router -> VPN server -> router -> kami -> macの経路でのそれぞれのip r
jhonny @ 192.168.1.0/24
user@jhonny:~$ ip r
0.0.0.0/1 via 10.8.0.13 dev tun0
default via 192.168.1.1 dev enp0s31f6 proto dhcp src 192.168.1.4 metric 100
10.6.0.0/24 dev br-8065b538746d proto kernel scope link src 10.6.0.1 linkdown
10.8.0.0/24 via 10.8.0.13 dev tun0
10.8.0.13 dev tun0 proto kernel scope link src 10.8.0.14
128.0.0.0/1 via 10.8.0.13 dev tun0
162.43.x.xxx via 192.168.1.1 dev enp0s31f6
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 linkdown
192.168.1.0/24 dev enp0s31f6 proto kernel scope link src 192.168.1.4 metric 100
192.168.1.1 dev enp0s31f6 proto dhcp scope link src 192.168.1.4 metric 100
162.43.x.xxx via 192.168.1.1 dev enp0s31f6
はvpn serverのip
VPN server @ Internet
user@x:/etc/openvpn/ccd# ip r
default via 162.43.53.1 dev ens3 proto static
10.8.0.0/24 via 10.8.0.2 dev tun0
10.8.0.2 dev tun0 proto kernel scope link src 10.8.0.1
100.64.0.0/22 via 10.8.0.2 dev tun0
162.43.53.0/24 dev ens3 proto kernel scope link src 162.x.xx.xxx
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
172.18.0.0/16 dev br-48978d0d6f40 proto kernel scope link src 172.18.0.1
192.168.1.0/24 via 10.8.0.2 dev tun0
192.168.2.0/24 via 10.8.0.2 dev tun0
192.168.40.0/24 via 10.8.0.2 dev tun0
kami @ 192.168.1.0/24
user@kami:~$ ip r
0.0.0.0/1 via 10.8.0.29 dev tun0
default via 192.168.2.1 dev enp6s0 proto static
10.8.0.0/24 via 10.8.0.29 dev tun0
10.8.0.29 dev tun0 proto kernel scope link src 10.8.0.30
128.0.0.0/1 via 10.8.0.29 dev tun0
162.x.xx.xxx via 192.168.2.1 dev enp6s0
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 linkdown
192.168.2.0/24 dev enp6s0 proto kernel scope link src 192.168.2.10
このnetwork構成で、 「jhonny(192.168.1.4) → Mac(192.168.2.3) に ping を打つ」場合の流れを、各ノードでパケットがどう見えているか 追いかけてみます。
登場人物
jhonny: クライアント (LAN: 192.168.1.4, tun0: 10.8.0.14) VPN server: サーバ (tun0: 10.8.0.1, global: 162.x.xx.xxx) kami: クライアント (LAN: 192.168.2.10, tun0: 10.8.0.30) Mac: 192.168.2.3
ルーティングの確認
- jhonny の ip r に 0.0.0.0/1 via 10.8.0.13 があるので、全トラフィックはVPNトンネル経由になる。
- サーバのルートに 192.168.2.0/24 via 10.8.0.2 dev tun0 があるので、192.168.2.0 宛は tun0 へ。
- kami のルートに 192.168.2.0/24 dev enp6s0 があるので、LANへ直出し。
パケットの旅路
1. jhonny 上で ping 送信
- 送信元: 192.168.1.4
- 宛先: 192.168.2.3
- jhonny のルート → デフォルトは tun0 -> だからパケットは tun0 にカプセル化されて VPN server へ
2. VPN server で受信
- サーバがカプセルを解くと、中身は
- src: 192.168.1.4
- dst: 192.168.2.3
- ルーティングを見て「192.168.2.0/24 は 10.8.0.2(kami)へ」 なのでパケットを tun0 に流す
3. kami で受信
- tun0 で受け取る:
- src: 192.168.1.4
- dst: 192.168.2.3
- ルーティングを見ると「192.168.2.0/24 は enp6s0」 なので LAN にそのまま出す
4. Mac で受信
Mac が en0 で受ける:
- src: 192.168.1.4
- dst: 192.168.2.3
- Mac は「192.168.1.0/24 の経路は無い → デフォルトGW (192.168.2.1)」へ返す tcpdump で見える例(Mac の en0 上):
返りの流れ(問題の部分)
5. Mac が echo reply を作成
- src: 192.168.2.3
- dst: 192.168.1.4
- ルートを見て「192.168.1.0/24 へのルートは無い」 → デフォルトゲートウェイ (192.168.2.1) へ送る
6. 192.168.2.1 ルータで破棄
- このルータは「192.168.1.0/24 は知らない」ので捨ててしまう
- その結果、jhonny には戻らない ここが「リターンパス問題」
リターンパス問題のまとめ
- jhonny → server → kami → Mac までは OK
- Mac → jhonny の返り道で迷子になる
- 理由:Mac や LAN ルータが 「192.168.1.0/24 への正しい返し方」を知らない
解決方法
応急処置:kami 側で NAT(SNAT/MASQUERADE)して「192.168.1.4 から来た」パケットを「192.168.2.10 から来た」ことにする 具体的には
iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o enp6s0 -j MASQUERADE
# 転送許可
iptables -A FORWARD -i tun0 -o enp6s0 -j ACCEPT
iptables -A FORWARD -i enp6s0 -o tun0 -m state --state ESTABLISHED,RELATED -j ACCEPT
これをすることで、Macに届くICMPパケットがsrc=192.168.2.10になる。