仮想マシンにbondingデバイスを追加する方法

#1 はじめに
bondingの仕組みを調べるための環境を構築します。
本記事では環境構築のみ。

図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のbond0は`10.0.0.30`、VM4のeth1は`10.0.0.40`に設定します。

```console:図2

                            10.0.0.0/8 

+----- vm3 -----+           +-- br1 --+          +----- vm4 -----+
|               |           |         |          |               |
|         .30   |           |         |          |               |
|        +---+  |           |         |          |               |
|        | b |  |           |         |          |               |
|        | o |[eth1] --- [vnet2]      |       .40|               |
|        | n |  |           |      [vent4] --- [eth1]            |
|        | d |[eth2] --- [vnet3]      |          |               |
|        | 0 |  |           |         |          |               |
|        +---+  |           |         |          |               |
|               |           |         |          |               |
+--- [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 環境作成手順
NICが壊れているかどうかの判定方法として、以下の2つがあります。
仮想化マシンのNICを使用するので、ここでは、ARP監視を使用してみます。
・MII監視
・ARP監視

##3.1 KVMホストで実施する手順
###3.1.1 仮想ブリッジの作成
KVMホスト仮想にブリッジ(br1)を作成します。

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

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

```console:ブリッジの状態確認
[root@kvm ~]# ip l show dev br1
12: br1: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether ce:67:a9:62:c9:7d 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
12: br1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/ether ce:67:a9:62:c9:7d brd ff:ff:ff:ff:ff:ff
```

###3.1.2 VM3のNIC追加
仮想マシンが実行中のとき、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を2つ追加します。

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

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

```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:b3:86:d2
vnet3      bridge     br1        virtio      52:54:00:fb:90:fb
```


###3.1.3 VM4のNIC追加
VM4にNICを追加します。

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

仮想ブリッジのvnet4にVM4のNIC(`52:54:00:33:a0:fc`)が追加されたことがわかります。

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


##3.2 ゲストで実施する手順
###3.2.1 VM4で実施する手順
NetworkManagerが動作していると、設定したIPアドレスが消えてしまい、
上手く動作しないので、NetworkManagerを停止します。

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

追加したNICにIPアドレスを設定します。

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


###3.2.2 VM3で実施する手順
NetworkManagerが動作していると、設定したIPアドレスが消えてしまい、
上手く動作しないので、NetworkManagerを停止します。

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

bondingデバイスを作成します。

```console:bondingデバイスの作成
[root@vm3 ~]# ip l add bond0 type bond
```

bondingのモードは、`active-backup`に設定します。
また、リンク状態(UP/DOWN)の監視方法は、ARPを使います。

```console:bondingデバイスのモード設定
[root@vm3 ~]# ip link set bond0 type bond arp_interval 1000 arp_ip_target 10.0.0.40 mode active-backup
```

```console:eth1をbondingデバイスに接続
[root@vm3 ~]# ip l set eth1 down
[root@vm3 ~]# ip link set eth1 master bond0
```

```console:eth2をbondingデバイスに接続
[root@vm3 ~]# ip l set eth2 down
[root@vm3 ~]# ip link set eth2 master bond0
```

bondingデバイスにIPアドレス(`10.0.0.30`)を設定します。

```console:bondingデバイスにIPアドレス設定
[root@vm3 ~]# ip addr add 10.0.0.30/8 dev bond0
```

bondingデバイスをUPすると、
ARPパケットがVM4(eth1)に1秒間隔で送信されるようになります。

```bondingデバイスのUP
[root@vm3 ~]# ip link set bond0 up
```




#4 設定の確認方法
activeのデバイスを確認します。
現在は、eth1がactiveであることがわかります。
eth1のリンク断を検出すると、activeなデバイスがeth2に切り替わります。

```console:activeデバイスの確認方法
[root@vm3 ~]# cat /sys/class/net/bond0/bonding/active_slave
eth1
```

ARP監視の間隔を確認します。
ARPパケットが1000(ms)間隔で送信される設定になっていることがわかります。

```console:ARP監視間隔の確認方法
[root@vm3 ~]# cat /sys/class/net/bond0/bonding/arp_interval
1000
```

ARPターゲットを確認します。
ARPパケットは、`10.0.0.40`に対して送信されることがわかります。

```console:ARPターゲットの確認方法
[root@vm3 ~]# cat /sys/class/net/bond0/bonding/arp_ip_target
10.0.0.40
```

#5 動作確認

##5.1 ARP監視の動作確認
VM3のbond0をUPすると、VM3からVM4に対して、ARPパケットが1秒間隔で送信されます。
なお、tsharkコマンドの使い方は[ここ(tsharkコマンドの使い方)](https://qiita.com/hana_shin/items/0d997d9d9dd435727edf)を参照してください。

```
[root@vm4 ~]#  tshark -i eth1 -Y 'arp.opcode==1 or arp.opcode==2' -n
Running as user "root" and group "root". This could be dangerous.
Capturing on 'eth1'
  1 0.000000000 52:54:00:b3:86:d2 -> ff:ff:ff:ff:ff:ff ARP 42 Who has 10.0.0.40?  Tell 10.0.0.30
  2 0.000081854 52:54:00:33:a0:fc -> 52:54:00:b3:86:d2 ARP 42 10.0.0.40 is at 52:54:00:33:a0:fc
  5 1.001786653 52:54:00:b3:86:d2 -> ff:ff:ff:ff:ff:ff ARP 42 Who has 10.0.0.40?  Tell 10.0.0.30
  6 1.001811054 52:54:00:33:a0:fc -> 52:54:00:b3:86:d2 ARP 42 10.0.0.40 is at 52:54:00:33:a0:fc
  9 2.003887200 52:54:00:b3:86:d2 -> ff:ff:ff:ff:ff:ff ARP 42 Who has 10.0.0.40?  Tell 10.0.0.30
 10 2.003908729 52:54:00:33:a0:fc -> 52:54:00:b3:86:d2 ARP 42 10.0.0.40 is at 52:54:00:33:a0:fc
