ping 真正发的东西,以及 MTU
本实验在 VM 中运行
用 veth 连接两个命名空间(pa、pb),并在其上观察 ICMP 和 UDP。
结构与第 2 个实验相同,但这次主角不是帧,而是数据包大小和错误消息。
目标
捕获 ping 发送的 ICMP echo,观察超过 MTU 的数据包被分片或拒绝, 并确认 UDP 如何告知端口已关闭(ICMP port unreachable)。
为什么重要
“ping 正常,但只有大文件会卡住”“SSH 正常,但网页只加载一半”—— 几乎总是 MTU 问题。小数据包可以通过,只有大数据包会在某处被丢弃; 由于 DF 位的存在,它又无法分片,而负责通知这一事实的 ICMP 还被防火墙拦截, 这是一种常见事故形态。每当 VPN、覆盖网络或隧道添加额外请求头时, 这个问题就会卷土重来,因此我们将亲手计算 1500 和 1472 这两个数字从何而来。
步骤
- 创建命名空间
pa、pb和 vethpa0↔pb0,配置10.60.0.1/24、10.60.0.2/24,并使两者能够互相 ping。 - 将从
pa向pb执行ping -c 3的结果保存到/root/icmp/ping.txt。 - 在
pa0上捕获 ICMP 的同时发送一次 ping,将包含 echo request 和 echo reply 的输出保存到/root/icmp/echo.txt。 - 使用
ping -M do禁止分片,同时不断改变大小,找出能够通过的最大负载,并在/root/icmp/mtu.txt中保存mtu=、max_payload=两行和失败消息。 - 在允许分片的情况下捕获负载为 3000 字节的 ping,将能够看到分片数据包的输出保存到
/root/icmp/frag.txt。 - 在
pb上启动监听 UDP 9001 的nc -lu(将收到的内容保存到/root/icmp/udp.txt),并从pa发送hello-udp。 - 从
pa向pb上关闭的 UDP 端口 9999 发送数据报并进行捕获,将能够看到udp port 9999 unreachable的输出保存到/root/icmp/unreach.txt。 - 将
pa0的 MTU 降至 1400,并在/root/icmp/report.md中写入mtu=1400、max_payload=两行,同时说明该数值为什么如此,以及分片为什么会成为问题。
参考
ping -M do -s <바이트>:启用 DF 并指定负载大小。MTU 1500 = IP 请求头 20 + ICMP 请求头 8 + 负载。- 可以像
ip netns exec pa timeout 5 tcpdump -i pa0 -nn -l icmp > 파일 2>&1 &这样设置捕获,然后再发送。 - UDP 服务器:
ip netns exec pb setsid timeout 20 nc -lu 10.60.0.2 9001 > /root/icmp/udp.txt 2>&1 < /dev/null &。客户端:echo hello-udp | ip netns exec pa nc -u -q 1 -w 1 10.60.0.2 9001。 - 更改 MTU:
ip -n pa link set pa0 mtu 1400。 - 常见错误:输入
-s 1500后疑惑为什么无法运行。必须减去 28 字节的请求头。
连接两台主机
创建命名空间 pa、pb 和 veth pa0↔pb0,配置 10.60.0.1/24、10.60.0.2/24,并使两者能够互相 ping。
顺序与第 2 个实验相同:netns add → link add veth → link set netns → addr add → link set up(包括 lo)。使用 ip netns exec pa ping -c 1 10.60.0.2 检查。
执行三次 ping
将从 pa 向 pb 执行 ping -c 3 的结果保存到 /root/icmp/ping.txt。
执行 ip netns exec pa ping -c 3 10.60.0.2 > /root/icmp/ping.txt。统计行中必须有 3 received。time= 是往返时间(RTT)。
捕获 echo
在 pa0 上捕获 ICMP 的同时发送一次 ping,将包含 echo request 和 echo reply 的输出保存到 /root/icmp/echo.txt。
执行 ip netns exec pa timeout 5 tcpdump -i pa0 -nn -l icmp > /root/icmp/echo.txt 2>&1 &,等待 1 秒后执行 ip netns exec pa ping -c 1 10.60.0.2。文件中必须各有一行 ICMP echo request 和 ICMP echo reply。
禁止分片
使用 ping -M do 禁止分片,同时不断改变大小,找出能够通过的最大负载,并在 /root/icmp/mtu.txt 中保存 mtu=、max_payload= 两行和失败消息。
ip -n pa link show pa0 的 mtu 为 1500。ping -M do -s 1472 可以成功,而 -s 1473 会以 message too long, mtu=1500 失败。将失败的 ping 输出(2>&1)附在两行下面。
进行分片
在允许分片的情况下捕获负载为 3000 字节的 ping,将能够看到分片数据包的输出保存到 /root/icmp/frag.txt。
设置捕获并执行 ip netns exec pa ping -c 1 -s 3000 10.60.0.2。3028 字节的数据包会被分成 1500 字节的分片,只有第一个分片显示为 ICMP echo request,其余显示为 ip-proto-1(因为 ICMP 请求头只在第一个分片中)。
UDP 没有握手
在 pb 上启动监听 UDP 9001 的 nc -lu(将收到的内容保存到 /root/icmp/udp.txt),并从 pa 发送 hello-udp。
服务器:ip netns exec pb setsid timeout 20 nc -lu 10.60.0.2 9001 > /root/icmp/udp.txt 2>&1 < /dev/null &。客户端:echo hello-udp | ip netns exec pa nc -u -q 1 -w 1 10.60.0.2 9001。文件中包含 hello-udp 即可。
关闭的 UDP 端口通过 ICMP 应答
从 pa 向 pb 上关闭的 UDP 端口 9999 发送数据报并进行捕获,将能够看到 udp port 9999 unreachable 的输出保存到 /root/icmp/unreach.txt。
将捕获过滤器设为 'icmp or udp',并执行 echo x | ip netns exec pa nc -u -q 1 -w 1 10.60.0.2 9999。一行 UDP 数据报之后会出现 ICMP 10.60.0.2 udp port 9999 unreachable。
降低 MTU 并总结
将 pa0 的 MTU 降至 1400,并在 /root/icmp/report.md 中写入 mtu=1400、max_payload= 两行,同时说明该数值为什么如此,以及分片为什么会成为问题。
执行 ip -n pa link set pa0 mtu 1400 后,ping -M do -s 1372 可以成功,而 -s 1373 不能成功。max_payload 为 1400 - 28。说明中必须包含 MTU 和分片(或 fragment)一词。