2019 年 11 月 26 日, IPv4 地址完全耗尽, 从上世纪九十年代 IETF 就开始着手设计 IPv6, 目前最新的已成为互联网标准的 IPv6 协议文档为 2017 年 7 月发布的 RFC 8200, IPv6 将地址标识符的长度由 32 位扩展到了 128 位, 在可预见的未来, 几乎不需要考虑 IPv6 地址耗尽的问题, 除此之外, IPv6 在 IPv4 的基础上进行了重新的设计, 将一些非通用的首部字段放到可选的拓展首部中, 减少对 IP Header 的处理以及不必要的网络带宽占用, IPv6 从提出以来到目前已经过去了二十多年, 但目前 IPv6 的使用率仍然不高, 这一方面因为网络协议尤其是像 IP 这样大体量的底层标准协议本身具有巨大的惯性, 协议的切换和升级需要各方面的力量去推动, 另一方面, NAT [RFC 2663] (Network Address Translate, 网络地址转换) 使得设备可以使用自主分配的内网 IP 而共用相同的公网 IP, 从而使得 IPv4 仍然可以很好地支撑整个互联网的运行, 但 IPv6 仍是未来的主流, 本文全面讨论 IPv6 的设计与运行原理

19.1 IPv6 与 IPv4 的区别概述

在开始详细讨论 IPv6 之前, 先对 IPv6 和 IPv4 的主要差别做一个简短的概述:

  • IPv6 将地址标识符的长度由 32 位扩展到了 128 位, 128 位的地址空间非常庞大, 在可预见的未来几乎 (甚至是永远) 不需要考虑地址空间耗尽的问题, 地址标识符长度的增加, 使得 IPv6 可以支持更多的地址分层

  • IPv6 简化了 IPv4 的 Header 设计, 移除了一些无用字段, 并将某些字段设置为可选 (Optional) 字段, 从而减少对 Header 的处理以及不必要的网络带宽占用

  • IPv6 重新设计了拓展字段, 通过链式方式更灵活地增加新的拓展

  • 提出了 Flow Label 的概念, 可以将一个序列的 IPv6 分组标记为属于某个流, 在传输链路上保证该流的服务质量

  • 将 IPv4 的 4 字节对齐改为 8 字节对齐

  • IPv6 增加了 Anycast (任播) Address 的地址类型, Anycast 指定了一组目标主机, IPv6 将分组交付给其中的某一个主机 (注意与 IP Multicasting (多播) 区分)

  • IPv6 取消了 IPv4 Header 中的 Checksum 因为上层 (如 TCP / UDP) 本身具有校验机制, 另外因为每经过一个路由器, Header 的值都会发生变化 (Hop Limit 一定会修改), 所以如果在 IP Header 中设置 Checksum, 则每次都需要重新计算生成 Checksum, 取消 Checksum 可以降低计算量

19.2 IPv6 固定首部

IPv6 的分组由 Header 和 Payload 两部分构成, IPv6 将拓展首部 (Extended Header) 移动到了 Payload 中, 与拓展首部相对应的固定首部就称为基本首部 (Base Header), IPv6 Base Header 的结构如下所示:

