Appearance
IPv4 数据报格式
2026 大纲 四(三)1 IPv4 分组。分片的计算规则(每片装多少、片偏移怎么递推、重装怎么判齐)全部在《IP 分片与重装》,本篇只到"哪三个字段与分片有关"为止。
一、首部里有什么
前面几篇讲的都是网络层"做什么"——选路、转发、编址。这一篇看它把这些约定写在哪里:IP 数据报的首部。
首部 = 固定部分 20 字节 + 可选部分 0~40 字节,且必须是 4 字节的整数倍(不足处用 0 填充)。为什么非要 4 字节对齐,下一节会看到它直接决定了某个字段的宽度。
| 字段 | 位数 | 单位 | 最小 → 最大 | 关键动作 |
|---|---|---|---|---|
| 版本 | 4 | — | 4(IPv4)/ 6(IPv6) | 直接看值判 IP 版本 |
| 首部长度 IHL | 4 | 4 B | 5 × 4 = 20 B → 15 × 4 = 60 B | 已知 IHL = n → 实际首部 = 4n 字节 |
| 区分服务 | 8 | — | — | 指示期望的服务质量,一般不用 |
| 总长度 | 16 | 1 B | 20 → 65535 B | 数据部分长度 = 总长 − 4 × IHL |
| 标识 | 16 | — | 0 → 65535 | 同一原报文的所有分片共享同一标识 |
| 标志 DF/MF | 1+1 | — | 0 / 1 | 最高位保留置 0;DF=1 不分片;MF=1 后面还有分片 |
| 片偏移 | 13 | 8 B | 0 → (2¹³−1)×8 = 65528 B | 片偏移值 × 8 = 在原报文数据部分中的字节起点 |
| TTL | 8 | 跳 | 0 → 255 | 每跳减 1,到 0 丢弃并发 ICMP 超时 |
| 协议 | 8 | — | 见下 | ICMP 1 / IGMP 2 / TCP 6 / EGP 8 / IGP 9 / UDP 17 / IPv6 41 / OSPF 89 |
| 首部校验和 | 16 | — | — | 16 位字反码求和;每跳重算 |
| 源 / 目的 IP | 各 32 | — | — | 端到端不变(NAT 例外),而 MAC 地址逐跳都换 |
表里有两个陷阱要先按住。
首部长度的单位不是字节。 字段值 × 4 才是字节数:值 7 → 28 字节(含 8 字节可选部分);反过来题目给"首部 28 字节",填进去的是 7。值小于 5 的数据报一定非法,收到就该丢弃并发 ICMP 参数问题报文。
由此推出:IHL 不等于 5 时算数据长度必须老实乘 4,即"数据部分 = 总长度 − 4 × IHL"。粗心用"总长度 − 20"会得到 1480 这种错得毫无违和感的数。
总长度含首部,MTU 限制的也是总长度。 以太网上一个不分片的数据报,总长度上限就是 1500,数据部分只有
顺带澄清另一个常被当成 MTU 的数字:576 不是 MTU。它是 IP 层的规定——"所有主机和路由器必须能接收"的最小长度,来历是 512(上层数据的合理下限)+ 60(最长 IP 首部)+ 4(富余)。1500 是以太网这一种链路的 MTU。两者层次不同。
标识字段也不是序号。 IP 是无连接的,数据报本来就允许乱序到达,编序号没有意义。标识唯一的用途是把同一个原始数据报的分片认出来。
交互可视化
二、三个长度字段的单位为什么互不相同
这是本篇最值得推一遍的地方——三个字段各自被不同的约束逼到了不同的单位上:
| 字段 | 位宽 | 单位 | 能表示的最大值 | 单位是被什么逼出来的 |
|---|---|---|---|---|
| 首部长度 | 4 | 4 字节 | 4 位若按 1 字节算最长只能描述 15 字节,连固定部分都装不下;而首部本来就 4 字节对齐,取 4 当单位长度永远是整数 | |
| 总长度 | 16 | 1 字节 | 16 位够宽,不需要放大;且数据长度必须精确到字节,放大就表示不出奇数长度 | |
| 片偏移 | 13 | 8 字节 | 比总长度还窄 3 位,却要覆盖同样大的空间,只能按 8 放大 |
一句话串起来:位宽不够就把单位放大,位宽够用就保持字节精度。
所以"4 位字段"和"4 字节对齐"是互为因果的一对设计:先决定首部按 32 位字对齐,才敢把长度字段压到 4 位。
顺带一个边界:片偏移最大 65528,总长度最大 65535,两者并不相等也不矛盾——片偏移指向的是数据部分中的位置,而 65535 里还含 20~60 字节的首部。
三、TTL:从"秒"到"跳"的改名史
TTL 的字面意思是 Time To Live,最初确实以秒为单位:每经过一台路由器,减去数据报在这台路由器上消耗掉的秒数,不足 1 秒就减 1。后来路由器越来越快,转发耗时远小于 1 秒,"减去消耗的秒数"永远退化成"减 1",于是标准把功能改成跳数限制,名字没改。
由此得到现在的口径:单位是跳不是秒;8 位 → 最多经过 255 台路由器;初值设为 1 表示"只能在本局域网内传送"——送到本网路由器,转发前减到 0 就被丢掉。这个性质正是 traceroute 逐跳探测的基础(见 ICMP 协议)。常见默认值:Linux 64,Windows 128。
四、源地址与目的地址:什么在变,什么不变
这两个字段各 32 位,是网络层"端到端"语义的载体:一个分组从源走到目的,中间换了十几次帧头,IP 首部里的源地址与目的地址一个字节都不变(NAT 是唯一的例外,而它正因此被批评破坏了层次)。反过来,链路层的 MAC 地址逐跳都换——它只在一段链路上有意义。
这两个字段里还能放一些特殊值,而特殊地址的分界线是"它能出现在源地址栏,还是目的地址栏":
0.0.0.0只能做源。 它表示"本网络上的本主机",是主机还没拿到 IP 时(比如发 DHCP Discover 那一刻)唯一能填的源地址;拿它当目的地址无从投递。255.255.255.255只能做目的。 它是受限广播地址,发送者不可能是"所有主机"。127.0.0.1两者皆可。 环回地址压根不出主机,源和目的都是它自己。- 普通单播地址两者皆可。
五、首部校验和
采用 16 位字的反码算术求和(不是 CRC,也不是普通二进制加法)。
发送方:校验和字段置 0 → 首部划成若干 16 位字 → 反码算术求和(普通二进制相加,最高位产生的进位加回最低位,称回卷)→ 把和取反码写入校验和字段。 接收方:把首部(这次包含已填好的校验和字段)同样求和 → 取反码 → 结果为 0 则首部未变,否则丢弃。
顺带说清一个看似矛盾的说法:校验通过究竟是全 1 还是全 0。反码求和之后、取反之前是全 1(0xFFFF);再取反之后是全 0(0x0000)。教材判据"此结果必为 0"说的是取反那一步,两种说法讲的是同一件事的前后两步。
为什么只管首部、不管数据。 IP 层的立场是"数据的正确性由端到端的上层负责"——TCP、UDP 的校验和覆盖了伪首部 + 首部 + 数据,链路层还有 CRC,网络层再算一遍数据是重复劳动。
为什么它必须便宜。 首部里有字段每一跳都会变(TTL 每跳减 1,做了分片则标志与片偏移也变),字段一变校验和就必须重算——这件事发生在每台路由器的转发热路径上,一秒要做几百万次。所以它不能用 CRC 这类复杂算法,只能用加法器就能干完的反码求和,而且只对 20 字节的首部做。
这条推理的下半截就是 IPv6 的动机:既然逐跳重算是纯开销,而下层有 CRC、上层有 TCP/UDP 校验和,那干脆整个字段砍掉(见 IPv6 基础)。
图里要抓住的一点:校验和字段的值在每一跳都不同。它保护的是"从上一跳到这一跳"这一段,不是端到端。
首部校验和的完整逐步算例,含接收方验证与过一跳后的重算(第一次学、或想亲手把回卷走一遍时展开)
某 IPv4 数据报首部固定 20 字节:版本 4,首部长度 5,区分服务 0,总长度 40,标识 0x2B7A,标志与片偏移全 0,TTL 32,协议 17(UDP),源地址 192.168.3.17,目的地址 10.2.0.5。
第 1 步:拼成 10 个 16 位字,校验和字段先置 0。 反码求和的操作单位是 16 位字;校验和字段自己参与计算会自我引用,所以先置 0。
| 序号 | 内容 | 十六进制 |
|---|---|---|
| 1 | 版本 4 | IHL 5 | TOS 0 | 4500 |
| 2 | 总长度 40 | 0028 |
| 3 | 标识 | 2B7A |
| 4 | 标志+片偏移 | 0000 |
| 5 | TTL 32 | 协议 17 | 2011 |
| 6 | 校验和(置 0) | 0000 |
| 7 | 源地址高 16 位(192.168) | C0A8 |
| 8 | 源地址低 16 位(3.17) | 0311 |
| 9 | 目的地址高 16 位(10.2) | 0A02 |
| 10 | 目的地址低 16 位(0.5) | 0005 |
拼字节的小验算:
第 2 步:反码求和,边加边回卷。 只要和超过 16 位,就把溢出的那一位加回最低位——这就是反码算术与普通加法唯一的区别。
第 3 步:取反码得到校验和。 标准规定填入的是和的反码,这样接收方"连校验和一起加"才会得到全 1。
第 4 步:接收方验证。 把第 6 字换成 0xA18B 重新求和:0x0000 → 校验通过。正好演示了"取反前全 1、取反后全 0"。
第 5 步:经过一台路由器之后。 TTL 由 32 减为 31,第 5 个 16 位字从 0x2011 变成 0x1F11,重算得到新校验和 0xA28B,与原来的 0xA18B 不同。首部只要动一个字节,校验和就是另一个值——所以它只能每跳重算,不能沿途沿用。
六、可选部分
长度 1~40 字节,用于排错、测量、安全、源路由、记录路由、时间戳等。多个选项一个接一个拼接、中间没有分隔符,最后用全 0 的填充字段补齐到 4 字节的整数倍。
实际上这些选项很少被使用,代价却是首部长度可变、每台路由器转发时都要多做一次判断。收益太小,所以 IPv6 索性把首部做成固定 40 字节,把可选功能挪进扩展首部。
本节小结
- 先定对齐再定字段宽度:首部按 4 字节对齐 → 首部长度字段才敢只占 4 位、单位取 4 字节,范围 20~60 B;总长度 16 位够宽故保持 1 字节精度(上限 65535),片偏移只有 13 位却要覆盖同样大的空间,只能放大到 8 字节(上限 65528)。位宽不够就放大单位,这是三个单位互不相同的唯一原因。
- 可靠性的分工决定了校验和的形态:数据交给上层的 TCP/UDP 校验和与下层的链路 CRC,网络层只保首部;而 TTL 每跳变、分片字段可能变,校验和每跳都得重算,所以只能用 16 位字反码求和这种加法级算法——发送方填"和的反码",接收方"连校验和一起加再取反得 0"。
- 把这份逐跳开销推到极限就是取消:IPv6 删掉校验和字段、把首部固定成 40 字节、把选项挪进扩展首部。IPv4 首部里"可变"带来的所有麻烦,IPv6 都是这么处理的。
考点速记
IP 首部在真题里被考过的形式有两种:一种问某个特殊地址能出现在哪个字段里,另一种直接给一段十六进制让你去数字节。
① 只能做源地址、不能做目的地址的是哪个(cn-2017-36)。四个候选 0.0.0.0、127.0.0.1、200.10.10.3、255.255.255.255,答 A:0.0.0.0。逐个判:0.0.0.0 表示"本网络上的本主机",是主机还没拿到 IP 时唯一能填的源地址,拿它当目的地址无从投递;255.255.255.255 正好相反,它是受限广播、只能做目的;127.0.0.1 环回和 200.10.10.3 普通单播两者皆可。
判据只有一条:这个地址代表的是"一个说不清是谁的发送方",还是"一群没法当发送方的接收方"。 前者只能做源,后者只能做目的。
② 从一段以太网帧的十六进制里读出 IP 首部(cn-2011-47,9 分)。这是把首部格式考到底的一道题——给出一个以太网帧的前 80 字节 dump,要你自己数偏移。
第 (1) 问要 Web 服务器的 IP。关键是把两层的偏移叠起来:以太网帧头是 14 字节(6 目的 MAC + 6 源 MAC + 2 类型),所以 IP 首部从帧的第 14 字节开始;而目的 IP 在 IP 首部内的偏移是 16~19,于是在帧里是 14 + 16 = 30~33 字节。取出这四个字节 40 aa 62 20 转十进制 → 64.170.98.32。
同一问还要默认网关的 MAC。帧的前 6 字节是目的 MAC,而这个帧是要发往 Internet 的(跨网段),所以目的 MAC 必然是默认网关的 MAC,不是 Web 服务器的——跨网段时主机根本不知道远端的 MAC,也不该用。答 00-21-27-21-51-EE。
第 (4) 问:这个分组经 NAT 路由器 R 转发时,IP 首部里哪些字段会被修改。答 源 IP 地址、TTL、首部校验和三个。推法就是本篇第五节那条链:TTL 每跳减 1 → 首部变了 → 校验和必须重算;而 R 是 NAT 路由器,还要把私有源地址换成公网地址。注意目的 IP 不变——出方向 NAT 只改源。
编者注(口径):标准答案给的是完整口径,即"若触发分片,总长度、标志、片偏移也会改"。本题这个数据报的总长度字段是
0x01EF= 495 B,小于 1500,实际不需要分片,所以那三项在本例中不变。
本篇练习区里还会出现另外九道题,考的是 IP 编址、子网划分与分片:cn-2010-37、cn-2011-38、cn-2017-38、cn-2019-37、cn-2022-35、cn-2023-39 讲在 IP 编址与子网划分;cn-2018-47 与 cn-2021-36 的分片计算讲在 IP 分片与重装;cn-2023-40 的 IPv4 与 IPv6 对照讲在 IPv6 基础。
易错:首部长度字段的单位是 4 字节。数据部分 = 总长度 − 4 × IHL,别顺手写成"总长度 − 20"。
易错:
0.0.0.0只能做源,255.255.255.255只能做目的,环回和普通单播两者皆可。
易错:从帧 dump 里数字节要把两层偏移叠起来:以太网帧头 14 字节 + IP 首部内偏移。
易错:跨网段发送时,帧的目的 MAC 是默认网关的 MAC,不是最终目的主机的。
易错:普通路由器转发就要改 TTL 和首部校验和,NAT 路由器再多改一个源 IP。目的 IP 在出方向不变。
易错:标识字段不是序号。 IP 无连接、本来就允许乱序,标识只用于把同一原报文的分片认出来。
易错:576 不是 MTU,它是 IP 层规定的"必须能接收"的最小长度;1500 是以太网这一种链路的 MTU。
易错:校验和"取反前全 1、取反后全 0"是同一件事的前后两步,两种说法不矛盾。
教材出处
- 谢希仁《计算机网络》(第 8 版):IP 数据报格式与各字段含义,见印刷版 p136–p139(4.2.5 IP 数据报的格式)。首部长度字段"所表示数的单位是 32 位字长(1 个 32 位字长是 4 字节)……最小值是 5……最大值 1111 时……即 60 字节";总长度"指首部和数据之和的长度,单位为字节……数据报的最大长度为
字节";关于 576 字节:协议 IP"规定在互联网中所有的主机和路由器必须能够接受长度不超过 576 字节的数据报。这是假定上层交下来的数据长度有 512 字节(合理的长度),加上最长的 IP 首部 60 字节,再加上 4 字节的富余量,就得到 576 字节"。 - 同书 p138:TTL 的单位变迁——"最初的设计以秒作为 TTL 值的单位……后来就把 TTL 字段的功能改为'跳数限制'(但名称不变)……现在 TTL 的单位不再是秒,而是跳数……数据报能在互联网中经过的路由器的最大数值是 255。若把 TTL 的初始值设置为 1,就表示这个数据报只能在本局域网中传送"。标识字段的说明在前一页 p137:"IP 软件在存储器中维持一个计数器,每产生一个数据报,计数器就加 1,并将此值赋给标识字段。但这个'标识'并不是序号,因为 IP 是无连接服务,数据报不存在按序接收的问题"。
- 同书 p139:首部检验和——"这个字段只检验数据报的首部,但不包括数据部分。这是因为数据报每经过一个路由器,路由器都要重新计算一下首部检验和(一些字段,如生存时间、标志、片偏移等都可能发生变化)。不检验数据部分可减少计算的工作量";算法为"把 IP 数据报首部划分为许多 16 位字的序列,并把检验和字段置零。用反码算术运算把所有 16 位字相加后,将得到的和的反码写入检验和字段";接收方"把首部的所有 16 位字再使用反码算术运算相加一次。将得到的和取反码……若首部未发生任何变化,则此结果必为 0"。同页脚注给出协议字段值表(ICMP 1、IGMP 2、TCP 6、UDP 17、OSPF 89)。
- 同书 p140:可选部分——"选项字段用来支持排错、测量以及安全等措施……此字段的长度可变,从 1 字节到 40 字节不等……最后用全 0 的填充字段补齐为 4 字节的整数倍",以及"实际上这些选项很少被使用……因此新的 IP 版本 IPv6 就把 IP 数据报的首部长度做成固定的了"。
- 同书 p124 表 4-2「一般不指派的特殊 IP 地址」:按"源地址使用 / 目的地址使用"两列给出五种情形,本篇第四节那几条即依此重排。
相关知识
网络层的功能|IP 分片与重装|IP 编址|ICMP 协议|IPv6 基础