只有IPv6的网络与IPv4最后的倔强


464XLAT 是走向 IPv6 单栈的过程中,用来让纯 IPv6 网络能够通过地址翻译访问 IPv4 网络的技术。他的工作流程是:IPv4 应用直接发送 IPv4 分组,到达 CLAT(Customer-side Translator,用户侧翻译器)进行无状态 NAT46 转换成 IPv6 分组,通过 IPv6 网络传输到达 PLAT(Provider-side Translator,运营商侧翻译器)后再进行 NAT64 转换成 IPv4 分组到达目标 IPv4 主机。

有了这个技术,IPv4 协议栈就可以在接入层上完全砍掉了,只需要有 CLAT 将 IPv4 分组送到 IPv4 网络的边界即可。CLAT 之所以叫“用户侧”,是因为他通常运行在光猫,甚至终端设备(如手机、电脑等)上。本文记录的是终端设备运行 CLAT。

2 月春节期间,我在家里的网络上设置了 NAT64 作为 PLAT,并配置了 DHCPv4 的 IPv6-Only Preferred 标记之后,DHCPv4 的地址分配骤减到 4 个,还在使用 IPv4 的只剩下智能家居、无线电台这些嵌入式设备了。

NAT64 enabled on 3 platforms

可以见到,图中 3 个平台的 IPv4 地址均为空或者 RFC 7335 指定用于 v4 转换的 192.0.0.0/29。

其实这种事情不是第一次干,早在 2019 年就尝试过一次:当时我用 tayga 在大学寝室的 OpenWrt 上配置了 NAT64,然后使用 bind9 做了 DNS64。这一套方案通过将没有 AAAA 记录的域名的 A 记录转换成 AAAA 的方式来实现 IPv4 网络的访问,也是当时的标准做法。这样操作的问题显而易见,毕竟总有应用不按照你所想的方式去查找服务器:他们使用指定的 DNS 服务器、使用 HTTPDNS,甚至使用了硬编码的 IPv4 地址。这些都是 DNS64 方案无法解决的,因此 DNS64 只能提升接入层的 IPv6 流量比例,而并不能完全抛弃接入层的 IPv4 协议栈。

所以更好的方案是,让设备去完成这个 IPv4→IPv6 的转换。那么问题来了,设备怎么知道 NAT64 可用、怎么知道使用什么前缀呢?当时 RFC 7050 提供了通过查询 ipv4only.arpa 获得 NAT64 前缀自动配置的方案,但是他依旧受到了 DNS 服务器的限制,不适合反映接入网络的真实情况,支持度也基本局限于蜂窝网络。甚至这个域名加入 IANA 的 Special-Use Domain Names 列表都是 2020 年的事情。

到了 2020 年,RFC 8781 往 Router Advertisement 中新增了一个名为 PREF64(Type 38)的选项,路由器可以在宣告默认路由、前缀等 v6 信息的同时,宣告将 IPv4 地址嵌入什么前缀就可以通过 NAT64 访问 IPv4 网络了。这才是自动发现应该有的最佳方案,毕竟 Router Advertisement 作为通常的终端设备自动发现并配置 IPv6 网络的必要手段,反应的也正是当前网络的信息。

PREF64 in RA and IPv6OnlyPreferred in DHCPv6

2021 年发布的 Android、2022 年发布的 iOS 16 和 macOS 13 都支持了 PREF64 参数。同期支持的还有 RFC 8925:他往 DHCPv4 中新增了一个名为 IPv6-Only Preferred(Option 108)的选项,支持 CLAT 的设备在尝试请求 IPv4 地址时如果收到了这个选项,那么他就会主动放弃 IPv4 协议栈。(当然也可以选择直接关闭 DHCPv4,只是这样不支持 CLAT 的设备就无法访问 IPv4 了。)

2025 年秋天,Windows 发布了 CLAT 的封测,我光速报名了。这下三大厂都支持了,于是便有了文章开头的内容。当然在测试过程中,我也发现了一些问题,已经反馈回相关负责人了。

