Docker上のFRRoutingでOSPFを動かしてみる

# はじめに
意外と情報が少なそうだったので、ここに記しておきます。個人のPC上など、なるべく省リソースでルーティングを試したい、という時に、Docker+FRRoutingはよい選択肢になるのではと思います。
※私自身初学者のため間違いなどあればコメントいただけると幸いです。

# 前提
下記図のように、VirtualBox上のUbuntuの上にDockerコンテナとしてFRRouting、および端末としてAlpine Linuxを建てます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/586231/8e6f14fc-d1ff-31bf-6142-6f8338baea2a.png)

使用したバージョンは以下の通り。
VirtualBox 6.1
Ubuntu 18.04
Docker 19.03.13
FRRouting 7.5

* Dockerまではインストールされていることを前提とします。
* Dockerネットワークの詳細についてはあまり触れません。必要に応じて参考ページを参照してください。
* FRRoutingのビルド方法はいろいろありますが、今回はなるべく手間をかけずに、[dockerhub](https://hub.docker.com/r/frrouting/frr)にあるものを使います。
* FRRoutingのルーティング設定方法として、/etc/frr/配下の設定ファイルに書き込む方法と、vtyshというCiscoライクなCLIで設定する方法があります。今回はよりルータを操作している感を出すために、(最大限)vtyshを使う方針とします。
* Ubuntu VMのスペックは、CPU 2 Core, メモリ4GB, ディスク10GBとしています。CPU、メモリは今回の構成ではそんなに必要なかったです。

# 作りたいネットワーク構成
このような最低限のネットワークを作り、OSPFを動かすところまでやってみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/586231/f49ce927-c3fb-6333-063a-84b2adef9a55.png)

frr1, frr2でOSPFを動かし、alpine1→alpine2にpingが通ることを目指します。

# Dockerネットワークとコンテナの作成
まずこちらのページを参考に、ネットワーク(ブリッジ)を作っていきます。
https://qiita.com/BooookStore/items/5862515209a31658f88c

```
# docker network create net1 --subnet=172.18.0.0/16
# docker network create net2 --subnet=172.19.0.0/16
# docker network create net3 --subnet=172.20.0.0/16
```

```
# docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
f7691e525fcd        bridge              bridge              local
5df12997a7cf        host                host                local
ad914b22edf4        net1                bridge              local
e7366bce6e2f        net2                bridge              local
9b0317dba894        net3                bridge              local
315847e7b1fe        none                null                local
```
net1～net3ができました。

次に、alpine1, alpine2を作ります。
ルーティングテーブルを編集するため、--privilegedをつけます。

```
# docker run -dit --name alpine1 --hostname alpine1 --privileged --net net1 alpine
# docker run -dit --name alpine2 --hostname alpine2 --privileged --net net3 alpine
```

frr1, frr2を作り、それぞれnet1とnet2、net2とnet3に接続します。
--privilegedをつけないと動かないようです。

```
# docker run -dit --name frr1 --hostname frr1 --privileged --net net1 frrouting/frr:v7.5.0
# docker network connect net2 frr1
# docker run -dit --name frr2 --hostname frr2 --privileged --net net2 frrouting/frr:v7.5.0
# docker network connect net3 frr2
```

# Alpineの設定
alpine1に入り、ルーティングテーブルを確認します。

```
# docker exec -it alpine1 /bin/sh
/ # route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         172.18.0.1      0.0.0.0         UG    0      0        0 eth0
172.18.0.0      0.0.0.0         255.255.0.0     U     0      0        0 eth0
```
デフォルトゲートウェイがブリッジ(172.18.0.1)になっているため、frr1に変更します。

```
/ # route add default gw 172.18.0.3
/ # route del default gw 172.18.0.1
/ # route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         172.18.0.3      0.0.0.0         UG    0      0        0 eth0
172.18.0.0      0.0.0.0         255.255.0.0     U     0      0        0 eth0

/ # exit
```

alpine2も同様にデフォルトゲートウェイを変更します。