Base Header 的长度是固定的, 占 40 比特, 各个字段的语义如下:

  • Version, 版本号, 长度占 4 比特, 指示 IP 协议的版本, 对于 IPv6 来说, 该字段的值为 6 (即 0110)

  • Traffic Class, 类似于 IPv4 的服务类型 (Type of Service) 字段, 用于区分不同 IPv6 分组的优先级和服务质量 (细节见 RFC 2474)

  • Flow Label, 流标签, 长度为 20 比特, 流标签由发送端随机生成, 用于标识一系列的 Datagram, IPv6 使用源地址 (Source Address), 目标地址 (Destination Address) 和流标签 (Flow Label) 构成的三元组唯一确定一个流, 因而属于同一个流的 Datagram 都拥有相同的 Flow Label, 运营商可以为特定流保证指明的服务质量 QoS (Quality of Service, 服务质量, 尤其是对于容量有限的网络, QoS 可以保证优先传输高优先级的数据), 了解更多 Flow Label 的细节可以阅读 RFC 6437

  • Payload Length, 长度为 16 比特, 顾名思义该字段指示 Datagram 的 Payload 部分的长度 (以字节为单位), 注意 IPv6 的扩展首部 (Extended Headers) 也在 Payload 部分, 因此 Payload Length 也包括了拓展首部的长度

  • Next Header, 长度为 8 比特, 用于标识拓展首部 (将在下面讨论) 的类型 (假设存在) 或上层协议的类型, 该字段使用数字标号来代表具体的协议, 协议编号由 IANA 注册并维护, 查询具体的协议编号可以参考 IANA-PN

  • Hop Limit, 长度为 8 位, 类似于 IPv4 的 TTL 字段, RFC 8200 指明该字段由发送端赋值, 每经过一个路由器便减去一, Datagram 在网络中传输时, 若中间的路由器减去一后该字段的值为 0 时应直接丢弃, 特别地, 接收端对于 Hop Limit 值为 0 的 Datagram 应正常接收不丢弃 (因为设置 Hop Limit 的目的主要是避免无法交付的 Datagram 在网络中不停地传播从而造成不必要的资源浪费, 若已经到达了目标主机则即使 Hop Limit 恰好减为 0 也应正常接收)

  • Source Address, 长度为 128 比特, 源 IPv6 地址

  • Destination Address, 长度为 128 比特, 目标 IPv6 地址

19.3 IPv6 拓展首部 (Extended Header) 的布局

19.2 可以看出, IPv6 的固定头部非常精简, 只有 8 个字段, 这减少了路由器或主机对 IP 头部处理的通用计算量, 例如在 IPv4 中, 无论是否进行分片, MF, DF 等标志位都必须要携带, 实际上这部分对于不进行分片的 IP Datagram 来说是冗余的, IPv6 改进了这一点, 基本首部只设置必要且通用的字段, 而对于其它的功能字段 (如分片) 则放到拓展首部中, 拓展首部设置在 Payload 部分, 拓展首部可以有 0 个或多个, 从而使得可以根据需要增添首部, 而不是都设置在固定首部中, 从而减少不必要的带宽占用和首部的处理计算量

IPv6 使用链式结构来表征一个 Datagram 携带的拓展首部, 在上一节关于 IPv6 固定首部的说明中, 有一个长度为 8 比特的 Next Header 字段, 该字段所使用的协议编号可以在 IANA 网站上查询到, IANA 为 IPv6 拓展首部定义了一组编号用于标识特定类型的 IPv6 拓展首部, 如下图所示:

当固定首部的 Next Header 字段的值是上图所示的列表中的某一个时, 代表 Datagram 包含有拓展首部, Next Header 字段的值指示了拓展首部的类型, 若 Next Header 的值不在上图所示的列表中, 则 Payload 只包含上层协议 (如 TCP / UDP) 的 PDU (协议数据单元), IPv6 的所有拓展首部中也都包含有 Next Header 字段, 通过该字段, 拓展首部可以继续连接着下一个拓展首部, 即构成了链式数据结构, IPv6 的固定首部与拓展首部的级联关系如下图所示:

第一行中, 基本首部的 Next Header 对应 TCP, 这意味着该 Datagram 只有固定首部, 没有拓展首部, 紧跟在基本首部之后的便是 TCP 的 PDU; 第二行中, 基本首部的 Next Header 对应 Routing (拓展首部类型的一种), 而 Routing 的 Next Header 对应 TCP, Routing 拓展首部之后便是 TCP 的 PDU; 第三行中, 基本首部的 Next Header 对应 Routing, 而 Routing 的 Next Header 对应 Fragment 拓展首部, Fragment 拓展首部的 Next Header 对应 TCP, 在此之后是 TCP 的 PDU