IP 包比链路的 MTU 大怎么办?拆成分片,到目的地再拼回去。但分片到底怎么标记、偏移怎么算、为什么说“丢一个全丢”、为什么 TCP 总是带 DF 位?这篇在真实网口上抓一个 4000 字节的 UDP 数据报,逐个分片看 id、MF、片偏移,再用实验和计算看分片的代价。
4000 字节的 UDP 数据(+ 8 字节 UDP 头 = 4008 字节的 IP 载荷)抓到 3 个 IP 分片:
序号 id MF DF 片偏移(字节) IP 总长 IP 载荷 以太网帧长
1 843 1 0 0 1500 1480 1514
2 843 1 0 1480 1500 1480 1514
3 843 0 0 2960 1068 1048 1082
规律:每个分片的 IP 载荷是 8 的倍数(最后一个除外),这里 1480 = 1500 − 20;最后一个分片 MF=0。偏移字段 13 位、单位 8 字节,所以抓到的“片偏移”是字节数 / 8 × 8。
只有第一个分片带 UDP 头(源 / 目的端口);后面的分片只有 IP 头 + 数据,单看它们不知道是哪个端口——所以带端口过滤的防火墙遇到分片很头疼
netstat -s -p ip 的计数这次:发出的分片 +3(与抓到的 3 个分片一致)同一个数据报的所有分片共用一个 id;MF(更多分片)位除最后一个外都置 1;片偏移字段 13 位、单位是 8 字节,所以每个分片的载荷是 8 的倍数。实测 4000 字节 UDP 加 8 字节头,在 MTU 1500 上得到载荷 1480 / 1480 / 1048 的三个分片,只有第一个带 UDP 头。
先看系统限制:net.inet.udp.maxdgram: 9216——默认情况下超过 9216 字节的 UDP 数据报 sendto 会报 Message too long,要把发送缓冲区调大(SO_SNDBUF)才能发更大的
不调大 SO_SNDBUF 直接发 20000 字节:[Errno 40] Message too long
应用调用一次 sendto(40000 字节),一次 recvfrom 拿到 40000 字节;lo0 上看到 1 个 IP 包(IP 总长远大于 MTU 16384,却没有分片——macOS 的 lo0 不对超过 MTU 的包分片,直接整包交给本机的接收方):
分片 1: id=4415 MF=0 偏移=0 IP 载荷=40008
内核计数: 被分片的包 +0,发出的分片 +0,收到的分片 +0,成功重组 +0macOS 的 lo0 MTU 是 16384,但 40000 字节的 UDP 数据报在 lo0 上仍是一个 IP 包,内核分片计数不动。想在本机看真分片,要用真实网口(如 en0)。
1000 字节的数据报:送达 953 / 1000 = 95.3%
20000 字节的数据报:送达 949 / 1000 = 94.9%
60000 字节的数据报:送达 950 / 1000 = 95.0%
一个 60000 字节的数据报在 lo0 上是 1 个 IP 包——lo0 不分片,所以 3 档大小的送达率都约等于 95%,看不出“丢一个分片全丢”的放大效应
要看放大效应得有真的分片(MTU 1500 的以太网路径),而丢包 / 延迟只能在 lo0 上注入;下面用公式和随机模拟补上(这是计算,不是实测)
数据报大小 分片数(MTU 1500) 理论送达率 0.95^n 模拟 10 万次
1000 字节 1 95.0% 95.1%
4000 字节 3 85.7% 85.6%
20000 字节 14 48.8% 48.8%
60000 字节 41 12.2% 12.2%任何一个分片丢失,目的地就无法重组整个数据报。分片越多,送达概率按 0.95^n 下降。lo0 上的实测因为没有真分片,三档大小都约 95%;放大效应这部分是公式和模拟,不是实测。
抓到 303 个 IP 包:MF 置位 0 个,片偏移非 0 0 个,DF 置位 302 个——TCP 的包全部带 DF,从不依赖分片;超过路径 MTU 时靠 ICMP“需要分片”来调整(见 PMTUD 一篇)
TCP 的包带 DF 位,并用 MSS 限制段长,发送方根据 ICMP 的反馈调整,基本不依赖分片;实测 303 个包里 302 个带 DF,没有 MF 或非零偏移。
net.inet.ip.maxfragpackets: 8192 net.inet.ip.maxfragsperpacket: 128 netstat -s -p ip 当前计数:收到的分片 1716;成功重组的包 0;发出的分片 12;被分片的包 0;重组超时丢弃的分片 0;因其它原因丢弃的分片 0 这些计数多半是 0 或很小:现代网络里 TCP 为主、有 PMTUD,真正的 IP 分片已经很少见;UDP 大数据报(DNS 大响应、QUIC 之前的一些协议)仍可能触发
系统限制同时重组的包数和每个包的分片数(maxfragpackets、maxfragsperpacket),超时的分片会被丢弃。netstat -s -p ip 可以看到各项计数。