• MTU: 数据链路层最大载荷 (不包括头), 1500
  • MSS: TCP 层最大载荷 (不包括头), 1460

最大传输单元 (Maximum Transmission Unit, MTU)

image.png

数据链路层传输的帧大小是有限制的,不能把一个太大的包直接塞给链路层,这个限制被称为「最大传输单元(Maximum Transmission Unit, MTU)」

下图是以太网的帧格式,以太网的帧最小的帧是 64 字节,除去 14 字节头部和 4 字节 CRC 字段,有效荷载最小为 46 字节。最大的帧是 1518 字节,除去 14 字节头部和 4 字节 CRC,有效荷载最大为 1500,这个值就是以太网的 MTU。因此如果传输 100KB 的数据,至少需要 (100 * 1024 / 1500) = 69 个以太网帧。

image.png

不同的数据链路层的 MTU 是不同的。通过 netstat -i 可以查看网卡的 mtu.

IP 分段

  • IPv4 数据报的最大大小为 65535 字节
  • 当一个 IP 数据包大于 MTU 时,IP 会把数据报文进行切割为多个小的片段(小于 MTU)

image.png

IP 头

IP 头部中有一个表示分片偏移量的字段,用来表示该分段在原始数据报文中的位置,如下图所示:

image.png

例子

  1. $ ping -s 3000 www.baidu.com
  • -s 增加 8B 的 ICMP 头

第一个包

image.png

  • More fragments: Set 表示这个包是 IP 分段包的一部分,还有其它的分片包
  • Fragment offset: 0 表示分片偏移量为 0
  • IP 包的 payload 的大小为 1480,加上 20 字节的头部正好是 1500

第二个包

image.png

  • 第一个包的 Fragment offset 从0开始, 负载1480 (0~1479), 所以第二个包的编号从1480开始
  • 注意 wireshark 中 Fragment offset 的显示值

第三个包

image.png

  • More fragments: Not set 这是最后一个分片

image.png

  • 1480 + 1480 + 48 = 3008

    IP 协议不会对丢包进行重传,那么 IP 分段中有分片丢失、损坏的话,会发生什么呢? 这种情况下,目标主机将没有办法将分段的数据包重组为一个完整的数据包,依赖于传输层是否进行重传

利用 IP 包分片的策略,有一种对应的网络攻击方式IP fragment attack,就是一直传More fragments = 1的包,导致接收方一直缓存分片,从而可能导致接收方内存耗尽。

网络中的木桶效应:路径 MTU

image.png

实际模拟路径 MTU 发现

ip 层设置不允许分片: 数据包设置了不允许分片Don’t fragment: Set, 那么分包在 tcp 层

image.png

因为有 MTU 的存在,TCP 每次发包的大小也限制了,这就是下面要介绍的 MSS

TCP 最大段大小(Max Segment Size,MSS)

MSS = MTU - IP header头大小 - TCP 头大小

在以太网中 TCP 的 MSS = 1500(MTU) - 20(IP 头大小) - 20(TCP 头大小)= 1460

image.png

例子

image.png

  • 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 :

image.png

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 大小。