QEMU 虚拟机无法访问网关/公网排查
六月 13, 2026 更新: 八月 16, 2026 [虚拟化与容器] #qemu #libvirt #iptables #网络 #docker #调试QEMU 虚拟机无法访问网关/公网 排查文档
现象
QEMU 虚拟机(libvirt)无法 ping 通网关 192.168.2.1,也无法访问公网。宿主机网络正常。
环境信息
| 项目 | 值 |
|---|---|
| 宿主机 IP | 192.168.2.100/24 (vmbr0) |
| 网关 | 192.168.2.1 (MAC: 74:45:2d:96:7f:cc) |
| 虚拟机 IP | 192.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
根因确认:
- FORWARD 链默认策略为
DROP - 仅有的两条涉及 vmbr0 的规则只允许
vmbr0 <-> tun0的 VPN 流量(192.168.2.0/24 <-> 192.168.4.0/22) - 没有规则允许 vmbr0 的普通桥接流量(如 VM -> 网关)
- 因此 VM 发出的包在到达网桥后被 iptables DROP 掉,无法到达物理网卡 eno1
数据流示意
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
-m physdev --physdev-is-bridged精确匹配经过桥接转发的流量- 将该规则插入 FORWARD 链首部,优先于 Docker 的链
持久化
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 无法访问公网。
排查要点(本次实测)
- VM → 宿主 ping 正常(0.15ms):目标为宿主本机 IP,走 INPUT 链,不受 FORWARD 影响
- VM → 网关 ICMP 100% 丢包,但
tcpdump -i vnet1能抓到 VM 发出的ICMP echo request——包已离开 VM tcpdump -i eno1抓不到 VM 的任何 ICMP/ARP——包在网桥转发阶段被丢弃iptables -L FORWARD -n -v:Chain FORWARD (policy DROP 2917 packets, 1032K bytes)——大量包已被丢弃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 其实一直没生效:
- 宿主只安装了
netfilter-persistent,但 iptables 插件(iptables-persistent)从未安装,/usr/share/netfilter-persistent/plugins.d/为空 netfilter-persistent save输出为空、退出码为 0,但/etc/iptables/rules.v4从未生成——静默失败- 因此一旦重启(或 iptables 规则被清空),上次的 FORWARD 规则全部丢失,问题必然复发
修复持久化:
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) |