2 月 19 日,Windows Insider Preview (Canary) 的代码从 Br(溴)更新到了 Kr(氪),build number 为 29531。这个版本引入了 CLAT,只是默认没有启用。如果你家也部署了 IPv6-Only Preferred 的网络,并想体验 Windows 的 CLAT 的话,可以通过以下两个操作开启:

  1. 使用 netsh interface clat set global permit=enabled 开启 CLAT。
  2. HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters 中新增 DhcpIpv6OnlyPreferred = (DWORD)1,这样 Windows 就会在 CLAT 可用时主动放弃 IPv4 协议栈。

因为 Windows 的实现还有些问题,所以暂时不太建议在自家网络之外的地方开启。等 Windows 正式版支持 CLAT 后,微软/苹果/谷歌三大商业操作系统就都有 CLAT 且 IPv6-only capable 了,接入层可以只有 IPv6 的好时代就来了!


另外再借个位置记录一些东西,是我在配置 464XLAT 过程中遇到的一些其他的坑。

首先是 Juniper。我使用 Juniper SRX 系列产品作为家里的防火墙已经有 7 年历史了,配置文件也是祖传的。为了能够把自己的 IPv6 在传出到电信之前转换成电信 DHCPv6-PD 下发的前缀,之前正常的工作的 static NAT 是这么写的:

set security nat static rule-set ctv6 from interface pp0.0
set security nat static rule-set ctv6 rule soha-home match destination-address 240e:390:REDA:CTED::/64
set security nat static rule-set ctv6 rule soha-home then static-nat prefix 2a0e:aa06:40e:beef::/64

而 IPv4 地址嵌入 IPv6 前缀也是一种 static NAT,我又加入了这样的代码:

set security nat static rule-set nat64 from zone INT-soha-home
set security nat static rule-set nat64 rule plat-soha match source-address ::/0
set security nat static rule-set nat64 rule plat-soha match destination-address 2a0e:aa06:40e:64::/96
set security nat static rule-set nat64 rule plat-soha then static-nat inet

结果 PLAT 一直不工作,打 trace 以后注意到系统说 The packet destination ip is not same as source ip version, drop it。原来之前因为偷懒,match 条件只写了 destination-address 而没写 source-address ::/0,导致他在处理跨地址簇报文的时候爆炸了。补回去以后工作正常,又顺手把 source NAT 那边的也都补上了。

然后是 Android 的坑。刚设置上 IPv6-Only Preferred 的时候,Android 手机直接掉线,显示 Wi-Fi 连接失败,而我可以确认他是在收到 DHCPv4 响应以后断开的,一开始我百思不得其解,直到发现了我手机上并没有 IPv6 的默认路由,紧接着注意到系统上设置了 accept_ra_min_lft=180。

accept_ra_min_lft on Android

我配置的 Router Advertisement 的最大发送间隔是 45s,而 Juniper 默认在 Router Advertisement 报文中填入的 lifetime 是最大发送间隔的 3 倍,即 135s,小于 Android 的默认 180s 限制,故不会自动产生默认路由。没有默认路由更别提上网了,所以 Android 就显示了“Wi-Fi 连接失败”。修改 lifetime 后 Android 也正常工作了。

这种行为比较违反 RFC,RFC 4861 可是说“receivers should handle any value”,只要 >0 就应当创建默认路由的。但 Android 这么设计是为了减少收到 RA 时拉起系统处理的电量消耗,变更发生于 2023 年 9 月。这下同样解答了我之前的另外一个疑惑,最近半年多来总是发现手机在用 IPv4 上网,而我一直以为是自己 IPv6 网太烂而回落到了 IPv4……


协议: 本文根据 Creative Commons Attribution-NonCommercial-ShareAlike 4.0 License 进行授权。

标签: ipv6 nat64


  • 这篇文章一条评论也没有……

撰写新评论

account_circle
mail
insert_link
mode_comment