经由 IPv4 VPN 服务器的 IPv6 仍然需要真正的 IPv6 出口
在 IPv4 隧道里承载 IPv6 只是容易的那一半。出口仍然需要真实的 IPv6 上游、可用的源前缀,以及为地址池准备的返回路由。
隧道建起来了,客户端从地址池拿到了 IPv6 地址,可每个 IPv6 目的地仍然超时。隧道里没有任何东西坏掉:IPv6 在 IPv4 传输里跑得很好,而这一直都不是难点。你分配的地址是一个服务器兑现不了的承诺,因为出口没有可以把它送进去的 IPv6 上游。要修的地方在出口,不在客户端配置里。
症状:有地址,没有路径
客户端侧的检查会显示隧道设备上有一个看起来像全球地址的 IPv6 地址,以及一条指向它的路由,所以这件事常被误读成客户端故障。流量进入隧道,服务器解封装,然后数据包落到一台路由表里没有指向任何真实位置的 IPv6 默认路由的主机上。取决于协议栈,你会得到立即不可达或者静默丢弃,无论如何客户端看到的都是一次和防火墙毫无区别的超时。
在碰客户端之前,先读服务器自己的路由表,因为路径缺失的那一半就住在那里。只有一条 IPv4 默认路由的服务器已经把答案告诉你了。
跟踪解封装之后的数据包
在隧道内部,数据包是普通的 IPv6,源地址就是客户端的地址。服务器剥掉传输封装,然后手上是一个必须转发给下一个 IPv6 下一跳的原生 IPv6 包。IPv4 默认路由带不动它,而把它翻译成 IPv4 是另一项没人部署过的功能。这就是全部故障,和 VPN 监听是否通过 IPv4 应答毫无关系。
IPv6 over IPv4 的封装机制在 RFC 4213 里有描述,其中重要的一点是两端仍然需要隧道另一侧真实的 IPv6 连通性。包一层报头并不能凭空变出上游。
两层隧道,两次独立检查
分别验证每一层,因为能用的外层隧道会掩盖坏掉的内层隧道。用从客户端到服务器的 IPv4 ping 检查外层路径,并确认你使用的端口。然后检查服务器上的内层路径,那里 ip -6 route 必须显示一条从真实上游学到的默认路由或具体路由,而且 ip -6 addr 必须显示上游接受的源前缀。再少都只是接口上的装饰。
唯一本地地址,或者一条没有任何东西往回转发的、看起来像全球地址的前缀,会高高兴兴地出现在接口上,然后哪里也去不了。公网可达性由上游授予,不是由地址的形状授予。
客户端也是同样的道理: ip -6 route get 2001:4860:4860::8888 应当指向隧道设备,如果指向物理设备,那客户端根本没有把 VPN 用于 IPv6。
源前缀、大小和 ICMPv6
问清楚上游会接受哪些源前缀用于转发,并把其中之一用于客户端地址池。给你一个 /64 用于服务器自身接口的供应商,并不一定把第二个 /64 或更大的块路由给你,而这个差别是一个下午的调试,一张支持工单用一个问题就能解决。请以书面形式询问,并把答复和配置放在一起。
把协议栈实际使用的额外封装算进预算,并让 ICMPv6 保持工作,包括 Packet Too Big。第二个报头会压缩可用载荷,所以能回应小 ping 的路径在文件传输时仍可能失败,而丢弃全部 ICMPv6 的防火墙会把一个可修复的尺寸问题变成黑洞。在宣布服务之前,先测一次大流量传输。
什么才算完全不同的问题
从纯 IPv6 客户端访问纯 IPv4 应用属于转换而不是隧道,需要类似有意部署的 NAT64 方案。目的端不会因为你多加了一个隧道报头就理解它。历史上的过渡服务同样值得留心:RFC 7526 废弃了 6to4 任播中继模型,所以围绕它写的教程描述的是已经没有维护中的公共中继的东西。
如果服务器没有可用的 IPv6 上游,选择是受支持的上游、另一个出口,或者一份记录在案的、让 IPv6 留在隧道之外的决定。不能接受的是,一边告诉客户端 IPv6 服务存在,一边 他们日常的 IPv6 连接悄悄绕过 VPN。