当互联网在婴儿阶段时,它还是一个私有的小圈子网络,作为教学与研究使用。然而,随着在大学接触了这种网络的学生走入社会,他们利用网络开发了许多不错的应用,推动了商业互联网的发展。

随着 World Wide Web 到来,家用电脑成为一般消费者访问世界的一个方便媒介。但是,人们发现,之前认为的消耗不完的 IP 地址有耗尽的风险。

因此,人们提出了 IPv6 技术,主要用来解决 IPv4 地址不足的问题。但人们也意识到,不可能让全世界的网络统一号令,在某个时刻同步更新为 IPv6 技术。为了实现过渡,人们还提出了网络地址转换(NAT) 技术作为短期的解决方案。

事实上,NAT 是一个太好的解决方案,以至于它极大地延缓了 IPv6 的推广进度。使得一个 1990 年代提出的技术在如今还没能取代 IPv4 技术。

但是,了解 IPv6 是有必要的,因为一些(作者写书时)比较新兴的应用,如网格计算、移动 IP 等,需要摆脱 NAT 才能创新;且像中国、印度这样的国家肯定会将 IPv4 池耗尽。这两个是推动 IPv6 实施的两个推手。

吸取了 IPv4 的教训,IPv6 的地址长度是 128 位,足够给地球上每个沙子分配独一无二的 IP 地址。我小时候听到这句话的时候觉得前途大好,但实际上并不是如此。因为所谓的 IP 地址池不能用简单的排列组合运算,而是要考虑一些约束问题。

举例来说,给所有在同一片沙漠的沙子分配局部 IP 地址,很可能现有的所有局部 IP 地址数就不够用。注意,此时并没有耗尽所有 IPv6 的地址种类,而是局部地址种类,更准确的说,是 link-local 地址。

当然,要是这成了个问题,说明 MAC 地址很可能也会耗尽,$2^{48}$,倒也不是没有可能。那就不仅要解决 IPv6 的问题了。

总而言之,当时设计 IPv4 地址的时候,人们就忽略了一些问题,导致 IP 地址不够用。设计 IPv6 标准的时候,人们吸取了教训,但或许仍然忽略了一些问题。所以说,IPv6 地址很可能未来也会耗尽。

IPv6 Addresses

IPv6 地址和 IPv4 地址的区别不只在长度,它们的写法也迥然不同。不仅格式不同,功能分区也不同。

Address Representation

前面讲过,对 IPv4 地址,可以用点分十进制表示。对 IPv6 地址的表示,则是按 16 位一分,分隔符用 :: 之间的每个部分用 0x0000 - 0xFFFF 表示。

比如 3ffe:1944:0100:000a:0000:00bc:2500:0d0b

但记住和书写这样的 IP 地址明显很复杂,因此,有两个化简技巧。

技巧 1:任何 16 位片段的十进制表示中,前导 0 可以省略。这样,任何一个长度小于 4 的片段都看作是省略了前导 0。

还是刚刚的例子,可以化简为 3ffe:1944:100:a:0:bc:2500:d0b。注意到有的片段是全 0,还可以进一步省略。

技巧 2:对于连续的全为 0 的片段,可以把它们用 :: 来表示。注意,在一个地址中只能出现一次 ::

在刚刚的例子中,简化了一次的地址可以进一步简化为 3ffe:1944:100:a::bc:2500:d0b,看上去还是很长。

那看看这个地址:ff02:0000:0000:0000:0000:0000:0000:0005,可以化简为 ff02::5,简洁了好多。直观的想法,就是把 0 当成气泡,可以挤压出去。

强调,这种化简只能出现一个,否则会有歧义。比如对这个地址 2001:0d02:0000:0000:0014:0000:0000:0095,化简为 2001:d02::14::95,就不知道每个 :: 对应了几个全 0 片段,无法复原。

