IPv6 邻居发现在路由式 VPN 上为何失败
邻居发现是链路本地协议,因此路由式隧道会终结它。网桥可以延伸链路,但这只能作为一项有后果的设计决策。
IPv6 的邻居发现属于某一条链路,而路由式隧道是两条链路之间的边界。这一句话几乎解释了所有“远端主机能按地址响应、却查不到”的报告。检查隧道之前,先检查你正在测试的地址的作用域。链路本地地址只在其自身链路上有意义,请求节点组播组同样如此受限。两者都不会穿过路由器,因此 ping 一个全球地址可以成功,而同一主机的发现却不行。
作用域先于配置
邻居请求与邻居通告携带 255 的跳数限制,并且按设计不会被转发。隧道与远端 LAN 之间的路由器会转发 IPv6 数据包,但不会代它中继邻居消息。所以隧道接口上的邻居缓存里只有对端和很少的其他内容,这是正确行为,而不是需要修复的缺陷。
像 fe80:: 条目这样的链路本地地址也是按区域限定的,同一段文本在另一条链路上可以指另一台主机。从隧道另一侧去测试其中一个,什么都证明不了,只能说明该地址有歧义。
陷阱在于“前缀在链路上”的假设
认为某前缀在链路上的主机会发送邻居请求,而不是把数据包交给路由器,向远端站点路由的 /64 也属于这种情况。当局域网段以“在链路上”的形式到达时,主机会向本地链路询问一台住在别处的主机,结果无人应答。请把一个更小的范围路由到远端主机,或者在该前缀将由之应答的路由器上终结它。
为单一服务使用 /128 是最不令人意外的安排,它彻底消除了歧义。邻居发现规范描述了消息集以及决定这一点的在链路上标志。
桥接是有意改变规则
当隧道承载以太网帧时,远端与本地网段变成同一条链路,组播能到达两侧,邻居发现的表现就像在交换机上一样。随之而来的是后果:广播与组播泛洪、跨越网桥的重复地址检测,以及路由器通告泄漏到本不该看到它们的网段。故障域如今也跨越两个站点。这些都不构成反对桥接的理由,而每一点都是要有意桥接、而非顺带桥接的原因。
为了让发现出现而把配置文件从 TUN 改成 TAP,是操作顺序的错误。服务器、客户端与地址规划必须一致,而移动客户端根本不提供 TAP 接口,所以这种设计在笔记本上能用、在手机上失败。邻居代理与中继是各自有要求的独立机制,属于网络管理员文档化的安排,而不是某个应用的设置。
用应用实际使用的地址去测试
用 ip -6 route get 确认路由,用 ip -6 neigh 查看邻居条目,然后 沿路由路径确认服务。DNS 与回程路径是另外两项检查,健康的邻居条目并不能证明其中任何一项。
如果应用坚持使用链路本地或基于发现的目地址,那是设计层面的讨论,而不是调优练习,因为没有任何客户端设置能让链路范围的机制跨路由器工作。
记下你采用的是哪种设计
在故障发生前记录它是路由式还是桥接式,因为两种部署对这些问题的回答不同,值班工程师不应从配置文件里去推断。
把消息集读一遍,就能消除把链路作用域特性解释成 VPN 缺陷的冲动,而那是这一领域代价最高的误诊。