主题
464XLAT:给 IPv4 字面量兜底
NAT64 那一篇结尾留了个问题:DNS64 只能拦截 DNS 查询, 应用里写死的 IPv4 地址它管不了。
这一篇讲业界怎么补这个洞。这是本教程唯一一篇没有配套实验的文档 —— VPP 26.06 没有 CLAT 实现,无法在本环境搭出来。所以这里讲清机制和取舍, 不假装能动手。
问题回顾
text
访问 http://example.com/ ✓ DNS64 合成 AAAA,NAT64 翻译,正常
访问 http://203.0.113.11/ ✗ 没有 DNS 查询
IPv6-only 客户端无法构造这个目的地址这不是边缘情况。实际中大量存在:
text
· 配置文件、脚本里写死的 IP
· App 内置的回源地址列表 / 容灾 IP
· 某些 SDK 的硬编码上报地址
· 在载荷里传 IPv4 地址的协议 —— 经典的是 FTP 的 PORT/PASV 命令
· 用 IP 直连做健康检查的运维工具在运营商的 IPv6-only 移动网里,这类应用会直接不可用。而运营商没法要求 成千上万个 App 都改代码。
464XLAT 的思路:翻两次
既然客户端手里只有 IPv4 地址,那就让客户端本地也有一个 IPv4 栈, 把 IPv4 包翻译成 IPv6 送过 IPv6-only 的网络,到网关那边再翻回 IPv4。
名字就是这么来的:4 → 6 → 4,两次翻译(XLAT = translate)。 RFC 6877 定义了这套组合。
两个组件:
text
CLAT Customer-side transLATor 跑在客户端(手机、CPE)
无状态,把本地 IPv4 包翻译成 IPv6
PLAT Provider-side transLATor 跑在运营商网关
有状态,就是标准的 NAT64关键点:PLAT 就是 NAT64,不需要任何改动。 464XLAT 的全部新增部分都在客户端侧。
CLAT 做什么
CLAT 拿到一个目的地址是 IPv4 的包,用和 NAT64 相同的规则合成 IPv6 地址:
text
应用发出 src=192.0.0.4 dst=203.0.113.11 (IPv4)
↓ CLAT 翻译
送进 IPv6 网络 src=<CLAT前缀>:: dst=64:ff9b::cb00:710b (IPv6)
↓ 穿过 IPv6-only 接入网
↓ PLAT(NAT64)翻译
到达服务器 src=<池地址> dst=203.0.113.11 (IPv4)源地址那一侧稍微复杂些:CLAT 需要一个 IPv6 前缀来表示客户端自己的 IPv4 地址。 通常从运营商分配给这台设备的 /64 里划一小段出来。
客户端上的 IPv4 地址从哪来
CLAT 会在本地创建一个虚拟 IPv4 接口,地址通常是 192.0.0.4 (RFC 7335 专门为此保留的 192.0.0.0/29 段)。
应用看到系统有 IPv4 地址、有 IPv4 默认路由,于是正常走 IPv4 socket, 完全不知道底下发生了什么。这正是 464XLAT 的价值 —— 对应用零改动。
为什么运营商大规模部署它
464XLAT 是目前移动网络 IPv6 化的主流方案。原因是它同时满足两件事:
text
✓ 接入网可以是纯 IPv6 省掉双栈的运维复杂度和地址成本
✓ IPv4-only 应用照常工作 不需要要求任何一个 App 改代码对比其他过渡方案:
text
┌──────────────┬──────────────────┬────────────────────────────┐
│ 方案 │ 接入网 │ 主要问题 │
├──────────────┼──────────────────┼────────────────────────────┤
│ 双栈 │ IPv4 + IPv6 │ 仍需给每个终端 IPv4 地址 │
│ │ │ 两套网络两套运维 │
├──────────────┼──────────────────┼────────────────────────────┤
│ NAT64+DNS64 │ 纯 IPv6 │ IPv4 字面量应用失效 │
│ 单独用 │ │ DNSSEC 冲突 │
├──────────────┼──────────────────┼────────────────────────────┤
│ 464XLAT │ 纯 IPv6 │ 需要客户端支持 CLAT │
│ │ │ 双重翻译有额外开销 │
├──────────────┼──────────────────┼────────────────────────────┤
│ DS-Lite │ 纯 IPv6 │ 隧道封装,MTU 问题更突出 │
│ │ │ 网关侧状态开销更大 │
└──────────────┴──────────────────┴────────────────────────────┘CLAT 的支持情况其实很好:Android 从 4.3 起内置, iOS、部分 CPE 和 OpenWrt 也支持。所以"需要客户端支持"在移动场景下基本不是障碍。
代价
双重翻译带来的三个实际问题
MTU 收缩。 IPv6 头比 IPv4 头大 20 字节。翻译后包会变大, 如果路径 MTU 不够就要分片或触发 PMTUD。移动网里 MTU 本来就紧张, 这是 464XLAT 部署时最常见的调优点。
排障链路变长。 一个故障可能出在 CLAT、IPv6 接入网、PLAT、 或者真正的 IPv4 侧。抓包要在两个协议族里对照看, 而且中间那段的地址是合成出来的,不直观。
仍然离不开 IPv4 地址。 PLAT 侧的 NAT64 地址池还是 IPv4。 464XLAT 让终端不再需要 IPv4 地址,但网关侧需要 —— 和 CGN 是同一个账。
VPP 在这套方案里的位置
text
✓ VPP 能做 PLAT
就是 nat64 插件,前面那一篇的配置直接可用
✗ VPP 26.06 没有 CLAT
CLAT 是客户端侧组件,通常由终端操作系统或 CPE 实现,
不是转发器的活所以如果你在用 VPP 建 IPv6-only 接入网:
text
网关侧 VPP + nat64 插件 ← 本教程第 5 章第一篇
DNS BIND / Unbound 的 DNS64 ← 独立部署,前缀必须一致
客户端 Android / iOS / CPE 自带 ← 不需要你做什么三处前缀必须完全一致
NAT64 前缀在三个地方出现,任何一处不一致,整套就不通:
text
1. VPP: nat64 add prefix 64:ff9b::/96
2. DNS64: dns64 prefix 64:ff9b::/96
3. 客户端 CLAT: 通过 RFC 7050(查询 ipv4only.arpa)自动发现,
或手工配置第 3 点值得展开:客户端怎么知道该用哪个前缀?标准做法是 RFC 7050 的启发式发现 —— 客户端查询 ipv4only.arpa 这个域名的 AAAA 记录。 这个域名只有 A 记录(192.0.0.170 和 192.0.0.171), 所以 DNS64 会用自己的前缀合成一条 AAAA 返回。客户端拿到合成结果, 减去已知的 IPv4 地址,就反推出了前缀。
聪明,但也脆弱:如果 DNS64 没配对,或者客户端用了不经过 DNS64 的解析器 (比如自带的 DoH),前缀发现就会失败。 这是 464XLAT 部署里 排障频率最高的一环。
小结
| 解决什么 | DNS64 管不了的 IPv4 地址字面量 |
| 机制 | 4→6→4 双重翻译:客户端 CLAT + 网关 PLAT |
| PLAT | 就是标准 NAT64,VPP 的 nat64 插件可直接充当 |
| CLAT | 客户端侧,VPP 不实现;Android 4.3+ / iOS / 部分 CPE 内置 |
| 主要代价 | MTU 收缩、排障链路变长、网关侧仍需 IPv4 地址池 |
| 部署要点 | NAT64 前缀在 VPP、DNS64、客户端三处必须一致 |
| 前缀发现 | RFC 7050,查询 ipv4only.arpa;DoH 会绕过它导致失败 |
第 5 章到此结束。IPv6 过渡这一块的全貌:
text
纯 IPv6 客户端 → IPv4 服务 NAT64(+ DNS64)
IPv4 字面量兜底 464XLAT(CLAT + PLAT)
IPv6 → IPv6 地址替换 NAT66 / NPTv6VPP 能覆盖其中的 NAT64(作为 PLAT)和有限的 NAT66, DNS64 和 CLAT 需要其他组件配合。
接下来第 6 章看两个偏门但有意思的翻译器:pnat(策略 1:1 NAT) 和 cnat(带负载均衡的翻译)。