OpenVPN 重启后 IPv6 出口失效:先查前缀归谁管
隧道重建后 IPv6 就断了。在重启任何组件之前,先弄清委派前缀、客户端地址和回程路由分别归谁负责。
IPv6 在 OpenVPN 重启的那一刻就停止转发流量,而 IPv4 仍在工作。隧道接口被重建,另一个管理其 IPv6 状态的组件要么丢了地址,要么丢了让前缀可达的路由。这里涉及三份彼此独立的状态,各自有不同的负责人:运营商委派的前缀及其有效期、下发给 VPN 客户端的地址,以及把该客户端前缀送回服务器的上游路由。在动任何守护进程之前,先确认每一份状态归谁管,因为重启错的那一个只会把问题藏一天,并在下次连接时原样返回。
三份 IPv6 状态,三个负责人
| State | Typical owner | What breaks when it is wrong |
|---|---|---|
| Delegated prefix and lifetime | Upstream router or provider via DHCPv6-PD | Clients keep addresses that the upstream no longer routes |
| Client addresses inside the tunnel | OpenVPN server config or a delegation daemon | No global address, or an address outside the routed prefix |
| Return route for the client prefix | Upstream router, or the server's own routing table | Outbound works, replies never arrive |
隧道内拥有全局范围的地址本身并不够。上游路由器仍必须把该前缀送回 VPN 服务器,而它们不会因为服务器恰好持有其中一个地址就这么做。
这也是把公网 /64 复制进隧道地址池的风险所在。路由到服务器的前缀,与上游视为 on-link 的前缀并不是一回事,邻居发现也无法填补这个缺口。
改动之前先读取服务器状态
在 IPv6 正常时和重启之后各采集一次状态,然后对比。只读视图就够了,在维护窗口里也不产生任何开销:
ip -6 addr show
ip -6 route show table all
ip -6 rule show
sysctl net.ipv6.conf.all.forwarding
重点看三类差异:某条路由消失了、某个地址转入了已弃用或无效的生命周期,以及某个前缀在前缀重新委派后发生变化,而 VPN 配置仍在通告旧前缀。
如果地址消失但前缀没变,说明负责分配地址的守护进程已不再管理该接口。如果前缀变了而地址没变,说明客户端配置已过期。这两种故障从浏览器看完全一样,却毫无共同点。
决定是否让 dhcpcd 管理这条隧道
dhcpcd 能发现接口,并把 DHCPv6 前缀的一部分委派给下游接口,其配置还可以显式指定委派目标。若没有明确目标,其文档所述行为可能波及其它接口,隧道也就这样拿到了一个没人规划过的地址。
有两种情况应让你把隧道的静态地址交给 OpenVPN:服务器在配置中推送固定地址,且重连时隧道名称会变。两者都成立时,就特意把该隧道排除在委派守护进程之外,并写清原因。
如果反过来,向隧道委派是有意为之,那就实现前缀变更的处理。这意味着在前缀或其有效期变化时做出反应、更新服务器通告的客户端前缀,并重新加回路由。在这种设计里排除该接口,恰恰去掉了唯一知道新前缀的组件。
重启 dhcpcd 看似能修好,因为它会重建状态,但它同时会续订或撤销你没想到的接口配置。这种恢复方式并不构成每次重启 VPN 之后的必备步骤。它还会把你当作证据的计数器清零。更好的做法是对你真正改过的接口做定向重载。
把 IPv6 与 IPv4 NAT 分开验证
先确认转发已开启、上游路由确实指向客户端前缀,再做一次新的、被放行的 IPv6 连接。同时检查客户端自己的 IPv6 路由表,因为客户端显示出公网地址,并不能证明它的请求走了隧道。
IPv4 伪装解决的是另一个问题:私有来源的回程路径。它对已被撤销的 IPv6 前缀毫无作用,加上它也不会让 IPv6 恢复。
最后留下责任记录:谁委派前缀、谁分配客户端地址、谁持有回程路由,以及重连时各自会发生什么。运维手册里的四行字,能让下一个人不必重启三个守护进程才找到真正关键的那一个。