```
# docker exec -it alpine2 /bin/sh
/ # route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         172.20.0.1      0.0.0.0         UG    0      0        0 eth0
172.20.0.0      0.0.0.0         255.255.0.0     U     0      0        0 eth0

/ # route add default gw 172.20.0.3
/ # route del default gw 172.20.0.1
/ # route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         172.20.0.3      0.0.0.0         UG    0      0        0 eth0
172.20.0.0      0.0.0.0         255.255.0.0     U     0      0        0 eth0

/ # exit
```

まだalpine1→alpine2へpingが通らないことを確認しておきます。

```
# docker exec -it alpine1 /bin/sh
/ # ping 172.20.0.2
PING 172.20.0.2 (172.20.0.2): 56 data bytes
^C
--- 172.20.0.2 ping statistics ---
6 packets transmitted, 0 packets received, 100% packet loss
```


# FRRoutingの設定

## frr1の設定
frr1に入り、ルーティングの設定をしていきます。

```
# docker exec -it frr1 /bin/sh
/ # ps
PID   USER     TIME  COMMAND
    1 root      0:00 /sbin/tini -- /usr/lib/frr/docker-start
    6 root      0:00 tail -f /dev/null
   21 root      0:00 /usr/lib/frr/watchfrr -d -F traditional zebra staticd
   33 frr       0:00 /usr/lib/frr/zebra -d -F traditional -A 127.0.0.1 -s 90000000
   38 frr       0:00 /usr/lib/frr/staticd -d -F traditional -A 127.0.0.1
   40 root      0:00 /bin/sh
   46 root      0:00 ps
```

既にwatchfrr, zebra, staticdが動いているのが確認できます。ospfは初期状態では動いていません。

この後、vtyshを使ってルータの設定をしていきますが、その前に事前設定として/etc/frr/vtysh.confを作っておきます。このファイルがないと、vtyshを起動したときに、

```
/ # vtysh
% Can't open configuration file /etc/frr/vtysh.conf due to 'No such file or directory'.
```
のように怒られます。

