达是酒中趣,琴上偶然音

VMware 偷走了我的 IPv4:一次纯靠 IPv6 完成的远程抢救

· Technology

那天我远程启动了 Mac Studio 上的 VMware Fusion,然后这台机器就”消失”了。

ping 不通,VNC 连不上,SSH 也没有任何回应。而我人在外地,走过去按电源键这个选项并不存在。

最后的结局是:机器一秒都没有宕过,只是它的 IPv4 协议栈被 VMware 悄悄地掐断了。而 IPv6 完好无损——那就是那扇没上锁的后门。


先说结论

VMware Fusion 的某个 vmnet 虚拟网卡抢占了 192.168.50.0/24 这个网段,而这正是局域网真实使用的网段。于是 macOS 在做路由查找时,把所有发往 192.168.50.* 的回包——包括发给自己网关的回包——全都丢进了那块虚拟网卡,而不是 en0

结果就是:包进得来,出不去。从外面看,这台机器像是彻底死了。

而 IPv6 使用带 scope 的路由(%接口),一个 IPv4 网段冲突根本碰不到它,VMware 的 vmnet 也不会去动 IPv6。所以这台机器的 IPv6 链路本地地址自始至终是通的。

修复方式:通过同网段的一台跳板机用 IPv6 SSH 进去,执行 vmnet-cli --stop


排查过程

一、网络路径是好的,只有这台 Mac 是黑的

从我远程的机器(位于 10.10.10.0/24)测试:

目标结果
192.168.50.254(网关)ping 约 8ms,健康
192.168.50.48(Mac Studio)100% 丢包,22 / 5900 / 445 / 548 等所有端口全部静默

网关通、Mac 不通,说明到这个网段的路由和链路都没问题,故障被精确地圈定在这台 Mac 自己身上。

二、known_hosts 帮了大忙

翻了一下本地的 known_hosts192.168.50.48 有 ed25519、rsa、ecdsa 三条记录,说明这台机器上 SSH 是开着的、而且我以前成功连上过。

更有用的是:同网段还有好几台机器也在 known_hosts.20 .21 .30 .45 .88 .137)。这些都是现成的跳板机。

这是一个很容易被忽略的细节——known_hosts 本质上是一份”我曾经能连上的机器清单”,救急的时候它就是地图。

三、跳到同一个二层网段上去看

选了 192.168.50.137(一台 RHEL8,网卡 ens192,地址 192.168.50.137/24)。从它上面看:

ARP 查询 .48      -> ac:de:48:11:22:33   (Apple 的 OUI,不是 VMware 的)
arping .48        -> 约 0.9ms 有回应       (二层活着)
ping .48 (IPv4)   -> 100% 丢包             (IPv4 回包路由坏了)

这三行信息量很大:

  1. arping 有回应 → 机器在二层是活的,网卡在工作,系统没死机。
  2. MAC 是 Apple 的 OUI → 排除了另一个高度可疑的原因:“某台桥接模式的虚拟机配了同样的静态 IP .48,把地址抢走了”。如果是那样,ARP 回来的应该是 VMware 的 OUI(00:0c:29 / 00:50:56 之类)。地址还在 Mac 自己手里,它只是回不了话。
  3. IPv4 ping 不通 → 问题就在 IPv4 的出向路由上。

四、IPv6 那边门是敞开的

Mac 的链路本地地址:fe80::1234:5678:9abc:def0
ping6               -> 0.47ms
SSH (tcp/22)        -> SSH-2.0-OpenSSH_10.2
屏幕共享 (tcp/5900) -> RFB 003.889

到这里就完全确认了:机器健健康康,只有 IPv4 的路由被污染了。


根因

VMware Fusion 会给它的 vmnet1(host-only)和 vmnet8(NAT)半随机地分配 192.168.x.0/24 网段。这次它挑中(或者被配置成)了 192.168.50.0/24——和真实局域网撞了个正着。

于是这台 Mac 上有两块网卡同时宣称拥有同一个前缀,路由查找开始把 192.168.50.* 解析到 vmnet 而不是 en0。IPv4 全面静默,但系统其他一切正常。

这是个特别阴险的故障模式:它不会报错,不会写日志告警,只在你启动虚拟机的那一刻,安静地把这台机器从网上抹掉。


救援

入口:经跳板机,用 IPv6 链路本地地址 SSH

USER 换成 Mac 上的真实账号名:

ssh -J root@192.168.50.137 'USER@fe80::1234:5678:9abc:def0%ens192'

两个关键点:

进去之后:确认冲突,然后清掉

# 这里会看到某个 vmnet 接口也在认领 192.168.50.x:
netstat -rn -f inet | grep 192.168.50

# 停掉 VMware 的所有虚拟网络,IPv4 几秒内就会回来:
sudo /Applications/VMware\ Fusion.app/Contents/Library/vmnet-cli --stop

# 再确认一次,现在应该只有 en0 认领这个网段:
netstat -rn -f inet | grep 192.168.50

如果不想把所有虚拟机都干掉,只想停掉那台闯祸的:

vmrun list
vmrun stop "/path/to/vm.vmx" soft

备选方案:不用 SSH,直接用 VNC

屏幕共享在 IPv6 上是通的,可以把它通过跳板机隧道出来,然后用 VNC 客户端连 localhost:5900

ssh -L 5900:[fe80::1234:5678:9abc:def0%ens192]:5900 root@192.168.50.137

永久解决:把 VMware 挪出局域网网段

治标之后要治本,让它以后再也撞不上:

sudo vi "/Library/Preferences/VMware Fusion/networking"
#   把  VNET_8_HOSTONLY_SUBNET 192.168.50.0  改成  172.16.108.0
#   (如果 VNET_1_ 也落在 192.168.50.0 上,一并改掉)
sudo /Applications/VMware\ Fusion.app/Contents/Library/vmnet-cli --configure
sudo /Applications/VMware\ Fusion.app/Contents/Library/vmnet-cli --start

下次怎么才能不这么狼狈

1. 给这台 Mac 装 Tailscale。 它跑在 utun 接口上,天然免疫 IPv4 前缀冲突。恰恰是这种故障模式下,它还是通的——连跳板机都不需要。

2. 把 VMware 的 vmnet 网段固定在你的局域网永远不会用的私有段,比如 172.16.x.0/24

3. 记住 IPv6 这条逃生通道。 这是这次最大的收获:

# 在同网段任意一台机器上,枚举所有邻居:
ping6 ff02::1%<接口>

# 即使目标主机的 IPv4 完全死透,带 scope 的 SSH 依然能进去:
ssh user@fe80::xxxx:xxxx:xxxx:xxxx%<接口>

很多人把 IPv6 当成”以后才用得上的东西”顺手关掉了。这次它是唯一那条把机器救回来的路。双栈不是冗余,是保险。


速查表

Mac Studio IPv4          192.168.50.48
Mac Studio MAC           ac:de:48:11:22:33   (Apple OUI)
Mac Studio IPv6 链路本地  fe80::1234:5678:9abc:def0
网关                     192.168.50.254
跳板机                   192.168.50.137  (RHEL8, ens192)