从 IPv4 到 IPv6

稍微熟悉 IPv6 的人都知道,IPv6 相对于 IPv4 并不只是简单地把地址从 32 位扩展到了 128 位,它同时还修复了 IPv4 协议设计中的一些不足。正如地址空间一样,当然设计 IPv4 时的不足已经逐渐凸显,理解这些 IPv4 的不足以及 IPv6 对它的改进对于深入理解 IPv6 至关重要。我在学习 IPv6 的时候就特别想找一个这样的文档,可惜一直没有发现,所以在这里简单总结一下,以方便初学 IPv6 的人。

1. 地址空间的划分

从 32 位一下子扩展到 128 位一个最明显的变化是,你很可能再也无法用脑子记住一个 IP 地址(除了 ::1 这种简单的地址)。玩笑。:-)

言归正传,IPv4 中的 A、B、C 类地址划分在 IPv6 中不存在了,这种死板的分配方式问题多多,本身就属于设计的不合理,连 IPv4 自己都已经舍弃它改用 Classless Inter-Domain Routing 了。所以,IPv6 的一个显著的变化是没了网络掩码(mask),而改用前缀(prefix)了,这样可以完全通过前缀决定网络了。同时,网络掩码的中间可以包含0,可它几乎没有什么用处,但前缀却是直接指定前x位含1,直接避免了含0的可能性,简化了设计。

在功能上,IPv4 中的地址分为三类:单播(unicast)、多播(multicast)和广播(broadcast),而 IPv6 中不再有广播地址,反而多了一个任播( anycast)。wikipedia 上有一幅图可以很好的解释它们的区别:

但是,任播地址是直接从单播地址上拿出来,并没有单独分类,IPv6 协议这么写的:

Anycast addresses are allocated from the unicast address space, using any of the defined unicast address formats. Thus, anycast addresses are syntactically indistinguishable from unicast addresses. When a unicast address is assigned to more than one interface, thus turning it into an anycast address, the nodes to which the address is assigned must be explicitly configured to know that it is an anycast address.
更详细的信息可见 RFC2373。好了,那么单播和多播地址又是如何划分的呢?这一点和 IPv4 一样,也是根据地址中的头几位来区分:

(图片来自《TCP/IP guide》)

从这里你可以看到了,单播地址还有多了一个非常重要的概念:约束(scope)。虽然在 IPv4 中 192.168.. 这种地址被用于私有地址,不应该出现在互联网上,但是,这种保证仅仅取决于路由,而非协议本身。而 IPv6 直接从协议规范上禁止了私有地址(其实叫 link-local 和 site-local )被转发到互联网,它为不同的地址划分了不同的范围,一个范围内的地址只能在这个范围内使用。

如上图,IPv6 规定了三种约束:link-local,site-local,和 global。link-local 的可达范围是本地物理连接的网络,任何路由都不能转发目的地址为这种地址的包(它是给 neighbour discovery 使用的);site-local 的可达范围是整个组织、站点,组织内部的路由可以转发这种包,但是绝对不能把它们转发到互联网上;global 就是可以在互联网上使用的地址。一个设备可以有多个 IPv6 地址,比如一个 link-local 的地址和一个 site-local 的。

多播地址的划分和 IPv4 类似,故此省略。

2. IPv6 头

让我们对比一下 IPv4 和 IPv6 的头部:

(图片来自这里

不难发现,IPv6 的头部结构明显简单了一些:

第一个最明显的改变是没有了 header checksum,这个是没有必要的,因为 checksum 完全可以由上层的 TCP,UDP 协议来做,在 IP 层再做一次是浪费。当然 IPv4 UDP 中的 checksum 可以不做,而到了 IPv6 UDP 中的 checksum 就是必须的了。给 IP 头做 checksum 的另一个缺点是,每次 TTL 改变的时候都需要重新计算 checksum。

第二个改变是没有了 IP options,因此也就没有了 Ip Header Length,因此 IPv6 头部也就是固定的大小,40 个字节。IPv6 之所以这么做一是因为它使用了 next header,使得这个没有必要;二是,这可以提高 IP 头的解析速度。

第三个改变是多了一个 Next Header,正如刚才提到的,如果你对网络比较熟悉的话,你不难发现这个设计的灵感来自于 IPSec,Next Header 是指向下一个头,有点儿类似于单链表。下一个头可以是一个 option(所以IP option没有必要)头,比如 Hop-by-Hop,也可以上一层的协议头,比如 TCP,UDP。这种灵活的设计使得 IPv6 头部可“链接”多个头部。

第四个改变是,IPv6 不再有 Fragment Offset 和 ID,这是因为分片(fragment)的 IPv6 包可以用 Next Header 来表示(知道它强大了吧?),而且 IPv6 协议已经禁止在中间的 router 上进行分片,如果需要分片必须在 host 上完成(PMTU),这可以大大提高路由的速度。其实 IP 包分片性能很差,IPv4 包分片的问题多多:ID 的生成是个问题,在对端把分段的包进行组装又是一个头疼的问题(该预留多少内存?中间少了几个分片怎么办?收到重复的分片怎么办?),所以能尽量避免分片就避免。

3. 邻居发现( Neighbour Discovery

IPv6 相比 IPv4 另一个显著的变化是没有了 ARP 协议,邻居发现协议使用的是 ICMPv6。ARP 协议是一个尴尬的存在,它处于第三层,但又不像 ICMP 协议那样基于 IP,而是和 IP 并列,从它的头部就可以看出:

(图片来自这里

这种设计使得 OSI 分层模式变得含糊。而 IPv6 使用的 ICMPv6 则完全基于 IP 之上,无须设计另外一个并列于 IP 层的协议。正如前面提到,邻居发现协议使用的是 link-local 地址,所以不会造成更大链路范围上的影响。link-local 地址是根据 MAC 地址自动生成的(协议规定的算法),因此有了 link-local 地址就可以马上和路由器进行通信了,因此也就有了 IP 地址自动分配的功能,即类似于 IPv4 的 DHCP,在 IPv6 上这被称为自动配置(Stateless Address Autoconfiguration)。

当然了,因为没有广播地址,邻居发现协议使用的是多播地址。具体协议的细节可以参考 ICMPv6 协议的定义,和 ICMP 并无显著的差别,故省略。