routerをKVMに立てる

に公開

はじめに

Linuxで作成した仮想routerを他ゲストとホストや同じセグメントの別端末と相互に通信することを目標とします。

動機

OSSを試したり構築の練習をするためにqemu+KVMで仮想マシンを建てて遊んでいます。
ローカルで完結するような、試したいことがでてきたら事前に作成したテンプレートをコピーしてサービスを入れれば良いと思います。
しかし、複数のシステムを同時に構築したとき、動いてくれるか/動かないよなを試しながらホスト側セグメントに影響を出さないようにするためには仮想マシン用のセグメントが欲しいと思い作っていました。

このドキュメントの記載方針について

ネットワーク設定については記載すると長くなってしまうため、設定方針のみを記載し、実際のconfigを末尾に記載します。

実行環境

構成要素 使用環境
ディストロ ArchLinux
ハイパーバイザ KVM
エミュレータ qemu

作業方針

nicの準備

ホストもrouterも外部と直接通信したいが、ゲストは外部と直接通信したくないとき、routerを経由させるために、下記nicを準備します。
今回作成したconfigは補足に記載します。

  1. ホスト/routerが使用するための外部向けnic
    1. ホスト用ブリッジを作成します。
    2. 物理nicをブリッジに接続します。
    3. 仮想nicを作成します。
  2. ゲスト/routerが使用するための内部向けnic
    1. ゲスト用ブリッジを作成します。
    2. tunデバイスをゲスト用ブリッジに接続する形で作成します。

router作成

routerとしたいゲストを下記コマンドにて起動し作成します。
Linuxの場合、 net.ipv4.ip_forward = 1を設定します。
リソースやOVMFのファイルはお好みで変更してください。
libvirtを使う場合は下記オプションが含まれるようにしてください。

qemu-system-x86_64 \
    -boot menu=on ${iso}
    -drive file=disks/rooter/rooter.img,if=ide,format=qcow2 \
    -enable-kvm -machine q35,accel=kvm  -cpu host,hv_vendor_id=whatever,hv_relaxed,hv_spinlocks=0x1fff,hv_vapic,hv_time,kvm=off \
    -smp cpus=4,cores=2,threads=2 -m 4G \
    -drive if=pflash,format=raw,readonly=on,file=disks/rooter/rooter_OVMF_CODE.fd \
    -drive if=pflash,format=raw,file=disks/rooter/rooter_OVMF_VARS.fd \
    -net nic,model=virtio -net tap,ifname=kvm_rooter,script=no,downscript=no

router起動

routerとしたいゲストを下記コマンドにて起動し作成します。
libvirtを使う場合は下記オプションが含まれるようにしてください。

qemu-system-x86_64 \
    -drive file=disks/rooter/rooter.img,if=ide,format=qcow2 \
    -enable-kvm -machine q35,accel=kvm \
    -cpu host,hv_vendor_id=whatever,hv_relaxed,hv_spinlocks=0x1fff,hv_vapic,hv_time,kvm=off \
    -smp cpus=4,cores=2,threads=2 -m 4G \
    -drive if=pflash,format=raw,readonly=on,file=disks/rooter/rooter_OVMF_CODE.fd \
    -drive if=pflash,format=raw,file=disks/rooter/rooter_OVMF_VARS.fd \
    -net nic,model=virtio -net tap,ifname=kvm_rooter,script=no,downscript=no \
    -device virtio-net,netdev=kvm_mcast -netdev socket,id=kvm_mcast,mcast=239.0.0.1:1234

routing設定

下記コマンドにてrouterとしたいゲストにrouting tableを設定します。

ip route add default via 外側 dev 外側nic
ip route add default via 内側 dev 内側nic

ゲスト作成/起動

ゲストマシンの仮想nicとして kvm_mcastを指定して作成/起動してください。

qemu-system-x86_64 \
    -drive file=disks/manage/manage.img,if=ide,format=qcow2 \
    -enable-kvm -machine q35,accel=kvm \
    -cpu host,hv_vendor_id=whatever,hv_relaxed,hv_spinlocks=0x1fff,hv_vapic,hv_time,kvm=off \
    -smp cpus=4,cores=2,threads=2 -m 4G \
    -drive if=pflash,format=raw,readonly=on,file=disks/manage/manage_OVMF_CODE.fd \
    -drive if=pflash,format=raw,file=disks/manage/manage_OVMF_VARS.fd \
    -device virtio-net,netdev=kvm_mcast -netdev socket,id=kvm_mcast,mcast=239.0.0.1:1234

