跳转至内容

QEMU/高级网络

来自 ArchWiki


高级桥接网络配置

手动创建桥接

本文或本节需要在语言、wiki 语法或风格方面进行改进。请参阅 Help:Style 获取参考。

原因: 本节需要大量清理,且可能包含过时的信息。(请在 Talk:QEMU/Advanced networking 中讨论)
提示 自QEMU 1.1起,网络桥接助手可以在无需额外脚本的情况下设置tun/tap。请参阅 QEMU#使用qemu-bridge-helper进行桥接网络配置

以下将描述如何将虚拟机桥接到主机接口(例如eth0),这可能是最常见的配置。此配置使得虚拟机看起来直接连接在外部网络上,与物理主机处于同一以太网段。

我们将用桥接适配器替换普通的以太网适配器,并将普通的以太网适配器绑定到它上面。

  • 安装 bridge-utils,它提供了用于操作桥接的brctl命令。
  • 启用IPv4转发
# sysctl -w net.ipv4.ip_forward=1

要使更改永久生效,请在/etc/sysctl.d/99-sysctl.conf中将net.ipv4.ip_forward = 0更改为net.ipv4.ip_forward = 1

  • 加载tun模块并配置其在启动时加载。详情请参阅 Kernel modules
  • 可选地创建桥接。详情请参阅 Bridge with netctl。请记住将桥接命名为br0,或更改下面的脚本以匹配您的桥接名称。在下面的run-qemu脚本中,如果未列出br0,则会进行设置,因为默认情况下假设主机不通过桥接访问网络。
  • 创建QEMU用于使用root:kvm权限(750)启动tap适配器的脚本
/etc/qemu-ifup
#!/bin/sh

echo "Executing /etc/qemu-ifup"
echo "Bringing up $1 for bridged mode..."
sudo /usr/bin/ip link set $1 up promisc on
echo "Adding $1 to br0..."
sudo /usr/bin/brctl addif br0 $1
sleep 2
  • /etc/qemu-ifdown中创建QEMU用于关闭tap适配器的脚本,并设置root:kvm权限(750)。
/etc/qemu-ifdown
#!/bin/sh

echo "Executing /etc/qemu-ifdown"
sudo /usr/bin/ip link set $1 down
sudo /usr/bin/brctl delif br0 $1
sudo /usr/bin/ip link delete dev $1
  • 使用visudo将以下内容添加到您的sudoers文件中
Cmnd_Alias      QEMU=/usr/bin/ip,/usr/bin/modprobe,/usr/bin/brctl
%kvm     ALL=NOPASSWD: QEMU
  • 您可以使用以下run-qemu脚本启动QEMU
run-qemu
#!/bin/bash
: '
e.g. with img created via:
qemu-img create -f qcow2 example.img 90G
run-qemu -cdrom archlinux-x86_64.iso -boot order=d -drive file=example.img,format=qcow2 -m 4G -enable-kvm -cpu host -smp 4
run-qemu -drive file=example.img,format=qcow2 -m 4G -enable-kvm -cpu host -smp 4
'

nicbr0() {
    sudo ip link set dev $1 promisc on up &> /dev/null
    sudo ip addr flush dev $1 scope host &>/dev/null
    sudo ip addr flush dev $1 scope site &>/dev/null
    sudo ip addr flush dev $1 scope global &>/dev/null
    sudo ip link set dev $1 master br0 &> /dev/null
}
_nicbr0() {
    sudo ip link set $1 promisc off down &> /dev/null
    sudo ip link set dev $1 nomaster &> /dev/null
}

