飞翔飞翔
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
  • 数据库

    • SQL教程
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON教程
  • 工具

    • Markdown指南
  • Git

    • GitFlow
  • Quartz

    • Quartz教程
  • Java

    • Java设计模式
  • 缓存

    • Redis教程
联系
阿里云
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
  • 数据库

    • SQL教程
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON教程
  • 工具

    • Markdown指南
  • Git

    • GitFlow
  • Quartz

    • Quartz教程
  • Java

    • Java设计模式
  • 缓存

    • Redis教程
联系
阿里云
  • 学习路径
  • IP协议

    • 认识 IP 协议与网络层定位
    • IPv4 编址体系与分类地址
    • CIDR 与子网划分实战
    • IPv6 编址体系
    • ARP 协议详解
    • NDP 协议详解
    • IGMP 与 MLD 组播侦听发现
    • VRRP 与网关冗余
    • IPv4 数据报首部解析
    • IPv6 数据报与扩展首部
    • IPsec 安全扩展
    • 分片、MTU 与路径发现
    • ICMP 与 Traceroute 原理
    • 路由基础与转发流水线
    • 动态路由协议
    • NAT 网络地址转换
    • DHCP 与自动配置
    • Wireshark 与命令行工具
    • IPv6 过渡技术
    • IP 协议栈排障与攻击防御
  • TCP协议

    • 认识 TCP 协议与传输层定位
    • TCP 报文段首部解析
    • 三次握手与连接建立
    • 四次挥手与连接释放
    • TCP 有限状态机
    • 序列号与确认机制
    • 超时重传与 RTO 计算
    • 滑动窗口与流量控制
    • 拥塞控制基础
    • 现代拥塞控制算法
    • TCP 选项与扩展
    • TCP 性能调优与内核参数
    • Nagle 算法与糊涂窗口综合征
    • TCP 定时器与 Keep-Alive 机制
    • TCP 安全与攻击防御
    • TCP 与上层/下层交互
    • TCP 综合实践与排障

分片、MTU 与路径发现

MTU:网络层的"限高杆"

MTU(Maximum Transmission Unit,最大传输单元)是链路层能承载的最大数据帧长度。常见值:

链路类型MTU备注
以太网1500 字节最常用
PPPoE(宽带拨号)1492 字节1500 - 8 字节 PPPoE 头
IEEE 802.11(Wi-Fi)2304 字节但通常限制在 1500
Jumbo Frame9000 字节数据中心内部
令牌环(已淘汰)4464 字节历史遗留

注意:MTU 是链路层概念,但直接影响 IP 层。如果 IP 数据报超过 MTU,必须分片或丢弃。

MSS:TCP 的"自我约束"

MSS(Maximum Segment Size)是 TCP 选项,在三次握手时协商:

MSS = MTU - IP 首部(20) - TCP 首部(20) = 1500 - 40 = 1460 字节

如果一方声明 MSS=1460,另一方声明 MSS=1400(因为中间有 PPPoE),双方取较小值 1400。

关键意义:TCP 尽量让每个段刚好装在一个 IP 包里,避免 IP 层分片。因为分片有开销、有丢包风险、有重组延迟。

生活例子:快递公司规定箱子最大 1500mm。你发货时把货物切成 1460mm 的块,留 40mm 给包装纸(IP+TCP 头)。如果知道对方只能收 1400mm 的箱子,你就切 1400mm。

IPv4 分片机制

当 IP 包超过出接口 MTU 时,路由器可以分片:

