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

六月 13, 2026 [linux, networking] #qemu #libvirt #iptables #bridge #networking #docker #debug

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 运行的环境中采用。

关键命令速查

用途命令
查看网桥成员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