说完了地址的表示,接下来讲讲网络号的表示。IPv6 的子网采用 bit-count 方式来表示,例如 3ffe:1944:100:a::bc:2500:d0b/64 表示网络部分为 64 位。当然了,如果只是为了表示前缀,可以把主机部分清零,更直观。

在 IPv6 地址中,有两个地方会用到全 0 地址 ::。第一种情况是在路由协议中的缺省地址,这样表示:::/0;另一种是未指定地址,用在一些邻居发现协议中,表示一个真正 IPv6 地址的缺失,这样表示:::/128

至于说 0 和 128 的含义,为什么把它们这样分配,并不是随心所欲,是有道理的。一般路由协议按照子网最长原则来匹配,你提供一个长度为 0 的子网,肯定是最后匹配,起到缺省路由的作用。另一方面,128 的长度表示一个完整的 IP 地址,对应一个主机,这和发送方地址的含义相符(发送方不可能是一个子网)。

IPv6 Address Types

IPv6 地址的种类有 3 种:

  • 单播地址
  • 任播地址
  • 多播地址

IPv6 没有广播地址,不过无伤大雅,因为目标范围是全体结点的多播地址就是实质上的广播地址。

Global Unicast Address

IPv6 的地址考虑了作用域,对一个地址按照全局和各种层次的局部归类。一个单播地址能够确定一个设备,全局单播地址就是在全世界范围内确定设备。格式在 RFC 3587 中规定。

格式和 IPv4 的大差不差,但有两个重大区别:

  • 区别一:主机号部分的长度固定,且名字叫 Interface ID,因为一个 IPv6 地址实际上并不能区分一台主机,而是一个主机的接口。不过更准确地来讲,一个 Interface ID 只是一个接口许多可用 IP 中的一个。
  • 区别二:子网 ID 部分属于网络部分,而不再属于主机部分。不再受限于 IPv4 原始的 Class Architechture,这样在含义上更直观。

Interface ID 一般 64 位,Subnet ID 一般 16 位。世界上的数字分配机构会给地区运营商分配 /32 或者 /35 长度的 IPv6 前缀,运营商根据客户的规模,可能会分配 /48、/64 或者是 /128 的地址范围,越来越小。

Identifying IPv6 Address Types

地址的前几位区分了地址类型。下面列一个表格总结一下常见的地址类型。

地址类型 高阶位(Hex)
未指定 ::/128
回环 ::1/128
多播 FF00::/8
链路局部单播 FE80::/10
全局单播 2xxx::/4 or 3xxx::/4

当然,可能表格的数据会过期,但初学者掌握这些足够了。

Local Unicast Addresses

当谈到所谓的全局单播地址时,这个地址是全局作用域的。IPv6 也提供了一些其他作用域的地址,比如 link-local 单播地址,指的是一个数据链路范围内唯一的地址。就像上表展示的那样,链路局部单播地址以 FE80 开头。

链路地址在一些只需要链路范围沟通的场合非常方便,比如一些邻居发现协议。它也允许那些没有被分配全局 IP 地址的设备和链路中的其他设备通信。

IPv6 同时也给出了其他作用域的地址,site-local 单播地址,和 IPv4 的私有地址很像。只不过这个地址面临一些问题:

  1. 关于 site 的定义;
  2. site 地址包泄露出其所在 site 的范围;
  3. 应用处理 site 包的复杂性。

总之,即使 site-local 地址有一些前景,如 NAT 或者 multihoming,IETF 最终还是废除了它。site-local 单播地址的前缀是 FFC0::/10。

Anycast Address

任播地址不再唯一确定一个设备,而是可以被绑定到多个设备上,表示一种服务。这样,许多设备(如服务器)同时对外宣布,可以在这个 IP 找到它,一个路由器就会记录到达这个 IP 的多个路线。

任播会造成一种假象,即有多个路线到达同一个 IP 地址(对应的主机),实际上,不同路线到达的是不同的主机,只不过它们的 IP 地址相同,且提供相同的服务。路由器可以选择代价最小的路线,提高效率。

