仮想マシンにNICを追加する方法

最新版は以下に記載しました。
https://hana-shin.hatenablog.com/entry/2022/06/05/214523

#1 はじめに
仮想ブリッジやtunデバイスの仕組みを調べるための環境を構築します。
本記事では環境構築のみ。

図1のKVMホスト環境に仮想ブリッジ(br1)を作成し、
仮想マシン(vm3,vm4)のNICを仮想ブリッジ(br1)に接続します。
最終的に図2の環境を作成します。図中の`[]`内はインタフェース名を表します。


```console:図1
+----- vm3 -----+                                +----- vm4 -----+
|               |                                |               |
|               |                                |               |
|               |                                |               |
|               |                                |               |
|               |                                |               |
+--- [eth0]-----+                                +--- [eth0] ----+
        | .10                                           | .20
        |                                               |
        |                192.168.122.0/24               |
        |                                               |
+--- [vnet0] ---------------- virbr0 ---------------- [vnet1] ---+
|                                                                |
|                                                                |
+------------------------- [virbr0-nic] -------------------------+
```

VM3とVM4のNICは、`10.0.0.0/8`のネットワークに所属するように作成します。
VM3のeth1は`10.0.0.30`、VM4のeth1は`10.0.0.40`に設定します。

```console:図2
                            10.0.0.0/8 

+----- vm3 -----+           +-- br1 --+          +----- vm4 -----+
|               |           |         |          |               |
|               |.30        |         |       .40|               |
|             [eth1] --- [vnet2]   [vent3] --- [eth1]            |
|               |           |         |          |               |
|               |           |         |          |               |
+--- [eth0]-----+           +---------+          +--- [eth0] ----+
        | .10                                           | .20
        |                                               |
        |                192.168.122.0/24               |
        |                                               |
+--- [vnet0] ---------------- virbr0 ---------------- [vnet1] ---+
|                                                                |
|                                                                |
+------------------------- [virbr0-nic] -------------------------+
```



