Set up bridged networking with libvirt and QEMU/KVM.
1. 問題點#
- 宿主機:Ubuntu 24.04 (核心版本6.5.0)
- 虛擬機:Windows 11
- 網路架構:Ubuntu電腦透過乙太網路線直接連到數據機(Modem),數據機還有開Wifi。
使用Virt Manager預設NAT網路下,Windows虛擬機的SMB服務無法被同Wifi網路下的其他裝置存取。所以我要建立一個新的虛擬網路橋接器,讓Windows虛擬機裡面系統跑的SMB服務可以從外部網路存取。
為何要建立新的橋接器?
在你安裝Virt Manager之後,Libvirt會在Linux新增一個叫做「virbr0」的虛擬橋接器(virtual bridge),負責處理虛擬機的網路連線。virbr0本身是作為NAT,在虛擬機開機後,Libvirrt會再新增一個vnet的界面連到virbr0,此時所有虛擬機位於該虛擬NAT之後,虛擬機可連線到外部網路,宿主機能連線到虛擬機,各個虛擬機之間也可以互相連線,但是無法從外部網路連線到虛擬機。
要讓外部網路連線到躲在虛擬NAT背後的虛擬機,是可以透過調整iptables forward規則的方式達成啦,但是規則設定上很麻煩,不如另外弄個完整的橋接器吧,讓它使用真正的橋接網路模式(bridged networking)。
Linux的橋接網路可以在多個裝置之間轉送封包。我們會建立一個新的虛擬橋接器「br0」,並把宿主機的實體乙太網路界面連上該橋接器,如此一來就能從外部網路存取虛擬機服務了。
且在新增br0虛擬橋接器之後,你還是可以讓虛擬機使用原本的virbr0連線。
建議不要使用Wifi無線網路裝置橋接,否則會多一道建立hostapd的手續。請在有乙太網路的狀態下建立橋接器。
2. 設定Libvirt#
用指令列出所有虛擬網路界面
sudo virsh net-list --all
- 列表應該會看到有名為
default
的網路界面,裝置為virbr0
,此為Libvirt自動建立,這就是上面說的NAT模式的虛擬橋接器。
# 範例輸出
Name State Autostart Persistent
----------------------------------------------------
default active yes yes
3. 用NetworkManager建立虛擬橋接器#
很多工具都能建立橋接器,比如NetworkManager、Netplan、iproute2、Network Bridge Utilities,還有KDE和GNOME的圖形界面。
鑑於很多Linux發行版都用NetworkManager管網路,我們就用附屬的nmcli工具來建立橋接器吧。
- 透過
sudo nmcli device status
指令得知,乙太網路連線的裝置(Device)名稱為enp0s2
,網路連線名稱(Name)為乙太網路連線1
# 範例輸出
NAME UUID TYPE DEVICE
乙太網路連線1 b32df45e-3d34-11ef-8781-ebb418880514 ethernet enp2s0
- 透過
ip addr
得知我的區域IP網段為192.168。
# 範例輸出
2: enp2s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether b0:6e:bf:ca:68:94 brd ff:ff:ff:ff:ff:ff
inet 192.168.1.102/24 brd 192.168.1.255 scope global dynamic noprefixroute enp2s0
- 新建一個叫做
br0
的橋接器,關閉STP
sudo nmcli connection add type bridge ifname br0 stp no
- 將
enp2s0
設定為br0的bridge-slave
sudo nmcli connection add type bridge-slave ifname enp2s0 master br0
- 關閉目前的乙太網路連線。
sudo nmcli connection down "乙太網路連線1"
- 確保它不會自動重新連線
sudo nmcli connection modify "乙太網路連線1" connection.autoconnect no
- 啟用剛剛新建的橋接器
sudo nmcli connection up bridge-br0
sudo nmcli connection up bridge-slave-enp2s0
- 用指令
sudo nmcli connection show --active
檢視橋接器狀態,確認連線名稱(Name)為bridge-br0
# 範例輸出
NAME UUID TYPE DEVICE
bridge-br0 50b8e41e-3d34-11ef-a6b9-13326122fb1e bridge br0
bridge-slave-enp2s0 52dd08d8-3d34-11ef-8017-8fce9b54d32b ethernet enp2s0
- 接著分配固定IP給bridge-br0
sudo nmcli connection modify "bridge-br0" ipv4.addresses "192.168.1.101/24"
- 然後設定bridge-br0使用Google的DNS
sudo nmcli connection modify "bridge-br0" ipv4.dns "8.8.8.8"
- 重新啟用連線
nmcli connection up bridge-br0
- 使用指令
sudo ip addr show br0
確認連線狀態
# 範例輸出
11: br0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether 46:d0:69:ba:ce:f5 brd ff:ff:ff:ff:ff:ff
inet 192.168.1.102/24 brd 192.168.1.255 scope global noprefixroute br0
valid_lft forever preferred_lft forever
4. QEMU/KVM虛擬機端的設定#
- (選擇性)使用sysctl關閉Linux核心的Netfilter功能,提昇虛擬機網路效能
sudo mkdir /etc/sysctl.d
sudo bash -c 'cat << EOF > /etc/sysctl.d/99-netfilter-bridge.conf
net.bridge.bridge-nf-call-ip6tables = 0
net.bridge.bridge-nf-call-iptables = 0
net.bridge.bridge-nf-call-arptables = 0
EOF'
- 載入br_netfilter核心模組,並設定開機自動載入
sudo modprobe br_netfilter
echo "br_netfilter" | sudo tee -a /etc/modules-load.d/br_netfilter.conf
- 套用設定
sudo sysctl -p /etc/sysctl.d/99-netfilter-bridge.conf
- 新建一個虛擬網路定義檔
bridged-network.xml
cat << EOF > ~/bridged-network.xml
<network>
<name>bridged-network</name>
<forward mode="bridge" />
<bridge name="br0" />
</network>
EOF
- 用virsh指令,按照xml內容新增一個叫做
bridge-newtork
的網路
sudo virsh net-define ~/bridged-network.xml
- 設定開機自動啟動bridged-network
sudo virsh net-start bridged-network
sudo virsh net-autostart bridged-network
開啟Virt Manager,調整虛擬機網路,選取
bridged-network
開機後Windows應該會自動連上網路,並取得一個區域IP。自此之後,Windows的SMB服務就能讓同一Wifi下的其他裝置存取了。
如果虛擬機本身需要固定IP,請在虛擬機內部設定。Windows開啟「網路和網際網路設定」調整,Linux則是使用nmcli指令修改。