运营商分配的 IPv6 地址通常都不固定:
1
2
3
4
5
|
[admin@shanghai-router] > /ipv6/dhcp-client/print
Columns: INTERFACE, STATUS, REQUEST, PREFIX
# INTERFACE STATUS REQUEST PREFIX
0 pppoe-chinanet bound prefix 240e:388:6501:XX00::/56, 40m57s
1 pppoe-chinamobile bound prefix 2409:8a1e:6a64:2XX0::/60, 2d20h10m
|
虽然在 PPPoE 存活期间地址不会变更,但是通常运营商的 BRAS 都有定时中断触发重连的机制,导致我们的 IPv6 地址发生变更。比较新的 RouterOS 版本中,DHCPv6 拿到的前缀失效后会让设备更新地址 lifetime
为 0 从而删除旧地址,是一个比较不错的解决方案。
然而在 RouterOS 上利用 BGP 全表优化多线互联网访问这篇文章中提到:
因为没有 NAT,设备也不能智能的选择用哪个前缀的 IP 地址,IPv6 的分流依然存在问题
所以,光有一个稳定的前缀还不够用,我们需要让设备的防火墙“智能”地将出方向的 IPv6 源地址更新成我们想要他使用的源地址,才能让多 ISP 优化达到最佳效果。为了达成这个目的,我们可以使用 netfilter 提供的 NETMAP 目标。
假设我们的路由器拥有以下前缀:
- 来自中国电信的:240e:388:6501:ab00::/56
- 来自中国移动的:2409:8a1e:6a64:2XX0::/60
- 来自自己宣告的:2602:feda:de0:700::/56
我们选择固定的地址和最小的前缀作为翻译的范围,也就是 2602:feda:de0:700::/60
。
Notice
如果你没有自己的前缀,也可以用 IPv6 ULA 地址来配置 IPv6 SLAAC,例如 fd34:38a:f295:a00::/64
针对从 pppoe-chinanet 接口出去的,源地址为 2602:feda:de0:700::/60 的连接,使用防火墙将源地址映射到 240e:388:6501:ab00::/60 内(也就是中国电信的地址):
1
|
/ipv6/firewall/nat add chain=srcnat out-interface=pppoe-chinanet src-address=2602:feda:de0:700::/60 action=netmap to-address=240e:388:6501:ab00::/60
|
当然数据不能只管出不管进,否则外侧服务的响应回到路由器之后,路由器发现没有直接相邻且使用 240e:388:6501:ab00::/60
这个前缀的地址。使用如下规则来处理回程流量:
1
|
/ipv6/firewall/nat add chain=dstnat in-interface=pppoe-chinanet dst-address=240e:388:6501:ab00::/60 action=netmap to-address=2602:feda:de0:700::/60
|
同理可以编写如下规则用于移动的出口:
1
2
3
|
/ipv6/firewall/nat
add chain=srcnat out-interface=pppoe-chinamobile src-address=2602:feda:de0:700::/60 action=netmap to-address=2409:8a1e:6a64:2XX0::/60
add chain=dstnat in-interface=pppoe-chinamobile dst-address=2409:8a1e:6a64:2XX0::/60 action=netmap to-address=2602:feda:de0:700::/60
|
随后我们优化一下 BGP 全表的 v6 过滤器:
1
2
3
4
5
6
7
8
9
10
11
|
if (dst-len > 0 && bgp-as-path [[:china_asn_list:]]$) {
if (bgp-as-path 4134$|4812$) {
set gw %pppoe-chinanet;
} else {
set gw %pppoe-chinamobile;
}
set distance -5;
accept;
} else {
reject;
}
|
最后我们还需要在对应的 DHCPv6 配置上增加一个更新脚本用于更新防火墙规则,以移动的为例:
1
2
3
4
5
6
7
8
9
10
|
:log info ("dhcpv6 up script started");
:local [/ipv6/pool/get [find name="ipv6-chinamobile-pd"] prefix];
:log info "prefix assigned by China Mobile is: $china_mobile_prefix";
:local china_mobile_new_prefix [:pick "$china_mobile_prefix" 0 ([:len "$china_mobile_prefix"] - 3)]
/ipv6/firewall/nat/set dst-address="$china_mobile_new_prefix/60" [find chain=dstnat action=netmap in-interface="pppoe-chinamobile"];
/ipv6/firewall/nat/set to-address="$china_mobile_new_prefix/60" [find chain=srcnat action=netmap out-interface="pppoe-chinamobile"];
:log info "netmap update okay";
|
这样就完成了!
我们可以挑一个 IPv6 地址作为目标试一试 traceroute:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
ntzyz@nas-v2 ~ % curl ipv6.ip.sb
2406:4440:10:<redacted>
ntzyz@nas-v2 ~ % mtr 240e:f7:e01f:f1::30 --report -z
Start: 2023-08-18T13:58:52+0800
HOST: nas-v2 Loss% Snt Last Avg Best Wrst StDev
1. AS142641 <redacted> 0.0% 10 0.3 0.4 0.3 0.4 0.0
2. AS24400 2409:801e:f0:1::2fb 10.0% 10 4.3 4.9 3.9 10.5 2.1
3. AS24400 2409:801e:f0:1::2fa 0.0% 10 4.2 4.3 3.9 5.2 0.4
4. AS9808 2409:8080:0:2:206:2 0.0% 10 4.7 4.7 4.5 4.8 0.1
5. AS9808 2409:8080:0:1:206:1 0.0% 10 21.8 16.4 7.9 25.4 6.6
6. AS9808 2409:8080:0:1:1106: 70.0% 10 10.2 10.2 10.2 10.3 0.0
7. AS9808 2409:8080:0:3:11e2: 0.0% 10 13.8 13.7 13.1 14.0 0.2
8. AS4134 240e::c:b:5100:302 0.0% 10 8.5 8.5 8.1 8.7 0.2
9. AS136190 240e:1c:b111:1fff:: 10.0% 10 12.2 15.3 11.1 43.4 10.5
10. AS136190 240e:1c:b112:1122:: 0.0% 10 11.9 11.9 11.8 12.0 0.1
11. AS136190 240e:f7:e01f:ff::11 0.0% 10 13.0 14.7 12.6 27.2 4.5
12. AS136190 240e:f7:e01f:f1::30 0.0% 10 10.6 11.5 10.6 11.8 0.3
|
可以看到,这台 NAS 访问 cloudflare 时使用的自己的前缀,访问国内的地址时实际上使用的是中国电信的前缀。