HASBR0="$( ip link show | grep br0 )"
if [ -z $HASBR0 ] ; then
    ROUTER="192.168.1.1"
    SUBNET="192.168.1."
    NIC=$(ip link show | grep en | grep 'state UP' | head -n 1 | cut -d":" -f 2 | xargs)
    IPADDR=$(ip addr show | grep -o "inet $SUBNET\([0-9]*\)" | cut -d ' ' -f2)
    sudo ip link add name br0 type bridge &> /dev/null
    sudo ip link set dev br0 up
    sudo ip addr add $IPADDR/24 brd + dev br0
    sudo ip route del default &> /dev/null
    sudo ip route add default via $ROUTER dev br0 onlink
    nicbr0 $NIC
    sudo iptables -I FORWARD -m physdev --physdev-is-bridged -j ACCEPT
fi

USERID=$(whoami)
precreationg=$(ip tuntap list | cut -d: -f1 | sort)
sudo ip tuntap add user $USERID mode tap
postcreation=$(ip tuntap list | cut -d: -f1 | sort)
TAP=$(comm -13 <(echo "$precreationg") <(echo "$postcreation"))
nicbr0 $TAP

printf -v MACADDR "52:54:%02x:%02x:%02x:%02x" $(( $RANDOM & 0xff)) $(( $RANDOM & 0xff )) $(( $RANDOM & 0xff)) $(( $RANDOM & 0xff ))
qemu-system-x86_64 -net nic,macaddr=$MACADDR,model=virtio \
    -net tap,ifname=$TAP,script=no,downscript=no,vhost=on \
    $@

_nicbr0 $TAP
sudo ip link set dev $TAP down &> /dev/null
sudo ip tuntap del $TAP mode tap

if [ -z $HASBR0 ] ; then
    _nicbr0 $NIC
    sudo ip addr del dev br0 $IPADDR/24 &> /dev/null
    sudo ip link set dev br0 down
    sudo ip link delete br0 type bridge &> /dev/null
    sudo ip route del default &> /dev/null
    sudo ip link set dev $NIC up
    sudo ip route add default via $ROUTER dev $NIC onlink &> /dev/null
fi

然后,要启动虚拟机,可以执行类似以下操作:

$ run-qemu -hda myvm.img -m 512
/etc/sysctl.d/10-disable-firewall-on-bridge.conf
net.bridge.bridge-nf-call-ip6tables = 0
net.bridge.bridge-nf-call-iptables = 0
net.bridge.bridge-nf-call-arptables = 0

为了在启动时应用上述参数,您还需要在启动时加载br-netfilter模块。否则,当sysctl尝试修改这些参数时,它们将不存在。

/etc/modules-load.d/br_netfilter.conf
br_netfilter

运行sysctl -p /etc/sysctl.d/10-disable-firewall-on-bridge.conf以立即应用更改。

请参阅 libvirt wikiFedora bug 512206。如果在启动时遇到关于不存在文件的sysctl错误,请确保bridge模块在启动时加载。详情请参阅 Kernel module#systemd

或者,您可以配置 iptables,通过添加以下规则允许所有流量在桥接上传输:

-I FORWARD -m physdev --physdev-is-bridged -j ACCEPT

通过iptables在物理设备和Tap设备之间共享网络

本文档或章节可合并至 Internet_sharing

注意: 内容重复,不特定于QEMU。(请在 Talk:QEMU/Advanced networking 中讨论)

桥接网络在有线接口(例如eth0)之间可以正常工作,并且易于设置。但是,如果主机通过无线设备连接到网络,则无法进行桥接。

请参阅 Network bridge#无线接口桥接 作为参考。

克服这一问题的一种方法是设置一个带有静态IP的tap设备,让Linux自动处理其路由,然后通过iptables规则在tap接口和连接到网络的设备之间转发流量。

请参阅 Internet sharing 作为参考。

在那里,您可以找到共享网络设备所需的信息,包括tap和tun设备。以下只是进一步提示了一些所需的主机配置。如上文参考所示,客户端需要配置为静态IP,并将分配给tap接口的IP用作网关。需要注意的是,客户端的DNS服务器在更改连接到网络的宿主设备时可能需要手动编辑。

为了允许每次启动时都进行IP转发,需要在/etc/sysctl.d内的sysctl配置文件中添加以下行:

