bigywater.tech
网络协议 · 9

IPv4 头部逐字段:版本、IHL、TOS、TTL、协议号与校验和

IPv4 头只有 20 字节,每个字节都有用。这篇抓一个真实的包,按字节拆开每个字段并验算校验和,再看 TTL、TOS、协议号和 IP 选项在真实流量里的样子。

45 00 03 d4
真实 IPv4 头的前 4 字节:版本 4、头长 20、TOS 0、总长 980
TTL 48 vs 64
从公网收到的包 TTL 与本机发出的包 TTL
94 04 00 00
IGMP 包头里的 Router Alert 选项,网段 20 秒里唯一出现的 IP 选项

固定 20 字节

实测 · 本机执行输出
  版本            4
  IHL(字节)       20
  TOS/DSCP+ECN  0x00
  总长            980
  id            52906
  标志            DF
  片偏移           0
  TTL           48
  协议            6
  校验和           0x2ebf
  校验和验算         通过
  源地址 / 目的地址 各 4 字节(公网 104.20.23.154 → 本机);头共 20 字节,载荷 960 字节(总长 − 头长)
  原始头字节: 45 00 03 d4 ce aa 40 00 30 06 2e bf 68 14 17 9a c0 a8 09 64

版本和头长共 1 字节(4 位 + 4 位),TOS 1 字节,总长 2 字节,id 2 字节,标志 + 片偏移 2 字节,TTL 1 字节,协议 1 字节,校验和 2 字节,源与目的地址各 4 字节。

校验和与 TTL

实测 · 本机执行输出
  收到的包(来自公网):校验和 0x2ebf,按反码求和验算 通过
  发出的包(本机发出):校验和 0xf0bd,验算 通过——真实网卡上 IP 头校验和是内核算好填上的,抓到时就是对的;(在 lo0 上则是 0,见第 1 篇)。TCP 校验和可能由网卡卸载,这里没有验证
  TTL:收到的包 48,发出的包 64。公网主机的初始 TTL 常见是 64 / 128 / 255,收到时 48 说明这个包大约经过了 16 个路由器(按初始值 64 推算,是估计,不是 traceroute 的实测)
  id:收到 52906,发出 0;标志:收到 DF,发出 DF。现代系统对 TCP 包设 DF 并让 id 为 0 或随机,id 只在分片重组时才有意义(见分片一篇)

收到和发出的 IP 头校验和都验算通过(在 lo0 上是 0);TTL 每过一个路由器减 1,从收到的 TTL 可以估计经过了多少跳(只是估计)。

TOS / DSCP

实测 · 本机执行输出
  设置的 TOS → 抓到包头第 2 字节: 0x00 → 0x00(DSCP=0, ECN=0), 0x28 → 0x28(DSCP=10, ECN=0), 0xb8 → 0xb8(DSCP=46, ECN=0)

高 6 位 DSCP、低 2 位 ECN;IP_TOS 设什么,头里就是什么,至于路由器是否尊重取决于网络策略。

IP 选项

实测 · 本机执行输出
  请求:IP 头 20 字节(IHL=5),总长 84;没有选项
  应答:IP 头 20 字节(IHL=5),总长 84;没有选项
  ping 输出: 64 bytes from <本机局域网IP>: icmp_seq=0 ttl=64 time=2.733 ms | 1 packets transmitted, 1 packets received, 0.0% packet loss
  结论:这台 macOS 的 ping -R 发出的包 IHL=5,没有任何 IP 选项,所以这里看不到 Record Route 选项;下一节在网段的真实流量里找到了带选项的包

IHL 大于 5 才有选项,极少见;这台机器的 ping -R 没有产生选项。

真实流量

实测 · 本机执行输出
  协议号: 6(TCP)×1841, 17(UDP)×714, 2(IGMP)×6, 1(ICMP)×4
  IHL(×4 字节)分布: 20 字节×2559, 24 字节×6——绝大多数包头都是 20 字节(没有选项)
  那 6 个 24 字节头的包:协议号都是 [2](IGMP),选项 4 字节: 94 04 00 00——选项类型 0x94 = Router Alert(复制标志 1、类 0、编号 20),告诉沿途路由器“这个包要看一下”,IGMP 就是这样做的
  最常见的 TTL 值: 64×1238, 50×721, 255×374, 1×62, 51×37, 52×35(64 = Linux / macOS 默认起点,128 = Windows,255 = 网络设备;小于起点的数字是经过的路由器扣掉的)

网段里绝大多数包头都是 20 字节;带选项的几乎只有 IGMP 的 Router Alert。协议号 6 / 17 / 1 / 2 分别是 TCP / UDP / ICMP / IGMP。

分步演示

IPv4 头部
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64(Apple 芯片)/ Python 3.14.8 / tcpdump(libpcap)/ scapy 2.8 / dpkt / dnspython / h2 / aioquic / OpenSSL / curl 8.7.1;抓包在 lo0(环回)上做,少数实验用真实网卡 en0(这台机器上 en0 是 Wi-Fi,tcpdump 看到的是系统转成以太网格式的帧,所以文中的“以太网头”都指这个 14 字节的头)或访问公网(会在文章里注明);需要丢包 / 延迟 / 限速时,用一个只作用于 lo0 端口 9400-9499 的 dummynet 辅助脚本(netlab);所有服务端都是本机自己起的进程;时间戳取自 tcpdump 的 -tt,数字每次运行会有小幅抖动。第一、二节在 en0 上抓向 example.com:80 的一次请求,公网地址是 example.com 的 104.20.23.154(本次解析结果);第三节在 lo0 上抓 UDP;第四节对默认网关 ping -R;第五节抓整个网段 20 秒,统计包头字段;TTL 推算跳数是估计
    • 没有验证:IP 选项在路由器上的处理(多数路径会丢弃带选项的包);真实的 Record Route / 时间戳选项(本机 ping 不产生);DSCP 在路由器上的实际处理;ECN 的使用(见 DSCP 与 ECN 一篇);IPv6 头(见 IPv6 一组)
    京ICP备2026067502号-1京公网安备11010502063487号