QEMU 虚拟机无法访问网关/公网排查
六月 13, 2026 [linux, networking] #qemu #libvirt #iptables #bridge #networking #docker #debugQEMU 虚拟机无法访问网关/公网 排查文档
现象
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 运行的环境中采用。
关键命令速查
| 用途 | 命令 |
|---|---|
| 查看网桥成员 | brctl show |
| 查看 bridge netfilter 状态 | sysctl net.bridge.bridge-nf-call-iptables |
| 查看 iptables FORWARD 链 | iptables -L FORWARD -n -v |
| 网桥上抓包 | tcpdump -i vmbr0 -e -n icmp |
| 物理接口上抓包 | tcpdump -i eno1 -e -n icmp |
| 保存 iptables 规则 | netfilter-persistent save |