环境:
- 带有 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