net.ipv4.ip_forward = 1
net.ipv6.conf.default.forwarding = 1
net.ipv6.conf.all.forwarding = 1

iptables规则可以如下所示:

# Forwarding from/to outside
iptables -A FORWARD -i ${INT} -o ${EXT_0} -j ACCEPT
iptables -A FORWARD -i ${INT} -o ${EXT_1} -j ACCEPT
iptables -A FORWARD -i ${INT} -o ${EXT_2} -j ACCEPT
iptables -A FORWARD -i ${EXT_0} -o ${INT} -j ACCEPT
iptables -A FORWARD -i ${EXT_1} -o ${INT} -j ACCEPT
iptables -A FORWARD -i ${EXT_2} -o ${INT} -j ACCEPT
# NAT/Masquerade (network address translation)
iptables -t nat -A POSTROUTING -o ${EXT_0} -j MASQUERADE
iptables -t nat -A POSTROUTING -o ${EXT_1} -j MASQUERADE
iptables -t nat -A POSTROUTING -o ${EXT_2} -j MASQUERADE

以上假设有3个设备连接到网络,并与一个内部设备共享流量,例如:

INT=tap0
EXT_0=eth0
EXT_1=wlan0
EXT_2=tun0

以上展示了允许有线和无线连接与tap设备共享的转发。

显示的转发规则是无状态的,并且仅用于纯转发。可以考虑限制特定流量,放置防火墙来保护客户机等。然而,这将降低网络性能,而简单的桥接则不包含这些。

附加:无论连接是有线还是无线,如果通过VPN连接到带有tun设备的远程站点,假设打开VPN连接的tun设备是tun0,并且应用了上述iptables规则,那么远程连接也将与客户机共享。这避免了客户机也需要打开VPN连接的需要。同样,由于客户机网络需要是静态的,那么当宿主通过这种方式远程连接时,很可能需要编辑客户机上的DNS服务器。

使用VDE2进行网络配置

本文或本节需要在语言、wiki 语法或风格方面进行改进。请参阅 Help:Style 获取参考。

原因: 本节需要大量清理,且可能包含过时的信息。(请在 Talk:QEMU/Advanced networking 中讨论)

什么是VDE?

VDE代表Virtual Distributed Ethernet。它最初是uml_switch的增强。它是一套用于管理虚拟网络的工具集。

其思想是创建虚拟交换机,这些交换机本质上是套接字,并将物理机和虚拟机“插入”其中。我们此处展示的配置非常简单;然而,VDE的功能远不止于此,它可以连接虚拟交换机,在不同主机上运行它们,并监控交换机中的流量。您可以阅读 该项目的文档

这种方法的优点是您无需为用户添加sudo权限。普通用户不应被允许运行modprobe。

基础知识

VDE支持可以通过vde2包进行安装

在我们的配置中,我们使用tun/tap在主机上创建一个虚拟接口。加载tun模块(详情请参阅 Kernel modules)。

# modprobe tun

现在创建虚拟交换机

# vde_switch -tap tap0 -daemon -mod 660 -group users

此行创建交换机,创建tap0,将其“插入”并允许users组的用户使用它。

接口已插入但尚未配置。要配置它,请运行此命令:

# ip addr add 192.168.100.254/24 dev tap0

现在,您只需以普通用户身份运行KVM并使用这些-net选项:

$ qemu-system-x86_64 -net nic -net vde -hda [...]

像在物理网络中一样配置您的客户机的网络。

提示 您可能需要为tap设备设置NAT以便虚拟机能够访问互联网。更多信息请参阅 Internet sharing#Enable NAT

启动脚本

启动VDE的主脚本示例

/etc/systemd/scripts/qemu-network-env
#!/bin/sh
# QEMU/VDE network environment preparation script

# The IP configuration for the tap device that will be used for
# the virtual machine network:

