VyOSとVRRPでデフォルトゲートウェイの冗長構成を構築する

# 使用技術

## VyOS

- OSSのソフトウェアルータ
- 詳しくは[VyOSをインストールしてSSH接続するまで](https://qiita.com/h_tyokinuhata/items/f1f6da1ba9e1265b4192)を参照

## FHRP

- First Hop Redundancy Protocolの略称
- 後述するVRRP、HSRP、GLBP等のプロトコルの総称
- これらのプロトコルは主に物理的な数台のルータをまとめて仮想的に１台に見せ、デフォルトゲートウェイの冗長化を行う

## VRRP

- Virtual Router Redundancy Protocolの略称
- HSRP(後述)を参考に開発され、IETFでRFCとして標準化されている
- IP上で動作し、プロトコル番号は`112`

### マスタルータ

- VIPを宛先として転送されてきたパケットをルーティングするルータ

### バックアップルータ

- マスタルータがダウンした際にVIPを引き継ぎ、マスタルータに昇格するルータ

### 仮想IPアドレス(VIP)

- マスタルータで保持されるIPアドレス
- デフォルトゲートウェイのIPアドレスとして扱うことが多い
- ちなみにVIPにはマスタルータのIPアドレスを指定可能(HSRPでは不可)

### 仮想MACアドレス

- マスタルータで保持されるMACアドレス
- バージョン2の場合、`00:00:5e:00:01:XX`の形式となる(`XX`にはVRIDが入る)

### VRRPグループ

- マスタルータとバックアップルータの集合

### VRID

- VRRPグループの識別子
- `0`〜`255`の範囲で指定可能

### VRRPプライオリティ

- より高い値を持つルータがマスタルータとなる
- 値が重複した場合、より高いIPアドレスを持つルータがマスタルータとして選定される
    - e.g. `192.168.0.1`と`192.168.0.2`の場合、`192.168.0.2`が選定される

### アドバタイズメント

- マスタルータからバックアップルータに向けて定期的(デフォルトでは１秒)に送信されるパケット
- バックアップルータがアドバタイズメントを一定時間受信できない場合(デフォルトでは３秒)、バックアップルータがマスタルータに昇格する

### プリエンプト

- マスタルータに障害が発生し、バックアップルータがマスタルータに昇格後、初めのマスタルータが復旧すると再びマスタルータに昇格し、現在のマスタルータはバックアップルータに戻るような設定

### バージョン

- バージョンは1(RFC 2338)、2(RFC 3768)、3(RFC 5799)の３種類
- バージョン2が使用されることが多い
- バージョン2での主な変更点
    - バージョン1ではVRRPプライオリティが`0`〜`255`だったが、バージョン2では`0`〜`65535`に拡張された
    - バックアップルータを複数台で構成できるようになった
- バージョン3での主な変更点
    - IPv6に対応
    - IPsecに対応

## HSRP

- Hot Standby Router Protocolの略
- Cisco独自のプロトコル
- HSRPにおけるアクティブルータとスタンバイルータがそれぞれVRRPにおけるマスタルータとバックアップルータのような役割を担う
- HSRPにおけるHelloパケットがVRRPにおけるアドバタイズメントのような役割を担う
- バージョンは1、2の2種類が存在する
- UDP上で動作し、ポート番号は`1985`

## GLBP

- Gateway Load Balancing Protocolの略称
- Cisco独自のプロトコル
- デフォルトゲートウェイの冗長化以外に負荷分散も行う
- つまり、バックアップのポジションにあるルータもアクティブに動作する
- UDP上で動作し、ポート番号は`3222`

# ネットワーク構成

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/78249/3a6733f8-9965-eecf-b4b9-98f86539960b.png)

- `pc01`はVRRPが動作確認用のサーバでUbuntuがインストールされている
- L2スイッチには`pc01`と２台のルータ`router01`と`router02`が接続されている
- `router01`は基本的にマスタルータ、`router02`は基本的にバックアップルータとして振る舞う
- ルータの各インタフェースに割り当てられるIPアドレスは以下の通り

|ホスト名|グローバルIPアドレス(eth1)|プライベートIPアドレス(eth2)|
|:--|:--|:--|
|`router01`|`X.X.X.X/28`|`192.168.0.1/24`|
|`router02`|`Y.Y.Y.Y/28`|`192.168.0.2/24`|
|仮想IPアドレス(VIP)|`Z.Z.Z.Z/28`|`192.168.0.254/24`|

# ネットワークの構築

## バージョンの確認

- 今回使用するVyOSのバージョンは`1.4`
    - 他のバージョンでは設定方法が異なる可能性があるため注意(特にVRRP周り)

