在 Windows 上,OpenVPN 适配器拿到地址、又丢掉地址,每隔几秒重复一次,通常不是 DHCP 故障。适配器被要求使用一个已有别的设备在应答的地址,于是 Windows 判定它不可用,客户端只能从头再来。客户端日志保持安静,因为冲突在它下面、在操作系统自己的网络协议栈里就被解决了。

这一区别很重要,因为所有针对隧道配置的排查步骤都会漏掉服务器端的重复地址。

先查 Windows 事件日志,而不是客户端日志

Windows 把地址冲突记录在 System 日志中,事件 ID 为 4198 和 4199。前者报告检测到某个地址的冲突,后者报告某个接口无法使用它的配置地址。两者都不会提到 OpenVPN,这正是它们容易被忽略的原因。

取出最近的事件,把时间戳与适配器的续租周期对比:Get-WinEvent -FilterHashtable @{LogName='System'; Id=4198,4199} -MaxEvents 40 | Format-Table TimeCreated,Id,Message -Wrap。每隔十五秒出现冲突,对应客户端反复重试同一地址,而某个持有者一直在应答它。连接时只出现一次冲突,则指向一次性碰撞。无论哪种情况,下一个问题都是还有谁占着这个地址。

三种布置会把两台主机放到同一个地址上

先问服务器它认为谁持有该地址,再问网络谁在应答该地址。grep -H ifconfig-push /etc/openvpn/ccd/* 列出每个固定的隧道地址及其所属证书。ip -4 neigh show dev tap0 显示网桥在第二层学到了什么。把这两份清单对比,一分钟内就能找出大多数重复。

三种布置几乎能解释全部情况:client-config-dir 条目把同一个地址固定给两张证书;被桥接的 tap 网段给某台机器分配了另一台主机已静态占用的地址;或者过期的租约文件返回了一个仍在连接的客户端认为属于自己的地址。第三种最隐蔽,因为 ifconfig-pool-persist 会故意为已知客户端复用地址。

让地址唯一,而不是去抢它

启动顺序只决定今天谁赢,所以任何依赖重启次序的修法都应视为临时措施。给每个客户端自己的一行 ifconfig-push,或者从不被任何网桥或服务器接口占用的范围里分配静态地址。还要检查客户端列表中重复的公共名,因为 duplicate-cn 允许两个会话共用一个身份,从而共用一个固定地址。如果某台被桥接的设备确实需要固定地址,就把它移到独立子网,或在服务器会记录的地方从地址池中预留。

只有在弄清楚 persist 文件里存了哪些租约之后再删它,否则下一次重连又会发出同样的地址。

按正确顺序重启,并确认每个地址只有一个持有者

先在服务器上应用改动,再让客户端重连,这样每个参与者都对着修正后的地址池注册。先重启客户端会让它们继续持有服务器仍认为被占用的地址。重启后读取状态文件,确认每个公共名只出现一次、只带一个地址。同一个名字出现两条记录,说明旧会话从未关闭,而不是地址池坏了。

如果客户端仍然保不住地址,检查同一主机上的网桥接口是否也带着它。从厂商设备继承了地址的网桥,即使 VPN 服务器已经把该地址交给某个客户端,仍会为它应答 ARP。

观察超过一整个续租周期再关单

一次干净的读数说明不了什么。至少观察适配器十分钟,并在中途强制重连一次:Get-NetIPAddress -InterfaceAlias "OpenVPN*" | Select-Object IPAddress,AddressState。地址应当挺过这次重连,并保持首选状态。

然后重跑第一步的事件查询,确认它不再返回新内容。跨越一次重连的安静日志才是冲突消失的证据,而单次连接后的安静日志只说明竞争还没发生。最后,测试隧道所服务的那个资源,因为失去地址的网桥可能在 VPN 看起来健康时破坏厂商的路径。

把地址池范围、每个固定地址及其所属主机记录到服务器笔记里。这样下一次碰撞只需几分钟就能发现,而不是花一下午重新排查。

相关文章