LabHub
学习 学习路径 课程

网络基础 — 在 Linux 虚拟机里动手

ping 真正发的东西,以及 MTU

在 LabHub 中继续学习

本实验在 VM 中运行

用 veth 连接两个命名空间(papb),并在其上观察 ICMP 和 UDP。 结构与第 2 个实验相同,但这次主角不是帧,而是数据包大小和错误消息

目标

捕获 ping 发送的 ICMP echo,观察超过 MTU 的数据包被分片或拒绝, 并确认 UDP 如何告知端口已关闭(ICMP port unreachable)。

为什么重要

“ping 正常,但只有大文件会卡住”“SSH 正常,但网页只加载一半”—— 几乎总是 MTU 问题。小数据包可以通过,只有大数据包会在某处被丢弃; 由于 DF 位的存在,它又无法分片,而负责通知这一事实的 ICMP 还被防火墙拦截, 这是一种常见事故形态。每当 VPN、覆盖网络或隧道添加额外请求头时, 这个问题就会卷土重来,因此我们将亲手计算 1500 和 1472 这两个数字从何而来。

步骤

  1. 创建命名空间 papb 和 veth pa0pb0,配置 10.60.0.1/2410.60.0.2/24,并使两者能够互相 ping。
  2. 将从 papb 执行 ping -c 3 的结果保存到 /root/icmp/ping.txt
  3. pa0 上捕获 ICMP 的同时发送一次 ping,将包含 echo request 和 echo reply 的输出保存到 /root/icmp/echo.txt
  4. 使用 ping -M do 禁止分片,同时不断改变大小,找出能够通过的最大负载,并在 /root/icmp/mtu.txt 中保存 mtu=max_payload= 两行和失败消息。
  5. 在允许分片的情况下捕获负载为 3000 字节的 ping,将能够看到分片数据包的输出保存到 /root/icmp/frag.txt
  6. pb 上启动监听 UDP 9001 的 nc -lu(将收到的内容保存到 /root/icmp/udp.txt),并从 pa 发送 hello-udp
  7. papb 上关闭的 UDP 端口 9999 发送数据报并进行捕获,将能够看到 udp port 9999 unreachable 的输出保存到 /root/icmp/unreach.txt
  8. pa0 的 MTU 降至 1400,并在 /root/icmp/report.md 中写入 mtu=1400max_payload= 两行,同时说明该数值为什么如此,以及分片为什么会成为问题。

参考

连接两台主机

创建命名空间 papb 和 veth pa0pb0,配置 10.60.0.1/2410.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

将从 papb 执行 ping -c 3 的结果保存到 /root/icmp/ping.txt

执行 ip netns exec pa ping -c 3 10.60.0.2 > /root/icmp/ping.txt。统计行中必须有 3 receivedtime= 是往返时间(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 requestICMP 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 应答

papb 上关闭的 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=1400max_payload= 两行,同时说明该数值为什么如此,以及分片为什么会成为问题。

执行 ip -n pa link set pa0 mtu 1400 后,ping -M do -s 1372 可以成功,而 -s 1373 不能成功。max_payload 为 1400 - 28。说明中必须包含 MTU 和分片(或 fragment)一词。