#2 環境
VMware Workstation 15 Playerで作成した仮想マシンを使用しました。
仮想マシンの中に仮想マシンを作る、いわゆるNested VMを使って、環境を構築しました。
Nested VMの作り方は、[ここ(Nested VMの作り方)](https://qiita.com/hana_shin/items/0c91935ae63f76a8a107)を参照してください。

なお、KVMホスト、および仮想マシン(VM3,VM4)のOS版数は以下のとおりです。

```console:OS版数
[root@kvm ~]# cat /etc/redhat-release
CentOS Linux release 7.6.1810 (Core)

[root@kvm ~]# uname -r
3.10.0-957.el7.x86_64
```

KVMホストのvirshコマンドの版数は以下のとおりです。

```console:virshコマンドの版数
[root@kvm ~]# virsh -v
4.5.0
```



#3 環境作成手順
##3.1 仮想ブリッジの作成
KVMホスト仮想にブリッジ(br1)を作成します。

```console:ブリッジの追加
[root@kvm ~]# ip l add br1 type bridge
```

仮想ブリッジの状態を確認します。状態がDOWNしていることがわかります。

```console:ブリッジの状態確認
[root@kvm ~]# ip l show dev br1
8: br1: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether 6a:d7:01:c0:b7:7b brd ff:ff:ff:ff:ff:ff
```

仮想ブリッジの状態をUPにします。

```console:ブリッジのアップ
[root@kvm ~]# ip l set dev br1 up
```

仮想ブリッジの状態を確認します。状態がUPしていることがわかります。

```console:ブリッジの状態確認
[root@kvm ~]# ip l show dev br1
8: br1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
    link/ether fe:54:00:65:1c:2a brd ff:ff:ff:ff:ff:ff
```

##3.2 NICの追加(VM3)
仮想マシンが実行中のとき、VM3とVM4にNICを追加します。
VM3,VM4ともに実行中であることがわかります。
なお、virshコマンドの使い方は、[ここ(virshコマンドの使い方)](https://qiita.com/hana_shin/items/3fc67e2e6132bd534932)を参照してください。

```console:仮想マシンの状態確認
[root@kvm ~]# virsh list
 Id    名前                         状態
----------------------------------------------------
 1     vm3                            実行中
 2     vm4                            実行中
```

VM3にNICを追加します。

```console:VM3にNICの追加
[root@kvm ~]# virsh attach-interface --type bridge --source br1 --model virtio vm3
インターフェースが正常に接続できました
```

仮想ブリッジのvnet1にVM3のNIC(`52:54:00:65:1c:2a`)が追加されたことがわかります。

```console:VM3のNIC確認
[root@kvm ~]# virsh domiflist vm3
インターフェース 種類     ソース  モデル   MAC
-------------------------------------------------------
vnet0      network    default    virtio      52:54:00:5e:65:7a
vnet2      bridge     br1        virtio      52:54:00:4f:ae:b2
```

次にIPアドレスを設定するため、VM3にsshでログインします。
NetworkManagerを停止します。
動作していると、設定したIPアドレスが消えてしまうことがあるので(原因不明)、
ここでは、NetworkManagerを停止した状態にしてみます。

```console:NetworkManager停止
[root@vm3 ~]# systemctl stop NetworkManager
```

VM3のeth1にIPアドレスを設定します。

```console:IPアドレス設定
[root@vm3 ~]# ip addr add 10.0.0.30/8 dev eth1
```

インタフェース(eth1)のリンクアップをします。

```console:インタフェースのリンクアップ
[root@vm3 ~]# ip link set eth1 up
```




##3.3 NICの追加(VM4)
VM4にNICを追加します。

```console:VM4にNICの追加
[root@kvm ~]# virsh attach-interface --type bridge --source br1 --model virtio vm4
インターフェースが正常に接続できました
```

仮想ブリッジのvnet3にVM4のNIC(`52:54:00:f4:c1:9a`)が追加されたことがわかります。

```console:VM4のNIC確認
[root@kvm ~]# virsh domiflist vm4
インターフェース 種類     ソース  モデル   MAC
-------------------------------------------------------
vnet1      network    default    virtio      52:54:00:35:03:3f
vnet3      bridge     br1        virtio      52:54:00:05:06:0f
```

次にIPアドレスを設定するため、VM4にsshでログインします。
NetworkManagerを停止します。
動作していると、設定したIPアドレスが消えてしまうことがあるので(原因不明)、
ここでは、NetworkManagerを停止した状態にしてみます。

```console:NetworkManager停止
[root@vm4 ~]# systemctl stop NetworkManager
```

VM4のeth1にIPアドレスを設定します。

```console:IPアドレス設定
[root@vm4 ~]# ip addr add 10.0.0.40/8 dev eth1
```

インタフェース(eth1)のリンクアップをします。

```console:インタフェースのリンクアップ
[root@vm4 ~]# ip link set eth1 up
```




#4 動作確認

##4.1 デバイスの確認
br1が本記事で作成した仮想ブリッジです。

```console:仮想ブリッジの確認
[root@kvm ~]# ip l show type bridge
3: virbr0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
    link/ether 52:54:00:80:1d:d6 brd ff:ff:ff:ff:ff:ff
8: br1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
    link/ether fe:54:00:0a:6e:3b brd ff:ff:ff:ff:ff:ff
```

vnet2,vnet3が本記事で作成したtunデバイスです。
ともに、仮想ブリッジ(br1)のインタフェースです。
vnet2はVM3、vnet3はVM4と接続しています。

```console:tunデバイスの確認
[root@kvm ~]# ip l show type tun
4: virbr0-nic: <BROADCAST,MULTICAST> mtu 1500 qdisc pfifo_fast master virbr0 state DOWN mode DEFAULT group default qlen 1000
    link/ether 52:54:00:80:1d:d6 brd ff:ff:ff:ff:ff:ff
5: vnet0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast master virbr0 state UNKNOWN mode DEFAULT group default qlen 1000
    link/ether fe:54:00:5e:65:7a brd ff:ff:ff:ff:ff:ff
6: vnet1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast master virbr0 state UNKNOWN mode DEFAULT group default qlen 1000
    link/ether fe:54:00:35:03:3f brd ff:ff:ff:ff:ff:ff
9: vnet2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast master br1 state UNKNOWN mode DEFAULT group default qlen 1000
    link/ether fe:54:00:0a:6e:3b brd ff:ff:ff:ff:ff:ff
10: vnet3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast master br1 state UNKNOWN mode DEFAULT group default qlen 1000
    link/ether fe:54:00:aa:d6:7a brd ff:ff:ff:ff:ff:ff
```



##4.2 pingによる確認
VM3(eth1)からVM4(eth1)に対してpingを1回実行してみます。
VM4から応答が返ってきていることがわかります。
なお、pingの使い方は、[pingコマンドの使い方](https://qiita.com/hana_shin/items/c31b0d05a91244c4db83)を参照してください。


```console:pingの実行結果
[root@vm3 ~]# ping -I eth1 -c 1 10.0.0.40
PING 10.0.0.40 (10.0.0.40) from 10.0.0.30 eth1: 56(84) bytes of data.
64 bytes from 10.0.0.40: icmp_seq=1 ttl=64 time=1.97 ms

--- 10.0.0.40 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 1.970/1.970/1.970/0.000 ms
```

##4.3 tsharkによる確認
仮想ブリッジのvnet2を通過するICMP echo request/replyパケットをtsharkを使って確認します。
確認をするため、KVMホストでtsharkコマンドを実行します。
なお、tsharkコマンドの使い方は[ここ(tsharkコマンドの使い方)](https://qiita.com/hana_shin/items/0d997d9d9dd435727edf)を参照してください。

```console:tsharkの実行
[root@kvm ~]#  tshark -i vnet2 -Y 'icmp.type==0 or icmp.type==8' -n
```

VM3(eth1)からVM4(eth1)に対してpingを1回実行してみます。

```console:ping実行
[root@vm3 ~]# ping -I eth1 -c 1 10.0.0.40
```

vnet2インタフェースをICMP echoパケットが通過していることがわかります。

```console:tsharkの実行結果
[root@kvm ~]#  tshark -i vnet2 -Y 'icmp.type==0 or icmp.type==8' -n
Running as user "root" and group "root". This could be dangerous.
Capturing on 'vnet2'
  1 0.000000000    10.0.0.30 -> 10.0.0.40    ICMP 98 Echo (ping) request  id=0x0ede, seq=1/256, ttl=64
  2 0.000415425    10.0.0.40 -> 10.0.0.30    ICMP 98 Echo (ping) reply    id=0x0ede, seq=1/256, ttl=64 (request in 1)
```


#5 あと始末
##5.1 VM3のNIC削除
VM3のNICを削除します。VM4も同様に削除します。

```console:VM3のNIC確認
[root@kvm ~]# virsh domiflist vm3
インターフェース 種類     ソース  モデル   MAC
-------------------------------------------------------
vnet0      network    default    virtio      52:54:00:5e:65:7a
vnet2      bridge     br1        virtio      52:54:00:4f:ae:b2
```

```console:VM3のNIC削除
[root@kvm ~]# virsh detach-interface --type bridge --mac 52:54:00:4f:ae:b2 vm3
インターフェースが正常に切断されました
```

##5.2 仮想ブリッジの削除
KVMホストに作成した仮想ブリッジを削除します。

```conole:仮想ブリッジの確認
[root@kvm ~]# ip l show dev br1
11: br1: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether 0e:bc:49:d4:e0:11 brd ff:ff:ff:ff:ff:ff
```

```console:仮想ブリッジの削除
[root@kvm ~]# ip l del dev br1
```

```conole:仮想ブリッジの確認
[root@kvm ~]# ip l show dev br1
Device "br1" does not exist.
```



#6 メモ


```console:仮想マシン側
 0xffffffffc03f6b60 : vp_notify+0x0/0x20 [virtio_pci]
 0xffffffffc03ea720 : virtqueue_kick+0x30/0x50 [virtio_ring]
 0xffffffffc0475584 : start_xmit+0x264/0x500 [virtio_net]
 0xffffffff9f838c66 : dev_hard_start_xmit+0x246/0x3b0 [kernel]
 0xffffffff9f865cea : sch_direct_xmit+0x11a/0x250 [kernel]
 0xffffffff9f83bbe1 : __dev_queue_xmit+0x491/0x650 [kernel]
 0xffffffff9f83bdb0 : dev_queue_xmit+0x10/0x20 [kernel]
 0xffffffff9f888736 : ip_finish_output+0x556/0x7b0 [kernel]
 0xffffffff9f888c93 : ip_output+0x73/0xe0 [kernel]
 0xffffffff9f886817 : ip_local_out_sk+0x37/0x40 [kernel]
 0xffffffff9f8896f6 : ip_send_skb+0x16/0x50 [kernel]
 0xffffffff9f889763 : ip_push_pending_frames+0x33/0x40 [kernel]
 0xffffffff9f8b085d : raw_sendmsg+0x88d/0xa40 [kernel]
 0xffffffff9f8bfeb9 : inet_sendmsg+0x69/0xb0 [kernel]
 0xffffffff9f8193a6 : sock_sendmsg+0xb6/0xf0 [kernel]
 0xffffffff9f819ad1 : SYSC_sendto+0x121/0x1c0 [kernel]
 0xffffffff9f81b5ee : SyS_sendto+0xe/0x10 [kernel]
 0xffffffff9f974ddb : system_call_fastpath+0x22/0x27 [kernel]
```

```console:KVMホスト側
 0xffffffffc08def10 : tun_net_xmit+0x0/0x300 [tun]
 0xffffffff87e38c66 : dev_hard_start_xmit+0x246/0x3b0 [kernel]
 0xffffffff87e65cea : sch_direct_xmit+0x11a/0x250 [kernel]
 0xffffffff87e3bbe1 : __dev_queue_xmit+0x491/0x650 [kernel]
 0xffffffff87e3bdb0 : dev_queue_xmit+0x10/0x20 [kernel]
 0xffffffffc086c3b8 : br_dev_queue_push_xmit+0xa8/0x190 [bridge]
 0xffffffffc086c4d9 : br_forward_finish+0x39/0xa0 [bridge]
 0xffffffffc086c5c9 : __br_forward+0x89/0x1d0 [bridge]
 0xffffffffc086c815 : br_forward+0xa5/0xb0 [bridge]
 0xffffffffc086e1cd : br_handle_frame_finish+0x28d/0x580 [bridge]
 0xffffffffc086e6d2 : br_handle_frame+0x1d2/0x320 [bridge]
 0xffffffff87e38fca : __netif_receive_skb_core+0x1fa/0xa10 [kernel]
 0xffffffff87e397f8 : __netif_receive_skb+0x18/0x60 [kernel]
 0xffffffff87e39880 : netif_receive_skb_internal+0x40/0xc0 [kernel]
 0xffffffff87e3991c : netif_receive_skb+0x1c/0x70 [kernel]
 0xffffffffc08e0085 : tun_get_user+0x815/0xbc0 [tun]
 0xffffffffc08e0496 : tun_sendmsg+0x66/0x90 [tun]
 0xffffffffc0917344 : handle_tx+0x214/0x650 [vhost_net]
 0xffffffffc09177b5 : handle_tx_kick+0x15/0x20 [vhost_net]
 0xffffffffc0902321 [vhost]
```


#Z 参考情報
[Add a second NIC for a libvirt guest](https://kashyapc.fedorapeople.org/virt/add-network-card-in-guest.txt)
[10.6. IP コマンドを使用したボンドおよびブリッジ上での VLAN の使用](https://access.redhat.com/documentation/ja-jp/red_hat_enterprise_linux/7/html/networking_guide/sec-vlan_on_bond_and_bridge_using_ip_commands)