IPv4 头只有 20 字节,每个字节都有用。这篇抓一个真实的包,按字节拆开每个字段并验算校验和,再看 TTL、TOS、协议号和 IP 选项在真实流量里的样子。
版本 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 字节。
收到的包(来自公网):校验和 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 → 抓到包头第 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 头 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。