IPv6 与 MTU:IPv4 通了为什么还会超时

验证:未记录
适用版本
未记录
验证范围
尚未登记验证记录。

本篇补两个常见盲区:IPv4/IPv6 分开选路,以及小包正常、大包卡住。使用 lab.sh,Ubuntu 额外安装 iputils-ping、nftables、tcpdump。每次重新实验先清理旧环境。

1. IPv6 有自己的地址、路由和邻居表#

sudo bash lab.sh connected
# 仅在实验命名空间启用 IPv6;不改宿主机。
for ns in lr-app lr-router lr-db; do
  sudo ip netns exec "$ns" sysctl -w net.ipv6.conf.all.disable_ipv6=0
  sudo ip netns exec "$ns" sysctl -w net.ipv6.conf.default.disable_ipv6=0
done
sudo ip -n lr-app -6 addr add 2001:db8:10::10/64 dev eth0 nodad
sudo ip -n lr-router -6 addr add 2001:db8:10::1/64 dev app0 nodad
sudo ip -n lr-router -6 addr add 2001:db8:20::1/64 dev db0 nodad
sudo ip -n lr-db -6 addr add 2001:db8:20::10/64 dev eth0 nodad
sudo ip netns exec lr-router sysctl -w net.ipv6.conf.all.forwarding=1
sudo ip -n lr-app -6 route add 2001:db8:20::/64 via 2001:db8:10::1
sudo ip -n lr-db -6 route add 2001:db8:10::/64 via 2001:db8:20::1
sudo ip -n lr-app -6 route get 2001:db8:20::10
sudo ip netns exec lr-app ping -6 -c 2 -W 2 2001:db8:20::10
sudo ip -n lr-app -6 neigh show

预期 IPv6 往返成功。2001:db8::/32 是文档地址,仅用于本隔离环境;nodad 跳过 DAD(重复地址检测)只是为了确定性的离线实验,不建议照搬生产。

再启动一个 IPv6 HTTP 健康端点,证明不只是 ICMP(互联网控制消息协议)可达:

sudo ip netns exec lr-db python3 -c 'import socket,runpy; socket.getfqdn=lambda name="":"lr-db"; runpy.run_module("http.server",run_name="__main__")' 8081 --bind 2001:db8:20::10 --directory /run/cookbook-routing-lab/www
# 另一个终端:
sudo ip netns exec lr-app curl --noproxy '*' -g --fail --max-time 3 'http://[2001:db8:20::10]:8081/health'

返回 backend-ready 后 Ctrl-C 停服务。删除 lr-db 的 IPv6 回程路由,IPv6 请求应失败,但原来的 IPv4 HTTP 仍成功;随后原样加回。IPv4 的 default、SG/NACL 规则与 IPv6 都不是同一套。

IPv6 下一跳常是 fe80:: 链路本地地址,必须明确接口;NDP(邻居发现协议)与 RA(路由器通告)依赖 ICMPv6。开启 IPv6 转发会影响默认的 RA 接收行为;确需路由器同时接收 RA 时,评估相应接口 accept_ra=2,不能靠全局乱改解决。不要笼统禁止全部 ICMPv6。内核 IPv6 参数

2. MTU 不匹配:先制造窄链路#

结束 IPv6 服务后清理并重新创建环境:

sudo bash lab.sh cleanup
sudo bash lab.sh connected
sudo ip -n lr-router link set db0 mtu 1280
sudo ip -n lr-db link set eth0 mtu 1280
sudo ip netns exec lr-app ping -c 1 -W 2 -M do -s 1200 10.20.0.10
sudo ip netns exec lr-app ping -c 1 -W 2 -M do -s 1400 10.20.0.10

MTU = Maximum Transmission Unit,最大传输单元。普通 IPv4 ICMP 包还需 20 字节 IP 头和 8 字节 ICMP 头,所以两次大小分别约 1228、1428;-M do 禁止分片。小包成功,大包应收到“需要分片/MTU 1280”等错误。

PMTUD(Path MTU Discovery,路径 MTU 发现)依赖相关 ICMP 错误返回源端。终端 A 抓 sudo ip netns exec lr-router tcpdump -ni app0 icmp,终端 B 重试大包,确认 MTU 提示,而不是只看 curl 超时。

3. 模拟 PMTUD 黑洞并恢复#

sudo ip netns exec lr-router nft add table ip mtu_lab
sudo ip netns exec lr-router nft 'add chain ip mtu_lab output { type filter hook output priority filter; policy accept; }'
sudo ip netns exec lr-router nft add rule ip mtu_lab output icmp type 3 icmp code 4 counter drop
# 只清本实验端点的路由缓存,使它重新探测;不对宿主机操作。
sudo ip -n lr-app route flush cache
sudo ip netns exec lr-app ping -c 1 -W 2 -M do -s 1400 10.20.0.10
sudo ip netns exec lr-router nft list table ip mtu_lab
sudo ip netns exec lr-router nft delete table ip mtu_lab
sudo ip -n lr-app route flush cache
sudo ip netns exec lr-app ping -c 1 -W 2 -M do -s 1400 10.20.0.10

屏蔽时应超时且 drop 计数增加;恢复后重新收到 MTU 提示,大包仍因 1280 链路而不能直接发送。最后将两端链路 MTU 改回 1500,清实验缓存,大包应恢复成功。

生产 VPN(虚拟专用网络)/隧道增加封装开销,应计算有效 MTU;MSS(TCP 最大报文段长度)调整仅处理 TCP,不修复 UDP,也不替代允许必要的 ICMP。ping、tracepath、TCP 重传与接口 MTU 要联合判断,不能靠把所有接口调成某个固定值碰运气。

sudo ip -n lr-router link set db0 mtu 1500
sudo ip -n lr-db link set eth0 mtu 1500
sudo ip -n lr-app route flush cache
sudo ip netns exec lr-app ping -c 1 -W 2 -M do -s 1400 10.20.0.10
sudo bash lab.sh cleanup

搜索文档

输入关键词,搜索所有文档。

↑ ↓ 选择 · Enter 打开 · Esc 关闭