任播的好处是,流量能够被导向最近的服务器,可以局部化流量,让网络中的流量模式更高效。

任播地址表示的是一种服务,而不是格式,因此,理论上任何 IPv6 地址都可以是任播地址。只不过,在 RFC 2526 中,确实定义了保留的任播地址格式。

Multicast Address

组播地址表示的是一组设备,也叫 multicast group,组播组。通常来说,一个目标地址为组播地址的包,发送地址是单播地址。在任何包中,组播地址不可能是发送地址,不然不合逻辑。

组播地址的范围可能包含一个设备、一群设备甚至是所有设备。

组播地址对于 IPv6 的一些基本功能很关键,尤其是一些即插即用的特性,如路由器发现和自动配置等等。本章后面要将的邻居发现协议就体现了组播地址的应用。

IPv6 组播地址的格式如下,截取自 RFC 3306

1
2
3
4
|    8   |  4 |  4 |                     112                     |
+--------+----+----+---------------------------------------------+
|11111111|flgs|scop| group ID |
+--------+----+----+---------------------------------------------+

前 8 位是组播地址的标志,FF。后四位是标志位,前三位为 0,只有第 4 位有用,表示这个地址是一个永久的著名地址(0),或者是临时的地址(1)。后四位表示组播范围,比如 0x2 表示 link-local。

因为组播组本身就表示一组结点,因此没必要给组播地址设计一个子网部分。所以,最后的 112 位都用作区分不同的组播组。现在前 80 位设定为 0,只使用后 32 位。

下面给出一个表格,展示一些著名的组播地址。

Address Multicast Group
FF02::1 All Nodes
FF02::2 All Routers
FF02::5 OSPFv3 Routers
FF02::6 OSPFv3 Designated Routers
FF02::9 RIPng Routers
FF02::A EIGRP Routers
FF02::B Mobile Agents
FF02::C DHCP Servers/Relay Agents
FF02::D All PIM Routers

种类很丰富。作为初学者,掌握前两个就足够了,剩下的那些等用到了自然会知道。

Embedded IPv4 Address

嵌入 IPv4 地址也是一个便于从 IPv4 网络迁移到 IPv6 网络的过渡技术,或者说让二者共存的技术。

要让 IPv4 地址的设备和 IPv6 地址的设备通信,就要把 IPv4 地址嵌入到 IPv6 地址中。反过来当然不行,因为 IPv4 地址装不下那么多 IPv6 地址的位数。

有两种主流的嵌入方式。第一种是不改变原始 IPv4 地址的分割方式,仍然按照点分十进制嵌入到 IPv6 地址中。

比如,10.23.1.5 嵌入后的 IPv6 地址是 FE80::5EFE:10.23.1.5

注意,嵌入后,原来的 IPv4 地址在 IPv6 地址中仍然只占用 32 位。换句话说,上面 IP 地址中省略了 4 个全 0 段,复原回去的表示是 FE80:0000:0000:0000:0000:5EFE:10.23.1.5。不要把 .: 看作是等价的分隔符。

另一种是按照 16 进制转换 IPv4 地址,不再使用点分十进制来嵌入。还是上面的 IP,它的十六进制表示为 0A17:0105,再给它安排一个前缀,2002:0A17:0105::/48 组合成完整的 IPv6 地址。

总而言之,这就是一些过渡技术,而且过渡技术不会再在本书提及,了解一下吧。

IPv6 Packet Header Format

看看 IPv6 报文头部的格式吧,它和 IPv4 的有类似之处,也有进步的地方。首先,截取 RFC 8200 的内容。很喜欢 RFC 的一个地方是,其中的图片都是字符画,相较于图片格式更方便复制。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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 +
| |
+ +
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

前 4 位是版本信息,IPv6 包的版本是 6。

后 8 位是传送类别信息,和 IPv4 的 ToS 字段对应。