```
/ # cp /etc/frr/vtysh.conf.sample /etc/frr/vtysh.conf
```
ついでに、integrated-vtysh-configの設定もしておきます。FRRoutingでは、設定ファイルとして各デーモンごとの/etc/frr/*.confを使うのか、/etc/frr/frr.confにまとめるのか、を選べます。今回はまとめていきたいので、integrated-vtysh-configを設定します。[公式docs](http://docs.frrouting.org/en/latest/basic.html)でもintegratedが推奨されているようです。

```:/etc/frr/vtysh.conf
!
! Sample configuration file for vtysh.
!
!service integrated-vtysh-config
!hostname quagga-router
!username root nopassword
!
service integrated-vtysh-config
```
最終行を追加しました。

vtyshに入り、各種設定を確認しておきます。

```
/ # vtysh

Hello, this is FRRouting (version 7.5_git).
Copyright 1996-2005 Kunihiro Ishiguro, et al.

frr1# show run
Building configuration...

Current configuration:
!
frr version 7.5_git
frr defaults traditional
hostname frr1
no ipv6 forwarding
service integrated-vtysh-config
!
line vty
!
end

frr1# show int brief
Interface       Status  VRF             Addresses
---------       ------  ---             ---------
eth0            up      default         172.18.0.3/16
eth1            up      default         172.19.0.2/16
lo              up      default

frr1# show ip route
Codes: K - kernel route, C - connected, S - static, R - RIP,
       O - OSPF, I - IS-IS, B - BGP, E - EIGRP, N - NHRP,
       T - Table, v - VNC, V - VNC-Direct, A - Babel, D - SHARP,
       F - PBR, f - OpenFabric,
       > - selected route, * - FIB route, q - queued, r - rejected, b - backup

K>* 0.0.0.0/0 [0/0] via 172.18.0.1, eth0, 00:01:54
C>* 172.18.0.0/16 is directly connected, eth0, 00:01:54
C>* 172.19.0.0/16 is directly connected, eth1, 00:01:49
```

設定ファイル(/etc/frr/daemons)を編集し、ospfd=yesとしてospfdを起動するようにします。

```:/etc/frr/daemons
# ATTENTION:
#
# When activating a daemon for the first time, a config file, even if it is
# empty, has to be present *and* be owned by the user and group "frr", else
# the daemon will not be started by /etc/init.d/frr. The permissions should
# be u=rw,g=r,o=.
# When using "vtysh" such a config file is also needed. It should be owned by
# group "frrvty" and set to ug=rw,o= though. Check /etc/pam.d/frr, too.
#
# The watchfrr, zebra and staticd daemons are always started.
#
bgpd=no
ospfd=yes
ospf6d=no
ripd=no
以下略
```


設定反映のため、一度frr1を抜けてFRRを再起動します。(コンテナ内で`/etc/init.d/frr restart`ではないことに注意)
(追記：コンテナ内で`/usr/lib/frr/frrinit.sh restart`でも全プロセスの再起動ができるようです。)

```
/ # exit
# docker restart frr1
frr1
```
再度frr1に入り確認します。

```
# docker exec -it frr1 /bin/sh
/ # ps
PID   USER     TIME  COMMAND
    1 root      0:00 /sbin/tini -- /usr/lib/frr/docker-start
    7 root      0:00 tail -f /dev/null
   14 root      0:00 /usr/lib/frr/watchfrr -d -F traditional zebra ospfd staticd
   32 frr       0:00 /usr/lib/frr/zebra -d -F traditional -A 127.0.0.1 -s 90000000
   37 frr       0:00 /usr/lib/frr/ospfd -d -F traditional -A 127.0.0.1
   40 frr       0:00 /usr/lib/frr/staticd -d -F traditional -A 127.0.0.1
   42 root      0:00 /bin/sh
   48 root      0:00 ps
```
ospfdが起動しています。

それではvtysh上でospfの設定をしていきます。

```
/ # vtysh

Hello, this is FRRouting (version 7.5_git).
Copyright 1996-2005 Kunihiro Ishiguro, et al.

frr1# conf t
frr1(config)# interface lo
frr1(config-if)# ip address 1.1.1.1/32
frr1(config-if)# exit
frr1(config)# router ospf
frr1(config-router)# router-info area 0.0.0.0
frr1(config-router)# network 172.18.0.0/16 area 0.0.0.0
frr1(config-router)# network 172.19.0.0/16 area 0.0.0.0
frr1(config-router)# end
```
```
frr1# show run
Building configuration...

Current configuration:
!
frr version 7.5_git
frr defaults traditional
hostname frr1
no ipv6 forwarding
service integrated-vtysh-config
!
interface lo
 ip address 1.1.1.1/32
!
router ospf
 network 172.18.0.0/16 area 0.0.0.0
 network 172.19.0.0/16 area 0.0.0.0
 router-info area
!
line vty
!
end
```
設定されました。show ip routeも確認しておきます。

```
frr1# show ip route
Codes: K - kernel route, C - connected, S - static, R - RIP,
       O - OSPF, I - IS-IS, B - BGP, E - EIGRP, N - NHRP,
       T - Table, v - VNC, V - VNC-Direct, A - Babel, D - SHARP,
       F - PBR, f - OpenFabric,
       > - selected route, * - FIB route, q - queued, r - rejected, b - backup

K>* 0.0.0.0/0 [0/0] via 172.18.0.1, eth0, 00:03:30
C>* 1.1.1.1/32 is directly connected, lo, 00:01:54
O   172.18.0.0/16 [110/10] is directly connected, eth0, weight 1, 00:00:43
C>* 172.18.0.0/16 is directly connected, eth0, 00:03:30
O   172.19.0.0/16 [110/10] is directly connected, eth1, weight 1, 00:00:37
C>* 172.19.0.0/16 is directly connected, eth1, 00:03:30
```

(必要に応じて)設定を永続化しておきます。

```
frr1# write mem
Note: this version of vtysh never writes vtysh.conf
Building Configuration...
Integrated configuration saved to /etc/frr/frr.conf
[OK]
```

```
/ # cat /etc/frr/frr.conf
frr version 7.5_git
frr defaults traditional
hostname frr1
no ipv6 forwarding
service integrated-vtysh-config
!
interface lo
 ip address 1.1.1.1/32
!
router ospf
 network 172.18.0.0/16 area 0.0.0.0
 network 172.19.0.0/16 area 0.0.0.0
 router-info area
!
line vty
!
```
frr.confに書き込まれました。

## frr2の設定
frr2も同様に設定します。(/etc/frr/vtysh.conf, /etc/frr/daemonsの設定→FRR再起動の部分の記載は省略)

```
/ # vtysh

Hello, this is FRRouting (version 7.5_git).
Copyright 1996-2005 Kunihiro Ishiguro, et al.

frr2# conf t
frr2(config)# interface lo
frr2(config-if)# ip address 2.2.2.2/32
frr2(config-if)# exit
frr2(config)# router ospf
frr2(config-router)# router-info area 0.0.0.0
frr2(config-router)# network 172.19.0.0/16 area 0.0.0.0
frr2(config-router)# network 172.20.0.0/16 area 0.0.0.0
frr2(config-router)# end
```

frr2の設定も(必要に応じて)永続化します。

ここまで設定した状態で、frr1に入りip routeを確認すると、OSPFで172.20.0.0/16が追加されています。

```
frr1# show ip route
Codes: K - kernel route, C - connected, S - static, R - RIP,
       O - OSPF, I - IS-IS, B - BGP, E - EIGRP, N - NHRP,
       T - Table, v - VNC, V - VNC-Direct, A - Babel, D - SHARP,
       F - PBR, f - OpenFabric,
       > - selected route, * - FIB route, q - queued, r - rejected, b - backup

K>* 0.0.0.0/0 [0/0] via 172.18.0.1, eth0, 00:49:06
C>* 1.1.1.1/32 is directly connected, lo, 00:47:30
O   172.18.0.0/16 [110/10] is directly connected, eth0, weight 1, 00:46:19
C>* 172.18.0.0/16 is directly connected, eth0, 00:49:06
O   172.19.0.0/16 [110/10] is directly connected, eth1, weight 1, 00:46:13
C>* 172.19.0.0/16 is directly connected, eth1, 00:49:06
O>* 172.20.0.0/16 [110/20] via 172.19.0.3, eth1, weight 1, 00:04:26
```

neighborもちゃんと認識されています。

```
frr1# show ip ospf neighbor

Neighbor ID     Pri State           Dead Time Address         Interface                        RXmtL RqstL DBsmL
2.2.2.2           1 Full/Backup       34.411s 172.19.0.3      eth1:172.19.0.2                      0     0     0
```

alpine1→alpine2にpingが通るか試してみます。

```
# docker exec -it alpine1 /bin/sh
/ # ping 172.20.0.2
PING 172.20.0.2 (172.20.0.2): 56 data bytes
64 bytes from 172.20.0.2: seq=0 ttl=62 time=0.386 ms
64 bytes from 172.20.0.2: seq=1 ttl=62 time=0.468 ms
64 bytes from 172.20.0.2: seq=2 ttl=62 time=0.467 ms
64 bytes from 172.20.0.2: seq=3 ttl=62 time=0.464 ms
^C
--- 172.20.0.2 ping statistics ---
4 packets transmitted, 4 packets received, 0% packet loss
round-trip min/avg/max = 0.386/0.446/0.468 ms
```
通りました！

# 追記
docker-compose化しました。一瞬で作りたい方はどうぞ。
https://github.com/tkna/docker_frrouting

# 参考
* [FRRouting User Guide](http://docs.frrouting.org/en/latest/)
* [Dockerで経路制御によりブリッジネットワーク間で通信してみる！](https://qiita.com/BooookStore/items/5862515209a31658f88c)
* [Getting Started - Simple OSPF/BGP Topology]
(https://iprouteget.net/posts/2020/12/getting-started-simple-ospf/bgp-topology/)