```bash
$ show version
Version:          VyOS 1.4-rolling-202303310716
...
```

## 初期設定

- `router01`、`router02`の両方で以下の設定を行った状態
    - `eth1`へのグローバルIPアドレスの割り当て
    - `eth2`へのプライベートIPアドレスの割り当て
    - ネクストホップをスタティックルーティングで設定

```bash:router01
# show interfaces
 ethernet eth1 {
     address X.X.X.X/28
     hw-id 9c:a3:ba:30:74:3a
 }
 ethernet eth2 {
     address 192.168.0.1/24
     hw-id 9c:a3:ba:30:5c:8e
 }
 loopback lo {
 }
```

```bash:router02
# show interfaces
 ethernet eth1 {
     address Y.Y.Y.Y/28
     hw-id 9c:a3:ba:30:fc:42
 }
 ethernet eth2 {
     address 192.168.0.2/24
     hw-id 9c:a3:ba:30:53:06
 }
 loopback lo {
 }
```

```bash
# run show configuration
...
protocols {
    static {
        route 0.0.0.0/0 {
            next-hop 153.127.206.97 {
            }
        }
    }
}
...
```

- `pc01`で以下の設定を行った状態
    - `eth0`へのプライベートIPアドレスの割り当て
    - VIPをデフォルトゲートウェイとして設定

```bash:pc01
$ ip address show dev eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 9c:a3:ba:30:39:07 brd ff:ff:ff:ff:ff:ff
    altname enp0s3
    altname ens3
    inet 192.168.0.10/24 brd 192.168.0.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::9ea3:baff:fe30:3907/64 scope link
       valid_lft forever preferred_lft forever
```

```bash:pc01
$ ip route show default
default via 192.168.0.254 dev eth0 proto static
```

## VRRPの設定

### router01の設定

- 設定モードに移行

```bash:router01
$ configure
```

- `eth1`側の設定
    - `advertise-interval`はアドバタイズの送信間隔(Helloタイマ)
    - `sync-group`を設定すると、`eth1`でフェイルオーバが発生した際に、`eth2`でもフェイルオーバさせることができる(その逆も然り)ため、`eth1`はマスタルータだが`eth2`はバックアップルータのような不一致を避けることができる
- 分からなかった点
    - マスタルータのダウン判定の間隔(Holdタイマ)の設定方法
    - VRRPのバージョンの指定方法

```bash:router01
# set high-availability vrrp group test01 vrid 10
# set high-availability vrrp group test01 interface eth1
# set high-availability vrrp group test01 address Z.Z.Z.Z/28
# set high-availability vrrp group test01 priority 200
# set high-availability vrrp group test01 advertise-interval 1
# set high-availability vrrp sync-group syncgrp01 member test01
```

- `eth2`側の設定

```bash:router01
# set high-availability vrrp group test02 vrid 20
# set high-availability vrrp group test02 interface eth2
# set high-availability vrrp group test02 address 192.168.0.254/24
# set high-availability vrrp group test02 priority 200
# set high-availability vrrp group test02 advertise-interval 1
# set high-availability vrrp sync-group syncgrp01 member test02
```

- 設定の変更を反映し、保存する

```bash:router01
# commit
# save
```

### router02の設定

- `router01`との設定の違いは基本的にVRRPプライオリティのみ
    - `router02`では`router01`よりもVRRPプライオリティを低く設定することでバックアップルータとして動作させる
- 設定モードに移行

```bash:router01
$ configure
```

- `eth1`側

```bash:router02
# set high-availability vrrp group test01 vrid 10
# set high-availability vrrp group test01 interface eth1
# set high-availability vrrp group test01 address Z.Z.Z.Z/28
# set high-availability vrrp group test01 priority 100
# set high-availability vrrp group test01 advertise-interval 1
# set high-availability vrrp sync-group syncgrp01 member test01
```

- `eth2`側

```bash:router02
# set high-availability vrrp group test02 vrid 20
# set high-availability vrrp group test02 interface eth2
# set high-availability vrrp group test02 address 192.168.0.254/24
# set high-availability vrrp group test02 priority 100
# set high-availability vrrp group test02 advertise-interval 1
# set high-availability vrrp sync-group syncgrp01 member test02
```

- 設定の変更を反映し、保存する

```bash:router01
# commit
# save
```

### 設定の確認

- `router01`の`eth1`と`eth2`がマスタルータとして動作している

```bash:router01
# run show vrrp
Name    Interface      VRID  State      Priority  Last Transition
------  -----------  ------  -------  ----------  -----------------
test01  eth1             10  MASTER          200  6m26s
test02  eth2             20  MASTER          200  6m26s
```

- `router02`の`eth1`と`eth2`がバックアップルータとして動作している

