ホスト OS が Ubuntu 22.04 LTS Desktop の環境において、KVM にインストールした仮想マシンからの通信 IPv4/IPv6 をそれぞれ NAPT する。仮想マシンは IPv4/IPv6 それぞれのプライベートアドレスを持つデュアルスタック構成となる。また、拡張のため物理 NIC に対してもブリッジ接続して別の PC からのインターネット接続も同様に NAPT を行う。
KVM からの Linux Bridge 作成
KVM から Linux Bridge の virbr1 を作成し、 Ubuntu 側で物理 NIC との紐付けなどの細かい編集を行う。初期状態は以下となる。 WiFi でインターネット接続を行っているため、仮想マシンからのインターネット通信はホスト OS の Ubuntu で NAPTを行い、 WiFi からインターネット接続となる。
$ nmcli device
DEVICE TYPE STATE CONNECTION
wlp6s0 wifi connected wlp6s0
virbr0 bridge connected virbr0
eth0 ethernet disconnected --
p2p-dev-wlp6s0 wifi-p2p disconnected --
lo loopback unmanaged --
$ nmcli connection
NAME UUID TYPE DEVICE
wlp6s0 xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx wifi wlp6s0
virbr0 xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx bridge virbr0
仮想ネットワーク名 | guest-virbr1 |
Spanning Tree | off |
Forwarding Delay | 0 |
DNS Server (dnsmasq) | no |
NAT Port Range | 1024 – 65535 |
IPv4 Forwarding Type | NAT |
IPv4 Address | 192.168.255.254/24 |
IPv4 DHCP | なし |
IPv4 Gateway | 192.168.255.254 |
IPv4 Route | Network: 192.168.0.0/16 Gateway: 192.168.255.253 |
IPv6 Forwarding Type | NAT |
IPv6 Network | fd00:0:0:ff::fe/64 |
IPv6 DHCP | なし |
IPv6 Autoconf | なし |
IPv6 Gateway | fd00:0:0:ff::fe |
IPv6 Route | Network: fd00::/8 Gateway: fd00:0:0:ff::fd |
上記の表のとおりに作成する Linux Bridge (virbr1) 用の XML ファイルを定義する。この時、デフォルトの XML を参考して作成する。また、 libvirt により DNS を明示的に OFF にしないと dnsmasq が動作してしまうため、別途 bind などで DNS サーバにしているときは注意が必要となる。
※UUID や MAC アドレスは自動でアサインされるので省略して良い。
$ cat /usr/share/libvirt/networks/default.xml
<network>
<name>default</name>
<bridge name='virbr0'/>
<forward/>
<ip address='192.168.122.1' netmask='255.255.255.0'>
<dhcp>
<range start='192.168.122.2' end='192.168.122.254'/>
</dhcp>
</ip>
</network>
$ cat ~/guest-virbr1.xml
<network>
<name>guest-virbr1</name>
<forward mode='nat'>
<nat ipv6='yes'>
<port start='1024' end='65535'/>
</nat>
</forward>
<bridge name='virbr1' stp='off' delay='0'/>
<dns enable='no'/>
<ip address='192.168.255.254' netmask='255.255.255.0'>
</ip>
<ip family='ipv6' address='fd00:0:0:ff::fe' prefix='64'>
</ip>
<route address='192.168.0.0' prefix='16' gateway='192.168.255.253'/>
<route family='ipv6' address='fd00::' prefix='8' gateway='fd00:0:0:ff::fd'/>
</network>
上記で作成した仮想ネットワーク (guest-virbr1) を KVM に定義・有効化することで連動して Linux Bridge (virbr1) が作成される。
$ virsh
virsh # net-list --all
Name State Autostart Persistent
-------------------------------------------------
default active yes yes
virsh # net-define --file /home/penguin/guest-virbr1.xml
Network guest-virbr1 defined from /home/penguin/guest-virbr1.xml
virsh # net-list --all
Name State Autostart Persistent
---------------------------------------------------
default active yes yes
guest-virbr1 inactive no yes
virsh # net-dumpxml guest-virbr1
<network>
<name>guest-virbr1</name>
<uuid>xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx</uuid>
<forward mode='nat'>
<nat ipv6='yes'>
<port start='1024' end='65535'/>
</nat>
</forward>
<bridge name='virbr1' stp='off' delay='0'/>
<mac address='52:54:00:xx:xx:xx'/>
<dns enable='no'/>
<ip address='192.168.255.254' netmask='255.255.255.0'>
</ip>
<ip family='ipv6' address='fd00:0:0:ff::fe' prefix='64'>
</ip>
<route address='192.168.0.0' prefix='16' gateway='192.168.255.253'/>
<route family='ipv6' address='fd00::' prefix='8' gateway='fd00:0:0:ff::fd'/>
</network>
定義しただけではまだ動作していないので、仮想ネットワークを有効化することで Linux Bridge を作成する。また、起動時に有効化となるように、自動起動も有効にする。
virsh # net-start --network guest-virbr1
Network guest-virbr1 started
virsh # net-autostart --network guest-virbr1
Network guest-virbr1 marked as autostarted
virsh # net-list --all
Name State Autostart Persistent
-------------------------------------------------
default active yes yes
guest-virbr1 active yes yes
virsh # exit
以下のように Linux Bridge が作成されたことを確認する。また、 nft コマンドより NAPT テーブルが連動して追加されていることを確認する。
※ NAPT の対象は同じネットワークセグメントだけになることに注意(ルーティングしたものは NAPT の対象外)
$ nmcli device
DEVICE TYPE STATE CONNECTION
wlp6s0 wifi connected wlp6s0
virbr0 bridge connected virbr0
virbr1 bridge connected (externally) virbr1
eth0 ethernet disconnected --
p2p-dev-wlp6s0 wifi-p2p disconnected --
lo loopback unmanaged --
$ nmcli connection
NAME UUID TYPE DEVICE
wlp6s0 xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx wifi wlp6s0
virbr0 xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx bridge virbr0
virbr1 xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx bridge virbr1
$ brctl show
bridge name bridge id STP enabled interfaces
virbr0 8000.525400xxxxxx no
virbr1 8000.525400xxxxxx no
$ sudo nft list table ip nat
table ip nat {
chain LIBVIRT_PRT {
ip saddr 192.168.255.0/24 ip daddr 224.0.0.0/24 counter packets 3 bytes 406 return
ip saddr 192.168.255.0/24 ip daddr 255.255.255.255 counter packets 0 bytes 0 return
meta l4proto tcp ip saddr 192.168.255.0/24 ip daddr != 192.168.240.0/24 counter packets 0 bytes 0 masquerade to :1024-65535
meta l4proto udp ip saddr 192.168.255.0/24 ip daddr != 192.168.240.0/24 counter packets 0 bytes 0 masquerade to :1024-65535
ip saddr 192.168.255.0/24 ip daddr != 192.168.255.0/24 counter packets 0 bytes 0 masquerade
ip saddr 192.168.122.0/24 ip daddr 224.0.0.0/24 counter packets 57 bytes 6371 return
ip saddr 192.168.122.0/24 ip daddr 255.255.255.255 counter packets 1 bytes 328 return
meta l4proto tcp ip saddr 192.168.122.0/24 ip daddr != 192.168.122.0/24 counter packets 5884 bytes 311224 masquerade to :1024-65535
meta l4proto udp ip saddr 192.168.122.0/24 ip daddr != 192.168.122.0/24 counter packets 1162 bytes 315499 masquerade to :1024-65535
ip saddr 192.168.122.0/24 ip daddr != 192.168.122.0/24 counter packets 3 bytes 180 masquerade
}
chain POSTROUTING {
type nat hook postrouting priority srcnat; policy accept;
counter packets 31839 bytes 2682246 jump LIBVIRT_PRT
}
}
$ sudo nft list table ip6 nat
table ip6 nat {
chain LIBVIRT_PRT {
ip6 saddr fd00:0:0:ff::/64 ip6 daddr ff02::/16 counter packets 0 bytes 0 return
meta l4proto tcp ip6 saddr fd00:0:0:ff::/64 ip6 daddr != fd00:0:0:ff::/64 counter packets 0 bytes 0 masquerade to :1024-65535
meta l4proto udp ip6 saddr fd00:0:0:ff::/64 ip6 daddr != fd00:0:0:ff::/64 counter packets 0 bytes 0 masquerade to :1024-65535
ip6 saddr fd00:0:0:ff::/64 ip6 daddr != fd00:0:0:ff::/64 counter packets 0 bytes 0 masquerade
}
chain POSTROUTING {
type nat hook postrouting priority srcnat; policy accept;
counter packets 22429 bytes 5412657 jump LIBVIRT_PRT
}
}
Linux Bridge と 物理 NIC の紐付け
作成した Linux Bridge (virbr1) に KVM で仮想マシンから VNET を割り当てることで IPv4/IPv6 それぞれのインターネット通信が可能となる。これに加えて別の物理 PC からのインターネットも可能とするために、ホスト OS の物理 NIC (eth0)と Linux Bridge (virbr1) を接続する。
$ nmcli connection add type ethernet slave-type bridge master virbr1 ifname eth0
Connection 'bridge-slave-eth0' (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx) successfully added.
$ nmcli connection
NAME UUID TYPE DEVICE
wlp6s0 xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx wifi wlp6s0
virbr0 xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx bridge virbr0
virbr1 xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx bridge virbr1
bridge-slave-eth0 xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx ethernet eth0
$ nmcli device
DEVICE TYPE STATE CONNECTION
wlp6s0 wifi connected wlp6s0
virbr0 bridge connected virbr0
virbr1 bridge connected (externally) virbr1
eth0 ethernet connected bridge-slave-eth0
p2p-dev-wlp6s0 wifi-p2p disconnected --
lo loopback unmanaged --
$ brctl show
bridge name bridge id STP enabled interfaces
virbr0 8000.525400xxxxxx no
virbr1 8000.525400xxxxxx no eth0
mncli コマンドの設定を反映するため、インタフェースの再起動を ホスト OS 側と KVM 側でそれぞれ行う。
※ KVM 側からだけだと nmcli からの設定がうまく反映されなかった
$ nmcli connection down virbr1; nmcli connection up virbr1
Connection 'virbr1' successfully deactivated (D-Bus active path: /org/freedesktop/NetworkManager/ActiveConnection/18)
Connection successfully activated (master waiting for slaves) (D-Bus active path: /org/freedesktop/NetworkManager/ActiveConnection/19)
$ virsh net-destroy --network guest-virbr1
Network guest-virbr1 destroyed
$ virsh net-start --network guest-virbr1
Network guest-virbr1 started
NAPT の動作確認
KVM の仮想マシンを起動してインターネット通信が可能なことを確認する。
$ virsh start FG-01
$ brctl show
bridge name bridge id STP enabled interfaces
virbr0 8000.xxxxxxxxxxxx no vnet0
virbr1 8000.xxxxxxxxxxxx no eth0
vnet1
$ virsh list
Id Name State
------------------------
1 FG-01 running
virsh # domiflist 1
Interface Type Source Model MAC
------------------------------------------------------------------
vnet0 network default virtio 52:54:00:xx:xx:xx
vnet1 bridge guest-virbr1 virtio 52:54:00:xx:xx:xx
$ virsh console 1
Connected to domain 'FG-01'
Escape character is ^] (Ctrl + ])
FG-01 # execute ping xxxxx.com
PING xxxxx.com (xxx.xxx.xxx.xxx): 56 data bytes
64 bytes from xxx.xxx.xxx.xxx: icmp_seq=0 ttl=46 time=223.7 ms
64 bytes from xxx.xxx.xxx.xxx: icmp_seq=1 ttl=46 time=177.2 ms
64 bytes from xxx.xxx.xxx.xxx: icmp_seq=2 ttl=46 time=996.2 ms
64 bytes from xxx.xxx.xxx.xxx: icmp_seq=3 ttl=46 time=296.1 ms
64 bytes from xxx.xxx.xxx.xxx: icmp_seq=4 ttl=46 time=192.6 ms
--- xxxxx.com ping statistics ---
5 packets transmitted, 5 packets received, 0% packet loss
round-trip min/avg/max = 177.2/377.1/996.2 ms
FG-01 # execute ping6 xxxxx.com
PING xxxxx.com(2001:xxxx:xxxx:xxxx::xxxx) 56 data bytes
64 bytes from 2001:xxxx:xxxx:xxxx::xxxx: icmp_seq=1 ttl=47 time=274 ms
64 bytes from 2001:xxxx:xxxx:xxxx::xxxx: icmp_seq=2 ttl=47 time=282 ms
64 bytes from 2001:xxxx:xxxx:xxxx::xxxx: icmp_seq=3 ttl=47 time=219 ms
64 bytes from 2001:xxxx:xxxx:xxxx::xxxx: icmp_seq=4 ttl=47 time=221 ms
64 bytes from 2001:xxxx:xxxx:xxxx::xxxx: icmp_seq=5 ttl=47 time=240 ms
--- xxxxx.com ping statistics ---
5 packets transmitted, 5 packets received, 0% packet loss, time 4002ms
rtt min/avg/max/mdev = 219.957/247.754/282.518/26.269 ms
参考URL
IPv6 NAT based network
Network XML format
Chapter 16. Configuring virtual machine network connections
Disable or change port of dnsmasq service in libvirt
KVM: how do I add host route when VM starts?