以前の記事

https://blog.ingenboy.com/post/problem_site2site/

こちらの記事でVPNクライアントを介して、別々のNWセグメントに所属するサーバ間で通信をする方法を軽く説目しました。 この問題は、リターンパス問題だったのですが、今回、改めてリターンパス問題について書きたい。

まず、

1. IP層での基本ルール

  1. 送信元IP と 宛先IP はパケットに埋め込まれていて、転送中は変わらない(※NATしない限り)。
  2. 各ホストやルータは「自分のルーティングテーブル」を参照して、この宛先IPはどこに渡すべきか? を決める。
  3. 宛先が自分の直接つながっているネットワークじゃなければ、**次のホップ(ゲートウェイ)**に渡す。

今回問題が起こった構成

[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

  1. サーバ(10.8.0.1) が「192.168.2.3にICMPを送りたい」と思う
  2. VPNトンネルを経由して kaminogou(192.168.2.10) に届く
  3. kaminogou の Linux がルーティングを見て「192.168.2.3は自分のLAN直結だからそのまま出す」
  4. Mac の en0 で「送信元=10.8.0.1 宛先=192.168.2.3」の ICMP が見える(ここまではOK)

Mac が返信するとき

  1. Mac が「宛先=10.8.0.1に返したい」と思う
  2. Mac のルーティングテーブルを見る
  3. 10.8.0.0/24 の経路が無い
  4. なのでデフォルトゲートウェイ(192.168.2.1)に投げる
  5. 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

ルーティングの確認

  1. jhonny の ip r に 0.0.0.0/1 via 10.8.0.13 があるので、全トラフィックはVPNトンネル経由になる。
  2. サーバのルートに 192.168.2.0/24 via 10.8.0.2 dev tun0 があるので、192.168.2.0 宛は tun0 へ。
  3. kami のルートに 192.168.2.0/24 dev enp6s0 があるので、LANへ直出し。

パケットの旅路

1. jhonny 上で ping 送信

  1. 送信元: 192.168.1.4
  2. 宛先: 192.168.2.3
  3. jhonny のルート → デフォルトは tun0 -> だからパケットは tun0 にカプセル化されて VPN server へ

2. VPN server で受信

  1. サーバがカプセルを解くと、中身は
  2. src: 192.168.1.4
  3. dst: 192.168.2.3
  4. ルーティングを見て「192.168.2.0/24 は 10.8.0.2(kami)へ」 なのでパケットを tun0 に流す

3. kami で受信

  1. tun0 で受け取る:
  2. src: 192.168.1.4
  3. dst: 192.168.2.3
  4. ルーティングを見ると「192.168.2.0/24 は enp6s0」 なので LAN にそのまま出す

4. Mac で受信

Mac が en0 で受ける:

  1. src: 192.168.1.4
  2. dst: 192.168.2.3
  3. Mac は「192.168.1.0/24 の経路は無い → デフォルトGW (192.168.2.1)」へ返す tcpdump で見える例(Mac の en0 上):

返りの流れ(問題の部分)

5. Mac が echo reply を作成

  1. src: 192.168.2.3
  2. dst: 192.168.1.4
  3. ルートを見て「192.168.1.0/24 へのルートは無い」 → デフォルトゲートウェイ (192.168.2.1) へ送る

6. 192.168.2.1 ルータで破棄

  1. このルータは「192.168.1.0/24 は知らない」ので捨ててしまう
  2. その結果、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になる。