本文面向的是开发者,旨在让开发者通过和 IPv4 对比的方式,了解 IPv6 的大概原理。专业网工或者想了解协议细节者,建议阅读 RFC 原文或设备厂商文档。

通信过程和几种表

本部分主要来复习下,计算机网络的基本通讯流程。

不管 IPv4 还是 IPv6,其单播通讯(关于单播参见下文)都依赖如下三种表:

类型设备层级核心字段
路由表主机/路由器三层目标 IP (key),下一跳 IP (value),网口 (value)
ARP 表(IPv6 类似的叫 nd cache)主机/路由器二层到三层之间IP 地址 (key) ,Mac 地址 (value),网口 (value)
Mac 地址表交换机一层到二层之间Mac 地址 (key),网口 (value)

这几种表在通讯过程中的用途需如下两种情况讨论:

  • 同网络通讯
  • 跨网络通讯

同网络通讯场景,假设网络拓扑如下(以 IPv4 为例),主机 A 发送 IP 报文给 Host B:

Host A  ------------ Switch 0 ------------ Host B
192.168.0.2/24                             192.168.0.3/24
  • Host A 构造消息
    • IP 层
      • 目标地址需应用配置,为:192.168.0.3
      • 源地址若未配置按照如下逻辑确定,为 192.168.0.2
        • 用 目标地址 匹配每一个网卡的网段,如果在同一网段,则使用该网卡的 IP 地址作为源地址(本例符合该场景)。
        • 否则,按照一定策略选择一个网卡作为源地址。
        • 在这个场景,只有一个网卡,所以源地址为 192.168.0.2
    • 以太网层
      • 目标地址按照如下逻辑确定,在这个场景中,Mac 地址就是 Host B 的 Mac 地址。
        • 如果 IP 层的目标地址和源地址属于同一网段,则使用目标 IP 地址查询 ARP 表,获取目标的 Mac 地址(本例符合该场景)。
        • 如果 IP 层的目标地址和源地址不属于同一网段,则查询路由表(主机的路由表是静态的下一跳固定位为网关地址),获取下一跳地址,则使用下一跳地址查询 ARP 表,获取目标的 Mac 地址。
        • 如果 ARP 表中没有查到,将使用 ARP/NDP 协议解析 Mac 地址(参见下文:Mac 地址解析
      • 源地址为选定网卡的 Mac 地址。
    • 物理层,直接按照 ARP 表选定的网口发送报文。
  • Switch 0 转发消息
    • 物理层,将信号还原为数据。
    • 以太网层,查找 Mac 地址表,获取到目标 Mac 地址所属的网口(ARP/NDP 协议解析 会触发交换机的 Mac 地址表的自学习特性。所以一般情况下会存在,如果不存在就是属于过期的情况)。
    • 物理层,如果 Mac 地址表找到了,直接将数据原封不动从该网口发送出去。如果找不到,则从所有网口发送出去。
  • Host B 接收消息
    • 物理层,将信号还原为数据。
    • 以太网层,检查包的目标 Mac 地址和当前网卡的 Mac 地址是否一样,不一样将丢弃。
    • IP 层,检查包的目标 IP 地址和当前网卡的 Mac 地址是否一样,不一样将丢弃。

跨网络通讯场景,假设网络拓扑如下(以 IPv4 为例),主机 A 发送 IP 报文给 Host B:

Host A  ------------------------ Router 0 ------------------------ Host B
192.168.0.2/24     192.168.0.1/24        192.168.1.1/24            192.168.1.2/24
  • Host A 构造消息
    • IP 层
      • 目标地址需应用配置,为:192.168.1.2
      • 源地址若未配置按照如下逻辑确定,为 192.168.0.2
        • 用 目标地址 匹配每一个网卡的网段,如果在同一网段,则使用该网卡的 IP 地址作为源地址(本例符合该场景)。
        • 否则,按照一定策略选择一个网卡作为源地址。
        • 在这个场景,只有一个网卡,所以源地址为 192.168.0.2
    • 以太网层
      • 目标地址按照如下逻辑确定,在这个场景中,Mac 地址就是 Router0 左侧网口的 Mac 地址。
        • 如果 IP 层的目标地址和源地址属于同一网段,则使用目标 IP 地址查询 ARP 表,获取目标的 Mac 地址。
        • 如果 IP 层的目标地址和源地址不属于同一网段,则查询路由表(主机的路由表是静态的下一跳固定位为网关地址),获取下一跳地址,则使用下一跳地址查询 ARP 表,获取目标的 Mac 地址(本例符合该场景)。
        • 如果 ARP 表中没有查到,将使用 ARP/NDP 协议解析 Mac 地址(参见下文:Mac 地址解析
      • 源地址为选定网卡的 Mac 地址。
    • 物理层,直接按照 ARP 表选定的网口发送报文。
  • Switch 0 路由消息
    • 物理层接收解析,将信号还原为数据。
    • 以太网层接收解析,检查包的目标 Mac 地址和当前设备的 Mac 地址是否一样,不一样将丢弃。
    • IP 层接收解析,更新 IP 头上的跳数。
    • 以太网层构造消息
      • 目标 Mac 地址,根据包的目标 IP 地址,查询路由表,获取到下一跳地址(本例中为:192.168.1.2),使用下一跳地址查询 ARP 表,获取目标的 Mac 地址。
      • 源 Mac 地址为当前设备的 Mac 地址。
    • 物理层,直接按照 ARP 表选定的网口发送报文。
  • Host B 接收消息
    • 物理层,将信号还原为数据。
    • 以太网层,检查包的目标 Mac 地址和当前网卡的 Mac 地址是否一样,不一样将丢弃。
    • IP 层,检查包的目标 IP 地址和当前网卡的 Mac 地址是否一样,不一样将丢弃。

值得一提的是:

  • 交换机只会用到 Mac 地址表,而主机和路由器的行为非常类似,都会用到 ARP 表路由表
  • 数据包经过交换器,数据包内容基本不变。
  • 数据包经过路由器:
    • IP 层源和目的 IP 地址不变,只有 跳数 加一。
    • 以太网层的源和目的地址都变了。

参考:

报文格式

参考:IPv4 和 IPv6 报头格式说明

先来观察 IPv4 Packet Header 格式,如下所示(RFC 791: INTERNET PROTOCOL IP 协议 ,关于 IPv4 更多参见: 通过 Linux API 学习网络协议栈(二)IP 协议)。

0                   1                   2                   3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Version|  IHL  |Type of Service|          Total Length         |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|         Identification        |Flags|      Fragment Offset    |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|  Time to Live |    Protocol   |         Header Checksum       |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                       Source Address                          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                    Destination Address                        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                    Options                    |    Padding    |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

先来观察 IPv6 Packet Header 格式,如下所示(RFC 8200: Internet Protocol, Version 6 (IPv6) Specification

0                   1                   2                   3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Version| Traffic Class |           Flow Label                  |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|         Payload Length        |  Next Header  |   Hop Limit   |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                                               |
+                                                               +
|                                                               |
+                         Source Address                        +
|                                                               |
+                                                               +
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                                               |
+                                                               +
|                                                               |
+                      Destination Address                      +
|                                                               |
+                                                               +
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  • IPv6 最重要的一个目标就是解决 IPv4 地址空间太小的问题,因此 IPv6 的地址长度是 128 位,是 IPv4 的 4 倍。
  • 虽然 IPv6 地址长度是 IPv4 的 4 倍,但是报头长度只是 IPv4 的 2 倍。从上面对比可以看出,IPv6 的报文格式似乎比 IPv4 的报文格式更简单,字段更少。IPv6将报头分文了两个部分:

    • 固定 40 个字节的基本报头(上图表述的就是基本报头),包含了路由过程所需要的数据;
    • 0 或多个 扩展报头,提供了更好的扩展性(而 IPv4 报文头中的选项字段最多只有40字节)。
  • IPv6 报头为定长的 40 bytes,IPv4 报头为不定长。IPv4 首部的选项字段允许 IP 首部被扩展,由此导致数据报首部长度可变,故不能预先确定数据字段从何开始,同时也使路由器处理一个 IP 数据报所需时间差异很大(有的要处理选项,有的不需要)。基于此,IPv6 采用固定 40 字节长度的报头长度(称基本报头)。然后 IPv6 通过扩展报头的选项字段实现类似于 IPv4 的扩展功能,并由 IPv6 基本报头的 Next Header 字段指向扩展报头(如果有的话)。路由器不处理扩展报头,提升了路由器转发效率。同时,IPv6 报头字段 64 bit 对齐,能够直接对内存进行存取。

  • 字段:IPv4 报头有 14 个字段(带选项和填充字段),基本的 IPv4 报头有 12 个字段;IPv6 报头只有 8 个字段。IPv4 中的 header length(4)、Identifier(16)、Flags(3)、Framented offset(13)、Options(Length variable、used for test)、Padding 这些项在 IPv6 中都没有了。

  • 首部检查和:每个路由器上 IPv4 首部检查和都需要重新计算,是一项耗时操作。加之数据链路层和传输层协议已经执行了检验操作,网络传输可靠性提升,所以 IPv6 不进行首部检查和,从而更快速处理 IP 分组。

  • 选项和填充:选项由扩展报头处理,填充字段也去掉。

地址表示法

在计算机看来,网络地址就是一个二进制串,但是人类无法直接记忆和书写二进制串,因此需要定义一套标准的人类可读的地址表示法。

在 IPv4 中,网络地址长度只有 32 位,通常使用点分十进制格式来表示一个 IP 地址,如 192.168.1.1,对于该地址所属网段的表示方式,在 IPv4 中有两种方式表示:

  • 前缀长度 192.168.1.1/24/24
  • 子网掩码 255.255.255.0

在 IPv6 中,网络地址长度变成了 128 位,如果仍然使用点分十进制表示,那个就太长了,且不易与 IPv4 的地址区分。因此:

  • IPv6地址被表示为以冒号(:)分隔的一连串16比特的十六进制数,每个IPv6地址被分为8组,每组的16比特用4个十六进制数来表示,组和组之间用冒号隔开,比如:2001:0000:130F:0000:0000:09C0:876A:130B
  • 为了简化IPv6地址的表示,对于IPv6地址中的 0 可以有下面的处理方式:
    • 每组中的前导 0 可以省略,即上述地址可写为 2001:0:130F:0:0:9C0:876A:130B
    • 如果地址中包含连续两个或多个均为 0 的组,则可以用双冒号 :: 来代替,即上述地址可写为 2001:0:130F::9C0:876A:130B
  • 注意在一个IPv6地址中只能使用一次双冒号 ::,否则当设备将 :: 转变为 0 以恢复 128 位地址时,将无法确定 :: 所代表的0的个数。

IPv6 中不再使用子网掩码来表示一个子网,而是使用前缀长度表示:IPv6地址/前缀长度,如:2001:0:130F::9C0:876A:130B/64

地址分类

相关概念:网络/子网/网段。不管 IPv4 还是 IPv6,都有该概念,子网是通过 IP 地址划分的,比如 192.168.0.1/24 就定义了一个子网,这个子网的地址范围是 192.168.0.1 ~ 192.168.0.255,其中,192.168.0.1 分配给路由器,即网关,192.168.0.2~192.168.0.254 分配给主机,192.168.0.255 就是广播地址。因此可以说一个网络/子网,有一个路由器(网关)(换句话说:路由器分割了网络),有 0 到多个主机(设备),有 1 个广播地址(广播域)。

在 IPv4 中,地址的分类,除了组播和保留地址外,会按照分配规则(A、B、C 类地址以及 CIDR)进行划分(决定一个组织可以获得多少个 IP 地址,这些 IP 地址组成个网络),然后再分为广播地址和单播地址:

  • A 类地址(0.0.0.0/8 ~ 127.255.255.255/8)、B 类地址(128.0.0.0/16 ~ 191.255.255.255/16)、C 类地址(192.0.0.0/24 ~ 223.255.255.255/24)。
    • 无分类编址 CIDR,即在 A、B、C 类地址上再次划分,比如:192.168.0.0/24 可以划分为 192.168.0.0/25192.168.0.128/25 两个子网。
      • 单播地址 192.168.0.0/25 的单播地址为 192.168.0.0 ~ 192.168.0.126
      • 广播地址 192.168.0.0/25 的广播地址为 192.168.0.127
  • D 类地址(组播):前缀为 1110,地址范围为 224.0.0.0 ~ 239.255.255.255(用法参见下文)
  • E 类地址(保留):前缀为 1111,地址范围为 240.0.0.0 ~ 255.255.255.255

由于 IPv6 地址空间巨大,因此 IPv6 没有强制的分配规则,所以 IPv6 的地址可以分为如下三类:

  • 单播地址:除了组播地址外的所有地址。
  • 组播地址:ff00::/8
  • 任播地址:从单播地址空间中进行分配,使用单播地址的格式。

可以看出,IPv6 和 IPv4 相比,添加了任播地址,删除了广播地址。

单播(unicast)就是点对点的通讯,开发者接触的 99.9% 的场景都是单播。

关于:广播(broadcast)、组播(multicast,又叫多播)、任播(anycast),参见下文专门的章节介绍。

常见的保留单播地址

保留单播地址指的是:非公网单播 IP 地址。

在 IPv4 中,常见保留的单播地址,可以分为如下几类:

  • 未指定地址:0.0.0.0/32
  • 本地回环地址 127.0.0.1/8
  • 链路本地地址:169.254.0.0/16
  • 私有地址空间(专用网络,私有 IP):
    • 10.0.0.0/810.0.0.0 ~ 10.255.255.255
    • 172.16.0.0/12172.16.0.0 ~ 172.31.255.255
    • 192.168.0.0/16192.168.0.0 ~ 192.168.255.255

在 IPv6 中,上述地址,也有对应物:

  • 未指定地址:::/32
  • 本地回环地址 ::1/128
  • 链路本地地址:fe80::/10
  • 私有地址空间(在 IPv6 中叫做 Unique local address):fc00::/7,其中 fd00::/8 可使用,fc00::/8 未定义。

广播

广播(broadcast),只在 IPv4 中存在,在 IPv6 中被组播(all-nodes multicast address,所有节点的组播地址 FF02::1)代替了,向当前网络内的所有主机发送报文(注意,广播不会跨网络/路由器/广播域,又称为本地广播)。在开发中,基本上不会直接接触 IPv4 的广播能力,而是通过上层 UDP 协议来实现广播。

IPv4 想要实现广播协议,其基于的数据链路层必须也要支持广播。因此 IPv4 的广播的原理就是以太网广播。

  • 假设发送方想向 192.168.0.1/24 网络内的所有主机发送一个广播消息,发送过程如下:
    • 在 UDP 层,目标地址设置为 255.255.255.255,端口设置为指定端口。
    • 在 IPv4 层,目标地址设置为 192.168.0.255
    • 以太网层,目标地址 Mac 地址设置为 FF:FF:FF:FF:FF:FF
  • 交换机接收到这个消息,将这个消息发送给所有的物理端口(主机)
  • 所有收到这个消息的主机将逐层解包
    • 以太网层,发现该消息的目标是指是广播消息,交由上层。
    • IPv4 层,交由上层处理。
    • 在 UDP 层,检查端口是否有进程绑定,没有则丢弃,否则交由进程处理。

从上文可以看出,在 IPv6 中废弃了 IPv4 的这种广播,原因是:

  • 广播对网络和主机都产生了很大的压力,不需要接收该消息的主机也会收到了这些消息。因此,推荐使用组播代替。
  • 广播只能在当前网络中进行,即本地广播,不支持跨网络广播(除非特殊支持)。

组播

组播(multicast,又叫多播)在 IPv4 和 IPv6 中都存在,并且其在 IPv6 中占有更重要的地位,取代了 ARP 协议,为 Mac 地址解析提供底层支持。

组播的应用场景比较多,比如 NTP 协议, mDNS 协议,IPTV,视频会议等。在应用层基本也是通过 UDP 协议使用。

在 IPv4 中,组播可以实现:发送方,向一组(1个或多个)主机,发送报文,只需要指定一个组播地址,即可将消息发送到这个组内的所有主机。

从这个需求,可以看出,要实现这个效果,则需要实现组播 IP 和多个主机的映射。因此,需要一个协议,可以将一个主机加入到一个组中,同样也需要将一个主机从一个组中移除。在 IPv4 中,这个协议就是 IGMP(组成员关系管理)。

此外组播和广播不同,可以实现跨网段的组网,因此需要一个协议,来进行跨网段路由,这个协议就是 PIM(域内组播路由协议),MBGP, MSDP(域间组播路由协议)。

在此,只讨论一下在同一个网段下,发送一个组播消息的大概流程:

  • 前置条件,需要接收消息的主机,通过 IGMP 协议,加入该组,该组的 IP 为 225.1.1.1,同时配置网卡的组播 Mac 地址(参见下文)。
  • 假设发送方想向 225.1.1.1 组发送消息,发送过程如下:
    • 在 IPv4 层,目标地址设置为 239.1.1.1(固定前缀为 4 位,所以地址空间为 28 位)。
    • 以太网层,目标地址 Mac 地址设置为,组播 Mac 地址:前缀为 01:00:5E:0(25位),后缀为广播地址的后 23 位,注意这个地址是一个逻辑地址,并不是一个真实的地址(无法一一对应,可能有冲突,但是不要紧,概率很小)。
  • 交换机接收到这个消息,会根据 IGMP 协议构建的组播表,定向的将消息发送给指定端口(主机)。
  • 所有收到这个消息的主机将逐层解包
    • 以太网层,校验目标 Mac 地址是否和组播 Mac 一致,如果一致交由上层处理。
    • IPv4 层,校验目标 IP 地址是否和组播 IP 一致,如果一致交由上层处理。
    • 在 UDP 层,检查端口是否有进程绑定,没有则丢弃,否则交由进程处理。

更多参见:华为 - 什么是组播新华三 - 组播技术白皮书

在 IPv6 中,组播的原理上和 IPv4 类似,详细可以看:新华三 - IPv6组播技术白皮书

下文将,着重介绍将组播中的一种特殊情况,其给 IPv6 取代 ARP 协议提供支持。

Mac 地址解析

在 IPv4 中,为了解决 IP 地址和 Mac 地址(以太网地址)的映射问题,使用的是 ARP 协议,ARP 协议基于的是以太网广播机制,这造成了难以避免的 ARP 攻击 和 ARP 风暴的问题。ARP 协议原理参见:博客

在 IPv6 中,Mac 地址解析不再使用 ARP 协议,而是使用基于一种点对点组播的 ICMPv6 协议。是 NDP (邻居发现协议)的一部分。

  • 每个 IPv6 单播地址的主机都会自动加入一个称为,被请求节点组播地址 的组播组。其组播地址为 FF02::1:FFxx:xxxx,其中 xx:xxxx 为该主机单播 IPv6 地址的最后 32 位。并将组播地址通过 MLD 协议(对应的是 IPv4 的 IGMP)进行上报。
  • 此时,主机 A,如果需要查询一个 IPv6 单播地址的 Mac 地址,发送者将:
    • ICMPv6 层,消息 Type = NS
    • IPv6 层,目标地址为单播 IPv6 地址的组播地址 FF02::1:FFxx:xxxx
    • 以太网层,目标地址 Mac 地址设置为组播地址:前缀为 33:33(16位),后缀为组播地址的后 32 位,即 FF:xx:xx:xx
  • 交换机接收到这个消息,会根据 MLD 协议构建的组播表,定向的将消息发送给指定端口(主机)。与此同时,向交换表加一条记录。
  • 只有主机 B 能收到该消息,将逐层解包:
    • 以太网层,校验目标 Mac 地址是否和组播 Mac 一致,如果一致交由上层处理。
    • IPv6 层,校验目标 IP 地址是否和组播 IP 一致,如果一致交由上层处理。
    • ICMPv6 处理,并发送回复消息,消息 Type = NA
  • 主机 B 回复消息
    • ICMPv6 层,消息 Type = NA
    • IPv6 层,目标地址为单播 IPv6 地址,即上一步接收到消息的 source IP,源地址设置为当前主机地址。
    • 以太网层,目标地址为上一步接收到消息的 source Mac 地址,源地址为当前主机地址。
  • 交换器接收该消息,根据交换表可以直接发送给主机 A
  • 主机 A 将收到该消息,将逐层解包:
    • 以太网层,校验目标 Mac 地址是否和 Mac 一致,如果一致交由上层处理。
    • IPv6 层,校验目标 IP 地址是否和 IP 一致,如果一致交由上层处理。
    • ICMPv6 处理,构建 nd cache 表(IPv4 中的 arp 表)。

IPv6 的 Mac 地址解析机制的总结:

  • 利用点对点组播,解决了 ARP 协议的风暴问题(由 ARP 广播查询,更改为了组播上报,单点查询),提高了效率。
  • 如果想要安全,可以使用 SEND 协议。
  • 通过 ICMPv6 统一实现,避免每一种数据链路都需要重复实现类似 ARP 类型的功能。

关于 NDP 协议的其他内容,参见下文的:链路本地地址

参考:

任播

IPv4 作为实验特性,IPv6 正式纳入标准。简单的来说,当一个单播地址被分配到多于一个的接口上时,这些接口组成了一个任播组,这个 IP 可以称为任播 IP。

任播在使用时,可以同时支持 TCP/UDP 协议的使用。

当请求该任播 IP,路由协议会按照一定的规则(如最短路径,超时时间等等)选择一个接口进行通讯。

可以看出,任播本质就是在网络层,实现负载均衡(参考:cloudflare blog)和就近服务的能力。

任播的应用场景有:

  • 根 DNS 服务器,采用任播提供服务。
  • CDN 实现就近接入,参考:cloudflare
  • 缓解 DDoS 攻击,参考:cloudflare

更多参见:Anycast

链路本地地址

在 IPv4 中,链路本地地址为 169.254.0.0/16,是可选项,所以,只有在 Mac 连 WIFI 抽风时,才可能可以看到这个地址(DHCP 失败时,一些操作系统可能会自动的分配一个链路本地地址)。链路本地地址的特点是:

  • 路由器不会将该地址转发到其他网络中,因此只能在网络内部使用。
  • 该类地址的分配是完全自动化的,主机随机分配,然后利用 ARP 协议检查是否和网络上的其他主机冲突。

在 IPv6 中,链路本地地址为 fe80::/10,每个接口必须分配一个,也就是说,IPv6 的每个网卡,最少要有两个单播地址:

  • 链路本地地址 fe80::/10 (scope link)。
  • 全局单播地址 (scope global)。

在 IPv4 中,IP 地址的分配主要有两种方式:手动配置、DHCP(有状态地址自动分配)。

在 IPv6 中,IP 地址的分配主要有三种方式:手动配置、DHCPv6(无状态地址自动分配)、无状态地址分配。

在这里,重点介绍无状态地址分配。无状态地址分配原理是:基于 IPv6 的链路本地地址,可以自动的分配一个全局单播地址。流程基本如下:

  • 主机启动,自动生成一个链路本地地址。
    • 一般按照 EUI-48EUI-64 算法,自动分配一个链路本地地址(可以是任何算法,由实现者决定)。
    • 通过上文提到 Mac 地址解析(ICMPv6) 类似的方式,网络中是否已经存在了该链路本地地址(存在是,将能收到响应 NA 消息),称为 DAD (Duplicate address Detection,重复地址检测)。
  • 主机通过,路由器发现协议,获取当前网络的网络信息(全局单播地址的前缀)
    • 主机发送一个 Type = RS 的 ICMPv6 消息(Router Solicitation 路由器请求),其目标地址为 all-router multicast address(即 ff02::2 一个路由器会加入的组播地址),源地址为链路本地地址。
    • 路由器收到该消息后,会回复一个 Type = RA 的 ICMPv6 消息(Router Advertisement,路由器公告),其目的地址为主机的链路本地地址,源地址为路由器的链路本地地址。RA 消息包含了当前网络的一些配置情况:
      • 是否使用 DHCPv6 分配地址?
      • 网络前缀是什么?
    • 此外:路由器会定时给所有节点发送 Type = RA,以告知节点网络变更情况,其目标地址是 all-nodes multicast address(即 ff02::1 一个所有节点会加入的组播地址)
  • 主机接收到 RA 消息后,根据网络前缀,自动生成一个全局单播地址。流程和第一步的自动生成一个链路本地地址类似。

可以看出,无状态地址分配有如下好处:

  • 真正的即插即用。节点连接到没有DHCP服务器的网络时,无需手动配置地址等参数便可访问网络。
  • 网络迁移方便。当一个站点的网络前缀发生变化,主机能够方便地进行重新编址而不影响网络连接。
  • 地址配置方式选择灵活。系统管理员可根据情况决定使用何种配置方式——有状态,无状态还是两者兼容。

由于,链路本地地址是一个 IPv6 中的一个整个网段,如果一个设备有多个网卡,连入了多个网络,他们的链路本地地址可能是一样,因此,需要区分从网卡。因此在应用层使用链路本地地址时,需要通过 %网卡名 指定网卡。 如:

ping -6 fe80::xxxxx%eth0

参考:

IPv6 网口地址

一旦节点启用 IPv6,那么网口(接口)就会自动绑定下列地址(也就是说通过这些地址可以将消息发送到该网口):

  • 单播地址
    • 回环地址 ::1
    • 本地链路地址 fe80::xxxxxx
    • 全局单播地址
  • 组播地址
    • 本地链路地址对应的,被请求节点组播地址 FF02::1:FFxx:xxxx
    • 全局单播地址对应的,被请求节点组播地址 FF02::1:FFxx:xxxx
    • 所有节点的组播地址 FF02::1
    • 如果是路由器,还会有所有路由器组播地址 FF02::2

IPv6 和 NAT

IPv6 最重要的职责就是解决 IPv4 地址空间太小的问题。因此在 IPv6 中推荐不使用 NAT。也就是说,建议组织内网也采用公网单播地址。

但是,在企业内部中,多数还是沿袭 IPv4 的习惯,仍然只给内网分配 私有 IPv6 地址,使用 IPv6NAT。主要原因是如果给内网分配公网单播地址。之前基于 NAT 的安全策略可能都要重新设计了,成本比较高。

Linux 常用命令

观察网卡绑定的 IP 地址,如果存在 IPv6 将返回包含 inet6 的行。如果是本地链路地址,则显示 scope link,如果是全局单播地址则显示 scope global

ip addr show 
ip addr show eth0

Socket API

#include <sys/socket.h>

// 创建 socket https://man7.org/linux/man-pages/man2/socket.2.html
int socket(int domain, int type, int protocol);

int tcp6_socket = socket(AF_INET6...)
// 是否支持 IPv4/IPv6 双栈,默认值为 `/proc/sys/net/ipv6/bindv6only`
// 可以通过 setsockopt 系统调用手动强制配置
int ipv6_only_flag = 0;
setsockopt(tcp6_socket, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&ipv6_only_flag, sizeof(ipv6_only_flag));

更多参见:通过 Linux API 学习网络协议栈(一)概览

其他参考