Flow Label 是 IPv6 新增加的字段,表示一个包属于的数据流。数据流的定义是发送方和接收方的 IP 加端口信息。这一变革是有争议的。一方面,它便于路由器实现更细粒度的控制,如同一个流的包一直走同一条路径转发,避免乱序问题;另一方面,一个 L3 路由器需要查看 L4 信息才能实现路由,打破了分层原则,也增加了路由器负担。

Payload Length 部分表明包的长度,单位是字节,占用了 20 位的空间,因此一个 IPv6 包最大的长度可以到达 1,048,575 字节。

Next Header 8 位,明确了 IPv6 报头的下个头部是什么类型的头部,它和 IPv4 的头部概念类似,但有所拓展。在这里,下一个头部既可以是传输层的协议,也可以是 Extenstion Headers,记录了诸如分片等信息。

Hop Limit 和 IPv4 的 TTL 直接对应,只不过换了个更准确的名字。

最底下是 128 位的源地址和 128 位的目的地址,不再赘述。

注意到,IPv6 包头部不再包含校验和信息,这是因为(除了无线的)传播媒介可靠性大大增加,而且上层的协议一般已经校验过了。

Extension Headers

把 IPv6 的报头和 IPv4 的比较一下,发现即使 IPv6 的地址长度有 4 倍,但报文头部只有两倍之多。再加上 IPv4 的报文很可能有一些 Options 部分,这样看下来,很可能 IPv6 的报文头部更短。

确实如此。因为 IPv6 的报文只携带最精简的信息,分片、认证等信息通过扩展头部来实现;而 IPv4 的报文不管是否分片都要携带和分片相关的信息。所以,IPv6 的报文更加精巧。

每个扩展头部都有一个 Next Header 部分,用来指明下一个头部是什么类型。扩展头部的类型和功能列一个表。

Header Type Function
Hop-By-Hop Options 每一跳都需要验证的内容,比如 Router Alert 和 Jumbo Payload Options
Routing 必须经过的路由器列表
Fragment 分片,IPv6 的路由器不会分片,只有发送者才负责分片,这点和 IPv4 不同。 IPv6 要求数据链路至少 1280 字节,发送者可以通过路径 MTU 发现来确定最小 MTU
Encapsulating Security Payload 当载荷加密时使用
Authentication Header 包必须在源和目的之间认证时使用
Destination Options 携带一些只需要目的结点验证或者 Routing 中结点验证的信息

只有一个强制性原则,即如果 Hop-By-Hop 启用了,它必须直接在 IPv6 报头之后。剩下的头部有一些推荐规则,不过只是推荐。

ICMPv6

IPv6 也需要一个控制协议来交换和处理错误和信息,这就是 ICMPv6。ICMPv6 在 RFC 2463 提出,看看它的格式:

1
2
3
4
5
6
7
8
9
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Type | Code | Checksum |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
+ Message Body +
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

和 ICMPv4 的格式一模一样,其实,ICMPv6 支持的指令和 ICMPv4 的也是大差不差,只不过略有区别。比如,ICMPv4 中的一些 Source Quench 和 Timestamp 在 ICMPv6 中不存在了。

而上文讲到的最小 MTU 发现协议需要 ICMPv6 的 Packet Too Big 消息;同时邻居发现协议也有一些独特的消息——这都是 ICMPv4 不具备的。

Neighbor Discovery Protocol

