IPv4 数据报首部解析
IP 数据报的结构
IP 数据报就像一封标准格式的快递单 + 货物:
- 首部(Header):20~60 字节的"快递单信息",包含收发地址、包裹大小、优先级等
- 数据(Payload):上层协议(TCP/UDP/ICMP)的内容,即"货物本身"
+------------------+------------------+
| IP 首部 | 数据(TCP段) |
| 20~60 字节 | 可变长度 |
+------------------+------------------+
## 固定首部 20 字节逐字段解析
把 20 字节展开成 5 行 × 32 位(每行 4 字节),这是 RFC 791 的标准画法:
0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |Version| IHL |Type of Service| Total Length | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Identification |Flags| Fragment Offset | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Time to Live | Protocol | Header Checksum | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Source Address | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Destination Address | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
### 1. Version(版本,4 位)
固定为 `0100`(即 4),表示 IPv4。IPv6 这里是 `0110`(6)。
### 2. IHL(首部长度,4 位)
表示首部有多少个 **32 位字(4 字节)**。最小值 5(5×4=20 字节),最大值 15(15×4=60 字节)。
如果首部有选项,IHL > 5。例如 IHL=6 表示首部 24 字节(20 字节固定 + 4 字节选项)。
### 3. TOS / DSCP / ECN(8 位)
这 8 位经历了三代演变:
**第一代:TOS(Type of Service,RFC 791)**
- 前 3 位:优先级(0~7,7 最高)
- 后 5 位:延迟/吞吐量/可靠性/开销请求
**第二代:DSCP + ECN(RFC 2474 / 3168)**
- 前 6 位:DSCP(区分服务代码点),决定 QoS 队列
- 后 2 位:ECN(显式拥塞通知)
**DSCP 常用值**:
| DSCP 值 | 名称 | 用途 |
|---------|------|------|
| 0 | BE(Best Effort)| 尽力而为,默认 |
| 46 | EF(Expedited Forwarding)| 语音/视频,最高优先级 |
| 10 | AF11 | 高优先级数据 |
| 18 | AF21 | 中优先级数据 |
**ECN 值**:
- 00:不支持 ECN
- 01/10:ECT(ECN-Capable Transport)
- 11:CE(Congestion Experienced),路由器标记拥塞
```mermaid
flowchart LR
subgraph TOSField["TOS / Traffic Class 字段 (8位)"]
direction LR
DSCP["DSCP (6位)<br/>区分服务代码点"]
ECN["ECN (2位)<br/>显式拥塞通知"]
end
DSCP --> Class0["000000<br/>BE 尽力而为"]
DSCP --> Class46["101110<br/>EF 语音优先"]
DSCP --> Class10["001010<br/>AF11 高优先级数据"]
ECN --> E00["00<br/>不支持"]
ECN --> E01["01/10<br/>ECT 支持"]
ECN --> E11["11<br/>CE 经历拥塞"]
style DSCP fill:#99ccff,stroke:#333
style ECN fill:#ffcc99,stroke:#333
style Class46 fill:#99ff99,stroke:#333,stroke-width:2px
style E11 fill:#ff9999,stroke:#333
这 8 位经历了三代演变:最初是 TOS(RFC 791),后来演进为 DSCP+ECN(RFC 2474/3168)。DSCP 决定包进入哪个 QoS 队列,ECN 让路由器在不丢包的情况下标记拥塞,配合 TCP 降速。
4. Total Length(总长度,16 位)
首部 + 数据的总字节数,最大 65535 字节(2^16 - 1)。但以太网 MTU 只有 1500,所以实际很少超过 1500。
计算示例:
首部 20 字节 + TCP 数据 1460 字节 = Total Length = 1480
5. Identification(标识,16 位)
同一个数据报分片后的所有片段拥有相同的 Identification 值,就像快递单号。目的主机靠这个字段识别"这些碎片属于同一个包裹"。
6. Flags(标志,3 位)
| 位 | 名称 | 含义 |
|---|---|---|
| 第 1 位 | 保留 | 必须为 0 |
| 第 2 位 | DF(Don't Fragment) | 1=禁止分片 |
| 第 3 位 | MF(More Fragments) | 1=后面还有分片 |
DF=1 的典型场景:
- Path MTU Discovery:发送方故意设 DF=1,如果中间路由器需要分片但 DF=1,就丢弃并返回 ICMP "需要分片但 DF 置位",发送方据此探测路径 MTU。
7. Fragment Offset(片偏移,13 位)
表示该分片在原始数据报中的位置,单位是 8 字节。例如 Offset=185 表示该分片从原始数据的第 185×8=1480 字节开始。
第一个分片的 Offset=0,MF=1(后面还有)最后一个分片的 MF=0,Offset>0不分片的包 MF=0,Offset=0
8. TTL(生存时间,8 位)
每经过一个路由器减 1,到 0 时丢弃并返回 ICMP Time Exceeded。初始值由操作系统决定:
- Windows:128
- Linux:64
- 某些 Unix:255
Traceroute 就是利用 TTL 递增来探测路径的。
9. Protocol(协议,8 位)
标识上层协议类型,常见值:
| 值 | 协议 |
|---|---|
| 1 | ICMP |
| 2 | IGMP |
| 6 | TCP |
| 17 | UDP |
| 41 | IPv6(6in4 隧道) |
| 47 | GRE |
| 50 | ESP(IPsec) |
| 51 | AH(IPsec) |
10. Header Checksum(首部校验和,16 位)
只校验首部,不校验数据。每经过一个路由器,TTL 减 1,校验和必须重新计算(因为 TTL 变了)。
IPv6 取消了首部校验和,因为链路层(以太网 CRC)和传输层(TCP/UDP 校验和)已经有足够校验,路由器重新计算校验和浪费 CPU 周期。
11. Source / Destination Address(各 32 位)
收发双方的 IPv4 地址。
选项字段(0~40 字节)
IHL 最大 15(60 字节),固定首部 20 字节,所以选项最多 40 字节。常见选项:
| 选项类型 | 用途 | 现状 |
|---|---|---|
| 记录路由 | 让每个路由器写入自己的 IP | 已废弃,安全/性能问题 |
| 时间戳 | 记录经过每个路由器的时间 | 已废弃 |
| 严格源路由 | 必须按指定路径逐跳转发 | 已废弃,被用于攻击 |
| 松散源路由 | 必须经过指定路由器,其余自由 | 已废弃 |
| 路由器警告 | 提示路由器需要检查该包 | RSVP / IGMP 仍在使用 |
现代互联网中,选项字段几乎不用,很多路由器遇到带选项的包会直接丢弃或慢路径处理。
Wireshark 实战:解读一个真实包
Internet Protocol Version 4, Src: 192.168.1.10, Dst: 8.8.8.8
0100 .... = Version: 4
.... 0101 = Header Length: 20 bytes (5)
Differentiated Services Field: 0x00 (DSCP: CS0, ECN: Not-ECT)
Total Length: 60
Identification: 0x1a2b (6699)
Flags: 0x4000, Don't fragment
0... .... .... .... = Reserved bit: Not set
.1.. .... .... .... = Don't fragment: Set
..0. .... .... .... = More fragments: Not set
Fragment offset: 0
Time to live: 128
Protocol: ICMP (1)
Header checksum: 0x1234 [validation disabled]
Source: 192.168.1.10
Destination: 8.8.8.8
解读:
- 版本 4,首部 20 字节
- 总长度 60 字节,数据部分 40 字节(ICMP Echo Request)
- DF=1,不允许分片(Windows 默认 ping 设 DF)
- TTL=128,Windows 主机发出
- Protocol=1,上层是 ICMP
本篇小结
- IPv4 首部固定 20 字节 + 选项最多 40 字节
- IHL 以 4 字节为单位;Total Length 以字节为单位
- Identification + Flags + Fragment Offset 管理分片
- TTL 防环,每跳减 1;Protocol 标识上层协议
- 首部校验和只覆盖首部,路由器需重新计算
- DSCP/ECN 在 TOS 字段中实现 QoS 和拥塞通知
- 选项字段现代已很少使用
动手实践
Wireshark 抓一个 ping 包,逐字段对照 RFC 791 的首部图
计算:如果 IHL=5,Total Length=1500,数据部分多少字节?
观察不同操作系统的 TTL 初始值:
ping 8.8.8.8看返回的 TTL(Linux 约 64,Windows 约 128)- 用
64 - 返回TTL或128 - 返回TTL估算经过了几跳
Wireshark 过滤
ip.dsfield.dscp == 46,观察是否有 EF 标记的包(企业网络中可能有语音流量)