#背景
VPNを使って拠点間通信(Site-to-Site Routing)を構成していると、 思ったようにパケットが届かないケースがあります。今回は以下の構成で発生した通信不通トラブルと、その解決方法を共有します。
🔧 ネットワーク構成
[A] 100.64.1.61 (100.64.1.0/22)
↓
[B] 100.64.1.27 (OpenVPNクライアント) → VPN → [C] OpenVPNサーバー → [D] 192.168.1.4 (192.168.1.0/24)
目標:AからD(192.168.1.4)への通信を通すこと。
❗ 問題の症状
- BからDには通信可能(VPN経由で192.168.1.4が見える)
- AからDへの通信は届かない
- しかしDからAへの通信(返信)は可能
🧠 原因
Aは 192.168.1.0/24
サブネットに属していないため、この宛先へのパケットをデフォルトゲートウェイに送ってしまっていた。
つまり、「行きのルート」が不在だったわけです。
✅ 解決策:Aに静的ルートを追加
一時的にルートを追加する場合は以下のコマンドを実行:
sudo ip route add 192.168.1.0/24 via 100.64.1.27
これで、Aは目的地 192.168.1.4 へのパケットをVPN中継ノードBに送るようになります。
UbuntuでNetplanを使用している場合、/etc/netplan/xxx.yaml を以下のように修正します:
network:
version: 2
renderer: networkd
ethernets:
enp3s0:
dhcp4: false
dhcp6: false
addresses: [100.64.1.61/22]
routes:
- to: default
via: 100.64.1.1
- to: 192.168.1.0/24
via: 100.64.1.27
nameservers:
addresses: [100.64.1.1, 8.8.8.8, 8.8.4.4]
ちなみに、netplan applyをやって疎通できなくなると困ることってありますよね。 そんな時のためにこれがあります
sudo netplan try
新しい設定をテスト適用し、120秒以内に「yes」入力がなければ元に戻す。 SSH切断時も自動ロールバックされるので、安全。
こんな感じで変更できる
ray@evn:/etc/netplan$ sudo netplan try
Do you want to keep these settings?
Press ENTER before the timeout to accept the new configuration
Changes will revert in 117 seconds
Configuration accepted.
安全!安心!
ルーティングテーブルを見てみよう
ray@evn:/etc/netplan$ ip r
default via 100.64.1.1 dev enp3s0 proto static
192.168.1.0/24 via 100.64.1.27 dev enp3s0 proto static
192.168.40.0/24 via 100.64.1.27 dev enp3s0 proto static
はい、staticで登録されていますね。 これで、 192.168.1.0/24 192.168.40.0/24 にアクセスするときはvpn clientに投げるということになりますね。すばらしい!
さらに続き
上の記事で、 VPN越しのルーティング設定により、拠点Aから拠点Dへの通信が可能になりました。しかしその後、拠点Dの先にある別のマシンE(例:192.168.40.130)にアクセスしようとしたところ、Aからの通信が通らないという問題が発生しました。
🧠 状況の詳細
- A(100.64.1.61) → D(192.168.40.10) → E(192.168.40.130)という経路を通したい
- AからDにはpingが通る(VPNルートが機能している)
- DからEにもpingは通る
- しかしAからEへのpingは失敗する
🔬 調査:パケットはEに届いているのか?
Dで tshark
を使い、Aからの ping をキャプチャすると、確かに以下のような ICMP Echo Request が見える:
100.64.1.61 → 192.168.40.130 ICMP Echo (ping) request
→ つまり、AからのリクエストはDを経由してEに届いている
ではなぜ応答が返ってこないのか?
結論
Eの観点では、A(100.64.1.61)は「未知のネットワーク」にいる。 ルーティングテーブルにその宛先の情報がなければ、Eはデフォルトゲートウェイにパケットを投げる。
しかし、それは今回求めている経路(D経由)ではないため、返答パケットが迷子になる。
解決策
EがmacOSの場合、ターミナルで以下のコマンドを実行:
sudo route -n add -net 100.64.0.0/22 192.168.40.10
設定が反映されていることを確認
route -n get 100.64.1.61
route to: 100.64.1.61
destination: 100.64.0.0
mask: 255.255.252.0
gateway: 192.168.40.10
interface: en0
flags: <UP,GATEWAY,DONE,STATIC,PRCLONING>
recvpipe sendpipe ssthresh rtt,msec rttvar hopcount mtu expire
0 0 0 0 0 0 1500 0
そして、実はDでもパケットを落とすiptablesのchainがあった。 応急処置でこれで対応
sudo iptables -P FORWARD ACCEPT
まとめ
- パケットにはdst/srcがついている
- ルーティングテーブルに情報が載っていないdstを送るときはとりあえずdefault gatewayに送信するようになっている。これは、レスポンスの時も同じ。(A -> Eにicmpが届かなかったのは、E -> Aへのルーティングがなかったことが原因の一つであった)
- routingの情報と、iptablesの設定をいい感じに組み合わせるのが大事。
network_concepts_comparison:
概念: “routing table(ルーティングテーブル)” 主な役割: “パケットをどこに送るかを決める” 処理タイミング: “送り先(next hop)を決定する段階” 影響対象: “全パケット” 例: “ip route add 192.168.1.0/24 via 10.0.0.1” OSI層: “主に L3(ネットワーク層)” 対象: “ルート決定”
概念: “iptables(パケットフィルタ)” 主な役割: “パケットを通すか止めるかを決める” 処理タイミング: “送り出す前後に通過の可否をチェックする段階” 影響対象: “ルールに一致したパケットのみ” 例: “iptables -A FORWARD -s A -d B -j DROP” OSI層: “L3〜L4(IPアドレス + ポートも見れる)” 対象: “フィルタリング、NAT、マングルなど”
パケット処理の流れ(送信時):
受信 → ルーティング → iptablesでFORWARD可否判定 → 宛先へ転送
→ (routing table) ↑
└─ ここでDROPされると"通らない"
ちなみに、ip_forwardはnatではない。
ip_forward_vs_nat:
項目: “主な役割” ip_forward: “パケットをルーティング(中継)する” nat: “IPアドレス(とポート)を書き換える”
項目: “目的” ip_forward: “異なるネットワーク間で通信させる” nat: “プライベートIPをグローバルIPで代理送信”
項目: “IPアドレスの変化” ip_forward: “変わらない(そのまま通す)” nat: “送信元や宛先が書き換えられる”
項目: “具体例①” ip_forward: | 192.168.1.10 → [Router] → 10.0.0.20 (VPNや拠点間接続) nat: | 192.168.1.10 → [Routerがsrcを10.0.0.1に変換] → 8.8.8.8 (インターネット通信)
項目: “具体例②” ip_forward: “拠点Aと拠点Bの間でVPNトンネルを張り、相互通信” nat: “家庭用ルータでLANの機器がインターネットに出る”
項目: “動作条件” ip_forward: “sysctl net.ipv4.ip_forward = 1 で有効化” nat: “iptables -t nat -A POSTROUTING -j MASQUERADE などで定義”
項目: “OSI層” ip_forward: “L3(ネットワーク層)” nat: “L3〜L4(アドレス + ポート)”
項目: “利用シーン” ip_forward: “社内セグメント間通信、VPN中継” nat: “インターネット共有、ポートフォワード”
項目: “セキュリティ影響” ip_forward: “原則パケットを素通し” nat: “アドレスマスキングによる内部秘匿性が上がる”
項目: “備考” ip_forward: “NATと併用されることが多いが別機能” nat: “ip_forward=1 が前提となることが多い”
natは、ローカルのパケットのdstをルータのに変えてくれるんだね。 これで、グローバルでipv4が枯渇するのを防いだりしている。
プライベートIPアドレス(例:192.168.x.x, 10.x.x.x)はインターネット上では通用しません。
グローバルネットのルータやISPは、そのアドレスを知らないし、戻しようもない。
だから、出口のルータが「これは俺から送ったことにしとくわ」と送信元IPを自分に書き換えて送り出す。
戻ってきたパケットは、ルータが内部にどのマシン宛か覚えていて、正しく戻す(コネクション追跡)。
最後に、上のA -> B -> C -> D -> Eのip rを見てみよう
A (100.64.1.61)
default via 100.64.1.1 dev enp3s0 proto static
192.168.1.0/24 via 100.64.1.27 dev enp3s0 proto static
192.168.40.0/24 via 100.64.1.27 dev enp3s0 proto static
B (100.64.1.27) VPN Client
pi@alpha:~ $ ip r
0.0.0.0/1 via 10.8.0.5 dev tun0
default via 100.64.1.1 dev eth0 proto dhcp src 100.64.1.27 metric 202
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
128.0.0.0/1 via 10.8.0.5 dev tun0
162.x.x.x via 100.64.1.1 dev eth0
C (162.x.x.x)
root@x162-43-53-234:~# 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.43.53.234
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 linkdown
172.18.0.0/16 dev br-a4ec221ec502 proto kernel scope link src 172.18.0.1
192.168.1.0/24 via 10.8.0.2 dev tun0
192.168.40.0/24 via 10.8.0.2 dev tun0
D (192.168.40.10)
0.0.0.0/1 via 10.8.0.21 dev tun0
default via 192.168.40.1 dev wlo1 proto static
10.8.0.0/24 via 10.8.0.21 dev tun0
10.8.0.21 dev tun0 proto kernel scope link src 10.8.0.22
128.0.0.0/1 via 10.8.0.21 dev tun0
162.x.x.x via 192.168.40.1 dev wlo1
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 linkdown
192.168.40.0/24 dev wlo1 proto kernel scope link src 192.168.40.10