IPv6 的最大特性就是即插即用,邻居发现协议(Neighbor Discovery Protocol, NDP) 就是即插即用特性的支撑,通过如下机制:

  • 路由器发现 —— 一个结点能够发现本地路由器,不需要 DHCP 的帮助;
  • 前缀发现 —— 一个结点能够发现这个链路被分配的前缀;
  • 参数发现 —— 一个结点能够发现这个链路的 MTU 和跳数限制;
  • 地址自动配置 —— 一个结点能决定它自己的完整地址,同样不需要 DHCP 的帮助;
  • 地址解析 —— 一个结点能发现链路上其他结点的链路层地址,不需要 ARP 的帮助;
  • 下一跳决定 —— 一个结点能够决定链路层上目的地方向的下一跳,不论是局部目的地还是指向目的地的路由器;
  • 邻居不可达探测 —— 一个结点能够发现同一链路上的邻居,不管是主机还是路由器,是否可达;
  • 重复地址探测 —— 一个结点能发现它想使用的地址是不是已经被别人使用了;
  • 重定向 —— 一个路由器能够通知主机一个更好的下一跳。

NDP Messages

NDP 在 RFC 2461 中定义,5 个新的 ICMPv6 消息是:

  • Router Advertisement(RA) 消息是路由器来广播它们的存在和链路的参数信息。这些消息是周期发送的,也可以针对 RS 消息响应;
  • Router Solicitation(RS) 消息发自主机,用来请求路由器的 RA 消息;
  • Neighbor Solicitation(NS) 消息发自结点,请求另一个结点的链路地址,也可用来检测重复地址和不可达邻居;
  • Neighbor Advertisement(NA) 消息响应 NS 消息,如果一个结点改变了它的链路状态,它可以发送一个无请求的 RA 消息来传达新地址;
  • Redirect 消息和 ICMPv4 的机制一样,只是正式化了。
1
2
3
4
5
6
7
8
9
10
11
12
13
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Type | Code | Checksum |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Cur Hop Limit |M|O| Reserved | Router Lifetime |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Reachable Time |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Retrans Timer |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Options ...
+-+-+-+-+-+-+-+-+-+-+-+-

上图展示了 Router Advertisement 的消息格式。对于携带这个 ICMPv6 请求的 IPv6 包,源总是发送包的地址,目的地址根据是周期发送还是响应 RS,可能是链路局部组播地址或者是发送 RS 请求的地址。

之前讲到,RA 消息发送路由器的信息和链路的信息。

Hop Limit 表示所有在这个链路的结点发包时,IPv6 报头的 Hop Limit 数字。如果路由器对 Hop Limit 没有限制,则为全 0。

M 是 Managed Address Configuration 标志。如果这位置 1,则链路上的主机会使用 stateful 地址自动配置,通过 DHCPv6;否则会使用 stateless 地址自动配置。地址自动配置在本章后方会讲到。

O 是 Other Stateful Configuration 标志。如果这位置 1,则链路上的主机会使用 DHCPv6 获得其他链路信息。它的作用是指定 M 控制范围之外的信息来源。

Router Lifetime 用来指定一个路由器的生命期。当然,只有当这个路由器是默认路由器时,这个值才非 0。

Reachable Time 用来探测邻居的可达性。它以毫秒为单位,指明了一个结点在接收到邻居的回应后,能假想这个邻居可达的时间范围。

Retransmit Timer 用来地址解析和邻居可达性检测。它规定了传送 NS 消息的最短时间间隔。

Options 可以包含 RA 的来源链路地址、MTU 以及分配给链路的一个或多个前缀。


1
2
3
4
5
6
7
8
9
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Type | Code | Checksum |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Reserved |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Options ...
+-+-+-+-+-+-+-+-+-+-+-+-

上图展示了 RS 消息格式。对于包裹这个 RS 请求的 IPv6 包,它的源地址是发送方的地址,如果发送方没被分配地址,则是全 0;目的地址是全体路由器组播地址(FF02::2)。

Options 部分可以包含发送方的链路地址。注意,如果发送方还没有被分配 IP 地址,则源链路地址不能写入到 Options 中。因为这可能会导致路由器错认为全 0 IP 地址对应发送方的 MAC 地址。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Type | Code | Checksum |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Reserved |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
+ +
| |
+ Target Address +
| |
+ +
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Options ...
+-+-+-+-+-+-+-+-+-+-+-+-