TAP_DEV=tap0
TAP_IP=192.168.100.254
TAP_MASK=24
TAP_NETWORK=192.168.100.0

# Host interface
NIC=eth0

case "$1" in
  start)
        echo -n "Starting VDE network for QEMU: "

        # If you want tun kernel module to be loaded by script uncomment here
	#modprobe tun 2>/dev/null
	## Wait for the module to be loaded
 	#while ! lsmod | grep -q "^tun"; do echo "Waiting for tun device"; sleep 1; done

        # Start tap switch
        vde_switch -tap "$TAP_DEV" -daemon -mod 660 -group users

        # Bring tap interface up
        ip address add "$TAP_IP"/"$TAP_MASK" dev "$TAP_DEV"
        ip link set "$TAP_DEV" up

        # Start IP Forwarding
        echo "1" > /proc/sys/net/ipv4/ip_forward
        iptables -t nat -A POSTROUTING -s "$TAP_NETWORK"/"$TAP_MASK" -o "$NIC" -j MASQUERADE
        ;;
  stop)
        echo -n "Stopping VDE network for QEMU: "
        # Delete the NAT rules
        iptables -t nat -D POSTROUTING -s "$TAP_NETWORK"/"$TAP_MASK" -o "$NIC" -j MASQUERADE

        # Bring tap interface down
        ip link set "$TAP_DEV" down

        # Kill VDE switch
        pgrep vde_switch | xargs kill -TERM
        ;;
  restart|reload)
        $0 stop
        sleep 1
        $0 start
        ;;
  *)
        echo "Usage: $0 {start|stop|restart|reload}"
        exit 1
esac
exit 0

使用上述脚本的systemd服务示例

/etc/systemd/system/qemu-network-env.service
[Unit]
Description=Manage VDE Switch

[Service]
Type=oneshot
ExecStart=/etc/systemd/scripts/qemu-network-env start
ExecStop=/etc/systemd/scripts/qemu-network-env stop
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target

更改qemu-network-env的权限,使其变为可执行

您可以像往常一样启动qemu-network-env.service

替代方法

如果上述方法不起作用,或者您不想折腾内核配置、TUN、dnsmasq和iptables,您可以执行以下操作来实现相同的结果:

# vde_switch -daemon -mod 660 -group users
# slirpvde --dhcp --daemon

然后,要启动虚拟机以连接到主机的网络:

$ qemu-system-x86_64 -net nic,macaddr=52:54:00:00:EE:03 -net vde disk_image

VDE2桥接

基于 quickhowto: qemu networking using vde, tun/tap, and bridge 图。连接到vde的任何虚拟机都将暴露在外部。例如,每台虚拟机都可以直接从您的ADSL路由器接收DHCP配置。

基础知识

请记住,您需要tun模块和 bridge-utils 包。

创建vde2/tap设备

# vde_switch -tap tap0 -daemon -mod 660 -group users
# ip link set tap0 up

创建桥接

# brctl addbr br0

添加设备

# brctl addif br0 eth0
# brctl addif br0 tap0

并配置桥接接口

# dhcpcd br0

启动脚本

所有设备都必须设置。只有桥接需要IP地址。对于桥接上的物理设备(例如eth0),可以使用netctl通过自定义以太网配置文件来完成:

/etc/netctl/ethernet-noip
Description='A more versatile static Ethernet connection'
Interface=eth0
Connection=ethernet
IP=no

以下自定义systemd服务可用于为users用户组中的用户创建和激活VDE2 tap接口。

/etc/systemd/system/vde2@.service
[Unit]
Description=Network Connectivity for %i
Wants=network.target
Before=network.target

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/bin/vde_switch -tap %i -daemon -mod 660 -group users
ExecStart=/usr/bin/ip link set dev %i up
ExecStop=/usr/bin/ip addr flush dev %i
ExecStop=/usr/bin/ip link set dev %i down

[Install]
WantedBy=multi-user.target

最后,您可以使用 netctl 创建桥接接口

参见