发布时间: ,大约 200 字 ,阅读时间:1 分钟,
用一个最简单的例子介绍,网络架构如下图所示:

假设内部网络的 IP 段是 fd01::/64,外部网络的 IP 段是 2001:0db8:1145:1419::/64,那么就有如下的映射关系:
| 内网 IP | 公网 IP |
|---|---|
| fd01::1 | 2001:0db8:1145:1419::1 |
| fd01::2 | 2001:0db8:1145:1419::2 |
| fd01::3 | 2001:0db8:1145:1419::3 |
| fd01::4 | 2001:0db8:1145:1419::4 |
| … | … |
以此类推。
这样做的话,与 2001:0db8:1145:1419:1145:1419:1981:0893 通信即相当于与 fd01::1145:1419:1981:0893 通信。
虽然机器上没有真正的公网 IP 地址,但是 NAT 穿透什么的都是不需要的。
而且这样做可以用无状态协议的方式实现。
但这也有限制。因为每个内部 IP 都有一个独立的公网 IP 映射,所以公网 IP 段的前缀长度必须等于内网 IP 段的前缀长度。
IPv6 有大量地址,运营商分配的最小前缀也是 /64,对于一个家庭来说,内网设备肯定足够分配了。但国内运营商联网一般使用 PPPoE 认证,分配 IP 地址一般采用动态分配。这样就会有如下图的情况:

图片上获取到的 IPv6 地址都是由 IPv6-RA 分配的。因为各种各样的原因,即使路由器换了地址,NAS 也不会马上去把以前那些没用的 IP 地址删掉,运气好的话 NAS 换了新的地址会马上去用新的,但大部分情况并不会(每次 ssh 上去 NAS 的 IPv6 都是不通的)。而且国内运营商都喜欢 PPPoE 每隔几天掐断一次,断一次 PPPoE 就会换一次 IP,所以这就导致了 IPv6 显得非常不稳定,需要经常重启或者重新断开重连才能恢复。
对于上图的网络结构,如果在单台路由器上直接给终端设备分配两个不同的 IPv6 地址,终端设备并不知道哪条路是最优解,这就导致可能对端是中国奠信的 IP 但是却使用了中国移动的线路访问。这样不仅没有办法控制终端走向最优出口 ISP,还可能让一大堆用户挤在同一个 ISP 的线路上,另外的 ISP 的线路基本空载。当然,买一段 IP 和一个 ASN 来直接 BGP 能解决,但大多数公司不会这样做。
通过搜索,可以找到 ip6tables -j NETMAP 可以实现这个功能,但是 OpenWRT 目前并没有直接提供开启的选项,所以这里需要写一个脚本来实现。
这个直接在 Luci 里面操作就可以了,做这一步主要是为了让客户端以为自己有公网 IPv6。建议修改成 2001:db8::/32,因为是用于文档的保留地址。

进入到 shell,并输入命令 opkg install ip6tables ip6tables-extra ip6tables-mod-nat iptables-mod-conntrack-extra iptables-mod-extra iptables-mod-dnetmap
Shell 文件在这里可以找到。在这篇文章中,我把这个脚本放进了 /usr/sbin/ 里面,并赋予了可执行的权限。
这一步也可以在 Luci 上操作,如下图所示。

脚本总共三个参数,第一个是实际接口名(比如 OpenWRT 设置了一个 PPPoE 拨号的接口叫 wan,那么实际接口名就是 pppoe-wan)。第二个是需要重定向到的 IPv6 地址(即内网 IPv6 地址)。第三个参数是可选的,只有不传和 connect 两个选择。如果不传的话就会把之前创建的 ip6tables 规则删掉,如果传 connect 会在删掉的同时去找相应接口的 IPv6 地址,并创建新的规则。
上面的钩子脚本的开头三个变量需要根据自己的环境去修改,钩子需要放入 /etc/hotplug.d/iface。如果第四步添加的 nptv6.sh 位置不在 /usr/sbin/ 的话,也需要把 /usr/sbin/ 替换掉。
钩子脚本也需要赋予可执行文件权限。
注意:OpenWRT 19.07 貌似并不会为 IPv6-ULA 地址添加 IPv6 的默认路由,所以脚本第 12 行还有一条添加路由的指令。如果是多线接入,需要修改 metric 调整顺序以及避免添加路由失败。

