QEMU/高度なネットワーキング
目次
高度なブリッジネットワーク構成
ブリッジを手動で作成する
以下では仮想マシンを eth0
などのホストインターフェイスにブリッジする方法を説明しています。おそらく一番よく使われている設定です。この設定では、物理的なホストマシンと同一の Ethernet セグメントに、直接外部ネットワークに仮想マシンが位置するようになります。
通常の Ethernet アダプタをブリッジアダプタで置き換えて、通常の Ethernet アダプタをブリッジアダプタに bind することにします。
- ブリッジを制御するための
brctl
が入っている bridge-utils をインストール。
- 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
モジュールをロードして起動時にロードするように設定してください。詳しくはカーネルモジュールを参照。
- オプションでブリッジを作成します。詳細は netctl でブリッジ接続 を参照してください。ブリッジに
br0
という名前を付けるか、以下のスクリプトをブリッジの名前に変更してください。以下のrun-qemu
スクリプトでは、リストにない場合はbr0
が設定されます。これは、デフォルトではホストがブリッジを介してネットワークにアクセスしていないと想定されているからです。
- QEMU が tap アダプターを有効にする際に使用するスクリプトをパーミッション
root:kvm
750 で作成します:
/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
- QEMU が tap アダプターを無効にする際に使用するスクリプトを
/etc/qemu-ifdown
にパーミッション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 wiki や Fedora bug 512206 を参照。起動中にファイルが存在しないというエラーが起こるときは、起動時に bridge
モジュールをロードするようにしてください。カーネルモジュール#systemd を参照。
または、次のようにルールを追加することで全てのトラフィックをブリッジで通すように iptables を設定することができます:
-I FORWARD -m physdev --physdev-is-bridged -j ACCEPT
iptables による物理デバイスと Tap デバイスのネットワーク共有
ブリッジネットワークは、有線インターフェイス (eth0 など) 間では正常に動作し、セットアップも簡単です。ただし、ホストがワイヤレスデバイスを介してネットワークに接続されている場合、ブリッジはできません。
参考として ネットワークブリッジ#ブリッジ上の無線インターフェイス を参照。
これを克服する1つの方法は、tap デバイスに静的 IP を設定し、linux に自動的にルーティングを処理させ、iptables ルールで tap インターフェイスとネットワークに接続されたデバイス間のトラフィックを転送することです。
参考として インターネット共有 を参照。
tap や tun など、デバイス間でネットワークを共有するために必要なものを見つけることができます。次に、必要なホスト構成のヒントを示します。上記の例で示したように、クライアントは、tap インターフェイスに割り当てられた IP をゲートウェイとして、静的 IP を設定する必要があります。注意点は、DNS サーバーがネットワークに接続されているホストデバイスから別のホストデバイスに変更された場合は、クライアント上の 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つのデバイスが、1つの内部デバイスとトラフィックを共有していると仮定しています。例えば次のようなものです:
INT=tap0 EXT_0=eth0 EXT_1=wlan0 EXT_2=tun0
上記は、tap デバイスとの有線および無線接続の共有を可能にする転送を示しています。
示されている転送ルールはステートレスであり、純粋な転送のためのものです。特定のトラフィックを制限し、ゲストや他の人を保護するためにファイアウォールを設置することを考えることができます。しかし、これらはネットワークパフォーマンスを低下させます。一方、シンプルなブリッジにはそのようなものはありません。
おまけ: 接続が有線または無線のいずれであっても、tun デバイスを使用してリモートサイトに VPN 経由で接続された場合、その接続用にオープンされた tun デバイスが tun0 であり、事前のiptablesルールが適用されていると仮定すると、リモート接続もゲストと共有されます。これにより、ゲストも VPN 接続をオープンする必要がなくなります。繰り返しますが、ゲストネットワークは静的である必要があるため、この方法でホストをリモート接続する場合、おそらくゲスト上の DNS サーバーを編集する必要あります。
VDE2 によるネットワーク
VDE とは?
VDE は Virtual Distributed Ethernet の略です。uml_switch の拡張として始まりました。仮想ネットワークを管理するためのツールボックスです。
基本的にはソケットである仮想スイッチを作成して、物理マシンと仮想マシンを両方ともスイッチに"接続"するという考えになります。以下で説明する設定はとてもシンプルです。ただし、VDE はさらに強力な力を持っており、仮想スイッチ同士を接続したり、別のホストでスイッチを動作させスイッチのトラフィックを監視することなどができます。プロジェクトのドキュメント を読むことを推奨。
この方法の利点はユーザーに sudo 権限を与える必要がないということです。通常ユーザーに modprobe の実行を許可する必要はありません。
基本
VDE サポートは vde2 パッケージでインストールできます。
この設定では、tun/tap を使ってホストに仮想インターフェイスを作成します。tun
モジュールをロード (詳しくはカーネルモジュールを参照):
# modprobe tun
仮想スイッチを作成:
# vde_switch -tap tap0 -daemon -mod 660 -group users
上記のコマンドでスイッチと tap0
が作成され、接続され、そして users
グループのユーザーがスイッチを使えるようにします。
インターフェイスは接続されてもまだ設定がされていません。設定するには、次のコマンドを実行:
# ip addr add 192.168.100.254/24 dev tap0
そして、通常ユーザーで -net
オプションを使って KVM を実行してください:
$ qemu-system-x86_64 -net nic -net vde -hda [...]
物理ネットワークでやるのと同じようにゲストのネットワークを設定してください。
起動スクリプト
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 Bridge
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 でカスタム Ethernet プロファイルを使います:
/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 でブリッジネットワークを作成することが可能です。