双栈隧道不是一个决定,而是三个,而且可以分别作答。外层隧道跑在哪个协议族上,客户端在内部获得哪些协议族,受保护的服务实际在哪些协议族上应答。把它们当作一个选择的设计,要么承载了从来不需要的 IPv6,要么分发了任何东西都无法回程路由的地址。把这三者分开,其余配置几乎就是机械的。

三类地址,分别决定

外部地址是两个网关互相到达的方式,它可以是 IPv4,而内部流量同时承载两个协议族。内部虚拟地址是客户端在隧道内的身份,而每个协议族都需要自己的地址池。受保护前缀是客户端可以到达的范围,它们属于服务而不是隧道。把第三类与第二类混为一谈会造成大多数规划错误,因为两者看起来都像私有网段。

仅 IPv4 的外部传输加上双栈内部流量,是一种正常且得到良好支持的安排。

把每一个都明确决定下来并写下来。如果外部路径只有 IPv4,就说明这一点,不要再担心 IPv6 端点的可达性。如果某个协议族在内部被分配了,但没有任何东西在它上面应答,要么把它从计划中去掉,要么让这些服务真正支持双栈。

在每个协议族中分配一个虚拟地址

在 swanctl 配置中,客户端请求虚拟地址,每个协议族中的通配符要求响应方各分配一个。响应方从地址池中取用,而地址池是与网关背后网络相互独立的对象。在计划中也把它们分开,因为与某个服务前缀重叠的地址池会产生看似正确、直到流量落到意外位置才暴露的路由。

两个都需要时就请求两个协议族,不需要时就只请求一个,因为未使用的地址是没有收益的策略面。具体请求只是请求而不是预留,响应方可能按自己的策略返回不同的值。这个细节在任何把地址硬编码进防火墙规则或监控检查的地方都很重要。在连接中按名称引用地址池,并把服务网络排除在地址池之外。

vips = 0.0.0.0, ::

为 IPv6 地址池生成你自己的唯一本地前缀,而不是照搬一个简短的示例值。示例展示的是设置的形状,而不是可以继承的默认值。

把目的地与地址池分开描述

流量选择器描述每一侧期望交换什么,因此网关的本地选择器应当写受保护的服务前缀,而不是客户端地址池。客户端的远程选择器反映这一意图。当两份清单描述的是不同对象时,隧道可以建立起来,却仍然什么都送不过去。

这些前缀需要在网关网络内部有回程路由和允许的转发,对你启用的每个协议族都是如此。

私有 IPv6 计划正是在这里开始不够用。唯一本地编址买来的是私有可达性,并不产生公网 IPv6 出口,所以假设这个未编号的协议族会直接沿用 IPv4 伪装规则的设计会让用户失望。如果公网 IPv6 访问属于需求的一部分,它需要一条有路由的计划和一个同意这样做的上游。否则就明确说明 IPv6 一侧仅限私有,这是一条诚实的边界,而不是需要掩盖的缺陷。

让 DNS 和回程路径成为有意为之

名称解析并不跟随传输。解析器可以通过仅 IPv4 的隧道返回 A 和 AAAA 记录,客户端随后会尝试一个你从未规划的协议族。决定哪些服务名应当带有可用的双栈应答,并让这两条路径都成为有意为之。一个解析到没有可用路径的协议族的名字,造成的是停顿而不是干净的错误,而停顿要难诊断得多。

单独记录外部端点的可达性,包括是否涉及任何 NAT 穿透以及它使用哪个协议族。

然后用六行写下这份计划:外部端点、IPv4 地址池、IPv6 地址池、IPv4 服务前缀、IPv6 服务前缀,以及拥有每一项应答的路由器。这种形状的计划回答了运维人员在凌晨三点真正会问的问题。任何更短的东西都会把双栈变成拧在仅 IPv4 策略上的第二个地址,而这通常就是它半途而废的原因。

相关文章