上图展示了 NS 消息的格式。一般包裹这个消息的 IPv6 包的发送地址是发送方的网卡地址。如果 NS 发送是为了检测重复地址,则发送方地址是全 0。目的地址可能是个搜寻结点的链路局部组播地址,也可能直接是目的地址。前者是一般情况,后者用在可达性检测。

ICMPv6 包中的 Target Address 是搜索的目标地址,它从来不会是个组播地址。

这里比较绕,因为有两个含义相近的 IPv6 地址:IPv6 包的目的地址和 ICMPv6 的目标地址。你可能会问,为什么要组播?因为如果不组播,在探寻邻居的时候,并不知道这个邻居的 MAC 地址(除非是可达性检测),因此包发不出去。

IP 组播对应 MAC 组播;IP 单播对应 MAC 单播,你不能期待提供了一个 IP 单播地址,MAC 层在找不到目的地的情况下自动转换为组播发送出去。

Options 可以包含发送方的链路地址。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Type | Code | Checksum |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|R|S|O| Reserved |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
+ +
| |
+ Target Address +
| |
+ +
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Options ...
+-+-+-+-+-+-+-+-+-+-+-+-

上图展示了 NA 消息的格式。承载这个包的 IPv6 包的源地址永远是发送方的地址;接收地址根据是否响应 NS 有不同。如果是响应 NS 的 NA,目的地址是 NS 的来源地址;否则是全结点的组播地址。

这个报文的结构有些复杂。

R 表示发送方是否为路由器,这位在邻居可达性检测中用来探测变为主机的路由器。这个变化不是说 Cisco 9300 变成了 Windows PC,而是这个路由器在功能上是否还进行路由转发,如果其路由功能被关闭了,实质上和一台主机就一样了。

S 是 Solicited Flag,表示这个 NA 是不是对 NS 的响应而发出的。

O 是 Override Flag,如果设定了,那么 NA 中的消息会覆盖现有的邻居缓存,并更新缓存的链路层地址。否则,不会更改现有缓存。

Target Address 其实就是发送方的地址。如果 RA 是响应 RS 的,则这个 Target Address 和 RS 中对应字段值相同;否则,就是发送方的地址。

之所以设计这样一个字段,是为了让这段报文的接收方知道报文来自于哪里,只有 L2 层和 L3 层知道是不够的,因为 ICMPv6 负责运行 NDP 协议。

Options 字段可以包含 NS 目标的链路地址,其实就是 NA 发送方的链路地址。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Type | Code | Checksum |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Reserved |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
+ +
| |
+ Target Address +
| |
+ +
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
+ +
| |
+ Destination Address +
| |
+ +
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Options ...
+-+-+-+-+-+-+-+-+-+-+-+-

上图展示了 Redirect 消息的格式。封装这个消息的 IPv6 报文的源地址就是发送方发送接口的地址;目的地址是触发这个消息的主机的地址。

ICMPv6 报文中的 Target Address 是对于某个目的地址 X 而言,更好的第一跳路由器的地址;Destination Address 就是这个目的地址 X。

Options 字段可以包含目标的链路地址,以及触发这个重定向报文的报文头部尽可能多的部分,只要保证这个重定向报文的大小不超过 1280 个字节。

上述五个报文的 Options 字段都符合 Type/Length/Value 三元组的形式。Type 指定 Options 类别,Length 指定 Value 部分以 8 字节为单位的长度,Value 可变长度,存放值。

1
2
3
4
5
6
7
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Type | Length | ... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
~ ... ~
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Router Discovery

一个路由器让自己的存在被感知、同时发送链路相关参数的方式是 RA。

对于 unsolicited RA,它们的目的地址是所有结点组播地址,源地址是路由器的链路局部 IPv6 地址。

思科路由器周期性地发送 RA,只要 IPv6 通过 ipv6 unicast-routing 启用。

RA 默认的发送间隔是 200 秒,可以通过 ipv6 nd ra-interval 来修改,nd 指的是 Neighbor Discovery。

