IP-in-IP 在原来的 IP 头前再加一个外层 IP 头,外层协议号是 4。GRE 在投递头和载荷之间再加一层 GRE 头,用 IPv4 投递时协议号是 47。两种封装都不加密载荷。截至 2026 年 10 月,内层生存时间在哪一步减少,两份规范也不相同。
外层头只把包送到隧道出口
一个可独立引用的定义句:IP-in-IP(RFC 2003)在原数据报前插入外层 IP 头。外层源和目的是隧道入口和出口,内层源和目的仍是原来的发送方和接收方。入口除下文所说的生存时间外,不改内层头。
外层版本字段是 4。协议字段是 4。这是 IPv4 里再套一份 IPv4,不是「任何 IP 套 IP」的统称。用 IPv6 做外层或内层,不在 RFC 2003 的头部格式里。
外层总长度覆盖外层头、内层头和载荷。外层生存时间由入口另选,取一个足以送到出口的值,不继承内层那个数。内层选项一般不复制到外层;入口可以为隧道这一段另加选项。
服务类型(TOS)从内层复制到外层。这一字节后来如何被差分服务和显式拥塞通知拆开解释,见 差分服务标记 和 显式拥塞通知。本文不把 1996 年的「复制 TOS」改写成现行隧道必须保留 ECN。
内层不允许分片时,外层的不允许分片位必须置位。内层允许分片时,外层可以置位,也可以不置。入口因此不能把一份已经要求路径 MTU 发现的内层包,在隧道里随意拆开。分片在哪一层重组,见 IP 分片。
生存时间在入口还是在出口减
一个可独立引用的定义句:若这次 IP-in-IP 封装是转发的一部分,入口把内层生存时间减一;否则封装时不改内层。减完为 0 就丢弃,并应当回一条 ICMP 超时。入口不得封装生存时间已经是 0 的数据报(RFC 2003 第 3.1 节)。
解封装本身不改内层生存时间。解完之后若内层已经是 0,出口必须丢弃。出口若再从自己的接口转发出去,按普通转发再减一次。本机产生的包送进隧道时算不算「转发」,决定入口减不加。某一客户端把本机流量送进这种隧道时走哪一种,本文没有测量。
外层生存时间只负责走到出口。沿途路由器减的是外层,这和 路由跟踪 里「外层不继承内层那个小值」是同一件事。IP-in-IP 多出来的,是入口在「属于转发」时可以先把内层减一。
出现环路时有两条必须:数据报的源地址若是这台路由器自己任一接口的地址,不得再装进隧道,应当丢弃;源地址若等于隧道出口的地址,同样不得装进隧道,应当丢弃。
隧道中间路由器发回的 ICMP,入口在报文里带有足够的原包副本时可以中继给内层发送方。其中「数据报过大」(类型 3、代码 4)必须中继。「端口不可达」不得中继,因为外层头没有端口。「协议不可达」不应当把代码 2 退回给内层发送方,因为对方发的时候用的不是协议 4。
GRE 多了至少四个字节
一个可独立引用的定义句:GRE(RFC 2784)先把载荷装进 GRE 头,再装进一种投递协议。用 IPv4 投递时,外层协议号是 47。GRE 头至少包含校验和是否存在、保留位、版本和 16 位协议类型。
版本必须是 0。版本 1 被 PPTP 使用(RFC 2784 第 7.1 节),头部不是这一套。那份历史位置见 VPN 技术史。本文只写版本 0。
协议类型用的是以太网类型。IPv4 载荷必须设为 0x800。接收方遇到不在当时登记表里的类型,应当丢弃。RFC 2784 的附录写明:写成那份文档时,已部署实现的交集不包括把 IPv6 当投递协议或载荷。这个缺口后来由 RFC 7676 补上,见下一节。
校验和位为 1 时,头里才有校验和与保留字段。校验和是 GRE 头和载荷的 16 位一的补码和,计算时该字段视为 0。保留字段若出现,必须发送为 0。符合规范的接收方必须接受并处理这个校验和。它盖住的是 GRE 头和载荷,不是外层投递头。
RFC 2784 还要求:保留字段的第 1 到第 5 位若非零,除非接收方实现了更早的 RFC 1701,否则必须丢弃。第 6 到第 12 位必须发送为 0,收到时必须忽略。
解封装一份 IPv4 载荷之后,必须按内层目的地址转发,并且必须把载荷的生存时间减一。若内层目的就是封装端,也就是隧道的另一头,必须丢弃,以免包再回到入口。这一减发生在出口转发时。它和 IP-in-IP「入口在转发时先减一、解封装本身不减」不是同一步。
后来加上的键、序号和 IPv6
RFC 2890 把第 2 位定义为 Key 存在,第 3 位定义为序号存在,并写明这两位的选择是为了与 RFC 1701 兼容。只按 RFC 2784 第 2.3 节丢弃第 1 到第 5 位、又没有实现 RFC 1701 或 RFC 2890 的接收方,仍会丢掉置了这两位的包。这是两份文档的关系,不是 2026 年 10 月对某一实现的测量。
| 位 | 置 1 时多出来的字段 | 不置位时 |
|---|---|---|
| 校验和(位 0) | 校验和与保留字段,共 4 字节 | 这两个字段不出现 |
| Key(位 2) | 4 字节的 Key | 字段不出现 |
| 序号(位 3) | 4 字节的序号 | 字段不出现 |
Key 由入口填入。如何取值不在 RFC 2890 里。同一条逻辑流使用同一个 Key,出口靠它辨认这条流。RFC 2890 第 3 节写明:尽管名字像密钥,Key 不做任何安全功能。
序号从 0 计到 2^32−1,第一份数据报的序号是 0,按模 2^32 递增。隧道建立时,接收方记住的「上一份成功解封装的序号」应当设为 2^32−1,这样序号 0 正好是上一份加一。落在上一份序号及其往前 2^31−1 个值里的包,应当静默丢弃。比上一份恰好多 1 的包是按序的。没有序号位的包也可以和带序号的包交错,并被当成按序。
既不是按序、也不是上述旧序号的,是缺口。接收方可以短暂缓存。RFC 2890 给缓存时限和每流上限起了名字,但没有在该节给出数值。时限一到,必须按序解开已经等得过久的包,即使序号仍有缺口。使用序号时,该节要求用 IPsec 保护 GRE 头和被封装的载荷:ESP 或 AH 二者之一必须用来保护 GRE 头。它引用的是当时的 RFC 2401、RFC 2406 和 RFC 2402。现行架构在 RFC 4301。本文只记录那一节的要求,不把它写成某一产品的配置。
这份序号不是 重放保护 里的抗重放窗口。RFC 2890 写的是不可靠但尽量按序:太旧的丢弃,超前的可以缓存。它也不加密。
RFC 7676 不改 GRE 头的格式。IPv6 载荷的协议类型必须是 0x86DD。IPv6 投递时,若 GRE 头紧跟外层头,下一头必须是 47;中间可以插入扩展头,那么最后一个扩展头的下一头必须是 47。
GRE 的路径 MTU 减去外层投递头和 GRE 头,得到这份文档所说的 GRE MTU。隧道必须能不拆开载荷、把 1280 字节的 IPv6 包从入口送到出口。载荷不超过这个 GRE MTU 时,不得拆载荷,也不得拆投递包。载荷更长、且 GRE MTU 至少 1280 时,必须丢弃载荷,并向内层源发送 ICMPv6 包过大,其中的 MTU 填这个 GRE MTU。GRE MTU 小于 1280 时,必须把整份 IPv6 载荷装进一个 GRE 头,再去拆外层投递包,由出口重组。
IPv6 投递头默认不能由入口分片,出口默认也不能重组;实现可以另有可选能力。IPv6 投递头自己没有校验和,GRE 校验和也不覆盖它。RFC 7676 因此要求:除非运营者认为目的地址损坏后被错误解封装的风险可接受,否则不得部署 GRE over IPv6。这是那份文档的部署条件,不是对某一种客户端的测量。
RFC 2784 的附录把路径 MTU、ICMP、差分服务和 IPv6 列为当时没有规定的已知问题,并描述当时的 IPv4 投递实现通常不置外层不允许分片、也不做路径 MTU 发现。那是 2000 年对已部署交集的描述。不要把它和 RFC 7676 的必须条款合成一条,也不要把它当成 2026 年 10 月的测量。探包如何用确认代替 ICMP,见 路径 MTU 探包。
它和加密隧道、虚拟网卡不是同一层
IPsec 传输模式与隧道模式 回答的是安全关联保护到哪一截、外层地址属于谁。ESP 可以插在外层头和内层头之间。IP-in-IP 与 GRE 只规定封装格式,不建立安全关联,也不隐藏内层之外的那一层地址。外层地址仍然要能被中间路由器转发。
TUN 与 TAP 回答的是虚拟接口上读写的是 IP 包还是以太网帧。接口类型不决定线路上是协议 4、协议 47,还是别的外层。远程接入常用的外层是 UDP 或 TCP,见 VPN 用 TCP 还是 UDP。看见协议 4 或 47,只说明这一跳用了这里的封装,不能从产品名称推出来。
一个可独立引用的结论句:IP-in-IP 没有单独的封装头,GRE 有。两者都不等于加密,Key 也不是密钥。内层生存时间何时减一,要看是哪一份规范、发生在入口还是出口。
本文不提供建立隧道、填写 Key 或打开序号的步骤。
现象对照
| 现象 | 更可能先看什么 |
|---|---|
| 外层协议号是 4,外层头后面立刻是另一份 IPv4 头 | IP-in-IP,中间没有 GRE 头 |
| 外层协议号是 47,后面先有版本和协议类型 | GRE 版本 0。类型 0x800 表示内层是 IPv4 |
| 协议类型是 0x86DD | RFC 7676 规定的 IPv6 载荷,不是 RFC 2784 附录里的已部署交集 |
| 带了 Key 的包被对端丢掉 | 对端可能只按 RFC 2784 丢弃第 1 到第 5 位 |
| 内层生存时间比进入隧道前少 1,外层是另一套 | 先分清减一发生在入口转发,还是出口解封装之后 |
核对清单
- 先看有没有 GRE 头:协议 4 是 IP 头接 IP 头;协议 47 中间还有 GRE 头;
- 版本 0 和版本 1 分开:本文的标志位只属于版本 0;
- Key 不是认证:它标识一条逻辑流。序号也不是 IPsec 重放窗口;
- 生存时间不要并成一步:IP-in-IP 在「属于转发」时于入口减内层;GRE 对 IPv4 载荷是在出口转发时减;
- IPv6 的必须条款在 RFC 7676:不要用 RFC 2784 附录里的旧实现描述代替它。