```bash:router02
# run show vrrp
Name    Interface      VRID  State      Priority  Last Transition
------  -----------  ------  -------  ----------  -----------------
test01  eth1             10  BACKUP          100  6m31s
test02  eth2             20  BACKUP          100  6m31s
```

- `router01`の`eth1`にはグローバルIPアドレスとVIP、`eth2`にはプライベートIPアドレスとVIPが設定されている

```bash:router01
# ip address show dev eth1
2: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 9c:a3:ba:30:74:3a brd ff:ff:ff:ff:ff:ff
    inet X.X.X.X/28 brd <broadcast_address> scope global eth1
       valid_lft forever preferred_lft forever
    inet Z.Z.Z.Z/28 scope global secondary eth1
       valid_lft forever preferred_lft forever
    inet6 fe80::9ea3:baff:fe30:743a/64 scope link
       valid_lft forever preferred_lft forever

# ip address show dev eth2
3: eth2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 9c:a3:ba:30:5c:8e brd ff:ff:ff:ff:ff:ff
    inet 192.168.0.1/24 brd 192.168.0.255 scope global eth2
       valid_lft forever preferred_lft forever
    inet 192.168.0.254/24 scope global secondary eth2
       valid_lft forever preferred_lft forever
    inet6 fe80::9ea3:baff:fe30:5c8e/64 scope link
       valid_lft forever preferred_lft forever
```

- `router02`の`eth1`にはグローバルIPアドレス、`eth2`にはプライベートIPアドレスが設定されている

```bash:router02
# ip address show dev eth1
2: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 9c:a3:ba:30:fc:42 brd ff:ff:ff:ff:ff:ff
    inet Y.Y.Y.Y/28 brd <broadcast_address> scope global eth1
       valid_lft forever preferred_lft forever
    inet6 fe80::9ea3:baff:fe30:fc42/64 scope link
       valid_lft forever preferred_lft forever

# ip address show dev eth2
3: eth2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 9c:a3:ba:30:53:06 brd ff:ff:ff:ff:ff:ff
    inet 192.168.0.2/24 brd 192.168.0.255 scope global eth2
       valid_lft forever preferred_lft forever
    inet6 fe80::9ea3:baff:fe30:5306/64 scope link
       valid_lft forever preferred_lft forever
```

- ちなみにMacアドレスを`00:00:5e:00:01:XX`の形式にするには以下の設定が追加で必要となる

```bash
# set high-availability vrrp group test01 rfc3768-compatibility
```

### バージョンの確認

- `tcpdump`でアドバタイズメントをキャプチャするとVRRPのバージョンが分かる
    - 今回の設定ではバージョン2が動作している
    - バージョン3を動作させる方法や、VyOSからVRRPのバージョンを確認する方法は分からなかった

```bash:router02
# tcpdump -i eth2 -c 5
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on eth2, link-type EN10MB (Ethernet), snapshot length 262144 bytes
23:42:30.862619 IP 192.168.0.1 > vrrp.mcast.net: VRRPv2, Advertisement, vrid 20, prio 200, authtype none, intvl 1s, length 20
23:42:31.862787 IP 192.168.0.1 > vrrp.mcast.net: VRRPv2, Advertisement, vrid 20, prio 200, authtype none, intvl 1s, length 20
23:42:32.862931 IP 192.168.0.1 > vrrp.mcast.net: VRRPv2, Advertisement, vrid 20, prio 200, authtype none, intvl 1s, length 20
23:42:33.863184 IP 192.168.0.1 > vrrp.mcast.net: VRRPv2, Advertisement, vrid 20, prio 200, authtype none, intvl 1s, length 20
23:42:34.863314 IP 192.168.0.1 > vrrp.mcast.net: VRRPv2, Advertisement, vrid 20, prio 200, authtype none, intvl 1s, length 20
5 packets captured
5 packets received by filter
0 packets dropped by kernel
```

## NATの設定

### NATの設定

- VRRPの設定だけでは`pc01`からインターネットへの通信ができないため、`router01`と`router02`でNATの設定を行う

```bash
$ configure
# set nat source rule 100
# set nat source rule 100 outbound-interface eth1
# set nat source rule 100 translation address Z.Z.Z.Z
# set nat source rule 100 source address 192.168.0.0/24
# commit
# save
```

### 設定の確認

- 送信元のIPアドレスが`192.168.0.0/24`のネットワークに所属する場合、VIPである`Z.Z.Z.Z`に変換される

```bash
# run show nat source rules
Rule    Source          Destination    Proto    Out-Int    Translation
------  --------------  -------------  -------  ---------  ---------------
100     192.168.0.0/24  0.0.0.0/0      IP       eth1       Z.Z.Z.Z
        sport any       dport any
```

