DHCP 不适用于桥接 QEMU 虚拟机

DHCP 不适用于桥接 QEMU 虚拟机

环境:

  • 带有 DHCP 服务器的路由器。此路由器管理 172.16.0.1/16 子网
  • 安装了 Ubuntu 20.04 的主机,有一个名为 eno0 的网卡。
  • 在主机上运行的 QEMU 虚拟机。

目的:

将虚拟机与主机的网卡桥接起来,通过 DHCP 协议获取 IP。

我已尝试过:

  • 在主机上设置网桥,并eno0在其中添加接口:

    ip link add name br0 type bridge
    ip link set br0 up
    ip link set eno0 master br0
    
  • br0通过 DHCP分配 IP 为:

    dhclient br0
    
  • 使用 tap 网络运行 qemu 机器:

    qemu-system-x86_64 \
         -enable-kvm \
         -nographic \
         -drive format=raw,file=/path/to/img \
         -netdev tap,id=nic0,br=br0,helper=/opt/qemu/libexec/qemu-bridge-helper \
         -device e1000e,mac=52:54:00:12:34:50,netdev=nic0
    
  • 虚拟机启动后,尝试通过udhcpc命令(busybox中的dhclient变体)获取IP:

    udpchc eth0
    

到目前为止,还不能为 eth0 分配子网 172.16.xx/16 中的 IP。

那么,我的配置有什么问题吗?


更新:

为了简化这个实验,我使用一个虚拟接口,比如 dm0,而不是 eno0,并在该 dm0 接口上设置一个 DHCP 服务器。

# Create a dummy interface
ip link add dm0 type dummy

# Create bridge and add dm0 to it
ip link add br0 type bridge
ip link set dm0 master br0

# Bring interfaces up
ip link set br0 up
ip link set dm0 up

# Setup a DHCP server on dm0, managed subnet is 10.0.0.1/24.
# Assign address to dm0 interface
ip addr add dev dm0 10.0.0.1/24

# Initiate QEMU virtual machine
qemu-system-x86_64 \
       -enable-kvm \
       -nographic \
       -drive format=raw,file=/path/to/img \
       -netdev tap,id=nic0,br=br0,helper=/opt/qemu/libexec/qemu-bridge-helper \
       -device e1000e,mac=52:54:00:12:34:50,netdev=nic0

# Try to obtain IP address in virtual machine
udhcpc -i eth0

使用命令在 dm0 和 tap0 接口上运行 tcpdump:

tcpdump -nli dm0
tcpdump -nli tap0

我可以看到 DHCP 服务器收到了 DHCP 请求,并且服务器通过分配新的 IP 地址做出响应。

# From dm0 interface
17:44:50.237133 IP 0.0.0.0.68 > 255.255.255.255.67: BOOTP/DHCP, Request from 52:54:00:12:34:51, length 300
17:44:50.237239 IP 10.0.0.1.67 > 10.0.0.200.68: BOOTP/DHCP, Reply, length 300

但是在接口 tap0 上只能看到 DHCP 请求包,没有看到响应包。

17:44:50.237122 IP 0.0.0.0.68 > 255.255.255.255.67: BOOTP/DHCP, Request from 52:54:00:12:34:51, length 300

看起来响应包被桥过滤掉了,这是VM无法获取有效地址的原因,我不知道如何弄清楚发生了什么。

答案1

我答应以答案的形式提出解决问题的评论,但我忘了。我真丢脸。让我先解决这个问题;@DouglasSu 请接受,以便未来的读者了解解决方案。

问题在于 Netfilter(Linux 防火墙)阻止了该数据包的转发。为什么它会阻止桥接包?

内核中有一个旋钮,用于控制是否调用桥接数据包的 IP 级防火墙。在古老的发行版中net.bridge.bridge-nf-call-iptables,它被称为 ,它控制整个系统行为(例如同时控制所有桥接器的行为)。在这样的旧系统中,您可以添加到/etc/sysctl.d/bridge.conf

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

并且iptables桥接数据包的规则不会被遍历。要检查设置,您可以运行sysctl | grep bridge-nf-call-iptables

在新系统中,每个桥都可以通过nf_call_iptables变量(及其朋友)进行控制:

# ip link add type bridge help
...
                  [ nf_call_iptables NF_CALL_IPTABLES ]
                  [ nf_call_ip6tables NF_CALL_IP6TABLES ]
                  [ nf_call_arptables NF_CALL_ARPTABLES ]
...

例如,您可以在创建期间为每个桥单独设置它:

# ip link add name br0 type bridge nf_call_iptables 0

并使用ip -d link show br0或检查值/sys/class/net/br0/bridge/nf_call_iptables。您还可以在运行时更改其值。

最后,您可以正确配置iptables以允许桥接数据包。匹配physdev以确定数据包是从哪个桥接端口进入的。

答案2

尝试这个:

sudo sysctl -w "net.ipv4.ip_forward=1"
sudo iptables -P FORWARD ACCEPT

相关内容