路由器的生存期默认是 1800 秒,可以通过 ipv6 nd ra-lifetime 修改,同理,也可以修改其他报文的字段。

如果不想让路由器发送 RA,通过 ipv6 nd supress-ra 来抑制 RA 发送。

当然,默认的间隔 200 秒对于一个刚刚加入局部链路的主机还是太长了,因此,它可以主动发送一个 RS 请求,源地址是未指定地址或者是自己的 IPv6 地址,目的地址是全体路由器的组播地址。

当路由器接收到 RS 后,它会返回 RA,如果 RS 的源地址是链路局部地址,它会采用单播发送,否则,会用全体结点的组播地址。

当一个主机接收到 RA 时,它会把这个路由器添加到自己的路由器表中,只要这个路由器的生存期大于 0。如果有多个路由器,主机怎么选择取决于实现方式。Redirect 功能此时就很重要,让那些选择错误路由器的包以后可以选对路由器。

Address Autoconfiguration

当一个 IPv6 主机初次活跃在链路上时,它可以自己配置 IPv6 地址。

通常,主机会在广播接口上,这时候,一个 MAC-to-EUI64 的策略将会使用,把 48 位的 MAC 地址转换为 64 位的 Interface ID。这样做的理论基础是因为 MAC 可以被假定为全世界唯一的(不过现在 MAC 也可以通过软件修改)。

转变的方法是,把 MAC 地址对半分,中间插入 0xFFFE,再把第七位 Universial/Local 翻转为 1(Universial)。

得到了 Interface ID,还需要前缀地址,如果主机只需要链路局部通信,那使用广为人知的 FE80::/10 即可。如果要和外部世界通信,根据配置不同,可能通过 DHCPv6 获取前缀,也可能通过 RA 获取前缀。

Duplicate Address Detection

即使 MAC 地址几乎总是能保证在任何范围内的地址唯一性,再额外确认一下这个唯一性仍然是明智的。因此,每当一个设备获得一个单播地址,使用前,它必须进行重复地址探测。

不论这个地址是 stateful 还是怎样获得的,都需要检测。唯一的一个例外是,这个地址是任播地址。另一个猜测是,如果 Interface ID 在局部链路前缀下的唯一性得到了验证,那在其他链路前缀下,使用同一 Interface ID 的地址不用被验证了。

一个结点获得一个地址后,会认为地址是试探性的。重复地址探测的方法是,发送一个 NS,目标地址是这个试探地址。发送方是全 0,接收方是一个搜寻结点的组播地址。

注意这个组播地址和之前讲的大类结点组播不同,在 FF02::1 的基础上有变化:FF02:0:0:0:0:1:FF00::/104 是前缀。后 24 位是目标地址。

选择 24 这个数字是精度和效率的平衡。如果选择 64 位,最精确,但路由器的空间开销就很大;如果选择的更少,精度太低,和广播没区别了。

如果有结点接收到了 NS,并且目标地址部分和它的某个地址相同,则它会返回 NA,目标地址和目的地址都是试探地址。发送 NS 的结点要是接收到了 NA,知道这个地址是重复的了,不能再用。

Neighbor Address Resolution

这节比较重要,请务必理解清楚。否则看下一章的 Replaced Router 的 Case Stuty 可能有些困惑。

第一章我们讲了,如果一个 IPv4 的主机想和邻居交流,需要通过 ARP 协议获得邻居的链路层地址。在 IPv6 中,主机通过 NDP 获得邻居的链路层地址。

当一个结点考察目标 IP 地址的时候,它会先想,这个 IP 是不是我所在的子网的?如果不在的话,把包直接发送给默认路由器就好了。默认路由器的链路层地址应该已经通过 RA 收到了,直接用路由器的链路地址作为这个链路层报文的目的地址。

