QEMU 虚拟机无法访问网关/公网排查

六月 13, 2026 更新: 八月 16, 2026 [虚拟化与容器] #qemu #libvirt #iptables #网络 #docker #调试

QEMU 虚拟机无法访问网关/公网 排查文档

现象

QEMU 虚拟机(libvirt)无法 ping 通网关 192.168.2.1,也无法访问公网。宿主机网络正常。

环境信息

项目值
宿主机 IP192.168.2.100/24 (vmbr0)
网关192.168.2.1 (MAC: 74:45:2d:96:7f:cc)
虚拟机 IP192.168.2.156 (MAC: 52:54:00:3c:13:73)
物理网卡eno1 (桥接到 vmbr0)
虚拟网桥vmbr0 (含 eno1 + vnet0)
TAP 设备vnet0 (VM 接口,桥接到 vmbr0)
虚拟化QEMU/KVM (libvirt 管理)

排查步骤

1. 确认宿主机网络正常

ping -c 2 192.168.2.1
# 结果:正常,0% 丢包

2. 确认网桥拓扑正确

brctl show vmbr0
# vmbr0:
#   eno1    (物理网卡)
#   vnet0   (虚拟机 TAP 接口)

3. 在 vmbr0 上抓包 — 发现 VM 发出的 ICMP 请求

tcpdump -i vmbr0 -e -n icmp

输出:

22:28:13.582982 52:54:00:3c:13:73 > 74:45:2d:96:7f:cc, ethertype IPv4 (0x0800), length 98: 192.168.2.156 > 192.168.2.1: ICMP echo request, id 10485, seq 68, length 64

结论: VM 发出的包到达了网桥 vmbr0,但没有收到任何 reply。

4. 在物理网卡 eno1 上抓包 — 发现包没有到达物理接口

tcpdump -i eno1 -e -n icmp and host 192.168.2.156
# 结果:0 packets captured

结论: VM 的 ICMP 请求在 vmbr0 到 eno1 之间被丢弃了。

5. 检查 bridge netfilter 设置 — 发现关键问题

sysctl net.bridge.bridge-nf-call-iptables
# net.bridge.bridge-nf-call-iptables = 1

关键发现: bridge-nf-call-iptables=1 意味着桥接的 L2 流量也会经过 iptables 处理。

虽然 /etc/sysctl.conf 中配置的是 =0,但 Docker 启动时会将其设为 1(Docker 依赖此设置来实现容器网络隔离和端口映射)。

6. 检查 iptables FORWARD 链 — 确认丢包点

iptables -L FORWARD -n -v
Chain FORWARD (policy DROP 3727K packets, 1454M bytes)
 pkts bytes target     prot opt in     out     source               destination
  229 24446 ACCEPT     all  --  vmbr0  tun0    192.168.2.0/24       192.168.4.0/22
  477  513K ACCEPT     all  --  tun0   vmbr0   192.168.4.0/22       192.168.2.0/24

根因确认:

数据流示意

VM (192.168.2.156)
    │
    ▼ ICMP echo request
 vnet0 ─── vmbr0 ─── ✗ iptables FORWARD DROP ─── eno1 (物理网卡)
                       (无匹配规则)                │
                                                   ▼
                                             Gateway (192.168.2.1)

修复方案

立即修复

添加规则允许桥接流量通过 iptables:

iptables -I FORWARD -m physdev --physdev-is-bridged -j ACCEPT

持久化

netfilter-persistent save

该命令将当前 iptables 规则保存到 /etc/iptables/rules.v4,重启后自动恢复。

验证

iptables -L FORWARD -n -v
Chain FORWARD (policy DROP)
 pkts bytes target     prot opt in     out     source               destination
  155 44480 ACCEPT     all  --  *      *       0.0.0.0/0            0.0.0.0/0            PHYSDEV match --physdev-is-bridged
tcpdump -i eno1 -e -n icmp
22:30:08.217904 52:54:00:3c:13:73 > 74:45:2d:96:7f:cc: 192.168.2.156 > 192.168.2.1: ICMP echo request
22:30:08.218266 74:45:2d:96:7f:cc > 52:54:00:3c:13:73: 192.168.2.1 > 192.168.2.156: ICMP echo reply

VM 的 ICMP 请求和网关的回复均已正常到达物理网卡,问题解决。

备选方案

如果不想依赖 iptables 规则,也可以禁用 bridge netfilter:

sysctl -w net.bridge.bridge-nf-call-iptables=0

但此方案会导致 Docker 的端口映射和容器间隔离失效,不推荐在有 Docker 运行的环境中采用。

2026-08-07 问题复发:根因是持久化静默失效

同一环境(宿主机 192.168.2.100、vmbr0、网关 192.168.2.1、VM 192.168.2.156)两个月后再次出现 VM 无法 ping 通网关,且 VM 无法访问公网。

排查要点(本次实测)

  1. VM → 宿主 ping 正常(0.15ms):目标为宿主本机 IP,走 INPUT 链,不受 FORWARD 影响
  2. VM → 网关 ICMP 100% 丢包,但 tcpdump -i vnet1 能抓到 VM 发出的 ICMP echo request——包已离开 VM
  3. tcpdump -i eno1 抓不到 VM 的任何 ICMP/ARP——包在网桥转发阶段被丢弃
  4. iptables -L FORWARD -n -v:Chain FORWARD (policy DROP 2917 packets, 1032K bytes)——大量包已被丢弃
  5. lsmod | grep br_netfilter:已加载(Docker 依赖),bridge-nf-call-iptables=1

排查小技巧——对比抓包定位丢包点:

观测点结果结论
VM 内 ping 宿主通INPUT 链正常
vnet1 上有 ICMP 出包有包已离开 VM
eno1 上无 ICMP/ARP无丢弃发生在网桥/IP 转发(FORWARD)阶段

本次修复

相比上文的 physdev 通用规则,按网桥接口的规则更直观、易维护:

iptables -I FORWARD -i vmbr0 -j ACCEPT
iptables -I FORWARD -o vmbr0 -j ACCEPT

验证:VM ping 网关 0.5ms、ping 公网 8.8.8.8 正常。

复发根因:持久化静默失效(重要教训)

上文的 netfilter-persistent save 其实一直没生效:

修复持久化:

apt install iptables-persistent   # 安装 15-ip4tables / 25-ip6tables 插件
netfilter-persistent save          # 这次输出 run-parts: executing ... 插件真正生效

验证:/etc/iptables/rules.v4 已生成且包含 vmbr0 规则。

教训: 任何"保存/持久化"类操作,执行后必须验证产物存在(文件已生成、内容正确),不能只依赖退出码。

关键命令速查

用途命令
查看网桥成员brctl show
查看 bridge netfilter 状态sysctl net.bridge.bridge-nf-call-iptables
查看 iptables FORWARD 链iptables -L FORWARD -n -v
放行网桥转发流量iptables -I FORWARD -i vmbr0 -j ACCEPT / -o vmbr0
网桥上抓包tcpdump -i vmbr0 -e -n icmp
物理接口上抓包tcpdump -i eno1 -e -n icmp
检查持久化插件ls /usr/share/netfilter-persistent/plugins.d/

相关:Wi-Fi 宿主上 VM 无法从局域网访问(macvtap 被 AP 丢弃单播 → proxy-ARP 路由方案)的完整排查记录见虚拟化笔记。 | 保存 iptables 规则 | netfilter-persistent save(确认生成 /etc/iptables/rules.v4) |