--- tags: [计算机网络, IPv4, IP首部, TTL, MTU, 分片, ECN, PMTUD, IP Options] create time: 2026-05-17 23:40 --- # IPv4 首部与分段重组 ## 概述 IPv4 数据包是互联网的基本传输单元。理解其首部结构,有助于排查 MTU 问题、分析抓包工具输出、以及理解 IP 级故障恢复机制。 ## IPv4 首部格式(32-bit fields) ``` Version(4b) │ IHL(4b) │ DSCP(6b) │ ECN(2b) │ Total Length(16b) │ ├───────────────────────────────────────────────────────────────────────┤ │ Identification(16b) │Rsv│DF│MF│ Offset(13b) │ ├───────────────────────────────────────────────────────────────────────┤ │ TTL(8b) │ Protocol(8b) │ Header Checksum(16b) │ ├───────────────────────────────────────────────────────────────────────┤ │ Source Address (32b) │ ├───────────────────────────────────────────────────────────────────────┤ │ Destination Address (32b) │ ├───────────────────────────────────────────────────────────────────────┤ │ Options (variable, 0-40B) │ │ ...padding... │ └───────────────────────────────────────────────────────────────────────┘ ``` ### 各字段详解 | 字段 | 大小 | 说明 | |------|------|------| | **Version** | 4 bit | 版本号,IPv4 = `0100` | | **IHL** (Internet Header Length) | 4 bit | 首部长度,单位 4 bytes。**最小值 5**(20 bytes),最大 15(60 bytes) | | **DSCP** (Differentiated Services Code Point) | 6 bit | QoS 优先级标记,替代早期的 ToS 字段前 6 位 | | **ECN** (Explicit Congestion Notification) | 2 bit | 显式拥塞通知。末 2 bit 用于在不丢包的情况下通知端主机网络拥塞(`00`=不支持, `10`/`01`=支持 ECN, `11`=拥塞已经历) | | **Total Length** | 16 bit | 整个 IP 包总长(含首部),最大 65535 字节 | | **Identification** | 16 bit | 唯一标识符。同一原始包的分片共享此 ID | | **Reserved** | 1 bit | 保留位,必须为 0 | | **DF** (Don't Fragment) | 1 bit | 置 1 则禁止分片;路由器不能分片的包如果 DF=1,直接丢弃并返回 ICMP "Fragmentation Needed" | | **MF** (More Fragments) | 1 bit | 置 1 表示后面还有分片;最后一个分片 MF=0 | | **Fragment Offset** | 13 bit | 该分片在原始数据中的偏移量(以 8 字节为单位) | | **TTL** (Time To Live) | 8 bit | 生存时间,每经过一个路由器减 1,归零时丢弃并发送 ICMP Timeout | | **Protocol** | 8 bit | 上层协议编号:TCP=6, UDP=17, ICMP=1, IGMP=2 | | **Header Checksum** | 16 bit | 仅校验首部的 CRC,**不校验 payload** | | **Source/Dest Address** | 各 32 bit | 源和目的 IP 地址 | ### 最小 vs 最大 IP 包 ``` 最小 IP 包: 20B 首部 + 0B 数据 = 20 bytes 最小以太网帧承载: 20B 首部 + 8B ICMP Echo = 28 bytes → 加上 14B EthHdr + 4B FCS = 46 bytes ≥ MTU 下限 ✅ 最大 IP 包: 20B 首部 + 65515B 数据 = 65535 bytes 受限于链路 MTU: 通常 1500B Payload → 最大常用包 1520 bytes ``` > [!tip] 为什么 IHL 最大值是 15? > IHL 以 4 字节为单位,最大值 15 × 4 = 60 bytes。去掉固定首部 20 bytes,Options 最多 **40 bytes**。这就是为什么 Options 字段上限是 40B。 ### Options 字段(可选,0-40 bytes) Options 是 IPv4 首部中最容易被忽略的部分,但在网络诊断和特殊场景中依然重要: | 选项类型 | 说明 | 典型用途 | |---------|------|---------| | **Record Route** | 记录包经过的每一跳路由器 IP | 路由追踪(类似 traceroute) | | **Timestamp** | 每个路由器记录到达时间戳 | 测量延迟、时钟同步分析 | | **Strict Source Route** | 指定完整路径,逐跳严格匹配 | 诊断路由问题(现代网络几乎禁用) | | **Loose Source Route** | 指定必须经过的中间节点,其余自由路由 | 绕路测试 | > [!warning] 安全注意 > 现代路由器和防火墙通常**丢弃包含 Options 的 IP 包**,因为 Source Route 曾被用于 IP 欺骗攻击。这也是为什么正常流量中你几乎看不到 Options。 ### Header Checksum 逐跳重算 与 L2 CRC 和 TCP/UDP Checksum 不同,IP Header Checksum **每经过一个路由器都要重新计算**——因为 TTL 会递减。 ```mermaid flowchart LR A["路由器收到包"] --> B["TTL -= 1"] B --> C["重新计算 Checksum"] C --> D["转发到下一跳"] style A fill:#87CEEB,color:#000 style D fill:#98FB98,color:#000 ``` > [!question] 为什么 IP Checksum 只校验首部? > 早期设计为减轻路由器负担——只校验 20 bytes 首部远比校验整个 64KB 包高效。数据完整性由上层协议(TCP/UDP Checksum)保证。这也是 IP 被称为"尽力而为"协议的原因之一。 ## TTL 深度解析 ### TTL 的常见误解 > [!question] TTL 是时间吗? > 很多人以为 TTL 的单位是秒——这其实是 IPv6 的 Hop Limit 的前身历史遗留误解。在 IPv4 中 TTL 是**跳数计数器**——每经过一个路由器(三层转发节点)减 1,不是按时间递减。名字容易误导,但行为很简单:减到 0 就丢弃。 ```mermaid flowchart LR A["Source
TTL=64"] -->|"路由器减1"| B["TTL=63"] B -->|"路由器减1"| C["TTL=62"] C -->|"路由器减1"| D["TTL=61"] D -->|"到目的地"| E["TTL=60
继续处理"] style A fill:#DDA0DD,color:#000 style E fill:#98FB98,color:#000 ``` ### 查看 TTL 的实践 ```bash # ping 默认从 64 或 128 开始,减去到达时的 TTL ≈ 经过了跳数 $ ping -c 1 example.com PING example.com (93.184.216.34): 56 data bytes 64 bytes from ... icmp_seq=0 ttl=55 time=23.1 ms # 64 - 55 = 9 跳 # Windows 默认 TTL=128 $ tracert example.com 1 1 ms 1 ms 1 ms 192.168.1.1 2 2 ms 2 ms 2 ms 10.0.0.1 ... 9 23 ms 23 ms 23 ms 93.184.216.34 ``` ### Linux 系统初始 TTL ```bash $ sysctl net.ipv4.ip_default_ttl net.ipv4.ip_default_ttl = 64 # macOS / Windows 默认 128 # 某些嵌入式设备可能用 32 或 60 ``` ## MTU 与分片(Fragmentation) ### 什么是 MTU? MTU(Maximum Transmission Unit)是链路层允许的最大 IP 包载荷大小。超出 MTU 的包需要被**分片**。 | 链路类型 | 典型 MTU | Jumbo Frame | |----------|---------|-------------| | Ethernet | 1500 bytes | 9000 bytes | | Wi-Fi (802.11n/ac/ax) | 1500 | ❌ 不支持 | | PPPoE (宽带拨号) | 1492 (8B PPPoE 头开销) | — | | IPv6 | 1280 (硬性最小 MTU) | 可选 | | GRE 隧道 | 1476 (24B GRE 头开销) | — | | VXLAN | 1450 (50B VXLAN 头开销) | — | ### 分片规则 当 IP 包 > MTU 且 DF=0 时,路由器将其切分为多个分片: ``` 原始 IP 包 (Payload = 3000 bytes) ── MTU=1500 ──→ 分片 1: Headers + [Bytes 0..1479] Offset=0 MF=1 分片 2: Headers + [Bytes 1480..2959] Offset=185*8=1480 MF=1 分片 3: Headers + [Bytes 2960..2999] Offset=370*8=2960 MF=0 (最后一片) ↑ offset × 8 = 实际偏移 ``` **关键限制:** 除最后一个分片外,每个分片的 payload 必须是 **8 字节的整数倍**。这就是为什么 offset 字段以 8 字节为单位。 ### 分片的优缺点 | 优点 | 缺点 | |------|------| | 允许大应用层数据穿越小 MTU 链路 | 增加路由器的 CPU 负担 | | 透明(对应用层无感知) | 任一丢包导致整个原始包失效 | | — | 攻击者可以利用分片做碎片攻击(Teardrop 等) | | — | IPv6 已取消中间路由器分片,改由端到端 PMTUD | ### Wireshark 中识别分片 实战排查时,抓包识别分片是最常见需求: ```bash # tcpdump 过滤分片包(offset > 0 或 MF=1 的包) $ tcpdump -i eth0 'ip[6:2] & 0x3fff != 0' -nn # Wireshark 显示过滤器 # ip.frag_offset > 0 → 非首片 # ip.flags.mf == 1 → 还有后续分片 # ip.id == 0x1234 → 按 Identification 追踪同组分片 ``` > [!tip] 分片重组技巧 > Wireshark 默认会自动重组分片。如果需要查看原始分片:`Edit → Preferences → IPv4 → 去勾选 "Reassemble fragmented IPv4 datagrams"`。 ### 常见分片攻击 | 攻击手法 | 原理 | 防御 | |---------|------|------| | **Teardrop** | 构造重叠偏移的分片,重组时内核缓冲区溢出 | 更新内核补丁(已修复) | | **Ping of Death** | 分片重组后 IP 包 > 65535 bytes,触发整数溢出 | 现代系统已防御 | | **Rose Attack** | 发送大量不完整的分片,消耗重组缓冲区 | 设置重组超时、限制并发分片数 | | **Fragment Overlap** | 后续分片覆盖前片数据,绕过基于首片的防火墙规则 | 防火墙需做完整分片重组后再检测 | ### PMTUD(Path MTU Discovery) 现代网络推荐使用 **路径 MTU 发现**而非分片: > [!warning] PMTUD Black Hole > 如果中间路由器**静默丢弃**了 DF=1 的大包,但不返回 ICMP,源主机会一直重传失败——这就是 PMTUD Black Hole。常见于错误配置的防火墙(拦截了 ICMP "Fragmentation Needed")。TCP 的对策是 `net.ipv4.tcp_mtu_probing`(值设为 2),主动探测合适的 MSS。 ```mermaid sequenceDiagram participant Src as 源主机 participant R as 瓶颈路由器(MTU=1400) participant Dst as 目标服务器 Src->>Dst: IP Packet (size=1500, DF=1) Note over Src: DF(Do Not Fragment)=1 R->>R: MTU=1400 < 1500, DF=1 → 无法分片! R->>Src: ICMP Type 3 Code 4 "Fragmentation Needed" Note over Src: Path MTU = 1400, Max IP Payload = 1400 - 20(IP头) = 1380 Src->>Dst: IP Packet (size=1400, DF=1) ✅ Dst-->>Src: ACK ✅ ``` ```bash # 检查当前 PMTUD 状态 $ ip route show | grep mtu default via 192.168.1.1 dev eth0 mtu 1500 # Linux 内核 PMTUD 控制 $ sysctl net.ipv4.tcp_mtu_probing net.ipv4.tcp_mtu_probing = 0 # 0=关闭, 1=低速率, 2=始终开启 ``` ## 协议编号对照表 | Protocol 值 | 名称 | 用途 | |------------|------|------| | 1 | ICMP | 网络诊断(ping、traceroute) | | 2 | IGMP | 组播管理 | | 6 | TCP | 可靠面向连接传输 | | 17 | UDP | 无连接不可靠传输 | | 47 | GRE | 虚拟私有隧道 | | 50 | ESP | IPsec 加密封装安全载荷 | | 51 | AH | IPsec 认证头 | | 89 | OSPF | 动态路由协议 | ## 关联笔记 - [[hhs/NETWORK/CIDR与子网划分]] — IP 地址的子网分配方法 - [[hhs/NETWORK/NAT原理与应用]] — NAT 如何处理 IP 包的 Checksum - [[hhs/NETWORK/ICMP与Ping-Traceroute]] — ICMP 协议细节