從「能互通」到「想出去」#

上一章把多個 Network Namespace 透過 Linux bridge 串起來,但 namespace 內仍然只認識同網段的 IP。要連到外網,需要兩個前提:

  • namespace 知道「不在本地網段的封包要丟給誰」
  • 那個被丟到的下一站,本身連得到外面

第一件事由 routing table(路由表)解決,第二件事下一章再處理。

看一下 routing table 長什麼樣#

在 host 上:

ip route

典型輸出包含兩種條目:

  • default via <gateway> dev <iface>:default gateway(預設閘道),所有不認識的目的地都丟給它
  • <network>/<prefix> dev <iface> proto kernel scope link src <ip>:直連網段(directly connected),同網段內封包直接從這張介面送

如果是用 namespace 模擬出來的環境,ip netns exec ns1 ip route 一開始會「只有」直連網段那條,因為從未設過 default gateway。

給 bridge 一個 IP,當作 namespace 的 gateway#

在前一章的拓撲中,br0 還沒有 IP。給它一個位於同一個子網的位址,例如 10.0.0.254

sudo ip addr add 10.0.0.254/24 dev br0
sudo ip link set br0 up

這時 host 自己也成為 10.0.0.0/24 這個子網的成員,封包能從 host 的 br0 介面送進這個網段。

在 namespace 裡 ping bridge#

namespace 內部已經有 10.0.0.1br010.0.0.254,兩者同網段,可以直接 ping:

sudo ip netns exec ns1 ping -c 3 10.0.0.254

預期能收到回覆。背後流程:

  • ns1 比對路由表,發現 10.0.0.25410.0.0.0/24,是直連網段
  • 透過 ARP 取得 br0 的 MAC
  • 封包從 veth-c1 出 → veth-h1 進 → bridge 把它送進 host 的 br0 介面
  • host 看到目的 IP 是自己的 br0,回 ICMP echo reply

在 namespace 設 default gateway#

下一步,要讓 namespace 把「非本地封包」丟給 br0

sudo ip netns exec ns1 ip route add default via 10.0.0.254
sudo ip netns exec ns1 ip route

第二行應該會看到:

  • default via 10.0.0.254 dev veth-c1
  • 10.0.0.0/24 dev veth-c1 proto kernel scope link src 10.0.0.1

同樣的設定要為每個 namespace 各做一次,否則只有 ns1 知道 default gateway。

試著 ping 外部 IP#

例如 ping 一個公開的 DNS:

sudo ip netns exec ns1 ping -c 3 8.8.8.8

這時很可能會發生兩種狀況:

  • 發出去之後完全沒有回應,逾時
  • 顯示 Destination Host Unreachable 或類似錯誤

封包確實有離開 namespace、有抵達 host,但「出不了 host」。要弄清楚為什麼,需要用 tcpdump 觀察封包到底走到哪一步、為什麼回不來。

用 tcpdump 觀察#

可以同時開兩個視窗,一個看 host 主要對外介面(例如 eth0),一個看 br0

sudo tcpdump -i br0 -nn icmp
sudo tcpdump -i eth0 -nn icmp

預期觀察到的封包樣貌:

  • br0 上會看到 10.0.0.1 > 8.8.8.8: ICMP echo request
  • eth0 上可能會看到一個來源 IP 仍然是 10.0.0.1 的封包試著要出去
  • 對端就算收得到,回應也回不來,因為外網根本不認識 10.0.0.1

這個觀察結果就是下一章要解的問題:封包出得了 host,但來源 IP 是私有位址,網際網路無從回覆。

路由 vs. 轉送#

到這裡要區分兩個概念:

  • routing:「我要把這個封包送去哪一站」,由 routing table 決定
  • forwarding:「我這台機器願不願意幫別的來源把封包轉出去」,由 ip_forward sysctl 決定

光有路由不夠,host 還必須被允許做 IP forwarding,這也是下一章的主題之一。

延伸閱讀#

  • man 8 ip-route
  • man 8 tcpdump
  • 「Linux Advanced Routing & Traffic Control HOWTO」