标签 ipv6 下的文章


IPv6真的每个地址都能用吗


TL;DR: IPv6 子网内第一个地址是任播地址,没有需求,最好别用。

在网上冲浪的时候,经常看到过类似“IPv6 取消了子网号和广播号,所有地址都能用”这样的言论。也经常看到有人给自己的主机或路由器配上形如 2001:db8:1:2:: 的地址。那么,IPv6 真的每个地址都能用吗?

首先我们先限制一下讨论的范围,要去除各种特殊的地址,例如未指定地址 ::/128、本地回环地址 ::1/128、链路本地地址 fe80::/10 还有多播地址 ff00::/8 等,因为这些地址大家都知道有特定含义、不能乱用。这里说的都是一个普通的单播地址,即全球单播地址(Global Unicast Address,目前是 2000::/3)或者唯一本地地址(Unique Local Address,fc00::/7)范围内的子网的地址。

接着用最近流行的句式讲结论:能,但不是完全能

诚然,IPv6 中没有 IPv4 中子网的第一个地址是网络号不能用、最后一个地址是广播号不能用的规定。但在规定 IPv6 地址结构的 RFC 4291 中,介绍了一种特别的任播地址——子网路由器任播地址(Subnet-Router Anycast Address) 。这个地址似乎一直被人忽略,也很少有人提,所以知道的人并不多。这个地址就是子网的第一个地址,例如 2001:db8:2:33::/64 这个子网(范围为 2001:db8:2:33::2001:db8:2:33:ffff:ffff:ffff:ffff)中的子网路由器任播地址就是 2001:db8:2:33::2001:db8:3:4:5:6:7:8/125 这个子网(范围为 2001:db8:3:4:5:6:7:82001:db8:3:4:5:6:7:f)中的子网路由器任播地址就是 2001:db8:3:4:5:6:7:8

如果了解任播(anycast)的概念应该知道,任播是一组主机共享一个地址。对于一个分组,只有这一组主机中的一台会收到,然后做出一定的响应。子网路由器任播地址就是给需要和子网内任意一台(不是指定的一个,也不是所有的)路由器通讯这种场景所保留的。因此这个地址也是有特殊含义的,应当在特定情况下使用,而不是随意使用。

目前很多系统都支持子网路由器任播地址,例如 Linux。Linux 会在 IPv6 转发处于开启状态的时候,自动绑定上机器目前所有子网的子网路由器任播地址。虽然只有路由器才会转发 IPv6 报文,但是有些时候(尤其是我们这些网络技术爱好者),子网内某些机子很可能会打开它,例如需要给 Docker 配上 IPv6 支持的时候。这样的情况下,如果你想通过子网内的第一个地址访问路由器,就有可能会变成访问了你这台 Docker 主机。当然,你可以在网络设备(如交换机)上设置规则来限制除了你指定的路由器之外的主机响应子网路由器任播地址的邻居请求(Neighbor Solicitation)信息,但是你不应该在完全不知道这个知识去使用子网内的第一个地址。

另外,因为/127 大小的子网只有 2 个、/128 大小的子网只有 1 个地址。因此子网路由器任播地址不会在这两个尺寸的子网内起作用。说到这个也是很有趣的:最初,RFC 3627 以这个理由不让大家在路由器之间使用/127 大小的子网,因为这样的子网只有两个地址,第一个地址同时属于两台路由器,那就只有一个地址提供给单独一个路由器了。显然/127 大小的子网做路由器间互联是最合适的,因为方便计算且只有两个地址。于是后来 RFC 6164 要求子网路由器任播地址不能作用在/127 大小的子网中,这样就可以在路由器之间用/127 大小的子网互联了。原来的 RFC 3627 也在第二年被 RFC 6547 废除了。