2025/7/23追記: ホストnicのルーターにルーティングを追加

ゲストとして決めたネットワークへ通信できるよう、ホストのnicに設定したネットワークにおけるルーターにゲストネットワークへの通信を仮想ルーターへ回すように設定してください。
忘れていると仮想ルーター以外のゲストへの通信ができません。

まとめ

本記事で作成したrouterを起動すれば他ゲストは外と疎通が取れず、
routerを起動すればホストが所属するセグメントと疎通が取れるようになると思います。

改訂履歴/嘘情報

  1. 「2025/7/23追記: ホストnicのルーターにルーティングを追加」を追加

参考文献

補足: Network config

systemd-networkdを使う場合

下記configを /etc/systemd/network に配置してsystemd-networkdを再起動します。

20-wired.network

[Match]
Name = enp*
 
[Network]
Bridge = hostbr0

40-hostbr0.netdev

[NetDev]
Name=hostbr0
Kind=bridge

41-virtbr0.netdev

[NetDev]
Name=virtbr0
Kind=bridge

42-kvm_rooter.netdev

[NetDev]
Name=kvm_rooter
Kind=tap

43-kvm_mcast.netdev

[NetDev]
Name=kvm_mcast
Kind=tap

50-hostbr0.network

[Match]
Name=hostbr0
[Network]
Address = (address)
DNS = (address)
DNS = (address)
Gateway = (address)

52-kvm_rooter.network

[Match]
Name=kvm_rooter
[Network]
Bridge=hostbr0

53-kvm_mcast

[Match]
Name=kvm_mcast
[Network]
Bridge=virtbr0
DHCP=ipv4

NetworkManagerを使う場合

下記ファイルが生成されるようコマンドを実行するか、 /etc/NetworkManager/system-connections に配置してください。

bridge-hostbr0.nmconnection

[connection]
id=bridge-hostbr0
uuid=07c253b5-c30d-4dc6-b5e9-d95a7c050959
type=bridge
interface-name=hostbr0
timestamp=1752698683

[ethernet]

[bridge]
stp=false

[ipv4]
address1=(address)
dns=(address)
gateway=(address)
method=manual

[ipv6]
addr-gen-mode=default
method=ignore

[proxy]

bridge-slave-enp4s0.nmconnection

[connection]
id=bridge-slave-enp4s0
uuid=b8d93691-303d-4e1e-80c8-f8290e98e552
type=ethernet
controller=07c253b5-c30d-4dc6-b5e9-d95a7c050959
interface-name=enp4s0
port-type=bridge

[ethernet]

[bridge-port]

bridge-slave-kvm_mcast.nmconnection

[connection]
id=bridge-slave-kvm_mcast
uuid=c7d9fbee-6c4c-4019-8e33-80ce80fc1657
type=tun
controller=07c253b5-c30d-4dc6-b5e9-d95a7c050959
interface-name=kvm_mcast
port-type=bridge

[ethernet]

[bridge-port]

bridge-slave-kvm_rooter.nmconnection

[connection]
id=bridge-slave-kvm_rooter
uuid=83e8971d-974e-4c2f-9c37-5ffff0a69103
type=tun
controller=07c253b5-c30d-4dc6-b5e9-d95a7c050959
interface-name=kvm_rooter
port-type=bridge

[ethernet]

[bridge-port]

bridge-virtbr0.nmconnection

[connection]
id=bridge-virtbr0
uuid=5e2078f5-b571-4b3d-ab0b-e7036471ff78
type=bridge
interface-name=virtbr0

[ethernet]

[bridge]

[ipv4]
method=auto

[ipv6]
addr-gen-mode=default
method=auto

[proxy]

kvm_mcast.nmconnection

[connection]
id=kvm_mcast
uuid=2604088b-f6c0-4b42-8c5e-0298b5724bee
type=tun
interface-name=kvm_mcast
controller=virtbr0
port-type=bridge
timestamp=1737243648

[tun]
mode=2
owner=0

[bridge-port]

kvm_rooter.nmconnection

[connection]
id=kvm_rooter
uuid=f1784eb4-083c-477e-879c-4cdef482af2c
type=tun
controller=hostbr0
interface-name=kvm_rooter
port-type=bridge

[tun]
mode=2

[bridge-port]
GitHubで編集を提案

Discussion