- MTU: 数据链路层最大载荷 (不包括头), 1500
- MSS: TCP 层最大载荷 (不包括头), 1460
最大传输单元 (Maximum Transmission Unit, MTU)
数据链路层传输的帧大小是有限制的,不能把一个太大的包直接塞给链路层,这个限制被称为「最大传输单元(Maximum Transmission Unit, MTU)」
下图是以太网的帧格式,以太网的帧最小的帧是 64 字节,除去 14 字节头部和 4 字节 CRC 字段,有效荷载最小为 46 字节。最大的帧是 1518 字节,除去 14 字节头部和 4 字节 CRC,有效荷载最大为 1500,这个值就是以太网的 MTU。因此如果传输 100KB 的数据,至少需要 (100 * 1024 / 1500) = 69 个以太网帧。
不同的数据链路层的 MTU 是不同的。通过 netstat -i
可以查看网卡的 mtu.
IP 分段
- IPv4 数据报的最大大小为 65535 字节
- 当一个 IP 数据包大于 MTU 时,IP 会把数据报文进行切割为多个小的片段(小于 MTU)
IP 头
IP 头部中有一个表示分片偏移量的字段,用来表示该分段在原始数据报文中的位置,如下图所示:
例子
$ ping -s 3000 www.baidu.com
-s
增加 8B 的 ICMP 头
第一个包
More fragments: Set
表示这个包是 IP 分段包的一部分,还有其它的分片包Fragment offset: 0
表示分片偏移量为 0- IP 包的 payload 的大小为 1480,加上 20 字节的头部正好是 1500
第二个包
- 第一个包的 Fragment offset 从0开始, 负载1480 (0~1479), 所以第二个包的编号从1480开始
- 注意 wireshark 中 Fragment offset 的显示值
第三个包
More fragments: Not set
这是最后一个分片
1480 + 1480 + 48 = 3008
IP 协议不会对丢包进行重传,那么 IP 分段中有分片丢失、损坏的话,会发生什么呢? 这种情况下,目标主机将没有办法将分段的数据包重组为一个完整的数据包,依赖于传输层是否进行重传。
利用 IP 包分片的策略,有一种对应的网络攻击方式IP fragment attack,就是一直传More fragments = 1的包,导致接收方一直缓存分片,从而可能导致接收方内存耗尽。
网络中的木桶效应:路径 MTU
实际模拟路径 MTU 发现
ip 层设置不允许分片: 数据包设置了不允许分片Don’t fragment: Set, 那么分包在 tcp 层
因为有 MTU 的存在,TCP 每次发包的大小也限制了,这就是下面要介绍的 MSS。
TCP 最大段大小(Max Segment Size,MSS)
MSS = MTU - IP header头大小 - TCP 头大小
在以太网中 TCP 的 MSS = 1500(MTU) - 20(IP 头大小) - 20(TCP 头大小)= 1460
例子
- Len: 指载荷大小
可以看到 TCP 的包体数据大小为 1448,因为TCP 头部里包含了 12 字节的选项(Options)字段,头部大小从之前的 20 字节变为了 32 字节,所以 TCP 包体大小变为了:1500(以太网 MTU) - 20(IP 固定表头大小) - 20(TCP 固定表头大小) - 12(TCP 表头选项) = 1448
为什么有时候抓包看到的单个数据包大于 MTU
- TSO(TCP Segment Offload)特性, 指由网卡代替 CPU 实现 packet 的分段和合并,节省系统资源,因此 TCP 可以抓到超过 MTU 的包,但是不是真正传输的单个包会超过链路的 MTU。
使用ethtool -k可以查看这个特性是否打开,比如 ethtool -k eth0
:
TCP 套接字选项 TCP_MAXSEG
用于设置 MSS.
常量 TCP_MIN_MSS 的值为 88,常量 MAX_TCP_WINDOW 的值为 32768,因此不在 88~32767 直接的 MSS 值会设置失败。
为什么 TCP_MAXSEG 的下界是 88?
- 这是因为 TCP 头包含了 20 字节的固定长度和 40 字节的可选参数,所以 TCP 头的最大长度是 60,IP 头最大长度也是 60。
- 为了保证在 TCP 头占满 60 字节、IP 头占满 60 字节的情况下,至少还能发 8 字节的数据,MSS 至少要等于 (MAX_IP_HDR + MAX_TCP_HDR + MIN_IP_FRAG) - (MIN_IP_HDR + MIN_TCP_HDR) = (60+60+8) - (20+20) = 88 字节。
那 MSS 设置一个比较大的值,比如 30000,实际 MSS 是 30000 吗?
- 后面真正发送 SYN 时,会根据设备的 MTU 重新计算最终的 MSS
小结
IP 数据包长度在超过链路的 MTU 时在发送之前需要分片,而 TCP 层为了 IP 层不用分片主动将包切割成 MSS 大小。