如果目的地址在同一个链路中,主机就要看看它的邻居缓存,看看这个 IPv6 地址是否有对应 MAC 地址的记录。

在 Windows XP 中查看邻居缓存的方式是 ipv6 nc,在思科 IOS 中的方式是 ipv6 show neighbors

如果这个地址在缓存里且处在 Reachable 状态,直接发送就 OK。如果不在,添加这个地址到表中,并标明 Incomplete。

随后主机发送一个以这个地址为目标地址的 NS 消息,Options 中应该包含主机的链路地址,这样对方才知道发送返回报文到哪里。如果在三次内得到了回复,则一切顺利;否则会得到一个 ICMP 错误消息。

这时候,同一子网的那个主机的链路地址会作为这个链路层报文的目的地址。

Privacy Address

隐私地址是为了解决隐私问题。试想,通过 MAC-to-EUI64 方法生成的 Interface ID 理论上永远不变。那么,针对这个 Interface ID,结合不同的前缀,公司可以监控到你的位置、服务提供商也可以持续监控你,不管你走到何处。这至少是个隐私问题,至多那就有更多利用可能了。

IPv6 解决这一问题的方式是隐私地址,通过伪随机算法生成随机数作为 Interface ID,每隔一段时间或者获得新的链路前缀时更换。这样就不会有不变的 Interface ID 问题了。

不过,要是地址一直变化,别人就无法通过 DNS 服务找到你。因此,如果别人想联系你,仍然要通过静态的地址。IPv6 确实也保留了静态地址。

听起来,好像还是有 BUG?监听者无法通过我发送的包监听我,但是仍然可以通过发送给我的包监听我?

事实上,这里的发送指的是首次唤起会话意义上的发送,比如用户访问网站。确实,用户给网站发送报文的时候,网站也会给用户发送,但这里网站的发送是一种响应,而不是唤起会话的发送。

所以,当用户浏览网页时,发送报文的 IPv6 地址是私有地址,网站给用户发送报文时,目的地址也是私有的。这样,在用户 - 网络的环路中,用户的静态地址没有暴露。

然而,如果用户想在电脑上运行 P2P 服务,那么别人必须通过静态地址才能找到它。此时,用户就成为了上一段例子中的服务器,会有别人主动找它连接。这时候别人的地址是私有地址,用户无法追溯,但用户的地址是静态的,可被追溯。

说句题外话,不知道现在这些车企是通过什么技术构建车联网的,如果是静态的 IPv6,那么监测一个车辆的位置信息,不通过 GPS 也是可以实现的。

Neighbor Unreachability Detection

前面讲到,邻居缓存中的记录有 Incomplete 状态,事实上,有 5 种可能的状态。

  • Incomplete:邻居地址解析进行中,一个 NS 发送到了搜寻结点组播地址,但是还没收到 NA;
  • Reachable:地址最近确认可达,最近指的是在 Reachable Time 字段的时间范围内确认可达;
  • Stale:自从上次确认可达,已经过去了 Reachable Time 时间;
  • Probe:正在通过给邻居发送 NS 来验证可达性;
  • Delay:一个 Stale 的记录会进入 Delay 状态,待 5 秒,如果没接收到任何可达性确认,则转移到 Probe 状态,这样方便在发送 NS 前,通过上层协议判断可达性。

这里的 Incomplete 和 Probe 容易混,前者是首次解析邻居,此时在邻居缓存中还不存在邻居对应的链路地址,后者是邻居已经解析过,缓存中有对应的链路地址,但超过了 Reachable Time,需要确认可达性。

邻居的可达性有两种判断方式:

  1. 通过上层协议的提示,如 TCP 的 ACK;
  2. 通过显示的 RA 或者 NA,这也必要,因为像 UDP 这样的协议并没有显式的 ACK。

邻居不可达检测是一个双向探测。一个没有经过 NS 的 NA 只能说明邻居可以和我通信,但我和邻居方向是否能通信没有确认,因此不会作为可达性的确认。