# 動作確認

## 疎通確認

- `pc01`からVIP宛ての通信

```bash:pc01
$ ping -c 5 192.168.0.254
PING 192.168.0.254 (192.168.0.254) 56(84) bytes of data.
64 bytes from 192.168.0.254: icmp_seq=1 ttl=64 time=1.03 ms
64 bytes from 192.168.0.254: icmp_seq=2 ttl=64 time=0.557 ms
64 bytes from 192.168.0.254: icmp_seq=3 ttl=64 time=0.514 ms
64 bytes from 192.168.0.254: icmp_seq=4 ttl=64 time=0.478 ms
64 bytes from 192.168.0.254: icmp_seq=5 ttl=64 time=0.517 ms

--- 192.168.0.254 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4071ms
rtt min/avg/max/mdev = 0.478/0.619/1.032/0.207 ms
```

- `pc01`からインターネット宛ての通信

```bash:pc01
$ ping -c 5 8.8.8.8
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=116 time=19.1 ms
64 bytes from 8.8.8.8: icmp_seq=2 ttl=116 time=20.7 ms
64 bytes from 8.8.8.8: icmp_seq=3 ttl=116 time=18.9 ms
64 bytes from 8.8.8.8: icmp_seq=4 ttl=116 time=18.9 ms
64 bytes from 8.8.8.8: icmp_seq=5 ttl=116 time=19.2 ms

--- 8.8.8.8 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4005ms
rtt min/avg/max/mdev = 18.914/19.356/20.663/0.661 ms
```

## フェイルオーバの検証

- 現在のマスタルータである`router01`がダウンした際に、`router02`がバックアップルータからマスタルータに昇格することを確認する
- まず`router01`をダウンさせる(今回は`poweroff`で擬似的に再現する)

```bash:router01
$ poweroff
Are you sure you want to poweroff this system? [y/N] y
```

- `router02`がバックアップルータからマスタルータに昇格している

```bash:router02
# run show vrrp
Name    Interface      VRID  State      Priority  Last Transition
------  -----------  ------  -------  ----------  -----------------
test01  eth1             10  MASTER          100  18s
test02  eth2             20  MASTER          100  18s
```

- `pc01`からVIP宛ての通信もできている

```bash:pc01
$ ping -c 5 192.168.0.254
PING 192.168.0.254 (192.168.0.254) 56(84) bytes of data.
64 bytes from 192.168.0.254: icmp_seq=1 ttl=64 time=1.02 ms
64 bytes from 192.168.0.254: icmp_seq=2 ttl=64 time=0.523 ms
64 bytes from 192.168.0.254: icmp_seq=3 ttl=64 time=0.644 ms
64 bytes from 192.168.0.254: icmp_seq=4 ttl=64 time=0.528 ms
64 bytes from 192.168.0.254: icmp_seq=5 ttl=64 time=0.444 ms

--- 192.168.0.254 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4075ms
rtt min/avg/max/mdev = 0.444/0.631/1.016/0.202 ms
```

- `pc01`からインターネット宛ての通信もできている

```bash:pc01
$ ping -c 5 8.8.8.8
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=116 time=19.4 ms
64 bytes from 8.8.8.8: icmp_seq=2 ttl=116 time=19.0 ms
64 bytes from 8.8.8.8: icmp_seq=3 ttl=116 time=18.8 ms
64 bytes from 8.8.8.8: icmp_seq=4 ttl=116 time=18.8 ms
64 bytes from 8.8.8.8: icmp_seq=5 ttl=116 time=18.9 ms

--- 8.8.8.8 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4006ms
rtt min/avg/max/mdev = 18.788/18.978/19.440/0.239 ms
```

## 復旧時の検証

- `router01`が復旧した際に、再び`router01`がマスタルータとなり、`router02`がバックアップルータとなることを確認する
- `router01`がマスタルータとして動作している

```bash:router01
# run show vrrp
Name    Interface      VRID  State      Priority  Last Transition
------  -----------  ------  -------  ----------  -----------------
test01  eth1             10  MASTER          200  2m36s
test02  eth2             20  MASTER          200  2m36s
```

- `router02`がバックアップルータとして動作している

```bash:router02
# run show vrrp
Name    Interface      VRID  State      Priority  Last Transition
------  -----------  ------  -------  ----------  -----------------
test01  eth1             10  BACKUP          100  2m25s
test02  eth2             20  BACKUP          100  2m25s
```

- なお、プリエンプトが無効化されている場合は切り替わらない
    - プリエンプトを無効化する設定は以下の通り

```bash
# set high-availability vrrp group test01 no-preempt
```