-snip-
```


##5.2 active/backupの動作確認
ping実行中、仮想ブリッジのデバイス(vnet2またはvnet3)をダウンします。
デバイスをダウンすると、activeのデバイスが切り替わることを確認してみます。

VM3(bond0)からVM4(eth1)に対してpingを実行します。
なお、pingコマンドの使い方は、[ここ(pingコマンドの使い方)](https://qiita.com/hana_shin/items/c31b0d05a91244c4db83)を参照してください。

```
[root@vm3 ~]# ping -I bond0 -D 10.0.0.40
```

activeのデバイスを確認します。eth1がactiveであることがわかります。

```console:
[root@vm3 ~]# cat /sys/class/net/bond0/bonding/active_slave
eth1
```


このとき、VM3のeth1が接続している仮想ブリッジのvent2デバイスをダウンします。

```
[root@kvm ~]# ip l set vnet2 down
```

activeのデバイスを確認します。
activeのデバイスがeth1からeth2に切り替わったことがわかります。

```console:
[root@vm3 ~]# cat /sys/class/net/bond0/bonding/active_slave
eth2
```

デバイスがeth1からeth2に切り替わるさい、シーケンス番号31,32,33,34の
ICMPパケットがロストしていることがわかります。
パケットロストの時間としては約5秒であることがわかります。


```console:pingの実行結果
[root@vm3 ~]# ping -I bond0 -D 10.0.0.40
-snip-
[1575039704.503299] 64 bytes from 10.0.0.40: icmp_seq=27 ttl=64 time=0.531 ms
[1575039705.503227] 64 bytes from 10.0.0.40: icmp_seq=28 ttl=64 time=0.449 ms
[1575039706.503992] 64 bytes from 10.0.0.40: icmp_seq=29 ttl=64 time=0.566 ms
[1575039707.504498] 64 bytes from 10.0.0.40: icmp_seq=30 ttl=64 time=0.479 ms
[1575039712.507103] 64 bytes from 10.0.0.40: icmp_seq=35 ttl=64 time=0.598 ms
[1575039713.507608] 64 bytes from 10.0.0.40: icmp_seq=36 ttl=64 time=0.804 ms
[1575039714.508439] 64 bytes from 10.0.0.40: icmp_seq=37 ttl=64 time=0.615 ms
```


##5.3 SystemTapによる動作確認
activeなデバイスが切り替わったときの様子をsystemtapを使って確認してみます。
なお、SystemTapの使い方は、[ここ(SystemTapの使い方)](https://qiita.com/hana_shin/items/9b265b4f9a51f98d0f4d)を参照してください。

```console:スクリプト
[root@vm3 stap]# cat ping.stp
#!/usr/bin/stap

function is_skb_conn(skb)
{
  iphdr  = __get_skb_iphdr(skb)
  daddr  = format_ipaddr(__ip_skb_daddr(iphdr), AF_INET())
  saddr  = format_ipaddr(__ip_skb_saddr(iphdr), AF_INET())
  if((saddr=="10.0.0.30") || (saddr=="10.0.0.40")){
    return 1
  }
}

probe kernel.function("__dev_queue_xmit@net/core/dev.c")
{
  if(is_skb_conn($skb)) {
    net_device = @cast($skb, "struct sk_buff", "kernel<linux/skbuff.h>")->dev
    dev_name = @cast(net_device, "struct net_device", "kernel<linux/netdevice.h>")->name
    printf("pp=%s,dev_name=%s\n", pp(), kernel_string(dev_name))
  }
}
```

systemtapを実行します。

```console:systemtap実行結果
[root@vm3 stap]# stap -v ping.stp
```


VM3からVM4に対してpingを実行します。

```console:pingの実行結果
[root@vm3 ~]# ping -I bond0 -D 10.0.0.40
```

このとき、VM3のeth1が接続している仮想ブリッジのvent2デバイスをダウンします。

```
[root@kvm ~]# ip l set vnet2 down
```

systemtapの実行結果から、activeなデバイスがeth1からeth2に切り替わったことがわかります。
また、ICMPパケットはbond0を通過したあと、eth1(またはeth2)を通過していることがわかります。

```console:systemtap実行結果
[root@vm3 stap]# stap -v ping.stp
-snip-
pp=kernel.function("__dev_queue_xmit@net/core/dev.c:3416"),dev_name=bond0
pp=kernel.function("__dev_queue_xmit@net/core/dev.c:3416"),dev_name=eth1
pp=kernel.function("__dev_queue_xmit@net/core/dev.c:3416"),dev_name=bond0
pp=kernel.function("__dev_queue_xmit@net/core/dev.c:3416"),dev_name=eth2
```

#Z 参考情報
[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)
[7.6. チャンネルボンディングの使用](https://access.redhat.com/documentation/ja-jp/red_hat_enterprise_linux/7/html/networking_guide/sec-using_channel_bonding)

```
[root@vm3 ~]# ip link set bond0 type bond miimon 100 mode active-backup
```