你连上公司 VPN,托盘图标变绿,内网能打开,而两米外的打印机却停止了响应。早上还能用的共享在同一台机器上超时,日志里却没有提到打印机。第一反应是 DNS,因为解析不了的名称看起来像不存在的名称,但问题在地址上:两个适配器现在都声称拥有 192.168.1.0/24,而这个前缀只能由一个路由胜出。按这个顺序走一遍,你会在改动任何东西之前看清是哪个适配器占了它。

从症状入手,而不是从隧道

线索是范围。如果内网能通而每个本地设备都失败,说明隧道是好的,本地路径不是。如果本地设备能通而内网失败,则相反。如果除了一个主机之外都正常,你看到的是一个两侧都存在的地址。

在碰任何设置之前,先记下一个本地地址和一个远端地址。“打印机”测不了;192.168.1.40 可以。这一步就能避免这个问题里最常被浪费的一个小时,也就是为了一个路由决定去重写 DNS 设置。

第 1 步:看两个适配器都在声称这个前缀

打开网络连接(ncpa.cpl),把两个适配器并排读一遍。以太网适配器显示 192.168.1.50/24,网关 192.168.1.1。VPN 适配器显示服务器下发的 192.168.1.201/24。两者都合法,两者都认为这个前缀是 on-link 的,而协议栈只会为它保留一个路由。

Get-NetIPAddress -AddressFamily IPv4 列出地址,Get-NetIPInterface | Sort-Object InterfaceMetric 显示协议栈的使用顺序。那一列度量值就是你即将读到的决定,所以把数字记下来,而不是靠记性。

用很小的代价驳倒 DNS 理论:Resolve-DnsName printer.office.lan 返回的还是昨天的地址。名称解析出来了;随后数据包走错了适配器。这里的两分钟能省下解析器设置里的一个下午。

第 2 步:读真正胜出的路由

Find-NetRoute -RemoteIPAddress 192.168.1.40 会打印针对该地址的接口、下一跳和源地址。把这段输出当作答案,而不是线索。想看哪条记录输了、输了多少,route print -4 给出整张表。

更具体胜过本意。下发的 192.168.0.0/25 胜过本地的 192.168.0.0/24,因为前缀更长,所以 192.168.0.0 到 192.168.0.127 走隧道,而上半段留在本地。半个 LAN 没有任何报错就消失,是那张表的正确行为。

tracert -d 192.168.1.40 从表外确认这一点:如果第一跳是隧道地址而不是你自己的路由器,路由就是全部故事。在 Linux 上同一个问题是 ip route get 192.168.1.40,它把源地址和设备打印在一行里。答案提到 tun0 时,就别再看浏览器了。

第 3 步:在怪罪客户端之前先查回程

正确的去程路由仍然会失败,如果远端无法回包。通过隧道访问客户端 LAN 时,那个 LAN 的网关必须知道隧道地址池在哪里:在那台路由器上加 ip route add 10.8.0.0/24 via 192.168.1.12,并从服务器下发 push “route 192.168.1.0 255.255.255.0”,让客户端知道回程。

没有第一条记录,回包就会离开目标奔向它的默认网关,那台网关把私有地址转给运营商然后丢弃。症状是单向的,也很容易读错:请求出现在 tun0 上,回包却从不出现。

VPN 主机上的 masquerade 规则可以免掉那条静态路由:iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE。它同时隐藏了真实的源地址,让之后每一次查日志都更难。目标主机默认网关写错时会产生同样的沉默,最好是 ICMP 重定向,通常是黑洞,所以抓包要在两端都做。

第 4 步:选一个你能与之相处的修法

针对这个问题有五种修法,它们不能互相替换。选择的依据是同一时间必须保住什么可达,而不是哪条命令最短。

RepairWhat it fixesWhat it costs
Flip interface metricsAlternating access: office now, home later, never bothGet-NetAdapter -Name "Ethernet 2" | Set-NetIPInterface -InterfaceMetric 21; set the VPN adapter back to 5000. A client that rewrites metrics on connect defeats it
Renumber one sideEvery host reachable in both directionsDHCP reservations, printer queues, firewall rules and hard-coded addresses all move
Narrow the advertised prefixRemote hosts really live in the upper halfBreaks the day someone adds a host below the cut
Translate with client-natBoth address plans stay untouchedStateless per-client mapping, unreadable logs, anything embedding addresses breaks
Publish one service with DNATOne service on the VPN host reachable from the LANBoth address plans stay; source addresses and reverse lookups stop being meaningful

度量值是最便宜的选择,也是人们最先动的。改动作用于接口,所以只要隧道是通的就有效,它该待的地方是客户端旁边的脚本里,而不是你会忘掉的 shell 里。它无法让同一个地址在两个网络上同时可达,正是这个限制把其他人都推到了表的下方。

把家里的 LAN 重新编号到 10.10.0.0/24 是干净的解法:一次 DHCP 租约续期,加上提到旧网段的保留地址、打印队列和硬编码地址。把公司那侧重新编号通常不由你决定。两边都支持时 IPv6 能彻底消除冲突,用 fd00::/8 里的唯一本地前缀代替需要记住的东西,但要用 TCP 测应用而不是 ping,因为 ICMP 通的时候服务也可能不通。

OpenVPN 自带的转换选项是最后手段,由服务器下发到客户端专属的目录条目里。它在选项名之后取四个参数——方向、网络、掩码和别名——而别名不是可选的:push "route 192.168.2.0 255.255.255.0" 和 push "client-nat dnat 192.168.2.0 255.255.255.0 192.168.1.0"。网络和掩码是客户端看到的网段(192.168.2.0/24),别名是服务器那侧真实的公司网段(192.168.1.0/24),下发的路由才让那个可见网段可达。于是公司在那台客户端看来就在 192.168.2.x 之下。把映射记进配置说明里,否则下一个人会把它当路由故障来调。

只发布一个服务是转换的窄版,它根本不需要 OpenVPN 选项。VPN 主机上的一条 DNAT 规则重新发布一个端口,于是 LAN 保留自己的地址,并通过它早已知道的主机地址访问服务。代价是常见的:服务把 VPN 主机当作源,于是按客户端分列的日志和反向查询都不再有意义。这覆盖的是一个打印队列或一个管理页面,而不是两侧主机互相可达。

还有什么仍然不会通,以及该记下什么

广播不会跨过被路由的隧道。DLNA、UPnP、NetBIOS 浏览和 Wake-on-LAN 依赖 tun 设备不承载的广播或多播,所以任何路由都修不好发现。用 dev tap 做桥接会延长网段,随之而来的是 DHCP 广播、ARP 和其他每一种发现包;远端路由器 LAN 侧的地址,优先用 route 192.168.2.1 255.255.255.255 这样的单播主机路由。

两台路由器在同一条路上都用 192.168.1.1,属于同一类问题:本地地址总是胜出,所以在某个 LAN 搬走之前,远端路由器页面一直不可达。加第二个适配器在两种情况下都改变不了什么。

在动手改之前先把证据记下来:ip route get 或 Find-NetRoute、对相关地址的 ping -c 3、一次 traceroute,以及客户端上的 tcpdump -ni tun0 icmp,然后是服务器上的路由表和 NAT 计数器。一个方向通、另一个方向死的 ping 指向回程而不是隧道,正是这一个差别决定你真正需要上面哪种修法。

相关文章