分片三件套

  • Identification:所有分片相同,表示"我们是同一个包"
  • Flags:DF(Don't Fragment)、MF(More Fragments)
  • Fragment Offset:该分片在原始包中的位置,单位 8 字节

分片计算示例

原始包:Total Length = 3000 字节,首部 20 字节,数据 2980 字节 MTU = 1500,每个分片最多 1480 字节数据(1500 - 20 首部)

分片MFDFOffset数据范围长度
第 1 片1000~14791500
第 2 片101851480~29591500
第 3 片003702960~297940

Offset 计算:

  • 第 1 片:0 / 8 = 0
  • 第 2 片:1480 / 8 = 185
  • 第 3 片:2960 / 8 = 370

目的主机重组:收到所有分片后,按 Offset 排序,拼接数据,交给上层。

分片的关键是 Identification 相同(表示同属一个包)、MF 标志(More Fragments,1 表示后面还有)、Fragment Offset(排序依据)。任何一个分片丢失,目的主机都无法重组,TCP 必须重传整个原始包。

分片的风险

  1. 任何一片丢失,整个包报废:TCP 必须重传整个原始包
  2. 重组开销:目的主机需要缓存、排序、拼接
  3. 防火墙绕过:攻击者利用分片重叠或极小分片绕过 ACL/IDS 检测
  4. 不同路径分片:如果路径 MTU 变化,后续分片可能在不同路由器分片,导致重组复杂

IPv6 分片机制

IPv6 的设计哲学:中间路由器绝对不分片。

  • 源主机在发送前就知道路径 MTU,按 MTU 大小发包
  • 如果包超过 MTU,中间路由器丢弃,返回 ICMPv6 "Packet Too Big"
  • 源主机收到后,自己分片(通过 Fragment 扩展首部)

为什么这样设计?

  • 路由器转发速度是核心指标,分片消耗 CPU
  • IPv6 要求所有链路至少支持 1280 字节,源主机至少能发 1280 字节的包

Path MTU Discovery(路径 MTU 发现)

经典 PMTUD(RFC 1191 / 1981)

原理:发送方故意设 DF=1,探测路径最小 MTU。

主机 A 发送 1500 字节包,DF=1
  ↓
路由器 R 发现下一跳 MTU=1400,但 DF=1 不能分片
  ↓
R 丢弃包,返回 ICMP "需要分片但 DF 置位"(Type 3, Code 4)
  ↓
A 收到 ICMP,得知路径 MTU < 1500
  ↓
A 尝试 1400 字节,DF=1
  ↓
如果还收到 ICMP,继续降;如果成功,1400 就是路径 MTU

PMTUD 的黑洞问题

很多防火墙为了安全,丢弃所有 ICMP(包括"需要分片")。结果是:

  • 发送方发大包,被中间路由器丢弃
  • 发送方永远收不到 ICMP,不知道要降 MTU
  • TCP 连接建立后,大包一直丢,小 ACK 能通,连接"半死不活"

生活例子:你寄一个大箱子,快递员说"太大了,换小箱子",但这条消息被门卫截获了。你一直没收到通知,以为箱子在路上,其实早被扔了。

PLPMTUD(Packetization Layer PMTUD,RFC 4821)

不依赖 ICMP,由 TCP 层主动探测:

  1. TCP 发送不同大小的探测包(带 DF=1)
  2. 如果某个大小丢包,就降一级再试
  3. 通过"是否收到 ACK"推断 MTU,而不是等 ICMP

现代操作系统(Windows Vista+、Linux 2.6.30+)默认启用 PLPMTUD。

经典 PMTUD 的致命弱点是依赖 ICMP 返回。一旦中间防火墙丢弃 ICMP(很多企业网/云环境默认如此),发送方永远不知道要降 MTU,TCP 连接表现为"能建立但传大数据就卡住"。PLPMTUD 通过"是否收到 ACK"推断 MTU,彻底摆脱对 ICMP 的依赖。

本篇小结

  • MTU 是链路层最大帧长,以太网 1500 字节
  • MSS = MTU - 40(IP+TCP 头),TCP 握手协商,避免 IP 分片
  • IPv4 路由器可分片,目的主机重组;IPv6 仅源主机分片
  • PMTUD 通过 DF=1 + ICMP 探测路径最小 MTU
  • ICMP 黑洞导致 PMTUD 失效,PLPMTUD 通过丢包推断解决
  • 分片有风险:丢一片全报废、防火墙绕过、重组开销

动手实践

  1. 观察本机 MTU:

    # Linux
    ip link show eth0
    # Windows
    netsh interface ipv4 show subinterfaces
    
  2. 强制分片测试:

    # Windows:ping -f -l 3000 8.8.8.8(DF=1,包大小 3000)
    # Linux:ping -M do -s 3000 8.8.8.8
    

    观察是否收到 "Fragmentation needed" 或请求超时

  3. Wireshark 抓分片包,观察 Identification、MF、Fragment Offset 字段

  4. 思考:为什么 IPv6 要求所有链路至少支持 1280 字节?如果某链路只支持 576 字节(如早期拨号),IPv6 怎么办?

上一页
IPsec 安全扩展
下一页
ICMP 与 Traceroute 原理